diff --git a/src/include/server/mir/shell/decoration.h b/src/include/server/mir/shell/decoration.h deleted file mode 100644 index 85f6c05e312..00000000000 --- a/src/include/server/mir/shell/decoration.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright © Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 or 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef MIR_SHELL_DECORATION_H_ -#define MIR_SHELL_DECORATION_H_ - -#include "mir/geometry/forward.h" -#include "mir_toolkit/common.h" - -namespace mir::shell::decoration -{ - auto compute_size_with_decorations(geometry::Size content_size, MirWindowType type, MirWindowState state) -> geometry::Size; -} - -#endif diff --git a/src/server/shell/abstract_shell.cpp b/src/server/shell/abstract_shell.cpp index a868d8fbcee..f6972e454b7 100644 --- a/src/server/shell/abstract_shell.cpp +++ b/src/server/shell/abstract_shell.cpp @@ -16,7 +16,6 @@ #include "mir/shell/abstract_shell.h" -#include "mir/shell/decoration.h" #include "mir/shell/input_targeter.h" #include "mir/shell/shell_report.h" #include "mir/shell/surface_specification.h" @@ -233,7 +232,7 @@ auto msh::AbstractShell::create_surface( if (wm_visible_spec.server_side_decorated.value_or(false) && wm_visible_spec.width.is_set() && wm_visible_spec.height.is_set()) { geom::Size const content_size{wm_visible_spec.width.value(), wm_visible_spec.height.value()}; - auto const size = decoration::compute_size_with_decorations( + auto const size = decoration_manager->compute_size_with_decorations( content_size, wm_visible_spec.type.value(), wm_visible_spec.state.value()); @@ -302,7 +301,7 @@ void msh::AbstractShell::modify_surface(std::shared_ptr const& s wm_relevant_mods.height = content_size.height; // When adding decorations we need to resize the window for WM - window_size = decoration::compute_size_with_decorations( + window_size = decoration_manager->compute_size_with_decorations( content_size, modifications.type.value_or(surface->type()), modifications.state.value_or(surface->state())); diff --git a/src/server/shell/decoration/CMakeLists.txt b/src/server/shell/decoration/CMakeLists.txt index 78785e7c0a2..22cc026d44b 100644 --- a/src/server/shell/decoration/CMakeLists.txt +++ b/src/server/shell/decoration/CMakeLists.txt @@ -9,7 +9,8 @@ set( basic_decoration.h basic_decoration.cpp window.h window.cpp input.h input.cpp - renderer.h renderer.cpp renderer_default.cpp + renderer.h renderer.cpp + decoration_strategy.h decoration_strategy.cpp ) add_library( diff --git a/src/server/shell/decoration/basic_decoration.cpp b/src/server/shell/decoration/basic_decoration.cpp index 0674b8710a2..bf4d74a7e37 100644 --- a/src/server/shell/decoration/basic_decoration.cpp +++ b/src/server/shell/decoration/basic_decoration.cpp @@ -42,23 +42,6 @@ namespace geom = mir::geometry; namespace msh = mir::shell; namespace msd = mir::shell::decoration; -namespace mir::shell::decoration -{ -// See src/server/shell/decoration/window.h for a full description of each property -StaticGeometry const default_geometry { - geom::Height{24}, // titlebar_height - geom::Width{6}, // side_border_width - geom::Height{6}, // bottom_border_height - geom::Size{16, 16}, // resize_corner_input_size - geom::Width{24}, // button_width - geom::Width{6}, // padding_between_buttons - geom::Height{14}, // title_font_height - geom::Point{8, 2}, // title_font_top_left - geom::Displacement{5, 5}, // icon_padding - geom::Width{1}, // detail_line_width -}; -} - namespace { template @@ -146,29 +129,22 @@ msd::BasicDecoration::BufferStreams::~BufferStreams() session->destroy_buffer_stream(bottom_border); } -auto msd::BasicDecoration::BufferStreams::create_buffer_stream() -> std::shared_ptr -{ - auto const stream = session->create_buffer_stream(mg::BufferProperties{ - geom::Size{1, 1}, - buffer_format, - mg::BufferUsage::software}); - return stream; -} - msd::BasicDecoration::BasicDecoration( std::shared_ptr const& shell, std::shared_ptr const& buffer_allocator, std::shared_ptr const& executor, std::shared_ptr const& cursor_images, - std::shared_ptr const& window_surface) + std::shared_ptr const& window_surface, + std::shared_ptr decoration_strategy) : threadsafe_self{std::make_shared>(executor)}, - static_geometry{std::make_shared(default_geometry)}, + decoration_strategy{decoration_strategy}, + static_geometry{decoration_strategy->static_geometry()}, shell{shell}, buffer_allocator{buffer_allocator}, cursor_images{cursor_images}, session{window_surface->session().lock()}, buffer_streams{std::make_unique(session, static_geometry->buffer_format)}, - renderer{std::make_unique(buffer_allocator, RendererStrategy::default_strategy(static_geometry))}, + renderer{std::make_unique(buffer_allocator, decoration_strategy->render_strategy())}, window_surface{window_surface}, decoration_surface{create_surface()}, window_state{new_window_state()}, @@ -176,7 +152,7 @@ msd::BasicDecoration::BasicDecoration( window_surface, threadsafe_self)}, input_manager{std::make_unique( - static_geometry, + decoration_strategy, decoration_surface, *window_state, threadsafe_self)}, @@ -194,6 +170,15 @@ msd::BasicDecoration::BasicDecoration( threadsafe_self->initialize(this); } +auto msd::BasicDecoration::BufferStreams::create_buffer_stream() -> std::shared_ptr +{ + auto const stream = session->create_buffer_stream(mg::BufferProperties{ + geom::Size{1, 1}, + buffer_format, + mg::BufferUsage::software}); + return stream; +} + msd::BasicDecoration::~BasicDecoration() { threadsafe_self->invalidate(); diff --git a/src/server/shell/decoration/basic_decoration.h b/src/server/shell/decoration/basic_decoration.h index e4beabd2905..fb4bc0dbc6a 100644 --- a/src/server/shell/decoration/basic_decoration.h +++ b/src/server/shell/decoration/basic_decoration.h @@ -18,6 +18,7 @@ #define MIR_SHELL_DECORATION_BASIC_DECORATION_H_ #include "decoration.h" +#include "decoration_strategy.h" #include "mir/geometry/rectangle.h" #include "mir_toolkit/common.h" @@ -72,7 +73,8 @@ class BasicDecoration std::shared_ptr const& buffer_allocator, std::shared_ptr const& executor, std::shared_ptr const& cursor_images, - std::shared_ptr const& window_surface); + std::shared_ptr const& window_surface, + std::shared_ptr decoration_strategy); ~BasicDecoration(); void window_state_updated(); @@ -103,7 +105,8 @@ class BasicDecoration std::optional previous_input_state); std::shared_ptr> const threadsafe_self; - std::shared_ptr const static_geometry; + std::shared_ptr const decoration_strategy; + std::shared_ptr const static_geometry; std::shared_ptr const shell; std::shared_ptr const buffer_allocator; diff --git a/src/server/shell/decoration/basic_manager.cpp b/src/server/shell/decoration/basic_manager.cpp index 4056707df00..43ab25f0689 100644 --- a/src/server/shell/decoration/basic_manager.cpp +++ b/src/server/shell/decoration/basic_manager.cpp @@ -16,6 +16,7 @@ #include "basic_manager.h" #include "decoration.h" +#include "decoration_strategy.h" #include #include @@ -49,8 +50,10 @@ class msd::DisplayConfigurationListener : public mg::NullDisplayConfigurationOb }; msd::BasicManager::BasicManager( + std::shared_ptr const& decoration_strategy, ObserverRegistrar& display_configuration_observers, DecorationBuilder&& decoration_builder) : + decoration_strategy{decoration_strategy}, decoration_builder{std::move(decoration_builder)}, display_config_monitor{std::make_shared( [&](mg::DisplayConfiguration const& config) @@ -92,7 +95,7 @@ void msd::BasicManager::decorate(std::shared_ptr const& surface) { decorations[surface.get()] = nullptr; lock.unlock(); - auto decoration = decoration_builder(locked_shell, surface); + auto decoration = decoration_builder(decoration_strategy, locked_shell, surface); lock.lock(); decoration->set_scale(scale); decorations[surface.get()] = std::move(decoration); @@ -128,6 +131,29 @@ void msd::BasicManager::undecorate_all() to_destroy.clear(); } +using mir::geometry::Size; + +auto msd::BasicManager::compute_size_with_decorations(Size content_size, + MirWindowType type, MirWindowState state) -> Size +{ + auto const geometry = decoration_strategy->static_geometry(); + + switch (border_type_for(type, state)) + { + case msd::BorderType::Full: + content_size.width += geometry->side_border_width * 2; + content_size.height += geometry->titlebar_height + geometry->bottom_border_height; + break; + case msd::BorderType::Titlebar: + content_size.height += geometry->titlebar_height; + break; + case msd::BorderType::None: + break; + } + + return content_size; +} + void msd::BasicManager::set_scale(float new_scale) { std::lock_guard lock{mutex}; @@ -139,4 +165,4 @@ void msd::BasicManager::set_scale(float new_scale) it.second->set_scale(scale); } } -} \ No newline at end of file +} diff --git a/src/server/shell/decoration/basic_manager.h b/src/server/shell/decoration/basic_manager.h index be4f2413924..5ae39b36f98 100644 --- a/src/server/shell/decoration/basic_manager.h +++ b/src/server/shell/decoration/basic_manager.h @@ -40,6 +40,7 @@ class Shell; namespace decoration { class Decoration; +class DecorationStrategy; class DisplayConfigurationListener; /// Facilitates decorating windows with Mir's built-in server size decorations @@ -48,10 +49,12 @@ class BasicManager : { public: using DecorationBuilder = std::function( + std::shared_ptr const& decoration_strategy, std::shared_ptr const& shell, std::shared_ptr const& surface)>; BasicManager( + std::shared_ptr const& decoration_strategy, mir::ObserverRegistrar& display_configuration_observers, DecorationBuilder&& decoration_builder); ~BasicManager(); @@ -60,8 +63,11 @@ class BasicManager : void decorate(std::shared_ptr const& surface) override; void undecorate(std::shared_ptr const& surface) override; void undecorate_all() override; + auto compute_size_with_decorations(geometry::Size content_size, MirWindowType type, + MirWindowState state) -> geometry::Size override; private: + std::shared_ptr const decoration_strategy; DecorationBuilder const decoration_builder; std::shared_ptr const display_config_monitor; std::weak_ptr shell; diff --git a/src/server/shell/decoration/renderer_default.cpp b/src/server/shell/decoration/decoration_strategy.cpp similarity index 84% rename from src/server/shell/decoration/renderer_default.cpp rename to src/server/shell/decoration/decoration_strategy.cpp index 03c494b91f7..b025233a190 100644 --- a/src/server/shell/decoration/renderer_default.cpp +++ b/src/server/shell/decoration/decoration_strategy.cpp @@ -15,22 +15,24 @@ */ -#include "renderer.h" +#include "decoration_strategy.h" #include "window.h" +#include "mir/fatal.h" #include "mir/geometry/displacement.h" #include "mir/log.h" #include -#include #include #include FT_FREETYPE_H +#include #include #include +#include +#include namespace ms = mir::scene; -namespace mg = mir::graphics; namespace geom = mir::geometry; namespace msh = mir::shell; namespace msd = mir::shell::decoration; @@ -44,21 +46,30 @@ namespace { static constexpr auto color(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 0xFF) -> uint32_t { +#if __BYTE_ORDER == __LITTLE_ENDIAN return ((uint32_t)b << 0) | ((uint32_t)g << 8) | ((uint32_t)r << 16) | ((uint32_t)a << 24); +#elif __BYTE_ORDER == __BIG_ENDIAN + return ((uint32_t)b << 24) | + ((uint32_t)g << 16) | + ((uint32_t)r << 8) | + ((uint32_t)a << 0); +#else +#error unsupported byte order +#endif } -uint32_t const default_focused_background = color(0x32, 0x32, 0x32); -uint32_t const default_unfocused_background = color(0x80, 0x80, 0x80); -uint32_t const default_focused_text = color(0xFF, 0xFF, 0xFF); -uint32_t const default_unfocused_text = color(0xA0, 0xA0, 0xA0); -uint32_t const default_normal_button = color(0x60, 0x60, 0x60); -uint32_t const default_active_button = color(0xA0, 0xA0, 0xA0); -uint32_t const default_close_normal_button = color(0xA0, 0x20, 0x20); -uint32_t const default_close_active_button = color(0xC0, 0x60, 0x60); -uint32_t const default_button_icon = color(0xFF, 0xFF, 0xFF); +uint32_t constexpr default_focused_background = color(0x32, 0x32, 0x32); +uint32_t constexpr default_unfocused_background = color(0x80, 0x80, 0x80); +uint32_t constexpr default_focused_text = color(0xFF, 0xFF, 0xFF); +uint32_t constexpr default_unfocused_text = color(0xA0, 0xA0, 0xA0); +uint32_t constexpr default_normal_button = color(0x60, 0x60, 0x60); +uint32_t constexpr default_active_button = color(0xA0, 0xA0, 0xA0); +uint32_t constexpr default_close_normal_button = color(0xA0, 0x20, 0x20); +uint32_t constexpr default_close_active_button = color(0xC0, 0x60, 0x60); +uint32_t constexpr default_button_icon = color(0xFF, 0xFF, 0xFF); /// Font search logic should be kept in sync with examples/example-server-lib/wallpaper_config.cpp auto default_font() -> std::string @@ -94,10 +105,10 @@ auto default_font() -> std::string "/usr/share/fonts", // Fedora/Arch }; - std::vector usable_search_paths; + std::vector usable_search_paths; for (auto const& path : font_path_search_paths) { - if (boost::filesystem::exists(path)) + if (std::filesystem::exists(path)) usable_search_paths.push_back(path); } @@ -107,8 +118,8 @@ auto default_font() -> std::string { for (auto const& path : usable_search_paths) { - auto const full_font_path = path + '/' + prefix + '/' + font.filename; - if (boost::filesystem::exists(full_font_path)) + auto const full_font_path = path / prefix / font.filename; + if (std::filesystem::exists(full_font_path)) return full_font_path; } } @@ -199,7 +210,7 @@ inline void render_minimize_icon( struct RendererStrategy : public msd::RendererStrategy { - RendererStrategy(std::shared_ptr const& static_geometry); + RendererStrategy(std::shared_ptr const& static_geometry); void update_state(WindowState const& window_state, InputState const& input_state) override; auto render_titlebar() -> std::optional override; @@ -208,7 +219,7 @@ struct RendererStrategy : public msd::RendererStrategy auto render_bottom_border() -> std::optional override; private: - std::shared_ptr const static_geometry; + std::shared_ptr const static_geometry; /// A visual theme for a decoration /// Focused and unfocused windows use a different theme @@ -288,12 +299,64 @@ struct RendererStrategy : public msd::RendererStrategy void redraw_titlebar_text(geom::Size scaled_titlebar_size); void redraw_titlebar_buttons(geom::Size scaled_titlebar_size); }; + +class DecorationStrategy : public msd::DecorationStrategy +{ +public: + auto static_geometry() const -> std::shared_ptr override; + auto render_strategy() const -> std::unique_ptr override; + auto button_placement(unsigned n, const WindowState& ws) const -> mir::geometry::Rectangle override; +}; } -auto msd::RendererStrategy::default_strategy(std::shared_ptr const& static_geometry) --> std::unique_ptr +auto msd::border_type_for(MirWindowType type, MirWindowState state) -> msd::BorderType { - return std::make_unique<::RendererStrategy>(static_geometry); + using BorderType = msd::BorderType; + + switch (type) + { + case mir_window_type_normal: + case mir_window_type_utility: + case mir_window_type_dialog: + case mir_window_type_freestyle: + case mir_window_type_satellite: + break; + + case mir_window_type_gloss: + case mir_window_type_menu: + case mir_window_type_inputmethod: + case mir_window_type_tip: + case mir_window_type_decoration: + case mir_window_types: + return BorderType::None; + } + + switch (state) + { + case mir_window_state_unknown: + case mir_window_state_restored: + return BorderType::Full; + + case mir_window_state_maximized: + case mir_window_state_vertmaximized: + case mir_window_state_horizmaximized: + return BorderType::Titlebar; + + case mir_window_state_minimized: + case mir_window_state_fullscreen: + case mir_window_state_hidden: + case mir_window_state_attached: + case mir_window_states: + return BorderType::None; + } + + mir::fatal_error("%s:%d: should be unreachable", __FILE__, __LINE__); + return {}; +} + +auto msd::DecorationStrategy::default_decoration_strategy() -> std::unique_ptr +{ + return std::make_unique<::DecorationStrategy>(); } class RendererStrategy::Text::Impl @@ -563,7 +626,7 @@ auto RendererStrategy::Text::Impl::utf8_to_utf32(std::string const& text) -> std return utf32_text; } -RendererStrategy::RendererStrategy(std::shared_ptr const& static_geometry) : +RendererStrategy::RendererStrategy(std::shared_ptr const& static_geometry) : static_geometry{static_geometry}, focused_theme{ default_focused_background, @@ -818,3 +881,42 @@ void RendererStrategy::set_focus_state(MirWindowFocusState focus_state) needs_solid_color_redraw = true; } } + + +auto DecorationStrategy::static_geometry() const -> std::shared_ptr +{ + static auto const geometry{std::make_shared( + geom::Height{24}, // titlebar_height + geom::Width{6}, // side_border_width + geom::Height{6}, // bottom_border_height + geom::Size{16, 16}, // resize_corner_input_size + geom::Width{24}, // button_width + geom::Width{6}, // padding_between_buttons + geom::Height{14}, // title_font_height + geom::Point{8, 2}, // title_font_top_left + geom::Displacement{5, 5}, // icon_padding + geom::Width{1}, // detail_line_width + mir_pixel_format_argb_8888 // buffer_format + )}; + return geometry; +} + +auto DecorationStrategy::render_strategy() const -> std::unique_ptr +{ + return std::make_unique<::RendererStrategy>(static_geometry()); +} + +auto DecorationStrategy::button_placement(unsigned n, const WindowState& ws) const -> mir::geometry::Rectangle +{ + auto const titlebar = ws.titlebar_rect(); + auto const geometry = static_geometry(); + + geom::X x = + titlebar.right() - + as_delta(ws.side_border_width()) - + n * as_delta(geometry->button_width + geometry->padding_between_buttons) - + as_delta(geometry->button_width); + return geom::Rectangle{ + {x, titlebar.top()}, + {geometry->button_width, titlebar.size.height}}; +} diff --git a/src/server/shell/decoration/decoration_strategy.h b/src/server/shell/decoration/decoration_strategy.h new file mode 100644 index 00000000000..98fd260651b --- /dev/null +++ b/src/server/shell/decoration/decoration_strategy.h @@ -0,0 +1,199 @@ +/* +* Copyright © Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MIR_SHELL_DECORATION_STRATEGY_H_ +#define MIR_SHELL_DECORATION_STRATEGY_H_ + +#include "mir/geometry/displacement.h" +#include "mir/geometry/point.h" +#include "mir/geometry/rectangle.h" +#include "mir/geometry/size.h" +#include "mir_toolkit/client_types.h" + +#include +#include +#include + +namespace mir::scene { class Surface; } + +namespace mir::shell::decoration +{ +using Pixel = uint32_t; + +/// Decoration geometry properties that don't change +struct StaticGeometry +{ + geometry::Height const titlebar_height; ///< Visible height of the top titlebar with the window's name and buttons + geometry::Width const side_border_width; ///< Visible width of the side borders + geometry::Height const bottom_border_height; ///< Visible height of the bottom border + geometry::Size const resize_corner_input_size; ///< The size of the input area of a resizable corner + ///< (does not effect surface input area, only where in the surface is + ///< considered a resize corner) + geometry::Width const button_width; ///< The width of window control buttons + geometry::Width const padding_between_buttons; ///< The gep between titlebar buttons + geometry::Height const title_font_height; ///< Height of the text used to render the window title + geometry::Point const title_font_top_left; ///< Where to render the window title + geometry::Displacement const icon_padding; ///< Padding inside buttons around icons + geometry::Width const icon_line_width; ///< Width for lines in button icons + + MirPixelFormat const buffer_format = mir_pixel_format_argb_8888; +}; + +enum class ButtonState +{ + Up, ///< The user is not interacting with this button + Hovered, ///< The user is hovering over this button + Down, ///< The user is currently pressing this button +}; + +enum class ButtonFunction +{ + Close, + Maximize, + Minimize, +}; + +enum class BorderType +{ + Full, ///< Full titlebar and border (for restored windows) + Titlebar, ///< Titlebar only (for maximized windows) + None, ///< No decorations (for fullscreen windows or popups) +}; + +auto border_type_for(MirWindowType type, MirWindowState state) -> BorderType; + +struct ButtonInfo +{ + ButtonFunction function; + ButtonState state; + geometry::Rectangle rect; + + auto operator==(ButtonInfo const& other) const -> bool + { + return function == other.function && + state == other.state && + rect == other.rect; + } +}; + +/// Describes the state of the interface (what buttons are pushed, etc) +class InputState +{ +public: + InputState( + std::vector const& buttons, + std::vector const& input_shape) + : buttons_{buttons}, + input_shape_{input_shape} + { + } + + auto buttons() const -> std::vector const& { return buttons_; } + auto input_shape() const -> std::vector const& { return input_shape_; } + +private: + InputState(InputState const&) = delete; + InputState& operator=(InputState const&) = delete; + + std::vector const buttons_; + std::vector const input_shape_; +}; + +/// Information about the geometry and type of decorations for a given window +/// Data is pulled from the surface on construction and immutable after that +class WindowState +{ +public: + WindowState( + std::shared_ptr const& static_geometry, + std::shared_ptr const& window_surface, + float scale); + + auto window_size() const -> geometry::Size; + auto border_type() const -> BorderType; + auto focused_state() const -> MirWindowFocusState; + auto window_name() const -> std::string; + + auto titlebar_width() const -> geometry::Width; + auto titlebar_height() const -> geometry::Height; + + auto side_border_width() const -> geometry::Width; + auto side_border_height() const -> geometry::Height; + + auto bottom_border_width() const -> geometry::Width; + auto bottom_border_height() const -> geometry::Height; + + auto titlebar_rect() const -> geometry::Rectangle; + auto left_border_rect() const -> geometry::Rectangle; + auto right_border_rect() const -> geometry::Rectangle; + auto bottom_border_rect() const -> geometry::Rectangle; + + auto scale() const -> float; + +private: + WindowState(WindowState const&) = delete; + WindowState& operator=(WindowState const&) = delete; + + std::shared_ptr const static_geometry; + geometry::Size const window_size_; + BorderType const border_type_; + MirWindowFocusState const focus_state_; + std::string window_name_; + float scale_; +}; + +class RendererStrategy +{ +public: + static auto alloc_pixels(geometry::Size size) -> std::unique_ptr; + + RendererStrategy() = default; + virtual ~RendererStrategy() = default; + + struct RenderedPixels + { + MirPixelFormat const format; + geometry::Size const size; + Pixel const* const pixels; + }; + + virtual void update_state(WindowState const& window_state, InputState const& input_state) = 0; + virtual auto render_titlebar() -> std::optional = 0; + virtual auto render_left_border() -> std::optional = 0; + virtual auto render_right_border() -> std::optional = 0; + virtual auto render_bottom_border() -> std::optional = 0; +}; + +class DecorationStrategy +{ +public: + + static auto default_decoration_strategy() -> std::unique_ptr; + + virtual auto static_geometry() const -> std::shared_ptr = 0; + virtual auto render_strategy() const -> std::unique_ptr = 0; + virtual auto button_placement(unsigned n, WindowState const& ws) const -> geometry::Rectangle = 0; + + DecorationStrategy() = default; + virtual ~DecorationStrategy() = default; + +private: + DecorationStrategy(DecorationStrategy const&) = delete; + DecorationStrategy& operator=(DecorationStrategy const&) = delete; +}; +} + +#endif //MIR_SHELL_DECORATION_STRATEGY_H_ diff --git a/src/server/shell/decoration/input.cpp b/src/server/shell/decoration/input.cpp index 79b0d848c78..2ddc9d023ae 100644 --- a/src/server/shell/decoration/input.cpp +++ b/src/server/shell/decoration/input.cpp @@ -118,27 +118,28 @@ struct msd::InputManager::Observer }; msd::InputManager::InputManager( - std::shared_ptr const& static_geometry, + std::shared_ptr const& decoration_strategy, std::shared_ptr const& decoration_surface, WindowState const& window_state, - std::shared_ptr> const& decoration) - : static_geometry{static_geometry}, - decoration_surface{decoration_surface}, - observer{std::make_shared(this)}, - decoration{decoration}, - widgets{ - std::make_shared(ButtonFunction::Close), - std::make_shared(ButtonFunction::Maximize), - std::make_shared(ButtonFunction::Minimize), - std::make_shared(mir_resize_edge_northwest), - std::make_shared(mir_resize_edge_northeast), - std::make_shared(mir_resize_edge_southwest), - std::make_shared(mir_resize_edge_southeast), - std::make_shared(mir_resize_edge_north), - std::make_shared(mir_resize_edge_south), - std::make_shared(mir_resize_edge_west), - std::make_shared(mir_resize_edge_east), - std::make_shared(mir_resize_edge_none)} + std::shared_ptr> const& decoration) : + decoration_strategy{decoration_strategy}, + static_geometry{decoration_strategy->static_geometry()}, + decoration_surface{decoration_surface}, + observer{std::make_shared(this)}, + decoration{decoration}, + widgets{ + std::make_shared(ButtonFunction::Close), + std::make_shared(ButtonFunction::Maximize), + std::make_shared(ButtonFunction::Minimize), + std::make_shared(mir_resize_edge_northwest), + std::make_shared(mir_resize_edge_northeast), + std::make_shared(mir_resize_edge_southwest), + std::make_shared(mir_resize_edge_southeast), + std::make_shared(mir_resize_edge_north), + std::make_shared(mir_resize_edge_south), + std::make_shared(mir_resize_edge_west), + std::make_shared(mir_resize_edge_east), + std::make_shared(mir_resize_edge_none)} { set_cursor(mir_resize_edge_none); update_window_state(window_state); @@ -159,7 +160,7 @@ void msd::InputManager::update_window_state(WindowState const& window_state) { if (widget->button) { - widget->rect = window_state.button_rect(button_index); + widget->rect = decoration_strategy->button_placement(button_index, window_state); button_index++; } else if (widget->resize_edge) diff --git a/src/server/shell/decoration/input.h b/src/server/shell/decoration/input.h index 5f8e39c0fc5..a0e8b4e493c 100644 --- a/src/server/shell/decoration/input.h +++ b/src/server/shell/decoration/input.h @@ -17,6 +17,7 @@ #ifndef MIR_SHELL_DECORATION_INPUT_H_ #define MIR_SHELL_DECORATION_INPUT_H_ +#include "decoration_strategy.h" #include "mir/geometry/rectangle.h" #include "mir_toolkit/common.h" @@ -49,63 +50,12 @@ class BasicDecoration; class StaticGeometry; template class ThreadsafeAccess; -enum class ButtonState -{ - Up, ///< The user is not interacting with this button - Hovered, ///< The user is hovering over this button - Down, ///< The user is currently pressing this button -}; - -enum class ButtonFunction -{ - Close, - Maximize, - Minimize, -}; - -struct ButtonInfo -{ - ButtonFunction function; - ButtonState state; - geometry::Rectangle rect; - - auto operator==(ButtonInfo const& other) const -> bool - { - return function == other.function && - state == other.state && - rect == other.rect; - } -}; - -/// Describes the state of the interface (what buttons are pushed, etc) -class InputState -{ -public: - InputState( - std::vector const& buttons, - std::vector const& input_shape) - : buttons_{buttons}, - input_shape_{input_shape} - { - } - - auto buttons() const -> std::vector const& { return buttons_; } - auto input_shape() const -> std::vector const& { return input_shape_; } - -private: - InputState(InputState const&) = delete; - InputState& operator=(InputState const&) = delete; - - std::vector const buttons_; - std::vector const input_shape_; -}; - /// Manages the observer that listens to user input class InputManager { public: InputManager( - std::shared_ptr const& static_geometry, + std::shared_ptr const& decoration_strategy, std::shared_ptr const& decoration_surface, WindowState const& window_state, std::shared_ptr> const& decoration); @@ -132,7 +82,8 @@ class InputManager class Observer; std::mutex mutex; - std::shared_ptr const static_geometry; + std::shared_ptr const decoration_strategy; + std::shared_ptr const static_geometry; std::shared_ptr decoration_surface; std::shared_ptr const observer; std::shared_ptr> decoration; diff --git a/src/server/shell/decoration/manager.h b/src/server/shell/decoration/manager.h index eae620b7f53..30870af674e 100644 --- a/src/server/shell/decoration/manager.h +++ b/src/server/shell/decoration/manager.h @@ -17,6 +17,9 @@ #ifndef MIR_SHELL_DECORATION_MANAGER_H_ #define MIR_SHELL_DECORATION_MANAGER_H_ +#include "mir/geometry/size.h" +#include "mir_toolkit/client_types.h" + #include namespace mir @@ -49,6 +52,10 @@ class Manager /// Removes decorations from all currently decorated windows virtual void undecorate_all() = 0; + /// Compute the corresponding size + virtual auto compute_size_with_decorations(geometry::Size content_size, MirWindowType type, MirWindowState state) + -> geometry::Size = 0; + private: Manager(Manager const&) = delete; Manager& operator=(Manager const&) = delete; diff --git a/src/server/shell/decoration/null_manager.h b/src/server/shell/decoration/null_manager.h index ea69e7b1b61..57159613034 100644 --- a/src/server/shell/decoration/null_manager.h +++ b/src/server/shell/decoration/null_manager.h @@ -33,6 +33,8 @@ class NullManager void decorate(std::shared_ptr const&) override {} void undecorate(std::shared_ptr const&) override {} void undecorate_all() override {} + auto compute_size_with_decorations(geometry::Size sz, MirWindowType, MirWindowState) -> geometry::Size override + { return sz; } }; } } diff --git a/src/server/shell/decoration/renderer.h b/src/server/shell/decoration/renderer.h index 24a5380dde7..1bc1a703d71 100644 --- a/src/server/shell/decoration/renderer.h +++ b/src/server/shell/decoration/renderer.h @@ -17,9 +17,11 @@ #ifndef MIR_SHELL_DECORATION_RENDERER_H_ #define MIR_SHELL_DECORATION_RENDERER_H_ +#include "decoration_strategy.h" +#include "input.h" + #include "mir/geometry/rectangle.h" -#include "input.h" #include #include @@ -39,32 +41,6 @@ class WindowState; class InputState; struct StaticGeometry; -using Pixel = uint32_t; - - -class RendererStrategy -{ -public: - static auto alloc_pixels(geometry::Size size) -> std::unique_ptr; - static auto default_strategy(std::shared_ptr const& static_geometry) -> std::unique_ptr; - - RendererStrategy() = default; - virtual ~RendererStrategy() = default; - - struct RenderedPixels - { - MirPixelFormat const format; - geometry::Size const size; - Pixel const* const pixels; - }; - - virtual void update_state(WindowState const& window_state, InputState const& input_state) = 0; - virtual auto render_titlebar() -> std::optional = 0; - virtual auto render_left_border() -> std::optional = 0; - virtual auto render_right_border() -> std::optional = 0; - virtual auto render_bottom_border() -> std::optional = 0; -}; - class Renderer { public: diff --git a/src/server/shell/decoration/window.cpp b/src/server/shell/decoration/window.cpp index 593d284620f..300d4039be0 100644 --- a/src/server/shell/decoration/window.cpp +++ b/src/server/shell/decoration/window.cpp @@ -17,65 +17,17 @@ #include "window.h" #include "threadsafe_access.h" #include "basic_decoration.h" +#include "decoration_strategy.h" #include "mir/scene/surface.h" #include "mir/scene/null_surface_observer.h" -#include "mir/shell/decoration.h" namespace ms = mir::scene; namespace geom = mir::geometry; namespace msd = mir::shell::decoration; -namespace -{ -auto border_type_for(MirWindowType type, MirWindowState state) -> msd::BorderType -{ - using BorderType = msd::BorderType; - - switch (type) - { - case mir_window_type_normal: - case mir_window_type_utility: - case mir_window_type_dialog: - case mir_window_type_freestyle: - case mir_window_type_satellite: - break; - - case mir_window_type_gloss: - case mir_window_type_menu: - case mir_window_type_inputmethod: - case mir_window_type_tip: - case mir_window_type_decoration: - case mir_window_types: - return BorderType::None; - } - - switch (state) - { - case mir_window_state_unknown: - case mir_window_state_restored: - return BorderType::Full; - - case mir_window_state_maximized: - case mir_window_state_vertmaximized: - case mir_window_state_horizmaximized: - return BorderType::Titlebar; - - case mir_window_state_minimized: - case mir_window_state_fullscreen: - case mir_window_state_hidden: - case mir_window_state_attached: - case mir_window_states: - return BorderType::None; - } - - mir::fatal_error("%s:%d: should be unreachable", __FILE__, __LINE__); - return {}; -} -} - msd::WindowState::WindowState( - std::shared_ptr const& static_geometry, + std::shared_ptr const& static_geometry, std::shared_ptr const& surface, float scale) : static_geometry{static_geometry}, @@ -215,19 +167,6 @@ auto msd::WindowState::bottom_border_rect() const -> geom::Rectangle {bottom_border_width(), bottom_border_height()}}; } -auto msd::WindowState::button_rect(unsigned n) const -> geom::Rectangle -{ - geom::Rectangle titlebar = titlebar_rect(); - geom::X x = - titlebar.right() - - as_delta(side_border_width()) - - n * as_delta(static_geometry->button_width + static_geometry->padding_between_buttons) - - as_delta(static_geometry->button_width); - return geom::Rectangle{ - {x, titlebar.top()}, - {static_geometry->button_width, titlebar.size.height}}; -} - auto msd::WindowState::scale() const -> float { return scale_; @@ -298,22 +237,3 @@ msd::WindowSurfaceObserverManager::~WindowSurfaceObserverManager() { surface_->unregister_interest(*observer); } - -auto msd::compute_size_with_decorations(geometry::Size content_size, MirWindowType type, MirWindowState state) - -> geometry::Size -{ - switch (border_type_for(type, state)) - { - case msd::BorderType::Full: - content_size.width += msd::default_geometry.side_border_width * 2; - content_size.height += msd::default_geometry.titlebar_height + msd::default_geometry.bottom_border_height; - break; - case msd::BorderType::Titlebar: - content_size.height += msd::default_geometry.titlebar_height; - break; - case msd::BorderType::None: - break; - } - - return content_size; -} diff --git a/src/server/shell/decoration/window.h b/src/server/shell/decoration/window.h index d5276b5a818..2b3be1cf8d3 100644 --- a/src/server/shell/decoration/window.h +++ b/src/server/shell/decoration/window.h @@ -39,80 +39,6 @@ namespace decoration class BasicDecoration; template class ThreadsafeAccess; -enum class BorderType -{ - Full, ///< Full titlebar and border (for restored windows) - Titlebar, ///< Titlebar only (for maximized windows) - None, ///< No decorations (for fullscreen windows or popups) -}; - -/// Decoration geometry properties that don't change -struct StaticGeometry -{ - geometry::Height const titlebar_height; ///< Visible height of the top titlebar with the window's name and buttons - geometry::Width const side_border_width; ///< Visible width of the side borders - geometry::Height const bottom_border_height; ///< Visible height of the bottom border - geometry::Size const resize_corner_input_size; ///< The size of the input area of a resizable corner - ///< (does not effect surface input area, only where in the surface is - ///< considered a resize corner) - geometry::Width const button_width; ///< The width of window control buttons - geometry::Width const padding_between_buttons; ///< The gep between titlebar buttons - geometry::Height const title_font_height; ///< Height of the text used to render the window title - geometry::Point const title_font_top_left; ///< Where to render the window title - geometry::Displacement const icon_padding; ///< Padding inside buttons around icons - geometry::Width const icon_line_width; ///< Width for lines in button icons - - MirPixelFormat const buffer_format = mir_pixel_format_argb_8888; -}; - -extern StaticGeometry const default_geometry; - -/// Information about the geometry and type of decorations for a given window -/// Data is pulled from the surface on construction and immutable after that -class WindowState -{ -public: - WindowState( - std::shared_ptr const& static_geometry, - std::shared_ptr const& window_surface, - float scale); - - auto window_size() const -> geometry::Size; - auto border_type() const -> BorderType; - auto focused_state() const -> MirWindowFocusState; - auto window_name() const -> std::string; - - auto titlebar_width() const -> geometry::Width; - auto titlebar_height() const -> geometry::Height; - - auto side_border_width() const -> geometry::Width; - auto side_border_height() const -> geometry::Height; - - auto bottom_border_width() const -> geometry::Width; - auto bottom_border_height() const -> geometry::Height; - - auto titlebar_rect() const -> geometry::Rectangle; - auto left_border_rect() const -> geometry::Rectangle; - auto right_border_rect() const -> geometry::Rectangle; - auto bottom_border_rect() const -> geometry::Rectangle; - - /// Returns the rectangle of the nth button - auto button_rect(unsigned n) const -> geometry::Rectangle; - - auto scale() const -> float; - -private: - WindowState(WindowState const&) = delete; - WindowState& operator=(WindowState const&) = delete; - - std::shared_ptr const static_geometry; - geometry::Size const window_size_; - BorderType const border_type_; - MirWindowFocusState const focus_state_; - std::string window_name_; - float scale_; -}; - /// Observes the decorated window and calls on_update when its state changes class WindowSurfaceObserverManager { diff --git a/src/server/shell/default_configuration.cpp b/src/server/shell/default_configuration.cpp index 34285311286..6e7ccf6f729 100644 --- a/src/server/shell/default_configuration.cpp +++ b/src/server/shell/default_configuration.cpp @@ -70,10 +70,12 @@ auto mir::DefaultServerConfiguration::the_decoration_manager() -> std::shared_pt [this]()->std::shared_ptr { return std::make_shared( + msd::DecorationStrategy::default_decoration_strategy(), *the_display_configuration_observer_registrar(), [buffer_allocator = the_buffer_allocator(), executor = the_main_loop(), cursor_images = the_cursor_images()]( + std::shared_ptr const& decoration_strategy, std::shared_ptr const& shell, std::shared_ptr const& surface) -> std::unique_ptr { @@ -82,7 +84,8 @@ auto mir::DefaultServerConfiguration::the_decoration_manager() -> std::shared_pt buffer_allocator, executor, cursor_images, - surface); + surface, + decoration_strategy); }); }); } diff --git a/tests/unit-tests/shell/test_decoration_basic_decoration.cpp b/tests/unit-tests/shell/test_decoration_basic_decoration.cpp index 8416b6972d5..c30e97fc8c1 100644 --- a/tests/unit-tests/shell/test_decoration_basic_decoration.cpp +++ b/tests/unit-tests/shell/test_decoration_basic_decoration.cpp @@ -187,7 +187,8 @@ struct DecorationBasicDecoration mt::fake_shared(buffer_allocator), mt::fake_shared(executor), mt::fake_shared(cursor_images), - mt::fake_shared(window_surface)); + mt::fake_shared(window_surface), + msd::DecorationStrategy::default_decoration_strategy()); executor.execute(); } diff --git a/tests/unit-tests/shell/test_decoration_basic_manager.cpp b/tests/unit-tests/shell/test_decoration_basic_manager.cpp index 5dcf7e6ba98..a5564d16e0d 100644 --- a/tests/unit-tests/shell/test_decoration_basic_manager.cpp +++ b/tests/unit-tests/shell/test_decoration_basic_manager.cpp @@ -22,6 +22,7 @@ #include #include +#include namespace ms = mir::scene; namespace msh = mir::shell; @@ -59,8 +60,10 @@ struct DecorationBasicManager registrar{std::make_shared>()}; msd::BasicManager manager{ + msd::DecorationStrategy::default_decoration_strategy(), *registrar, [this]( + std::shared_ptr const&, std::shared_ptr const&, std::shared_ptr const&) -> std::unique_ptr {