MirAL-0.3

There’s a new MirAL release (0.3.0) available in ‘Zesty Zapus’ (Ubuntu 17.04). MirAL is a project aimed at simplifying the development of Mir servers and particularly providing a stable ABI and sensible default behaviors.

Unsurprisingly, given the project’s original goal, the ABI is unchanged. The changes in 0.3.0 fall into three categories:

  1. bugfixes;
  2. enabling keymap in newer Mir versions; and,
  3. additional features for shell developers to use.

Bugfixes

#1631958 Crash after closing Qt dialog

#1632325, #1633052 tooltips positioned wrong with mir-0.24

#1625849 [Xmir] Alt+` switch between different X11-apps not just windows.

Added miral-xrun as a better way to use Xmir

(unnumbered) miral-shell splash screen should be fullscreen.

(unnumbered) deduplicate logging of WindowSpecification::top_left

Enabling Keyboard Map in newer Mir versions

A new class miral::Keymap allows the keyboard map to be specified either programmatically or (by default) on the command line. Being in the UK I can get a familiar keyboard layout like this:

miral-shell --keymap gb

The class is also provided on Mir versions prior to 0.24.1, but does nothing.

Additional Features For Shell Developers To Use

#1629349 Shell wants way to associate initially requested window creation state with the window later created.

Shell code can now set a userdata property on the WindowSpecification in place_new_surface() and this is transferred to the WindowInfo.

Added miral/version.h to allow permit compile-time feature detection. If you want to detect different versions of MirAL at compile time you can, for example, write:

#include <miral/version.h>
#if MIRAL_VERSION >= MIR_VERSION_NUMBER(0, 3, 0)
#include <miral/keymap.h>
#endif

A convenient overload of WindowManagerTools::modify_window() that
doesn’t require the WindowInfo

adding wallpaper to egmde

Before we begin

My previous egmde post described how to build and install MirAL. If you’re using Ubuntu 16.10 (Yakkety) then that is no longer necessary as MirAL is in the archive. All you need is:

$ sudo apt install libmiral-dev

Otherwise, you can follow the instructions there.

The egmde source for this article is available at the same place:

$ git clone https://github.com/AlanGriffiths/egmde.git

The example code

In this article we add some simple, Ubuntu orange wallpaper. The previous egmde.cpp file is largely unchanged we just add a couple of headers and update the main program that looked like this:

int main(int argc, char const* argv[])
{
    miral::MirRunner runner{argc, argv};

    return runner.run_with(
        {
            set_window_managment_policy<ExampleWindowManagerPolicy>()
        });
}

Now it is:

int main(int argc, char const* argv[])
{
    miral::MirRunner runner{argc, argv};
    Wallpaper wallpaper;
    runner.add_stop_callback([&] { wallpaper.stop(); });
    return runner.run_with(
        {
            miral::StartupInternalClient{"wallpaper", std::ref(wallpaper)},
            set_window_managment_policy<ExampleWindowManagerPolicy>()
        });
}

The Wallpaper class is what we’ll be implementing here, StartupInternalClient starts it as an in-process Mir client and the verbose lambda incantations work-around the limitations of the current MirAL implementation.

The Wallpaper class uses a simple “Worker” class to pass work off to a separate thread. I’ll only show the header here as the methods as self-explanatory:

class Worker
{
public:
    ~Worker();
    void start_work();
    void enqueue_work(std::function<void()> const& functor);
    void stop_work();

};

The Wallpaper class

class Wallpaper : Worker
{
public:
    // These operators are the protocol for an "Internal Client"
    void operator()(miral::toolkit::Connection c) { start(c); }
    void operator()(std::weak_ptr<mir::scene::Session> const&){ }

    void start(miral::toolkit::Connection connection);
    void stop();
private:
    std::mutex mutable mutex;
    miral::toolkit::Connection connection;
    miral::toolkit::Surface surface;
    void create_surface();
};

The start and stop methods are fairly self-explanatory:

void Wallpaper::start(miral::toolkit::Connection connection)
{
    {
        std::lock_guard<decltype(mutex)> lock{mutex};
        this->connection = connection;
    }
    enqueue_work([this]{ create_surface(); });
    start_work();
}
void Wallpaper::stop()
{
    {
        std::lock_guard<decltype(mutex)> lock{mutex};
        surface.reset();
        connection.reset();
    }
    stop_work();
}

Most of the work happens in the create_surface() method that creates a surface of a type that will never get focus (and therefore will never be raised above anything else):

void Wallpaper::create_surface()
{
    std::lock_guard<decltype(mutex)> lock{mutex};
    auto const spec = SurfaceSpec::for_normal_surface(
        connection, 100, 100, mir_pixel_format_xrgb_8888)
        .set_buffer_usage(mir_buffer_usage_software)
        .set_type(mir_surface_type_gloss)
        .set_name("wallpaper");

    mir_surface_spec_set_fullscreen_on_output(spec, 0);

    surface = spec.create_surface();
    uint8_t pattern[4] = { 0x14, 0x48, 0xDD, 0xFF };

    MirGraphicsRegion graphics_region;
    MirBufferStream* buffer_stream = mir_surface_get_buffer_stream(surface);
    mir_buffer_stream_get_graphics_region(buffer_stream, &graphics_region);

    render_pattern(&graphics_region, pattern);
    mir_buffer_stream_swap_buffers_sync(buffer_stream);
}

This is unsophisticated, but the point is that the client API is available to do whatever rendering we like.

Now, when we run egmde we no longer get a boring black rectangle. Now we get an Ubuntu orange one.

How to run X11 applications on simple Mir servers

There are a few “gotchas” in running X11 applications (via Xmir) on Mir servers so I’m sharing a short script to make it easier.

The following script will work with the example servers from the “mir-demos” package, the miral-shell (from “miral-examples”) and my own egmde project. (With Unity8 there’s a little more to it but as there is existing “magic” in place for launching X11 applications I won’t bother to discuss it further.)

The principle issue is that each Xmir session is seen as a single application by the Mir server, so we need to create an Xmir server for each application for everything to make sense. And that means each application needs a separate port to connect to its Xmir server.

For this to work you need to have a Mir server running, and have Xmir installed.

Here’s the script:

$ cat ~/bin/Xmir-run
#!/bin/bash
port=0
while [ -e "/tmp/.X11-unix/X${port}" ]; do
    let port+=1
done

Xmir -rootless :${port} & pid=$!
DISPLAY=:${port} $*
kill ${pid}

The first part of this script finds an available port to run Xmir on.

The next part starts an Xmir server in “rootless” mode and remembers the pid.

Then we run the command passed to the script until it exits.

Finally, we kill the Xmir server.

Simple!

MirAL-0.2

There’s a new MirAL release (0.2.0) available in Yakkety. MirAL is a project aimed at simplifying the development of Mir servers and particularly providing a stable ABI and sensible default behaviors.

Unsurprisingly, given the project’s original goal, the ABI is unchanged. The changes in 0.2.0 fall into four categories:

  1. additional features for shell developers to use;
  2. enabling features available in newer Mir versions,
  3. improvements to the example server; and,
  4. bugfixes.

Additional Features for Shell Developers to use

There is a new “–window-management-trace” option for debugging window management. This provides detailed logging of the calls to and from the window management policy.

There is a new miral::CursorTheme class to load cursor themes.

Features available in newer Mir versions

Pointer confinement is now available (where underlying Mir version >= 0.24)
Enhanced “surface placement” handling and the results of “surface placement” requests are notified to clients (where supported by underlying Mir version >= 0.25).

Improvements to the Example Server

Improved resizing of size-constrained windows in miral-shell example.

Bugfixes

#1621917 tests fail when built against lp:mir

#1626659 Dialogs with parents should be modal i.e. they receive focus when the parent is clicked

#1628482 Deadlock in default window manager when Ctrl+Alt+Backspace with a client connected

#1590099 Need to support pointer confinement in Mir and toolkits using Mir

#1616818 User can drag menus (and other inappropriate) surfaces

#1628033 Starting qtcreator on miral-shell gives an orphan “titlebar”

#1628594 advise_focus_lost() isn’t called when the active window is minimised/hidden

#1628630 miral should not change surface geometry because it got maximized

#1628981 TitlebarWindowManager: sometime the initial display of titlebars doesn’t happen

#1625853 Mouse cursor disappears (or just never changes) when entering the windows of Qt apps