Introduction
I recently (re)introduced a simple shell based on Mir: egmde. This shell is just the code needed to illustrate these articles and, maybe, inspire others to build on it but it is not intended to be a product.
At the end of the last article we could run egmde as a desktop and run and start Wayland based applications from a terminal. We could also select the keyboard layout and cusomize the wallpaper.
In this article we provide an integrated “launcher” to start applications so that applications do not need to be launched from the terminal.
Preparation
The code in this article needs Mir 0.31 or later. This exists in Ubuntu 18.04 and Fedora 28 but it is recommended to use the mir-team/release PPA.
On Ubuntu use:
$ sudo apt-add-repository ppa:mir-team/release
$ sudo apt update
$ sudo apt install g++ cmake pkg-config
$ sudo apt install libmiral-dev mir-graphics-drivers-desktop libfreetype6-dev libboost-all-dev
$ sudo apt install qtwayland5
Or, if using Fedora, use:
$ sudo dnf install gcc-c++ cmake mir-devel boost-devel freetype-devel qt5-qtwayland
The full code for this example is available on github:
$ git clone https://github.com/AlanGriffiths/egmde.git
$ git checkout article-3
Naturally, the code is likely to evolve, 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. 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 simple gradient wallpaper with simple instructions:
If you press Ctrl-Alt-A then the launcher appears on top of whatever you are currently running (initially nothing) and updated instructions:
That should be enough of a clue to get you started.
Other changes since last time
There’s also a small change to the wallpaper I introduced in the previous article, it is now possible to customize both the top and bottom colour for the gradient. This followed from some “corridor testing” by my wife who found the previous option and default colour unappealing.
This allows a “better brighter wallpaper” (as well as my choice):
Here’s the corresponding .config file:
$ cat ~/.config/egmde.config
wallpaper-top=0x8080ff
wallpaper-bottom=0x8080ff
Another small change is that I found a way to pick up the current keyboard and added that to the miral-desktop launch script so that it doesn’t need to be set by hand.
The example code
Wallpaper
I’ll first deal quickly with the wallpaper changes these options are provided by:
CommandLineOption{[&](auto& option) { wallpaper.top(option);},
"wallpaper-top", "Colour of wallpaper RGB", "0x000000"},
CommandLineOption{[&](auto& option) { wallpaper.bottom(option);},
"wallpaper-bottom", "Colour of wallpaper RGB", "0x92006a"},
The corresponding changes to the Wallpaper class are easy to follow.
Launcher
The main addition to the code os the “launcher”. I’ll concentrate on the changes to egmde.cpp as the Launcher class itself is a “legacy” Mir client. It is on my list to support “internal” Wayland clients in Mir, but that hasn’t happened yet.
The first update to the main program is adding this:
ExternalClientLauncher external_client_launcher;
egmde::Launcher launcher{external_client_launcher};
Note: If you look at the real code at the time of writing you’ll see there’s a bit of preprocessor magic to support the released version of Mir which doesn’t have ExternalClientLauncher. I’m going to ignore this (and another workaround for the lack of logind support) as these will be addressed by a Mir update in the near future.
Next, there’s a piece of code or handling keyboard input and showing the launcher:
auto const keyboard_shortcuts = [&](MirEvent const* event)
{
if (mir_event_get_type(event) != mir_event_type_input)
return false;
MirInputEvent const* input_event = mir_event_get_input_event(event);
if (mir_input_event_get_type(input_event) != mir_input_event_type_key)
return false;
MirKeyboardEvent const* kev = mir_input_event_get_keyboard_event(input_event);
if (mir_keyboard_event_action(kev) != mir_keyboard_action_down)
return false;
MirInputEventModifiers mods = mir_keyboard_event_modifiers(kev);
if (!(mods & mir_input_event_modifier_alt) || !(mods & mir_input_event_modifier_ctrl))
return false;
switch (mir_keyboard_event_scan_code(kev))
{
case KEY_A:
launcher.show();
return true;
case KEY_BACKSPACE:
runner.stop();
return true;
default:
return false;
}
};
This is a lambda that is later added to the Mir event processing and looks for either Ctrl-Alt-A or Ctrl-Alt-BkSp and shows the launcher or stops the runner accordingly.
Finally, as promised, these are added to the “run_with()” list which now looks as follows:
return runner.run_with(
{
...
external_client_launcher,
StartupInternalClient{"launcher", std::ref(launcher)},
AppendEventFilter{keyboard_shortcuts},
...
});
A final word
The MirAL API is designed so that keeping separate things separate is easy: the launcher code doesn’t have any inter-dependency with the wallpaper or the the window management code. So we could replace any of these without affecting the others.
That makes it easy to understand the individual components and customise them to build a server to suit your requirements.