mirror of
https://github.com/hyprwm/hyprpaper.git
synced 2026-05-09 01:58:17 +02:00
feat(config): re-add source= include directive support
Re-implements the source= directive for including external config files, which was originally added in PR #267 for v0.7.6 but lost during the v0.8.0 hyprtoolkit rewrite. Features: - Include external config files using source=/path/to/file.conf - Glob pattern support (e.g., source=~/.config/hypr/hyprpaper.d/*.conf) - Tilde expansion for home directory paths - Relative paths resolved relative to the current config file - Proper error handling and logging for missing/invalid files This restores parity with Hyprland's source= behavior, enabling modular configuration management that was lost in the v0.8.0 transition. Fixes: hyprwm/hyprpaper#302
This commit is contained in:
parent
e4413583bc
commit
023d7e6e62
2 changed files with 98 additions and 0 deletions
|
|
@ -1,6 +1,7 @@
|
|||
#include "ConfigManager.hpp"
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <glob.h>
|
||||
#include <hyprlang.hpp>
|
||||
#include <hyprutils/path/Path.hpp>
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
|
|
@ -37,6 +38,9 @@ using namespace std::string_literals;
|
|||
return std::string(result).starts_with("image/");
|
||||
}
|
||||
|
||||
// Forward declaration for the source handler
|
||||
static Hyprlang::CParseResult handleSource(const char* COMMAND, const char* VALUE);
|
||||
|
||||
static std::string getMainConfigPath() {
|
||||
static const auto paths = Hyprutils::Path::findConfig("hyprpaper");
|
||||
|
||||
|
|
@ -60,6 +64,8 @@ void CConfigManager::init() {
|
|||
m_config.addSpecialConfigValue("wallpaper", "fit_mode", Hyprlang::STRING{"cover"});
|
||||
m_config.addSpecialConfigValue("wallpaper", "timeout", Hyprlang::INT{0});
|
||||
|
||||
m_config.registerHandler(&handleSource, "source", Hyprlang::SHandlerOptions{});
|
||||
|
||||
m_config.commence();
|
||||
|
||||
auto result = m_config.parse();
|
||||
|
|
@ -168,3 +174,93 @@ std::vector<CConfigManager::SSetting> CConfigManager::getSettings() {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string absolutePath(const std::string& rawpath, const std::string& currentConfigPath) {
|
||||
if (rawpath.empty())
|
||||
return "";
|
||||
|
||||
std::filesystem::path path(rawpath);
|
||||
|
||||
// Handle tilde expansion
|
||||
if (!rawpath.empty() && rawpath[0] == '~') {
|
||||
static auto HOME = getenv("HOME");
|
||||
if (HOME && HOME[0] != '\0')
|
||||
path = std::string{HOME} + rawpath.substr(1);
|
||||
}
|
||||
|
||||
// Make relative paths relative to the current config file's directory
|
||||
if (!path.is_absolute() && !currentConfigPath.empty()) {
|
||||
auto configDir = std::filesystem::path(currentConfigPath).parent_path();
|
||||
path = configDir / path;
|
||||
}
|
||||
|
||||
return std::filesystem::absolute(path).string();
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleSource(const char* COMMAND, const char* VALUE) {
|
||||
Hyprlang::CParseResult result;
|
||||
|
||||
std::string value = VALUE;
|
||||
|
||||
// Trim whitespace
|
||||
while (!value.empty() && std::isspace(value.front()))
|
||||
value.erase(value.begin());
|
||||
while (!value.empty() && std::isspace(value.back()))
|
||||
value.pop_back();
|
||||
|
||||
if (value.empty()) {
|
||||
result.setError("source= requires a file path");
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto PATH = absolutePath(value, g_config->getCurrentConfigPath());
|
||||
|
||||
if (PATH.empty()) {
|
||||
result.setError("source= path is empty");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Support glob patterns
|
||||
glob_t globResult;
|
||||
int globStatus = glob(PATH.c_str(), GLOB_TILDE | GLOB_NOSORT, nullptr, &globResult);
|
||||
|
||||
if (globStatus == GLOB_NOMATCH) {
|
||||
globfree(&globResult);
|
||||
// No glob match - try as a literal path
|
||||
if (!std::filesystem::exists(PATH)) {
|
||||
result.setError(std::format("source file '{}' not found", PATH).c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
// Parse the single file
|
||||
auto parseResult = g_config->hyprlang()->parseFile(PATH.c_str());
|
||||
if (parseResult.error) {
|
||||
result.setError(std::format("error parsing '{}': {}", PATH, parseResult.getError()).c_str());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (globStatus != 0) {
|
||||
globfree(&globResult);
|
||||
result.setError(std::format("glob error for pattern '{}'", PATH).c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
// Process all matched files
|
||||
for (size_t i = 0; i < globResult.gl_pathc; i++) {
|
||||
const std::string matchedPath = globResult.gl_pathv[i];
|
||||
|
||||
if (!std::filesystem::is_regular_file(matchedPath)) {
|
||||
g_logger->log(LOG_WARN, "source: skipping non-regular file '{}'", matchedPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto parseResult = g_config->hyprlang()->parseFile(matchedPath.c_str());
|
||||
if (parseResult.error) {
|
||||
g_logger->log(LOG_ERR, "error parsing '{}': {}", matchedPath, parseResult.getError());
|
||||
}
|
||||
}
|
||||
|
||||
globfree(&globResult);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ class CConfigManager {
|
|||
|
||||
std::vector<SSetting> getSettings();
|
||||
|
||||
const std::string& getCurrentConfigPath() const { return m_currentConfigPath; }
|
||||
|
||||
private:
|
||||
Hyprlang::CConfig m_config;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue