Taking the mirclient out of MirAL

Taking the mirclient out of MirAL

Our MirAL library was originally written in a very different environment to the one it exists in today. We had a far too rapidly changing mirserver ABI and a stable mirclient ABI and wanted to provide a way for shell authors to write shells against a stable ABI.

As mirclient was ABI stable, we freely used parts of that API in writing MirAL and shells using MirAL. But the time has come to say “goodbye” to mirclient and that affects MirAL.

We need to think through what needs doing, what could be done and how best to approach it. This post is just the start of that discussion.

What mirclient ABIs does MirAL use depend on?

To find out the nature of the problem I ran a script to pick out the #include dependencies from the build scripts of our example shells (see “Notes” below) and counted the headers used from mirclient, this turns out to be over half the headers exposed by mirclient:

$ grep include/client/ deps.tmp | wc -l
37
$ find include/client/ -name \*.h | wc -l
63

Dropping mirclient means that these headers (and the ABIs they describe) will be unavailable. We don’t want to break things, so we need to plan a way forwards.

One approach is to cut down mirclient to those ABIs that are used by shells (although that in itself is breaking the mirclient ABI). More attractive is the option of reworking the MirAL API and providing a migration path.

What else does MirAL API expose?

There are also a few headers from the mircore library:

$ grep include/core/ deps.tmp | wc -l
18
$ find include/core/ -name \*.h | wc -l
21

But mircore is ABI stable, and not being dropped. So that’s not a problem.

While we’re changing the MirAL ABI, what else?

If we’re going to be breaking the MirAL ABI, are there other changes we should consider at the same time? We’ve learnt a lot about what the Mir server API should have looked like from MirAL, but some of the choices made are worth revisiting in the light of experience.

  1. Because the the API evolved from the existing interfaces, there are functions that no longer make sense. In particular, mutators should not be needed for (e.g. WindowInfo) as changes ought to be applied via WindowSpecification and WindowManagerTools::modify_window().

  2. There are functions that no longer serve a sane purpose. Some examples:

    • ApplicationInfo::add_window()/remove_window();
    • WindowInfo::add_child()/remove_child(); and,
    • WindowManagerTools::force_close()
  3. The separation of WindowInfo & Window and ApplicationInfo & Application no longer makes sense.

  4. Threading model & two-stage commit.

    • Should we replace calling into the WMP from assorted threads with a polling model?
    • There are a number of interactions that require the client to take action to finalize. E.g. resizing wants a resized buffer.
  5. The current MirAL API is very C++ specific. It uses language mechanisms (templates, CopyAssign semantics, etc.) in a way that is hard to access from other languages.

Migration path

We need a way to ensure downstream projects can migrate at their ease. So we’re not going to break stuff until we know where we’re going and how to migrate downstream code.

Things we know we can do right now are:

  1. Remove headers for ABIs we won’t support going forwards from packaging. That includes all of the mirserver headers, and half of the mirclient headers.
  2. Mark as “deprecated” any functions we plan to drop.

After that we may want to build a new API (and ABI) in parallel to the existing one and provide guidance for switching over at compile time.


Notes

The full list of #includes used by our example shells

$ grep "\.h$" -R cmake-build-debug/examples/miral-kiosk/ cmake-build-debug/examples/miral-shell/ cmake-build-debug/examples/example-server-lib/ | grep -v /usr/ | sed -n "s/.*\.\.\(.*\.h\)$/\1/p" | sort | uniq | grep -v /examples/
/include/client/mir/client/connection.h
/include/client/mir_toolkit/client_types.h
/include/client/mir_toolkit/event.h
/include/client/mir_toolkit/events/event.h
/include/client/mir_toolkit/events/input_configuration_event.h
/include/client/mir_toolkit/events/input_device_state_event.h
/include/client/mir_toolkit/events/input/input_event.h
/include/client/mir_toolkit/events/input/keyboard_event.h
/include/client/mir_toolkit/events/input/mir_toolkit/events/event.h
/include/client/mir_toolkit/events/input/mir_toolkit/events/input/keyboard_event.h
/include/client/mir_toolkit/events/input/mir_toolkit/events/input/pointer_event.h
/include/client/mir_toolkit/events/input/mir_toolkit/events/input/touch_event.h
/include/client/mir_toolkit/events/input/mir_toolkit/mir_input_device_types.h
/include/client/mir_toolkit/events/input/pointer_event.h
/include/client/mir_toolkit/events/input/touch_event.h
/include/client/mir_toolkit/events/keymap_event.h
/include/client/mir_toolkit/events/mir_toolkit/common.h
/include/client/mir_toolkit/events/mir_toolkit/events/input_configuration_event.h
/include/client/mir_toolkit/events/mir_toolkit/events/input_device_state_event.h
/include/client/mir_toolkit/events/mir_toolkit/events/input/input_event.h
/include/client/mir_toolkit/events/mir_toolkit/events/keymap_event.h
/include/client/mir_toolkit/events/mir_toolkit/events/orientation_event.h
/include/client/mir_toolkit/events/mir_toolkit/events/prompt_session_event.h
/include/client/mir_toolkit/events/mir_toolkit/events/resize_event.h
/include/client/mir_toolkit/events/mir_toolkit/events/surface_event.h
/include/client/mir_toolkit/events/mir_toolkit/events/surface_output_event.h
/include/client/mir_toolkit/events/mir_toolkit/events/window_event.h
/include/client/mir_toolkit/events/mir_toolkit/events/window_output_event.h
/include/client/mir_toolkit/events/orientation_event.h
/include/client/mir_toolkit/events/prompt_session_event.h
/include/client/mir_toolkit/events/resize_event.h
/include/client/mir_toolkit/events/surface_event.h
/include/client/mir_toolkit/events/surface_output_event.h
/include/client/mir_toolkit/events/window_event.h
/include/client/mir_toolkit/events/window_output_event.h
/include/client/mir_toolkit/mir_connection.h
/include/client/mir_toolkit/mir_toolkit/events/event.h
/include/core/mir/fatal.h
/include/core/mir/fd.h
/include/core/mir/geometry/dimensions.h
/include/core/mir/geometry/displacement.h
/include/core/mir/geometry/mir/geometry/dimensions.h
/include/core/mir/geometry/mir/geometry/point.h
/include/core/mir/geometry/mir/geometry/rectangle.h
/include/core/mir/geometry/mir/geometry/size.h
/include/core/mir/geometry/point.h
/include/core/mir/geometry/rectangle.h
/include/core/mir/geometry/rectangles.h
/include/core/mir/geometry/size.h
/include/core/mir/int_wrapper.h
/include/core/mir/mir/fatal.h
/include/core/mir/optional_value.h
/include/core/mir_toolkit/common.h
/include/core/mir_toolkit/deprecations.h
/include/core/mir_toolkit/mir_input_device_types.h
/include/miral/miral/append_event_filter.h
/include/miral/miral/application_authorizer.h
/include/miral/miral/application.h
/include/miral/miral/application_info.h
/include/miral/miral/canonical_window_manager.h
/include/miral/miral/command_line_option.h
/include/miral/miral/cursor_theme.h
/include/miral/miral/debug_extension.h
/include/miral/miral/display_configuration.h
/include/miral/miral/display_configuration_option.h
/include/miral/miral/external_client.h
/include/miral/miral/internal_client.h
/include/miral/miral/keymap.h
/include/miral/miral/lambda_as_function.h
/include/miral/miral/minimal_window_manager.h
/include/miral/miral/miral/application.h
/include/miral/miral/miral/window.h
/include/miral/miral/miral/window_specification.h
/include/miral/miral/mir/optional_value.h
/include/miral/miral/runner.h
/include/miral/miral/set_window_management_policy.h
/include/miral/miral/wayland_extensions.h
/include/miral/miral/window.h
/include/miral/miral/window_info.h
/include/miral/miral/window_management_options.h
/include/miral/miral/window_management_policy.h
/include/miral/miral/window_manager_tools.h
/include/miral/miral/window_specification.h
/include/miral/miral/zone.h
2 Likes

Errata

It doesn’t affect the thrust of the article, but I noticed a few non-existent files there. After filtering through ls 2> /dev/null:

./include/client/mir/client/connection.h
./include/client/mir_toolkit/client_types.h
./include/client/mir_toolkit/event.h
./include/client/mir_toolkit/events/event.h
./include/client/mir_toolkit/events/input_configuration_event.h
./include/client/mir_toolkit/events/input_device_state_event.h
./include/client/mir_toolkit/events/input/input_event.h
./include/client/mir_toolkit/events/input/keyboard_event.h
./include/client/mir_toolkit/events/input/pointer_event.h
./include/client/mir_toolkit/events/input/touch_event.h
./include/client/mir_toolkit/events/keymap_event.h
./include/client/mir_toolkit/events/orientation_event.h
./include/client/mir_toolkit/events/prompt_session_event.h
./include/client/mir_toolkit/events/resize_event.h
./include/client/mir_toolkit/events/surface_event.h
./include/client/mir_toolkit/events/surface_output_event.h
./include/client/mir_toolkit/events/window_event.h
./include/client/mir_toolkit/events/window_output_event.h
./include/client/mir_toolkit/mir_connection.h
./include/core/mir/fatal.h
./include/core/mir/fd.h
./include/core/mir/geometry/dimensions.h
./include/core/mir/geometry/displacement.h
./include/core/mir/geometry/point.h
./include/core/mir/geometry/rectangle.h
./include/core/mir/geometry/rectangles.h
./include/core/mir/geometry/size.h
./include/core/mir/int_wrapper.h
./include/core/mir/optional_value.h
./include/core/mir_toolkit/common.h
./include/core/mir_toolkit/deprecations.h
./include/core/mir_toolkit/mir_input_device_types.h
./include/miral/miral/append_event_filter.h
./include/miral/miral/application_authorizer.h
./include/miral/miral/application.h
./include/miral/miral/application_info.h
./include/miral/miral/canonical_window_manager.h
./include/miral/miral/command_line_option.h
./include/miral/miral/cursor_theme.h
./include/miral/miral/debug_extension.h
./include/miral/miral/display_configuration.h
./include/miral/miral/display_configuration_option.h
./include/miral/miral/external_client.h
./include/miral/miral/internal_client.h
./include/miral/miral/keymap.h
./include/miral/miral/lambda_as_function.h
./include/miral/miral/minimal_window_manager.h
./include/miral/miral/runner.h
./include/miral/miral/set_window_management_policy.h
./include/miral/miral/wayland_extensions.h
./include/miral/miral/window.h
./include/miral/miral/window_info.h
./include/miral/miral/window_management_options.h
./include/miral/miral/window_management_policy.h
./include/miral/miral/window_manager_tools.h
./include/miral/miral/window_specification.h
./include/miral/miral/zone.h

What mirclient ABIs does MirAL use depend on?

$ grep include/client/ deps2.tmp | wc -l
19
$ find include/client/ -name \*.h | wc -l
63

What else does MirAL API expose?

$ grep include/core/ deps2.tmp | wc -l
13
$ find include/core/ -name \*.h | wc -l
21

MirAL 3.0 - current status

Just an update following the initial pass at cleaning up for MirAL 3.0:

$ grep "\.h$" -R cmake-build-debug/examples/miral-kiosk/ cmake-build-debug/examples/miral-shell/ cmake-build-debug/examples/example-server-lib/ | grep -v /usr/ | grep /include/client/ | sed -n "s~.*\.\./\(.*\.h\)$~\1~p" | sort | uniq | xargs ls 2> /dev/null
include/client/mir_toolkit/event.h
include/client/mir_toolkit/events/event.h
include/client/mir_toolkit/events/input_configuration_event.h
include/client/mir_toolkit/events/input_device_state_event.h
include/client/mir_toolkit/events/input/input_event.h
include/client/mir_toolkit/events/input/keyboard_event.h
include/client/mir_toolkit/events/input/pointer_event.h
include/client/mir_toolkit/events/input/touch_event.h
include/client/mir_toolkit/events/keymap_event.h
include/client/mir_toolkit/events/orientation_event.h
include/client/mir_toolkit/events/prompt_session_event.h
include/client/mir_toolkit/events/resize_event.h
include/client/mir_toolkit/events/surface_event.h
include/client/mir_toolkit/events/surface_output_event.h
include/client/mir_toolkit/events/window_event.h
include/client/mir_toolkit/events/window_output_event.h

That’s 16 “events” files from libmirclient-dev still exposed by libmiral-dev. And that’s essentially just:

miral/append_event_filter.h:#include <mir_toolkit/event.h>
miral/window_management_policy.h:#include <mir_toolkit/event.h>

Replacing (or hiding through a layer of indirection) the use of mirclient APIs for handling input events in the MirAL interface ought to enable us to remove these.

And now (after landing https://github.com/MirServer/mir/pull/1476) MirAL based code has no direct dependencies on libmirclient.

But may need a new header and namespace directive to build. Vis:

#include <miral/toolkit_event.h>
...
using namespace miral::toolkit;