summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVaxry <vaxry@vaxry.net>2025-01-24 20:30:12 +0000
committerVaxry <vaxry@vaxry.net>2025-01-24 20:30:21 +0000
commitd8f79d7678f428738f3173020109bd7b7fbd0840 (patch)
tree8efc7a843e1f82cd7fcfb3c544124075a7d68782 /src
parent80b2fd135b45f2c2e41ceb312314908f6a3e7b30 (diff)
core: add --verify-config to verify the config with Hyprland
fixes #9135
Diffstat (limited to 'src')
-rw-r--r--src/Compositor.cpp18
-rw-r--r--src/Compositor.hpp11
-rw-r--r--src/config/ConfigManager.cpp30
-rw-r--r--src/config/ConfigManager.hpp8
-rw-r--r--src/main.cpp17
-rw-r--r--src/managers/AnimationManager.cpp3
-rw-r--r--src/managers/KeybindManager.cpp7
7 files changed, 69 insertions, 25 deletions
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index cb0665e5..ab9c16f7 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -22,6 +22,7 @@
#include <cstring>
#include <filesystem>
#include <ranges>
+#include <print>
#include <unordered_set>
#include "debug/HyprCtl.hpp"
#include "debug/CrashReporter.hpp"
@@ -162,7 +163,10 @@ void CCompositor::restoreNofile() {
Debug::log(ERR, "Failed restoring NOFILE limits");
}
-CCompositor::CCompositor() : m_iHyprlandPID(getpid()) {
+CCompositor::CCompositor(bool onlyConfig) : m_bOnlyConfigVerification(onlyConfig), m_iHyprlandPID(getpid()) {
+ if (onlyConfig)
+ return;
+
m_szHyprTempDataRoot = std::string{getenv("XDG_RUNTIME_DIR")} + "/hypr";
if (m_szHyprTempDataRoot.starts_with("/hypr")) {
@@ -226,7 +230,7 @@ CCompositor::CCompositor() : m_iHyprlandPID(getpid()) {
}
CCompositor::~CCompositor() {
- if (!m_bIsShuttingDown)
+ if (!m_bIsShuttingDown && !m_bOnlyConfigVerification)
cleanup();
}
@@ -262,6 +266,16 @@ static bool filterGlobals(const wl_client* client, const wl_global* global, void
//
void CCompositor::initServer(std::string socketName, int socketFd) {
+ if (m_bOnlyConfigVerification) {
+ g_pHookSystem = makeUnique<CHookSystemManager>();
+ g_pKeybindManager = makeUnique<CKeybindManager>();
+ g_pAnimationManager = makeUnique<CHyprAnimationManager>();
+ g_pConfigManager = makeUnique<CConfigManager>();
+
+ std::println("\n\n======== Config parsing result:\n\n{}", g_pConfigManager->verify());
+ return;
+ }
+
m_sWLDisplay = wl_display_create();
wl_display_set_global_filter(m_sWLDisplay, ::filterGlobals, nullptr);
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index f2e2ca14..295878df 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -24,7 +24,7 @@ enum eManagersInitStage : uint8_t {
class CCompositor {
public:
- CCompositor();
+ CCompositor(bool onlyConfig = false);
~CCompositor();
wl_display* m_sWLDisplay;
@@ -71,10 +71,11 @@ class CCompositor {
bool m_bUnsafeState = false; // unsafe state is when there is no monitors.
bool m_bNextIsUnsafe = false;
PHLMONITORREF m_pUnsafeOutput; // fallback output for the unsafe state
- bool m_bIsShuttingDown = false;
- bool m_bFinalRequests = false;
- bool m_bDesktopEnvSet = false;
- bool m_bWantsXwayland = true;
+ bool m_bIsShuttingDown = false;
+ bool m_bFinalRequests = false;
+ bool m_bDesktopEnvSet = false;
+ bool m_bWantsXwayland = true;
+ bool m_bOnlyConfigVerification = false;
// ------------------------------------------------- //
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index f5c9b3ee..c5d70329 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -19,6 +19,7 @@
#include "../render/Renderer.hpp"
#include "../hyprerror/HyprError.hpp"
#include "../managers/input/InputManager.hpp"
+#include "../managers/eventLoop/EventLoopManager.hpp"
#include "../managers/LayoutManager.hpp"
#include "../managers/EventManager.hpp"
#include "../debug/HyprNotificationOverlay.hpp"
@@ -730,15 +731,18 @@ CConfigManager::CConfigManager() {
resetHLConfig();
- Debug::log(INFO,
- "!!!!HEY YOU, YES YOU!!!!: further logs to stdout / logfile are disabled by default. BEFORE SENDING THIS LOG, ENABLE THEM. Use debug:disable_logs = false to do so: "
- "https://wiki.hyprland.org/Configuring/Variables/#debug");
+ if (!g_pCompositor->m_bOnlyConfigVerification) {
+ Debug::log(
+ INFO,
+ "!!!!HEY YOU, YES YOU!!!!: further logs to stdout / logfile are disabled by default. BEFORE SENDING THIS LOG, ENABLE THEM. Use debug:disable_logs = false to do so: "
+ "https://wiki.hyprland.org/Configuring/Variables/#debug");
+ }
Debug::disableLogs = reinterpret_cast<int64_t* const*>(m_pConfig->getConfigValuePtr("debug:disable_logs")->getDataStaticPtr());
Debug::disableTime = reinterpret_cast<int64_t* const*>(m_pConfig->getConfigValuePtr("debug:disable_time")->getDataStaticPtr());
- if (ERR.has_value())
- g_pHyprError->queueCreate(ERR.value(), CHyprColor{1.0, 0.1, 0.1, 1.0});
+ if (g_pEventLoopManager && ERR.has_value())
+ g_pEventLoopManager->doLater([ERR] { g_pHyprError->queueCreate(ERR.value(), CHyprColor{1.0, 0.1, 0.1, 1.0}); });
}
std::optional<std::string> CConfigManager::generateConfig(std::string configPath) {
@@ -820,11 +824,23 @@ void CConfigManager::reload() {
EMIT_HOOK_EVENT("preConfigReload", nullptr);
setDefaultAnimationVars();
resetHLConfig();
- configCurrentPath = getMainConfigPath();
- const auto ERR = m_pConfig->parse();
+ configCurrentPath = getMainConfigPath();
+ const auto ERR = m_pConfig->parse();
+ m_bLastConfigVerificationWasSuccessful = !ERR.error;
postConfigReload(ERR);
}
+std::string CConfigManager::verify() {
+ setDefaultAnimationVars();
+ resetHLConfig();
+ configCurrentPath = getMainConfigPath();
+ const auto ERR = m_pConfig->parse();
+ m_bLastConfigVerificationWasSuccessful = !ERR.error;
+ if (ERR.error)
+ return ERR.getError();
+ return "config ok";
+}
+
void CConfigManager::setDefaultAnimationVars() {
m_AnimationTree.createNode("__internal_fadeCTM");
m_AnimationTree.createNode("global");
diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp
index 31ad81cd..564c1ebf 100644
--- a/src/config/ConfigManager.hpp
+++ b/src/config/ConfigManager.hpp
@@ -144,6 +144,7 @@ class CConfigManager {
void init();
void reload();
+ std::string verify();
int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = "");
float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = "");
@@ -257,9 +258,10 @@ class CConfigManager {
{"scrollmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollMouse; }},
{"scrolltouchpad", [](const PHLWINDOW& pWindow) { return &pWindow->m_sWindowData.scrollTouchpad; }}};
- bool m_bWantsMonitorReload = false;
- bool m_bNoMonitorReload = false;
- bool isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
+ bool m_bWantsMonitorReload = false;
+ bool m_bNoMonitorReload = false;
+ bool isLaunchingExecOnce = false; // For exec-once to skip initial ws tracking
+ bool m_bLastConfigVerificationWasSuccessful = true;
private:
UP<Hyprlang::CConfig> m_pConfig;
diff --git a/src/main.cpp b/src/main.cpp
index bb5e5622..d244f10f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -27,6 +27,7 @@ static void help() {
--wayland-fd FD - Sets the Wayland socket fd (for Wayland socket handover)
--systeminfo - Prints system infos
--i-am-really-stupid - Omits root user privileges check (why would you do that?)
+ --verify-config - Do not run Hyprland, only print if the config has any errors
--version -v - Print this binary's version)");
}
@@ -49,7 +50,7 @@ int main(int argc, char** argv) {
std::string configPath;
std::string socketName;
int socketFd = -1;
- bool ignoreSudo = false;
+ bool ignoreSudo = false, verifyConfig = false;
std::vector<std::string> args{argv + 1, argv + argc};
@@ -124,6 +125,9 @@ int main(int argc, char** argv) {
} else if (*it == "--systeminfo") {
std::println("{}", systemInfoRequest(eHyprCtlOutputFormat::FORMAT_NORMAL, ""));
return 0;
+ } else if (*it == "--verify-config") {
+ verifyConfig = true;
+ continue;
} else {
std::println(stderr, "[ ERROR ] Unknown option '{}' !", *it);
help();
@@ -138,9 +142,8 @@ int main(int argc, char** argv) {
" Hint: Use the --i-am-really-stupid flag to omit that check.");
return 1;
- } else if (ignoreSudo && NInit::isSudo()) {
+ } else if (ignoreSudo && NInit::isSudo())
std::println("Superuser privileges check is omitted. I hope you know what you're doing.");
- }
if (socketName.empty() ^ (socketFd == -1)) {
std::println(stderr,
@@ -150,12 +153,13 @@ int main(int argc, char** argv) {
return 1;
}
- std::println("Welcome to Hyprland!");
+ if (!verifyConfig)
+ std::println("Welcome to Hyprland!");
// let's init the compositor.
// it initializes basic Wayland stuff in the constructor.
try {
- g_pCompositor = makeUnique<CCompositor>();
+ g_pCompositor = makeUnique<CCompositor>(verifyConfig);
g_pCompositor->explicitConfigPath = configPath;
} catch (const std::exception& e) {
std::println(stderr, "Hyprland threw in ctor: {}\nCannot continue.", e.what());
@@ -164,6 +168,9 @@ int main(int argc, char** argv) {
g_pCompositor->initServer(socketName, socketFd);
+ if (verifyConfig)
+ return !g_pConfigManager->m_bLastConfigVerificationWasSuccessful;
+
if (!envEnabled("HYPRLAND_NO_RT"))
NInit::gainRealTime();
diff --git a/src/managers/AnimationManager.cpp b/src/managers/AnimationManager.cpp
index f884acdd..964f7cfa 100644
--- a/src/managers/AnimationManager.cpp
+++ b/src/managers/AnimationManager.cpp
@@ -34,7 +34,8 @@ static int wlTick(SP<CEventLoopTimer> self, void* data) {
CHyprAnimationManager::CHyprAnimationManager() {
m_pAnimationTimer = SP<CEventLoopTimer>(new CEventLoopTimer(std::chrono::microseconds(500), wlTick, nullptr));
- g_pEventLoopManager->addTimer(m_pAnimationTimer);
+ if (g_pEventLoopManager) // null in --verify-config mode
+ g_pEventLoopManager->addTimer(m_pAnimationTimer);
addBezierWithName("linear", Vector2D(0.0, 0.0), Vector2D(1.0, 1.0));
}
diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp
index 4706c1e1..e2191bc6 100644
--- a/src/managers/KeybindManager.cpp
+++ b/src/managers/KeybindManager.cpp
@@ -172,8 +172,11 @@ CKeybindManager::CKeybindManager() {
},
nullptr);
- g_pEventLoopManager->addTimer(m_pLongPressTimer);
- g_pEventLoopManager->addTimer(m_pRepeatKeyTimer);
+ // null in --verify-config mode
+ if (g_pEventLoopManager) {
+ g_pEventLoopManager->addTimer(m_pLongPressTimer);
+ g_pEventLoopManager->addTimer(m_pRepeatKeyTimer);
+ }
static auto P = g_pHookSystem->hookDynamic("configReloaded", [this](void* hk, SCallbackInfo& info, std::any param) {
// clear cuz realloc'd