Improve keyboard_state error messages
This commit is contained in:
		
							parent
							
								
									91339f6ad4
								
							
						
					
					
						commit
						a595b61e0f
					
				|  | @ -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_; | ||||||
|  |  | ||||||
|  | @ -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*: ++ | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue