diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp index be90eee4d2..6e2e88b5c6 100644 --- a/include/modules/keyboard_state.hpp +++ b/include/modules/keyboard_state.hpp @@ -1,49 +1,60 @@ #pragma once -#include -#include +#include +#include +#include +#include +#include +#include -#include -#include +#include +#include #include "AModule.hpp" #include "bar.hpp" -#include "util/sleeper_thread.hpp" - -extern "C" { -#include -#include -} +#include "client.hpp" +#include "util/json.hpp" namespace waybar::modules { class KeyboardState : public AModule { public: - KeyboardState(const std::string&, const waybar::Bar&, const Json::Value&); - virtual ~KeyboardState(); - auto update() -> void override; + KeyboardState(const std::string &, const waybar::Bar &, const Json::Value &); + ~KeyboardState(); + void update(); private: - auto tryAddDevice(const std::string&) -> void; - Gtk::Box box_; + Gtk::Label layout_label_; Gtk::Label numlock_label_; Gtk::Label capslock_label_; Gtk::Label scrolllock_label_; + std::string layout_format_; std::string numlock_format_; std::string capslock_format_; std::string scrolllock_format_; - const std::chrono::seconds interval_; + std::string tooltip_format_ = ""; std::string icon_locked_; std::string icon_unlocked_; - std::string devices_path_; + bool hide_single_; - struct libinput* libinput_; - std::unordered_map libinput_devices_; - std::set binding_keys; + struct wl_seat *seat_; + struct wl_keyboard *keyboard_; + struct xkb_context *xkb_context_; + struct xkb_state *xkb_state_; + struct xkb_keymap *xkb_keymap_; + struct rxkb_context *rxkb_context_; - util::SleeperThread libinput_thread_, hotplug_thread_; + void update_layout(std::string full_name); + void update_led(Gtk::Label *, std::string format, std::string name, bool locked); + + public: + void register_seat(struct wl_registry *, uint32_t name, uint32_t version); + void handle_keymap(uint32_t format, int32_t fd, uint32_t size); + void handle_modifiers(uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group); + void handle_seat_capabilities(unsigned int caps); }; } // namespace waybar::modules diff --git a/meson.build b/meson.build index b3e6ca0f63..f9886bd605 100644 --- a/meson.build +++ b/meson.build @@ -88,8 +88,8 @@ pipewire = dependency('libpipewire-0.3', required: get_option('pipewire')) playerctl = dependency('playerctl', version : ['>=2.0.0'], required: get_option('mpris')) libpulse = dependency('libpulse', required: get_option('pulseaudio')) libudev = dependency('libudev', required: get_option('libudev')) -libevdev = dependency('libevdev', required: get_option('libevdev')) libmpdclient = dependency('libmpdclient', required: get_option('mpd')) +xkbcommon = dependency('xkbcommon') xkbregistry = dependency('xkbregistry') libjack = dependency('jack', required: get_option('jack')) libwireplumber = dependency('wireplumber-0.5', required: get_option('wireplumber')) @@ -167,6 +167,7 @@ src_files = files( 'src/modules/disk.cpp', 'src/modules/idle_inhibitor.cpp', 'src/modules/image.cpp', + 'src/modules/keyboard_state.cpp', 'src/modules/load.cpp', 'src/modules/temperature.cpp', 'src/modules/user.cpp', @@ -193,6 +194,7 @@ man_files = files( 'man/waybar-disk.5.scd', 'man/waybar-idle-inhibitor.5.scd', 'man/waybar-image.5.scd', + 'man/waybar-keyboard-state.5.scd', 'man/waybar-states.5.scd', 'man/waybar-menu.5.scd', 'man/waybar-temperature.5.scd', @@ -453,13 +455,6 @@ if libudev.found() and (is_linux or libepoll.found()) ) endif -if libevdev.found() and (is_linux or libepoll.found()) and libinput.found() and (is_linux or libinotify.found()) - add_project_arguments('-DHAVE_LIBEVDEV', language: 'cpp') - add_project_arguments('-DHAVE_LIBINPUT', language: 'cpp') - src_files += files('src/modules/keyboard_state.cpp') - man_files += files('man/waybar-keyboard-state.5.scd') -endif - if libmpdclient.found() add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp') src_files += files( @@ -548,11 +543,11 @@ executable( libinotify, libepoll, libmpdclient, - libevdev, gtk_layer_shell, libsndio, tz_dep, - xkbregistry, + xkbcommon, + xkbregistry, cava, libgps ], diff --git a/src/factory.cpp b/src/factory.cpp index 204081066c..7f888be377 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -73,9 +73,6 @@ #include "modules/backlight.hpp" #include "modules/backlight_slider.hpp" #endif -#ifdef HAVE_LIBEVDEV -#include "modules/keyboard_state.hpp" -#endif #ifdef HAVE_GAMEMODE #include "modules/gamemode.hpp" #endif @@ -120,6 +117,7 @@ #include "modules/cffi.hpp" #include "modules/custom.hpp" #include "modules/image.hpp" +#include "modules/keyboard_state.hpp" #include "modules/temperature.hpp" #include "modules/user.hpp" @@ -277,6 +275,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, if (ref == "image") { return new waybar::modules::Image(id, config_[name]); } + if (ref == "keyboard-state") { + return new waybar::modules::KeyboardState(id, bar_, config_[name]); + } #ifdef HAVE_DBUSMENU if (ref == "tray") { return new waybar::modules::SNI::Tray(id, bar_, config_[name]); @@ -295,11 +296,6 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name, return new waybar::modules::BacklightSlider(id, config_[name]); } #endif -#ifdef HAVE_LIBEVDEV - if (ref == "keyboard-state") { - return new waybar::modules::KeyboardState(id, bar_, config_[name]); - } -#endif #ifdef HAVE_LIBPULSE if (ref == "pulseaudio") { return new waybar::modules::Pulseaudio(id, config_[name]); diff --git a/src/modules/keyboard_state.cpp b/src/modules/keyboard_state.cpp index 18ce0a7c4b..f69b1830ec 100644 --- a/src/modules/keyboard_state.cpp +++ b/src/modules/keyboard_state.cpp @@ -1,89 +1,146 @@ #include "modules/keyboard_state.hpp" -#include +#include #include -#include - -#include - -extern "C" { -#include -#include -#include -#include -#include -#include -#include -#include -} +#include +#include +#include -class errno_error : public std::runtime_error { - public: - int code; - errno_error(int code, const std::string& msg) - : std::runtime_error(getErrorMsg(code, msg.c_str())), code(code) {} - errno_error(int code, const char* msg) : std::runtime_error(getErrorMsg(code, msg)), code(code) {} - - private: - static auto getErrorMsg(int err, const char* msg) -> std::string { - std::string error_msg{msg}; - error_msg += ": "; - -#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 32) - // strerrorname_np gets the error code's name; it's nice to have, but it's a recent GNU - // extension - const auto errno_name = strerrorname_np(err); - error_msg += errno_name; - error_msg += " "; -#endif - - const auto errno_str = strerror(err); - error_msg += errno_str; - - return error_msg; - } -}; +#include +#include +#include +#include +#include +#include +#include -auto openFile(const std::string& path, int flags) -> int { - int fd = open(path.c_str(), flags); - if (fd < 0) { - if (errno == EACCES) { - throw errno_error(errno, "Can't open " + path + " (are you in the input group?)"); - } else { - throw errno_error(errno, "Can't open " + path); +#include "gdkmm/general.h" +#include "glibmm/error.h" +#include "glibmm/refptr.h" +#include "util/format.hpp" +#include "util/rewrite_string.hpp" +#include "util/string.hpp" + +namespace waybar::modules { + +std::string maybe_str(const char *s) { return s ? std::string(s) : ""; } + +std::string get_layout_name(struct xkb_keymap *keymap, struct xkb_state *state) { + xkb_layout_index_t n = xkb_keymap_num_layouts(keymap); + xkb_layout_index_t i; + + for (i = 0; i < n; i++) { + if (xkb_state_layout_index_is_active(state, i, XKB_STATE_LAYOUT_EFFECTIVE)) { + return std::string(xkb_keymap_layout_get_name(keymap, i)); } } - return fd; + return ""; } -auto closeFile(int fd) -> void { - int res = close(fd); - if (res < 0) { - throw errno_error(errno, "Can't close file"); +struct rxkb_layout *get_layout(struct rxkb_context *context, std::string full_name) { + if (full_name != "") { + struct rxkb_layout *layout = rxkb_layout_first(context); + + while (layout) { + if (maybe_str(rxkb_layout_get_description(layout)) == full_name) { + return layout; + } + layout = rxkb_layout_next(layout); + } } + + return nullptr; } -auto openDevice(int fd) -> libevdev* { - libevdev* dev; - int err = libevdev_new_from_fd(fd, &dev); - if (err < 0) { - throw errno_error(-err, "Can't create libevdev device"); +std::string country_flag(std::string short_name) { + if (short_name.size() != 2) return ""; + unsigned char result[] = "\xf0\x9f\x87\x00\xf0\x9f\x87\x00"; + result[3] = short_name[0] + 0x45; + result[7] = short_name[1] + 0x45; + // Check if both emojis are in A-Z symbol bounds + if (result[3] < 0xa6 || result[3] > 0xbf) return ""; + if (result[7] < 0xa6 || result[7] > 0xbf) return ""; + return std::string{reinterpret_cast(result)}; +} + +static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, + int32_t fd, uint32_t size) { + static_cast(data)->handle_keymap(format, fd, size); +} + +static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, + struct wl_surface *surface, struct wl_array *keys) { + /* nop */ +} + +static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, + struct wl_surface *surface) { + /* nop */ +} + +static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, + uint32_t time, uint32_t key, uint32_t _state) { + /* nop */ +} + +static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group) { + static_cast(data)->handle_modifiers(mods_depressed, mods_latched, mods_locked, + group); +} + +static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, + int32_t delay) { + /* nop */ +} + +static const struct wl_keyboard_listener keyboard_impl = { + .keymap = keyboard_keymap, + .enter = keyboard_enter, + .leave = keyboard_leave, + .key = keyboard_key, + .modifiers = keyboard_modifiers, + .repeat_info = keyboard_repeat_info, +}; + +static void seat_capabilities(void *data, struct wl_seat *wl_seat, unsigned int caps) { + static_cast(data)->handle_seat_capabilities(caps); +} + +static void seat_name(void *data, struct wl_seat *wl_seat, const char *name) { /* nop */ } + +const struct wl_seat_listener seat_impl = { + .capabilities = seat_capabilities, + .name = seat_name, +}; + +static void handle_global(void *data, struct wl_registry *registry, uint32_t name, + const char *interface, uint32_t version) { + if (std::strcmp(interface, wl_seat_interface.name) == 0) { + static_cast(data)->register_seat(registry, name, version); } - return dev; } -auto supportsLockStates(const libevdev* dev) -> bool { - return libevdev_has_event_type(dev, EV_LED) && libevdev_has_event_code(dev, EV_LED, LED_NUML) && - libevdev_has_event_code(dev, EV_LED, LED_CAPSL) && - libevdev_has_event_code(dev, EV_LED, LED_SCROLLL); +static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + /* nop */ } -waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, - const Json::Value& config) - : AModule(config, "keyboard-state", id, false, !config["disable-scroll"].asBool()), +static const wl_registry_listener registry_listener_impl = {.global = handle_global, + .global_remove = handle_global_remove}; + +KeyboardState::KeyboardState(const std::string &id, const waybar::Bar &bar, + const Json::Value &config) + : waybar::AModule(config, "keyboard-state", id, "{}"), box_(bar.orientation, 0), + layout_label_(""), numlock_label_(""), capslock_label_(""), + scrolllock_label_(""), + layout_format_(config_["format"].isString() ? config_["format"].asString() + : config_["format"]["layout"].isString() + ? config_["format"]["layout"].asString() + : "{short}"), numlock_format_(config_["format"].isString() ? config_["format"].asString() : config_["format"]["numlock"].isString() ? config_["format"]["numlock"].asString() @@ -96,27 +153,25 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& : config_["format"]["scrolllock"].isString() ? config_["format"]["scrolllock"].asString() : "{name} {icon}"), - interval_( - std::chrono::seconds(config_["interval"].isUInt() ? config_["interval"].asUInt() : 1)), + tooltip_format_(config_["tooltip-format"].isString() ? config_["tooltip-format"].asString() + : ""), icon_locked_(config_["format-icons"]["locked"].isString() ? config_["format-icons"]["locked"].asString() : "locked"), icon_unlocked_(config_["format-icons"]["unlocked"].isString() ? config_["format-icons"]["unlocked"].asString() : "unlocked"), - devices_path_("/dev/input/"), - libinput_(nullptr), - libinput_devices_({}) { - static struct libinput_interface interface = { - [](const char* path, int flags, void* user_data) { return open(path, flags); }, - [](int fd, void* user_data) { close(fd); }}; - if (config_["interval"].isUInt()) { - spdlog::warn("keyboard-state: interval is deprecated"); - } - - libinput_ = libinput_path_create_context(&interface, NULL); - + hide_single_(config["hide-single-layout"].isBool() && config["hide-single-layout"].asBool()), + seat_{nullptr}, + keyboard_{nullptr}, + xkb_context_{nullptr}, + xkb_state_{nullptr}, + xkb_keymap_{nullptr}, + rxkb_context_{nullptr} { box_.set_name("keyboard-state"); + if (config_["layout"].asBool()) { + event_box_.add(box_); + } if (config_["numlock"].asBool()) { numlock_label_.get_style_context()->add_class("numlock"); box_.pack_end(numlock_label_, false, false, 0); @@ -129,197 +184,158 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& scrolllock_label_.get_style_context()->add_class("scrolllock"); box_.pack_end(scrolllock_label_, false, false, 0); } + box_.pack_end(layout_label_, false, false, 0); if (!id.empty()) { box_.get_style_context()->add_class(id); } box_.get_style_context()->add_class(MODULE_CLASS); event_box_.add(box_); - if (config_["device-path"].isString()) { - std::string dev_path = config_["device-path"].asString(); - tryAddDevice(dev_path); - if (libinput_devices_.empty()) { - spdlog::error("keyboard-state: Cannot find device {}", dev_path); - } - } + xkb_context_ = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - auto keys = config_["binding-keys"]; - if (keys.isArray()) { - for (const auto& key : keys) { - if (key.isInt()) { - binding_keys.insert(key.asInt()); - } else { - spdlog::warn("Cannot read key binding {} as int.", key.asString()); - } - } - } else { - binding_keys.insert(KEY_CAPSLOCK); - binding_keys.insert(KEY_NUMLOCK); - binding_keys.insert(KEY_SCROLLLOCK); - } + rxkb_context_ = rxkb_context_new(RXKB_CONTEXT_LOAD_EXOTIC_RULES); + rxkb_context_parse_default_ruleset(rxkb_context_); - DIR* dev_dir = opendir(devices_path_.c_str()); - if (dev_dir == nullptr) { - throw errno_error(errno, "Failed to open " + devices_path_); - } - dirent* ep; - while ((ep = readdir(dev_dir))) { - if (ep->d_type == DT_DIR) continue; - std::string dev_path = devices_path_ + ep->d_name; - tryAddDevice(dev_path); + struct wl_display *display = Client::inst()->wl_display; + struct wl_registry *registry = wl_display_get_registry(display); + + wl_registry_add_listener(registry, ®istry_listener_impl, this); + wl_display_roundtrip(display); + + if (!seat_) { + spdlog::error("Failed to get wayland seat"); + return; } +} + +KeyboardState::~KeyboardState() { + xkb_context_unref(xkb_context_); + rxkb_context_unref(rxkb_context_); +} + +void KeyboardState::update() { + if (xkb_keymap_ && xkb_state_) { + auto full_name = get_layout_name(xkb_keymap_, xkb_state_); + update_layout(full_name); - if (libinput_devices_.empty()) { - throw errno_error(errno, "Failed to find keyboard device"); + int numlock = xkb_state_led_name_is_active(xkb_state_, XKB_LED_NAME_NUM); + update_led(&numlock_label_, numlock_format_, "Num", numlock); + + int capslock = xkb_state_led_name_is_active(xkb_state_, XKB_LED_NAME_CAPS); + update_led(&capslock_label_, capslock_format_, "Caps", capslock); + + int scrolllock = xkb_state_led_name_is_active(xkb_state_, XKB_LED_NAME_SCROLL); + update_led(&scrolllock_label_, scrolllock_format_, "Scroll", scrolllock); } - libinput_thread_ = [this] { - dp.emit(); - while (1) { - struct pollfd fd = {libinput_get_fd(libinput_), POLLIN, 0}; - poll(&fd, 1, -1); - libinput_dispatch(libinput_); - struct libinput_event* event; - while ((event = libinput_get_event(libinput_))) { - auto type = libinput_event_get_type(event); - if (type == LIBINPUT_EVENT_KEYBOARD_KEY) { - auto keyboard_event = libinput_event_get_keyboard_event(event); - auto state = libinput_event_keyboard_get_key_state(keyboard_event); - if (state == LIBINPUT_KEY_STATE_RELEASED) { - uint32_t key = libinput_event_keyboard_get_key(keyboard_event); - if (binding_keys.contains(key)) { - dp.emit(); - } - } - } - libinput_event_destroy(event); - } - } - }; + AModule::update(); +} + +void KeyboardState::update_layout(std::string full_name) { + auto layout = get_layout(rxkb_context_, full_name); + if (!layout) { + return; + } - hotplug_thread_ = [this] { - int fd; - fd = inotify_init(); - if (fd < 0) { - spdlog::error("Failed to initialize inotify: {}", strerror(errno)); + if (hide_single_) { + xkb_layout_index_t n = xkb_keymap_num_layouts(xkb_keymap_); + if (n < 2) { + layout_label_.hide(); return; + } else { + layout_label_.show(); } - inotify_add_watch(fd, devices_path_.c_str(), IN_CREATE | IN_DELETE); - while (1) { - int BUF_LEN = 1024 * (sizeof(struct inotify_event) + 16); - char buf[BUF_LEN]; - int length = read(fd, buf, 1024); - if (length < 0) { - spdlog::error("Failed to read inotify: {}", strerror(errno)); - return; - } - for (int i = 0; i < length;) { - struct inotify_event* event = (struct inotify_event*)&buf[i]; - std::string dev_path = devices_path_ + event->name; - if (event->mask & IN_CREATE) { - // Wait for device setup - int timeout = 10; - while (timeout--) { - try { - int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY); - closeFile(fd); - break; - } catch (const errno_error& e) { - if (e.code == EACCES) { - sleep(1); - } - } - } - tryAddDevice(dev_path); - } else if (event->mask & IN_DELETE) { - auto it = libinput_devices_.find(dev_path); - if (it != libinput_devices_.end()) { - spdlog::info("Keyboard {} has been removed.", dev_path); - libinput_devices_.erase(it); - } - } - i += sizeof(struct inotify_event) + event->len; - } + } + + auto short_name = maybe_str(rxkb_layout_get_name(layout)); + auto variant = maybe_str(rxkb_layout_get_variant(layout)); + auto short_description = maybe_str(rxkb_layout_get_brief(layout)); + + auto display_layout = + trim(fmt::format(fmt::runtime(layout_format_), fmt::arg("short", short_name), + fmt::arg("shortDescription", short_description), fmt::arg("long", full_name), + fmt::arg("variant", variant), fmt::arg("flag", country_flag(short_name)))); + layout_label_.set_markup(display_layout); + + if (tooltipEnabled()) { + if (tooltip_format_ != "") { + auto tooltip_display_layout = trim( + fmt::format(fmt::runtime(tooltip_format_), fmt::arg("short", short_name), + fmt::arg("shortDescription", short_description), fmt::arg("long", full_name), + fmt::arg("variant", variant), fmt::arg("flag", country_flag(short_name)))); + layout_label_.set_tooltip_markup(tooltip_display_layout); } - }; + } } -waybar::modules::KeyboardState::~KeyboardState() { - for (const auto& [_, dev_ptr] : libinput_devices_) { - libinput_path_remove_device(dev_ptr); +void KeyboardState::update_led(Gtk::Label *label, std::string format, std::string name, + bool locked) { + std::string text = + fmt::format(fmt::runtime(format), fmt::arg("icon", locked ? icon_locked_ : icon_unlocked_), + fmt::arg("name", name)); + label->set_markup(text); + + if (locked) { + label->get_style_context()->add_class("locked"); + } else { + label->get_style_context()->remove_class("locked"); } } -auto waybar::modules::KeyboardState::update() -> void { - sleep(0); // Wait for keyboard status change - int numl = 0, capsl = 0, scrolll = 0; - - try { - std::string dev_path; - if (config_["device-path"].isString() && - libinput_devices_.find(config_["device-path"].asString()) != libinput_devices_.end()) { - dev_path = config_["device-path"].asString(); - } else { - dev_path = libinput_devices_.begin()->first; - } - int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY); - auto dev = openDevice(fd); - numl = libevdev_get_event_value(dev, EV_LED, LED_NUML); - capsl = libevdev_get_event_value(dev, EV_LED, LED_CAPSL); - scrolll = libevdev_get_event_value(dev, EV_LED, LED_SCROLLL); - libevdev_free(dev); - closeFile(fd); - } catch (const errno_error& e) { - // ENOTTY just means the device isn't an evdev device, skip it - if (e.code != ENOTTY) { - spdlog::warn(e.what()); - } +void KeyboardState::handle_keymap(uint32_t format, int32_t fd, uint32_t size) { + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + spdlog::error("unknown keymap format {0}", format); + return; + } + char *map_shm = static_cast(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0)); + if (map_shm == MAP_FAILED) { + close(fd); + spdlog::error("unable to initialize keymap shm"); + return; } - struct { - bool state; - Gtk::Label& label; - const std::string& format; - const char* name; - } label_states[] = { - {(bool)numl, numlock_label_, numlock_format_, "Num"}, - {(bool)capsl, capslock_label_, capslock_format_, "Caps"}, - {(bool)scrolll, scrolllock_label_, scrolllock_format_, "Scroll"}, - }; - for (auto& label_state : label_states) { - std::string text; - text = fmt::format(fmt::runtime(label_state.format), - fmt::arg("icon", label_state.state ? icon_locked_ : icon_unlocked_), - fmt::arg("name", label_state.name)); - label_state.label.set_markup(text); - if (label_state.state) { - label_state.label.get_style_context()->add_class("locked"); - } else { - label_state.label.get_style_context()->remove_class("locked"); - } + xkb_keymap_unref(xkb_keymap_); + xkb_state_unref(xkb_state_); + + xkb_keymap_ = xkb_keymap_new_from_string(xkb_context_, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(map_shm, size); + close(fd); + + xkb_state_ = xkb_state_new(xkb_keymap_); + dp.emit(); +} + +void KeyboardState::handle_modifiers(uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { + if (!xkb_state_) { + return; } + xkb_state_update_mask(xkb_state_, mods_depressed, mods_latched, mods_locked, 0, 0, group); + dp.emit(); +} - AModule::update(); +void KeyboardState::handle_seat_capabilities(unsigned int caps) { + if (keyboard_) { + wl_keyboard_release(keyboard_); + keyboard_ = nullptr; + } + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) { + keyboard_ = wl_seat_get_keyboard(seat_); + wl_keyboard_add_listener(keyboard_, &keyboard_impl, this); + } } -auto waybar::modules ::KeyboardState::tryAddDevice(const std::string& dev_path) -> void { - try { - int fd = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY); - auto dev = openDevice(fd); - if (supportsLockStates(dev)) { - spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path); - if (libinput_devices_.find(dev_path) == libinput_devices_.end()) { - auto device = libinput_path_add_device(libinput_, dev_path.c_str()); - libinput_device_ref(device); - libinput_devices_[dev_path] = device; - } - } - libevdev_free(dev); - closeFile(fd); - } catch (const errno_error& e) { - // ENOTTY just means the device isn't an evdev device, skip it - if (e.code != ENOTTY) { - spdlog::warn(e.what()); - } +void KeyboardState::register_seat(struct wl_registry *registry, uint32_t name, uint32_t version) { + if (seat_) { + spdlog::warn("Register seat again although already existing!"); + return; } + version = std::min(version, wl_seat_interface.version); + + seat_ = static_cast(wl_registry_bind(registry, name, &wl_seat_interface, version)); + wl_seat_add_listener(seat_, &seat_impl, this); } + +} /* namespace waybar::modules */