summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorvaxerski <vaxry@vaxry.net>2025-01-19 15:39:19 +0100
committervaxerski <vaxry@vaxry.net>2025-01-19 16:11:12 +0100
commit8dd2cd41fb4c5a5eb6886cc190419b36084cabfa (patch)
tree713dfeb4f2bf75c2a04ed93166b5f9109dac9a84 /src
parent0a0e56d99c3b5f900ec561b3fe9a3c8a64fe1217 (diff)
core: move to inotify for monitoring the config files
instead of manually polling every second which is not efficient, use inotify. an added bonus is that inotify is much much faster
Diffstat (limited to 'src')
-rw-r--r--src/Compositor.cpp7
-rw-r--r--src/Compositor.hpp1
-rw-r--r--src/config/ConfigManager.cpp70
-rw-r--r--src/config/ConfigManager.hpp7
-rw-r--r--src/config/ConfigWatcher.cpp72
-rw-r--r--src/config/ConfigWatcher.hpp32
-rw-r--r--src/debug/HyprCtl.cpp7
-rw-r--r--src/managers/ThreadManager.cpp26
-rw-r--r--src/managers/ThreadManager.hpp16
-rw-r--r--src/managers/eventLoop/EventLoopManager.cpp11
-rw-r--r--src/managers/eventLoop/EventLoopManager.hpp2
-rw-r--r--src/plugins/PluginAPI.cpp3
-rw-r--r--src/plugins/PluginSystem.cpp5
13 files changed, 143 insertions, 116 deletions
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index 889ae181..325bd882 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -4,6 +4,7 @@
#include "debug/Log.hpp"
#include "helpers/Splashes.hpp"
#include "config/ConfigValue.hpp"
+#include "config/ConfigWatcher.hpp"
#include "managers/CursorManager.hpp"
#include "managers/TokenManager.hpp"
#include "managers/PointerManager.hpp"
@@ -44,7 +45,6 @@
#include "managers/KeybindManager.hpp"
#include "managers/SessionLockManager.hpp"
-#include "managers/ThreadManager.hpp"
#include "managers/XWaylandManager.hpp"
#include "config/ConfigManager.hpp"
@@ -551,7 +551,6 @@ void CCompositor::cleanup() {
g_pProtocolManager.reset();
g_pHyprRenderer.reset();
g_pHyprOpenGL.reset();
- g_pThreadManager.reset();
g_pConfigManager.reset();
g_pLayoutManager.reset();
g_pHyprError.reset();
@@ -567,6 +566,7 @@ void CCompositor::cleanup() {
g_pEventLoopManager.reset();
g_pVersionKeeperMgr.reset();
g_pDonationNagManager.reset();
+ g_pConfigWatcher.reset();
if (m_pAqBackend)
m_pAqBackend.reset();
@@ -631,9 +631,6 @@ void CCompositor::initManagers(eManagersInitStage stage) {
g_pSeatManager = std::make_unique<CSeatManager>();
} break;
case STAGE_LATE: {
- Debug::log(LOG, "Creating the ThreadManager!");
- g_pThreadManager = std::make_unique<CThreadManager>();
-
Debug::log(LOG, "Creating CHyprCtl");
g_pHyprCtl = std::make_unique<CHyprCtl>();
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index 0a701a59..430e583c 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -5,7 +5,6 @@
#include <sys/resource.h>
#include "defines.hpp"
-#include "managers/ThreadManager.hpp"
#include "managers/XWaylandManager.hpp"
#include "managers/KeybindManager.hpp"
#include "managers/SessionLockManager.hpp"
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 52f2e316..ebeb2397 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -1,6 +1,7 @@
#include <re2/re2.h>
#include "ConfigManager.hpp"
+#include "ConfigWatcher.hpp"
#include "../managers/KeybindManager.hpp"
#include "../Compositor.hpp"
@@ -372,8 +373,8 @@ static Hyprlang::CParseResult handlePlugin(const char* c, const char* v) {
CConfigManager::CConfigManager() {
const auto ERR = verifyConfigExists();
- configPaths.emplace_back(getMainConfigPath());
- m_pConfig = std::make_unique<Hyprlang::CConfig>(configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true});
+ m_configPaths.emplace_back(getMainConfigPath());
+ m_pConfig = std::make_unique<Hyprlang::CConfig>(m_configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true});
m_pConfig->addConfigValue("general:border_size", Hyprlang::INT{1});
m_pConfig->addConfigValue("general:no_border_on_floating", Hyprlang::INT{0});
@@ -795,7 +796,7 @@ std::string CConfigManager::getConfigString() {
std::string configString;
std::string currFileContent;
- for (const auto& path : configPaths) {
+ for (const auto& path : m_configPaths) {
std::ifstream configFile(path);
configString += ("\n\nConfig File: " + path + ": ");
if (!configFile.is_open()) {
@@ -883,10 +884,10 @@ std::optional<std::string> CConfigManager::resetHLConfig() {
finalExecRequests.clear();
// paths
- configPaths.clear();
+ m_configPaths.clear();
std::string mainConfigPath = getMainConfigPath();
Debug::log(LOG, "Using config: {}", mainConfigPath);
- configPaths.push_back(mainConfigPath);
+ m_configPaths.emplace_back(mainConfigPath);
const auto RET = verifyConfigExists();
@@ -897,6 +898,8 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("render:explicit_sync");
static int prevEnabledExplicit = *PENABLEEXPLICIT;
+ g_pConfigWatcher->setWatchList(m_configPaths);
+
for (auto const& w : g_pCompositor->m_vWindows) {
w->uncacheWindowDecos();
}
@@ -1029,17 +1032,14 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
void CConfigManager::init() {
+ g_pConfigWatcher->setOnChange([this](const CConfigWatcher::SConfigWatchEvent& e) {
+ Debug::log(LOG, "CConfigManager: file {} modified, reloading", e.file);
+ reload();
+ });
+
const std::string CONFIGPATH = getMainConfigPath();
reload();
- struct stat fileStat;
- int err = stat(CONFIGPATH.c_str(), &fileStat);
- if (err != 0) {
- Debug::log(WARN, "Error at statting config, error {}", errno);
- }
-
- configModifyTimes[CONFIGPATH] = fileStat.st_mtime;
-
isFirstLaunch = false;
}
@@ -1080,37 +1080,6 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::
return RET.error ? RET.getError() : "";
}
-void CConfigManager::tick() {
- std::string CONFIGPATH = getMainConfigPath();
- if (!std::filesystem::exists(CONFIGPATH)) {
- Debug::log(ERR, "Config doesn't exist??");
- return;
- }
-
- bool parse = false;
-
- for (auto const& cf : configPaths) {
- struct stat fileStat;
- int err = stat(cf.c_str(), &fileStat);
- if (err != 0) {
- Debug::log(WARN, "Error at ticking config at {}, error {}: {}", cf, err, strerror(err));
- continue;
- }
-
- // check if we need to reload cfg
- if (fileStat.st_mtime != configModifyTimes[cf] || m_bForceReload) {
- parse = true;
- configModifyTimes[cf] = fileStat.st_mtime;
- }
- }
-
- if (parse) {
- m_bForceReload = false;
-
- reload();
- }
-}
-
Hyprlang::CConfigValue* CConfigManager::getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback) {
const auto VAL = m_pConfig->getSpecialConfigValuePtr("device", val.c_str(), dev.c_str());
@@ -1719,8 +1688,7 @@ void CConfigManager::handlePluginLoads() {
if (pluginsChanged) {
g_pHyprError->destroy();
- m_bForceReload = true;
- tick();
+ reload();
}
}
@@ -2732,16 +2700,8 @@ std::optional<std::string> CConfigManager::handleSource(const std::string& comma
Debug::log(ERR, "source= file doesn't exist: {}", value);
return "source= file " + value + " doesn't exist!";
}
- configPaths.push_back(value);
-
- struct stat fileStat;
- int err = stat(value.c_str(), &fileStat);
- if (err != 0) {
- Debug::log(WARN, "Error at ticking config at {}, error {}: {}", value, err, strerror(err));
- return {};
- }
+ m_configPaths.emplace_back(value);
- configModifyTimes[value] = fileStat.st_mtime;
auto configCurrentPathBackup = configCurrentPath;
configCurrentPath = value;
diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp
index bef52584..1962e4d2 100644
--- a/src/config/ConfigManager.hpp
+++ b/src/config/ConfigManager.hpp
@@ -142,8 +142,8 @@ class CConfigManager {
public:
CConfigManager();
- void tick();
void init();
+ void reload();
int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = "");
float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = "");
@@ -258,15 +258,13 @@ class CConfigManager {
{"scrolltouchpad", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollTouchpad; }}};
bool m_bWantsMonitorReload = false;
- bool m_bForceReload = false;
bool m_bNoMonitorReload = false;
bool isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
private:
std::unique_ptr<Hyprlang::CConfig> m_pConfig;
- std::vector<std::string> configPaths; // stores all the config paths
- std::unordered_map<std::string, time_t> configModifyTimes; // stores modify times
+ std::vector<std::string> m_configPaths;
Hyprutils::Animation::CAnimationConfigTree m_AnimationTree;
@@ -302,7 +300,6 @@ class CConfigManager {
std::optional<std::string> generateConfig(std::string configPath);
std::optional<std::string> verifyConfigExists();
void postConfigReload(const Hyprlang::CParseResult& result);
- void reload();
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
};
diff --git a/src/config/ConfigWatcher.cpp b/src/config/ConfigWatcher.cpp
new file mode 100644
index 00000000..38191e9e
--- /dev/null
+++ b/src/config/ConfigWatcher.cpp
@@ -0,0 +1,72 @@
+#include "ConfigWatcher.hpp"
+#include <sys/inotify.h>
+#include "../debug/Log.hpp"
+#include <ranges>
+#include <fcntl.h>
+
+CConfigWatcher::CConfigWatcher() : m_inotifyFd(inotify_init()) {
+ if (m_inotifyFd < 0) {
+ Debug::log(ERR, "CConfigWatcher couldn't open an inotify node. Config will not be automatically reloaded");
+ return;
+ }
+
+ const int FLAGS = fcntl(m_inotifyFd, F_GETFL, 0);
+ if (fcntl(m_inotifyFd, F_SETFL, FLAGS | O_NONBLOCK) < 0) {
+ Debug::log(ERR, "CConfigWatcher couldn't non-block inotify node. Config will not be automatically reloaded");
+ close(m_inotifyFd);
+ m_inotifyFd = -1;
+ return;
+ }
+}
+
+CConfigWatcher::~CConfigWatcher() {
+ if (m_inotifyFd >= 0)
+ close(m_inotifyFd);
+}
+
+int CConfigWatcher::getInotifyFD() {
+ return m_inotifyFd;
+}
+
+void CConfigWatcher::setWatchList(const std::vector<std::string>& paths) {
+
+ // we clear all watches first, because whichever fired is now invalid
+ // or that is at least what it seems to be.
+ // since we don't know which fired,
+ // plus it doesn't matter that much, these ops are done rarely and fast anyways.
+
+ // cleanup old paths
+ for (auto& watch : m_watches) {
+ inotify_rm_watch(m_inotifyFd, watch.wd);
+ }
+
+ m_watches.clear();
+
+ // add new paths
+ for (const auto& path : paths) {
+ m_watches.emplace_back(SInotifyWatch{
+ .wd = inotify_add_watch(m_inotifyFd, path.c_str(), IN_MODIFY),
+ .file = path,
+ });
+ }
+}
+
+void CConfigWatcher::setOnChange(const std::function<void(const SConfigWatchEvent&)>& fn) {
+ m_watchCallback = fn;
+}
+
+void CConfigWatcher::onInotifyEvent() {
+ inotify_event ev;
+ while (read(m_inotifyFd, &ev, sizeof(ev)) > 0) {
+ const auto WD = std::ranges::find_if(m_watches.begin(), m_watches.end(), [wd = ev.wd](const auto& e) { return e.wd == wd; });
+
+ if (WD == m_watches.end()) {
+ Debug::log(ERR, "CConfigWatcher: got an event for wd {} which we don't have?!", ev.wd);
+ return;
+ }
+
+ m_watchCallback(SConfigWatchEvent{
+ .file = WD->file,
+ });
+ }
+}
diff --git a/src/config/ConfigWatcher.hpp b/src/config/ConfigWatcher.hpp
new file mode 100644
index 00000000..0a698fc8
--- /dev/null
+++ b/src/config/ConfigWatcher.hpp
@@ -0,0 +1,32 @@
+#pragma once
+#include <memory>
+#include <vector>
+#include <string>
+#include <functional>
+
+class CConfigWatcher {
+ public:
+ CConfigWatcher();
+ ~CConfigWatcher();
+
+ struct SConfigWatchEvent {
+ std::string file;
+ };
+
+ int getInotifyFD();
+ void setWatchList(const std::vector<std::string>& paths);
+ void setOnChange(const std::function<void(const SConfigWatchEvent&)>& fn);
+ void onInotifyEvent();
+
+ private:
+ struct SInotifyWatch {
+ int wd = -1;
+ std::string file;
+ };
+
+ std::function<void(const SConfigWatchEvent&)> m_watchCallback;
+ std::vector<SInotifyWatch> m_watches;
+ int m_inotifyFd = -1;
+};
+
+inline std::unique_ptr<CConfigWatcher> g_pConfigWatcher = std::make_unique<CConfigWatcher>(); \ No newline at end of file
diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp
index 41b0abe1..cfdba100 100644
--- a/src/debug/HyprCtl.cpp
+++ b/src/debug/HyprCtl.cpp
@@ -1121,13 +1121,10 @@ static std::string reloadRequest(eHyprCtlOutputFormat format, std::string reques
const auto REQMODE = request.substr(request.find_last_of(' ') + 1);
- g_pConfigManager->m_bForceReload = true;
-
- if (REQMODE == "config-only") {
+ if (REQMODE == "config-only")
g_pConfigManager->m_bNoMonitorReload = true;
- }
- g_pConfigManager->tick();
+ g_pConfigManager->reload();
return "ok";
}
diff --git a/src/managers/ThreadManager.cpp b/src/managers/ThreadManager.cpp
deleted file mode 100644
index bd124c99..00000000
--- a/src/managers/ThreadManager.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "ThreadManager.hpp"
-#include "../debug/HyprCtl.hpp"
-#include "../Compositor.hpp"
-#include "../config/ConfigValue.hpp"
-
-static int handleTimer(void* data) {
- const auto PTM = (CThreadManager*)data;
-
- static auto PDISABLECFGRELOAD = CConfigValue<Hyprlang::INT>("misc:disable_autoreload");
-
- if (*PDISABLECFGRELOAD != 1)
- g_pConfigManager->tick();
-
- wl_event_source_timer_update(PTM->m_esConfigTimer, 1000);
-
- return 0;
-}
-
-CThreadManager::CThreadManager() : m_esConfigTimer(wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, handleTimer, this)) {
- wl_event_source_timer_update(m_esConfigTimer, 1000);
-}
-
-CThreadManager::~CThreadManager() {
- if (m_esConfigTimer)
- wl_event_source_remove(m_esConfigTimer);
-}
diff --git a/src/managers/ThreadManager.hpp b/src/managers/ThreadManager.hpp
deleted file mode 100644
index 13e2fcd8..00000000
--- a/src/managers/ThreadManager.hpp
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-#include "../defines.hpp"
-struct wl_event_source;
-
-class CThreadManager {
- public:
- CThreadManager();
- ~CThreadManager();
-
- wl_event_source* m_esConfigTimer = nullptr;
-
- private:
-};
-
-inline std::unique_ptr<CThreadManager> g_pThreadManager; \ No newline at end of file
diff --git a/src/managers/eventLoop/EventLoopManager.cpp b/src/managers/eventLoop/EventLoopManager.cpp
index db8b49b6..f98efc92 100644
--- a/src/managers/eventLoop/EventLoopManager.cpp
+++ b/src/managers/eventLoop/EventLoopManager.cpp
@@ -1,6 +1,7 @@
#include "EventLoopManager.hpp"
#include "../../debug/Log.hpp"
#include "../../Compositor.hpp"
+#include "../../config/ConfigWatcher.hpp"
#include <algorithm>
#include <limits>
@@ -27,6 +28,8 @@ CEventLoopManager::~CEventLoopManager() {
wl_event_source_remove(m_sWayland.eventSource);
if (m_sIdle.eventSource)
wl_event_source_remove(m_sIdle.eventSource);
+ if (m_configWatcherInotifySource)
+ wl_event_source_remove(m_configWatcherInotifySource);
if (m_sTimers.timerfd >= 0)
close(m_sTimers.timerfd);
}
@@ -42,9 +45,17 @@ static int aquamarineFDWrite(int fd, uint32_t mask, void* data) {
return 1;
}
+static int configWatcherWrite(int fd, uint32_t mask, void* data) {
+ g_pConfigWatcher->onInotifyEvent();
+ return 0;
+}
+
void CEventLoopManager::enterLoop() {
m_sWayland.eventSource = wl_event_loop_add_fd(m_sWayland.loop, m_sTimers.timerfd, WL_EVENT_READABLE, timerWrite, nullptr);
+ if (const auto FD = g_pConfigWatcher->getInotifyFD(); FD >= 0)
+ m_configWatcherInotifySource = wl_event_loop_add_fd(m_sWayland.loop, FD, WL_EVENT_READABLE, configWatcherWrite, nullptr);
+
aqPollFDs = g_pCompositor->m_pAqBackend->getPollFDs();
for (auto const& fd : aqPollFDs) {
m_sWayland.aqEventSources.emplace_back(wl_event_loop_add_fd(m_sWayland.loop, fd->fd, WL_EVENT_READABLE, aquamarineFDWrite, fd.get()));
diff --git a/src/managers/eventLoop/EventLoopManager.hpp b/src/managers/eventLoop/EventLoopManager.hpp
index 39d8bbeb..90402de2 100644
--- a/src/managers/eventLoop/EventLoopManager.hpp
+++ b/src/managers/eventLoop/EventLoopManager.hpp
@@ -51,6 +51,8 @@ class CEventLoopManager {
SIdleData m_sIdle;
std::vector<SP<Aquamarine::SPollFD>> aqPollFDs;
+ wl_event_source* m_configWatcherInotifySource = nullptr;
+
friend class CSyncTimeline;
};
diff --git a/src/plugins/PluginAPI.cpp b/src/plugins/PluginAPI.cpp
index b6bbc460..fbb1ec1b 100644
--- a/src/plugins/PluginAPI.cpp
+++ b/src/plugins/PluginAPI.cpp
@@ -4,6 +4,7 @@
#include "../plugins/PluginSystem.hpp"
#include "../managers/HookSystemManager.hpp"
#include "../managers/LayoutManager.hpp"
+#include "../managers/eventLoop/EventLoopManager.hpp"
#include "../config/ConfigManager.hpp"
#include "../debug/HyprNotificationOverlay.hpp"
#include <dlfcn.h>
@@ -72,7 +73,7 @@ APICALL bool HyprlandAPI::removeLayout(HANDLE handle, IHyprLayout* layout) {
}
APICALL bool HyprlandAPI::reloadConfig() {
- g_pConfigManager->m_bForceReload = true;
+ g_pEventLoopManager->doLater([] { g_pConfigManager->reload(); });
return true;
}
diff --git a/src/plugins/PluginSystem.cpp b/src/plugins/PluginSystem.cpp
index ce7dd7c7..e849bf11 100644
--- a/src/plugins/PluginSystem.cpp
+++ b/src/plugins/PluginSystem.cpp
@@ -5,6 +5,7 @@
#include "../config/ConfigManager.hpp"
#include "../managers/LayoutManager.hpp"
#include "../managers/HookSystemManager.hpp"
+#include "../managers/eventLoop/EventLoopManager.hpp"
CPluginSystem::CPluginSystem() {
g_pFunctionHookSystem = std::make_unique<CHookSystem>();
@@ -82,7 +83,7 @@ CPlugin* CPluginSystem::loadPlugin(const std::string& path) {
PLUGIN->version = PLUGINDATA.version;
PLUGIN->name = PLUGINDATA.name;
- g_pConfigManager->m_bForceReload = true;
+ g_pEventLoopManager->doLater([] { g_pConfigManager->reload(); });
Debug::log(LOG, R"( [PluginSystem] Plugin {} loaded. Handle: {:x}, path: "{}", author: "{}", description: "{}", version: "{}")", PLUGINDATA.name, (uintptr_t)MODULE, path,
PLUGINDATA.author, PLUGINDATA.description, PLUGINDATA.version);
@@ -137,7 +138,7 @@ void CPluginSystem::unloadPlugin(const CPlugin* plugin, bool eject) {
Debug::log(LOG, " [PluginSystem] Plugin {} unloaded.", PLNAME);
// reload config to fix some stuf like e.g. unloadedPluginVars
- g_pConfigManager->m_bForceReload = true;
+ g_pEventLoopManager->doLater([] { g_pConfigManager->reload(); });
}
void CPluginSystem::unloadAllPlugins() {