internal: move to libhyprlang for config handling

This commit is contained in:
Vaxry 2023-12-31 01:37:50 +01:00
parent ef0e051255
commit 39ad021c75
7 changed files with 150 additions and 211 deletions

View file

@ -65,6 +65,7 @@ target_link_libraries(hyprpaper
GLESv2
pthread
magic
hyprlang
${CMAKE_THREAD_LIBS_INIT}
${CMAKE_SOURCE_DIR}/wlr-layer-shell-unstable-v1-protocol.o
${CMAKE_SOURCE_DIR}/xdg-shell-protocol.o

View file

@ -27,14 +27,21 @@ The development files of these packages need to be installed on the system for `
- libglvnd-core
- libjpeg-turbo
- libwebp
- hyprlang
Also `gcc-c++` and `ninja` needs to installed.
Please note hyprpaper > 0.5.0 depends on [hyprlang](https://github.com/hyprwm/hyprlang) which is new
and might not be packaged for your distro yet. If that's the case, build and install it from source.
To install all of these in Fedora, run this command:
```
sudo dnf install wayland-devel wayland-protocols-devel pango-devel cairo-devel file-devel libglvnd-devel libglvnd-core-devel libjpeg-turbo-devel libwebp-devel gcc-c++
```
On Arch:
```
sudo pacman -S ninja gcc wayland-protocols libjpeg-turbo libwebp pango cairo pkgconf cmake libglvnd wayland
```
On OpenSUSE:
```
sudo zypper install ninja gcc-c++ wayland-protocols-devel Mesa-libGLESv3-devel file-devel

View file

@ -1,8 +1,8 @@
#include "Hyprpaper.hpp"
#include <filesystem>
#include <fstream>
#include <sys/types.h>
#include <signal.h>
#include <sys/types.h>
CHyprpaper::CHyprpaper() = default;
@ -18,6 +18,8 @@ void CHyprpaper::init() {
g_pConfigManager = std::make_unique<CConfigManager>();
g_pIPCSocket = std::make_unique<CIPCSocket>();
g_pConfigManager->parse();
m_sDisplay = (wl_display*)wl_display_connect(nullptr);
if (!m_sDisplay) {
@ -27,7 +29,7 @@ void CHyprpaper::init() {
preloadAllWallpapersFromConfig();
if (m_bIPCEnabled)
if (std::any_cast<int64_t>(g_pConfigManager->config->getConfigValue("ipc")))
g_pIPCSocket->initialize();
// run
@ -129,7 +131,6 @@ void CHyprpaper::preloadAllWallpapersFromConfig() {
} else {
m_mWallpaperTargets[wp].create(wp);
}
}
g_pConfigManager->m_dRequestedPreloads.clear();
@ -450,6 +451,8 @@ SPoolBuffer* CHyprpaper::getPoolBuffer(SMonitor* pMonitor, CWallpaperTarget* pWa
}
void CHyprpaper::renderWallpaperForMonitor(SMonitor* pMonitor) {
static auto* const PRENDERSPLASH = reinterpret_cast<int64_t*>(g_pConfigManager->config->getConfigValuePtr("splash"));
static auto* const PSPLASHOFFSET = reinterpret_cast<float*>(g_pConfigManager->config->getConfigValuePtr("splash_offset"));
const auto PWALLPAPERTARGET = m_mMonitorActiveWallpaperTargets[pMonitor];
const auto CONTAIN = m_mMonitorWallpaperRenderData[pMonitor->name].contain;
@ -508,7 +511,7 @@ void CHyprpaper::renderWallpaperForMonitor(SMonitor* pMonitor) {
cairo_paint(PCAIRO);
if (g_pHyprpaper->m_bRenderSplash && getenv("HYPRLAND_INSTANCE_SIGNATURE")) {
if (*PRENDERSPLASH && getenv("HYPRLAND_INSTANCE_SIGNATURE")) {
auto SPLASH = execAndGet("hyprctl splash");
SPLASH.pop_back();
@ -524,9 +527,9 @@ void CHyprpaper::renderWallpaperForMonitor(SMonitor* pMonitor) {
cairo_text_extents_t textExtents;
cairo_text_extents(PCAIRO, SPLASH.c_str(), &textExtents);
cairo_move_to(PCAIRO, ((DIMENSIONS.x - textExtents.width * scale) / 2.0) / scale, ((DIMENSIONS.y * (100 - m_fSplashOffset)) / 100 - textExtents.height * scale) / scale);
cairo_move_to(PCAIRO, ((DIMENSIONS.x - textExtents.width * scale) / 2.0) / scale, ((DIMENSIONS.y * (100 - *PSPLASHOFFSET)) / 100 - textExtents.height * scale) / scale);
Debug::log(LOG, "Splash font size: %d, pos: %.2f, %.2f", FONTSIZE, (DIMENSIONS.x - textExtents.width) / 2.0 / scale, ((DIMENSIONS.y * (100 - m_fSplashOffset)) / 100 - textExtents.height * scale) / scale);
Debug::log(LOG, "Splash font size: %d, pos: %.2f, %.2f", FONTSIZE, (DIMENSIONS.x - textExtents.width) / 2.0 / scale, ((DIMENSIONS.y * (100 - *PSPLASHOFFSET)) / 100 - textExtents.height * scale) / scale);
cairo_show_text(PCAIRO, SPLASH.c_str());

View file

@ -1,13 +1,13 @@
#pragma once
#include "defines.hpp"
#include "config/ConfigManager.hpp"
#include "render/WallpaperTarget.hpp"
#include "helpers/Monitor.hpp"
#include "defines.hpp"
#include "events/Events.hpp"
#include "helpers/PoolBuffer.hpp"
#include "helpers/MiscFunctions.hpp"
#include "helpers/Monitor.hpp"
#include "helpers/PoolBuffer.hpp"
#include "ipc/Socket.hpp"
#include "render/WallpaperTarget.hpp"
#include <mutex>
struct SWallpaperRenderData {
@ -17,17 +17,17 @@ struct SWallpaperRenderData {
class CHyprpaper {
public:
// important
wl_display* m_sDisplay; // assured
wl_compositor* m_sCompositor; // assured
wl_shm* m_sSHM; // assured
zwlr_layer_shell_v1* m_sLayerShell = nullptr; // expected
wl_display* m_sDisplay; // assured
wl_compositor* m_sCompositor; // assured
wl_shm* m_sSHM; // assured
zwlr_layer_shell_v1* m_sLayerShell = nullptr; // expected
wp_fractional_scale_manager_v1* m_sFractionalScale = nullptr; // will remain null if not bound
wp_viewporter* m_sViewporter = nullptr; // expected
wp_viewporter* m_sViewporter = nullptr; // expected
// init the utility
CHyprpaper();
void init();
void tick(bool force);
void init();
void tick(bool force);
std::unordered_map<std::string, CWallpaperTarget> m_mWallpaperTargets;
std::unordered_map<std::string, std::string> m_mMonitorActiveWallpapers;
@ -36,39 +36,36 @@ public:
std::vector<std::unique_ptr<SPoolBuffer>> m_vBuffers;
std::vector<std::unique_ptr<SMonitor>> m_vMonitors;
bool m_bIPCEnabled = true;
bool m_bRenderSplash = false;
float m_fSplashOffset = 2;
std::string m_szExplicitConfigPath;
bool m_bNoFractionalScale = false;
bool m_bNoFractionalScale = false;
void removeOldHyprpaperImages();
void preloadAllWallpapersFromConfig();
void recheckAllMonitors();
void ensureMonitorHasActiveWallpaper(SMonitor*);
void createLSForMonitor(SMonitor*);
void renderWallpaperForMonitor(SMonitor*);
void createBuffer(SPoolBuffer*, int32_t, int32_t, uint32_t);
void destroyBuffer(SPoolBuffer*);
int createPoolFile(size_t, std::string&);
bool setCloexec(const int&);
void clearWallpaperFromMonitor(const std::string&);
SMonitor* getMonitorFromName(const std::string&);
bool isPreloaded(const std::string&);
void recheckMonitor(SMonitor*);
void ensurePoolBuffersPresent();
void removeOldHyprpaperImages();
void preloadAllWallpapersFromConfig();
void recheckAllMonitors();
void ensureMonitorHasActiveWallpaper(SMonitor*);
void createLSForMonitor(SMonitor*);
void renderWallpaperForMonitor(SMonitor*);
void createBuffer(SPoolBuffer*, int32_t, int32_t, uint32_t);
void destroyBuffer(SPoolBuffer*);
int createPoolFile(size_t, std::string&);
bool setCloexec(const int&);
void clearWallpaperFromMonitor(const std::string&);
SMonitor* getMonitorFromName(const std::string&);
bool isPreloaded(const std::string&);
void recheckMonitor(SMonitor*);
void ensurePoolBuffersPresent();
SPoolBuffer* getPoolBuffer(SMonitor*, CWallpaperTarget*);
void unloadWallpaper(const std::string&);
void createSeat(wl_seat*);
bool lockSingleInstance(); // fails on multi-instance
void unlockSingleInstance();
void unloadWallpaper(const std::string&);
void createSeat(wl_seat*);
bool lockSingleInstance(); // fails on multi-instance
void unlockSingleInstance();
std::mutex m_mtTickMutex;
std::mutex m_mtTickMutex;
SMonitor* m_pLastMonitor = nullptr;
SMonitor* m_pLastMonitor = nullptr;
private:
bool m_bShouldExit = false;
bool m_bShouldExit = false;
};
inline std::unique_ptr<CHyprpaper> g_pHyprpaper;

View file

@ -1,135 +1,18 @@
#include "ConfigManager.hpp"
#include "../Hyprpaper.hpp"
CConfigManager::CConfigManager() {
// Initialize the configuration
// Read file from default location
// or from an explicit location given by user
static Hyprlang::CParseResult handleWallpaper(const char* C, const char* V) {
const std::string COMMAND = C;
const std::string VALUE = V;
Hyprlang::CParseResult result;
std::string configPath = getMainConfigPath();
std::ifstream ifs;
ifs.open(configPath);
if (!ifs.good()) {
Debug::log(WARN, "Config file `%s` couldn't be opened. Running without a config!", configPath.c_str());
return;
}
std::string line = "";
int linenum = 1;
if (ifs.is_open()) {
while (std::getline(ifs, line)) {
// Read line by line
try {
parseLine(line);
} catch (...) {
Debug::log(ERR, "Error reading line from config. Line:");
Debug::log(NONE, "%s", line.c_str());
parseError += "Config error at line " + std::to_string(linenum) + ": Line parsing error.";
}
if (!parseError.empty()) {
parseError = "Config error at line " + std::to_string(linenum) + ": " + parseError;
break;
}
++linenum;
}
ifs.close();
}
if (!parseError.empty()) {
Debug::log(WARN, "Config parse error: \n%s\n\nRunning and ignoring errors...\n", parseError.c_str());
return;
}
}
std::string CConfigManager::getMainConfigPath() {
if (!g_pHyprpaper->m_szExplicitConfigPath.empty())
return g_pHyprpaper->m_szExplicitConfigPath;
static const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
std::string configPath;
if (!xdgConfigHome)
configPath = getenv("HOME") + std::string("/.config");
else
configPath = xdgConfigHome;
return configPath + "/hypr/hyprpaper.conf";
}
std::string CConfigManager::removeBeginEndSpacesTabs(std::string str) {
while (str[0] == ' ' || str[0] == '\t') {
str = str.substr(1);
}
while (str.length() != 0 && (str[str.length() - 1] == ' ' || str[str.length() - 1] == '\t')) {
str = str.substr(0, str.length() - 1);
}
return str;
}
void CConfigManager::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)
line = line.substr(0, COMMENTSTART);
// Strip line
while (line[0] == ' ' || line[0] == '\t') {
line = line.substr(1);
}
// And parse
// check if command
const auto EQUALSPLACE = line.find_first_of('=');
if (EQUALSPLACE == std::string::npos)
return;
const auto COMMAND = removeBeginEndSpacesTabs(line.substr(0, EQUALSPLACE));
const auto VALUE = removeBeginEndSpacesTabs(line.substr(EQUALSPLACE + 1));
parseKeyword(COMMAND, VALUE);
}
void CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE) {
if (COMMAND == "wallpaper")
handleWallpaper(COMMAND, VALUE);
else if (COMMAND == "preload")
handlePreload(COMMAND, VALUE);
else if (COMMAND == "unload")
handleUnload(COMMAND, VALUE);
else if (COMMAND == "ipc")
g_pHyprpaper->m_bIPCEnabled = VALUE == "1" || VALUE == "yes" || VALUE == "on" || VALUE == "true";
else if (COMMAND == "splash")
g_pHyprpaper->m_bRenderSplash = VALUE == "1" || VALUE == "yes" || VALUE == "on" || VALUE == "true";
else if (COMMAND == "splash_offset") {
try {
g_pHyprpaper->m_fSplashOffset = std::clamp(std::stof(VALUE), 0.f, 100.f);
} catch (std::exception& e) {
parseError = "invalid splash_offset value " + VALUE;
}
} else
parseError = "unknown keyword " + COMMAND;
}
void CConfigManager::handleWallpaper(const std::string& COMMAND, const std::string& VALUE) {
if (VALUE.find_first_of(',') == std::string::npos) {
parseError = "wallpaper failed (syntax)";
return;
result.setError("wallpaper failed (syntax)");
return result;
}
auto MONITOR = VALUE.substr(0, VALUE.find_first_of(','));
auto WALLPAPER = trimPath(VALUE.substr(VALUE.find_first_of(',') + 1));
auto WALLPAPER = g_pConfigManager->trimPath(VALUE.substr(VALUE.find_first_of(',') + 1));
bool contain = false;
@ -144,21 +27,25 @@ void CConfigManager::handleWallpaper(const std::string& COMMAND, const std::stri
}
if (!std::filesystem::exists(WALLPAPER)) {
parseError = "wallpaper failed (no such file)";
return;
result.setError("wallpaper failed (no such file)");
return result;
}
if (std::find(m_dRequestedPreloads.begin(), m_dRequestedPreloads.end(), WALLPAPER) == m_dRequestedPreloads.end() && !g_pHyprpaper->isPreloaded(WALLPAPER)) {
parseError = "wallpaper failed (not preloaded)";
return;
if (std::find(g_pConfigManager->m_dRequestedPreloads.begin(), g_pConfigManager->m_dRequestedPreloads.end(), WALLPAPER) == g_pConfigManager->m_dRequestedPreloads.end() && !g_pHyprpaper->isPreloaded(WALLPAPER)) {
result.setError("wallpaper failed (not preloaded)");
return result;
}
g_pHyprpaper->clearWallpaperFromMonitor(MONITOR);
g_pHyprpaper->m_mMonitorActiveWallpapers[MONITOR] = WALLPAPER;
g_pHyprpaper->m_mMonitorWallpaperRenderData[MONITOR].contain = contain;
return result;
}
void CConfigManager::handlePreload(const std::string& COMMAND, const std::string& VALUE) {
static Hyprlang::CParseResult handlePreload(const char* C, const char* V) {
const std::string COMMAND = C;
const std::string VALUE = V;
auto WALLPAPER = VALUE;
if (WALLPAPER[0] == '~') {
@ -167,30 +54,19 @@ void CConfigManager::handlePreload(const std::string& COMMAND, const std::string
}
if (!std::filesystem::exists(WALLPAPER)) {
parseError = "preload failed (no such file)";
return;
Hyprlang::CParseResult result;
result.setError((std::string{"no such file: "} + WALLPAPER).c_str());
return result;
}
m_dRequestedPreloads.emplace_back(WALLPAPER);
g_pConfigManager->m_dRequestedPreloads.emplace_back(WALLPAPER);
return Hyprlang::CParseResult{};
}
void CConfigManager::handleUnload(const std::string& COMMAND, const std::string& VALUE) {
auto WALLPAPER = VALUE;
if (VALUE == "all") {
handleUnloadAll(COMMAND, VALUE);
return;
}
if (WALLPAPER[0] == '~') {
static const char* const ENVHOME = getenv("HOME");
WALLPAPER = std::string(ENVHOME) + WALLPAPER.substr(1);
}
g_pHyprpaper->unloadWallpaper(WALLPAPER);
}
void CConfigManager::handleUnloadAll(const std::string& COMMAND, const std::string& VALUE) {
static Hyprlang::CParseResult handleUnloadAll(const char* C, const char* V) {
const std::string COMMAND = C;
const std::string VALUE = V;
std::vector<std::string> toUnload;
for (auto& [name, target] : g_pHyprpaper->m_mWallpaperTargets) {
@ -211,6 +87,69 @@ void CConfigManager::handleUnloadAll(const std::string& COMMAND, const std::stri
for (auto& tu : toUnload)
g_pHyprpaper->unloadWallpaper(tu);
return Hyprlang::CParseResult{};
}
static Hyprlang::CParseResult handleUnload(const char* C, const char* V) {
const std::string COMMAND = C;
const std::string VALUE = V;
auto WALLPAPER = VALUE;
if (VALUE == "all")
return handleUnloadAll(C, V);
if (WALLPAPER[0] == '~') {
static const char* const ENVHOME = getenv("HOME");
WALLPAPER = std::string(ENVHOME) + WALLPAPER.substr(1);
}
g_pHyprpaper->unloadWallpaper(WALLPAPER);
return Hyprlang::CParseResult{};
}
CConfigManager::CConfigManager() {
// Initialize the configuration
// Read file from default location
// or from an explicit location given by user
std::string configPath = getMainConfigPath();
config = std::make_unique<Hyprlang::CConfig>(configPath.c_str(), Hyprlang::SConfigOptions{});
config->addConfigValue("ipc", {1L});
config->addConfigValue("splash", {1L});
config->addConfigValue("splash_offset", {2.F});
config->registerHandler(&handleWallpaper, "wallpaper", {.allowFlags = false});
config->registerHandler(&handleUnload, "unload", {.allowFlags = false});
config->registerHandler(&handlePreload, "preload", {.allowFlags = false});
config->registerHandler(&handleUnloadAll, "unloadAll", {.allowFlags = false});
config->commence();
}
void CConfigManager::parse() {
const auto ERROR = config->parse();
if (ERROR.error)
std::cout << "Error in config: \n"
<< ERROR.getError() << "\n";
}
std::string CConfigManager::getMainConfigPath() {
if (!g_pHyprpaper->m_szExplicitConfigPath.empty())
return g_pHyprpaper->m_szExplicitConfigPath;
static const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
std::string configPath;
if (!xdgConfigHome)
configPath = getenv("HOME") + std::string("/.config");
else
configPath = xdgConfigHome;
return configPath + "/hypr/hyprpaper.conf";
}
// trim from both ends

View file

@ -1,5 +1,6 @@
#pragma once
#include "../defines.hpp"
#include <hyprlang.hpp>
class CIPCSocket;
@ -7,23 +8,15 @@ class CConfigManager {
public:
// gets all the data from the config
CConfigManager();
void parse();
std::deque<std::string> m_dRequestedPreloads;
std::string getMainConfigPath();
private:
std::string parseError;
void parseLine(std::string&);
std::string removeBeginEndSpacesTabs(std::string in);
void parseKeyword(const std::string&, const std::string&);
void handleWallpaper(const std::string&, const std::string&);
void handlePreload(const std::string&, const std::string&);
void handleUnload(const std::string&, const std::string&);
void handleUnloadAll(const std::string&, const std::string&);
std::string trimPath(std::string path);
std::unique_ptr<Hyprlang::CConfig> config;
private:
friend class CIPCSocket;
};

View file

@ -99,12 +99,11 @@ bool CIPCSocket::mainThreadParseRequest() {
// parse
if (copy.find("wallpaper") == 0 || copy.find("preload") == 0 || copy.find("unload") == 0) {
g_pConfigManager->parseError = ""; // reset parse error
g_pConfigManager->parseKeyword(copy.substr(0, copy.find_first_of(' ')), copy.substr(copy.find_first_of(' ') + 1));
const auto RESULT = g_pConfigManager->config->parseDynamic(copy.substr(0, copy.find_first_of(' ')).c_str(), copy.substr(copy.find_first_of(' ') + 1).c_str());
if (!g_pConfigManager->parseError.empty()) {
m_szReply = g_pConfigManager->parseError;
if (RESULT.error) {
m_szReply = RESULT.getError();
m_bReplyReady = true;
m_bRequestReady = false;
return false;