From 6dfa31fb1778da2989a28179304f9e1ccf1e3e4e Mon Sep 17 00:00:00 2001 From: Grant Moyer Date: Sun, 7 Feb 2021 15:05:11 -0500 Subject: [PATCH] Basic keyboard state module --- README.md | 2 + include/factory.hpp | 3 ++ include/modules/keyboard_state.hpp | 31 +++++++++++++ meson.build | 7 +++ meson_options.txt | 1 + src/factory.cpp | 5 ++ src/modules/keyboard_state.cpp | 73 ++++++++++++++++++++++++++++++ 7 files changed, 122 insertions(+) create mode 100644 include/modules/keyboard_state.hpp create mode 100644 src/modules/keyboard_state.cpp diff --git a/README.md b/README.md index b104adec..37c0cfc7 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ libappindicator-gtk3 [Tray module] libdbusmenu-gtk3 [Tray module] libmpdclient [MPD module] libsndio [sndio module] +libevdev [KeyboardState module] ``` **Build dependencies** @@ -86,6 +87,7 @@ sudo apt install \ clang-tidy \ gobject-introspection \ libdbusmenu-gtk3-dev \ + libevdev-dev \ libfmt-dev \ libgirepository1.0-dev \ libgtk-3-dev \ diff --git a/include/factory.hpp b/include/factory.hpp index 1cae68c9..4b9f32aa 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -38,6 +38,9 @@ #ifdef HAVE_LIBUDEV #include "modules/backlight.hpp" #endif +#ifdef HAVE_LIBEVDEV +#include "modules/keyboard_state.hpp" +#endif #ifdef HAVE_LIBPULSE #include "modules/pulseaudio.hpp" #endif diff --git a/include/modules/keyboard_state.hpp b/include/modules/keyboard_state.hpp new file mode 100644 index 00000000..99ed602b --- /dev/null +++ b/include/modules/keyboard_state.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#if FMT_VERSION < 60000 +#include +#else +#include +#endif +#include "ALabel.hpp" +#include "util/sleeper_thread.hpp" + +extern "C" { +#include +} + +namespace waybar::modules { + +class KeyboardState : public ALabel { + public: + KeyboardState(const std::string&, const Json::Value&); + ~KeyboardState(); + auto update() -> void; + + private: + std::string dev_path_; + int fd_; + libevdev* dev_; + util::SleeperThread thread_; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index e80448e0..7ac6fd6a 100644 --- a/meson.build +++ b/meson.build @@ -94,6 +94,7 @@ 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')) libudev = dependency('libudev', required: get_option('libudev')) +libevdev = dependency('libevdev', required: get_option('libevdev')) libmpdclient = dependency('libmpdclient', required: get_option('mpd')) libsndio = compiler.find_library('sndio', required: get_option('sndio')) @@ -215,6 +216,11 @@ if libudev.found() and (is_linux or libepoll.found()) src_files += 'src/modules/backlight.cpp' endif +if libevdev.found() and (is_linux or libepoll.found()) + add_project_arguments('-DHAVE_LIBEVDEV', language: 'cpp') + src_files += 'src/modules/keyboard_state.cpp' +endif + if libmpdclient.found() add_project_arguments('-DHAVE_LIBMPDCLIENT', language: 'cpp') src_files += 'src/modules/mpd/mpd.cpp' @@ -270,6 +276,7 @@ executable( libudev, libepoll, libmpdclient, + libevdev, gtk_layer_shell, libsndio, tz_dep diff --git a/meson_options.txt b/meson_options.txt index cb5581b3..fefb3dc3 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,6 +1,7 @@ option('libcxx', type : 'boolean', value : false, description : 'Build with Clang\'s libc++ instead of libstdc++ on Linux.') option('libnl', type: 'feature', value: 'auto', description: 'Enable libnl support for network related features') option('libudev', type: 'feature', value: 'auto', description: 'Enable libudev support for udev related features') +option('libevdev', type: 'feature', value: 'auto', description: 'Enable libevdev support for evdev related features') option('pulseaudio', type: 'feature', value: 'auto', description: 'Enable support for pulseaudio') option('systemd', type: 'feature', value: 'auto', description: 'Install systemd user service unit') option('dbusmenu-gtk', type: 'feature', value: 'auto', description: 'Enable support for tray') diff --git a/src/factory.cpp b/src/factory.cpp index 1f907894..cab23079 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -70,6 +70,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { return new waybar::modules::Backlight(id, config_[name]); } #endif +#ifdef HAVE_LIBEVDEV + if (ref == "keyboard_state") { + return new waybar::modules::KeyboardState(id, 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 new file mode 100644 index 00000000..ea9ae577 --- /dev/null +++ b/src/modules/keyboard_state.cpp @@ -0,0 +1,73 @@ +#include "modules/keyboard_state.hpp" +#include + +extern "C" { +#include +#include +#include +} + +waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Json::Value& config) + : ALabel(config, "keyboard_state", id, "{temperatureC}", 1) { + if (config_["device-path"].isString()) { + dev_path_ = config_["device-path"].asString(); + } else { + dev_path_ = ""; + } + + fd_ = open(dev_path_.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY); + if (fd_ < 0) { + throw std::runtime_error("Can't open " + dev_path_); + } + int err = libevdev_new_from_fd(fd_, &dev_); + if (err < 0) { + throw std::runtime_error("Can't create libevdev device"); + } + if (!libevdev_has_event_type(dev_, EV_LED)) { + throw std::runtime_error("Device doesn't support LED events"); + } + if (!libevdev_has_event_code(dev_, EV_LED, LED_NUML) || !libevdev_has_event_code(dev_, EV_LED, LED_CAPSL)) { + throw std::runtime_error("Device doesn't support num lock or caps lock events"); + } + + thread_ = [this] { + dp.emit(); + thread_.sleep_for(interval_); + }; +} + +waybar::modules::KeyboardState::~KeyboardState() { + libevdev_free(dev_); + int err = close(fd_); + if (err < 0) { + // Not much we can do, so ignore it. + } +} + +auto waybar::modules::KeyboardState::update() -> void { + int err = LIBEVDEV_READ_STATUS_SUCCESS; + while (err == LIBEVDEV_READ_STATUS_SUCCESS) { + input_event ev; + err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_NORMAL, &ev); + while (err == LIBEVDEV_READ_STATUS_SYNC) { + err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_SYNC, &ev); + } + } + if (err != -EAGAIN) { + throw std::runtime_error("Failed to sync evdev device"); + } + + int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML); + //int capsl = libevdev_get_event_value(dev_, EV_LED, LED_CAPSL); + + std::string text; + if (numl) { + text = fmt::format(format_, "num lock enabled"); + label_.set_markup(text); + } else { + text = fmt::format(format_, "num lock disabled"); + label_.set_markup(text); + } + + ALabel::update(); +}