diff --git a/include/IModule.hpp b/include/IModule.hpp index 4b3aafef17..bab2329edc 100644 --- a/include/IModule.hpp +++ b/include/IModule.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace waybar { @@ -9,6 +10,7 @@ class IModule { virtual ~IModule() = default; virtual auto update() -> void = 0; virtual operator Gtk::Widget &() = 0; + virtual void handleSeat(struct wl_seat*, uint32_t) {}; Glib::Dispatcher dp; // Hmmm Maybe I should create an abstract class ? }; diff --git a/include/bar.hpp b/include/bar.hpp index a948eaca8c..7e30eb1eb2 100644 --- a/include/bar.hpp +++ b/include/bar.hpp @@ -17,6 +17,7 @@ class Bar { Bar(const Bar&) = delete; auto toggle() -> void; + void handleSeat(struct wl_seat*, uint32_t); const Client& client; Gtk::Window window; diff --git a/include/client.hpp b/include/client.hpp index 015510501b..a91b0587fc 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -34,6 +34,8 @@ class Client { uint32_t name, const char *interface, uint32_t version); static void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name); + static void seatName(void *data, struct wl_seat *wl_seat, const char *name) {} + static void seatCapabilities(void *data, struct wl_seat *wl_seat, uint32_t caps); }; } diff --git a/include/factory.hpp b/include/factory.hpp index 51aff7cfee..a10f3f17d0 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -19,6 +19,7 @@ #ifdef HAVE_LIBPULSE #include "modules/pulseaudio.hpp" #endif +#include "modules/kbdlayout.hpp" #include "modules/custom.hpp" namespace waybar { diff --git a/include/modules/kbdlayout.hpp b/include/modules/kbdlayout.hpp new file mode 100644 index 0000000000..95c916718b --- /dev/null +++ b/include/modules/kbdlayout.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include "ALabel.hpp" +#include +#include +#include + +namespace waybar::modules { + +class KbdLayout : public ALabel { + public: + KbdLayout(const Json::Value&); + ~KbdLayout(); + auto update() -> void; + void handleSeat(struct wl_seat* seat, uint32_t caps); + struct wl_keyboard *wl_kbd_ = nullptr; + struct xkb_context *xkb_ctx_ = nullptr; + struct xkb_keymap *keymap_ = nullptr; + struct xkb_state *xkb_state_ = nullptr; + uint32_t current_group_ = 0; + +private: + +static void kbdKeymap(void *data, struct wl_keyboard *wl_kbd, uint32_t format, + int fd, uint32_t size); +static void kbdEnter(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, + struct wl_surface *surf, struct wl_array *keys) {} +static void kbdLeave(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, struct wl_surface *surf) {} + +static void kbdKey(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, uint32_t time, + uint32_t key, uint32_t state) {} + +static void kbdModifiers(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group); + +static void kbdRepeatInfo(void *data, struct wl_keyboard *wl_kbd, int32_t rate, + int32_t delay) {} + std::string layout_ = "EN"; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index 6861c57fc0..035423f7a9 100644 --- a/meson.build +++ b/meson.build @@ -39,6 +39,7 @@ sigcpp = dependency('sigc++-2.0') libnl = dependency('libnl-3.0', required: get_option('libnl')) libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl')) libpulse = dependency('libpulse', required: get_option('pulseaudio')) +xkbcommon = dependency('xkbcommon') src_files = files( 'src/factory.cpp', @@ -48,6 +49,7 @@ src_files = files( 'src/modules/clock.cpp', 'src/modules/custom.cpp', 'src/modules/cpu.cpp', + 'src/modules/kbdlayout.cpp', 'src/main.cpp', 'src/bar.cpp', 'src/client.cpp' @@ -109,7 +111,8 @@ executable( giounix, libnl, libnlgen, - libpulse + libpulse, + xkbcommon ], include_directories: [include_directories('include')], install: true, diff --git a/resources/style.css b/resources/style.css index 25132272e6..4ca06114d9 100644 --- a/resources/style.css +++ b/resources/style.css @@ -29,7 +29,7 @@ window#waybar { border-bottom: 3px solid white; } -#clock, #battery, #cpu, #memory, #network, #pulseaudio, #custom-spotify, #tray, #mode { +#clock, #battery, #cpu, #memory, #network, #pulseaudio, #custom-spotify, #tray, #mode, kbdlayout { padding: 0 10px; margin: 0 5px; } @@ -100,3 +100,8 @@ window#waybar { #tray { background-color: #2980b9; } + +#kbdlayout { + background: #1f4d96; + color: white; +} diff --git a/src/bar.cpp b/src/bar.cpp index 961a3215a7..ff30270b05 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -144,6 +144,21 @@ auto waybar::Bar::toggle() -> void wl_surface_commit(surface); } +void waybar::Bar::handleSeat(struct wl_seat* seat, uint32_t caps) { + zwlr_layer_surface_v1_get_keyboard_modifiers(layer_surface, 1, seat); + wl_surface_commit(surface); + + for (auto it = modules_left_.begin(); it != modules_left_.end(); ++it) { + (*it)->handleSeat(seat, caps); + } + for (auto it = modules_center_.begin(); it != modules_center_.end(); ++it) { + (*it)->handleSeat(seat, caps); + } + for (auto it = modules_right_.begin(); it != modules_right_.end(); ++it) { + (*it)->handleSeat(seat, caps); + } +} + auto waybar::Bar::setupConfig() -> void { std::ifstream file(client.config_file); diff --git a/src/client.cpp b/src/client.cpp index 8c38ce394b..1f6b32c280 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -43,6 +43,13 @@ waybar::Client::Client(int argc, char* argv[]) } +void waybar::Client::seatCapabilities(void *data, struct wl_seat *wl_seat, uint32_t caps) { + auto o = static_cast(data); + for (auto it = o->bars.begin(); it != o->bars.end(); ++it) { + (*it)->handleSeat(wl_seat, caps); + } +} + void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { @@ -58,8 +65,13 @@ void waybar::Client::handleGlobal(void *data, struct wl_registry *registry, o->bars.emplace_back(std::make_unique(*o, std::move(output), name)); } } else if (strcmp(interface, wl_seat_interface.name) == 0) { + static const struct wl_seat_listener seat_listener = { + seatCapabilities, + seatName + }; o->seat = static_cast(wl_registry_bind(registry, name, &wl_seat_interface, version)); + wl_seat_add_listener(o->seat, &seat_listener, o); } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { o->xdg_output_manager = static_cast( diff --git a/src/factory.cpp b/src/factory.cpp index 4fba870464..b002489065 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -46,6 +46,9 @@ waybar::IModule* waybar::Factory::makeModule(const std::string &name) const return new waybar::modules::Pulseaudio(config_[name]); } #endif + if (ref == "kbdlayout") { + return new waybar::modules::KbdLayout(config_[name]); + } if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) { return new waybar::modules::Custom(ref.substr(7), config_[name]); } diff --git a/src/modules/kbdlayout.cpp b/src/modules/kbdlayout.cpp new file mode 100644 index 0000000000..17b2cdd77b --- /dev/null +++ b/src/modules/kbdlayout.cpp @@ -0,0 +1,87 @@ +#include "modules/kbdlayout.hpp" + +waybar::modules::KbdLayout::KbdLayout(const Json::Value &config) + : ALabel(config, "{layout}"), current_group_(0) { + label_.set_name("kbdlayout"); + enum xkb_context_flags ctx_flags = static_cast(XKB_CONTEXT_NO_DEFAULT_INCLUDES | XKB_CONTEXT_NO_ENVIRONMENT_NAMES); + xkb_ctx_ = xkb_context_new(ctx_flags); +} + +waybar::modules::KbdLayout::~KbdLayout() { + xkb_context_unref(xkb_ctx_); +} + +void waybar::modules::KbdLayout::handleSeat(struct wl_seat* seat, uint32_t caps) { + static const struct wl_keyboard_listener kbd_listener = { + kbdKeymap, + kbdEnter, + kbdLeave, + kbdKey, + kbdModifiers, + kbdRepeatInfo + }; + + if (!wl_kbd_ && (caps & WL_SEAT_CAPABILITY_KEYBOARD)) { + wl_kbd_ = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(wl_kbd_, &kbd_listener, this); + } else if (wl_kbd_ && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) { + wl_keyboard_destroy(wl_kbd_); + wl_kbd_ = NULL; + xkb_keymap_unref(keymap_); + keymap_ = NULL; + } +} + +auto waybar::modules::KbdLayout::update() -> void +{ + auto format = format_; + + xkb_keycode_t keycode = 38; + xkb_layout_index_t layout = xkb_state_key_get_layout(xkb_state_, keycode); + layout_ = xkb_keymap_layout_get_name(keymap_, layout); + + label_.set_label(fmt::format(format, fmt::arg("layout", layout_))); +} + +void waybar::modules::KbdLayout::kbdKeymap(void *data, struct wl_keyboard *wl_kbd, uint32_t format, int fd, uint32_t size) { + auto o = static_cast(data); + void *buf; + + buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buf == MAP_FAILED) { + printf("Failed to mmap keymap: %d\n", errno); + close(fd); + return; + } + + o->keymap_ = xkb_keymap_new_from_buffer(o->xkb_ctx_, static_cast(buf), size - 1, + XKB_KEYMAP_FORMAT_TEXT_V1, static_cast(0)); + munmap(buf, size); + close(fd); + if (!o->keymap_) { + printf("Failed to compile keymap!\n"); + return; + } + + o->xkb_state_ = xkb_state_new(o->keymap_); + if (!o->xkb_state_) { + printf("Failed to create XKB state!\n"); + return; + } + + o->dp.emit(); +} + +void waybar::modules::KbdLayout::kbdModifiers(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { + + auto o = static_cast(data); + + xkb_state_update_mask(o->xkb_state_, mods_depressed, mods_latched, + mods_locked, 0, 0, group); + if (o->current_group_ != group) { + o->current_group_ = group; + o->dp.emit(); + } +}