Hypr/src/config/ConfigManager.cpp

509 lines
17 KiB
C++
Raw Normal View History

2021-11-21 12:40:03 +01:00
#include "ConfigManager.hpp"
#include "../windowManager.hpp"
2021-11-21 13:11:51 +01:00
#include "../KeybindManager.hpp"
2021-11-21 12:40:03 +01:00
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fstream>
#include <algorithm>
#include <string.h>
2021-11-21 12:40:03 +01:00
#include <iostream>
void ConfigManager::init() {
configValues["border_size"].intValue = 1;
configValues["gaps_in"].intValue = 5;
configValues["gaps_out"].intValue = 20;
configValues["rounding"].intValue = 5;
configValues["main_mod"].strValue = "SUPER";
2021-12-21 12:22:41 +01:00
configValues["intelligent_transients"].intValue = 1;
2022-01-06 16:38:41 +01:00
configValues["no_unmap_saving"].intValue = 1;
2021-11-21 20:40:22 +01:00
2021-12-05 12:05:44 +01:00
configValues["focus_when_hover"].intValue = 1;
2021-12-04 23:16:24 +01:00
configValues["layout"].intValue = LAYOUT_DWINDLE;
configValues["layout:no_gaps_when_only"].intValue = 0;
2021-12-04 23:16:24 +01:00
2021-11-21 12:40:03 +01:00
configValues["max_fps"].intValue = 60;
2021-11-27 19:07:33 +01:00
configValues["bar:monitor"].intValue = 0;
configValues["bar:enabled"].intValue = 1;
configValues["bar:height"].intValue = 15;
configValues["bar:col.bg"].intValue = 0xFF111111;
configValues["bar:col.high"].intValue = 0xFFFF3333;
2021-11-30 21:11:27 +01:00
configValues["bar:font.main"].strValue = "Noto Sans";
configValues["bar:font.secondary"].strValue = "Noto Sans";
configValues["bar:mod_pad_in"].intValue = 4;
configValues["bar:no_tray_saving"].intValue = 1;
2021-11-21 20:40:22 +01:00
2021-11-30 21:11:27 +01:00
configValues["status_command"].strValue = "date +%I:%M\\ %p"; // Time // Deprecated
2021-11-22 19:06:00 +01:00
// Set Colors ARGB
configValues["col.active_border"].intValue = 0x77FF3333;
configValues["col.inactive_border"].intValue = 0x77222222;
// animations
configValues["animations:speed"].floatValue = 1;
configValues["animations:enabled"].intValue = 0;
configValues["animations:cheap"].intValue = 1;
configValues["animations:borders"].intValue = 1;
configValues["animations:workspaces"].intValue = 0;
2021-11-30 20:10:37 +01:00
if (!g_pWindowManager->statusBar) {
isFirstLaunch = true;
}
2021-12-23 22:40:41 +01:00
lastModifyTime = 0;
tick();
2021-11-23 16:49:53 +01:00
applyKeybindsToX();
2021-11-21 12:40:03 +01:00
}
2021-11-27 19:07:33 +01:00
void configSetValueSafe(const std::string& COMMAND, const std::string& VALUE) {
if (ConfigManager::configValues.find(COMMAND) == ConfigManager::configValues.end()) {
ConfigManager::parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">: No such field.";
2021-11-27 19:07:33 +01:00
return;
}
2021-11-27 19:07:33 +01:00
auto& CONFIGENTRY = ConfigManager::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);
} else
CONFIGENTRY.intValue = stol(VALUE);
} catch (...) {
Debug::log(WARN, "Error reading value of " + COMMAND);
ConfigManager::parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">.";
2021-11-27 19:07:33 +01:00
}
} else if (CONFIGENTRY.floatValue != -1) {
try {
CONFIGENTRY.floatValue = stof(VALUE);
} catch (...) {
Debug::log(WARN, "Error reading value of " + COMMAND);
ConfigManager::parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">.";
2021-11-27 19:07:33 +01:00
}
} else if (CONFIGENTRY.strValue != "") {
try {
CONFIGENTRY.strValue = VALUE;
} catch (...) {
Debug::log(WARN, "Error reading value of " + COMMAND);
ConfigManager::parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">.";
2021-11-27 19:07:33 +01:00
}
}
}
2021-11-21 13:11:51 +01:00
void handleBind(const std::string& command, const std::string& value) {
// example:
// bind=SUPER,G,exec,dmenu_run <args>
auto valueCopy = value;
const auto MOD = valueCopy.substr(0, valueCopy.find_first_of(","));
valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1);
const auto KEY = KeybindManager::getKeyCodeFromName(valueCopy.substr(0, valueCopy.find_first_of(",")));
valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1);
const auto HANDLER = valueCopy.substr(0, valueCopy.find_first_of(","));
valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1);
const auto COMMAND = valueCopy;
Dispatcher dispatcher = nullptr;
if (HANDLER == "exec") dispatcher = KeybindManager::call;
if (HANDLER == "killactive") dispatcher = KeybindManager::killactive;
if (HANDLER == "fullscreen") dispatcher = KeybindManager::toggleActiveWindowFullscreen;
2021-11-25 16:54:08 +01:00
if (HANDLER == "movewindow") dispatcher = KeybindManager::movewindow;
2021-12-05 12:05:44 +01:00
if (HANDLER == "movefocus") dispatcher = KeybindManager::movefocus;
if (HANDLER == "movetoworkspace") dispatcher = KeybindManager::movetoworkspace;
2021-11-21 13:11:51 +01:00
if (HANDLER == "workspace") dispatcher = KeybindManager::changeworkspace;
if (HANDLER == "lastworkspace") dispatcher = KeybindManager::changetolastworkspace;
2021-11-21 15:15:33 +01:00
if (HANDLER == "togglefloating") dispatcher = KeybindManager::toggleActiveWindowFloating;
2021-11-21 13:11:51 +01:00
2021-12-27 09:40:37 +01:00
if (dispatcher && KEY != 0)
2021-12-06 15:04:47 +01:00
KeybindManager::keybinds.push_back(Keybind(KeybindManager::modToMask(MOD), KEY, COMMAND, dispatcher));
2021-11-21 13:11:51 +01:00
}
2021-11-24 19:17:13 +01:00
void handleRawExec(const std::string& command, const std::string& args) {
2021-11-30 20:13:56 +01:00
// Exec in the background dont wait for it.
2021-12-23 22:40:41 +01:00
RETURNIFBAR;
2021-11-30 20:13:56 +01:00
if (fork() == 0) {
execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr);
_exit(0);
}
2021-11-24 19:17:13 +01:00
}
void handleStatusCommand(const std::string& command, const std::string& args) {
if (g_pWindowManager->statusBar)
g_pWindowManager->statusBar->setStatusCommand(args);
2021-11-24 19:17:13 +01:00
}
2021-11-27 19:07:33 +01:00
void parseModule(const std::string& COMMANDC, const std::string& VALUE) {
2022-03-01 20:40:44 +01:00
SBarModule* module = new SBarModule();
g_pWindowManager->statusBar->modules.push_back(module);
2021-11-27 19:07:33 +01:00
auto valueCopy = VALUE;
const auto ALIGN = valueCopy.substr(0, valueCopy.find_first_of(","));
valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1);
if (ALIGN == "pad") {
const auto ALIGNR = valueCopy.substr(0, valueCopy.find_first_of(","));
valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1);
const auto PADW = valueCopy;
2022-03-01 20:40:44 +01:00
if (ALIGNR == "left") module->alignment = LEFT;
else if (ALIGNR == "right") module->alignment = RIGHT;
else if (ALIGNR == "center") module->alignment = CENTER;
2021-11-27 19:07:33 +01:00
try {
2022-03-01 20:40:44 +01:00
module->pad = stol(PADW);
2021-11-27 19:07:33 +01:00
} catch (...) {
Debug::log(ERR, "Module creation pad error: invalid pad");
ConfigManager::parseError = "Module creation error in pad: invalid pad.";
2021-11-27 19:07:33 +01:00
return;
}
2022-03-01 20:40:44 +01:00
module->isPad = true;
2021-11-27 19:07:33 +01:00
2022-03-01 20:40:44 +01:00
module->color = 0;
module->bgcolor = 0;
2021-11-27 19:07:33 +01:00
return;
}
2021-11-30 21:11:27 +01:00
const auto ICON = valueCopy.substr(0, valueCopy.find_first_of(","));
valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1);
2021-11-27 19:07:33 +01:00
const auto COL1 = valueCopy.substr(0, valueCopy.find_first_of(","));
valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1);
const auto COL2 = valueCopy.substr(0, valueCopy.find_first_of(","));
valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1);
const auto UPDATE = valueCopy.substr(0, valueCopy.find_first_of(","));
valueCopy = valueCopy.substr(valueCopy.find_first_of(",") + 1);
const auto COMMAND = valueCopy;
2022-03-01 20:40:44 +01:00
if (ALIGN == "left") module->alignment = LEFT;
else if (ALIGN == "right") module->alignment = RIGHT;
else if (ALIGN == "center") module->alignment = CENTER;
2021-11-27 19:07:33 +01:00
try {
2022-03-01 20:40:44 +01:00
module->color = stol(COL1.substr(2), nullptr, 16);
module->bgcolor = stol(COL2.substr(2), nullptr, 16);
2021-11-27 19:07:33 +01:00
} catch (...) {
Debug::log(ERR, "Module creation color error: invalid color");
ConfigManager::parseError = "Module creation error in color: invalid color.";
2022-03-01 20:40:44 +01:00
g_pWindowManager->statusBar->modules.pop_back();
delete module;
2021-11-27 19:07:33 +01:00
return;
}
try {
2022-03-01 20:40:44 +01:00
module->updateEveryMs = stol(UPDATE);
2021-11-27 19:07:33 +01:00
} catch (...) {
Debug::log(ERR, "Module creation error: invalid update interval");
ConfigManager::parseError = "Module creation error in interval: invalid interval.";
2022-03-01 20:40:44 +01:00
g_pWindowManager->statusBar->modules.pop_back();
delete module;
2021-11-27 19:07:33 +01:00
return;
}
2022-03-01 20:40:44 +01:00
module->icon = ICON;
2021-11-30 21:11:27 +01:00
2022-03-01 20:40:44 +01:00
module->value = COMMAND;
2021-11-27 19:07:33 +01:00
}
2021-12-20 20:50:35 +01:00
void handleWindowRule(const std::string& command, const std::string& value) {
const auto RULE = value.substr(0, value.find_first_of(","));
const auto VALUE = value.substr(value.find_first_of(",") + 1);
// check rule and value
if (RULE == "" || VALUE == "") {
return;
}
// verify we support a rule
if (RULE != "float"
&& RULE != "tile"
&& RULE.find("move") != 0
&& RULE.find("size") != 0
2022-01-18 17:11:03 +01:00
&& RULE.find("nointerventions") != 0
&& RULE.find("monitor") != 0) {
2021-12-20 20:50:35 +01:00
Debug::log(ERR, "Invalid rule found: " + RULE);
ConfigManager::parseError = "Invalid rule found: " + RULE;
return;
}
ConfigManager::windowRules.push_back({RULE, VALUE});
}
2021-11-21 12:40:03 +01:00
void parseLine(std::string& line) {
// first check if its not a comment
const auto COMMENTSTART = line.find_first_of('#');
if (COMMENTSTART == 0)
return;
// now, cut the comment off
if (COMMENTSTART != std::string::npos)
2021-12-10 21:57:08 +01:00
line = line.substr(0, COMMENTSTART);
2021-11-21 12:40:03 +01:00
2021-11-27 19:07:33 +01:00
// remove shit at the beginning
while (line[0] == ' ' || line[0] == '\t') {
line = line.substr(1);
}
if (line.find(" {") != std::string::npos) {
auto cat = line.substr(0, line.find(" {"));
transform(cat.begin(), cat.end(), cat.begin(), ::tolower);
ConfigManager::currentCategory = cat;
2021-11-27 19:07:33 +01:00
return;
}
if (line.find("}") != std::string::npos && ConfigManager::currentCategory != "") {
ConfigManager::currentCategory = "";
return;
}
2021-11-21 12:40:03 +01:00
// And parse
// check if command
const auto EQUALSPLACE = line.find_first_of('=');
if (EQUALSPLACE == std::string::npos)
return;
const auto COMMAND = line.substr(0, EQUALSPLACE);
const auto VALUE = line.substr(EQUALSPLACE + 1);
2021-11-21 13:11:51 +01:00
if (COMMAND == "bind") {
handleBind(COMMAND, VALUE);
return;
2021-11-24 19:17:13 +01:00
} else if (COMMAND == "exec") {
handleRawExec(COMMAND, VALUE);
return;
} else if (COMMAND == "exec-once") {
if (ConfigManager::isFirstLaunch)
handleRawExec(COMMAND, VALUE);
2021-11-30 20:10:37 +01:00
return;
2021-11-24 19:17:13 +01:00
} else if (COMMAND == "status_command") {
handleStatusCommand(COMMAND, VALUE);
return;
2021-12-20 20:50:35 +01:00
} else if (COMMAND == "windowrule") {
handleWindowRule(COMMAND, VALUE);
return;
2021-11-21 13:11:51 +01:00
}
if (COMMAND == "module" && ConfigManager::currentCategory == "bar") {
if (g_pWindowManager->statusBar)
parseModule(COMMAND, VALUE);gi
return;
}
configSetValueSafe(ConfigManager::currentCategory + (ConfigManager::currentCategory == "" ? "" : ":") + COMMAND, VALUE);
2021-11-21 12:40:03 +01:00
}
void ConfigManager::loadConfigLoadVars() {
2021-12-18 23:30:54 -08:00
const auto ORIGBORDERSIZE = configValues["border_size"].intValue;
2021-11-21 12:40:03 +01:00
Debug::log(LOG, "Reloading the config!");
ConfigManager::parseError = ""; // reset the error
ConfigManager::currentCategory = ""; // reset the category
2021-12-20 20:50:35 +01:00
ConfigManager::windowRules.clear(); // Clear rules
2021-11-21 12:40:03 +01:00
2021-11-27 19:07:33 +01:00
if (loadBar && g_pWindowManager->statusBar) {
// clear modules as we overwrite them
for (auto& m : g_pWindowManager->statusBar->modules) {
2022-03-01 20:40:44 +01:00
g_pWindowManager->statusBar->destroyModule(m);
2021-11-27 19:07:33 +01:00
}
g_pWindowManager->statusBar->modules.clear();
}
2021-11-21 13:11:51 +01:00
KeybindManager::keybinds.clear();
2021-11-21 12:40:03 +01:00
const char* const ENVHOME = getenv("HOME");
2021-11-30 21:11:27 +01:00
const std::string CONFIGPATH = ENVHOME + (ISDEBUG ? (std::string) "/.config/hypr/hyprd.conf" : (std::string) "/.config/hypr/hypr.conf");
2021-11-21 12:40:03 +01:00
std::ifstream ifs;
ifs.open(CONFIGPATH.c_str());
if (!ifs.good()) {
Debug::log(WARN, "Config reading error. (No file?)");
ConfigManager::parseError = "The config could not be read. (No file?)";
2021-12-08 22:27:53 +01:00
ifs.close();
2021-11-21 12:40:03 +01:00
return;
}
std::string line = "";
int linenum = 1;
2021-11-21 12:40:03 +01:00
if (ifs.is_open()) {
while (std::getline(ifs, line)) {
// Read line by line.
2021-11-21 13:11:51 +01:00
try {
parseLine(line);
} catch(...) {
Debug::log(ERR, "Error reading line from config. Line:");
Debug::log(NONE, line);
parseError = "Config error at line " + std::to_string(linenum) + ": Line parsing error.";
}
if (parseError != "" && parseError.find("Config error at line") != 0) {
parseError = "Config error at line " + std::to_string(linenum) + ": " + parseError;
2021-11-21 13:11:51 +01:00
}
++linenum;
2021-11-21 13:11:51 +01:00
2021-11-21 12:40:03 +01:00
}
ifs.close();
}
2021-11-21 12:40:03 +01:00
2021-12-04 23:16:24 +01:00
// recalc all workspaces
g_pWindowManager->recalcAllWorkspaces();
2021-11-21 12:40:03 +01:00
2021-11-21 19:59:59 +01:00
// Reload the bar as well, don't load it before the default is loaded.
if (loadBar && g_pWindowManager->statusBar && (configValues["bar:enabled"].intValue == 1 || parseError != "")) {
g_pWindowManager->statusBar->destroy();
// make the bar height visible
if (parseError != "") {
configValues["bar:height"].intValue = 15;
}
2021-12-17 23:31:12 +01:00
if (configValues["bar:monitor"].intValue > g_pWindowManager->monitors.size()) {
configValues["bar:monitor"].intValue = 0;
Debug::log(ERR, "Incorrect value in MonitorID for the bar. Setting to 0.");
}
2021-11-27 19:07:33 +01:00
g_pWindowManager->statusBar->setup(configValues["bar:monitor"].intValue);
} else if (g_pWindowManager->statusBar) {
g_pWindowManager->statusBar->destroy();
2021-11-21 19:59:59 +01:00
}
2021-12-04 23:16:24 +01:00
// Ensure correct layout
if (configValues["layout"].intValue < 0 || configValues["layout"].intValue > 1) {
Debug::log(ERR, "Invalid layout ID, falling back to 0.");
configValues["layout"].intValue = 0;
}
2021-11-21 19:59:59 +01:00
loadBar = true;
2021-11-30 20:10:37 +01:00
isFirstLaunch = false;
2021-12-18 23:23:51 -08:00
2021-12-18 23:30:54 -08:00
if (ORIGBORDERSIZE != configValues["border_size"].intValue) EWMH::refreshAllExtents();
2021-11-21 12:40:03 +01:00
}
2021-11-23 16:49:53 +01:00
void ConfigManager::applyKeybindsToX() {
if (g_pWindowManager->statusBar) {
Debug::log(LOG, "Not applying the keybinds because status bar not null");
return; // If we are in the status bar don't do this.
}
2021-12-27 09:34:20 +01:00
Debug::log(LOG, "Applying " + std::to_string(KeybindManager::keybinds.size()) + " keybinds to X.");
2021-11-23 16:49:53 +01:00
xcb_ungrab_key(g_pWindowManager->DisplayConnection, XCB_GRAB_ANY, g_pWindowManager->Screen->root, XCB_MOD_MASK_ANY);
xcb_ungrab_button(g_pWindowManager->DisplayConnection, XCB_GRAB_ANY, g_pWindowManager->Screen->root, XCB_MOD_MASK_ANY);
2021-11-23 16:49:53 +01:00
for (auto& keybind : KeybindManager::keybinds) {
xcb_grab_key(g_pWindowManager->DisplayConnection, 1, g_pWindowManager->Screen->root,
2021-12-06 15:04:47 +01:00
keybind.getMod(), KeybindManager::getKeycodeFromKeysym(keybind.getKeysym()),
2021-11-23 16:49:53 +01:00
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
}
xcb_flush(g_pWindowManager->DisplayConnection);
// MOD + mouse
xcb_grab_button(g_pWindowManager->DisplayConnection, 0,
g_pWindowManager->Screen->root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE,
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, g_pWindowManager->Screen->root, XCB_NONE,
1, KeybindManager::modToMask(configValues["main_mod"].strValue));
2021-11-23 16:49:53 +01:00
xcb_grab_button(g_pWindowManager->DisplayConnection, 0,
g_pWindowManager->Screen->root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE,
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, g_pWindowManager->Screen->root, XCB_NONE,
3, KeybindManager::modToMask(configValues["main_mod"].strValue));
2021-11-23 16:49:53 +01:00
xcb_flush(g_pWindowManager->DisplayConnection);
}
2021-11-21 12:40:03 +01:00
void ConfigManager::tick() {
const char* const ENVHOME = getenv("HOME");
2021-11-30 21:11:27 +01:00
const std::string CONFIGPATH = ENVHOME + (ISDEBUG ? (std::string) "/.config/hypr/hyprd.conf" : (std::string) "/.config/hypr/hypr.conf");
2021-11-21 12:40:03 +01:00
struct stat fileStat;
int err = stat(CONFIGPATH.c_str(), &fileStat);
if (err != 0) {
2021-12-08 22:27:53 +01:00
Debug::log(WARN, "Error at ticking config, error " + std::to_string(errno));
2021-11-21 12:40:03 +01:00
}
// check if we need to reload cfg
2021-12-08 22:27:53 +01:00
if(fileStat.st_mtime != lastModifyTime) {
2021-11-21 12:40:03 +01:00
lastModifyTime = fileStat.st_mtime;
ConfigManager::loadConfigLoadVars();
2021-11-23 16:49:53 +01:00
// So that X updates our grabbed keys.
ConfigManager::applyKeybindsToX();
2021-11-21 12:40:03 +01:00
// so that the WM reloads the windows.
emptyEvent();
}
}
2021-12-18 15:01:20 +01:00
int ConfigManager::getInt(std::string v) {
2021-11-21 12:40:03 +01:00
return configValues[v].intValue;
}
2021-12-18 15:01:20 +01:00
float ConfigManager::getFloat(std::string v) {
2021-11-21 12:40:03 +01:00
return configValues[v].floatValue;
}
2021-12-18 15:01:20 +01:00
std::string ConfigManager::getString(std::string v) {
2021-11-21 12:40:03 +01:00
return configValues[v].strValue;
}
std::vector<SWindowRule> ConfigManager::getMatchingRules(xcb_window_t w) {
const auto PWINDOW = g_pWindowManager->getWindowFromDrawable(w);
if (!PWINDOW)
return std::vector<SWindowRule>();
std::vector<SWindowRule> returns;
for (auto& rule : ConfigManager::windowRules) {
// check if we have a matching rule
if (rule.szValue.find("class:") == 0) {
std::regex classCheck(rule.szValue.substr(strlen("class:")));
if (!std::regex_search(PWINDOW->getClassName(), classCheck))
continue;
} else if (rule.szValue.find("role:") == 0) {
std::regex roleCheck(rule.szValue.substr(strlen("role:")));
if (!std::regex_search(PWINDOW->getRoleName(), roleCheck))
continue;
} else {
continue;
}
// applies. Read the rule and behave accordingly
Debug::log(LOG, "Window rule " + rule.szRule + "," + rule.szValue + " matched.");
returns.push_back(rule);
}
return returns;
}