-
Notifications
You must be signed in to change notification settings - Fork 103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add APIs to override decoration managers and customize decorations (Take 2) #3666
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proposing the interface for review without "dragging implementation code along" is good.
However, I would rather have focused first on separating out the "policy" part of decorations as otherwise the miral::*Decoration*
interface is somewhat speculative. (In particular, there's no way to "hook up" input in the current draft.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, you said this was "ready to review", but I now see it change whilst reviewing. I'll leave it for now and just post what I've seen so far
examples/miral-shell/decoration/user_decoration_manager_example.cpp
Outdated
Show resolved
Hide resolved
examples/miral-shell/decoration/user_decoration_manager_example.h
Outdated
Show resolved
Hide resolved
@@ -14,6 +14,8 @@ | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
|
|||
#include "decoration/decoration.h" | |||
#include "miral/decoration.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#include "miral/decoration.h" |
#include <string> | ||
#include <vector> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#include <string> | |
#include <vector> |
bad7442
to
8786b40
Compare
manager in miral-shell
Co-authored-by: Alan Griffiths <[email protected]>
Also, fix symbols.
To allow for forward declares.
This should guarantee that the lifetime is extended since `self` is a shared ptr
The implementation is now called `UserDecoration` and is implemented as an external class in miral-shell. The interface does book keeping of the necessary data members.
Respects floating/maximized/minimized states.
Should make it easier and less messy to edit things, a lot more readable too
Should give a bit more context than just `miral::{WindowState,StaticGeometry}`
Removed the usage of the pimpl pattern as well since it's internal anyway.
`miral::decoration` And move some renderer specific variables into `Renderer` instead of the outer namespace.
`miral::DecorationBasicManager`.
Took the chance to revert some shared -> unique ptr changes done earlier, as well as fixing some lifetime issues.
8786b40
to
6d461ef
Compare
`DecorationBasicManager` instead.
6d461ef
to
2613f31
Compare
for (auto const& widget : widgets) | ||
{ | ||
if (widget->button) | ||
button_rects.push_back(widget); | ||
} | ||
|
||
return button_rects; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would've been nice to do this using the ranges API but it seems not ready yet...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"not ready yet"? There's not much code using ranges in our codebase, but we have used it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like I'm who's not ready. I spent about 20 minutes trying to filter and collect but couldn't figure it out.
Something like:
widgets
| std::ranges::views::filter([](auto& widget){ return widget->button; })
| std::ranges::to<std::vector<Widget const>>();
But it always complained about piping widgets
into std::ranges::views::filter
, I tried looking at cppreference, but couldn't figure it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think that "expose everything so that a consumer-side implementation works, then try to hide the stuff that shouldn't be exposed" is a long route to the destination.
First separating the stuff that should be exposed and then exposing it would be easier
struct InputContext | ||
{ | ||
InputContext( | ||
std::shared_ptr<msh::Shell> const& shell, | ||
std::shared_ptr<ms::Session> const& session, | ||
std::shared_ptr<ms::Surface> const& window_surface, | ||
std::shared_ptr<MirEvent const> const& latest_event, | ||
std::shared_ptr<mir::input::CursorImages> const& cursor_images, | ||
std::shared_ptr<ms::Surface> const& decoration_surface); | ||
|
||
void request_move() const; | ||
|
||
void request_resize(MirResizeEdge edge) const; | ||
|
||
void set_cursor(std::string const& cursor_image_name) const; | ||
|
||
void request_close() const; | ||
|
||
void request_toggle_maximize() const; | ||
|
||
void request_minimize() const; | ||
private: | ||
std::shared_ptr<msh::Shell> const shell; | ||
std::shared_ptr<ms::Session> const session; | ||
std::shared_ptr<ms::Surface> const window_surface; | ||
std::shared_ptr<const MirEvent> const latest_event; | ||
std::shared_ptr<mir::input::CursorImages> const cursor_images; | ||
std::shared_ptr<ms::Surface> const decoration_surface; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a good abstraction to have in the miral API: if we change the implementation then there's an ABI break.
The API should be more like:
struct InputContext | |
{ | |
InputContext( | |
std::shared_ptr<msh::Shell> const& shell, | |
std::shared_ptr<ms::Session> const& session, | |
std::shared_ptr<ms::Surface> const& window_surface, | |
std::shared_ptr<MirEvent const> const& latest_event, | |
std::shared_ptr<mir::input::CursorImages> const& cursor_images, | |
std::shared_ptr<ms::Surface> const& decoration_surface); | |
void request_move() const; | |
void request_resize(MirResizeEdge edge) const; | |
void set_cursor(std::string const& cursor_image_name) const; | |
void request_close() const; | |
void request_toggle_maximize() const; | |
void request_minimize() const; | |
private: | |
std::shared_ptr<msh::Shell> const shell; | |
std::shared_ptr<ms::Session> const session; | |
std::shared_ptr<ms::Surface> const window_surface; | |
std::shared_ptr<const MirEvent> const latest_event; | |
std::shared_ptr<mir::input::CursorImages> const cursor_images; | |
std::shared_ptr<ms::Surface> const decoration_surface; | |
}; | |
struct InputContext | |
{ | |
void request_move() const; | |
void request_resize(MirResizeEdge edge) const; | |
void set_cursor(std::string const& cursor_image_name) const; | |
void request_close() const; | |
void request_toggle_maximize() const; | |
void request_minimize() const; | |
private: | |
// TODO there needs to be a constructor usable by the API implementation | |
struct Self; | |
std::shared_ptr<Self> self; | |
}; |
using Pixel = uint32_t; | ||
|
||
// Can we use even more keywords? | ||
static inline auto const buffer_format = mir_pixel_format_argb_8888; | ||
static inline auto const bytes_per_pixel = 4; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like we are only supporting argb-8888 s/w rendering? Might the user want more choice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I know this is the most flexible set up at the moment. It wastes memory if you don't need transparency and performance if you want to draw using the GPU, but those are concessions I'm willing to make while I'm experimenting around
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If these restrictions were part of the example, I would be fine with it. If we're telling shell developers "this is the only way" then I'm not. Our objective is an API we are willing to support indefinitely, that's very different to "while I'm experimenting around".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How flexible do we want this to be? If we stick to software rendering, we can allow the user to determine the pixel format, and that would modify buffer allocation and pixel access (for render_row
, draw_rect
, and any other functions that we might want to add in the future).
If we want things to be even more generic (allowing GPU rendering), then I'm a bit lost.
I think sticking to software rendering at the moment would be a good idea since it still allows for GPU rendering with a bit off inefficiency (render and copy to CPU)
|
||
namespace miral | ||
{ | ||
namespace decoration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still not convinced a namespace is appropriate. (I would probably nest auxiliary types in CustomDecoration
.
include/miral/miral/decoration.h
Outdated
auto location() const -> mir::geometry::Point; | ||
auto pressed() const -> bool; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think shell authors may want more information:
- what modifier keys are pressed
- touch count, and individual touches
- which mouse buttons are pressed
std::function<void()> on_redraw; | ||
}; | ||
|
||
class Decoration // Placeholder names |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Placeholder names"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is more of a note to myself. When I was getting the code to work at the start, I wrote down the first name that came to mind without giving it much thought. So I think giving that some time once everything settles might be good
namespace msh = mir::shell; | ||
namespace msd = msh::decoration; | ||
namespace ms = mir::scene; | ||
namespace mc = mir::compositor; | ||
namespace mg = mir::graphics; | ||
namespace geometry = mir::geometry; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pulling far too much implementation detail into the API
void init( | ||
std::shared_ptr<ms::Surface> const& window_surface, | ||
std::shared_ptr<ms::Surface> const& decoration_surface, | ||
std::shared_ptr<mg::GraphicBufferAllocator> const& buffer_allocator, | ||
std::shared_ptr<msh::Shell> const& shell, | ||
std::shared_ptr<mir::input::CursorImages> const& cursor_images | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not used by the example code, and looks like an implementation detail being leaked.
|
||
void set_custom_geometry(std::shared_ptr<miral::decoration::StaticGeometry> geometry); | ||
|
||
auto to_decoration() -> std::unique_ptr<msd::Decoration>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not used by the example code, and looks like an implementation detail being leaked
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set_custom_decoration
is, to_decoration
isn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought that to_decoration()
was what I was commenting on
DecorationAdapter( | ||
std::shared_ptr<DecorationRedrawNotifier> const& redraw_notifier, | ||
|
||
// Called by user code in response to input events | ||
Renderer::RenderingCallback render_titlebar, | ||
Renderer::RenderingCallback render_left_border, | ||
Renderer::RenderingCallback render_right_border, | ||
Renderer::RenderingCallback render_bottom_border, | ||
|
||
// Called by Mir to inform user code of input events | ||
OnProcessEnter process_enter, | ||
OnProcessLeave process_leave, | ||
OnProcessDown process_down, | ||
OnProcessUp process_up, | ||
OnProcessMove process_move, | ||
OnProcessDrag process_drag, | ||
|
||
// Called by Mir to inform user code of changes to the window being decorated | ||
OnWindowAttribChanged attrib_changed, | ||
OnWindowResized window_resized_to, | ||
OnWindowRenamed window_renamed, | ||
|
||
// Called by Mir to inform user code that the display scale changed | ||
OnScaleChanged scale_changed, | ||
|
||
// Called whenever the internal WindowState is updated | ||
OnWindowStateUpdated update_decoration_window_state); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A long list of parameters like this is not maintainable: C.f. discussion about using builders to help
return std::nullopt; | ||
} | ||
|
||
void UserDecoration::InputManager::process_drag(miral::DeviceEvent& device, InputContext ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the context could be integrated into the input manager? Would require some sort of interface between the internal machinery and the custom decoration to initialize the input manager. Maybe passing the decoration manager to DecorationAdapter
during construction?
(low priority, the main reason behind this suggestion is that most meaningful actions require a context, but passing it isn't bad as well)
} | ||
decoration->redraw_notifier()->notify(); | ||
}, | ||
[decoration](auto...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This (window_resized_to) might not be very useful for the user as we already take care of everything related to resizing for them.
static auto create_manager(mir::Server&) | ||
-> std::shared_ptr<miral::DecorationManagerAdapter>; | ||
|
||
struct InputManager |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having the definition of the input manager here is unnecessary
{ | ||
public: | ||
DeviceEvent(mir::shell::decoration::DeviceEvent); | ||
operator mir::shell::decoration::DeviceEvent() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary, we get inputs as msd::DeviceEvents
and only need to convert them to miral::DeviceEvent
|
||
private: | ||
struct Impl; | ||
std::shared_ptr<Impl> impl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use unique_ptr
?
Edit: The reason I'm not already using unique_ptr
is because it has some issues compiling with incomplete classes. Googling for a bit shows that the spec allows it, but I haven't looked deep enough to make it work.
src/miral/decoration_adapter.cpp
Outdated
void md::InputContext::request_close() const{ | ||
window_surface->request_client_surface_close(); | ||
} | ||
|
||
void md::InputContext::request_toggle_maximize() const{ | ||
msh::SurfaceSpecification spec; | ||
if (window_surface->state() == mir_window_state_maximized) | ||
spec.state = mir_window_state_restored; | ||
else | ||
spec.state = mir_window_state_maximized; | ||
shell->modify_surface(session, window_surface, spec); | ||
} | ||
|
||
void md::InputContext::request_minimize() const{ | ||
msh::SurfaceSpecification spec; | ||
spec.state = mir_window_state_minimized; | ||
shell->modify_surface(session, window_surface, spec); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs formatting
md::OnWindowAttribChanged attrib_changed, | ||
md::OnWindowResized window_resized_to, | ||
md::OnWindowRenamed window_renamed) : | ||
on_attrib_changed{attrib_changed}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should use braces to match the style of the rest of the code
auto window_spec = [this] | ||
{ | ||
msh::SurfaceSpecification spec; | ||
|
||
window_surface->set_window_margins( | ||
as_delta(window_state->titlebar_height()), geom::DeltaX{}, geom::DeltaY{}, geom::DeltaX{}); | ||
|
||
if (window_state->window_size().width.as_value()) | ||
spec.width = window_state->window_size().width; | ||
if (window_state->window_size().height.as_value()) | ||
spec.height = window_state->window_size().height; | ||
|
||
// Could probably be `decoration->get_input_shape(window_state)`. | ||
// This will do for now. | ||
spec.input_shape = { | ||
window_state->titlebar_rect(), | ||
window_state->bottom_border_rect(), | ||
window_state->left_border_rect(), | ||
window_state->right_border_rect(), | ||
}; | ||
|
||
return spec; | ||
}(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be moved closer to the place of usage
void undecorate(std::shared_ptr<mir::scene::Surface> const& surface) override; | ||
void undecorate_all() override; | ||
|
||
virtual ~DecorationManagerAdapter(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't remember why I added this virtual
keyword. This class isn't meant to be inherited from.
// TODO: probably only frees one pointer, but not the whole set of things | ||
// pointing at the decoration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Old comment from when this was a shared_ptr
Attempt 1: Naive. Trying to expose an internal (an unstable) mir API to users.
Attempt 2: Bottom up. Trying to bring out the API from
msd::BasicDecoration
and expose it via adapters.Attempt 3 (we are here): Top down. Trying to extract the minimal API needed for users to implement at least something similar to
msd::BasicDecoration
(The code is nasty on purpose)