Skip to content
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

Wayland server side decorations #3425

Merged
merged 23 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e80daf5
Add xdg-decoration-unstable-v1 and modify CMakeLists.txt to generate …
tarek-y-ismail Jun 14, 2024
feca272
Add first prototype of xdg-decoration-unstable-v1.
tarek-y-ismail Jun 14, 2024
cc04d24
Apply formatting
tarek-y-ismail Jun 14, 2024
5b8027c
Add missing configure event call.
tarek-y-ismail Jun 14, 2024
68aad57
Apply formatting.
tarek-y-ismail Jun 14, 2024
59c8c08
Remove `mir::wayland` alias in header.
tarek-y-ismail Jun 14, 2024
826096a
Add missing copyright notice to xdg_decoration_unstable_v1.cpp
tarek-y-ismail Jun 14, 2024
7e75976
Strip logs from xdg_decoration_unstable_v1.cpp
tarek-y-ismail Jun 14, 2024
ba4db7c
Add basic error handling for xdg decoration
tarek-y-ismail Jun 24, 2024
9d89052
Apply formatting to xdg_decoration_unstable_v1.cpp
Jun 24, 2024
b8f0b2c
Convert the wayland error check to a sanity check in
Jun 24, 2024
0741868
Add listeners for decoration and toplevel destruction
tarek-y-ismail Jun 26, 2024
2a42cf4
Log instead of throwing `orphaned` when toplevels are destroyed.
Jun 28, 2024
f52391e
Add `destroy_toplevel_before_decoration` to expected failure list.
Jun 28, 2024
6632601
Wrap `toplevels_with_decorations` in a class.
Jul 1, 2024
f240d13
Fix comment typo in `mir::frontend::ToplevelsWithDecorations::unregis…
Jul 1, 2024
27d1207
Make comment in `acceptance-tests/wayland/CMakeLists.txt` clearer.
Jul 1, 2024
e0ca81e
Fix lifetime issues related to `toplevels_with_decorations`.
Jul 1, 2024
2012b20
Fix naming convention of `{register,unregister}Toplevel`.
Jul 1, 2024
6faabb4
Remove redundant `ToplevelsWithDecorations` construction code and dis…
tarek-y-ismail Jul 1, 2024
e69e676
Always unregister toplevel on destruction.
Jul 1, 2024
1af2a26
Improve semantics of `unregister_toplevel` and explain decoration
tarek-y-ismail Jul 2, 2024
faa1e16
Rename `destroy_toplevel_before_decoration` to `destroy_toplevel_befo…
tarek-y-ismail Jul 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/server/frontend_wayland/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ set(
text_input_v1.cpp text_input_v1.h
primary_selection_v1.cpp primary_selection_v1.h
session_lock_v1.cpp session_lock_v1.h
xdg_decoration_unstable_v1.cpp xdg_decoration_unstable_v1.h
${PROJECT_SOURCE_DIR}/src/include/server/mir/frontend/wayland.h
${CMAKE_CURRENT_BINARY_DIR}/wayland_frontend.tp.c
${CMAKE_CURRENT_BINARY_DIR}/wayland_frontend.tp.h
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
#include "wl_seat.h"
#include "wl_shell.h"
#include "wlr_screencopy_v1.h"
#include "xdg-decoration-unstable-v1_wrapper.h"
#include "xdg_decoration_unstable_v1.h"
#include "xdg_output_v1.h"
#include "xdg_shell_stable.h"
#include "xdg_shell_v6.h"
Expand Down Expand Up @@ -214,6 +216,10 @@ std::vector<ExtensionBuilder> const internal_extension_builders = {
{
return mf::create_mir_shell_v1(ctx.display);
}),
make_extension_builder<mw::XdgDecorationManagerV1>([](auto const& ctx)
{
return mf::create_xdg_decoration_unstable_v1(ctx.display);
})
};

ExtensionBuilder const xwayland_builder {
Expand Down
190 changes: 190 additions & 0 deletions src/server/frontend_wayland/xdg_decoration_unstable_v1.cpp
AlanGriffiths marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

#include "xdg_decoration_unstable_v1.h"

#include "mir/log.h"
#include "mir/shell/surface_specification.h"
#include "mir/wayland/client.h"
#include "mir/wayland/protocol_error.h"

#include "xdg-decoration-unstable-v1_wrapper.h"
#include "xdg_output_v1.h"
#include "xdg_shell_stable.h"

#include <memory>
#include <unordered_set>

namespace mir
{
namespace frontend
{
class ToplevelsWithDecorations
{
public:
ToplevelsWithDecorations() :
toplevels_with_decorations{std::make_shared<std::unordered_set<wl_resource*>>()}
{
}

/// \return true if no duplicates existed before insertion, false otherwise.
bool registerToplevel(wl_resource* toplevel)
AlanGriffiths marked this conversation as resolved.
Show resolved Hide resolved
{
auto [_, inserted] = toplevels_with_decorations->insert(toplevel);
return inserted;
}

/// \return true if only one element was erased, false otherwise.
bool unregisterToplevel(wl_resource* toplevel)
{
return toplevels_with_decorations->erase(toplevel) == 1;
}

private:
std::shared_ptr<std::unordered_set<wl_resource*>> toplevels_with_decorations;
};
AlanGriffiths marked this conversation as resolved.
Show resolved Hide resolved

class XdgDecorationManagerV1 : public wayland::XdgDecorationManagerV1
{
public:
XdgDecorationManagerV1(wl_resource* resource);

class Global : public wayland::XdgDecorationManagerV1::Global
{
public:
Global(wl_display* display);

private:
void bind(wl_resource* new_zxdg_decoration_manager_v1) override;
};

private:
void get_toplevel_decoration(wl_resource* id, wl_resource* toplevel) override;
ToplevelsWithDecorations toplevels_with_decorations;
AlanGriffiths marked this conversation as resolved.
Show resolved Hide resolved
};

class XdgToplevelDecorationV1 : public wayland::XdgToplevelDecorationV1
{
public:
XdgToplevelDecorationV1(wl_resource* id, mir::frontend::XdgToplevelStable* toplevel);

void set_mode(uint32_t mode) override;
void unset_mode() override;

private:
void update_mode(uint32_t new_mode);

static uint32_t const default_mode = Mode::client_side;

mir::frontend::XdgToplevelStable* toplevel;
uint32_t mode;
};
} // namespace frontend
} // namespace mir

auto mir::frontend::create_xdg_decoration_unstable_v1(wl_display* display)
-> std::shared_ptr<mir::wayland::XdgDecorationManagerV1::Global>
{
return std::make_shared<XdgDecorationManagerV1::Global>(display);
}

mir::frontend::XdgDecorationManagerV1::Global::Global(wl_display* display) :
wayland::XdgDecorationManagerV1::Global::Global{display, Version<1>{}}
{
}

void mir::frontend::XdgDecorationManagerV1::Global::bind(wl_resource* new_zxdg_decoration_manager_v1)
{
new XdgDecorationManagerV1{new_zxdg_decoration_manager_v1};
}

mir::frontend::XdgDecorationManagerV1::XdgDecorationManagerV1(wl_resource* resource) :
mir::wayland::XdgDecorationManagerV1{resource, Version<1>{}},
toplevels_with_decorations{}
{
}

void mir::frontend::XdgDecorationManagerV1::get_toplevel_decoration(wl_resource* id, wl_resource* toplevel)
{
using Error = mir::frontend::XdgToplevelDecorationV1::Error;

auto* tl = mir::frontend::XdgToplevelStable::from(toplevel);
if (!tl)
{
BOOST_THROW_EXCEPTION(std::runtime_error("Invalid toplevel pointer"));
}

auto decoration = new XdgToplevelDecorationV1{id, tl};
if (!toplevels_with_decorations.registerToplevel(toplevel))
{
BOOST_THROW_EXCEPTION(mir::wayland::ProtocolError(
resource, Error::already_constructed, "Decoration already constructed for this toplevel"));
}

decoration->add_destroy_listener(
[&toplevels_with_decorations = this->toplevels_with_decorations, toplevel]()
AlanGriffiths marked this conversation as resolved.
Show resolved Hide resolved
{
toplevels_with_decorations.unregisterToplevel(toplevel);
});

tl->add_destroy_listener(
[&toplevels_with_decorations = this->toplevels_with_decorations, client = this->client, toplevel]()
AlanGriffiths marked this conversation as resolved.
Show resolved Hide resolved
{
if (!client->is_being_destroyed() && !toplevels_with_decorations.unregisterToplevel(toplevel))
{
mir::log_warning("Toplevel destroyed before attached decoration!");
// https://github.com/canonical/mir/issues/3452
/* BOOST_THROW_EXCEPTION(mir::wayland::ProtocolError( */
/* resource, Error::orphaned, "Toplevel destroyed before its attached decoration")); */
}
});
}

mir::frontend::XdgToplevelDecorationV1::XdgToplevelDecorationV1(
wl_resource* id, mir::frontend::XdgToplevelStable* toplevel) :
wayland::XdgToplevelDecorationV1{id, Version<1>{}},
toplevel{toplevel}
{
}

void mir::frontend::XdgToplevelDecorationV1::update_mode(uint32_t new_mode)
{
auto spec = shell::SurfaceSpecification{};

mode = new_mode;
switch (mode)
{
case Mode::client_side:
spec.server_side_decorated = false;
break;
case Mode::server_side:
spec.server_side_decorated = true;
break;
}

this->toplevel->apply_spec(spec);
AlanGriffiths marked this conversation as resolved.
Show resolved Hide resolved
send_configure_event(mode);
}

void mir::frontend::XdgToplevelDecorationV1::set_mode(uint32_t mode)
{
update_mode(mode);
}

void mir::frontend::XdgToplevelDecorationV1::unset_mode()
{
update_mode(default_mode);
}
30 changes: 30 additions & 0 deletions src/server/frontend_wayland/xdg_decoration_unstable_v1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

#ifndef MIR_FRONTEND_XDG_DECORATION_UNSTABLE_V1_H
#define MIR_FRONTEND_XDG_DECORATION_UNSTABLE_V1_H

#include "xdg-decoration-unstable-v1_wrapper.h"

namespace mir
{
namespace frontend
{
auto create_xdg_decoration_unstable_v1(wl_display* display) -> std::shared_ptr<wayland::XdgDecorationManagerV1::Global>;
}
} // namespace mir

#endif // MIR_FRONTEND_RELATIVE_POINTER_UNSTABLE_V1_H
36 changes: 0 additions & 36 deletions src/server/frontend_wayland/xdg_shell_stable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,42 +73,6 @@ class XdgSurfaceStable : public mw::XdgSurface
XdgShellStable const& xdg_shell;
};

class XdgToplevelStable : mw::XdgToplevel, public WindowWlSurfaceRole
{
public:
XdgToplevelStable(
wl_resource* new_resource,
XdgSurfaceStable* xdg_surface,
WlSurface* surface);

void set_parent(std::optional<struct wl_resource*> const& parent) override;
void set_title(std::string const& title) override;
void set_app_id(std::string const& app_id) override;
void show_window_menu(struct wl_resource* seat, uint32_t serial, int32_t x, int32_t y) override;
void move(struct wl_resource* seat, uint32_t serial) override;
void resize(struct wl_resource* seat, uint32_t serial, uint32_t edges) override;
void set_max_size(int32_t width, int32_t height) override;
void set_min_size(int32_t width, int32_t height) override;
void set_maximized() override;
void unset_maximized() override;
void set_fullscreen(std::optional<struct wl_resource*> const& output) override;
void unset_fullscreen() override;
void set_minimized() override;

void handle_commit() override {};
void handle_state_change(MirWindowState /*new_state*/) override;
void handle_active_change(bool /*is_now_active*/) override;
void handle_resize(std::optional<geometry::Point> const& new_top_left, geometry::Size const& new_size) override;
void handle_close_request() override;

private:
static XdgToplevelStable* from(wl_resource* surface);
void send_toplevel_configure();
void destroy_role() const override;

mw::Weak<XdgSurfaceStable> const xdg_surface;
};

class XdgPositionerStable : public mw::XdgPositioner, public shell::SurfaceSpecification
{
public:
Expand Down
36 changes: 36 additions & 0 deletions src/server/frontend_wayland/xdg_shell_stable.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,42 @@ class XdgPopupStable : public wayland::XdgPopup, public WindowWlSurfaceRole
auto popup_rect() const -> std::optional<geometry::Rectangle>;
};

class XdgToplevelStable : mir::wayland::XdgToplevel, public WindowWlSurfaceRole
{
public:
XdgToplevelStable(
wl_resource* new_resource,
XdgSurfaceStable* xdg_surface,
WlSurface* surface);

void set_parent(std::optional<struct wl_resource*> const& parent) override;
void set_title(std::string const& title) override;
void set_app_id(std::string const& app_id) override;
void show_window_menu(struct wl_resource* seat, uint32_t serial, int32_t x, int32_t y) override;
void move(struct wl_resource* seat, uint32_t serial) override;
void resize(struct wl_resource* seat, uint32_t serial, uint32_t edges) override;
void set_max_size(int32_t width, int32_t height) override;
void set_min_size(int32_t width, int32_t height) override;
void set_maximized() override;
void unset_maximized() override;
void set_fullscreen(std::optional<struct wl_resource*> const& output) override;
void unset_fullscreen() override;
void set_minimized() override;

void handle_commit() override {};
void handle_state_change(MirWindowState /*new_state*/) override;
void handle_active_change(bool /*is_now_active*/) override;
void handle_resize(std::optional<geometry::Point> const& new_top_left, geometry::Size const& new_size) override;
void handle_close_request() override;

static XdgToplevelStable* from(wl_resource* surface);

private:
void send_toplevel_configure();
void destroy_role() const override;

mir::wayland::Weak<XdgSurfaceStable> const xdg_surface;
};
}
}

Expand Down
1 change: 1 addition & 0 deletions src/wayland/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ mir_generate_protocol_wrapper(mirwayland "z" wlr-screencopy-unstable-v1.xml)
mir_generate_protocol_wrapper(mirwayland "zwlr_" wlr-virtual-pointer-unstable-v1.xml)
mir_generate_protocol_wrapper(mirwayland "ext_" ext-session-lock-v1.xml)
mir_generate_protocol_wrapper(mirwayland "zmir_" mir-shell-unstable-v1.xml)
mir_generate_protocol_wrapper(mirwayland "z" xdg-decoration-unstable-v1.xml)

target_link_libraries(mirwayland
PUBLIC
Expand Down
4 changes: 4 additions & 0 deletions tests/acceptance-tests/wayland/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ set(EXPECTED_FAILURES

# Not yet implemented, see https://github.com/MirServer/mir/issues/2861
XdgPopupTest.when_parent_surface_is_moved_a_reactive_popup_is_moved

# Should report an `orphaned` protocol error/violation , but we log instead
# because of https://github.com/canonical/mir/issues/3452
XdgDecorationV1Test.destroy_toplevel_before_decoration
)

if (MIR_SIGBUS_HANDLER_ENVIRONMENT_BROKEN)
Expand Down
Loading
Loading