diff --git a/src/server/shell/decoration/basic_decoration.cpp b/src/server/shell/decoration/basic_decoration.cpp index bf4d74a7e3..fb48634862 100644 --- a/src/server/shell/decoration/basic_decoration.cpp +++ b/src/server/shell/decoration/basic_decoration.cpp @@ -56,6 +56,15 @@ struct PropertyComparison { } + template + PropertyComparison(TYPE (OBJ::*member)) + : comp{[=](OBJ const* a, OBJ const* b) + { + return (a->*member) == (b->*member); + }} + { + } + auto operator()(OBJ const* a, OBJ const* b) const -> bool { return comp(a, b); @@ -138,16 +147,15 @@ msd::BasicDecoration::BasicDecoration( std::shared_ptr decoration_strategy) : threadsafe_self{std::make_shared>(executor)}, 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)}, + buffer_streams{std::make_unique(session, decoration_strategy->buffer_format())}, renderer{std::make_unique(buffer_allocator, decoration_strategy->render_strategy())}, window_surface{window_surface}, decoration_surface{create_surface()}, - window_state{new_window_state()}, + window_state{decoration_strategy->new_window_state(window_surface, scale)}, window_surface_observer_manager{std::make_unique( window_surface, threadsafe_self)}, @@ -193,7 +201,7 @@ msd::BasicDecoration::~BasicDecoration() void msd::BasicDecoration::window_state_updated() { auto previous_window_state = std::move(window_state); - window_state = new_window_state(); + window_state = decoration_strategy->new_window_state(window_surface, scale); input_manager->update_window_state(*window_state); @@ -259,11 +267,6 @@ void msd::BasicDecoration::set_scale(float new_scale) window_state_updated(); } -auto msd::BasicDecoration::new_window_state() const -> std::unique_ptr -{ - return std::make_unique(static_geometry, window_surface, scale); -} - auto msd::BasicDecoration::create_surface() const -> std::shared_ptr { msh::SurfaceSpecification params; @@ -280,7 +283,7 @@ auto msd::BasicDecoration::create_surface() const -> std::shared_ptrcreate_buffer_stream(mg::BufferProperties{ geom::Size{1, 1}, - static_geometry->buffer_format, + decoration_strategy->buffer_format(), mg::BufferUsage::software}), {}, }}; @@ -318,7 +321,7 @@ void msd::BasicDecoration::update( if (input_updated({ &InputState::input_shape})) { - spec.input_shape = input_state->input_shape(); + spec.input_shape = input_state->input_shape; } if (window_updated({ diff --git a/src/server/shell/decoration/basic_decoration.h b/src/server/shell/decoration/basic_decoration.h index fb4bc0dbc6..097b3d1447 100644 --- a/src/server/shell/decoration/basic_decoration.h +++ b/src/server/shell/decoration/basic_decoration.h @@ -57,7 +57,6 @@ class StreamSpecification; namespace decoration { template class ThreadsafeAccess; -class StaticGeometry; class WindowState; class WindowSurfaceObserverManager; class InputManager; @@ -90,9 +89,6 @@ class BasicDecoration void set_scale(float scale) override; protected: - /// Creates an up-to-date WindowState object - auto new_window_state() const -> std::unique_ptr; - /// Returns paramaters to create the decoration surface auto create_surface() const -> std::shared_ptr; @@ -106,7 +102,6 @@ class BasicDecoration std::shared_ptr> const threadsafe_self; 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 43ab25f068..4657648f33 100644 --- a/src/server/shell/decoration/basic_manager.cpp +++ b/src/server/shell/decoration/basic_manager.cpp @@ -136,22 +136,7 @@ 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; + return decoration_strategy->compute_size_with_decorations(content_size, type, state); } void msd::BasicManager::set_scale(float new_scale) diff --git a/src/server/shell/decoration/decoration_strategy.cpp b/src/server/shell/decoration/decoration_strategy.cpp index b025233a19..9faca93461 100644 --- a/src/server/shell/decoration/decoration_strategy.cpp +++ b/src/server/shell/decoration/decoration_strategy.cpp @@ -37,13 +37,30 @@ namespace geom = mir::geometry; namespace msh = mir::shell; namespace msd = mir::shell::decoration; -using msd::StaticGeometry; using msd::WindowState; -using msd::Pixel; using msd::InputState; namespace { +/// Decoration geometry properties that don't change +struct StaticGeometry +{ + geom::Height const titlebar_height; ///< Visible height of the top titlebar with the window's name and buttons + geom::Width const side_border_width; ///< Visible width of the side borders + geom::Height const bottom_border_height; ///< Visible height of the bottom border + geom::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) + geom::Width const button_width; ///< The width of window control buttons + geom::Width const padding_between_buttons; ///< The gep between titlebar buttons + geom::Height const title_font_height; ///< Height of the text used to render the window title + geom::Point const title_font_top_left; ///< Where to render the window title + geom::Displacement const icon_padding; ///< Padding inside buttons around icons + geom::Width const icon_line_width; ///< Width for lines in button icons + + MirPixelFormat const buffer_format = mir_pixel_format_argb_8888; +}; + static constexpr auto color(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 0xFF) -> uint32_t { #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -213,12 +230,14 @@ struct RendererStrategy : public msd::RendererStrategy 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; - auto render_left_border() -> std::optional override; - auto render_right_border() -> std::optional override; - auto render_bottom_border() -> std::optional override; + auto render_titlebar(msd::BufferMaker const* buffer_maker) -> std::optional> override; + auto render_left_border(msd::BufferMaker const* buffer_maker) -> std::optional> override; + auto render_right_border(msd::BufferMaker const* buffer_maker) -> std::optional> override; + auto render_bottom_border(msd::BufferMaker const* buffer_maker) -> std::optional> override; private: + using Pixel = msd::BufferMaker::Pixel; + std::shared_ptr const static_geometry; /// A visual theme for a decoration @@ -269,7 +288,7 @@ struct RendererStrategy : public msd::RendererStrategy geom::Width line_width, Pixel color)> const render_icon; ///< Draws button's icon to the given buffer }; - std::map button_icons; + std::map button_icons; float scale{1.0f}; std::string name; @@ -289,7 +308,7 @@ struct RendererStrategy : public msd::RendererStrategy bool needs_titlebar_redraw{true}; bool needs_titlebar_buttons_redraw{true}; - std::vector buttons; + std::vector buttons; void update_solid_color_pixels(); @@ -298,15 +317,74 @@ struct RendererStrategy : public msd::RendererStrategy void redraw_titlebar_background(geom::Size scaled_titlebar_size); void redraw_titlebar_text(geom::Size scaled_titlebar_size); void redraw_titlebar_buttons(geom::Size scaled_titlebar_size); + + static auto alloc_pixels(geom::Size size) -> std::unique_ptr; }; class DecorationStrategy : public msd::DecorationStrategy { public: - auto static_geometry() const -> std::shared_ptr override; + ~DecorationStrategy() override = default; + + auto static_geometry() const -> std::shared_ptr; auto render_strategy() const -> std::unique_ptr override; - auto button_placement(unsigned n, const WindowState& ws) const -> mir::geometry::Rectangle override; + auto button_placement(unsigned n, const WindowState& ws) const -> geom::Rectangle override; + auto compute_size_with_decorations(geom::Size content_size, MirWindowType type, MirWindowState state) const + -> geom::Size override; + auto buffer_format() const -> MirPixelFormat override; + auto new_window_state(const std::shared_ptr& window_surface, + float scale) const -> std::unique_ptr override; + auto resize_corner_input_size() const -> geom::Size override; }; + +auto DecorationStrategy::resize_corner_input_size() const -> geom::Size +{ + return static_geometry()->resize_corner_input_size; +} + +auto DecorationStrategy::buffer_format() const -> MirPixelFormat +{ + return static_geometry()->buffer_format; +} + +auto DecorationStrategy::new_window_state(const std::shared_ptr& window_surface, + float scale) const -> std::unique_ptr + +{ + return std::make_unique( + window_surface, + static_geometry()->titlebar_height, + static_geometry()->side_border_width, + static_geometry()->bottom_border_height, + scale); +} + +auto DecorationStrategy::compute_size_with_decorations( + geom::Size content_size, MirWindowType type, MirWindowState state) const -> geom::Size +{ + switch (msd::border_type_for(type, state)) + { + case msd::BorderType::Full: + content_size.width += static_geometry()->side_border_width * 2; + content_size.height += static_geometry()->titlebar_height + static_geometry()->bottom_border_height; + break; + case msd::BorderType::Titlebar: + content_size.height += static_geometry()->titlebar_height; + break; + case msd::BorderType::None: + break; + } + + return content_size; +} + +auto RendererStrategy::alloc_pixels(geom::Size size) -> std::unique_ptr +{ + if (size_t const buf_size = area(size) * sizeof(Pixel)) + return std::unique_ptr{new Pixel[buf_size]}; + else + return {}; +} } auto msd::border_type_for(MirWindowType type, MirWindowState state) -> msd::BorderType @@ -354,9 +432,9 @@ auto msd::border_type_for(MirWindowType type, MirWindowState state) -> msd::Bord return {}; } -auto msd::DecorationStrategy::default_decoration_strategy() -> std::unique_ptr +auto msd::DecorationStrategy::default_decoration_strategy() -> std::shared_ptr { - return std::make_unique<::DecorationStrategy>(); + return std::make_shared<::DecorationStrategy>(); } class RendererStrategy::Text::Impl @@ -637,17 +715,17 @@ RendererStrategy::RendererStrategy(std::shared_ptr const& static current_theme{nullptr}, text{Text::instance()}, button_icons{ - {msd::ButtonFunction::Close, { + {msd::Button::Close, { default_close_normal_button, default_close_active_button, default_button_icon, render_close_icon}}, - {msd::ButtonFunction::Maximize, { + {msd::Button::Maximize, { default_normal_button, default_active_button, default_button_icon, render_maximize_icon}}, - {msd::ButtonFunction::Minimize, { + {msd::Button::Minimize, { default_normal_button, default_active_button, default_button_icon, @@ -704,11 +782,11 @@ void RendererStrategy::update_state(WindowState const& window_state, InputState needs_titlebar_redraw = true; } - if (input_state.buttons() != buttons) + if (input_state.buttons != buttons) { // If the number of buttons or their location changed, redraw the whole titlebar // Otherwise if the buttons are in the same place, just redraw them - if (input_state.buttons().size() != buttons.size()) + if (input_state.buttons.size() != buttons.size()) { needs_titlebar_redraw = true; } @@ -716,16 +794,16 @@ void RendererStrategy::update_state(WindowState const& window_state, InputState { for (unsigned i = 0; i < buttons.size(); i++) { - if (input_state.buttons()[i].rect != buttons[i].rect) + if (input_state.buttons[i].rect != buttons[i].rect) needs_titlebar_redraw = true; } } - buttons = input_state.buttons(); + buttons = input_state.buttons; needs_titlebar_buttons_redraw = true; } } -auto RendererStrategy::render_titlebar() -> std::optional +auto RendererStrategy::render_titlebar(msd::BufferMaker const* maker) -> std::optional> { auto const scaled_titlebar_size{titlebar_size * scale}; @@ -753,34 +831,34 @@ auto RendererStrategy::render_titlebar() -> std::optional needs_titlebar_redraw = false; needs_titlebar_buttons_redraw = false; - return RenderedPixels{static_geometry->buffer_format, scaled_titlebar_size, titlebar_pixels.get()}; + return maker->make_buffer(static_geometry->buffer_format, scaled_titlebar_size, titlebar_pixels.get()); } -auto RendererStrategy::render_left_border() -> std::optional +auto RendererStrategy::render_left_border(msd::BufferMaker const* maker) -> std::optional> { auto const scaled_left_border_size{left_border_size * scale}; if (!area(scaled_left_border_size)) return std::nullopt; update_solid_color_pixels(); - return RenderedPixels{static_geometry->buffer_format, scaled_left_border_size, solid_color_pixels.get()}; + return maker->make_buffer(static_geometry->buffer_format, scaled_left_border_size, solid_color_pixels.get()); } -auto RendererStrategy::render_right_border() -> std::optional +auto RendererStrategy::render_right_border(msd::BufferMaker const* maker) -> std::optional> { auto const scaled_right_border_size{right_border_size * scale}; if (!area(scaled_right_border_size)) return std::nullopt; update_solid_color_pixels(); - return RenderedPixels{static_geometry->buffer_format, scaled_right_border_size, solid_color_pixels.get()}; + return maker->make_buffer(static_geometry->buffer_format, scaled_right_border_size, solid_color_pixels.get()); } -auto RendererStrategy::render_bottom_border() -> std::optional +auto RendererStrategy::render_bottom_border(msd::BufferMaker const* maker) -> std::optional> { auto const scaled_bottom_border_size{bottom_border_size * scale}; if (!area(scaled_bottom_border_size)) return std::nullopt; update_solid_color_pixels(); - return RenderedPixels{static_geometry->buffer_format, scaled_bottom_border_size, solid_color_pixels.get()}; + return maker->make_buffer(static_geometry->buffer_format, scaled_bottom_border_size, solid_color_pixels.get()); } void RendererStrategy::update_solid_color_pixels() @@ -839,7 +917,7 @@ void RendererStrategy::redraw_titlebar_buttons(geom::Size const scaled_titlebar_ if (icon != button_icons.end()) { Pixel button_color = icon->second.normal_color; - if (button.state == msd::ButtonState::Hovered) + if (button.state == msd::Button::Hovered) button_color = icon->second.active_color; for (geom::Y y{scaled_button_rect.top()}; y < scaled_button_rect.bottom(); y += geom::DeltaY{1}) { @@ -906,7 +984,7 @@ auto DecorationStrategy::render_strategy() const -> std::unique_ptr(static_geometry()); } -auto DecorationStrategy::button_placement(unsigned n, const WindowState& ws) const -> mir::geometry::Rectangle +auto DecorationStrategy::button_placement(unsigned n, const WindowState& ws) const -> geom::Rectangle { auto const titlebar = ws.titlebar_rect(); auto const geometry = static_geometry(); diff --git a/src/server/shell/decoration/decoration_strategy.h b/src/server/shell/decoration/decoration_strategy.h index 98fd260651..903ec8ada0 100644 --- a/src/server/shell/decoration/decoration_strategy.h +++ b/src/server/shell/decoration/decoration_strategy.h @@ -28,44 +28,10 @@ #include namespace mir::scene { class Surface; } +namespace mir::graphics { class Buffer; } 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) @@ -75,53 +41,55 @@ enum class BorderType auto border_type_for(MirWindowType type, MirWindowState state) -> BorderType; -struct ButtonInfo +/// Minimize, maximize, and close buttons +struct Button { - ButtonFunction function; - ButtonState state; - geometry::Rectangle rect; - - auto operator==(ButtonInfo const& other) const -> bool + enum class Function { - return function == other.function && - state == other.state && - rect == other.rect; - } -}; + Close, + Maximize, + Minimize, + }; + using enum Function; -/// 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} + enum class State { - } + 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 + }; + using enum State; - auto buttons() const -> std::vector const& { return buttons_; } - auto input_shape() const -> std::vector const& { return input_shape_; } + Function function; + State state; + geometry::Rectangle rect; -private: - InputState(InputState const&) = delete; - InputState& operator=(InputState const&) = delete; + auto operator==(Button const& other) const -> bool = default; +}; - std::vector const buttons_; - std::vector const input_shape_; +/// Describes the state of the interface (what buttons are pushed, etc) +struct InputState +{ + std::vector