diff --git a/include/common/mir/input/keyboard_leds.h b/include/common/mir/input/keyboard_leds.h
new file mode 100644
index 00000000000..6e62d580e21
--- /dev/null
+++ b/include/common/mir/input/keyboard_leds.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright © Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 2 or 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef MIR_INPUT_KEYBOARD_LEDS_H
+#define MIR_INPUT_KEYBOARD_LEDS_H
+
+#include "mir/flags.h"
+#include
+
+namespace mir
+{
+namespace input
+{
+enum class KeyboardLed : uint32_t
+{
+ caps_lock = (1 << 0),
+ num_lock = (1 << 1),
+ scroll_lock = (1 << 2)
+};
+
+KeyboardLed mir_enable_enum_bit_operators(KeyboardLed);
+using KeyboardLeds = mir::Flags;
+
+}
+}
+
+#endif //MIR_INPUT_KEYBOARD_LEDS_H
diff --git a/include/platform/mir/input/led_observer_registrar.h b/include/platform/mir/input/led_observer_registrar.h
new file mode 100644
index 00000000000..d05b0a2e909
--- /dev/null
+++ b/include/platform/mir/input/led_observer_registrar.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright © Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 or 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef MIR_INPUT_LED_OBSERVER_REGISTER_H
+#define MIR_INPUT_LED_OBSERVER_REGISTER_H
+
+#include "mir/input/keyboard_leds.h"
+#include "mir_toolkit/mir_input_device_types.h"
+#include
+
+namespace mir
+{
+namespace input
+{
+class LedObserver
+{
+public:
+ virtual ~LedObserver() = default;
+ virtual void leds_set(KeyboardLeds leds) = 0;
+};
+
+class LedObserverRegistrar
+{
+public:
+ LedObserverRegistrar() = default;
+ virtual ~LedObserverRegistrar() = default;
+
+ virtual void register_interest(std::weak_ptr const& observer, MirInputDeviceId id) = 0;
+ virtual void unregister_interest(LedObserver const& observer, MirInputDeviceId id) = 0;
+
+protected:
+ LedObserverRegistrar(LedObserverRegistrar const&) = delete;
+ LedObserverRegistrar& operator=(LedObserverRegistrar const&) = delete;
+};
+}
+}
+
+#endif //MIR_INPUT_LED_OBSERVER_REGISTER_H
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 45390f52706..5bf5e15e28a 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -62,6 +62,7 @@ add_library(mircommon SHARED
${PROJECT_SOURCE_DIR}/include/common/mir/input/mir_touchpad_config.h
${PROJECT_SOURCE_DIR}/include/common/mir/input/mir_touchscreen_config.h
${PROJECT_SOURCE_DIR}/include/common/mir/input/mir_keyboard_config.h
+ ${PROJECT_SOURCE_DIR}/include/common/mir/input/keyboard_leds.h
${MIR_COMMON_SOURCES}
)
diff --git a/src/include/server/mir/default_server_configuration.h b/src/include/server/mir/default_server_configuration.h
index fefcaf19cad..f1d05bb7d5b 100644
--- a/src/include/server/mir/default_server_configuration.h
+++ b/src/include/server/mir/default_server_configuration.h
@@ -121,8 +121,14 @@ class TouchVisualizer;
class CursorImages;
class Seat;
class KeyMapper;
+class LedObserverRegistrar;
+namespace receiver
+{
+class XKBMapperRegistrar;
+}
}
+
namespace logging
{
class Logger;
@@ -315,6 +321,7 @@ class DefaultServerConfiguration : public virtual ServerConfiguration
virtual std::shared_ptr the_touch_visualizer();
virtual std::shared_ptr the_seat();
virtual std::shared_ptr the_key_mapper();
+ virtual std::shared_ptr the_led_observer_registrar();
// new input reading related parts:
virtual std::shared_ptr the_input_reading_multiplexer();
@@ -422,7 +429,7 @@ class DefaultServerConfiguration : public virtual ServerConfiguration
CachedPtr decoration_manager;
CachedPtr application_not_responding_detector;
CachedPtr cookie_authority;
- CachedPtr key_mapper;
+ CachedPtr xkb_mapper_registrar;
std::shared_ptr console_services;
std::shared_ptr decoration_strategy;
diff --git a/src/include/server/mir/input/xkb_mapper_registrar.h b/src/include/server/mir/input/xkb_mapper_registrar.h
new file mode 100644
index 00000000000..ff1e4e9f87f
--- /dev/null
+++ b/src/include/server/mir/input/xkb_mapper_registrar.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright © Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 or 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef MIR_INPUT_RECEIVER_XKB_MAPPER_H_
+#define MIR_INPUT_RECEIVER_XKB_MAPPER_H_
+
+#include "mir/input/led_observer_registrar.h"
+#include "mir/input/key_mapper.h"
+#include "mir/input/keymap.h"
+#include "mir/input/keyboard_leds.h"
+#include "mir/optional_value.h"
+#include "mir/events/xkb_modifiers.h"
+#include "mir/observer_multiplexer.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace mir
+{
+namespace input
+{
+
+using XKBContextPtr = std::unique_ptr;
+XKBContextPtr make_unique_context();
+
+using XKBStatePtr = std::unique_ptr;
+using XKBComposeTablePtr = std::unique_ptr;
+using XKBComposeStatePtr = std::unique_ptr;
+
+namespace receiver
+{
+
+class XKBMapperRegistrar : public KeyMapper, public LedObserverRegistrar
+{
+public:
+ explicit XKBMapperRegistrar(Executor&);
+
+ void set_key_state(MirInputDeviceId id, std::vector const& key_state) override;
+ void set_keymap_for_device(MirInputDeviceId id, std::shared_ptr map) override;
+ void set_keymap_for_all_devices(std::shared_ptr map) override;
+ void clear_keymap_for_device(MirInputDeviceId id) override;
+ void clear_all_keymaps() override;
+ void map_event(MirEvent& event) override;
+ MirInputEventModifiers modifiers() const override;
+ MirInputEventModifiers device_modifiers(MirInputDeviceId di) const override;
+ auto xkb_modifiers() const -> MirXkbModifiers override;
+ void register_interest(std::weak_ptr const& observer, MirInputDeviceId id) override;
+ void unregister_interest(LedObserver const& observer, MirInputDeviceId id) override;
+
+protected:
+ XKBMapperRegistrar(XKBMapperRegistrar const&) = delete;
+ XKBMapperRegistrar& operator=(XKBMapperRegistrar const&) = delete;
+
+private:
+ void set_keymap(MirInputDeviceId id, std::shared_ptr new_keymap);
+ void set_keymap(std::shared_ptr new_keymap);
+ void update_modifier();
+
+ std::mutex mutable guard;
+
+ struct ComposeState
+ {
+ ComposeState(XKBComposeTablePtr const& table);
+ xkb_keysym_t update_state(xkb_keysym_t mapped_key, MirKeyboardAction action, std::string& text);
+ private:
+ XKBComposeStatePtr state;
+ std::unordered_set consumed_keys;
+ mir::optional_value> last_composed_key;
+ };
+
+ struct XkbMappingState
+ {
+ class XkbMappingStateLedRegistrar : public ObserverMultiplexer
+ {
+ public:
+ explicit XkbMappingStateLedRegistrar(Executor&);
+ void leds_set(KeyboardLeds leds) override;
+ };
+
+ explicit XkbMappingState(
+ std::shared_ptr keymap,
+ std::shared_ptr compiled_keymap,
+ Executor& executor);
+ void set_key_state(std::vector const& key_state);
+
+ bool update_and_map(MirEvent& event, ComposeState* compose_state);
+ MirInputEventModifiers modifiers() const;
+ auto xkb_modifiers() const -> MirXkbModifiers;
+ void notify_leds_changed();
+ XkbMappingStateLedRegistrar& get_registrar();
+ private:
+ /// Returns a pair containing the keysym for the given scancode and if any XKB modifiers have been changed
+ auto update_state(
+ uint32_t scan_code,
+ MirKeyboardAction direction,
+ ComposeState* compose_state,
+ std::string& text) -> std::pair;
+ void press_modifier(MirInputEventModifiers mod);
+ void release_modifier(MirInputEventModifiers mod);
+
+ std::shared_ptr const keymap;
+ std::shared_ptr const compiled_keymap;
+ XKBStatePtr state;
+ MirInputEventModifiers modifier_state{0};
+ xkb_led_index_t num_led;
+ xkb_led_index_t caps_led;
+ xkb_led_index_t scroll_led;
+ XkbMappingStateLedRegistrar registrar;
+ };
+
+ XkbMappingState* get_keymapping_state(MirInputDeviceId id);
+ ComposeState* get_compose_state(MirInputDeviceId id);
+
+ Executor& executor;
+ XKBContextPtr context;
+ std::shared_ptr default_keymap;
+ std::shared_ptr default_compiled_keymap;
+ XKBComposeTablePtr compose_table;
+ MirXkbModifiers xkb_modifiers_;
+ std::optional last_device_id;
+
+ mir::optional_value modifier_state;
+ std::unordered_map> device_mapping;
+ std::unordered_map> device_composing;
+};
+}
+}
+}
+
+#endif // MIR_INPUT_RECEIVER_XKB_MAPPER_H_
diff --git a/src/platforms/evdev/libinput_device.cpp b/src/platforms/evdev/libinput_device.cpp
index c98ff74b11b..17ed5fc3b9b 100644
--- a/src/platforms/evdev/libinput_device.cpp
+++ b/src/platforms/evdev/libinput_device.cpp
@@ -106,6 +106,19 @@ auto get_axis_source(libinput_event_pointer* pointer) -> MirPointerAxisSource
}
}
+void mie::LibInputDevice::leds_set(KeyboardLeds leds)
+{
+ int led = 0;
+ if (contains(leds, mir::input::KeyboardLed::caps_lock))
+ led |= LIBINPUT_LED_CAPS_LOCK;
+ if (contains(leds, mir::input::KeyboardLed::num_lock))
+ led |= LIBINPUT_LED_NUM_LOCK;
+ if (contains(leds, mir::input::KeyboardLed::scroll_lock))
+ led |= LIBINPUT_LED_SCROLL_LOCK;
+
+ libinput_device_led_update(device(), static_cast(led));
+}
+
mie::LibInputDevice::LibInputDevice(std::shared_ptr const& report, LibInputDevicePtr dev)
: report{report}, device_{std::move(dev)}, pointer_pos{0, 0}, button_state{0}
{
diff --git a/src/platforms/evdev/libinput_device.h b/src/platforms/evdev/libinput_device.h
index 46f53be78b2..205c3faa203 100644
--- a/src/platforms/evdev/libinput_device.h
+++ b/src/platforms/evdev/libinput_device.h
@@ -23,6 +23,8 @@
#include "mir/input/event_builder.h"
#include "mir/input/input_device.h"
#include "mir/input/input_device_info.h"
+#include "mir/input/keyboard_leds.h"
+#include "mir/input/led_observer_registrar.h"
#include "mir/input/touchscreen_settings.h"
#include "mir/geometry/point.h"
@@ -43,7 +45,7 @@ class OutputInfo;
class InputReport;
namespace evdev
{
-class LibInputDevice : public input::InputDevice
+class LibInputDevice : public input::InputDevice, public mir::input::LedObserver
{
public:
LibInputDevice(std::shared_ptr const& report, LibInputDevicePtr dev);
@@ -58,6 +60,8 @@ class LibInputDevice : public input::InputDevice
optional_value get_touchscreen_settings() const override;
void apply_settings(TouchscreenSettings const&) override;
+ void leds_set(KeyboardLeds leds) override;
+
void process_event(libinput_event* event);
::libinput_device* device() const;
::libinput_device_group* group();
diff --git a/src/server/input/CMakeLists.txt b/src/server/input/CMakeLists.txt
index 784b9d0c99f..b6c28e32b67 100644
--- a/src/server/input/CMakeLists.txt
+++ b/src/server/input/CMakeLists.txt
@@ -27,6 +27,7 @@ set(
seat_observer_multiplexer.h
idle_poking_dispatcher.cpp
virtual_input_device.cpp
+ xkb_mapper_registrar.cpp
${PROJECT_SOURCE_DIR}/src/include/server/mir/input/seat_observer.h
${PROJECT_SOURCE_DIR}/src/include/server/mir/input/input_dispatcher.h
${PROJECT_SOURCE_DIR}/src/include/server/mir/input/seat.h
diff --git a/src/server/input/default_configuration.cpp b/src/server/input/default_configuration.cpp
index 5475a24abc4..b6260aa8c06 100644
--- a/src/server/input/default_configuration.cpp
+++ b/src/server/input/default_configuration.cpp
@@ -36,8 +36,9 @@
#include "mir/input/touch_visualizer.h"
#include "mir/input/input_probe.h"
#include "mir/input/platform.h"
-#include "mir/input/xkb_mapper.h"
+#include "mir/input/xkb_mapper_registrar.h"
#include "mir/input/vt_filter.h"
+#include "mir/input/device.h"
#include "mir/options/configuration.h"
#include "mir/options/option.h"
#include "mir/dispatch/multiplexing_dispatchable.h"
@@ -313,7 +314,8 @@ std::shared_ptr mir::DefaultServerConfiguration::the_
the_input_reading_multiplexer(),
the_clock(),
the_key_mapper(),
- the_server_status_listener());
+ the_server_status_listener(),
+ the_led_observer_registrar());
// lp:1675357: KeyRepeatDispatcher must be informed about removed input devices, otherwise
// pressed keys get repeated indefinitely
@@ -325,13 +327,22 @@ std::shared_ptr mir::DefaultServerConfiguration::the_
std::shared_ptr mir::DefaultServerConfiguration::the_key_mapper()
{
- return key_mapper(
- []()
+ return xkb_mapper_registrar(
+ [default_executor=the_main_loop()]()
{
- return std::make_shared();
+ return std::make_shared(*default_executor);
});
}
+std::shared_ptr mir::DefaultServerConfiguration::the_led_observer_registrar()
+{
+ return xkb_mapper_registrar(
+ [default_executor=the_main_loop()]()
+ {
+ return std::make_shared(*default_executor);
+ });
+}
+
std::shared_ptr mir::DefaultServerConfiguration::the_seat_observer()
{
return seat_observer_multiplexer(
diff --git a/src/server/input/default_input_device_hub.cpp b/src/server/input/default_input_device_hub.cpp
index 0cbc31887f8..6725f8edc7c 100644
--- a/src/server/input/default_input_device_hub.cpp
+++ b/src/server/input/default_input_device_hub.cpp
@@ -32,6 +32,8 @@
#include "mir/log.h"
#include "boost/throw_exception.hpp"
+#include "mir/input/led_observer_registrar.h"
+#include "mir/input/key_mapper.h"
#include
#include
@@ -199,13 +201,15 @@ mi::DefaultInputDeviceHub::DefaultInputDeviceHub(
std::shared_ptr const& input_multiplexer,
std::shared_ptr const& clock,
std::shared_ptr const& key_mapper,
- std::shared_ptr const& server_status_listener)
+ std::shared_ptr const& server_status_listener,
+ std::shared_ptr led_observer_registrar)
: seat{seat},
input_dispatchable{input_multiplexer},
device_queue(std::make_shared()),
clock(clock),
key_mapper(key_mapper),
server_status_listener(server_status_listener),
+ led_observer_registrar{std::move(led_observer_registrar)},
device_id_generator{0}
{
input_dispatchable->add_watch(device_queue);
@@ -492,6 +496,11 @@ auto mi::DefaultInputDeviceHub::add_device(std::shared_ptr const& d
seat->add_device(*handle);
dev->start(seat, input_dispatchable);
+ if (auto const observer = std::dynamic_pointer_cast(device))
+ {
+ led_observer_registrar->register_interest(observer, handle->id());
+ }
+
return handle;
}
else
@@ -534,6 +543,11 @@ void mi::DefaultInputDeviceHub::remove_device(std::shared_ptr const
BOOST_THROW_EXCEPTION(std::logic_error("Input device not managed by server"));
}
+ if (auto const observer = std::dynamic_pointer_cast(device))
+ {
+ led_observer_registrar->unregister_interest(*observer, (*pos)->id());
+ }
+
devices.erase(pos, end(devices));
}
diff --git a/src/server/input/default_input_device_hub.h b/src/server/input/default_input_device_hub.h
index 18d236d1334..cf362a92fd5 100644
--- a/src/server/input/default_input_device_hub.h
+++ b/src/server/input/default_input_device_hub.h
@@ -54,6 +54,7 @@ namespace input
{
class InputSink;
class InputDeviceObserver;
+class LedObserverRegistrar;
class DefaultDevice;
class Seat;
class KeyMapper;
@@ -86,7 +87,8 @@ class DefaultInputDeviceHub :
std::shared_ptr const& input_multiplexer,
std::shared_ptr const& clock,
std::shared_ptr const& key_mapper,
- std::shared_ptr const& server_status_listener);
+ std::shared_ptr const& server_status_listener,
+ std::shared_ptr led_observer_registrar);
// InputDeviceRegistry - calls from mi::Platform
auto add_device(std::shared_ptr const& device) -> std::weak_ptr override;
@@ -118,6 +120,7 @@ class DefaultInputDeviceHub :
std::shared_ptr const clock;
std::shared_ptr const key_mapper;
std::shared_ptr const server_status_listener;
+ std::shared_ptr const led_observer_registrar;
ThreadSafeList> observers;
/// Does not guarantee it's own threadsafety, non-const methods should not be called from multiple threads at once
diff --git a/src/server/input/xkb_mapper_registrar.cpp b/src/server/input/xkb_mapper_registrar.cpp
new file mode 100644
index 00000000000..21fd72cfe3e
--- /dev/null
+++ b/src/server/input/xkb_mapper_registrar.cpp
@@ -0,0 +1,569 @@
+/*
+ * Copyright © Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 or 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "mir/input/xkb_mapper_registrar.h"
+#include "mir/input/keymap.h"
+#include "mir/input/keyboard_leds.h"
+#include "mir/events/event_private.h"
+#include "mir/events/event_builders.h"
+
+#include
+
+#include
+
+namespace mi = mir::input;
+namespace mev = mir::events;
+namespace mircv = mi::receiver;
+
+namespace
+{
+
+char const* get_locale_from_environment()
+{
+ char const* loc = getenv("LC_ALL");
+ if (!loc)
+ loc = getenv("LC_CTYPE");
+ if (!loc)
+ loc = getenv("LANG");
+ if (!loc)
+ loc = "C";
+ return loc;
+}
+
+uint32_t constexpr to_xkb_scan_code(uint32_t evdev_scan_code)
+{
+ // xkb scancodes are offset by 8 from evdev scancodes for compatibility with X protocol.
+ return evdev_scan_code + 8;
+}
+
+MirInputEventModifiers modifier_from_xkb_scan_code(uint32_t key)
+{
+ switch(key)
+ {
+ case to_xkb_scan_code(KEY_RIGHTSHIFT): return mir_input_event_modifier_shift_right;
+ case to_xkb_scan_code(KEY_LEFTSHIFT): return mir_input_event_modifier_shift_left;
+ case to_xkb_scan_code(KEY_RIGHTALT): return mir_input_event_modifier_alt_right;
+ case to_xkb_scan_code(KEY_LEFTALT): return mir_input_event_modifier_alt_left;
+ case to_xkb_scan_code(KEY_RIGHTCTRL): return mir_input_event_modifier_ctrl_right;
+ case to_xkb_scan_code(KEY_LEFTCTRL): return mir_input_event_modifier_ctrl_left;
+ case to_xkb_scan_code(KEY_LEFTMETA): return mir_input_event_modifier_meta_left;
+ case to_xkb_scan_code(KEY_RIGHTMETA): return mir_input_event_modifier_meta_right;
+ case to_xkb_scan_code(KEY_CAPSLOCK): return mir_input_event_modifier_caps_lock;
+ case to_xkb_scan_code(KEY_SCREENLOCK): return mir_input_event_modifier_scroll_lock;
+ case to_xkb_scan_code(KEY_NUMLOCK): return mir_input_event_modifier_num_lock;
+ default: return MirInputEventModifiers{0};
+ }
+}
+
+bool is_toggle_modifier(MirInputEventModifiers key)
+{
+ return key == mir_input_event_modifier_caps_lock ||
+ key == mir_input_event_modifier_scroll_lock ||
+ key == mir_input_event_modifier_num_lock;
+}
+
+MirInputEventModifiers expand_modifiers(MirInputEventModifiers modifiers)
+{
+ if (modifiers == 0)
+ return mir_input_event_modifier_none;
+
+ if ((modifiers & mir_input_event_modifier_alt_left) || (modifiers & mir_input_event_modifier_alt_right))
+ modifiers |= mir_input_event_modifier_alt;
+
+ if ((modifiers & mir_input_event_modifier_ctrl_left) || (modifiers & mir_input_event_modifier_ctrl_right))
+ modifiers |= mir_input_event_modifier_ctrl;
+
+ if ((modifiers & mir_input_event_modifier_shift_left) || (modifiers & mir_input_event_modifier_shift_right))
+ modifiers |= mir_input_event_modifier_shift;
+
+ if ((modifiers & mir_input_event_modifier_meta_left) || (modifiers & mir_input_event_modifier_meta_right))
+ modifiers |= mir_input_event_modifier_meta;
+
+ return modifiers;
+}
+
+mi::XKBComposeStatePtr make_unique_compose_state(mi::XKBComposeTablePtr const& table)
+{
+ return {xkb_compose_state_new(table.get(), XKB_COMPOSE_STATE_NO_FLAGS), &xkb_compose_state_unref};
+}
+
+mi::XKBComposeTablePtr make_unique_compose_table_from_locale(mi::XKBContextPtr const& context, std::string const& locale)
+{
+ return {xkb_compose_table_new_from_locale(
+ context.get(),
+ locale.c_str(),
+ XKB_COMPOSE_COMPILE_NO_FLAGS),
+ &xkb_compose_table_unref};
+}
+
+mi::XKBStatePtr make_unique_state(xkb_keymap* keymap)
+{
+ return {xkb_state_new(keymap), xkb_state_unref};
+}
+}
+
+mi::XKBContextPtr mi::make_unique_context()
+{
+ auto context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ if (!context)
+ {
+ fatal_error("Failed to create XKB context");
+ }
+ return {context, &xkb_context_unref};
+}
+
+mircv::XKBMapperRegistrar::XkbMappingState::XkbMappingStateLedRegistrar::XkbMappingStateLedRegistrar(mir::Executor& executor)
+ : ObserverMultiplexer(executor)
+{
+}
+
+void mircv::XKBMapperRegistrar::XkbMappingState::XkbMappingStateLedRegistrar::leds_set(mir::input::KeyboardLeds leds)
+{
+ for_each_observer(&LedObserver::leds_set, leds);
+}
+
+mircv::XKBMapperRegistrar::XKBMapperRegistrar(Executor& executor) :
+ executor(executor),
+ context{make_unique_context()},
+ compose_table{make_unique_compose_table_from_locale(context, get_locale_from_environment())}
+{
+}
+
+void mircv::XKBMapperRegistrar::set_key_state(MirInputDeviceId id, std::vector const& key_state)
+{
+ std::lock_guard lg(guard);
+
+ auto mapping_state = get_keymapping_state(id);
+ if (mapping_state)
+ mapping_state->set_key_state(key_state);
+}
+
+void mircv::XKBMapperRegistrar::update_modifier()
+{
+ modifier_state = mir::optional_value{};
+ xkb_modifiers_ = {};
+ if (!device_mapping.empty())
+ {
+ MirInputEventModifiers new_modifier = 0;
+ for (auto const& mapping_state : device_mapping)
+ {
+ new_modifier |= mapping_state.second->modifiers();
+ auto const device_xkb_modifiers = mapping_state.second->xkb_modifiers();
+ xkb_modifiers_.depressed |= device_xkb_modifiers.depressed;
+ xkb_modifiers_.latched |= device_xkb_modifiers.latched;
+ xkb_modifiers_.locked |= device_xkb_modifiers.locked;
+ if (last_device_id && mapping_state.first == last_device_id.value())
+ {
+ xkb_modifiers_.effective_layout = device_xkb_modifiers.effective_layout;
+ }
+ }
+
+ modifier_state = new_modifier;
+ }
+}
+
+void mircv::XKBMapperRegistrar::map_event(MirEvent& ev)
+{
+ std::lock_guard lg(guard);
+
+ auto type = mir_event_get_type(&ev);
+
+ if (type == mir_event_type_input)
+ {
+ auto input_event = mir_event_get_input_event(&ev);
+ auto input_type = mir_input_event_get_type(input_event);
+ auto device_id = mir_input_event_get_device_id(input_event);
+
+ if (input_type == mir_input_event_type_key)
+ {
+ auto mapping_state = get_keymapping_state(device_id);
+ if (mapping_state)
+ {
+ last_device_id = device_id;
+ auto compose_state = get_compose_state(device_id);
+ if (mapping_state->update_and_map(ev, compose_state))
+ {
+ update_modifier();
+ mapping_state->notify_leds_changed();
+ }
+ }
+
+ auto& key_event = *ev.to_input()->to_keyboard();
+ if (!key_event.xkb_modifiers())
+ {
+ key_event.set_xkb_modifiers(xkb_modifiers_);
+ }
+ }
+ else if (modifier_state.is_set())
+ {
+ mev::set_modifier(ev, expand_modifiers(modifier_state.value()));
+ }
+ }
+}
+
+mircv::XKBMapperRegistrar::XkbMappingState* mircv::XKBMapperRegistrar::get_keymapping_state(MirInputDeviceId id)
+{
+ auto dev_keymap = device_mapping.find(id);
+
+ if (dev_keymap != end(device_mapping))
+ {
+ return dev_keymap->second.get();
+ }
+ if (default_keymap)
+ {
+ auto mapping_state = std::make_unique(
+ default_keymap, default_compiled_keymap, executor);
+ decltype(device_mapping.begin()) insertion_pos;
+ std::tie(insertion_pos, std::ignore) =
+ device_mapping.emplace(
+ std::piecewise_construct,
+ std::forward_as_tuple(id),
+ std::forward_as_tuple(std::move(mapping_state)));
+
+ return insertion_pos->second.get();
+ }
+ return nullptr;
+}
+
+void mircv::XKBMapperRegistrar::set_keymap_for_all_devices(std::shared_ptr new_keymap)
+{
+ set_keymap(new_keymap);
+}
+
+void mircv::XKBMapperRegistrar::set_keymap(std::shared_ptr new_keymap)
+{
+ std::lock_guard lg(guard);
+ default_keymap = std::move(new_keymap);
+ default_compiled_keymap = default_keymap->make_unique_xkb_keymap(context.get());
+ device_mapping.clear();
+}
+
+void mircv::XKBMapperRegistrar::set_keymap_for_device(MirInputDeviceId id, std::shared_ptr new_keymap)
+{
+ set_keymap(id, std::move(new_keymap));
+}
+
+void mircv::XKBMapperRegistrar::set_keymap(MirInputDeviceId id, std::shared_ptr new_keymap)
+{
+ std::lock_guard lg(guard);
+
+ auto compiled_keymap = new_keymap->make_unique_xkb_keymap(context.get());
+ auto mapping_state = std::make_unique(std::move(new_keymap), std::move(compiled_keymap), executor);
+
+ device_mapping.erase(id);
+ device_mapping.emplace(
+ std::piecewise_construct,
+ std::forward_as_tuple(id),
+ std::forward_as_tuple(std::move(mapping_state)));
+}
+
+void mircv::XKBMapperRegistrar::clear_all_keymaps()
+{
+ std::lock_guard lg(guard);
+ default_keymap.reset();
+ device_mapping.clear();
+ update_modifier();
+}
+
+void mircv::XKBMapperRegistrar::clear_keymap_for_device(MirInputDeviceId id)
+{
+ std::lock_guard lg(guard);
+ device_mapping.erase(id);
+ update_modifier();
+}
+
+MirInputEventModifiers mircv::XKBMapperRegistrar::modifiers() const
+{
+ std::lock_guard lg(guard);
+ if (modifier_state.is_set())
+ return expand_modifiers(modifier_state.value());
+ return mir_input_event_modifier_none;
+}
+
+MirInputEventModifiers mircv::XKBMapperRegistrar::device_modifiers(MirInputDeviceId id) const
+{
+ std::lock_guard lg(guard);
+
+ auto it = device_mapping.find(id);
+ if (it == end(device_mapping))
+ return mir_input_event_modifier_none;
+ return expand_modifiers(it->second->modifiers());
+}
+
+auto mircv::XKBMapperRegistrar::xkb_modifiers() const -> MirXkbModifiers
+{
+ std::lock_guard lg(guard);
+ return xkb_modifiers_;
+}
+
+void mircv::XKBMapperRegistrar::register_interest(std::weak_ptr const& observer, MirInputDeviceId id)
+{
+ auto it = device_mapping.find(id);
+ if (it == end(device_mapping))
+ return;
+
+ auto& registrar = it->second->get_registrar();
+ registrar.register_interest(observer);
+}
+
+void mircv::XKBMapperRegistrar::unregister_interest(LedObserver const& observer, MirInputDeviceId id)
+{
+ auto it = device_mapping.find(id);
+ if (it == end(device_mapping))
+ return;
+
+ auto& registrar = it->second->get_registrar();
+ registrar.unregister_interest(observer);
+}
+
+mircv::XKBMapperRegistrar::XkbMappingState::XkbMappingState(
+ std::shared_ptr keymap,
+ std::shared_ptr compiled_keymap,
+ Executor& executor)
+ : keymap{std::move(keymap)},
+ compiled_keymap{std::move(compiled_keymap)},
+ state{make_unique_state(this->compiled_keymap.get())},
+ num_led{xkb_keymap_led_get_index(this->compiled_keymap.get(), XKB_LED_NAME_NUM)},
+ caps_led{xkb_keymap_led_get_index(this->compiled_keymap.get(), XKB_LED_NAME_CAPS)},
+ scroll_led{xkb_keymap_led_get_index(this->compiled_keymap.get(), XKB_LED_NAME_SCROLL)},
+ registrar(executor)
+{
+}
+
+void mircv::XKBMapperRegistrar::XkbMappingState::set_key_state(std::vector const& key_state)
+{
+ modifier_state = mir_input_event_modifier_none;
+ std::unordered_set pressed_codes;
+ std::string t;
+ for (uint32_t scan_code : key_state)
+ {
+ bool already_pressed = pressed_codes.count(scan_code) > 0;
+
+ update_state(to_xkb_scan_code(scan_code),
+ (already_pressed) ? mir_keyboard_action_up : mir_keyboard_action_down,
+ nullptr,
+ t);
+
+ if (already_pressed)
+ pressed_codes.erase(scan_code);
+ else
+ pressed_codes.insert(scan_code);
+ }
+}
+
+bool mircv::XKBMapperRegistrar::XkbMappingState::update_and_map(MirEvent& event, mircv::XKBMapperRegistrar::ComposeState* compose_state)
+{
+ auto& key_ev = *event.to_input()->to_keyboard();
+ uint32_t xkb_scan_code = to_xkb_scan_code(key_ev.scan_code());
+ auto old_state = modifier_state;
+ std::string key_text;
+ auto const update_result = update_state(xkb_scan_code, key_ev.action(), compose_state, key_text);
+ xkb_keysym_t const keysym = update_result.first;
+ bool const xkb_modifiers_updated = update_result.second;
+
+ key_ev.set_keysym(keysym);
+ key_ev.set_text(key_text.c_str());
+ // TODO we should also indicate effective/consumed modifier state to properly
+ // implement short cuts with keys that are only reachable via modifier keys
+ key_ev.set_modifiers(expand_modifiers(modifier_state));
+ key_ev.set_keymap(keymap);
+
+ return old_state != modifier_state || xkb_modifiers_updated;
+}
+
+MirInputEventModifiers mircv::XKBMapperRegistrar::XkbMappingState::modifiers() const
+{
+ return modifier_state;
+}
+
+auto mircv::XKBMapperRegistrar::XkbMappingState::xkb_modifiers() const -> MirXkbModifiers
+{
+ return MirXkbModifiers{
+ xkb_state_serialize_mods(state.get(), XKB_STATE_MODS_DEPRESSED),
+ xkb_state_serialize_mods(state.get(), XKB_STATE_MODS_LATCHED),
+ xkb_state_serialize_mods(state.get(), XKB_STATE_MODS_LOCKED),
+ xkb_state_serialize_layout(state.get(), XKB_STATE_LAYOUT_EFFECTIVE)};
+}
+
+void mircv::XKBMapperRegistrar::XkbMappingState::notify_leds_changed()
+{
+ KeyboardLeds leds;
+ if (xkb_state_led_index_is_active(state.get(), caps_led))
+ leds |= KeyboardLed::caps_lock;
+ if (xkb_state_led_index_is_active(state.get(), num_led))
+ leds |= KeyboardLed::num_lock;
+ if (xkb_state_led_index_is_active(state.get(), scroll_led))
+ leds |= KeyboardLed::scroll_lock;
+
+ registrar.leds_set(leds);
+}
+
+mircv::XKBMapperRegistrar::XkbMappingState::XkbMappingStateLedRegistrar& mircv::XKBMapperRegistrar::XkbMappingState::get_registrar()
+{
+ return registrar;
+}
+
+auto mircv::XKBMapperRegistrar::XkbMappingState::update_state(
+ uint32_t scan_code,
+ MirKeyboardAction action,
+ mircv::XKBMapperRegistrar::ComposeState* compose_state,
+ std::string& text)
+-> std::pair
+{
+ auto keysym = xkb_state_key_get_one_sym(state.get(), scan_code);
+ auto const mod_change = modifier_from_xkb_scan_code(scan_code);
+
+ // Occasionally, we see XKB_KEY_Meta_L where XKB_KEY_Alt_L is correct
+ if (mod_change == mir_input_event_modifier_alt_left)
+ {
+ keysym = XKB_KEY_Alt_L;
+ }
+
+ if(action == mir_keyboard_action_down || action == mir_keyboard_action_repeat)
+ {
+ char buffer[7];
+ // scan code? really? not keysym?
+ xkb_state_key_get_utf8(state.get(), scan_code, buffer, sizeof(buffer));
+ text = buffer;
+ }
+
+ if (compose_state)
+ keysym = compose_state->update_state(keysym, action, text);
+
+ uint32_t mask{0};
+ if (action == mir_keyboard_action_up)
+ {
+ mask = xkb_state_update_key(state.get(), scan_code, XKB_KEY_UP);
+ // TODO get the modifier state from xkbcommon and apply it
+ // for all other modifiers manually track them here:
+ release_modifier(mod_change);
+ }
+ else if (action == mir_keyboard_action_down)
+ {
+ mask = xkb_state_update_key(state.get(), scan_code, XKB_KEY_DOWN);
+ // TODO get the modifier state from xkbcommon and apply it
+ // for all other modifiers manually track them here:
+ press_modifier(mod_change);
+ }
+
+ bool const xkb_modifiers_changed =
+ (mask & XKB_STATE_MODS_DEPRESSED) ||
+ (mask & XKB_STATE_MODS_LATCHED) ||
+ (mask & XKB_STATE_MODS_LOCKED) ||
+ (mask & XKB_STATE_LAYOUT_EFFECTIVE);
+
+ return {keysym, xkb_modifiers_changed};
+}
+
+mircv::XKBMapperRegistrar::ComposeState* mircv::XKBMapperRegistrar::get_compose_state(MirInputDeviceId id)
+{
+ auto dev_compose_state = device_composing.find(id);
+
+ if (dev_compose_state != end(device_composing))
+ {
+ return dev_compose_state->second.get();
+ }
+ if (compose_table)
+ {
+ decltype(device_composing.begin()) insertion_pos;
+ std::tie(insertion_pos, std::ignore) =
+ device_composing.emplace(std::piecewise_construct,
+ std::forward_as_tuple(id),
+ std::forward_as_tuple(std::make_unique(compose_table)));
+
+ return insertion_pos->second.get();
+ }
+ return nullptr;
+}
+
+mircv::XKBMapperRegistrar::ComposeState::ComposeState(XKBComposeTablePtr const& table) :
+ state{make_unique_compose_state(table)}
+{
+}
+
+xkb_keysym_t mircv::XKBMapperRegistrar::ComposeState::update_state(xkb_keysym_t mapped_key, MirKeyboardAction action, std::string& mapped_text)
+{
+ // the state machine only cares about downs
+ if (action == mir_keyboard_action_down)
+ {
+ if (xkb_compose_state_feed(state.get(), mapped_key) == XKB_COMPOSE_FEED_ACCEPTED)
+ {
+ auto result = xkb_compose_state_get_status(state.get());
+ if (result == XKB_COMPOSE_COMPOSED)
+ {
+ char buffer[7];
+ xkb_compose_state_get_utf8(state.get(), buffer, sizeof(buffer));
+ mapped_text = buffer;
+ auto composed_key_sym = xkb_compose_state_get_one_sym(state.get());
+ last_composed_key = std::make_tuple(mapped_key, composed_key_sym);
+ return composed_key_sym;
+ }
+ else if (result == XKB_COMPOSE_COMPOSING)
+ {
+ mapped_text = "";
+ consumed_keys.insert(mapped_key);
+ return XKB_KEY_NoSymbol;
+ }
+ else if (result == XKB_COMPOSE_CANCELLED)
+ {
+ consumed_keys.insert(mapped_key);
+ return XKB_KEY_NoSymbol;
+ }
+ }
+ }
+ else
+ {
+ if (last_composed_key.is_set() &&
+ mapped_key == std::get<0>(last_composed_key.value()))
+ {
+ if (action == mir_keyboard_action_up)
+ {
+ mapped_text = "";
+ return std::get<1>(last_composed_key.consume());
+ }
+ else
+ {
+ // on repeat get the text of the compose state
+ char buffer[7];
+ xkb_compose_state_get_utf8(state.get(), buffer, sizeof(buffer));
+ mapped_text = buffer;
+ return std::get<1>(last_composed_key.value());
+ }
+ }
+ if (consumed_keys.erase(mapped_key))
+ {
+ mapped_text = "";
+ return XKB_KEY_NoSymbol;
+ }
+ }
+
+ return mapped_key;
+}
+
+
+void mircv::XKBMapperRegistrar::XkbMappingState::press_modifier(MirInputEventModifiers mod)
+{
+ if (is_toggle_modifier(mod))
+ modifier_state ^= mod;
+ else
+ modifier_state |= mod;
+}
+
+void mircv::XKBMapperRegistrar::XkbMappingState::release_modifier(MirInputEventModifiers mod)
+{
+ if (!is_toggle_modifier(mod))
+ modifier_state &= ~mod;
+}
diff --git a/src/server/symbols.map b/src/server/symbols.map
index d3875c06e21..5b6e9aaf91a 100644
--- a/src/server/symbols.map
+++ b/src/server/symbols.map
@@ -1344,6 +1344,7 @@ local: *;
MIR_SERVER_INTERNAL_2.18 {
global:
extern "C++" {
+ mir::DefaultServerConfiguration::the_led_observer_registrar*;
mir::DecorationStrategy::?DecorationStrategy*;
mir::DecorationStrategy::DecorationStrategy*;
mir::DecorationStrategy::operator*;
@@ -1352,8 +1353,39 @@ global:
mir::Server::set_the_decoration_strategy*;
mir::Server::the_decoration_strategy*;
mir::Server::the_idle_handler*;
+ mir::input::make_unique_context*;
+ mir::input::receiver::XKBMapperRegistrar::XKBMapperRegistrar*;
+ mir::input::receiver::XKBMapperRegistrar::clear_all_keymaps*;
+ mir::input::receiver::XKBMapperRegistrar::clear_keymap_for_device*;
+ mir::input::receiver::XKBMapperRegistrar::device_modifiers*;
+ mir::input::receiver::XKBMapperRegistrar::map_event*;
+ mir::input::receiver::XKBMapperRegistrar::modifiers*;
+ mir::input::receiver::XKBMapperRegistrar::operator*;
+ mir::input::receiver::XKBMapperRegistrar::register_interest*;
+ mir::input::receiver::XKBMapperRegistrar::set_key_state*;
+ mir::input::receiver::XKBMapperRegistrar::set_keymap_for_all_devices*;
+ mir::input::receiver::XKBMapperRegistrar::set_keymap_for_device*;
+ mir::input::receiver::XKBMapperRegistrar::unregister_interest*;
+ mir::input::receiver::XKBMapperRegistrar::xkb_modifiers*;
mir::shell::IdleHandlerObserver::?IdleHandlerObserver*;
mir::shell::IdleHandlerObserver::IdleHandlerObserver*;
+ non-virtual?thunk?to?mir::DefaultServerConfiguration::the_led_observer_registrar*;
+ non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::clear_all_keymaps*;
+ non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::clear_keymap_for_device*;
+ non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::device_modifiers*;
+ non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::map_event*;
+ non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::modifiers*;
+ non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::register_interest*;
+ non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::set_key_state*;
+ non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::set_keymap_for_all_devices*;
+ non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::set_keymap_for_device*;
+ non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::unregister_interest*;
+ non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::xkb_modifiers*;
+ non-virtual?thunk?to?mir::shell::IdleHandlerObserver::?IdleHandlerObserver*;
+ typeinfo?for?mir::input::receiver::XKBMapperRegistrar;
+ typeinfo?for?mir::shell::IdleHandlerObserver;
+ vtable?for?mir::input::receiver::XKBMapperRegistrar;
+ vtable?for?mir::shell::IdleHandlerObserver;
non-virtual?thunk?to?mir::DecorationStrategy::?DecorationStrategy*;
non-virtual?thunk?to?mir::DefaultServerConfiguration::set_the_decoration_strategy*;
non-virtual?thunk?to?mir::DefaultServerConfiguration::the_decoration_strategy*;
@@ -1366,3 +1398,4 @@ global:
vtable?for?mir::shell::IdleHandlerObserver;
};
} MIR_SERVER_INTERNAL_2.17;
+
diff --git a/tests/include/mir/test/doubles/mock_led_observer_registrar.h b/tests/include/mir/test/doubles/mock_led_observer_registrar.h
new file mode 100644
index 00000000000..1fdd45ef577
--- /dev/null
+++ b/tests/include/mir/test/doubles/mock_led_observer_registrar.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright © Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef MIR_TEST_DOUBLES_LED_OBSERVER_REGISTRAR_H_
+#define MIR_TEST_DOUBLES_LED_OBSERVER_REGISTRAR_H_
+
+#include "mir/input/led_observer_registrar.h"
+
+#include
+
+namespace mir
+{
+namespace test
+{
+namespace doubles
+{
+struct MockLedObserverRegistrar : input::LedObserverRegistrar
+{
+ MOCK_METHOD2(register_interest, void(std::weak_ptr const&, MirInputDeviceId));
+ MOCK_METHOD2(unregister_interest, void(input::LedObserver const&, MirInputDeviceId));
+};
+
+}
+}
+}
+
+#endif
diff --git a/tests/include/mir/test/doubles/mock_libinput.h b/tests/include/mir/test/doubles/mock_libinput.h
index efa51257621..4487f8290d2 100644
--- a/tests/include/mir/test/doubles/mock_libinput.h
+++ b/tests/include/mir/test/doubles/mock_libinput.h
@@ -177,6 +177,7 @@ class MockLibInput
MOCK_METHOD2(libinput_device_config_dwt_set_enabled, libinput_config_status(libinput_device*, libinput_config_dwt_state enable));
MOCK_METHOD1(libinput_device_config_dwt_get_enabled, libinput_config_dwt_state(libinput_device*));
MOCK_METHOD1(libinput_device_config_dwt_get_default_enabled, libinput_config_dwt_state(libinput_device*));
+ MOCK_METHOD2(libinput_device_led_update, void(libinput_device*, libinput_led));
template
PtrT get_next_fake_ptr()
diff --git a/tests/integration-tests/input/test_single_seat_setup.cpp b/tests/integration-tests/input/test_single_seat_setup.cpp
index ae320c5d094..a399eb2d372 100644
--- a/tests/integration-tests/input/test_single_seat_setup.cpp
+++ b/tests/integration-tests/input/test_single_seat_setup.cpp
@@ -26,6 +26,7 @@
#include "mir/test/doubles/mock_touch_visualizer.h"
#include "mir/test/doubles/mock_cursor_listener.h"
#include "mir/test/doubles/mock_input_manager.h"
+#include "mir/test/doubles/mock_led_observer_registrar.h"
#include "mir/test/doubles/mock_seat_report.h"
#include "mir/test/doubles/mock_server_status_listener.h"
#include "mir/test/doubles/mock_scene_session.h"
@@ -41,7 +42,7 @@
#include "mir/scene/session_container.h"
#include "mir/input/device.h"
-#include "mir/input/xkb_mapper.h"
+#include "mir/input/xkb_mapper_registrar.h"
#include "mir/input/device_capability.h"
#include "mir/input/mir_pointer_config.h"
#include "mir/input/mir_touchpad_config.h"
@@ -90,13 +91,14 @@ struct SingleSeatInputDeviceHubSetup : ::testing::Test
NiceMock mock_visualizer;
NiceMock mock_seat_observer;
NiceMock mock_status_listener;
- mi::receiver::XKBMapper key_mapper;
+ mi::receiver::XKBMapperRegistrar key_mapper{mir::immediate_executor};
mir::dispatch::MultiplexingDispatchable multiplexer;
mtd::AdvanceableClock clock;
mtd::MockInputManager mock_input_manager;
ms::SessionContainer session_container;
ms::BroadcastingSessionEventSink session_event_sink;
mtd::FakeDisplayConfigurationObserverRegistrar display_config;
+ NiceMock led_observer_registrar;
mi::BasicSeat seat{mt::fake_shared(mock_dispatcher), mt::fake_shared(mock_visualizer),
mt::fake_shared(mock_cursor_listener), mt::fake_shared(display_config),
mt::fake_shared(key_mapper), mt::fake_shared(clock),
@@ -106,7 +108,8 @@ struct SingleSeatInputDeviceHubSetup : ::testing::Test
mt::fake_shared(multiplexer),
mt::fake_shared(clock),
mt::fake_shared(key_mapper),
- mt::fake_shared(mock_status_listener)};
+ mt::fake_shared(mock_status_listener),
+ mt::fake_shared(led_observer_registrar)};
NiceMock mock_observer;
mi::ConfigChanger changer{
mt::fake_shared(mock_input_manager),
diff --git a/tests/mir_test_doubles/mock_libinput.cpp b/tests/mir_test_doubles/mock_libinput.cpp
index c7970659f71..61e2bb9863e 100644
--- a/tests/mir_test_doubles/mock_libinput.cpp
+++ b/tests/mir_test_doubles/mock_libinput.cpp
@@ -654,6 +654,11 @@ libinput_config_dwt_state libinput_device_config_dwt_get_default_enabled(libinpu
return global_libinput->libinput_device_config_dwt_get_default_enabled(device);
}
+void libinput_device_led_update(libinput_device *device, libinput_led leds)
+{
+ global_libinput->libinput_device_led_update(device, leds);
+}
+
libinput_event* mtd::MockLibInput::setup_touch_event(libinput_device* dev, libinput_event_type type, uint64_t event_time, int slot,
float x, float y, float major, float minor, float pressure, float orientation)
{
diff --git a/tests/unit-tests/input/evdev/test_evdev_device_detection.cpp b/tests/unit-tests/input/evdev/test_evdev_device_detection.cpp
index 32e8214e27c..57f1c63f980 100644
--- a/tests/unit-tests/input/evdev/test_evdev_device_detection.cpp
+++ b/tests/unit-tests/input/evdev/test_evdev_device_detection.cpp
@@ -21,6 +21,8 @@
#include "src/server/report/null_report_factory.h"
#include "mir_test_framework/libinput_environment.h"
+#include "mir/test/doubles/mock_led_observer_registrar.h"
+#include "mir/test/fake_shared.h"
#include
#include
@@ -29,6 +31,8 @@
namespace mtf = mir_test_framework;
namespace mi = mir::input;
namespace mie = mi::evdev;
+namespace mt = mir::test;
+namespace mtd = mt::doubles;
struct EvdevDeviceDetection : public ::testing::TestWithParam>
{
@@ -41,6 +45,7 @@ TEST_P(EvdevDeviceDetection, evaluates_expected_input_class)
auto const& param = GetParam();
auto dev = env.setup_device(std::get<0>(param));
std::shared_ptr lib = mie::make_libinput(nullptr);
+ NiceMock led_observer_registrar;
mie::LibInputDevice device(mir::report::null_input_report(), mie::make_libinput_device(lib, dev));
auto info = device.get_device_info();
EXPECT_THAT(info.capabilities, Eq(std::get<1>(param)));
diff --git a/tests/unit-tests/input/test_default_input_device_hub.cpp b/tests/unit-tests/input/test_default_input_device_hub.cpp
index 29cdef6116d..8a166efd8bb 100644
--- a/tests/unit-tests/input/test_default_input_device_hub.cpp
+++ b/tests/unit-tests/input/test_default_input_device_hub.cpp
@@ -21,6 +21,7 @@
#include "mir/test/doubles/mock_input_seat.h"
#include "mir/test/doubles/mock_event_sink.h"
#include "mir/test/doubles/mock_key_mapper.h"
+#include "mir/test/doubles/mock_led_observer_registrar.h"
#include "mir/test/doubles/mock_server_status_listener.h"
#include "mir/test/doubles/advanceable_clock.h"
#include "mir/test/fake_shared.h"
@@ -67,6 +68,7 @@ MATCHER_P(WithName, name,
struct InputDeviceHubTest : ::testing::Test
{
mir::dispatch::MultiplexingDispatchable multiplexer;
+ NiceMock led_observer_registrar;
NiceMock mock_seat;
NiceMock mock_key_mapper;
NiceMock mock_server_status_listener;
@@ -76,7 +78,8 @@ struct InputDeviceHubTest : ::testing::Test
mt::fake_shared(multiplexer),
mt::fake_shared(clock),
mt::fake_shared(mock_key_mapper),
- mt::fake_shared(mock_server_status_listener)};
+ mt::fake_shared(mock_server_status_listener),
+ mt::fake_shared(led_observer_registrar)};
NiceMock mock_observer;
NiceMock device{"device","dev-1", mi::DeviceCapability::unknown};
NiceMock another_device{"another_device","dev-2", mi::DeviceCapability::keyboard};
diff --git a/tests/unit-tests/input/test_seat_input_device_tracker.cpp b/tests/unit-tests/input/test_seat_input_device_tracker.cpp
index c9418fb0a70..244ae11514b 100644
--- a/tests/unit-tests/input/test_seat_input_device_tracker.cpp
+++ b/tests/unit-tests/input/test_seat_input_device_tracker.cpp
@@ -17,7 +17,7 @@
#include "src/server/input/seat_input_device_tracker.h"
#include "src/server/input/default_event_builder.h"
-#include "mir/input/xkb_mapper.h"
+#include "mir/input/xkb_mapper_registrar.h"
#include "mir/test/doubles/mock_input_dispatcher.h"
#include "mir/test/doubles/mock_cursor_listener.h"
#include "mir/test/doubles/mock_touch_visualizer.h"
@@ -65,7 +65,7 @@ struct SeatInputDeviceTracker : ::testing::Test
mi::DefaultEventBuilder third_device_builder{
third_device,
mt::fake_shared(clock)};
- mi::receiver::XKBMapper mapper;
+ mi::receiver::XKBMapperRegistrar mapper{mir::immediate_executor};
mi::SeatInputDeviceTracker tracker{
mt::fake_shared(mock_dispatcher), mt::fake_shared(mock_visualizer), mt::fake_shared(mock_cursor_listener),
mt::fake_shared(mapper), mt::fake_shared(clock), mt::fake_shared(mock_seat_report)};