Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -57,6 +57,7 @@ set(
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
xdg_dialog_v1.cpp xdg_dialog_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
6 changes: 6 additions & 0 deletions src/server/frontend_wayland/wayland_default_configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include "xdg_activation_v1.h"
#include "xdg-decoration-unstable-v1_wrapper.h"
#include "xdg_decoration_unstable_v1.h"
#include "xdg_dialog_v1.h"
#include "xdg_output_v1.h"
#include "xdg_shell_stable.h"
#include "xdg_shell_v6.h"
Expand Down Expand Up @@ -261,6 +262,10 @@ std::vector<ExtensionBuilder> const internal_extension_builders = {
{
return mf::create_xdg_decoration_unstable_v1(ctx.display, ctx.decoration_strategy);
}),
make_extension_builder<mw::XdgWmDialogV1>([](auto const& ctx)
{
return mf::create_xdg_dialog_v1(ctx.display);
}),
make_extension_builder<mw::FractionalScaleManagerV1>([](auto const& ctx)
{
return mf::create_fractional_scale_v1(ctx.display);
Expand Down Expand Up @@ -399,6 +404,7 @@ auto mf::get_standard_extensions() -> std::vector<std::string>
mw::TextInputManagerV3::interface_name,
mw::MirShellV1::interface_name,
mw::XdgDecorationManagerV1::interface_name,
mw::XdgWmDialogV1::interface_name,
mw::XdgActivationV1::interface_name,
mw::FractionalScaleManagerV1::interface_name};
}
Expand Down
154 changes: 154 additions & 0 deletions src/server/frontend_wayland/xdg_dialog_v1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* 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_dialog_v1.h"

#include <mir/wayland/protocol_error.h>

#include "xdg-dialog-v1_wrapper.h"
#include "xdg_shell_stable.h"

#include <memory>
#include <unordered_set>

namespace mir
{
namespace frontend
{
class ToplevelsWithDialogs
{
public:
ToplevelsWithDialogs() = default;
ToplevelsWithDialogs(ToplevelsWithDialogs const&) = delete;
ToplevelsWithDialogs& operator=(ToplevelsWithDialogs const&) = delete;

/// \return true if no duplicates existed before insertion, false otherwise.
bool register_toplevel(wl_resource* toplevel)
{
auto [_, inserted] = toplevels_with_dialogs.insert(toplevel);
return inserted;
}

/// \return true if the toplevel was still registered, false otherwise.
bool unregister_toplevel(wl_resource* toplevel)
{
return toplevels_with_dialogs.erase(toplevel) > 0;
}

private:
std::unordered_set<wl_resource*> toplevels_with_dialogs;
};

class XdgDialogV1 : public wayland::XdgDialogV1
{
public:
explicit XdgDialogV1(wl_resource* id);

void set_modal() override;
void unset_modal() override;
};

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

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

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

private:
void get_xdg_dialog(wl_resource* id, wl_resource* toplevel) override;

std::shared_ptr<ToplevelsWithDialogs> const toplevels_with_dialogs;
};
} // namespace frontend
} // namespace mir

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

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

void mir::frontend::XdgWmDialogV1::Global::bind(wl_resource* new_xdg_wm_dialog_v1)
{
new XdgWmDialogV1{new_xdg_wm_dialog_v1};
}

mir::frontend::XdgWmDialogV1::XdgWmDialogV1(wl_resource* resource) :
wayland::XdgWmDialogV1{resource, Version<1>{}},
toplevels_with_dialogs{std::make_shared<ToplevelsWithDialogs>()}
{
}

void mir::frontend::XdgWmDialogV1::get_xdg_dialog(wl_resource* id, wl_resource* toplevel)
{
auto* tl = XdgToplevelStable::from(toplevel);
if (!tl)
{
BOOST_THROW_EXCEPTION(std::runtime_error(
"Failed to obtain XdgToplevelStable from xdg_toplevel resource"));
}

if (!toplevels_with_dialogs->register_toplevel(toplevel))
{
BOOST_THROW_EXCEPTION(mir::wayland::ProtocolError(
resource, Error::already_used, "xdg_dialog_v1 already created for this toplevel"));
}

auto* dialog = new XdgDialogV1{id};
dialog->add_destroy_listener(
[toplevels_with_dialogs = this->toplevels_with_dialogs, toplevel]()
{
toplevels_with_dialogs->unregister_toplevel(toplevel);
});

tl->add_destroy_listener(
[toplevels_with_dialogs = this->toplevels_with_dialogs, toplevel]()
{
toplevels_with_dialogs->unregister_toplevel(toplevel);
});

tl->set_type(mir_window_type_dialog);
}

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

void mir::frontend::XdgDialogV1::set_modal()
{
// In Mir, mir_window_type_dialog already represents a modal dialog.
// No additional state change is needed.
}

void mir::frontend::XdgDialogV1::unset_modal()
{
// Mir does not distinguish between modal and non-modal dialogs at the
// window type level; the toplevel remains a dialog regardless.
}
32 changes: 32 additions & 0 deletions src/server/frontend_wayland/xdg_dialog_v1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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_DIALOG_V1_H
#define MIR_FRONTEND_XDG_DIALOG_V1_H

#include "xdg-dialog-v1_wrapper.h"

struct wl_display;

namespace mir
{
namespace frontend
{
auto create_xdg_dialog_v1(struct wl_display* display) -> std::shared_ptr<wayland::XdgWmDialogV1::Global>;
}
}

#endif // MIR_FRONTEND_XDG_DIALOG_V1_H
1 change: 1 addition & 0 deletions src/wayland/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ mir_generate_protocol_wrapper(mirwayland "z" xdg-decoration-unstable-v1.xml)
mir_generate_protocol_wrapper(mirwayland "wp_" viewporter.xml)
mir_generate_protocol_wrapper(mirwayland "wp_" fractional-scale-v1.xml)
mir_generate_protocol_wrapper(mirwayland "z" xdg-activation-v1.xml)
mir_generate_protocol_wrapper(mirwayland "" xdg-dialog-v1.xml)
mir_generate_protocol_wrapper(mirwayland "wp_" linux-drm-syncobj-v1.xml)
mir_generate_protocol_wrapper(mirwayland "ext_" ext-data-control-v1.xml)
# We don't strip the "ext_" prefix here, because it would conflict
Expand Down
104 changes: 104 additions & 0 deletions wayland-protocols/xdg-dialog-v1.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="xdg_dialog_v1">

<copyright>
Copyright © 2023 Carlos Garnacho

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>

<interface name="xdg_wm_dialog_v1" version="1">
<description summary="create dialogs related to other toplevels">
The xdg_wm_dialog_v1 interface is exposed as a global object allowing
to create xdg_dialog_v1 objects. These objects can be used to
coordinate the creation of dialogs relative to another toplevel.

Warning! The protocol described in this file is currently in the testing
phase. Backward compatible changes may be added together with the
corresponding interface version bump. Backward incompatible changes can
only be done by creating a new major version of the extension.
</description>

<enum name="error">
<description summary="error values for this object"/>
<entry name="already_used" value="0"
summary="the xdg_toplevel object has already been used to create a xdg_dialog_v1"/>
</enum>

<request name="destroy" type="destructor">
<description summary="destroy the dialog manager object">
Destroys the xdg_wm_dialog_v1 object. This does not affect
the xdg_dialog_v1 objects created from it.
</description>
</request>

<request name="get_xdg_dialog">
<description summary="create a dialog object">
Create a new dialog object associated with the given toplevel, to
allow managing dialogs.

Only one xdg_dialog_v1 may exist for each xdg_toplevel. If an
xdg_dialog_v1 already exists for the given xdg_toplevel, the
already_used error is raised.
</description>
<arg name="id" type="new_id" interface="xdg_dialog_v1"/>
<arg name="toplevel" type="object" interface="xdg_toplevel"/>
</request>
</interface>

<interface name="xdg_dialog_v1" version="1">
<description summary="dialog object">
A dialog object is associated to a xdg_toplevel. It allows the
compositor to treat the toplevel as a dialog and apply dialog-specific
policies.

Compositors may treat dialogs differently from regular windows in many
ways, e.g. refusing to iconify or maximize them. The user can usually
dismiss a dialog by pressing a dedicated key (e.g. Escape).
</description>

<request name="destroy" type="destructor">
<description summary="destroy the dialog object">
Destroys the xdg_dialog_v1 object. This does not affect
the dialog state on the associated xdg_toplevel.
</description>
</request>

<request name="set_modal">
<description summary="mark dialog as modal">
Hint that the dialog has modal behavior. See the description of
xdg_dialog_v1 for more details about what is expected from
modal dialogs.

This request does not take effect for an unmapped toplevel.
</description>
</request>

<request name="unset_modal">
<description summary="mark dialog as not modal">
Hint that the dialog does not have modal behavior. See the description
of xdg_dialog_v1 for more details.

This request does not take effect for an unmapped toplevel.
</description>
</request>
</interface>

</protocol>
Loading