SlideShare a Scribd company logo
1 of 81
The MirAL Story
Alan GriffithsAlan Griffiths
alan@octopull.co.uk
An Experience Report
A story
To compare experiences
To inspire and learn
Technical Debt
A story about technical debt
About the debt
About “repaying” the debt
A happy ending
Technical Debt
"Shipping first time code is like going into debt. A little
debt speeds development so long as it is paid back
promptly with a rewrite... The danger occurs when the debt
is not repaid. Every minute spent on not-quite-right code
counts as interest on that debt. Entire engineering
organizations can be brought to a stand-still under the
debt load of an unconsolidated implementation, object-
oriented or otherwise."
— Ward Cunningham, 1992
Technical Debt
A metaphor
Doesn’t communicate the issue
Debt is normal for a business
Bank loan
Mortgage
Technical Debt
Payday loan
Setting the scene: what is Mir?
Part of the Linux “graphics stack”
Supports
client
“applications” (or “apps”)
servers
Shells (desktop environments)
System compositors
X11 client/server
Xorg
Unity7 Application
Input/graphics
Mir client/server
libmirserver
Unity8
libmirclient
Application
Input/graphics
Mir client/server
libmirserver
Unity8
libmirclient
Application
Input/graphics
QtMir toolkit
Mir client/server
libmirserver
Unity8
libmirclient
Application
libmirserver
Unity
System
Compositor
Input/graphics
Setting the scene: Mir platforms
The graphics platform can be:
the “Mesa” stack
Desktop
Internet of Things
the “Android” stack
Phone or tablet
Another Mir server
Desktop or phone
???
Mir client/server
libmirserver
Unity8
libmirclient
Application
“mesa”
QtMir toolkit
DRM+libinput
or X11
“nested”
Mir servers
Unity8
on tablet/phone
on desktop
unity-system-compositor
Mir demos
mir_demo_server
mir_proving_server
Working S/W vs Technical Debt
We ship working software
But we incurred some “technical debt”
APIs and ABIs
API “Application Programming Interface”
Used by other programmers to write code
An “API breaking change” means...
Rewriting the programs
ABI “Application Binary Interface”
Used by other programs when they runs
An “ABI breaking change” means...
Rebuilding the programs
Mir is a set of libraries
There are headers (APIs) and libraries (ABIs)
For client development
For server development
Mir APIs and ABIs
For clients:
APIs
backward compatible
ABIs
backward compatible
For servers:
APIs
sometimes break
ABIs
usually break
A slide from ACCU 2013
class ServerConfiguration
{
public:
virtual std::shared_ptr<frontend::Communicator> the_communicator() = 0;
virtual std::shared_ptr<shell::SessionStore> the_session_store() = 0;
virtual std::shared_ptr<graphics::Display> the_display() = 0;
virtual std::shared_ptr<compositor::Drawer> the_drawer() = 0;
virtual std::shared_ptr<input::InputManager> the_input_manager() = 0;
protected:
ServerConfiguration() = default;
virtual ~ServerConfiguration() = default;
ServerConfiguration(ServerConfiguration const&) = delete;
ServerConfiguration& operator=(ServerConfiguration const&) = delete;
};
An ABI problem
class DefaultServerConfiguration : public virtual ServerConfiguration
{
public:
…
virtual std::shared_ptr<Shell> the_shell();
…
virtual std::shared_ptr<DisplayLayout> the_shell_display_layout();
A flexible way to the configure system
Almost every change caused an ABI break
The vtable layout matters
 The Mir server API and ABI
libmirserver-dev rapidly evolving API
Not designed for ABI stability
Every release had ABI breaking changes
When ABI breaks
Downstream projects need rebuilding
When API breaks
Downstream projects need reworking
 The cost: “interest payments”
Releasing Mir means
Updating downstreams
QtMir & Unity8
Unity System Compositor
A silo containing downstreams
Automated tests of downstreams
Manual tests of full stack
And triaging errors from multiple projects
Bad, but not bad enough?
Releasing Mir is expensive
Can’t just release Mir code when ready
Downstream projects need updates
Reviews and testing
Test failures are not isolated to Mir
It takes man-days effort and weeks elapsed
For Mir server projects
Each and Every Mir release
Need to be rebuilt and retested
And probably updated
Only possible if “owned” by Canonical
Mir clients
Client applications can be:
“Native” – using the Mir client API directly
GTK3 – GDK has a “Mir backend”
Qt – Qt has a “Mir backend”
SDL – SDL has a “Mir backend”
Kodi – Kodi has a “Mir backend”
X11 – using Xmir
Bad, but not bad enough?
The server projects are Canonical’s
So we can change them “easily”
There are “only a few”
Client toolkits are not Canonical’s
So we can’t change them “easily”
There are a lot of client toolkits
We don’t break the client ABI or API
Debt reduction
The developers have tried to reduce the cost
By “unpublishing” unused APIs
By replacing unstable ABIs in libmirserver
Write a new API & deprecate old
Update downstream
Delete (or “unpublish”) the old API
A more stable ABI
class DefaultServerConfiguration : public virtual ServerConfiguration
{
public:
…
virtual std::shared_ptr<Shell> the_shell();
…
virtual std::shared_ptr<DisplayLayout> the_shell_display_layout();
class Server
{
public:
…
void wrap_shell(Wrapper<Shell> const& wrapper);
…
void wrap_display_configuration_policy(
Wrapper<DisplayConfigurationPolicy> const& wrapper);
Other Priorities
We’ve tried to reduce the cost but...
We deliver new functionality
We improve performance
We support new hardware
We fix bugs
The result:
As fast as we improved things
other issues come along
“Elevating a system from chaos to order
takes energy and conscious effort.”
— Thomas Voss, 2016
“Friday Labs”
Canonical allows ½ day for approved “side projects”
So I made the case for tackling the Mir ABI
Management were sceptical
“Friday Labs”
Canonical allows ½ day for approved “side projects”
So I made the case for tackling the Mir ABI
Management were willing to let me try
I started a “Mir Abstraction Layer” project
MirAL: Mir Abstraction Layer
A separate project to “abstract” the Mir API
Design the API with ABI stability in mind
Narrow focus on Window Management
Window Management
What is a “shell”?
Part of a “Desktop Environment”
KDE, Gnome, Awesome, Cinnamon, LXDE, …
Controls
Where application windows appear
What window states (“fullscreen”, “restored”, …)
mean
Switching applications
Other “chrome” (launchers, status, notifications)
MirAL: init()
I copied “example servers” from Mir
And set up a new project to work in
And immediately found packaging bugs in Mir
Headers referenced, but not published
Dependencies missed in pkg-config
A “test framework” that wouldn’t link
MirAL: init()
I filed bugs against Mir
...and fixed them in my “day job”
MirAL: init()
I started refactoring the example code
Focussed on Window Management
We had three styles
 “floating”
 “tiling”
 “fullscreen”
Extracted commonality
Mined abstractions
MirAL
libmirserver
miral-shell
App
Input/graphics
libmiral
miral-kiosk
floating or tiling
MirAL: The Main Abstractions
There were three principle roles
A “basic window manager”
Generic behaviours and defaults
A “window management policy”
Used by the manager
The customization point
And “window management tools”
Used by the policy
MirAL
Mir
miral-shell
App
libmiral
miral-kiosk
BasicWindowManager WindowManagerTools
WindowManagerPolicy
FloatingWindowManagerPolicy
KioskWindowManagerPolicy
TilingWindowManagerPolicy
 A look at code using MirAL
The main() function
A “policy” class
A bit of policy implementation
main() at ACCU 2016
int main(int argc, char const* argv[])
{
using namespace miral;
SpinnerSplash spinner;
return MirRunner{argc, argv}.run_with(
{
WindowManagerOptions
{
add_window_manager_policy<CanonicalWindowManagerPolicy>("canonical", spinner),
add_window_manager_policy<TilingWindowManagerPolicy>("tiling"),
},
display_configuration_options,
QuitOnCtrlAltBkSp{},
StartupInternalClient{"Intro", spinner}
});
}
class CanonicalWindowManagerPolicy : public miral::WindowManagementPolicy
main() at ACCU 2017
int main(int argc, char const* argv[])
{
using namespace miral;
std::function<void()> shutdown_hook{[]{}};
SpinnerSplash spinner;
InternalClientLauncher launcher;
ActiveOutputsMonitor outputs_monitor;
WindowManagerOptions window_managers
{
add_window_manager_policy<TitlebarWindowManagerPolicy>("titlebar", spinner, launcher, shutdown_hook),
add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher, outputs_monitor),
};
MirRunner runner{argc, argv};
runner.add_stop_callback([&] { shutdown_hook(); });
auto const quit_on_ctrl_alt_bksp = [&](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;
if (mir_keyboard_event_scan_code(kev) != KEY_BACKSPACE)
return false;
runner.stop();
return true;
};
Keymap config_keymap;
DebugExtension debug_extensions;
return runner.run_with(
{
CommandLineOption{[&](std::string const& ) { },
"desktop_file_hint", "Ignored for Unity8 compatibility", "miral-shell.desktop"},
CursorTheme{"default"},
window_managers,
display_configuration_options,
launcher,
outputs_monitor,
config_keymap,
debug_extensions,
AppendEventFilter{quit_on_ctrl_alt_bksp},
StartupInternalClient{"Intro", spinner},
CommandLineOption{[&](std::string const& typeface) { ::titlebar::font_file(typeface); },
"shell-titlebar-font", "font file to use for titlebars", ::titlebar::font_file()}
});
}
main() at ACCU 2017
int main(int argc, char const* argv[])
{
using namespace miral;
std::function<void()> shutdown_hook{[]{}};
SpinnerSplash spinner;
InternalClientLauncher launcher;
ActiveOutputsMonitor outputs_monitor;
WindowManagerOptions window_managers
{
add_window_manager_policy<TitlebarWindowManagerPolicy>("titlebar", spinner, launcher, shutdown_hook),
add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher, outputs_monitor),
};
MirRunner runner{argc, argv};
runner.add_stop_callback([&] { shutdown_hook(); });
auto const quit_on_ctrl_alt_bksp = [&](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;
if (mir_keyboard_event_scan_code(kev) != KEY_BACKSPACE)
return false;
runner.stop();
return true;
};
Keymap config_keymap;
DebugExtension debug_extensions;
return runner.run_with(
{
CommandLineOption{[&](std::string const& ) { },
"desktop_file_hint", "Ignored for Unity8 compatability", "miral-shell.desktop"},
CursorTheme{"default"},
window_managers,
display_configuration_options,
launcher,
outputs_monitor,
config_keymap,
debug_extensions,
AppendEventFilter{quit_on_ctrl_alt_bksp},
StartupInternalClient{"Intro", spinner},
CommandLineOption{[&](std::string const& typeface) { ::titlebar::font_file(typeface); },
"shell-titlebar-font", "font file to use for titlebars", ::titlebar::font_file()}
});
}
int main(int argc, char const* argv[])
{
using namespace miral;
std::function<void()> shutdown_hook{[]{}};
SpinnerSplash spinner;
InternalClientLauncher launcher;
ActiveOutputsMonitor outputs_monitor;
WindowManagerOptions window_managers
{
add_window_manager_policy<TitlebarWindowManagerPolicy>(
"titlebar", spinner, launcher, shutdown_hook),
add_window_manager_policy<TilingWindowManagerPolicy>(
"tiling", spinner, launcher, outputs_monitor),
};
MirRunner runner{argc, argv};
runner.add_stop_callback([&] { shutdown_hook(); });
int main(int argc, char const* argv[])
{
using namespace miral;
std::function<void()> shutdown_hook{[]{}};
SpinnerSplash spinner;
InternalClientLauncher launcher;
ActiveOutputsMonitor outputs_monitor;
WindowManagerOptions window_managers
{
add_window_manager_policy<TitlebarWindowManagerPolicy>(
"titlebar", spinner, launcher, shutdown_hook),
add_window_manager_policy<TilingWindowManagerPolicy>(
"tiling", spinner, launcher, outputs_monitor),
};
MirRunner runner{argc, argv};
runner.add_stop_callback([&] { shutdown_hook(); });
main() at ACCU 2017
int main(int argc, char const* argv[])
{
using namespace miral;
std::function<void()> shutdown_hook{[]{}};
SpinnerSplash spinner;
InternalClientLauncher launcher;
ActiveOutputsMonitor outputs_monitor;
WindowManagerOptions window_managers
{
add_window_manager_policy<TitlebarWindowManagerPolicy>("titlebar", spinner, launcher, shutdown_hook),
add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher, outputs_monitor),
};
MirRunner runner{argc, argv};
runner.add_stop_callback([&] { shutdown_hook(); });
auto const quit_on_ctrl_alt_bksp = [&](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;
if (mir_keyboard_event_scan_code(kev) != KEY_BACKSPACE)
return false;
runner.stop();
return true;
};
Keymap config_keymap;
DebugExtension debug_extensions;
return runner.run_with(
{
CommandLineOption{[&](std::string const& ) { },
"desktop_file_hint", "Ignored for Unity8 compatability", "miral-shell.desktop"},
CursorTheme{"default"},
window_managers,
display_configuration_options,
launcher,
outputs_monitor,
config_keymap,
debug_extensions,
AppendEventFilter{quit_on_ctrl_alt_bksp},
StartupInternalClient{"Intro", spinner},
CommandLineOption{[&](std::string const& typeface) { ::titlebar::font_file(typeface); },
"shell-titlebar-font", "font file to use for titlebars", ::titlebar::font_file()}
});
}
auto const quit_on_ctrl_alt_bksp = [&](MirEvent const* event)
{
if (mir_event_get_type(event) != mir_event_type_input)
return false;
auto const input_event = mir_event_get_input_event(event);
if (mir_input_event_get_type(input_event) != mir_input_event_type_key)
return false;
auto 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;
if (mir_keyboard_event_scan_code(kev) != KEY_BACKSPACE)
return false;
runner.stop();
return true;
};
auto const quit_on_ctrl_alt_bksp = [&](MirEvent const* event)
{
if (mir_event_get_type(event) != mir_event_type_input)
return false;
auto const input_event = mir_event_get_input_event(event);
if (mir_input_event_get_type(input_event) != mir_input_event_type_key)
return false;
auto 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;
if (mir_keyboard_event_scan_code(kev) != KEY_BACKSPACE)
return false;
runner.stop();
return true;
};
main() at ACCU 2017
int main(int argc, char const* argv[])
{
using namespace miral;
std::function<void()> shutdown_hook{[]{}};
SpinnerSplash spinner;
InternalClientLauncher launcher;
ActiveOutputsMonitor outputs_monitor;
WindowManagerOptions window_managers
{
add_window_manager_policy<TitlebarWindowManagerPolicy>("titlebar", spinner, launcher, shutdown_hook),
add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher, outputs_monitor),
};
MirRunner runner{argc, argv};
runner.add_stop_callback([&] { shutdown_hook(); });
auto const quit_on_ctrl_alt_bksp = [&](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;
if (mir_keyboard_event_scan_code(kev) != KEY_BACKSPACE)
return false;
runner.stop();
return true;
};
Keymap config_keymap;
DebugExtension debug_extensions;
return runner.run_with(
{
CommandLineOption{[&](std::string const& ) { },
"desktop_file_hint", "Ignored for Unity8 compatability", "miral-shell.desktop"},
CursorTheme{"default"},
window_managers,
display_configuration_options,
launcher,
outputs_monitor,
config_keymap,
debug_extensions,
AppendEventFilter{quit_on_ctrl_alt_bksp},
StartupInternalClient{"Intro", spinner},
CommandLineOption{[&](std::string const& typeface) { ::titlebar::font_file(typeface); },
"shell-titlebar-font", "font file to use for titlebars", ::titlebar::font_file()}
});
}
Keymap config_keymap;
DebugExtension debug_extensions;
return runner.run_with(
{
CommandLineOption{[&](std::string const& ) { },
"desktop_file_hint",
"Ignored for Unity8 compatibility",
"miral-shell.desktop"},
CursorTheme{"default"},
window_managers,
display_configuration_options,
launcher,
outputs_monitor,
config_keymap,
debug_extensions,
AppendEventFilter{quit_on_ctrl_alt_bksp},
StartupInternalClient{"Intro", spinner},
CommandLineOption{[&](std::string const& typeface)
{ ::titlebar::font_file(typeface); },
"shell-titlebar-font",
"font file to use for titlebars", ::titlebar::font_file()}
});
}
Keymap config_keymap;
DebugExtension debug_extensions;
return runner.run_with(
{
CommandLineOption{[&](std::string const& ) { },
"desktop_file_hint",
"Ignored for Unity8 compatibility",
"miral-shell.desktop"},
CursorTheme{"default"},
window_managers,
display_configuration_options,
launcher,
outputs_monitor,
config_keymap,
debug_extensions,
AppendEventFilter{quit_on_ctrl_alt_bksp},
StartupInternalClient{"Intro", spinner},
CommandLineOption{[&](std::string const& typeface)
{ ::titlebar::font_file(typeface); },
"shell-titlebar-font",
"font file to use for titlebars", ::titlebar::font_file()}
});
}
The Kiosk main()
int main(int argc, char const* argv[])
{
SwSplash splash;
CommandLineOption maximise_roots{
[&](bool maximize_root_windows)
{ KioskWindowManagerPolicy::maximize_root_windows = maximize_root_windows; },
"kiosk-maximize-root-windows",
"Force root windows to maximized",
KioskWindowManagerPolicy::maximize_root_windows};
CommandLineOption startup_only{
[&](bool startup_only)
{ KioskAuthorizer::startup_only = startup_only; },
"kiosk-startup-apps-only",
"Only allow applications to connect during startup",
KioskAuthorizer::startup_only};
return MirRunner{argc, argv}.run_with(
{
set_window_management_policy<KioskWindowManagerPolicy>(splash),
SetApplicationAuthorizer<KioskAuthorizer>{splash},
Keymap{},
maximise_roots,
startup_only,
StartupInternalClient{"Intro", splash}
});
}
The Kiosk Policy
class KioskWindowManagerPolicy : public CanonicalWindowManagerPolicy
{
public:
KioskWindowManagerPolicy(WindowManagerTools const& tools, SwSplash const&);
void advise_focus_gained(WindowInfo const& info) override;
virtual void advise_new_window(WindowInfo const& window_info) override;
bool handle_keyboard_event(MirKeyboardEvent const* event) override;
bool handle_touch_event(MirTouchEvent const* event) override;
bool handle_pointer_event(MirPointerEvent const* event) override;
static std::atomic<bool> maximize_root_windows;
private:
SwSplash const splash;
};
The Kiosk implementation
bool KioskWindowManagerPolicy::handle_touch_event(MirTouchEvent const* event)
{
auto const count = mir_touch_event_point_count(event);
long total_x = 0;
long total_y = 0;
for (auto i = 0U; i != count; ++i)
{
total_x += mir_touch_event_axis_value(event, i, mir_touch_axis_x);
total_y += mir_touch_event_axis_value(event, i, mir_touch_axis_y);
}
Point const cursor{total_x/count, total_y/count};
tools.select_active_window(tools.window_at(cursor));
return false;
}
Shells based on MirAL
miral-shell
The traditional “floating” example
miral-shell --window-manager tiling
An example of a different WM policy
miral-kiosk
Very basic WM for simple requirements
MirAL: An ABI Stable Design
Avoiding
exposing data layout that might change
virtual functions tables that might change
Using
Cheshire Cat (a.k.a. Pimpl) idiom
Wrapping Mir types with focussed wrappers
struct SurfaceSpecification
{
bool is_empty() const;
optional_value<geometry::Width> width;
optional_value<geometry::Height> height;
optional_value<MirPixelFormat> pixel_format;
optional_value<std::string> name;
…
};
A struct Mir exposes
struct SurfaceSpecification
{
bool is_empty() const;
optional_value<geometry::Width> width;
optional_value<geometry::Height> height;
optional_value<MirPixelFormat> pixel_format;
optional_value<std::string> name;
…
optional_value<MirShellChrome> shell_chrome;
optional_value<MirPointerConfinementState> confine_pointer;
optional_value<std::shared_ptr<graphics::CursorImage>> cursor_image;
optional_value<StreamCursor> stream_cursor;
};
A struct Mir exposes
struct WindowInfo
{
WindowInfo();
WindowInfo(Window const& window, WindowSpecification const& params);
~WindowInfo();
explicit WindowInfo(WindowInfo const& that);
WindowInfo& operator=(WindowInfo const& that);
bool can_be_active() const;
bool can_morph_to(MirWindowType new_type) const;
...
auto name() const -> std::string;
void name(std::string const& name);
auto type() const -> MirWindowType;
void type(MirWindowType type);
...
private:
struct Self;
std::unique_ptr<Self> self;
};
A “struct” MirAL exposes
Preserving ABI
Functions
Adding is OK
Removing breaks
ABI
Changing parameter
lists breaks ABI
Renaming dubious
Types
Adding is OK
Removing breaks
ABI
Changing layout
breaks ABI
Adding virtual
functions dubious
Renaming dubious
Renaming functions
#include <mir/version.h>
#define MIRAL_FAKE_OLD_SYMBOL(old_sym, new_sym)
extern "C" __attribute__((alias(#new_sym))) void old_sym();
#define MIRAL_FAKE_NEW_SYMBOL(old_sym, new_sym)
extern "C" __attribute__((alias(#old_sym))) void new_sym();
#if (MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 26, 0))
#define MIRAL_BOTH_VERSIONS(old_sym, new_sym)
MIRAL_FAKE_OLD_SYMBOL(old_sym, new_sym)
#else
#define MIRAL_BOTH_VERSIONS(old_sym, new_sym)
MIRAL_FAKE_NEW_SYMBOL(old_sym, new_sym)
#endif
Renaming functions
#include <mir/version.h>
#define MIRAL_FAKE_OLD_SYMBOL(old_sym, new_sym)
extern "C" __attribute__((alias(#new_sym))) void old_sym();
#define MIRAL_FAKE_NEW_SYMBOL(old_sym, new_sym)
extern "C" __attribute__((alias(#old_sym))) void new_sym();
#if (MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 26, 0))
#define MIRAL_BOTH_VERSIONS(old_sym, new_sym)
MIRAL_FAKE_OLD_SYMBOL(old_sym, new_sym)
#else
#define MIRAL_BOTH_VERSIONS(old_sym, new_sym)
MIRAL_FAKE_NEW_SYMBOL(old_sym, new_sym)
#endif
MIRAL_BOTH_VERSIONS(
_ZNK5miral10WindowInfo12can_morph_toE14MirSurfaceType,
_ZNK5miral10WindowInfo12can_morph_toE13MirWindowType)
bool miral::WindowInfo::can_morph_to(MirWindowType new_type) const
...
MIRAL_BOTH_VERSIONS(
_ZNK5miral10WindowInfo12can_morph_toE14MirSurfaceType,
_ZNK5miral10WindowInfo12can_morph_toE13MirWindowType)
bool miral::WindowInfo::can_morph_to(MirWindowType new_type) const
...
Maintaining layout
class Keymap
{
public:
Keymap();
/// Specify a keymap.
explicit Keymap(std::string const& keymap);
/// Specify a new keymap.
void set_keymap(std::string const& keymap);
~Keymap();
Keymap(Keymap const& that);
auto operator=(Keymap const& rhs) -> Keymap&;
void operator()(mir::Server& server) const;
private:
struct Self;
std::shared_ptr<Self> self;
};
Adding virtual functions
class WindowManagementPolicy
{
public:
/// before any related calls begin
virtual void advise_begin();
/// after any related calls end
virtual void advise_end();
virtual auto place_new_window(
ApplicationInfo const& app_info,
WindowSpecification const& requested_specification) -> WindowSpecification = 0;
virtual void handle_window_ready(WindowInfo& window_info) = 0;
virtual void handle_modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0;
virtual void handle_raise_window(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 advise_new_app(ApplicationInfo& application);
virtual void advise_delete_app(ApplicationInfo const& application);
virtual void advise_new_window(WindowInfo const& window_info);
virtual void advise_focus_lost(WindowInfo const& window_info);
virtual void advise_focus_gained(WindowInfo const& window_info);
virtual void advise_state_change(WindowInfo const& window_info, MirWindowState state);
virtual void advise_move_to(WindowInfo const& window_info, Point top_left);
virtual void advise_resize(WindowInfo const& window_info, Size const& new_size);
virtual void advise_delete_window(WindowInfo const& window_info);
virtual void advise_raise(std::vector<Window> const& windows);
virtual auto confirm_inherited_move(WindowInfo const& window_info, Displacement movement) -> Rectangle = 0;
virtual ~WindowManagementPolicy() = default;
WindowManagementPolicy() = default;
WindowManagementPolicy(WindowManagementPolicy const&) = delete;
WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete;
};
Adding virtual functions
class WindowManagementPolicy
{
public:
/// before any related calls begin
virtual void advise_begin();
/// after any related calls end
virtual void advise_end();
virtual auto place_new_window(
ApplicationInfo const& app_info,
WindowSpecification const& requested_specification) -> WindowSpecification = 0;
virtual void handle_window_ready(WindowInfo& window_info) = 0;
virtual void handle_modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0;
virtual void handle_raise_window(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 advise_new_app(ApplicationInfo& application);
virtual void advise_delete_app(ApplicationInfo const& application);
virtual void advise_new_window(WindowInfo const& window_info);
virtual void advise_focus_lost(WindowInfo const& window_info);
virtual void advise_focus_gained(WindowInfo const& window_info);
virtual void advise_state_change(WindowInfo const& window_info, MirWindowState state);
virtual void advise_move_to(WindowInfo const& window_info, Point top_left);
virtual void advise_resize(WindowInfo const& window_info, Size const& new_size);
virtual void advise_delete_window(WindowInfo const& window_info);
virtual void advise_raise(std::vector<Window> const& windows);
virtual auto confirm_inherited_move(WindowInfo const& window_info, Displacement movement) -> Rectangle = 0;
virtual ~WindowManagementPolicy() = default;
WindowManagementPolicy() = default;
WindowManagementPolicy(WindowManagementPolicy const&) = delete;
WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete;
};
virtual void advise_adding_to_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual void advise_removing_from_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual void advise_adding_to_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual void advise_removing_from_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
Adding virtual functions
class WindowManagementPolicy
{
public:
/// before any related calls begin
virtual void advise_begin();
/// after any related calls end
virtual void advise_end();
virtual auto place_new_window(
ApplicationInfo const& app_info,
WindowSpecification const& requested_specification) -> WindowSpecification = 0;
virtual void handle_window_ready(WindowInfo& window_info) = 0;
virtual void handle_modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0;
virtual void handle_raise_window(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 advise_new_app(ApplicationInfo& application);
virtual void advise_delete_app(ApplicationInfo const& application);
virtual void advise_new_window(WindowInfo const& window_info);
virtual void advise_focus_lost(WindowInfo const& window_info);
virtual void advise_focus_gained(WindowInfo const& window_info);
virtual void advise_state_change(WindowInfo const& window_info, MirWindowState state);
virtual void advise_move_to(WindowInfo const& window_info, Point top_left);
virtual void advise_resize(WindowInfo const& window_info, Size const& new_size);
virtual void advise_delete_window(WindowInfo const& window_info);
virtual void advise_raise(std::vector<Window> const& windows);
virtual auto confirm_inherited_move(WindowInfo const& window_info, Displacement movement) -> Rectangle = 0;
virtual ~WindowManagementPolicy() = default;
WindowManagementPolicy() = default;
WindowManagementPolicy(WindowManagementPolicy const&) = delete;
WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete;
};
class WorkspacePolicy
{
public:
virtual void advise_adding_to_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual void advise_removing_from_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual ~WorkspacePolicy() = default;
WorkspacePolicy() = default;
WorkspacePolicy(WorkspacePolicy const&) = delete;
WorkspacePolicy& operator=(WorkspacePolicy const&) = delete;
};
class WorkspacePolicy
{
public:
virtual void advise_adding_to_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual void advise_removing_from_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual ~WorkspacePolicy() = default;
WorkspacePolicy() = default;
WorkspacePolicy(WorkspacePolicy const&) = delete;
WorkspacePolicy& operator=(WorkspacePolicy const&) = delete;
};
Adding virtual functions
class WindowManagementPolicy
{
public:
/// before any related calls begin
virtual void advise_begin();
/// after any related calls end
virtual void advise_end();
virtual auto place_new_window(
ApplicationInfo const& app_info,
WindowSpecification const& requested_specification) -> WindowSpecification = 0;
virtual void handle_window_ready(WindowInfo& window_info) = 0;
virtual void handle_modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0;
virtual void handle_raise_window(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 advise_new_app(ApplicationInfo& application);
virtual void advise_delete_app(ApplicationInfo const& application);
virtual void advise_new_window(WindowInfo const& window_info);
virtual void advise_focus_lost(WindowInfo const& window_info);
virtual void advise_focus_gained(WindowInfo const& window_info);
virtual void advise_state_change(WindowInfo const& window_info, MirWindowState state);
virtual void advise_move_to(WindowInfo const& window_info, Point top_left);
virtual void advise_resize(WindowInfo const& window_info, Size const& new_size);
virtual void advise_delete_window(WindowInfo const& window_info);
virtual void advise_raise(std::vector<Window> const& windows);
virtual auto confirm_inherited_move(WindowInfo const& window_info, Displacement movement) -> Rectangle = 0;
virtual ~WindowManagementPolicy() = default;
WindowManagementPolicy() = default;
WindowManagementPolicy(WindowManagementPolicy const&) = delete;
WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete;
};
class WorkspacePolicy
{
public:
virtual void advise_adding_to_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual void advise_removing_from_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual ~WorkspacePolicy() = default;
WorkspacePolicy() = default;
WorkspacePolicy(WorkspacePolicy const&) = delete;
WorkspacePolicy& operator=(WorkspacePolicy const&) = delete;
};
class WorkspacePolicy
{
public:
virtual void advise_adding_to_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual void advise_removing_from_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual ~WorkspacePolicy() = default;
WorkspacePolicy() = default;
WorkspacePolicy(WorkspacePolicy const&) = delete;
WorkspacePolicy& operator=(WorkspacePolicy const&) = delete;
};
class FloatingWindowManagerPolicy :
public miral::CanonicalWindowManagerPolicy,
public miral::WorkspacePolicy
{
...
class FloatingWindowManagerPolicy :
public miral::CanonicalWindowManagerPolicy,
public miral::WorkspacePolicy
{
...
Adding virtual functions
class WindowManagementPolicy
{
public:
/// before any related calls begin
virtual void advise_begin();
/// after any related calls end
virtual void advise_end();
virtual auto place_new_window(
ApplicationInfo const& app_info,
WindowSpecification const& requested_specification) -> WindowSpecification = 0;
virtual void handle_window_ready(WindowInfo& window_info) = 0;
virtual void handle_modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0;
virtual void handle_raise_window(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 advise_new_app(ApplicationInfo& application);
virtual void advise_delete_app(ApplicationInfo const& application);
virtual void advise_new_window(WindowInfo const& window_info);
virtual void advise_focus_lost(WindowInfo const& window_info);
virtual void advise_focus_gained(WindowInfo const& window_info);
virtual void advise_state_change(WindowInfo const& window_info, MirWindowState state);
virtual void advise_move_to(WindowInfo const& window_info, Point top_left);
virtual void advise_resize(WindowInfo const& window_info, Size const& new_size);
virtual void advise_delete_window(WindowInfo const& window_info);
virtual void advise_raise(std::vector<Window> const& windows);
virtual auto confirm_inherited_move(WindowInfo const& window_info, Displacement movement) -> Rectangle = 0;
virtual ~WindowManagementPolicy() = default;
WindowManagementPolicy() = default;
WindowManagementPolicy(WindowManagementPolicy const&) = delete;
WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete;
};
class WorkspacePolicy
{
public:
virtual void advise_adding_to_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual void advise_removing_from_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual ~WorkspacePolicy() = default;
WorkspacePolicy() = default;
WorkspacePolicy(WorkspacePolicy const&) = delete;
WorkspacePolicy& operator=(WorkspacePolicy const&) = delete;
};
class WorkspacePolicy
{
public:
virtual void advise_adding_to_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual void advise_removing_from_workspace(
std::shared_ptr<Workspace> const& workspace,
std::vector<Window> const& windows);
virtual ~WorkspacePolicy() = default;
WorkspacePolicy() = default;
WorkspacePolicy(WorkspacePolicy const&) = delete;
WorkspacePolicy& operator=(WorkspacePolicy const&) = delete;
};
auto find_workspace_policy(unique_ptr<WindowManagementPolicy> const& policy)
-> WorkspacePolicy*
{
WorkspacePolicy* result = dynamic_cast<WorkspacePolicy*>(policy.get());
if (result)
return result;
static WorkspacePolicy null_workspace_policy;
return &null_workspace_policy;
}
...
BasicWindowManager::BasicWindowManager(
FocusController* focus_controller,
shared_ptr<DisplayLayout> const& display_layout,
shared_ptr<PersistentSurfaceStore> const& persistent_surface_store,
WindowManagementPolicyBuilder const& build) :
...
policy(build(WindowManagerTools{this})),
workspace_policy{find_workspace_policy(policy)},
auto find_workspace_policy(unique_ptr<WindowManagementPolicy> const& policy)
-> WorkspacePolicy*
{
WorkspacePolicy* result = dynamic_cast<WorkspacePolicy*>(policy.get());
if (result)
return result;
static WorkspacePolicy null_workspace_policy;
return &null_workspace_policy;
}
...
BasicWindowManager::BasicWindowManager(
FocusController* focus_controller,
shared_ptr<DisplayLayout> const& display_layout,
shared_ptr<PersistentSurfaceStore> const& persistent_surface_store,
WindowManagementPolicyBuilder const& build) :
...
policy(build(WindowManagerTools{this})),
workspace_policy{find_workspace_policy(policy)},
ABI stability
Checking
Semi-automated update of linker script
debian/libmiral.symbols
abidiff
Maintaining
Discipline
Toolchain tricks
Concept Proven
MirAL
Has stable ABI
Should be usable outside Canonical
Supports writing a “shell” or “Desktop Enviroment”
Works on desktop, tablet, phone or IOT
Comes with worked examples
Repurposing MirAL
“at work” we started thinking about desktop
Need to consolidate basic window management
There’s a lot of stuff all shells need
Unity8 is the “wrong place”
Should be somewhere useful to all Mir servers
This was a job for MirAL
A bit more about QtMir
QtMir is an adapter between Qt and Mir
Used by Unity8
Might be usable by other Qt based shells
We needed to migrate QtMir to use MirAL
While not stopping QtMir development
Unity8/QtMir and MirAL
libmirserver
miral-shell
App
libmiral
miral-kiosk Unity8
QtMir
Unity8/QtMir on MirAL
(goal)
libmirserver
miral-shell
App
libmiral
miral-kiosk Unity8
QtMir
MirAL became my “day job”
We copied the QtMir source into MirAL
We set about
Removing Mir dependencies and using MirAL
Using MirAL window management with Qt
Testing and fixing MirAL window management
Keeping our QtMir copy in step
We proved the concept
Unity8/QtMir on MirAL
(actual)
libmirserver
miral-shell
App
libmiral
miral-kiosk Unity8
QtMir
Still depends on Mir
Unity8/QtMir on MirAL
(work in progress)
libmirserver
miral-shell
App
libmiral
miral-kiosk Unity8
QtMir
pseudo “miral”
Reversal of roles
Instead of QtMir code in MirAL
We have “namespace miral” code in QtMir
Functionality that belongs in libmiral
But we need experience
before committing to API & ABI
Shells based on MirAL
Unity8
“convergent” shell for...
desktop
Phone & tablet
miral-kiosk
Very basic WM for...
Dragonboard
Raspberry Pi
IOT
Shells based on MirAL
Unity8
“convergent” shell for...
desktop
Phone & tablet
miral-kiosk
Very basic WM for...
Dragonboard
Raspberry Pi
IOT
miral-shell
The canonical example
Testing toolkits
miral-shell --window-manager tiling
Demonstrate a different WM policy
Reduced debt
MirAL releases
A few hours work
Within a day elapsed
No “downstreams” in the silo
Can deliver features often
Mir releases only need MirAL in the silo
MirAL and toolkits
Thinking about desktop also means toolkits
qtubuntu, gtk-mir, SDL2, …
These hadn’t been proven against a real Mir
server
MirAL had the necessary support
This was another job for MirAL
Using MirAL
to test window management
$ sudo apt install miral-examples
$ miral-app --window-management-trace
$ <toolkit based app>
$ sudo apt install xmir
$ miral-xrun <X11 based app>
Using MirAL
to develop shells
$ sudo apt install libmiral-dev
$ pkg-config --cflags miral
A “shell” or “desktop environment”
That works on desktop, phone or IOT
That works with GTK++, Qt and SDL applications
That works with Xmir
An unintended spin-off
libmirclientcpp-dev
A C++ wrapper for the Mir client API
RAII
Function chaining
Summary
We incurred technical debt
It didn’t solve itself
But with a bit of imagination we found a way
Hopefully you found this useful
The MirAL Story
https://launchpad.net/miralhttps://launchpad.net/miral
http://voices.canonical.com/tag/miral/http://voices.canonical.com/tag/miral/
alan@octopull.co.ukalan@octopull.co.uk

More Related Content

Similar to The MirAL Story

Cut your Grails application to pieces - build feature plugins
Cut your Grails application to pieces - build feature pluginsCut your Grails application to pieces - build feature plugins
Cut your Grails application to pieces - build feature pluginsGR8Conf
 
Easy integration of Bluemix services with your applications
Easy integration of Bluemix services with your applicationsEasy integration of Bluemix services with your applications
Easy integration of Bluemix services with your applicationsJack-Junjie Cai
 
Advanced iOS Debbuging (Reloaded)
Advanced iOS Debbuging (Reloaded)Advanced iOS Debbuging (Reloaded)
Advanced iOS Debbuging (Reloaded)Massimo Oliviero
 
Containerize, PaaS, or Go Serverless!?
Containerize, PaaS, or Go Serverless!?Containerize, PaaS, or Go Serverless!?
Containerize, PaaS, or Go Serverless!?Phil Estes
 
Micro-Frontends JSVidCon
Micro-Frontends JSVidConMicro-Frontends JSVidCon
Micro-Frontends JSVidConAmir Zuker
 
EMA - Measuring the User Experience in the Cloud
EMA - Measuring the User Experience in the CloudEMA - Measuring the User Experience in the Cloud
EMA - Measuring the User Experience in the CloudCorrelsense
 
The fundamental problems of GUI applications and why people choose React
The fundamental problems of GUI applications and why people choose ReactThe fundamental problems of GUI applications and why people choose React
The fundamental problems of GUI applications and why people choose ReactOliver N
 
Developing maintainable Cordova applications
Developing maintainable Cordova applicationsDeveloping maintainable Cordova applications
Developing maintainable Cordova applicationsIvano Malavolta
 
Pitfalls of machine learning in production
Pitfalls of machine learning in productionPitfalls of machine learning in production
Pitfalls of machine learning in productionAntoine Sauray
 
MongoDB.local Atlanta: Introduction to Serverless MongoDB
MongoDB.local Atlanta: Introduction to Serverless MongoDBMongoDB.local Atlanta: Introduction to Serverless MongoDB
MongoDB.local Atlanta: Introduction to Serverless MongoDBMongoDB
 
11 0029-01 selling development tools in the cloud
11 0029-01 selling development tools in the cloud11 0029-01 selling development tools in the cloud
11 0029-01 selling development tools in the cloudJohn McDonald
 
Web sphere application server performance tuning workshop
Web sphere application server performance tuning workshopWeb sphere application server performance tuning workshop
Web sphere application server performance tuning workshopRohit Kelapure
 
Kubernetes & Co, beyond the hype
Kubernetes & Co, beyond the hypeKubernetes & Co, beyond the hype
Kubernetes & Co, beyond the hypeAlexandre Touret
 
Modern ASP.NET Webskills
Modern ASP.NET WebskillsModern ASP.NET Webskills
Modern ASP.NET WebskillsCaleb Jenkins
 
OpenWhisk Introduction
OpenWhisk IntroductionOpenWhisk Introduction
OpenWhisk IntroductionIoana Baldini
 
Introduction to single page application with angular js
Introduction to single page application with angular jsIntroduction to single page application with angular js
Introduction to single page application with angular jsMindfire Solutions
 

Similar to The MirAL Story (20)

Cut your Grails application to pieces - build feature plugins
Cut your Grails application to pieces - build feature pluginsCut your Grails application to pieces - build feature plugins
Cut your Grails application to pieces - build feature plugins
 
Easy integration of Bluemix services with your applications
Easy integration of Bluemix services with your applicationsEasy integration of Bluemix services with your applications
Easy integration of Bluemix services with your applications
 
Advanced iOS Debbuging (Reloaded)
Advanced iOS Debbuging (Reloaded)Advanced iOS Debbuging (Reloaded)
Advanced iOS Debbuging (Reloaded)
 
CODE IGNITER
CODE IGNITERCODE IGNITER
CODE IGNITER
 
Containerize, PaaS, or Go Serverless!?
Containerize, PaaS, or Go Serverless!?Containerize, PaaS, or Go Serverless!?
Containerize, PaaS, or Go Serverless!?
 
Micro-Frontends JSVidCon
Micro-Frontends JSVidConMicro-Frontends JSVidCon
Micro-Frontends JSVidCon
 
EMA - Measuring the User Experience in the Cloud
EMA - Measuring the User Experience in the CloudEMA - Measuring the User Experience in the Cloud
EMA - Measuring the User Experience in the Cloud
 
The fundamental problems of GUI applications and why people choose React
The fundamental problems of GUI applications and why people choose ReactThe fundamental problems of GUI applications and why people choose React
The fundamental problems of GUI applications and why people choose React
 
Developing maintainable Cordova applications
Developing maintainable Cordova applicationsDeveloping maintainable Cordova applications
Developing maintainable Cordova applications
 
Pitfalls of machine learning in production
Pitfalls of machine learning in productionPitfalls of machine learning in production
Pitfalls of machine learning in production
 
MongoDB.local Atlanta: Introduction to Serverless MongoDB
MongoDB.local Atlanta: Introduction to Serverless MongoDBMongoDB.local Atlanta: Introduction to Serverless MongoDB
MongoDB.local Atlanta: Introduction to Serverless MongoDB
 
11 0029-01 selling development tools in the cloud
11 0029-01 selling development tools in the cloud11 0029-01 selling development tools in the cloud
11 0029-01 selling development tools in the cloud
 
Web sphere application server performance tuning workshop
Web sphere application server performance tuning workshopWeb sphere application server performance tuning workshop
Web sphere application server performance tuning workshop
 
Csharp dot net
Csharp dot netCsharp dot net
Csharp dot net
 
Kubernetes & Co, beyond the hype
Kubernetes & Co, beyond the hypeKubernetes & Co, beyond the hype
Kubernetes & Co, beyond the hype
 
Openobject bi
Openobject biOpenobject bi
Openobject bi
 
Modern ASP.NET Webskills
Modern ASP.NET WebskillsModern ASP.NET Webskills
Modern ASP.NET Webskills
 
OpenWhisk Introduction
OpenWhisk IntroductionOpenWhisk Introduction
OpenWhisk Introduction
 
Introduction to single page application with angular js
Introduction to single page application with angular jsIntroduction to single page application with angular js
Introduction to single page application with angular js
 
Spring Framework-II
Spring Framework-IISpring Framework-II
Spring Framework-II
 

Recently uploaded

Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptkotipi9215
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - InfographicHr365.us smith
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideChristina Lin
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataBradBedford3
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfPower Karaoke
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxTier1 app
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 

Recently uploaded (20)

Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.ppt
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - Infographic
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdf
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 

The MirAL Story

  • 1. The MirAL Story Alan GriffithsAlan Griffiths alan@octopull.co.uk
  • 2. An Experience Report A story To compare experiences To inspire and learn
  • 3. Technical Debt A story about technical debt About the debt About “repaying” the debt A happy ending
  • 4. Technical Debt "Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite... The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt. Entire engineering organizations can be brought to a stand-still under the debt load of an unconsolidated implementation, object- oriented or otherwise." — Ward Cunningham, 1992
  • 5. Technical Debt A metaphor Doesn’t communicate the issue Debt is normal for a business Bank loan Mortgage Technical Debt Payday loan
  • 6. Setting the scene: what is Mir? Part of the Linux “graphics stack” Supports client “applications” (or “apps”) servers Shells (desktop environments) System compositors
  • 11. Setting the scene: Mir platforms The graphics platform can be: the “Mesa” stack Desktop Internet of Things the “Android” stack Phone or tablet Another Mir server Desktop or phone ???
  • 13. Mir servers Unity8 on tablet/phone on desktop unity-system-compositor Mir demos mir_demo_server mir_proving_server
  • 14. Working S/W vs Technical Debt We ship working software But we incurred some “technical debt”
  • 15. APIs and ABIs API “Application Programming Interface” Used by other programmers to write code An “API breaking change” means... Rewriting the programs ABI “Application Binary Interface” Used by other programs when they runs An “ABI breaking change” means... Rebuilding the programs
  • 16. Mir is a set of libraries There are headers (APIs) and libraries (ABIs) For client development For server development
  • 17. Mir APIs and ABIs For clients: APIs backward compatible ABIs backward compatible For servers: APIs sometimes break ABIs usually break
  • 18. A slide from ACCU 2013 class ServerConfiguration { public: virtual std::shared_ptr<frontend::Communicator> the_communicator() = 0; virtual std::shared_ptr<shell::SessionStore> the_session_store() = 0; virtual std::shared_ptr<graphics::Display> the_display() = 0; virtual std::shared_ptr<compositor::Drawer> the_drawer() = 0; virtual std::shared_ptr<input::InputManager> the_input_manager() = 0; protected: ServerConfiguration() = default; virtual ~ServerConfiguration() = default; ServerConfiguration(ServerConfiguration const&) = delete; ServerConfiguration& operator=(ServerConfiguration const&) = delete; };
  • 19. An ABI problem class DefaultServerConfiguration : public virtual ServerConfiguration { public: … virtual std::shared_ptr<Shell> the_shell(); … virtual std::shared_ptr<DisplayLayout> the_shell_display_layout(); A flexible way to the configure system Almost every change caused an ABI break The vtable layout matters
  • 20.  The Mir server API and ABI libmirserver-dev rapidly evolving API Not designed for ABI stability Every release had ABI breaking changes When ABI breaks Downstream projects need rebuilding When API breaks Downstream projects need reworking
  • 21.  The cost: “interest payments” Releasing Mir means Updating downstreams QtMir & Unity8 Unity System Compositor A silo containing downstreams Automated tests of downstreams Manual tests of full stack And triaging errors from multiple projects
  • 22. Bad, but not bad enough? Releasing Mir is expensive Can’t just release Mir code when ready Downstream projects need updates Reviews and testing Test failures are not isolated to Mir It takes man-days effort and weeks elapsed
  • 23. For Mir server projects Each and Every Mir release Need to be rebuilt and retested And probably updated Only possible if “owned” by Canonical
  • 24. Mir clients Client applications can be: “Native” – using the Mir client API directly GTK3 – GDK has a “Mir backend” Qt – Qt has a “Mir backend” SDL – SDL has a “Mir backend” Kodi – Kodi has a “Mir backend” X11 – using Xmir
  • 25. Bad, but not bad enough? The server projects are Canonical’s So we can change them “easily” There are “only a few” Client toolkits are not Canonical’s So we can’t change them “easily” There are a lot of client toolkits We don’t break the client ABI or API
  • 26. Debt reduction The developers have tried to reduce the cost By “unpublishing” unused APIs By replacing unstable ABIs in libmirserver Write a new API & deprecate old Update downstream Delete (or “unpublish”) the old API
  • 27. A more stable ABI class DefaultServerConfiguration : public virtual ServerConfiguration { public: … virtual std::shared_ptr<Shell> the_shell(); … virtual std::shared_ptr<DisplayLayout> the_shell_display_layout(); class Server { public: … void wrap_shell(Wrapper<Shell> const& wrapper); … void wrap_display_configuration_policy( Wrapper<DisplayConfigurationPolicy> const& wrapper);
  • 28. Other Priorities We’ve tried to reduce the cost but... We deliver new functionality We improve performance We support new hardware We fix bugs The result: As fast as we improved things other issues come along
  • 29. “Elevating a system from chaos to order takes energy and conscious effort.” — Thomas Voss, 2016
  • 30. “Friday Labs” Canonical allows ½ day for approved “side projects” So I made the case for tackling the Mir ABI Management were sceptical
  • 31. “Friday Labs” Canonical allows ½ day for approved “side projects” So I made the case for tackling the Mir ABI Management were willing to let me try I started a “Mir Abstraction Layer” project
  • 32. MirAL: Mir Abstraction Layer A separate project to “abstract” the Mir API Design the API with ABI stability in mind Narrow focus on Window Management
  • 33. Window Management What is a “shell”? Part of a “Desktop Environment” KDE, Gnome, Awesome, Cinnamon, LXDE, … Controls Where application windows appear What window states (“fullscreen”, “restored”, …) mean Switching applications Other “chrome” (launchers, status, notifications)
  • 34. MirAL: init() I copied “example servers” from Mir And set up a new project to work in And immediately found packaging bugs in Mir Headers referenced, but not published Dependencies missed in pkg-config A “test framework” that wouldn’t link
  • 35. MirAL: init() I filed bugs against Mir ...and fixed them in my “day job”
  • 36. MirAL: init() I started refactoring the example code Focussed on Window Management We had three styles  “floating”  “tiling”  “fullscreen” Extracted commonality Mined abstractions
  • 38. MirAL: The Main Abstractions There were three principle roles A “basic window manager” Generic behaviours and defaults A “window management policy” Used by the manager The customization point And “window management tools” Used by the policy
  • 40.  A look at code using MirAL The main() function A “policy” class A bit of policy implementation
  • 41. main() at ACCU 2016 int main(int argc, char const* argv[]) { using namespace miral; SpinnerSplash spinner; return MirRunner{argc, argv}.run_with( { WindowManagerOptions { add_window_manager_policy<CanonicalWindowManagerPolicy>("canonical", spinner), add_window_manager_policy<TilingWindowManagerPolicy>("tiling"), }, display_configuration_options, QuitOnCtrlAltBkSp{}, StartupInternalClient{"Intro", spinner} }); } class CanonicalWindowManagerPolicy : public miral::WindowManagementPolicy
  • 42. main() at ACCU 2017 int main(int argc, char const* argv[]) { using namespace miral; std::function<void()> shutdown_hook{[]{}}; SpinnerSplash spinner; InternalClientLauncher launcher; ActiveOutputsMonitor outputs_monitor; WindowManagerOptions window_managers { add_window_manager_policy<TitlebarWindowManagerPolicy>("titlebar", spinner, launcher, shutdown_hook), add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher, outputs_monitor), }; MirRunner runner{argc, argv}; runner.add_stop_callback([&] { shutdown_hook(); }); auto const quit_on_ctrl_alt_bksp = [&](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; if (mir_keyboard_event_scan_code(kev) != KEY_BACKSPACE) return false; runner.stop(); return true; }; Keymap config_keymap; DebugExtension debug_extensions; return runner.run_with( { CommandLineOption{[&](std::string const& ) { }, "desktop_file_hint", "Ignored for Unity8 compatibility", "miral-shell.desktop"}, CursorTheme{"default"}, window_managers, display_configuration_options, launcher, outputs_monitor, config_keymap, debug_extensions, AppendEventFilter{quit_on_ctrl_alt_bksp}, StartupInternalClient{"Intro", spinner}, CommandLineOption{[&](std::string const& typeface) { ::titlebar::font_file(typeface); }, "shell-titlebar-font", "font file to use for titlebars", ::titlebar::font_file()} }); }
  • 43. main() at ACCU 2017 int main(int argc, char const* argv[]) { using namespace miral; std::function<void()> shutdown_hook{[]{}}; SpinnerSplash spinner; InternalClientLauncher launcher; ActiveOutputsMonitor outputs_monitor; WindowManagerOptions window_managers { add_window_manager_policy<TitlebarWindowManagerPolicy>("titlebar", spinner, launcher, shutdown_hook), add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher, outputs_monitor), }; MirRunner runner{argc, argv}; runner.add_stop_callback([&] { shutdown_hook(); }); auto const quit_on_ctrl_alt_bksp = [&](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; if (mir_keyboard_event_scan_code(kev) != KEY_BACKSPACE) return false; runner.stop(); return true; }; Keymap config_keymap; DebugExtension debug_extensions; return runner.run_with( { CommandLineOption{[&](std::string const& ) { }, "desktop_file_hint", "Ignored for Unity8 compatability", "miral-shell.desktop"}, CursorTheme{"default"}, window_managers, display_configuration_options, launcher, outputs_monitor, config_keymap, debug_extensions, AppendEventFilter{quit_on_ctrl_alt_bksp}, StartupInternalClient{"Intro", spinner}, CommandLineOption{[&](std::string const& typeface) { ::titlebar::font_file(typeface); }, "shell-titlebar-font", "font file to use for titlebars", ::titlebar::font_file()} }); } int main(int argc, char const* argv[]) { using namespace miral; std::function<void()> shutdown_hook{[]{}}; SpinnerSplash spinner; InternalClientLauncher launcher; ActiveOutputsMonitor outputs_monitor; WindowManagerOptions window_managers { add_window_manager_policy<TitlebarWindowManagerPolicy>( "titlebar", spinner, launcher, shutdown_hook), add_window_manager_policy<TilingWindowManagerPolicy>( "tiling", spinner, launcher, outputs_monitor), }; MirRunner runner{argc, argv}; runner.add_stop_callback([&] { shutdown_hook(); }); int main(int argc, char const* argv[]) { using namespace miral; std::function<void()> shutdown_hook{[]{}}; SpinnerSplash spinner; InternalClientLauncher launcher; ActiveOutputsMonitor outputs_monitor; WindowManagerOptions window_managers { add_window_manager_policy<TitlebarWindowManagerPolicy>( "titlebar", spinner, launcher, shutdown_hook), add_window_manager_policy<TilingWindowManagerPolicy>( "tiling", spinner, launcher, outputs_monitor), }; MirRunner runner{argc, argv}; runner.add_stop_callback([&] { shutdown_hook(); });
  • 44. main() at ACCU 2017 int main(int argc, char const* argv[]) { using namespace miral; std::function<void()> shutdown_hook{[]{}}; SpinnerSplash spinner; InternalClientLauncher launcher; ActiveOutputsMonitor outputs_monitor; WindowManagerOptions window_managers { add_window_manager_policy<TitlebarWindowManagerPolicy>("titlebar", spinner, launcher, shutdown_hook), add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher, outputs_monitor), }; MirRunner runner{argc, argv}; runner.add_stop_callback([&] { shutdown_hook(); }); auto const quit_on_ctrl_alt_bksp = [&](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; if (mir_keyboard_event_scan_code(kev) != KEY_BACKSPACE) return false; runner.stop(); return true; }; Keymap config_keymap; DebugExtension debug_extensions; return runner.run_with( { CommandLineOption{[&](std::string const& ) { }, "desktop_file_hint", "Ignored for Unity8 compatability", "miral-shell.desktop"}, CursorTheme{"default"}, window_managers, display_configuration_options, launcher, outputs_monitor, config_keymap, debug_extensions, AppendEventFilter{quit_on_ctrl_alt_bksp}, StartupInternalClient{"Intro", spinner}, CommandLineOption{[&](std::string const& typeface) { ::titlebar::font_file(typeface); }, "shell-titlebar-font", "font file to use for titlebars", ::titlebar::font_file()} }); } auto const quit_on_ctrl_alt_bksp = [&](MirEvent const* event) { if (mir_event_get_type(event) != mir_event_type_input) return false; auto const input_event = mir_event_get_input_event(event); if (mir_input_event_get_type(input_event) != mir_input_event_type_key) return false; auto 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; if (mir_keyboard_event_scan_code(kev) != KEY_BACKSPACE) return false; runner.stop(); return true; }; auto const quit_on_ctrl_alt_bksp = [&](MirEvent const* event) { if (mir_event_get_type(event) != mir_event_type_input) return false; auto const input_event = mir_event_get_input_event(event); if (mir_input_event_get_type(input_event) != mir_input_event_type_key) return false; auto 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; if (mir_keyboard_event_scan_code(kev) != KEY_BACKSPACE) return false; runner.stop(); return true; };
  • 45. main() at ACCU 2017 int main(int argc, char const* argv[]) { using namespace miral; std::function<void()> shutdown_hook{[]{}}; SpinnerSplash spinner; InternalClientLauncher launcher; ActiveOutputsMonitor outputs_monitor; WindowManagerOptions window_managers { add_window_manager_policy<TitlebarWindowManagerPolicy>("titlebar", spinner, launcher, shutdown_hook), add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher, outputs_monitor), }; MirRunner runner{argc, argv}; runner.add_stop_callback([&] { shutdown_hook(); }); auto const quit_on_ctrl_alt_bksp = [&](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; if (mir_keyboard_event_scan_code(kev) != KEY_BACKSPACE) return false; runner.stop(); return true; }; Keymap config_keymap; DebugExtension debug_extensions; return runner.run_with( { CommandLineOption{[&](std::string const& ) { }, "desktop_file_hint", "Ignored for Unity8 compatability", "miral-shell.desktop"}, CursorTheme{"default"}, window_managers, display_configuration_options, launcher, outputs_monitor, config_keymap, debug_extensions, AppendEventFilter{quit_on_ctrl_alt_bksp}, StartupInternalClient{"Intro", spinner}, CommandLineOption{[&](std::string const& typeface) { ::titlebar::font_file(typeface); }, "shell-titlebar-font", "font file to use for titlebars", ::titlebar::font_file()} }); } Keymap config_keymap; DebugExtension debug_extensions; return runner.run_with( { CommandLineOption{[&](std::string const& ) { }, "desktop_file_hint", "Ignored for Unity8 compatibility", "miral-shell.desktop"}, CursorTheme{"default"}, window_managers, display_configuration_options, launcher, outputs_monitor, config_keymap, debug_extensions, AppendEventFilter{quit_on_ctrl_alt_bksp}, StartupInternalClient{"Intro", spinner}, CommandLineOption{[&](std::string const& typeface) { ::titlebar::font_file(typeface); }, "shell-titlebar-font", "font file to use for titlebars", ::titlebar::font_file()} }); } Keymap config_keymap; DebugExtension debug_extensions; return runner.run_with( { CommandLineOption{[&](std::string const& ) { }, "desktop_file_hint", "Ignored for Unity8 compatibility", "miral-shell.desktop"}, CursorTheme{"default"}, window_managers, display_configuration_options, launcher, outputs_monitor, config_keymap, debug_extensions, AppendEventFilter{quit_on_ctrl_alt_bksp}, StartupInternalClient{"Intro", spinner}, CommandLineOption{[&](std::string const& typeface) { ::titlebar::font_file(typeface); }, "shell-titlebar-font", "font file to use for titlebars", ::titlebar::font_file()} }); }
  • 46. The Kiosk main() int main(int argc, char const* argv[]) { SwSplash splash; CommandLineOption maximise_roots{ [&](bool maximize_root_windows) { KioskWindowManagerPolicy::maximize_root_windows = maximize_root_windows; }, "kiosk-maximize-root-windows", "Force root windows to maximized", KioskWindowManagerPolicy::maximize_root_windows}; CommandLineOption startup_only{ [&](bool startup_only) { KioskAuthorizer::startup_only = startup_only; }, "kiosk-startup-apps-only", "Only allow applications to connect during startup", KioskAuthorizer::startup_only}; return MirRunner{argc, argv}.run_with( { set_window_management_policy<KioskWindowManagerPolicy>(splash), SetApplicationAuthorizer<KioskAuthorizer>{splash}, Keymap{}, maximise_roots, startup_only, StartupInternalClient{"Intro", splash} }); }
  • 47. The Kiosk Policy class KioskWindowManagerPolicy : public CanonicalWindowManagerPolicy { public: KioskWindowManagerPolicy(WindowManagerTools const& tools, SwSplash const&); void advise_focus_gained(WindowInfo const& info) override; virtual void advise_new_window(WindowInfo const& window_info) override; bool handle_keyboard_event(MirKeyboardEvent const* event) override; bool handle_touch_event(MirTouchEvent const* event) override; bool handle_pointer_event(MirPointerEvent const* event) override; static std::atomic<bool> maximize_root_windows; private: SwSplash const splash; };
  • 48. The Kiosk implementation bool KioskWindowManagerPolicy::handle_touch_event(MirTouchEvent const* event) { auto const count = mir_touch_event_point_count(event); long total_x = 0; long total_y = 0; for (auto i = 0U; i != count; ++i) { total_x += mir_touch_event_axis_value(event, i, mir_touch_axis_x); total_y += mir_touch_event_axis_value(event, i, mir_touch_axis_y); } Point const cursor{total_x/count, total_y/count}; tools.select_active_window(tools.window_at(cursor)); return false; }
  • 49. Shells based on MirAL miral-shell The traditional “floating” example miral-shell --window-manager tiling An example of a different WM policy miral-kiosk Very basic WM for simple requirements
  • 50. MirAL: An ABI Stable Design Avoiding exposing data layout that might change virtual functions tables that might change Using Cheshire Cat (a.k.a. Pimpl) idiom Wrapping Mir types with focussed wrappers
  • 51. struct SurfaceSpecification { bool is_empty() const; optional_value<geometry::Width> width; optional_value<geometry::Height> height; optional_value<MirPixelFormat> pixel_format; optional_value<std::string> name; … }; A struct Mir exposes
  • 52. struct SurfaceSpecification { bool is_empty() const; optional_value<geometry::Width> width; optional_value<geometry::Height> height; optional_value<MirPixelFormat> pixel_format; optional_value<std::string> name; … optional_value<MirShellChrome> shell_chrome; optional_value<MirPointerConfinementState> confine_pointer; optional_value<std::shared_ptr<graphics::CursorImage>> cursor_image; optional_value<StreamCursor> stream_cursor; }; A struct Mir exposes
  • 53. struct WindowInfo { WindowInfo(); WindowInfo(Window const& window, WindowSpecification const& params); ~WindowInfo(); explicit WindowInfo(WindowInfo const& that); WindowInfo& operator=(WindowInfo const& that); bool can_be_active() const; bool can_morph_to(MirWindowType new_type) const; ... auto name() const -> std::string; void name(std::string const& name); auto type() const -> MirWindowType; void type(MirWindowType type); ... private: struct Self; std::unique_ptr<Self> self; }; A “struct” MirAL exposes
  • 54. Preserving ABI Functions Adding is OK Removing breaks ABI Changing parameter lists breaks ABI Renaming dubious Types Adding is OK Removing breaks ABI Changing layout breaks ABI Adding virtual functions dubious Renaming dubious
  • 55. Renaming functions #include <mir/version.h> #define MIRAL_FAKE_OLD_SYMBOL(old_sym, new_sym) extern "C" __attribute__((alias(#new_sym))) void old_sym(); #define MIRAL_FAKE_NEW_SYMBOL(old_sym, new_sym) extern "C" __attribute__((alias(#old_sym))) void new_sym(); #if (MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 26, 0)) #define MIRAL_BOTH_VERSIONS(old_sym, new_sym) MIRAL_FAKE_OLD_SYMBOL(old_sym, new_sym) #else #define MIRAL_BOTH_VERSIONS(old_sym, new_sym) MIRAL_FAKE_NEW_SYMBOL(old_sym, new_sym) #endif
  • 56. Renaming functions #include <mir/version.h> #define MIRAL_FAKE_OLD_SYMBOL(old_sym, new_sym) extern "C" __attribute__((alias(#new_sym))) void old_sym(); #define MIRAL_FAKE_NEW_SYMBOL(old_sym, new_sym) extern "C" __attribute__((alias(#old_sym))) void new_sym(); #if (MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 26, 0)) #define MIRAL_BOTH_VERSIONS(old_sym, new_sym) MIRAL_FAKE_OLD_SYMBOL(old_sym, new_sym) #else #define MIRAL_BOTH_VERSIONS(old_sym, new_sym) MIRAL_FAKE_NEW_SYMBOL(old_sym, new_sym) #endif MIRAL_BOTH_VERSIONS( _ZNK5miral10WindowInfo12can_morph_toE14MirSurfaceType, _ZNK5miral10WindowInfo12can_morph_toE13MirWindowType) bool miral::WindowInfo::can_morph_to(MirWindowType new_type) const ... MIRAL_BOTH_VERSIONS( _ZNK5miral10WindowInfo12can_morph_toE14MirSurfaceType, _ZNK5miral10WindowInfo12can_morph_toE13MirWindowType) bool miral::WindowInfo::can_morph_to(MirWindowType new_type) const ...
  • 57. Maintaining layout class Keymap { public: Keymap(); /// Specify a keymap. explicit Keymap(std::string const& keymap); /// Specify a new keymap. void set_keymap(std::string const& keymap); ~Keymap(); Keymap(Keymap const& that); auto operator=(Keymap const& rhs) -> Keymap&; void operator()(mir::Server& server) const; private: struct Self; std::shared_ptr<Self> self; };
  • 58. Adding virtual functions class WindowManagementPolicy { public: /// before any related calls begin virtual void advise_begin(); /// after any related calls end virtual void advise_end(); virtual auto place_new_window( ApplicationInfo const& app_info, WindowSpecification const& requested_specification) -> WindowSpecification = 0; virtual void handle_window_ready(WindowInfo& window_info) = 0; virtual void handle_modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0; virtual void handle_raise_window(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 advise_new_app(ApplicationInfo& application); virtual void advise_delete_app(ApplicationInfo const& application); virtual void advise_new_window(WindowInfo const& window_info); virtual void advise_focus_lost(WindowInfo const& window_info); virtual void advise_focus_gained(WindowInfo const& window_info); virtual void advise_state_change(WindowInfo const& window_info, MirWindowState state); virtual void advise_move_to(WindowInfo const& window_info, Point top_left); virtual void advise_resize(WindowInfo const& window_info, Size const& new_size); virtual void advise_delete_window(WindowInfo const& window_info); virtual void advise_raise(std::vector<Window> const& windows); virtual auto confirm_inherited_move(WindowInfo const& window_info, Displacement movement) -> Rectangle = 0; virtual ~WindowManagementPolicy() = default; WindowManagementPolicy() = default; WindowManagementPolicy(WindowManagementPolicy const&) = delete; WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete; };
  • 59. Adding virtual functions class WindowManagementPolicy { public: /// before any related calls begin virtual void advise_begin(); /// after any related calls end virtual void advise_end(); virtual auto place_new_window( ApplicationInfo const& app_info, WindowSpecification const& requested_specification) -> WindowSpecification = 0; virtual void handle_window_ready(WindowInfo& window_info) = 0; virtual void handle_modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0; virtual void handle_raise_window(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 advise_new_app(ApplicationInfo& application); virtual void advise_delete_app(ApplicationInfo const& application); virtual void advise_new_window(WindowInfo const& window_info); virtual void advise_focus_lost(WindowInfo const& window_info); virtual void advise_focus_gained(WindowInfo const& window_info); virtual void advise_state_change(WindowInfo const& window_info, MirWindowState state); virtual void advise_move_to(WindowInfo const& window_info, Point top_left); virtual void advise_resize(WindowInfo const& window_info, Size const& new_size); virtual void advise_delete_window(WindowInfo const& window_info); virtual void advise_raise(std::vector<Window> const& windows); virtual auto confirm_inherited_move(WindowInfo const& window_info, Displacement movement) -> Rectangle = 0; virtual ~WindowManagementPolicy() = default; WindowManagementPolicy() = default; WindowManagementPolicy(WindowManagementPolicy const&) = delete; WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete; }; virtual void advise_adding_to_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual void advise_removing_from_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual void advise_adding_to_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual void advise_removing_from_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows);
  • 60. Adding virtual functions class WindowManagementPolicy { public: /// before any related calls begin virtual void advise_begin(); /// after any related calls end virtual void advise_end(); virtual auto place_new_window( ApplicationInfo const& app_info, WindowSpecification const& requested_specification) -> WindowSpecification = 0; virtual void handle_window_ready(WindowInfo& window_info) = 0; virtual void handle_modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0; virtual void handle_raise_window(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 advise_new_app(ApplicationInfo& application); virtual void advise_delete_app(ApplicationInfo const& application); virtual void advise_new_window(WindowInfo const& window_info); virtual void advise_focus_lost(WindowInfo const& window_info); virtual void advise_focus_gained(WindowInfo const& window_info); virtual void advise_state_change(WindowInfo const& window_info, MirWindowState state); virtual void advise_move_to(WindowInfo const& window_info, Point top_left); virtual void advise_resize(WindowInfo const& window_info, Size const& new_size); virtual void advise_delete_window(WindowInfo const& window_info); virtual void advise_raise(std::vector<Window> const& windows); virtual auto confirm_inherited_move(WindowInfo const& window_info, Displacement movement) -> Rectangle = 0; virtual ~WindowManagementPolicy() = default; WindowManagementPolicy() = default; WindowManagementPolicy(WindowManagementPolicy const&) = delete; WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete; }; class WorkspacePolicy { public: virtual void advise_adding_to_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual void advise_removing_from_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual ~WorkspacePolicy() = default; WorkspacePolicy() = default; WorkspacePolicy(WorkspacePolicy const&) = delete; WorkspacePolicy& operator=(WorkspacePolicy const&) = delete; }; class WorkspacePolicy { public: virtual void advise_adding_to_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual void advise_removing_from_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual ~WorkspacePolicy() = default; WorkspacePolicy() = default; WorkspacePolicy(WorkspacePolicy const&) = delete; WorkspacePolicy& operator=(WorkspacePolicy const&) = delete; };
  • 61. Adding virtual functions class WindowManagementPolicy { public: /// before any related calls begin virtual void advise_begin(); /// after any related calls end virtual void advise_end(); virtual auto place_new_window( ApplicationInfo const& app_info, WindowSpecification const& requested_specification) -> WindowSpecification = 0; virtual void handle_window_ready(WindowInfo& window_info) = 0; virtual void handle_modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0; virtual void handle_raise_window(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 advise_new_app(ApplicationInfo& application); virtual void advise_delete_app(ApplicationInfo const& application); virtual void advise_new_window(WindowInfo const& window_info); virtual void advise_focus_lost(WindowInfo const& window_info); virtual void advise_focus_gained(WindowInfo const& window_info); virtual void advise_state_change(WindowInfo const& window_info, MirWindowState state); virtual void advise_move_to(WindowInfo const& window_info, Point top_left); virtual void advise_resize(WindowInfo const& window_info, Size const& new_size); virtual void advise_delete_window(WindowInfo const& window_info); virtual void advise_raise(std::vector<Window> const& windows); virtual auto confirm_inherited_move(WindowInfo const& window_info, Displacement movement) -> Rectangle = 0; virtual ~WindowManagementPolicy() = default; WindowManagementPolicy() = default; WindowManagementPolicy(WindowManagementPolicy const&) = delete; WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete; }; class WorkspacePolicy { public: virtual void advise_adding_to_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual void advise_removing_from_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual ~WorkspacePolicy() = default; WorkspacePolicy() = default; WorkspacePolicy(WorkspacePolicy const&) = delete; WorkspacePolicy& operator=(WorkspacePolicy const&) = delete; }; class WorkspacePolicy { public: virtual void advise_adding_to_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual void advise_removing_from_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual ~WorkspacePolicy() = default; WorkspacePolicy() = default; WorkspacePolicy(WorkspacePolicy const&) = delete; WorkspacePolicy& operator=(WorkspacePolicy const&) = delete; }; class FloatingWindowManagerPolicy : public miral::CanonicalWindowManagerPolicy, public miral::WorkspacePolicy { ... class FloatingWindowManagerPolicy : public miral::CanonicalWindowManagerPolicy, public miral::WorkspacePolicy { ...
  • 62. Adding virtual functions class WindowManagementPolicy { public: /// before any related calls begin virtual void advise_begin(); /// after any related calls end virtual void advise_end(); virtual auto place_new_window( ApplicationInfo const& app_info, WindowSpecification const& requested_specification) -> WindowSpecification = 0; virtual void handle_window_ready(WindowInfo& window_info) = 0; virtual void handle_modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0; virtual void handle_raise_window(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 advise_new_app(ApplicationInfo& application); virtual void advise_delete_app(ApplicationInfo const& application); virtual void advise_new_window(WindowInfo const& window_info); virtual void advise_focus_lost(WindowInfo const& window_info); virtual void advise_focus_gained(WindowInfo const& window_info); virtual void advise_state_change(WindowInfo const& window_info, MirWindowState state); virtual void advise_move_to(WindowInfo const& window_info, Point top_left); virtual void advise_resize(WindowInfo const& window_info, Size const& new_size); virtual void advise_delete_window(WindowInfo const& window_info); virtual void advise_raise(std::vector<Window> const& windows); virtual auto confirm_inherited_move(WindowInfo const& window_info, Displacement movement) -> Rectangle = 0; virtual ~WindowManagementPolicy() = default; WindowManagementPolicy() = default; WindowManagementPolicy(WindowManagementPolicy const&) = delete; WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete; }; class WorkspacePolicy { public: virtual void advise_adding_to_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual void advise_removing_from_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual ~WorkspacePolicy() = default; WorkspacePolicy() = default; WorkspacePolicy(WorkspacePolicy const&) = delete; WorkspacePolicy& operator=(WorkspacePolicy const&) = delete; }; class WorkspacePolicy { public: virtual void advise_adding_to_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual void advise_removing_from_workspace( std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows); virtual ~WorkspacePolicy() = default; WorkspacePolicy() = default; WorkspacePolicy(WorkspacePolicy const&) = delete; WorkspacePolicy& operator=(WorkspacePolicy const&) = delete; }; auto find_workspace_policy(unique_ptr<WindowManagementPolicy> const& policy) -> WorkspacePolicy* { WorkspacePolicy* result = dynamic_cast<WorkspacePolicy*>(policy.get()); if (result) return result; static WorkspacePolicy null_workspace_policy; return &null_workspace_policy; } ... BasicWindowManager::BasicWindowManager( FocusController* focus_controller, shared_ptr<DisplayLayout> const& display_layout, shared_ptr<PersistentSurfaceStore> const& persistent_surface_store, WindowManagementPolicyBuilder const& build) : ... policy(build(WindowManagerTools{this})), workspace_policy{find_workspace_policy(policy)}, auto find_workspace_policy(unique_ptr<WindowManagementPolicy> const& policy) -> WorkspacePolicy* { WorkspacePolicy* result = dynamic_cast<WorkspacePolicy*>(policy.get()); if (result) return result; static WorkspacePolicy null_workspace_policy; return &null_workspace_policy; } ... BasicWindowManager::BasicWindowManager( FocusController* focus_controller, shared_ptr<DisplayLayout> const& display_layout, shared_ptr<PersistentSurfaceStore> const& persistent_surface_store, WindowManagementPolicyBuilder const& build) : ... policy(build(WindowManagerTools{this})), workspace_policy{find_workspace_policy(policy)},
  • 63. ABI stability Checking Semi-automated update of linker script debian/libmiral.symbols abidiff Maintaining Discipline Toolchain tricks
  • 64. Concept Proven MirAL Has stable ABI Should be usable outside Canonical Supports writing a “shell” or “Desktop Enviroment” Works on desktop, tablet, phone or IOT Comes with worked examples
  • 65. Repurposing MirAL “at work” we started thinking about desktop Need to consolidate basic window management There’s a lot of stuff all shells need Unity8 is the “wrong place” Should be somewhere useful to all Mir servers This was a job for MirAL
  • 66. A bit more about QtMir QtMir is an adapter between Qt and Mir Used by Unity8 Might be usable by other Qt based shells We needed to migrate QtMir to use MirAL While not stopping QtMir development
  • 69. MirAL became my “day job” We copied the QtMir source into MirAL We set about Removing Mir dependencies and using MirAL Using MirAL window management with Qt Testing and fixing MirAL window management Keeping our QtMir copy in step We proved the concept
  • 71. Unity8/QtMir on MirAL (work in progress) libmirserver miral-shell App libmiral miral-kiosk Unity8 QtMir pseudo “miral”
  • 72. Reversal of roles Instead of QtMir code in MirAL We have “namespace miral” code in QtMir Functionality that belongs in libmiral But we need experience before committing to API & ABI
  • 73. Shells based on MirAL Unity8 “convergent” shell for... desktop Phone & tablet miral-kiosk Very basic WM for... Dragonboard Raspberry Pi IOT
  • 74. Shells based on MirAL Unity8 “convergent” shell for... desktop Phone & tablet miral-kiosk Very basic WM for... Dragonboard Raspberry Pi IOT miral-shell The canonical example Testing toolkits miral-shell --window-manager tiling Demonstrate a different WM policy
  • 75. Reduced debt MirAL releases A few hours work Within a day elapsed No “downstreams” in the silo Can deliver features often Mir releases only need MirAL in the silo
  • 76. MirAL and toolkits Thinking about desktop also means toolkits qtubuntu, gtk-mir, SDL2, … These hadn’t been proven against a real Mir server MirAL had the necessary support This was another job for MirAL
  • 77. Using MirAL to test window management $ sudo apt install miral-examples $ miral-app --window-management-trace $ <toolkit based app> $ sudo apt install xmir $ miral-xrun <X11 based app>
  • 78. Using MirAL to develop shells $ sudo apt install libmiral-dev $ pkg-config --cflags miral A “shell” or “desktop environment” That works on desktop, phone or IOT That works with GTK++, Qt and SDL applications That works with Xmir
  • 79. An unintended spin-off libmirclientcpp-dev A C++ wrapper for the Mir client API RAII Function chaining
  • 80. Summary We incurred technical debt It didn’t solve itself But with a bit of imagination we found a way Hopefully you found this useful

Editor's Notes

  1. So here&amp;apos;s the high-level configuration abstraction. Essentially it means there are five “trees” of objects that need to be built.