diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 75546a63..d76a1800 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -105,6 +105,24 @@ void CConfigManager::setDefaultVars() { configValues["autogenerated"].intValue = 0; } +void CConfigManager::setDeviceDefaultVars(const std::string& dev) { + auto& cfgValues = deviceConfigs[dev]; + + cfgValues["kb_layout"].strValue = "us"; + cfgValues["kb_variant"].strValue = STRVAL_EMPTY; + cfgValues["kb_options"].strValue = STRVAL_EMPTY; + cfgValues["kb_rules"].strValue = STRVAL_EMPTY; + cfgValues["kb_model"].strValue = STRVAL_EMPTY; + cfgValues["repeat_rate"].intValue = 25; + cfgValues["repeat_delay"].intValue = 600; + cfgValues["natural_scroll"].intValue = 0; + cfgValues["numlock_by_default"].intValue = 0; + cfgValues["disable_while_typing"].intValue = 1; + cfgValues["clickfinger_behavior"].intValue = 0; + cfgValues["middle_button_emulation"].intValue = 0; + cfgValues["tap-to-click"].intValue = 1; +} + void CConfigManager::init() { loadConfigLoadVars(); @@ -134,33 +152,56 @@ void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::s parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">: No such field."; } - return; + if (COMMAND.find("device:") == 0 /* devices parsed later */) { + parseError = ""; + } else { + return; + } } + SConfigValue* CONFIGENTRY = nullptr; - auto& CONFIGENTRY = configValues.at(COMMAND); - if (CONFIGENTRY.intValue != -1) { + if (COMMAND.find("device:") == 0) { + const auto DEVICE = COMMAND.substr(7).substr(0, COMMAND.find_last_of(':') - 7); + const auto CONFIGVAR = COMMAND.substr(COMMAND.find_last_of(':') + 1); + + if (!deviceConfigExists(DEVICE)) + setDeviceDefaultVars(DEVICE); + + auto it = deviceConfigs.find(DEVICE); + + if (it->second.find(CONFIGVAR) == it->second.end()) { + parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">: No such field."; + return; + } + + CONFIGENTRY = &it->second.at(CONFIGVAR); + } else { + CONFIGENTRY = &configValues.at(COMMAND); + } + + if (CONFIGENTRY->intValue != -1) { try { if (VALUE.find("0x") == 0) { // Values with 0x are hex const auto VALUEWITHOUTHEX = VALUE.substr(2); - CONFIGENTRY.intValue = stol(VALUEWITHOUTHEX, nullptr, 16); + CONFIGENTRY->intValue = stol(VALUEWITHOUTHEX, nullptr, 16); } else - CONFIGENTRY.intValue = stol(VALUE); + CONFIGENTRY->intValue = stol(VALUE); } catch (...) { Debug::log(WARN, "Error reading value of %s", COMMAND.c_str()); parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; } - } else if (CONFIGENTRY.floatValue != -1) { + } else if (CONFIGENTRY->floatValue != -1) { try { - CONFIGENTRY.floatValue = stof(VALUE); + CONFIGENTRY->floatValue = stof(VALUE); } catch (...) { Debug::log(WARN, "Error reading value of %s", COMMAND.c_str()); parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; } - } else if (CONFIGENTRY.strValue != "") { + } else if (CONFIGENTRY->strValue != "") { try { - CONFIGENTRY.strValue = VALUE; + CONFIGENTRY->strValue = VALUE; } catch (...) { Debug::log(WARN, "Error reading value of %s", COMMAND.c_str()); parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; @@ -706,6 +747,7 @@ void CConfigManager::loadConfigLoadVars() { g_pAnimationManager->removeAllBeziers(); m_mAdditionalReservedAreas.clear(); configDynamicVars.clear(); + deviceConfigs.clear(); // paths configPaths.clear(); @@ -849,7 +891,7 @@ void CConfigManager::tick() { } std::mutex configmtx; -SConfigValue CConfigManager::getConfigValueSafe(std::string val) { +SConfigValue CConfigManager::getConfigValueSafe(const std::string& val) { std::lock_guard lg(configmtx); SConfigValue copy = configValues[val]; @@ -857,15 +899,29 @@ SConfigValue CConfigManager::getConfigValueSafe(std::string val) { return copy; } -int CConfigManager::getInt(std::string v) { +SConfigValue CConfigManager::getConfigValueSafeDevice(const std::string& dev, const std::string& val) { + std::lock_guard lg(configmtx); + + const auto it = deviceConfigs.find(dev); + + if (it == deviceConfigs.end()) { + return SConfigValue(); + } + + SConfigValue copy = it->second[val]; + + return copy; +} + +int CConfigManager::getInt(const std::string& v) { return getConfigValueSafe(v).intValue; } -float CConfigManager::getFloat(std::string v) { +float CConfigManager::getFloat(const std::string& v) { return getConfigValueSafe(v).floatValue; } -std::string CConfigManager::getString(std::string v) { +std::string CConfigManager::getString(const std::string& v) { const auto VAL = getConfigValueSafe(v).strValue; if (VAL == STRVAL_EMPTY) @@ -874,6 +930,23 @@ std::string CConfigManager::getString(std::string v) { return VAL; } +int CConfigManager::getDeviceInt(const std::string& dev, const std::string& v) { + return getConfigValueSafeDevice(dev, v).intValue; +} + +float CConfigManager::getDeviceFloat(const std::string& dev, const std::string& v) { + return getConfigValueSafeDevice(dev, v).floatValue; +} + +std::string CConfigManager::getDeviceString(const std::string& dev, const std::string& v) { + const auto VAL = getConfigValueSafeDevice(dev, v).strValue; + + if (VAL == STRVAL_EMPTY) + return ""; + + return VAL; +} + void CConfigManager::setInt(std::string v, int val) { configValues[v].intValue = val; } @@ -981,3 +1054,10 @@ void CConfigManager::performMonitorReload() { SConfigValue* CConfigManager::getConfigValuePtr(std::string val) { return &configValues[val]; } + +bool CConfigManager::deviceConfigExists(const std::string& dev) { + const auto it = deviceConfigs.find(dev); + + return it != deviceConfigs.end(); +} + diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index 8c0bd857..57124ece 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -52,13 +52,18 @@ public: void tick(); void init(); - int getInt(std::string); - float getFloat(std::string); - std::string getString(std::string); + int getInt(const std::string&); + float getFloat(const std::string&); + std::string getString(const std::string&); void setFloat(std::string, float); void setInt(std::string, int); void setString(std::string, std::string); + int getDeviceInt(const std::string&, const std::string&); + float getDeviceFloat(const std::string&, const std::string&); + std::string getDeviceString(const std::string&, const std::string&); + bool deviceConfigExists(const std::string&); + SConfigValue* getConfigValuePtr(std::string); SMonitorRule getMonitorRuleFor(std::string); @@ -81,6 +86,7 @@ private: std::unordered_map configModifyTimes; // stores modify times std::unordered_map configDynamicVars; // stores dynamic vars declared by the user std::unordered_map configValues; + std::unordered_map> deviceConfigs; // stores device configs std::string configCurrentPath; @@ -100,12 +106,15 @@ private: // internal methods void setDefaultVars(); + void setDeviceDefaultVars(const std::string&); void applyUserDefinedVars(std::string&, const size_t); void loadConfigLoadVars(); - SConfigValue getConfigValueSafe(std::string); + SConfigValue getConfigValueSafe(const std::string&); + SConfigValue getConfigValueSafeDevice(const std::string&, const std::string&); void parseLine(std::string&); void configSetValueSafe(const std::string&, const std::string&); + void handleDeviceConfig(const std::string&, const std::string&); void handleRawExec(const std::string&, const std::string&); void handleMonitor(const std::string&, const std::string&); void handleBind(const std::string&, const std::string&, bool locked = false); diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp index d32ae333..33278053 100644 --- a/src/helpers/WLClasses.hpp +++ b/src/helpers/WLClasses.hpp @@ -71,7 +71,12 @@ struct SKeyboard { bool active = false; + std::string name = ""; + xkb_rule_names currentRules = {0}; + int repeatRate = 0; + int repeatDelay = 0; + int numlockOn = -1; // For the list lookup bool operator==(const SKeyboard& rhs) { @@ -87,6 +92,8 @@ struct SMouse { pixman_region32_t confinedTo; + std::string name = ""; + DYNLISTENER(commitConstraint); DYNLISTENER(destroyMouse); @@ -168,6 +175,8 @@ struct STablet { wlr_tablet_v2_tablet* wlrTabletV2 = nullptr; wlr_input_device* wlrDevice = nullptr; + std::string name = ""; + bool operator==(const STablet& b) { return wlrDevice == b.wlrDevice; } @@ -186,6 +195,8 @@ struct STabletTool { bool active = true; + std::string name = ""; + DYNLISTENER(TabletToolDestroy); DYNLISTENER(TabletToolSetCursor); @@ -198,6 +209,8 @@ struct STabletPad { wlr_tablet_v2_tablet_pad* wlrTabletPadV2 = nullptr; STablet* pTabletParent = nullptr; + std::string name = ""; + DYNLISTENER(Attach); DYNLISTENER(Button); DYNLISTENER(Strip); diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index e9d77489..548af552 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -352,10 +352,11 @@ void CInputManager::newKeyboard(wlr_input_device* keyboard) { PNEWKEYBOARD->keyboard = keyboard; - const auto REPEATRATE = g_pConfigManager->getInt("input:repeat_rate"); - const auto REPEATDELAY = g_pConfigManager->getInt("input:repeat_delay"); - - wlr_keyboard_set_repeat_info(keyboard->keyboard, std::max(0, REPEATRATE), std::max(0, REPEATDELAY)); + try { + PNEWKEYBOARD->name = std::string(keyboard->name); + } catch (std::exception& e) { + Debug::log(ERR, "Keyboard had no name???"); // logic error + } PNEWKEYBOARD->hyprListener_keyboardMod.initCallback(&keyboard->keyboard->events.modifiers, &Events::listener_keyboardMod, PNEWKEYBOARD, "Keyboard"); PNEWKEYBOARD->hyprListener_keyboardKey.initCallback(&keyboard->keyboard->events.key, &Events::listener_keyboardKey, PNEWKEYBOARD, "Keyboard"); @@ -378,17 +379,23 @@ void CInputManager::setKeyboardLayout() { } void CInputManager::applyConfigToKeyboard(SKeyboard* pKeyboard) { + const auto HASCONFIG = g_pConfigManager->deviceConfigExists(pKeyboard->name); ASSERT(pKeyboard); - const auto RULES = g_pConfigManager->getString("input:kb_rules"); - const auto MODEL = g_pConfigManager->getString("input:kb_model"); - const auto LAYOUT = g_pConfigManager->getString("input:kb_layout"); - const auto VARIANT = g_pConfigManager->getString("input:kb_variant"); - const auto OPTIONS = g_pConfigManager->getString("input:kb_options"); + const auto REPEATRATE = HASCONFIG ? g_pConfigManager->getDeviceInt(pKeyboard->name, "repeat_rate") : g_pConfigManager->getInt("input:repeat_rate"); + const auto REPEATDELAY = HASCONFIG ? g_pConfigManager->getDeviceInt(pKeyboard->name, "repeat_delay") : g_pConfigManager->getInt("input:repeat_delay"); + + const auto NUMLOCKON = HASCONFIG ? g_pConfigManager->getDeviceInt(pKeyboard->name, "numlock_by_default") : g_pConfigManager->getInt("input:numlock_by_default"); + + const auto RULES = HASCONFIG ? g_pConfigManager->getDeviceString(pKeyboard->name, "kb_rules") : g_pConfigManager->getString("input:kb_rules"); + const auto MODEL = HASCONFIG ? g_pConfigManager->getDeviceString(pKeyboard->name, "kb_model") : g_pConfigManager->getString("input:kb_model"); + const auto LAYOUT = HASCONFIG ? g_pConfigManager->getDeviceString(pKeyboard->name, "kb_layout") : g_pConfigManager->getString("input:kb_layout"); + const auto VARIANT = HASCONFIG ? g_pConfigManager->getDeviceString(pKeyboard->name, "kb_variant") : g_pConfigManager->getString("input:kb_variant"); + const auto OPTIONS = HASCONFIG ? g_pConfigManager->getDeviceString(pKeyboard->name, "kb_options") : g_pConfigManager->getString("input:kb_options"); try { - if (RULES != "" && RULES == std::string(pKeyboard->currentRules.rules) && MODEL == std::string(pKeyboard->currentRules.model) && LAYOUT == std::string(pKeyboard->currentRules.layout) && VARIANT == std::string(pKeyboard->currentRules.variant) && OPTIONS == std::string(pKeyboard->currentRules.options)) { + if (NUMLOCKON == pKeyboard->numlockOn && REPEATDELAY == pKeyboard->repeatDelay && REPEATRATE == pKeyboard->repeatRate && RULES != "" && RULES == std::string(pKeyboard->currentRules.rules) && MODEL == std::string(pKeyboard->currentRules.model) && LAYOUT == std::string(pKeyboard->currentRules.layout) && VARIANT == std::string(pKeyboard->currentRules.variant) && OPTIONS == std::string(pKeyboard->currentRules.options)) { Debug::log(LOG, "Not applying config to keyboard, it did not change."); return; } @@ -397,6 +404,12 @@ void CInputManager::applyConfigToKeyboard(SKeyboard* pKeyboard) { // we can ignore those and just apply } + wlr_keyboard_set_repeat_info(pKeyboard->keyboard->keyboard, std::max(0, REPEATRATE), std::max(0, REPEATDELAY)); + + pKeyboard->repeatDelay = REPEATDELAY; + pKeyboard->repeatRate = REPEATRATE; + pKeyboard->numlockOn = NUMLOCKON; + xkb_rule_names rules = { .rules = RULES.c_str(), .model = MODEL.c_str(), @@ -430,7 +443,7 @@ void CInputManager::applyConfigToKeyboard(SKeyboard* pKeyboard) { wlr_keyboard_modifiers wlrMods = {0}; - if (g_pConfigManager->getInt("input:numlock_by_default") == 1) { + if (NUMLOCKON == 1) { // lock numlock const auto IDX = xkb_map_mod_get_index(KEYMAP, XKB_MOD_NAME_NUM); @@ -453,37 +466,44 @@ void CInputManager::newMouse(wlr_input_device* mouse) { const auto PMOUSE = &m_lMice.back(); PMOUSE->mouse = mouse; + try { + PMOUSE->name = std::string(mouse->name); + } catch(std::exception& e) { + Debug::log(ERR, "Mouse had no name???"); // logic error + } + + const auto HASCONFIG = g_pConfigManager->deviceConfigExists(PMOUSE->name); if (wlr_input_device_is_libinput(mouse)) { const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(mouse); - if (g_pConfigManager->getInt("input:touchpad:clickfinger_behavior") == 0) // toggle software buttons or clickfinger + if ((HASCONFIG ? g_pConfigManager->getDeviceInt(PMOUSE->name, "clickfinger_behavior") : g_pConfigManager->getInt("input:touchpad:clickfinger_behavior")) == 0) // toggle software buttons or clickfinger libinput_device_config_click_set_method(LIBINPUTDEV, LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS); else libinput_device_config_click_set_method(LIBINPUTDEV, LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER); if (libinput_device_config_middle_emulation_is_available(LIBINPUTDEV)) { // middleclick on r+l mouse button pressed - if (g_pConfigManager->getInt("input:touchpad:middle_button_emulation") == 1) + if ((HASCONFIG ? g_pConfigManager->getDeviceInt(PMOUSE->name, "middle_button_emulation") : g_pConfigManager->getInt("input:touchpad:middle_button_emulation")) == 1) libinput_device_config_middle_emulation_set_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED); else libinput_device_config_middle_emulation_set_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED); } if (libinput_device_config_tap_get_finger_count(LIBINPUTDEV)) // this is for tapping (like on a laptop) - if (g_pConfigManager->getInt("input:touchpad:tap-to-click") == 1) + if ((HASCONFIG ? g_pConfigManager->getDeviceInt(PMOUSE->name, "tap-to-click") : g_pConfigManager->getInt("input:touchpad:tap-to-click")) == 1) libinput_device_config_tap_set_enabled(LIBINPUTDEV, LIBINPUT_CONFIG_TAP_ENABLED); if (libinput_device_config_scroll_has_natural_scroll(LIBINPUTDEV)) { double w = 0, h = 0; if (libinput_device_has_capability(LIBINPUTDEV, LIBINPUT_DEVICE_CAP_POINTER) && libinput_device_get_size(LIBINPUTDEV, &w, &h) == 0) // pointer with size is a touchpad - libinput_device_config_scroll_set_natural_scroll_enabled(LIBINPUTDEV, g_pConfigManager->getInt("input:touchpad:natural_scroll")); + libinput_device_config_scroll_set_natural_scroll_enabled(LIBINPUTDEV, (HASCONFIG ? g_pConfigManager->getDeviceInt(PMOUSE->name, "natural_scroll") : g_pConfigManager->getInt("input:touchpad:natural_scroll"))); else - libinput_device_config_scroll_set_natural_scroll_enabled(LIBINPUTDEV, g_pConfigManager->getInt("input:natural_scroll")); + libinput_device_config_scroll_set_natural_scroll_enabled(LIBINPUTDEV, (HASCONFIG ? g_pConfigManager->getDeviceInt(PMOUSE->name, "natural_scroll") : g_pConfigManager->getInt("input:natural_scroll"))); } if (libinput_device_config_dwt_is_available(LIBINPUTDEV)) { - const auto DWT = static_cast(g_pConfigManager->getInt("input:touchpad:disable_while_typing") != 0); + const auto DWT = static_cast((HASCONFIG ? g_pConfigManager->getDeviceInt(PMOUSE->name, "disable_while_typing") : g_pConfigManager->getInt("input:touchpad:disable_while_typing")) != 0); libinput_device_config_dwt_set_enabled(LIBINPUTDEV, DWT); } } diff --git a/src/managers/input/Tablets.cpp b/src/managers/input/Tablets.cpp index 4285d06b..6a62e58e 100644 --- a/src/managers/input/Tablets.cpp +++ b/src/managers/input/Tablets.cpp @@ -4,6 +4,12 @@ void CInputManager::newTabletTool(wlr_input_device* pDevice) { const auto PNEWTABLET = &m_lTablets.emplace_back(); + try { + PNEWTABLET->name = std::string(pDevice->name); + } catch (std::exception& e) { + Debug::log(ERR, "Tablet had no name???"); // logic error + } + PNEWTABLET->wlrTablet = pDevice->tablet; PNEWTABLET->wlrDevice = pDevice; PNEWTABLET->wlrTabletV2 = wlr_tablet_create(g_pCompositor->m_sWLRTabletManager, g_pCompositor->m_sSeat.seat, pDevice); @@ -151,6 +157,12 @@ STabletTool* CInputManager::ensureTabletToolPresent(wlr_tablet_tool* pTool) { void CInputManager::newTabletPad(wlr_input_device* pDevice) { const auto PNEWPAD = &m_lTabletPads.emplace_back(); + try { + PNEWPAD->name = std::string(pDevice->name); + } catch (std::exception& e) { + Debug::log(ERR, "Pad had no name???"); // logic error + } + PNEWPAD->wlrTabletPadV2 = wlr_tablet_pad_create(g_pCompositor->m_sWLRTabletManager, g_pCompositor->m_sSeat.seat, pDevice); PNEWPAD->hyprListener_Button.initCallback(&pDevice->tablet_pad->events.button, [](void* owner, void* data) {