What the bespoke Wayland extensions API might look like (update 1)

Last month I posted What the bespoke Wayland extensions API might look like Which began:

We’ve discussed supporting shell specific Wayland extensions in the past (here and here).

My view following those discussions is that we should encourage “broadly useful” Wayland protocol extensions to be submitted as PRs to the Mir repository. As many such protocols will need to interact with Mir internal APIs this will ensure they can access these features.

However, there are also some plausible protocol extensions that will be specific to particular shells. For example, before Unity8 can transition to Wayland they will need something to replace the “Trusted Prompt Sessions” provided by the libmirclient API.

I’ve now started seriously thinking about how to implement a MirAL API to enable this. There are (at least) three aspects of this API to consider:

  1. Enabling a shell to register and implement a protocol extension;
  2. Supporting integration with other abstractions exposed by the MirAL API; and,
  3. Allowing the shell to control which protocols are available to specific clients.

That led to some useful discussion and exploration and I’ve now pushed an update to the PR (https://github.com/MirServer/mir/pull/721). The API parts are the “MirAL 2.5” parts of this:

Filtering protocol extensions

class WaylandExtensions
{
    ...

    /// \remark Since MirAL 2.5
    using Filter = std::function<bool(Application const& app, char const* protocol)>;

    /// Set an extension filter callback to control the extensions available to specific clients
    /// \remark Since MirAL 2.5
    void set_filter(Filter const& extension_filter);

This provides the shell with a way to filter the protocol extensions available to each client application. That is useful when only some clients should have access to the protocol (e.g. only parts of the desktop should access layer-shell).

Adding protocol extensions

    /// \remark Since MirAL 2.5
    using Executor = std::function<void(std::function<void()>&& work)>;

    /// A Builder creates and registers an extension protocol.
    /// The Builder is provided the wl_display so that the extension can be registered and
    /// an executor that allows server initiated code to be executed on the Wayland mainloop.
    /// It returns a shared pointer to the implementation. Mir will manage the lifetime.
    /// \remark Since MirAL 2.5
    using Builder  = std::function<std::shared_ptr<void>(wl_display* display, Executor const& run_on_wayland_mainloop)>;
    ...
};

/// Add a bespoke Wayland extension.
/// \remark Since MirAL 2.5
auto with_extension(
    WaylandExtensions const& wayland_extensions,
    std::string const& name, WaylandExtensions::Builder const& builder) -> WaylandExtensions;

This provides the shell access to the wl_display for registering the protocol and provides an executor so that the implementation can run code on the Wayland mainloop.

The code that uses these APIs looks like this:

    auto const server_exit_status = runner.run_with({
        ...
        with_extension(
            with_filter(
                miral::WaylandExtensions{"wl_shell:xdg_wm_base:zxdg_shell_v6:zwlr_layer_shell_v1:org_kde_kwin_server_decoration_manager"},
                &dummy_wayland_protocol_extension_filter),
            "org_kde_kwin_server_decoration_manager", &me::server_decoration_extension,
            "some_other_extension", &some_other_extension),

Implementing protocol extensions

In the PR I’ve done a couple more things (incompletely - this is still a sketch) to make implement the org_kde_kwin_server_decoration_manager protocol:

  1. I’ve used the Mir “Wayland generator” to create the server-side template for the protocol
  2. I’ve split the internally generated code into a new shared library
    As far as I can see, support for items 2 and 3 in my list above can be added in a later iteration.

There is reason for this:

  1. I’ve used the Mir “Wayland generator” as we want to make it easy to implement protocols outside the Mir tree and subsequently to submit ones that matter upstream to the Mir project.
  2. I’ve split the internally generated code into a new shared library as there are some entry points needed by the generated code. I’m not sure whether this is essential yet, not what the minimal necessary content of this library would be.
1 Like

The PR link is wrong, should be https://github.com/MirServer/mir/pull/721