Journal Articles

CVu Journal Vol 28, #2 - May 2016 + Programming Topics
Browse in : All > Journals > CVu > 282 (9)
All > Topics > Programming (877)
Any of these categories - All of these categories

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: Come Code With Me

Author: Martin Moene

Date: 07 May 2016 09:29:14 +01:00 or Sat, 07 May 2016 09:29:14 +01:00

Summary: Alan Griffiths outlines an Open Source project and invites contributions.

Body: 

One of the advantages of working on Open Source projects is that is possible to talk about them and the code involved with few restrictions. In this case I’m working on a project that would greatly benefit from wider input and I would especially welcome contributions from ACCU members.

This is a C++ 14 project, with a lot of potential features that are not important to my immediate goals, so I’m unlikely to get to them any time soon. There’s a range of work available varying from entry level programmer to domain expert, so if people want to use it as a context for trying C++ 14 features (or even C++17 features) that’s great.

I’ve not heard anything about ACCU ‘mentored’ projects recently, but if any ACCU members want to use this project as a basis for this, then I’d be very happy. And I’m willing to provide support for this.

The project is about developing a ‘Desktop Environment’ or ‘Shell’. There are a lot of these in the Linux world; a few examples (in no particular order): KDE, Gnome, Unity7, Cinnamon, LXDE, and Awesome. Canonical’s interest lies in Unity8 – which is the ‘convergent’ shell currently being used on phones and tablets and in preparation for desktop use.

Introducing the Mir Abstraction Layer

The principle Open Source project I’ve been working on for the last few years is Mir. Mir is a library for writing Linux display servers and shells that are independent of the underlying graphics stack. It fits into a similar role as an X server or Weston (a Wayland server) but was initially motivated by Canonical’s vision of ‘convergent’ computing.

The Mir project has had some success in meeting Canonical’s immediate needs – it is running in the Ubuntu Touch phones and tablets, and as an experimental option for running the Unity8 shell on the desktop. But because of the concentration of effort on delivering the features needed for this internal use, it hasn’t really addressed the needs of potential users outside of Canonical.

Mir provides two APIs for users: the ‘client’ API is for applications that run on Mir and that is largely used by toolkits. There is support for Mir in the GTK and Qt toolkits, and in SDL. This works pretty well and the Mir client API has remained backwards compatible for a couple of years and can do so for the foreseeable future.

The problem is that the server-side ABI compatibility is broken by almost every release of Mir. This isn’t a big problem for Canonical, as the API is fairly stable and both Mir and Unity8 are in rapid development: rebuilding Unity8 every time Mir is released is a small overhead. But for independent developers the lack of a stable ABI is problematic as they cannot easily synchronize their releases to updates of Mir.

My answer to this is to provide a stable ‘abstraction layer’ written over the top of the current Mir server API that will provide a stable ABI. There are a number of other goals that can be addressed at the same time:

At the time of writing the Mir Abstraction Layer (miral) is both a proof-of-concept and a work-in-progress but may be of interest as a modern C++ codebase to experiment with.

Building and using MirAL

These instructions assume that you’re using Ubuntu 16.04LTS; I’ve not tried earlier Ubuntu versions or other distributions.

You’ll need a few development and utility packages installed, along with the mir development packages (if you’re working on a phone or tablet use mir-graphics-drivers-android in place of mir-graphics-drivers-desktop):

  $ sudo apt-get install cmake g++ make bzr python-  imaging
  $ sudo apt-get install mir-graphics-drivers-  desktop libmirserver-dev libmirclient-dev

With these installed you can checkout and build miral:

$ bzr branch lp:miral $ mkdir miral/build $ cd miral/build $ cmake .. $ make

This creates libmiral.so in the lib directory and an example shell (miral-shell) in the bin directory. This can be run directly:

  $ bin/miral-shell

With the default options this runs in a window on your X11 desktop (which is convenient for development). To run independently of X you need to grant access to the graphics hardware and specify a VT to run in. For example:

  $ sudo bin/miral-shell --vt 4 --arw-file --file
  $XDG_RUNTIME_DIR/mir_socket

The miral-shell example is simple, don’t expect to see a sophisticated launcher by default. You can start mir apps from the command-line. For example:

  $ bin/miral-run gnome-terminal

That’s right, many standard GTK+ applications ‘just work’ on Mir (as GDK has a Mir backend). Not all ‘GTK’ applications work however: those that assume the existence of an X11 server will have problems.

miral-shell supports a lot of the familiar commands of a desktop environment. For example:

To exit from miral-shell press Ctrl-Alt-BkSp.

Starting applications in miral-shell

If you have a terminal session running in the MirAL desktop (as described above) you can start programs from it. GTK, Qt and SDL applications will ‘just work’ provided that they don’t bypass the toolkit and attempt to make X11 protocol calls that are not available.

  $ gedit
  $ 7kaa

From outside the MirAL session the ‘miral-run’ script sets a few environment variables to configure the Mir support in the various toolkits. (There’s some special treatment for gnome-terminal as starting that can conflict with the desktop default.)

  $ bin/miral-run gnome-calculator
  $ bin/miral-run 7kaa

There are also some examples of native Mir client applications in the mir-demos package. These are typically basic graphics demos:

  $ sudo apt-get install mir-demos
  $ mir_demo_client_egltriangle

What does using the MirAL API look like?

The main() program from miral-shell looks like Listing 1.

int main(int argc, char const* argv[])
{
  using namespace miral;
  return MirRunner{argc, argv}.run_with(
    {
      WindowManagerOptions
      {
        add_window_manager_policy<CanonicalWindowManagerPolicy>("canonical"),
        add_window_manager_policy<TilingWindowManagerPolicy>("tiling"),
      },
    display_configuration_options,
    QuitOnCtrlAltBkSp{},
    InternalClient{"Intro", spinner_splash, spinner_server_notification}
  });
}
			
Listing 1

The shell is providing CanonicalWindowManagerPolicy, TilingWindowManagerPolicy, spinner_splash and spinner_server_notification. The rest is from MirAL.

If you look for the corresponding code in lp:qtmir and lp:mir you’ll find it less clear, more verbose and scattered over multiple files.

A WindowManagerPolicy needs to implement an interface for handling a set of events (see Listing 2).

The way these events are handled define the behaviour of the shell. This interface is going to change as part of my ABI stabilization work as it mentions some Mir server API types directly (for example, mir::scene::SurfaceCreationParameters) and an interface like this is a risk to long term ABI stability as adding new functions would break client code.

class WindowManagementPolicy
{
public:
    virtual void handle_app_info_updated(mir::geometry::Rectangles const& displays) = 0;
    virtual void handle_displays_updated(mir::geometry::Rectangles const& displays) = 0;
    virtual auto handle_place_new_surface(
        ApplicationInfo const& app_info,
        mir::scene::SurfaceCreationParameters const& request_parameters)
    -> mir::scene::SurfaceCreationParameters = 0;
    virtual void handle_new_window(WindowInfo& window_info) = 0;
    virtual void handle_window_ready(WindowInfo& window_info) = 0;
    virtual void handle_modify_window(WindowInfo& window_info,
        mir::shell::SurfaceSpecification const& modifications) = 0;
    virtual void handle_delete_window(WindowInfo& window_info) = 0;
    virtual auto handle_set_state(WindowInfo& window_info, MirSurfaceState value)
    -> MirSurfaceState = 0;
    virtual void generate_decorations_for(WindowInfo& window_info) = 0;
    virtual bool handle_keyboard_event(MirKeyboardEvent const* event) = 0;
    virtual bool handle_touch_event(MirTouchEvent const* event) = 0;
    virtual bool handle_pointer_event(MirPointerEvent const* event) = 0;
    virtual void handle_raise_window(WindowInfo& window_info) = 0;
    virtual ~WindowManagementPolicy() = default;
    
    WindowManagementPolicy() = default;
    WindowManagementPolicy(WindowManagementPolicy const&) = delete;
    WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete;
};
			
Listing 2

The principle interface for controlling Mir is similar (see Listing 3).

class WindowManagerTools
{
public:
    virtual auto build_window(
        std::shared_ptr<mir::scene::Session> const& session,
        mir::scene::SurfaceCreationParameters const& parameters)
    -> WindowInfo& = 0;
    virtual auto count_applications() const -> unsigned int = 0;
    virtual void for_each_application(
        std::function<void(ApplicationInfo& info)> const& functor) = 0;
    virtual auto find_application(
        std::function<bool(ApplicationInfo const& info)> const& predicate)
    -> Application = 0;
    virtual auto info_for(std::weak_ptr<mir::scene::Session> const& session) const
    -> ApplicationInfo& = 0;
    virtual auto info_for(std::weak_ptr<mir::scene::Surface> const& surface) const
    -> WindowInfo& = 0;
    virtual auto info_for(Window const& window) const -> WindowInfo& = 0;
    virtual auto focused_application() const -> Application = 0;
    virtual auto focused_window() const -> Window = 0;
    virtual void focus_next_application() = 0;
    virtual void set_focus_to(Window const& window) = 0;
    virtual auto window_at(mir::geometry::Point cursor) const -> Window = 0;
    virtual auto active_display() -> mir::geometry::Rectangle const = 0;
    virtual void forget(Window const& window) = 0;
    virtual void raise_tree(Window const& root) = 0;
    virtual void size_to_output(mir::geometry::Rectangle& rect) = 0;
    virtual bool place_in_output(mir::graphics::DisplayConfigurationOutputId id,
                                 mir::geometry::Rectangle& rect) = 0;
    virtual ~WindowManagerTools() = default;
    WindowManagerTools() = default;
    WindowManagerTools(WindowManagerTools const&) = delete;
    WindowManagerTools& operator=(WindowManagerTools const&) = delete;
};
			
Listing 3

Again this still mentions a few Mir server API types and that needs fixing before miral is ready for release, but as this is implemented by the library (and not the client) there is less of an issue in using an interface for this purpose.

Exercises for the reader

As I said in the introduction, my focus is the MiraAL ABI and making it one that can maintain compatibility into the future. But there are also a lot of interesting possibilities for using that ABI and extending the miral-shell. I’ve tried to put some of these into a tasks_for_the_interested_reader.md file in the project (but I’m sure there are more).

It doesn’t take long using miral-shell to realize that it is lacking such things as a ‘launcher’ and a ‘status bar’ and that the ‘titlebars’ are little more than a placeholder for the functionality that one would expect. Much of that will have no effect on the Miral API and I’m unlikely to get around to it. Other features (like animated transitions) will need additional support from the API and I’m hoping to get that right with the input of people trying to use it ‘in anger’.

Canonical are committed to providing Mir on a range of platforms including phones, tablets and desktops. There is also work to provide it as a ‘snap’ in the ‘internet of things’. The miral-shell already works in all these environments (for a suitably forgiving value of ‘works’) and is an early opportunity to learn about this alternative to X11.

Acknowledgements

Thanks to Sam Spilsbury for his comments on an early draft and to Steve Love for dealing efficiently with a late submission.

Notes: 

More fields may be available via dynamicdata ..