Browse in : |
All
> Topics
> Design
All > Journals > CVu > 302 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: Writing a Wayland Server Using Mir
Author: Bob Schmidt
Date: 06 May 2018 17:39:33 +01:00 or Sun, 06 May 2018 17:39:33 +01:00
Summary: Alan Griffiths explains the basics of a new X11 replacement API.
Body:
We need a new windowing system
The X-Windows system has been, and still is, immensely successful in providing a way to interact with computers. It underlies many desktop environments and graphical user interface toolkits and lets them work together. But it comes from an era when computers were very different from now, and there are real concerns that are hard to meet.
In 1980, computers were big things managed by experts and, maybe, connected to another computer in the same organization. Today a phone is a computer managed by a non-expert, and connected to the Internet. Then, having floating point co-processor was a feature; now, having a graphics co-processor is required.
In 1980, the cost of developing software was such that any benefit to be gained by ‘listening in’ on what was happening on the same computer was negligible. It wasn’t as though there were billions of computers being used for online banking that would willingly install your program.
Adapting X11 to the new requirements of security and graphics performance isn’t feasible. The open source world has settled on a replacement: Wayland. Wayland is a set of protocols and extension protocols that allow applications (clients) to talk to compositors (servers) without many of the problems inherent in X11.
Most open source desktop environments have been based on the X.Org server implementation and a range of different window managers and compositors. All this needs adapting to work with Wayland.
For example, the Gnome project has been adapting it’s Mutter window manager to support Wayland; KDE has been adapting Kwin; and so on.
I work on another project, Mir, which in addition to being a compositor aims to provide the building blocks of generic window management and independence from the underlying graphics stack.
In this article I’m going to show you how easy it is to write a Wayland server using Mir.
Building the code
The code in this article needs Mir 0.31 or later. This exists in Ubuntu 18.04 and Fedora 28 both unreleased at the time of writing (but due for release before you read this), or from the mir-team/release PPA (See Sidebar).
It is also useful to install the weston
package as the example makes use of weston-terminal
as a Wayland based terminal application and the Qt toolkit’s Wayland support : qtwayland5
.
On Ubuntu use:
$ sudo apt install libmiral-dev mir-graphics- drivers-desktop weston qtwayland5 g++ cmake
Or, if using Fedora, use:
$ sudo dnf install mir-devel weston qtwayland5 g++ cmake
The ‘Mir Abstraction Layer’ (MirAL) presents the server-side functionality in a way that makes it easy to use MirAL. There was an introduction to MirAL in C Vu 28.2.
To illustrate MirAL I’m going to show what is involved in writing a (very simple) window manager. It runs on desktops, tablets and phones and supports keyboard, mouse and touch input. It will support applications using the GTK and Qt toolkits, SDL2 applications and (using Xwayland X11 applications.
The full code for this example is available on github:
$ git clone https://github.com/AlanGriffiths/ egmde.git $ git checkout article-1
Naturally, the code is likely to evolve and inspire additional articles, so you will find other branches, but this branch goes with this article. Assuming that you’ve MirAL installed as described above you can now build egmde as follows:
$ mkdir egmde/build $ cd egmde/build $ cmake .. $ make
After this you can start a basic egmde based desktop. By default this will use VT4, so first switch to VT4 (Ctrl-Alt-F4) to sign in and switch back again. Then type:
$ ./egmde-desktop
You should see a blank screen with a weston-terminal session. From this you can run commands and, in particular, start graphical applications. Perhaps qtcreator to examine the code?
There’s very little code needed to get this basic shell running:
$ wc -l *.h *.cpp *.sh 88 egwindowmanager.h 34 egmde.cpp 420 egwindowmanager.cpp 47 egmde-desktop.sh 589 total
The example code
A lot of the functionality (default placement of windows, menus etc.) comes with the MirAL library. For this exercise we’ll implement one class and write a main function that injects it into MirAL. The main program looks like this:
using namespace miral; int main(int argc, char const* argv[]) { MirRunner runner{argc, argv}; return runner.run_with( { set_window_management_policy<egmde ::WindowManagerPolicy>() }); }
Most of the logic belonging to egmde is in the egmde::WindowManagerPolicy
class. This implements a miral::WindowManagerPolicy
interface that allows the window management to be customized. It looks like Listing 1.
class WindowManagerPolicy : public CanonicalWindowManagerPolicy { public: using CanonicalWindowManagerPolicy ::CanonicalWindowManagerPolicy; bool handle_keyboard_event( MirKeyboardEvent const* event) override; bool handle_pointer_event( MirPointerEvent const* event) override; bool handle_touch_event( MirTouchEvent const* event) override; Rectangle confirm_placement_on_display( WindowInfo const& window_info, MirWindowState new_state, Rectangle const& new_placement) override; void handle_request_drag_and_drop( WindowInfo& window_info) override; void handle_request_move( WindowInfo& window_info, MirInputEvent const* input_event) override; void handle_request_resize( WindowInfo& window_info, MirInputEvent const* input_event, MirResizeEdge edge) override; private: ... }; |
Listing 1 |
These are the functions that it is necessary to implement for a minimal shell. I won’t reproduce them all here, but Listing 2 should give a flavor.
bool egmde::WindowManagerPolicy ::handle_keyboard_event( MirKeyboardEvent const* event) { auto const action = mir_keyboard_event_action(event); auto const shift_state = mir_keyboard_event_modifiers(event) & shift_states; if (action == mir_keyboard_action_down && shift_state == mir_input_event_modifier_alt) { switch (mir_keyboard_event_scan_code(event)) { case KEY_F4: tools.ask_client_to_close( tools.active_window()); return true; case KEY_TAB: tools.focus_next_application(); return true; case KEY_GRAVE: tools.focus_next_within_application(); return true; default:; } } return false; } |
Listing 2 |
The way of the MirAL API
The MirAL API is designed so that it is easy to implement and run a Mir server and compose any special features. One aspect of this is the provision of building blocks that can be tailored by the developer and ‘hooked up’ in the main()
function by adding them to the run_with()
list. One of these is an ‘internal client’ – a ‘client’ that runs in the server process but can use the client APIs to, for example, draw a surface.
The next iteration of egmde will add an internal client that paints a configurable wallpaper. I won’t show how that is implemented here, but I will show how it is added to main()
program (see Listing 3).
int main(int argc, char const* argv[]) { MirRunner runner{argc, argv}; egmde::Wallpaper wallpaper; runner.add_stop_callback([&] {wallpaper.stop(); }); return runner.run_with( { CommandLineOption{ std::ref(wallpaper), "wallpaper", "Colour of wallpaper RGB", "0x92006a"}, StartupInternalClient{"wallpaper", std::ref(wallpaper)}, set_window_management_policy <egmde::WindowManagerPolicy>() }); } |
Listing 3 |
The CommandLineOption
utility does a number of things, adds a configuration option to the command line, process the command line, and calls it’s first argument. In this case we pass an instance of egmde::Wallpaper
implements the function call operator to accept the wallpaper colour.
The StartupInternalClient
utility takes an internal client object, waits for the server to start and then connects the client to the server, notifying the internal client object of both the client-side and server-side connection so that the server ‘knows’ which client this is.
By supplying customizations to the run_with()
as a list we make it easy to ensure the server is initialized before they are used and give the user flexibility in setting these objects up. For example, the wallpaper
instance can be created and used in a a shutdown hook using add_stop_callback()
before being used in the run_with()
list.
This is achieved by declaring run_with()
to take an initializer list:
auto run_with(std::initializer_list <std::function<void(::mir::Server&)>> options) -> int;
Each of the supplied utilities ‘knows’ how to integrate itself into the system using the mir::Server
. User code should not need to do this directly, so part of the MirAL ‘abstraction’ is to keep this as an opaque type.
This approach has been proven effective by use in more advanced servers such as Unity8.
The capabilities
Mir tries to be agnostic about the window management style. There are example shells in the Mir project implementing several styles: ‘floating’ windows, ‘tiling’ windows and fullscreen ‘kiosk’ windows.
Although it was abandoned by Canonical, the Unity8 desktop shell demonstrates the potential of a shell based on Mir.
Mir is also used for Canonical’s ‘Internet of Things’ kiosk and for the Ubuntu Touch phone operating system.
The limitations
Support for Wayland isn’t complete in Mir (nor, to varying extents, other compositors) and it isn’t complete in the toolkits used to write applications. However, the commitment and momentum is there. It will happen soon.
Conclusion
If you are interested in experimenting with writing a shell to support Wayland clients then Mir might be an option.
Other ways to get Mir and use for development | |
|
References
egmde: https://github.com/AlanGriffiths/egmde/wiki
Unity8 demo: https://www.youtube.com/watch?v=LUxVdURZdRk
egmde demo: https://www.youtube.com/watch?v=e4_SEybCB0M
Unity8 use of MirAL API: https://github.com/ubports/qtmir/blob/master/src/platforms/mirserver/qmirserver_p.cpp#L91
Ubuntu Touch: https://ubports.com/
Notes:
More fields may be available via dynamicdata ..