diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 653d2b9..440f4f2 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(logid DeviceManager.cpp Device.cpp Receiver.cpp + Configuration.cpp backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp diff --git a/src/logid/Configuration.cpp b/src/logid/Configuration.cpp index 6e30e31..9bb1e14 100644 --- a/src/logid/Configuration.cpp +++ b/src/logid/Configuration.cpp @@ -16,521 +16,79 @@ * */ -#include +#include #include #include -#include -#include -#include -#include -#include -#include #include "Configuration.h" -#include "util.h" +#include "util/log.h" using namespace logid; using namespace libconfig; -Configuration::Configuration(const char *config_file) +Configuration::Configuration(const std::string& config_file) { - //Read config file - try - { - cfg.readFile(config_file); - } - catch(const FileIOException &e) - { - log_printf(ERROR, "I/O Error while reading %s: %s", config_file, e.what()); + try { + _config.readFile(config_file.c_str()); + } catch(const FileIOException &e) { + logPrintf(ERROR, "I/O Error while reading %s: %s", config_file.c_str(), + e.what()); + throw e; + } catch(const ParseException &e) { + logPrintf(ERROR, "Parse error in %s, line %d: %s", e.getFile(), + e.getLine(), e.getError()); throw e; } - catch(const ParseException &e) - { - log_printf(ERROR, "Parse error in %s, line %d: %s", e.getFile(), e.getLine(), e.getError()); - throw e; - } - const Setting &root = cfg.getRoot(); - try - { - auto& _blacklist = root.lookup("blacklist"); - if(_blacklist.isArray() || _blacklist.isList()) - { - int len = _blacklist.getLength(); - for(int i = 0; i < len; i++) - { - if(!_blacklist[i].isNumber()) { - log_printf(WARN, "Line %d: blacklist must only contain " - "PIDs", _blacklist[i].getSourceLine()); - if(_blacklist.isArray()) - break; - if(_blacklist.isList()) - continue; - } - blacklist.push_back((int)_blacklist[i]); - } - } - else - { - log_printf(WARN, "Line %d: blacklist must be an array or list, " - "ignnoring.", _blacklist.getSourceLine()); - } - } + const Setting &root = _config.getRoot(); + Setting* devices; + + try { devices = &root["devices"]; } catch(const SettingNotFoundException &e) { - } - - Setting* _devices; - - try { _devices = &root["devices"]; } - catch(const SettingNotFoundException &e) - { - log_printf(WARN, "No devices listed in config file."); + logPrintf(WARN, "No devices listed in config file."); return; } - for(int i = 0; i < _devices->getLength(); i++) + for(int i = 0; i < devices->getLength(); i++) { - const Setting &device = (*_devices)[i]; + const Setting &device = (*devices)[i]; std::string name; - try - { - if(!device.lookupValue("name", name)) - { - log_printf(WARN, "Line %d: 'name' must be a string, skipping device.", device["name"].getSourceLine()); + try { + if(!device.lookupValue("name", name)) { + logPrintf(WARN, "Line %d: 'name' must be a string, skipping " + "device.", device["name"].getSourceLine()); continue; } - } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "Line %d: Missing 'name' field, skipping device.", device.getSourceLine()); + } catch(SettingNotFoundException &e) { + logPrintf(WARN, "Line %d: Missing 'name' field, skipping device." + , device.getSourceLine()); continue; } - devices.insert({name, new DeviceConfig(device)}); + _device_paths.insert({name, device.getPath()}); } } -DeviceConfig::DeviceConfig(const libconfig::Setting &root) +libconfig::Setting& Configuration::getSetting(std::string path) { - try - { - int d; - if(!root.lookupValue("dpi", d)) - throw SettingTypeException(root["dpi"]); - dpi = new int(d); - } - catch(const SettingNotFoundException &e) { } - catch(const SettingTypeException &e) - { - log_printf(WARN, "Line %d: DPI must me an integer; not setting.", root["dpi"].getSourceLine()); - } - - try - { - const Setting& hr = root["hiresscroll"]; - uint8_t hss = 0; - try - { - bool b; - if(!hr.lookupValue("hires", b)) - throw SettingTypeException(root["hires"]); - if(b) hss |= HIDPP20::IHiresScroll::HiRes; - } - catch(SettingNotFoundException &e) { } - catch(SettingTypeException &e) - { - log_printf(INFO, "Line %d: hires field must be a boolean", hr["hires"].getSourceLine()); - } - - try - { - bool b; - if(!hr.lookupValue("invert", b)) - throw SettingTypeException(root["invert"]); - if(b) hss |= HIDPP20::IHiresScroll::Inverted; - } - catch(SettingNotFoundException &e) { } - catch(SettingTypeException &e) - { - log_printf(INFO, "Line %d: invert field must be a boolean", hr["invert"].getSourceLine()); - } - - try - { - bool b; - if(!hr.lookupValue("target", b)) - throw SettingTypeException(root["target"]); - if(b) hss |= HIDPP20::IHiresScroll::Target; - } - catch(SettingNotFoundException &e) { } - catch(SettingTypeException &e) - { - log_printf(INFO, "Line %d: target field must be a boolean", hr["target"].getSourceLine()); - } - - hiresscroll = new uint8_t(hss); - } - catch(const SettingNotFoundException &e) - { - log_printf(INFO, "Missing hiresscroll option, not setting."); - } - catch(const SettingTypeException &e) - { - log_printf(WARN, "Line %d: hiresscroll should be an object", root["hiresscroll"].getSourceLine()); - } - - try - { - const Setting& ss = root["smartshift"]; - smartshift = new HIDPP20::ISmartShift::SmartshiftStatus {}; - bool on; - int threshold; - try - { - if (ss.lookupValue("on", on)) smartshift->Active = new bool(on); - else log_printf(WARN, "Line %d: on field must be a boolean", ss["on"].getSourceLine()); - } - catch(const SettingNotFoundException &e) { } - - try - { - if (ss.lookupValue("threshold", threshold)) - { - if(threshold < 0) - { - threshold = 1; - log_printf(INFO, "Smartshift threshold must be > 0 or < 100, setting to 1."); - } - if(threshold >= 100) - { - threshold = 99; - log_printf(INFO, "Smartshift threshold must be > 0 or < 100, setting to 99."); - } - smartshift->AutoDisengage = new uint8_t(threshold); - smartshift->DefaultAutoDisengage = new uint8_t(threshold); - } - else log_printf(WARN, "Line %d: threshold must be an integer", ss["threshold"].getSourceLine()); - } - catch(const SettingNotFoundException &e) { } - } - catch(const SettingNotFoundException &e) { } - catch(const SettingTypeException &e) - { - log_printf(WARN, "Line %d: smartshift field must be an object", root["smartshift"].getSourceLine()); - } - - Setting* buttons; - try - { - buttons = &root["buttons"]; - } - catch(const SettingNotFoundException &e) - { - log_printf(WARN, "No button configuration found, reverting to null config."); - new std::map(); - return; - } - - for(int i = 0; i < buttons->getLength(); i++) - { - const Setting &button = (*buttons)[i]; - - int cid; - try { button.lookupValue("cid", cid); } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "Entry on line %d is missing a cid", button.getSourceLine()); - continue; - } - - if(actions.find(cid) != actions.end()) - { - log_printf(WARN, "Duplicate entries for cid 0x%x, skipping entry on line %d", cid, button.getSourceLine()); - continue; - } - - Setting* action_config; - try { action_config = &button["action"]; } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "cid 0x%x is missing an action, not diverting!", cid); - continue; - } - - Action action_type; - try - { - std::string action_type_str; - action_config->lookupValue("type", action_type_str); - action_type = stringToAction(action_type_str); - } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "cid 0x%x is missing an action type, not diverting!", cid); - continue; - } - catch(std::invalid_argument &e) - { - log_printf(WARN, "Line %d: %s", (*action_config)["type"].getSourceLine(), e.what()); - continue; - } - - try { actions.insert({cid, parse_action(action_type, action_config)}); } - catch(std::exception &e) { log_printf(ERROR, "%s", e.what()); } - } + return _config.lookup(path); } -ButtonAction* logid::parse_action(Action type, const Setting* action_config, bool is_gesture) +std::string Configuration::getDevice(std::string name) { - if(type == Action::None) return new NoAction(); - if(type == Action::Keypress) - { - std::vector keys; - try - { - const Setting &keys_config = (*action_config)["keys"]; - for (int i = 0; i < keys_config.getLength(); i++) - { - int keycode = libevdev_event_code_from_name(EV_KEY, keys_config[i]); - if(keycode == -1) - { - const char* keyname = keys_config[i]; - log_printf(WARN, "%s is not a valid keycode, skipping", keyname); - } - else keys.push_back(keycode); - } - } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "Expected keys parameter on line %d", action_config->getSourceLine()); - } - - return new KeyAction(keys); - } - else if(type == Action::Gestures) - { - if(is_gesture) - { - log_printf(WARN, "Line %d: Recursive gesture, defaulting to no action.", action_config->getSourceLine()); - return new NoAction(); - } - std::map gestures; - - Setting* gestures_config; - - try { gestures_config = &(*action_config)["gestures"]; } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "No gestures parameter for line %d, skipping", action_config->getSourceLine()); - throw e; - } - - for(int i = 0; i < gestures_config->getLength(); i++) - { - const Setting &gesture_config = (*gestures_config)[i]; - - std::string direction_str; - Direction direction; - try - { - gesture_config.lookupValue("direction", direction_str); - direction = stringToDirection(direction_str); - } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "No direction set on line %d", gesture_config.getSourceLine()); - continue; - } - catch(std::invalid_argument &e) - { - log_printf(WARN, "Line %d: %s", gesture_config["direction"].getSourceLine(), e.what()); - continue; - } - - if(gestures.find(direction) != gestures.end()) - { - log_printf(WARN, "Entry on line %d is a duplicate, skipping...", gesture_config["direction"].getSourceLine()); - continue; - } - - GestureMode mode; - try - { - std::string mode_str; - gesture_config.lookupValue("mode", mode_str); - mode = stringToGestureMode(mode_str); - } - catch (SettingNotFoundException &e) - { - log_printf(INFO, "Gesture mode on line %d not found, defaulting to OnRelease", gesture_config.getSourceLine()); - mode = GestureMode::OnRelease; - } - - if(mode == GestureMode::NoPress) - { - gestures.insert({direction, new Gesture(new NoAction(), mode)}); - continue; - } - - if(mode == GestureMode::Axis) - { - Gesture::axis_info axis; - try - { - std::string axis_str; - if(!gesture_config.lookupValue("axis", axis_str)) - throw SettingTypeException(gesture_config["axis"]); - axis.code = libevdev_event_code_from_name(EV_REL, axis_str.c_str()); - } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "Line %d: No axis found, defaulting to no action.", gesture_config.getSourceLine()); - gestures.insert({direction, new Gesture(new NoAction(), GestureMode::NoPress)}); - continue; - } - catch(SettingTypeException &e) - { - log_printf(WARN, "Line %d: Axis must be a string (e.g. 'REL_WHEEL')", gesture_config["axis"].getSourceLine()); - gestures.insert({direction, new Gesture(new NoAction(), GestureMode::NoPress)}); - continue; - } - - axis.multiplier = 1; - try - { - if(!gesture_config.lookupValue("axis_multiplier", axis.multiplier)) - { - int im = 1; - if(!gesture_config.lookupValue("axis_multiplier", im)) - throw SettingTypeException(gesture_config["axis_multiplier"]); - axis.multiplier = (float)im; - } - } - catch(SettingNotFoundException &e) { } - catch(SettingTypeException &e) - { - log_printf(WARN, "Line %d: axis_multiplier must be a float/integer", gesture_config["axis_multiplier"].getSourceLine()); - continue; - } - - gestures.insert({direction, new Gesture(new NoAction(), GestureMode::Axis, &axis)}); - continue; - } - - Setting* g_action; - try { g_action = &gesture_config["action"]; } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "No action set for %s", direction_str.c_str()); - continue; - } - - Action g_type; - try - { - std::string type_str; - g_action->lookupValue("type", type_str); - g_type = stringToAction(type_str); - } - catch(SettingNotFoundException &e) - { - log_printf(INFO, "Missing an action type on line %d, skipping", g_action->getSourceLine()); - continue; - } - catch(std::invalid_argument &e) - { - log_printf(WARN, "Line %d: %s", (*g_action)["type"].getSourceLine(), e.what()); - continue; - } - - ButtonAction* ba; - - try { ba = parse_action(g_type, g_action, true); } - catch(std::exception &e) { continue; } - - if(mode == GestureMode::OnFewPixels) - { - try - { - int pp; - if(!gesture_config.lookupValue("pixels", pp)) - throw SettingTypeException(gesture_config["pixels"]); - gestures.insert({direction, new Gesture(ba, mode, &pp)}); - } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "Line %d: OnFewPixels requires a 'pixels' field.", gesture_config.getSourceLine()); - } - catch(SettingTypeException &e) - { - log_printf(WARN, "Line %d: pixels must be an integer", gesture_config["pixels"].getSourceLine()); - continue; - } - } - else gestures.insert({direction, new Gesture(ba, mode)}); - } - - return new GestureAction(gestures); - } - else if(type == Action::ToggleSmartshift) return new SmartshiftAction(); - else if(type == Action::ToggleHiresScroll) return new HiresScrollAction(); - else if(type == Action::CycleDPI) - { - std::vector dpis; - try - { - const Setting &keys_config = (*action_config)["dpis"]; - for (int i = 0; i < keys_config.getLength(); i++) - { - dpis.push_back((int)keys_config[i]); - } - } - catch(SettingNotFoundException &e) - { - log_printf(ERROR, "Line %d: CycleDPI action is missing 'dpis' field, defaulting to NoAction.", action_config->getSourceLine()); - } - - return new CycleDPIAction(dpis); - } - else if(type == Action::ChangeDPI) - { - int inc; - try - { - action_config->lookupValue("inc", inc); - } - catch(SettingNotFoundException &e) - { - log_printf(ERROR, "Line %d: ChangeDPI action is missing an 'inc' field, defaulting to NoAction.",action_config->getSourceLine()); - return new NoAction(); - } - - return new ChangeDPIAction(inc); - } - - log_printf(ERROR, "This shouldn't have happened. Unhandled action type? Defaulting to NoAction"); - return new NoAction(); + auto it = _device_paths.find(name); + if(it == _device_paths.end()) + throw DeviceNotFound(name); + else + return it->second; } -DeviceConfig::DeviceConfig(DeviceConfig* dc, Device* dev) : baseConfig (false) +Configuration::DeviceNotFound::DeviceNotFound(std::string name) : + _name (std::move(name)) { - dpi = dc->dpi; - smartshift = dc->smartshift; - hiresscroll = dc->hiresscroll; - for(auto it : dc->actions) - actions.insert( { it.first, it.second->copy(dev) } ); } -DeviceConfig::DeviceConfig() +const char * Configuration::DeviceNotFound::what() { - dpi = nullptr; - hiresscroll = nullptr; - smartshift = nullptr; - actions = {}; -} - -DeviceConfig::~DeviceConfig() -{ - for(auto it : this->actions) - delete(it.second); + return _name.c_str(); } diff --git a/src/logid/Configuration.h b/src/logid/Configuration.h index 53a3d6a..b9d5200 100644 --- a/src/logid/Configuration.h +++ b/src/logid/Configuration.h @@ -21,43 +21,32 @@ #include #include -#include -#include "Actions.h" +#include namespace logid { - class DeviceConfig; - class ButtonAction; - enum class Action; - - class DeviceConfig - { - public: - DeviceConfig(); - ~DeviceConfig(); - DeviceConfig(DeviceConfig* dc, Device* dev); - DeviceConfig(const libconfig::Setting& root); - const int* dpi = nullptr; - HIDPP20::ISmartShift::SmartshiftStatus* smartshift = nullptr; - const uint8_t* hiresscroll = nullptr; - std::map actions; - const bool baseConfig = true; - }; - class Configuration { public: - Configuration(const char* config_file); - Configuration() {} - std::map devices; - std::vector blacklist; + explicit Configuration(const std::string& config_file); + Configuration() = default; + libconfig::Setting& getSetting(std::string path); + std::string getDevice(std::string name); + + class DeviceNotFound : public std::exception + { + public: + explicit DeviceNotFound(std::string name); + virtual const char* what(); + private: + std::string _name; + }; private: - libconfig::Config cfg; + std::map _device_paths; + libconfig::Config _config; }; - ButtonAction* parse_action(Action action, const libconfig::Setting* action_config, bool is_gesture=false); - - extern Configuration* global_config; + extern std::shared_ptr global_config; } -#endif //LOGID_CONFIGURATION_H \ No newline at end of file +#endif //LOGID_CONFIGURATION_H diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 4959afb..da8746a 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -23,16 +23,28 @@ using namespace logid; using namespace logid::backend; Device::Device(std::string path, backend::hidpp::DeviceIndex index) : - _hidpp20 (path, index), _path (std::move(path)), _index (index) + _hidpp20 (path, index), _path (std::move(path)), _index (index), + _config (global_config, this) { - logPrintf(DEBUG, "logid::Device created on %s:%d", _path.c_str(), _index); + ///TODO: Initialize features } Device::Device(const std::shared_ptr& raw_device, hidpp::DeviceIndex index) : _hidpp20(raw_device, index), _path - (raw_device->hidrawPath()), _index (index) + (raw_device->hidrawPath()), _index (index), + _config (global_config, this) { - logPrintf(DEBUG, "logid::Device created on %s:%d", _path.c_str(), _index); + ///TODO: Initialize features +} + +std::string Device::name() +{ + return _hidpp20.name(); +} + +uint16_t Device::pid() +{ + return _hidpp20.pid(); } void Device::sleep() @@ -44,3 +56,24 @@ void Device::wakeup() { logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index); } + +DeviceConfig& Device::config() +{ + return _config; +} + +DeviceConfig::DeviceConfig(std::shared_ptr config, Device* + device) : _device (device), _config (config) +{ + try { + _root_setting = config->getDevice(device->name()); + } catch(Configuration::DeviceNotFound& e) { + logPrintf(INFO, "Device %s not configured, using default config.", + device->name().c_str()); + } +} + +std::string DeviceConfig::getSetting(std::string path) +{ + return _root_setting + '/' + path; +} diff --git a/src/logid/Device.h b/src/logid/Device.h index 9b4e130..5b5fddd 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -22,9 +22,23 @@ #include "backend/hidpp/defs.h" #include "backend/hidpp20/Device.h" #include "features/DeviceFeature.h" +#include "Configuration.h" namespace logid { + class Device; + + class DeviceConfig + { + public: + DeviceConfig(std::shared_ptr config, Device* device); + std::string getSetting(std::string path); + private: + Device* _device; + std::string _root_setting; + std::shared_ptr _config; + }; + /* TODO: Implement HID++ 1.0 support * Currently, the logid::Device class has a hardcoded requirement * for an HID++ 2.0 device. @@ -35,6 +49,12 @@ namespace logid Device(std::string path, backend::hidpp::DeviceIndex index); Device(const std::shared_ptr& raw_device, backend::hidpp::DeviceIndex index); + + std::string name(); + uint16_t pid(); + + DeviceConfig& config(); + void wakeup(); void sleep(); private: @@ -42,6 +62,7 @@ namespace logid std::string _path; backend::hidpp::DeviceIndex _index; std::vector> _features; + DeviceConfig _config; }; } diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 110f134..654630f 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -180,6 +180,16 @@ Report Device::sendReport(Report& report) return response; } +std::string Device::name() const +{ + return _name; +} + +uint16_t Device::pid() const +{ + return _pid; +} + void Device::listen() { if(!_raw_device->isListening()) diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 3ba524e..cc6cade 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -72,6 +72,9 @@ namespace hidpp DeviceIndex deviceIndex() const; std::tuple version() const; + std::string name() const; + uint16_t pid() const; + void listen(); // Runs asynchronously void stopListening(); diff --git a/src/logid/features/DeviceFeature.h b/src/logid/features/DeviceFeature.h index accef5e..f3ae122 100644 --- a/src/logid/features/DeviceFeature.h +++ b/src/logid/features/DeviceFeature.h @@ -20,13 +20,30 @@ #define LOGID_FEATURES_DEVICEFEATURE_H namespace logid { + class Device; namespace features { class DeviceFeature { public: + explicit DeviceFeature(Device* dev) : _device (dev) + { + } virtual void configure() = 0; virtual void listen() = 0; + class Config + { + public: + explicit Config(Device* dev) : _device (dev) + { + } + protected: + virtual const std::string configPath() = 0; + Device* _device; + std::string root_setting; + }; + private: + Device* _device; }; }} diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index 35274e1..ac6a446 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -38,7 +38,7 @@ using namespace logid; std::string config_file = DEFAULT_CONFIG_FILE; LogLevel logid::global_loglevel = INFO; -// Configuration* logid::global_config; +std::shared_ptr logid::global_config; std::unique_ptr logid::device_manager; bool logid::kill_logid = false; @@ -155,12 +155,15 @@ int main(int argc, char** argv) { readCliOptions(argc, argv); - /* // Read config - try { global_config = new Configuration(config_file.c_str()); } - catch (std::exception &e) { global_config = new Configuration(); } - + try { + global_config = std::make_shared(config_file); + } + catch (std::exception &e) { + global_config = std::make_shared(); + } + /* //Create an evdev device called 'logid' try { global_evdev = new EvdevDevice(evdev_name); } catch(std::system_error& e)