Skip to content

Commit

Permalink
(#620) Enabling keyboard LEDs when the modifier state changes
Browse files Browse the repository at this point in the history
  • Loading branch information
mattkae committed Jul 11, 2024
1 parent ef638b6 commit ccb60fb
Show file tree
Hide file tree
Showing 20 changed files with 966 additions and 15 deletions.
40 changes: 40 additions & 0 deletions include/common/mir/input/keyboard_leds.h
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#ifndef MIR_INPUT_KEYBOARD_LEDS_H
#define MIR_INPUT_KEYBOARD_LEDS_H

#include "mir/flags.h"
#include <cstdint>

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<KeyboardLed>;

}
}

#endif //MIR_INPUT_KEYBOARD_LEDS_H
51 changes: 51 additions & 0 deletions include/platform/mir/input/led_observer_registrar.h
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#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 <memory>

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<LedObserver> 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
1 change: 1 addition & 0 deletions src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}
)

Expand Down
9 changes: 8 additions & 1 deletion src/include/server/mir/default_server_configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,14 @@ class TouchVisualizer;
class CursorImages;
class Seat;
class KeyMapper;
class LedObserverRegistrar;
namespace receiver
{
class XKBMapperRegistrar;
}
}


namespace logging
{
class Logger;
Expand Down Expand Up @@ -315,6 +321,7 @@ class DefaultServerConfiguration : public virtual ServerConfiguration
virtual std::shared_ptr<input::TouchVisualizer> the_touch_visualizer();
virtual std::shared_ptr<input::Seat> the_seat();
virtual std::shared_ptr<input::KeyMapper> the_key_mapper();
virtual std::shared_ptr<input::LedObserverRegistrar> the_led_observer_registrar();

// new input reading related parts:
virtual std::shared_ptr<dispatch::MultiplexingDispatchable> the_input_reading_multiplexer();
Expand Down Expand Up @@ -422,7 +429,7 @@ class DefaultServerConfiguration : public virtual ServerConfiguration
CachedPtr<shell::decoration::Manager> decoration_manager;
CachedPtr<scene::ApplicationNotRespondingDetector> application_not_responding_detector;
CachedPtr<cookie::Authority> cookie_authority;
CachedPtr<input::KeyMapper> key_mapper;
CachedPtr<input::receiver::XKBMapperRegistrar> xkb_mapper_registrar;
std::shared_ptr<ConsoleServices> console_services;
std::shared_ptr<DecorationStrategy> decoration_strategy;

Expand Down
147 changes: 147 additions & 0 deletions src/include/server/mir/input/xkb_mapper_registrar.h
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#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 <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-compose.h>
#include <mutex>
#include <optional>
#include <unordered_map>
#include <unordered_set>

namespace mir
{
namespace input
{

using XKBContextPtr = std::unique_ptr<xkb_context, void(*)(xkb_context*)>;
XKBContextPtr make_unique_context();

using XKBStatePtr = std::unique_ptr<xkb_state, void(*)(xkb_state*)>;
using XKBComposeTablePtr = std::unique_ptr<xkb_compose_table, void(*)(xkb_compose_table*)>;
using XKBComposeStatePtr = std::unique_ptr<xkb_compose_state, void(*)(xkb_compose_state*)>;

namespace receiver
{

class XKBMapperRegistrar : public KeyMapper, public LedObserverRegistrar
{
public:
explicit XKBMapperRegistrar(Executor&);

void set_key_state(MirInputDeviceId id, std::vector<uint32_t> const& key_state) override;
void set_keymap_for_device(MirInputDeviceId id, std::shared_ptr<Keymap> map) override;
void set_keymap_for_all_devices(std::shared_ptr<Keymap> 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<LedObserver> 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<Keymap> new_keymap);
void set_keymap(std::shared_ptr<Keymap> 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<xkb_keysym_t> consumed_keys;
mir::optional_value<std::tuple<xkb_keysym_t,xkb_keysym_t>> last_composed_key;
};

struct XkbMappingState
{
class XkbMappingStateLedRegistrar : public ObserverMultiplexer<LedObserver>
{
public:
explicit XkbMappingStateLedRegistrar(Executor&);
void leds_set(KeyboardLeds leds) override;
};

explicit XkbMappingState(
std::shared_ptr<Keymap> keymap,
std::shared_ptr<xkb_keymap> compiled_keymap,
Executor& executor);
void set_key_state(std::vector<uint32_t> 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<xkb_keysym_t, bool>;
void press_modifier(MirInputEventModifiers mod);
void release_modifier(MirInputEventModifiers mod);

std::shared_ptr<Keymap> const keymap;
std::shared_ptr<xkb_keymap> 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<Keymap> default_keymap;
std::shared_ptr<xkb_keymap> default_compiled_keymap;
XKBComposeTablePtr compose_table;
MirXkbModifiers xkb_modifiers_;
std::optional<MirInputDeviceId> last_device_id;

mir::optional_value<MirInputEventModifiers> modifier_state;
std::unordered_map<MirInputDeviceId, std::unique_ptr<XkbMappingState>> device_mapping;
std::unordered_map<MirInputDeviceId, std::unique_ptr<ComposeState>> device_composing;
};
}
}
}

#endif // MIR_INPUT_RECEIVER_XKB_MAPPER_H_
13 changes: 13 additions & 0 deletions src/platforms/evdev/libinput_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<libinput_led>(led));
}

mie::LibInputDevice::LibInputDevice(std::shared_ptr<mi::InputReport> const& report, LibInputDevicePtr dev)
: report{report}, device_{std::move(dev)}, pointer_pos{0, 0}, button_state{0}
{
Expand Down
6 changes: 5 additions & 1 deletion src/platforms/evdev/libinput_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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<InputReport> const& report, LibInputDevicePtr dev);
Expand All @@ -58,6 +60,8 @@ class LibInputDevice : public input::InputDevice
optional_value<TouchscreenSettings> 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();
Expand Down
1 change: 1 addition & 0 deletions src/server/input/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 16 additions & 5 deletions src/server/input/default_configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -313,7 +314,8 @@ std::shared_ptr<mi::DefaultInputDeviceHub> 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
Expand All @@ -325,13 +327,22 @@ std::shared_ptr<mi::DefaultInputDeviceHub> mir::DefaultServerConfiguration::the_

std::shared_ptr<mi::KeyMapper> mir::DefaultServerConfiguration::the_key_mapper()
{
return key_mapper(
[]()
return xkb_mapper_registrar(
[default_executor=the_main_loop()]()
{
return std::make_shared<mi::receiver::XKBMapper>();
return std::make_shared<mi::receiver::XKBMapperRegistrar>(*default_executor);
});
}

std::shared_ptr<mi::LedObserverRegistrar> mir::DefaultServerConfiguration::the_led_observer_registrar()
{
return xkb_mapper_registrar(
[default_executor=the_main_loop()]()
{
return std::make_shared<mi::receiver::XKBMapperRegistrar>(*default_executor);
});
}

std::shared_ptr<mi::SeatObserver> mir::DefaultServerConfiguration::the_seat_observer()
{
return seat_observer_multiplexer(
Expand Down
Loading

0 comments on commit ccb60fb

Please sign in to comment.