Skip to content

Commit

Permalink
decoration-strategy-rework (#3712)
Browse files Browse the repository at this point in the history
Improves the interface between the decoration strategies and the rest of
the subsystem:

1. Encapsulates the StaticGeometry
2. Moves the Button enums into the struct (with a few renames)
3. Adds some documentation
4. Simplifies buffer creation API
5. Make InputState a struct
6. Simplify use of WindowState

Generally, making the decoration strategy simpler and easier to
customise
  • Loading branch information
tarek-y-ismail authored Jan 30, 2025
2 parents 3a8c5a9 + b58389b commit 5e15fde
Show file tree
Hide file tree
Showing 10 changed files with 287 additions and 251 deletions.
25 changes: 14 additions & 11 deletions src/server/shell/decoration/basic_decoration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ struct PropertyComparison
{
}

template<typename TYPE>
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);
Expand Down Expand Up @@ -138,16 +147,15 @@ msd::BasicDecoration::BasicDecoration(
std::shared_ptr<DecorationStrategy> decoration_strategy)
: threadsafe_self{std::make_shared<ThreadsafeAccess<BasicDecoration>>(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<BufferStreams>(session, static_geometry->buffer_format)},
buffer_streams{std::make_unique<BufferStreams>(session, decoration_strategy->buffer_format())},
renderer{std::make_unique<Renderer>(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<WindowSurfaceObserverManager>(
window_surface,
threadsafe_self)},
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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<WindowState>
{
return std::make_unique<WindowState>(static_geometry, window_surface, scale);
}

auto msd::BasicDecoration::create_surface() const -> std::shared_ptr<scene::Surface>
{
msh::SurfaceSpecification params;
Expand All @@ -280,7 +283,7 @@ auto msd::BasicDecoration::create_surface() const -> std::shared_ptr<scene::Surf
params.streams = {{
session->create_buffer_stream(mg::BufferProperties{
geom::Size{1, 1},
static_geometry->buffer_format,
decoration_strategy->buffer_format(),
mg::BufferUsage::software}),
{},
}};
Expand Down Expand Up @@ -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({
Expand Down
5 changes: 0 additions & 5 deletions src/server/shell/decoration/basic_decoration.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ class StreamSpecification;
namespace decoration
{
template<typename T> class ThreadsafeAccess;
class StaticGeometry;
class WindowState;
class WindowSurfaceObserverManager;
class InputManager;
Expand Down Expand Up @@ -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<WindowState>;

/// Returns paramaters to create the decoration surface
auto create_surface() const -> std::shared_ptr<scene::Surface>;

Expand All @@ -106,7 +102,6 @@ class BasicDecoration

std::shared_ptr<ThreadsafeAccess<BasicDecoration>> const threadsafe_self;
std::shared_ptr<DecorationStrategy> const decoration_strategy;
std::shared_ptr<StaticGeometry> const static_geometry;

std::shared_ptr<shell::Shell> const shell;
std::shared_ptr<graphics::GraphicBufferAllocator> const buffer_allocator;
Expand Down
17 changes: 1 addition & 16 deletions src/server/shell/decoration/basic_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
136 changes: 107 additions & 29 deletions src/server/shell/decoration/decoration_strategy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -213,12 +230,14 @@ struct RendererStrategy : public msd::RendererStrategy
RendererStrategy(std::shared_ptr<StaticGeometry> const& static_geometry);

void update_state(WindowState const& window_state, InputState const& input_state) override;
auto render_titlebar() -> std::optional<RenderedPixels> override;
auto render_left_border() -> std::optional<RenderedPixels> override;
auto render_right_border() -> std::optional<RenderedPixels> override;
auto render_bottom_border() -> std::optional<RenderedPixels> override;
auto render_titlebar(msd::BufferMaker const* buffer_maker) -> std::optional<std::shared_ptr<mir::graphics::Buffer>> override;
auto render_left_border(msd::BufferMaker const* buffer_maker) -> std::optional<std::shared_ptr<mir::graphics::Buffer>> override;
auto render_right_border(msd::BufferMaker const* buffer_maker) -> std::optional<std::shared_ptr<mir::graphics::Buffer>> override;
auto render_bottom_border(msd::BufferMaker const* buffer_maker) -> std::optional<std::shared_ptr<mir::graphics::Buffer>> override;

private:
using Pixel = msd::BufferMaker::Pixel;

std::shared_ptr<StaticGeometry> const static_geometry;

/// A visual theme for a decoration
Expand Down Expand Up @@ -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<msd::ButtonFunction, Icon const> button_icons;
std::map<msd::Button::Function, Icon const> button_icons;

float scale{1.0f};
std::string name;
Expand All @@ -289,7 +308,7 @@ struct RendererStrategy : public msd::RendererStrategy
bool needs_titlebar_redraw{true};
bool needs_titlebar_buttons_redraw{true};

std::vector<msd::ButtonInfo> buttons;
std::vector<msd::Button> buttons;

void update_solid_color_pixels();

Expand All @@ -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<Pixel[]>;
};

class DecorationStrategy : public msd::DecorationStrategy
{
public:
auto static_geometry() const -> std::shared_ptr<StaticGeometry> override;
~DecorationStrategy() override = default;

auto static_geometry() const -> std::shared_ptr<StaticGeometry>;
auto render_strategy() const -> std::unique_ptr<mir::shell::decoration::RendererStrategy> 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<mir::scene::Surface>& window_surface,
float scale) const -> std::unique_ptr<WindowState> 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<mir::scene::Surface>& window_surface,
float scale) const -> std::unique_ptr<WindowState>

{
return std::make_unique<WindowState>(
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<Pixel[]>
{
if (size_t const buf_size = area(size) * sizeof(Pixel))
return std::unique_ptr<Pixel[]>{new Pixel[buf_size]};
else
return {};
}
}

auto msd::border_type_for(MirWindowType type, MirWindowState state) -> msd::BorderType
Expand Down Expand Up @@ -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<DecorationStrategy>
auto msd::DecorationStrategy::default_decoration_strategy() -> std::shared_ptr<DecorationStrategy>
{
return std::make_unique<::DecorationStrategy>();
return std::make_shared<::DecorationStrategy>();
}

class RendererStrategy::Text::Impl
Expand Down Expand Up @@ -637,17 +715,17 @@ RendererStrategy::RendererStrategy(std::shared_ptr<StaticGeometry> 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,
Expand Down Expand Up @@ -704,28 +782,28 @@ 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;
}
else
{
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<RenderedPixels>
auto RendererStrategy::render_titlebar(msd::BufferMaker const* maker) -> std::optional<std::shared_ptr<mir::graphics::Buffer>>
{
auto const scaled_titlebar_size{titlebar_size * scale};

Expand Down Expand Up @@ -753,34 +831,34 @@ auto RendererStrategy::render_titlebar() -> std::optional<RenderedPixels>
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<RenderedPixels>
auto RendererStrategy::render_left_border(msd::BufferMaker const* maker) -> std::optional<std::shared_ptr<mir::graphics::Buffer>>
{
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<RenderedPixels>
auto RendererStrategy::render_right_border(msd::BufferMaker const* maker) -> std::optional<std::shared_ptr<mir::graphics::Buffer>>
{
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<RenderedPixels>
auto RendererStrategy::render_bottom_border(msd::BufferMaker const* maker) -> std::optional<std::shared_ptr<mir::graphics::Buffer>>
{
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()
Expand Down Expand Up @@ -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})
{
Expand Down Expand Up @@ -906,7 +984,7 @@ auto DecorationStrategy::render_strategy() const -> std::unique_ptr<mir::shell::
return std::make_unique<::RendererStrategy>(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();
Expand Down
Loading

0 comments on commit 5e15fde

Please sign in to comment.