#pragma once #include #include #include namespace hidpp { /// Battery state of a wireless Logitech device. struct BatteryStatus { enum class Level : uint8_t { UNKNOWN, CRITICAL, LOW, GOOD, FULL }; bool connected = false; int percentage = -1; // 0..100, or -1 when the device only reports a level Level level = Level::UNKNOWN; bool charging = false; bool externalPower = false; }; /// Reads the battery level of a Logitech HID++ 2.0 device. /// /// Prefers the power_supply entry that hid-logitech-hidpp exports, and falls /// back to speaking HID++ 2.0 over /dev/hidraw* when that driver is not bound /// (the receiver then sits on hid-generic and exports no battery at all). /// /// read() blocks on wireless round-trips that can take seconds when the device /// is asleep, so it must be called off the GTK main thread. class BatteryReader { public: explicit BatteryReader(std::string deviceName); ~BatteryReader(); BatteryReader(const BatteryReader &) = delete; BatteryReader &operator=(const BatteryReader &) = delete; BatteryStatus read(); private: /// A resolved HID++ endpoint: which hidraw node, which device slot on it, /// and the feature index the battery lives behind. struct Endpoint { int fd = -1; uint8_t deviceIndex = 0; uint8_t featureIdx = 0; uint16_t featureId = 0; // kFeatureUnifiedBattery or kFeatureBatteryStatus }; std::string deviceName; Endpoint endpoint{}; std::optional readFromSysfs() const; std::optional readFromHidpp(); /// Finds the hidraw node and device slot our device answers on. Expensive: /// only called on first use and after the cached endpoint stops replying. bool resolveEndpoint(); void closeEndpoint(); }; } // namespace hidpp