feat(keyboard): improve keyboard response time
Use libinput event for keyboard state updates. The state will update when CAPS_LOCK, NUM_LOCK or SCROLL_LOCK has been released, `interval` will have no effect after this change.pull/1661/head
parent
fd24d7bcf6
commit
061f4550f4
|
@ -3,12 +3,15 @@
|
|||
#include <fmt/chrono.h>
|
||||
#include <gtkmm/label.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AModule.hpp"
|
||||
#include "bar.hpp"
|
||||
#include "util/sleeper_thread.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <libinput.h>
|
||||
}
|
||||
|
||||
namespace waybar::modules {
|
||||
|
@ -20,6 +23,8 @@ class KeyboardState : public AModule {
|
|||
auto update() -> void;
|
||||
|
||||
private:
|
||||
auto findKeyboards() -> void;
|
||||
|
||||
Gtk::Box box_;
|
||||
Gtk::Label numlock_label_;
|
||||
Gtk::Label capslock_label_;
|
||||
|
@ -34,6 +39,8 @@ class KeyboardState : public AModule {
|
|||
|
||||
int fd_;
|
||||
libevdev* dev_;
|
||||
struct libinput* libinput_;
|
||||
std::unordered_map<std::string, struct libinput_device*> libinput_devices_;
|
||||
|
||||
util::SleeperThread thread_;
|
||||
};
|
||||
|
|
|
@ -90,6 +90,7 @@ giounix = dependency('gio-unix-2.0', required: (get_option('dbusmenu-gtk').enabl
|
|||
jsoncpp = dependency('jsoncpp')
|
||||
sigcpp = dependency('sigc++-2.0')
|
||||
libepoll = dependency('epoll-shim', required: false)
|
||||
libinput = dependency('libinput', required: get_option('libinput'))
|
||||
libnl = dependency('libnl-3.0', required: get_option('libnl'))
|
||||
libnlgen = dependency('libnl-genl-3.0', required: get_option('libnl'))
|
||||
upower_glib = dependency('upower-glib', required: get_option('upower_glib'))
|
||||
|
@ -243,8 +244,9 @@ 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())
|
||||
if libevdev.found() and (is_linux or libepoll.found()) and libinput.found()
|
||||
add_project_arguments('-DHAVE_LIBEVDEV', language: 'cpp')
|
||||
add_project_arguments('-DHAVE_LIBINPUT', language: 'cpp')
|
||||
src_files += 'src/modules/keyboard_state.cpp'
|
||||
endif
|
||||
|
||||
|
@ -304,6 +306,7 @@ executable(
|
|||
gtkmm,
|
||||
dbusmenu_gtk,
|
||||
giounix,
|
||||
libinput,
|
||||
libnl,
|
||||
libnlgen,
|
||||
upower_glib,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
option('libcxx', type : 'boolean', value : false, description : 'Build with Clang\'s libc++ instead of libstdc++ on Linux.')
|
||||
option('libinput', type: 'feature', value: 'auto', description: 'Enable libinput support for libinput related features')
|
||||
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')
|
||||
|
|
|
@ -8,8 +8,12 @@
|
|||
|
||||
extern "C" {
|
||||
#include <fcntl.h>
|
||||
#include <libinput.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <poll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
}
|
||||
|
||||
class errno_error : public std::runtime_error {
|
||||
|
@ -73,6 +77,51 @@ auto supportsLockStates(const libevdev* dev) -> bool {
|
|||
libevdev_has_event_code(dev, EV_LED, LED_SCROLLL);
|
||||
}
|
||||
|
||||
auto waybar::modules::KeyboardState::findKeyboards() -> void {
|
||||
if (config_["device-path"].isString()) {
|
||||
std::string dev_path = config_["device-path"].asString();
|
||||
libinput_devices_[dev_path] = nullptr;
|
||||
fd_ = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
||||
dev_ = openDevice(fd_);
|
||||
} else {
|
||||
DIR* dev_dir = opendir("/dev/input/by-path");
|
||||
if (dev_dir == nullptr) {
|
||||
throw errno_error(errno, "Failed to open /dev/input");
|
||||
}
|
||||
dirent* ep;
|
||||
while ((ep = readdir(dev_dir))) {
|
||||
if (ep->d_type == DT_DIR) continue;
|
||||
std::string dev_path = std::string("/dev/input/by-path/") + ep->d_name;
|
||||
int fd = openFile(dev_path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
||||
try {
|
||||
auto dev = openDevice(fd);
|
||||
if (supportsLockStates(dev)) {
|
||||
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path);
|
||||
if (libinput_devices_.empty()) {
|
||||
fd_ = fd;
|
||||
dev_ = dev;
|
||||
} else {
|
||||
libevdev_free(dev);
|
||||
closeFile(fd);
|
||||
}
|
||||
libinput_devices_[dev_path] = libinput_path_add_device(libinput_, dev_path.c_str());
|
||||
} else {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dev_ == nullptr) {
|
||||
throw errno_error(errno, "Failed to find keyboard device");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()),
|
||||
|
@ -100,7 +149,14 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
|
|||
? config_["format-icons"]["unlocked"].asString()
|
||||
: "unlocked"),
|
||||
fd_(0),
|
||||
dev_(nullptr) {
|
||||
dev_(nullptr),
|
||||
libinput_(nullptr),
|
||||
libinput_devices_({}) {
|
||||
struct libinput_interface interface = {
|
||||
[](const char* path, int flags, void* user_data) { return open(path, flags); },
|
||||
[](int fd, void* user_data) { close(fd); }};
|
||||
libinput_ = libinput_path_create_context(&interface, NULL);
|
||||
|
||||
box_.set_name("keyboard-state");
|
||||
if (config_["numlock"].asBool()) {
|
||||
numlock_label_.get_style_context()->add_class("numlock");
|
||||
|
@ -119,44 +175,39 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
|
|||
}
|
||||
event_box_.add(box_);
|
||||
|
||||
if (config_["device-path"].isString()) {
|
||||
std::string dev_path = config_["device-path"].asString();
|
||||
fd_ = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
||||
dev_ = openDevice(fd_);
|
||||
} else {
|
||||
DIR* dev_dir = opendir("/dev/input");
|
||||
if (dev_dir == nullptr) {
|
||||
throw errno_error(errno, "Failed to open /dev/input");
|
||||
}
|
||||
dirent* ep;
|
||||
while ((ep = readdir(dev_dir))) {
|
||||
if (ep->d_type != DT_CHR) continue;
|
||||
std::string dev_path = std::string("/dev/input/") + ep->d_name;
|
||||
int fd = openFile(dev_path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY);
|
||||
try {
|
||||
auto dev = openDevice(fd);
|
||||
if (supportsLockStates(dev)) {
|
||||
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path);
|
||||
fd_ = fd;
|
||||
dev_ = dev;
|
||||
break;
|
||||
}
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
closeFile(fd);
|
||||
}
|
||||
if (dev_ == nullptr) {
|
||||
throw errno_error(errno, "Failed to find keyboard device");
|
||||
}
|
||||
}
|
||||
findKeyboards();
|
||||
|
||||
thread_ = [this] {
|
||||
dp.emit();
|
||||
thread_.sleep_for(interval_);
|
||||
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);
|
||||
switch (key) {
|
||||
case KEY_CAPSLOCK:
|
||||
case KEY_NUMLOCK:
|
||||
case KEY_SCROLLLOCK:
|
||||
dp.emit();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (type == LIBINPUT_EVENT_DEVICE_REMOVED) {
|
||||
// TODO: Handle device removal.
|
||||
// Clear libinput_devices_ and re-find keyboards.
|
||||
}
|
||||
libinput_event_destroy(event);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -167,9 +218,14 @@ waybar::modules::KeyboardState::~KeyboardState() {
|
|||
} catch (const std::runtime_error& e) {
|
||||
spdlog::warn(e.what());
|
||||
}
|
||||
for (const auto& [_, dev_ptr] : libinput_devices_) {
|
||||
libinput_path_remove_device(dev_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
auto waybar::modules::KeyboardState::update() -> void {
|
||||
sleep(0); // wait for keyboard status change
|
||||
|
||||
int err = LIBEVDEV_READ_STATUS_SUCCESS;
|
||||
while (err == LIBEVDEV_READ_STATUS_SUCCESS) {
|
||||
input_event ev;
|
||||
|
|
Loading…
Reference in New Issue