Improve keyboard_state error messages

This commit is contained in:
Grant Moyer 2022-02-25 12:22:55 -05:00
parent 91339f6ad4
commit a595b61e0f
3 changed files with 85 additions and 38 deletions

View File

@ -24,8 +24,6 @@ class KeyboardState : public AModule {
auto update() -> void; auto update() -> void;
private: private:
static auto openDevice(const std::string&) -> std::pair<int, libevdev*>;
Gtk::Box box_; Gtk::Box box_;
Gtk::Label numlock_label_; Gtk::Label numlock_label_;
Gtk::Label capslock_label_; Gtk::Label capslock_label_;

View File

@ -8,6 +8,8 @@ waybar - keyboard-state module
The *keyboard-state* module displays the state of number lock, caps lock, and scroll lock. The *keyboard-state* module displays the state of number lock, caps lock, and scroll lock.
You must be a member of the input group to use this module.
# CONFIGURATION # CONFIGURATION
*interval*: ++ *interval*: ++

View File

@ -1,6 +1,8 @@
#include "modules/keyboard_state.hpp" #include "modules/keyboard_state.hpp"
#include <errno.h>
#include <filesystem> #include <filesystem>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <string.h>
extern "C" { extern "C" {
#include <sys/types.h> #include <sys/types.h>
@ -8,6 +10,63 @@ extern "C" {
#include <fcntl.h> #include <fcntl.h>
} }
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 {
const auto errno_name = strerrorname_np(err);
const auto errno_str = strerror(err);
std::string error_msg{msg};
error_msg += ": ";
error_msg += errno_name;
error_msg += " ";
error_msg += errno_str;
return error_msg;
}
};
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);
}
}
return fd;
}
auto closeFile(int fd) -> void {
int res = close(fd);
if (res < 0) {
throw errno_error(errno, "Can't close file");
}
}
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");
}
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);
}
waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar& bar, const Json::Value& config) 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()), : AModule(config, "keyboard-state", id, false, !config["disable-scroll"].asBool()),
box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0), box_(bar.vertical ? Gtk::ORIENTATION_VERTICAL : Gtk::ORIENTATION_HORIZONTAL, 0),
@ -48,26 +107,36 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
if (config_["device-path"].isString()) { if (config_["device-path"].isString()) {
std::string dev_path = config_["device-path"].asString(); std::string dev_path = config_["device-path"].asString();
std::tie(fd_, dev_) = openDevice(dev_path); fd_ = openFile(dev_path, O_NONBLOCK | O_CLOEXEC | O_RDONLY);
dev_ = openDevice(fd_);
} else { } else {
DIR* dev_dir = opendir("/dev/input"); DIR* dev_dir = opendir("/dev/input");
if (dev_dir == nullptr) { if (dev_dir == nullptr) {
throw std::runtime_error("Failed to open /dev/input"); throw errno_error(errno, "Failed to open /dev/input");
} }
dirent *ep; dirent *ep;
while ((ep = readdir(dev_dir))) { while ((ep = readdir(dev_dir))) {
if (ep->d_type != DT_CHR) continue; if (ep->d_type != DT_CHR) continue;
std::string dev_path = std::string("/dev/input/") + ep->d_name; 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 { try {
std::tie(fd_, dev_) = openDevice(dev_path); auto dev = openDevice(fd);
spdlog::info("Found device {} at '{}'", libevdev_get_name(dev_), dev_path); if (supportsLockStates(dev)) {
break; spdlog::info("Found device {} at '{}'", libevdev_get_name(dev), dev_path);
} catch (const std::runtime_error& e) { fd_ = fd;
continue; 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) { if (dev_ == nullptr) {
throw std::runtime_error("Failed to find keyboard device"); throw errno_error(errno, "Failed to find keyboard device");
} }
} }
@ -79,35 +148,13 @@ waybar::modules::KeyboardState::KeyboardState(const std::string& id, const Bar&
waybar::modules::KeyboardState::~KeyboardState() { waybar::modules::KeyboardState::~KeyboardState() {
libevdev_free(dev_); libevdev_free(dev_);
int err = close(fd_); try {
if (err < 0) { closeFile(fd_);
// Not much we can do, so ignore it. } catch (const std::runtime_error& e) {
spdlog::warn(e.what());
} }
} }
auto waybar::modules::KeyboardState::openDevice(const std::string& path) -> std::pair<int, libevdev*> {
int fd = open(path.c_str(), O_NONBLOCK | O_CLOEXEC | O_RDONLY);
if (fd < 0) {
throw std::runtime_error("Can't open " + path);
}
libevdev* dev;
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)
|| !libevdev_has_event_code(dev, EV_LED, LED_SCROLLL)) {
throw std::runtime_error("Device doesn't support num lock, caps lock, or scroll lock events");
}
return std::make_pair(fd, dev);
}
auto waybar::modules::KeyboardState::update() -> void { auto waybar::modules::KeyboardState::update() -> void {
int err = LIBEVDEV_READ_STATUS_SUCCESS; int err = LIBEVDEV_READ_STATUS_SUCCESS;
while (err == LIBEVDEV_READ_STATUS_SUCCESS) { while (err == LIBEVDEV_READ_STATUS_SUCCESS) {
@ -117,8 +164,8 @@ auto waybar::modules::KeyboardState::update() -> void {
err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_SYNC, &ev); err = libevdev_next_event(dev_, LIBEVDEV_READ_FLAG_SYNC, &ev);
} }
} }
if (err != -EAGAIN) { if (-err != EAGAIN) {
throw std::runtime_error("Failed to sync evdev device"); throw errno_error(-err, "Failed to sync evdev device");
} }
int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML); int numl = libevdev_get_event_value(dev_, EV_LED, LED_NUML);