mirror of
https://github.com/hyprwm/hyprpaper.git
synced 2025-12-20 04:30:02 +01:00
feat: add directory wallpaper support and deferred preloading
- Add support for selecting random wallpapers from directories - Add defer_preload option to reduce memory usage - Update README with new features - Improve path handling with tilde expansion
This commit is contained in:
parent
f827dc3197
commit
17cc5c13ad
4 changed files with 163 additions and 41 deletions
|
|
@ -80,6 +80,8 @@ preload = /path/to/next_image.png
|
|||
wallpaper = monitor1,/path/to/image.png
|
||||
#if more than one monitor in use, can load a 2nd image
|
||||
wallpaper = monitor2,/path/to/next_image.png
|
||||
# you can also specify a directory path to randomly select a wallpaper from
|
||||
wallpaper = monitor3,/path/to/directory
|
||||
# .. more monitors
|
||||
|
||||
#enable splash text rendering over the wallpaper
|
||||
|
|
@ -88,6 +90,9 @@ splash = true
|
|||
#fully disable ipc
|
||||
# ipc = off
|
||||
|
||||
#enable deferred preloading (reduces memory usage but may cause slight delay when switching wallpapers)
|
||||
# defer_preload = 1
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <fstream>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <random>
|
||||
|
||||
CHyprpaper::CHyprpaper() = default;
|
||||
|
||||
|
|
@ -152,35 +153,74 @@ void CHyprpaper::unloadWallpaper(const std::string& path) {
|
|||
}
|
||||
|
||||
void CHyprpaper::preloadAllWallpapersFromConfig() {
|
||||
if (std::any_cast<Hyprlang::INT>(g_pConfigManager->config->getConfigValue("defer_preload"))) {
|
||||
g_pConfigManager->m_dRequestedPreloads.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_pConfigManager->m_dRequestedPreloads.empty())
|
||||
return;
|
||||
|
||||
for (auto& wp : g_pConfigManager->m_dRequestedPreloads) {
|
||||
// For directory-based preloads, always reselect a random file.
|
||||
// For files, we'll skip if already loaded.
|
||||
bool shouldSkip = !std::filesystem::is_directory(wp);
|
||||
|
||||
// check if it doesnt exist
|
||||
bool exists = false;
|
||||
for (auto& [ewp, cls] : m_mWallpaperTargets) {
|
||||
if (ewp == wp) {
|
||||
Debug::log(LOG, "Ignoring request to preload {} as it already is preloaded!", ewp);
|
||||
exists = true;
|
||||
break;
|
||||
if (std::filesystem::is_directory(wp)) {
|
||||
// Always unload previous wallpaper from this directory
|
||||
std::vector<std::string> toUnload;
|
||||
for (auto& [loadedPath, target] : m_mWallpaperTargets) {
|
||||
std::filesystem::path pLoaded(loadedPath);
|
||||
if (pLoaded.parent_path() == std::filesystem::path(wp)) {
|
||||
toUnload.push_back(loadedPath);
|
||||
break; // Only unload one wallpaper per directory
|
||||
}
|
||||
}
|
||||
for (auto& path : toUnload) {
|
||||
unloadWallpaper(path);
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> images;
|
||||
for (auto& entry : std::filesystem::directory_iterator(wp)) {
|
||||
if (entry.is_regular_file()) {
|
||||
auto ext = entry.path().extension().string();
|
||||
if (ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".webp" || ext == ".jxl")
|
||||
images.push_back(entry.path());
|
||||
}
|
||||
}
|
||||
if (!images.empty()) {
|
||||
// Use CRandomGenerator instead
|
||||
wp = images[CRandomGenerator::get().getRandomIndex(images.size())].string();
|
||||
} else {
|
||||
Debug::log(LOG, "No valid images in directory {}", wp);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (exists)
|
||||
continue;
|
||||
// For non-directory requests, skip if already loaded.
|
||||
if (shouldSkip) {
|
||||
bool exists = false;
|
||||
for (auto& [ewp, cls] : m_mWallpaperTargets) {
|
||||
if (ewp == wp) {
|
||||
Debug::log(LOG, "Ignoring request to preload {} as it already is preloaded!", ewp);
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exists)
|
||||
continue;
|
||||
}
|
||||
|
||||
m_mWallpaperTargets[wp] = CWallpaperTarget();
|
||||
if (std::filesystem::is_symlink(wp)) {
|
||||
auto real_wp = std::filesystem::read_symlink(wp);
|
||||
auto real_wp = std::filesystem::read_symlink(wp);
|
||||
std::filesystem::path absolute_path = std::filesystem::path(wp).parent_path() / real_wp;
|
||||
absolute_path = absolute_path.lexically_normal();
|
||||
m_mWallpaperTargets[wp].create(absolute_path);
|
||||
absolute_path = absolute_path.lexically_normal();
|
||||
m_mWallpaperTargets[wp].create(absolute_path.string());
|
||||
} else {
|
||||
m_mWallpaperTargets[wp].create(wp);
|
||||
}
|
||||
}
|
||||
|
||||
g_pConfigManager->m_dRequestedPreloads.clear();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,54 +2,101 @@
|
|||
#include "../Hyprpaper.hpp"
|
||||
#include <hyprutils/path/Path.hpp>
|
||||
#include <filesystem>
|
||||
#include <random>
|
||||
|
||||
// Utility function to safely expand a tilde at the beginning of a path.
|
||||
static std::string expandTilde(const std::string& path) {
|
||||
if (!path.empty() && path[0] == '~') {
|
||||
if (const char* home = getenv("HOME"))
|
||||
return std::string(home) + path.substr(1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleWallpaper(const char* C, const char* V) {
|
||||
const std::string COMMAND = C;
|
||||
const std::string VALUE = V;
|
||||
const std::string COMMAND = C;
|
||||
const std::string VALUE = V;
|
||||
Hyprlang::CParseResult result;
|
||||
|
||||
if (VALUE.find_first_of(',') == std::string::npos) {
|
||||
// Cache the position of the comma delimiter.
|
||||
auto delimPos = VALUE.find_first_of(',');
|
||||
if (delimPos == std::string::npos) {
|
||||
result.setError("wallpaper failed (syntax)");
|
||||
return result;
|
||||
}
|
||||
|
||||
auto MONITOR = VALUE.substr(0, VALUE.find_first_of(','));
|
||||
auto WALLPAPER = g_pConfigManager->trimPath(VALUE.substr(VALUE.find_first_of(',') + 1));
|
||||
auto MONITOR = VALUE.substr(0, delimPos);
|
||||
auto WALLPAPER = g_pConfigManager->trimPath(VALUE.substr(delimPos + 1));
|
||||
|
||||
bool contain = false;
|
||||
|
||||
if (WALLPAPER.find("contain:") == 0) {
|
||||
WALLPAPER = WALLPAPER.substr(8);
|
||||
contain = true;
|
||||
}
|
||||
|
||||
bool tile = false;
|
||||
|
||||
if (WALLPAPER.find("tile:") == 0) {
|
||||
WALLPAPER = WALLPAPER.substr(5);
|
||||
tile = true;
|
||||
}
|
||||
|
||||
if (WALLPAPER[0] == '~') {
|
||||
static const char* const ENVHOME = getenv("HOME");
|
||||
WALLPAPER = std::string(ENVHOME) + WALLPAPER.substr(1);
|
||||
}
|
||||
// Use the helper to expand '~'
|
||||
WALLPAPER = expandTilde(WALLPAPER);
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
if (!std::filesystem::exists(WALLPAPER, ec)) {
|
||||
result.setError((std::string{"wallpaper failed ("} + (ec ? ec.message() : std::string{"no such file"}) + std::string{": "} + WALLPAPER + std::string{")"}).c_str());
|
||||
result.setError(((ec ? ec.message() : std::string{"no such file"}) + std::string{": "} + WALLPAPER).c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
// If it's a directory, always force a reload
|
||||
if (std::filesystem::is_directory(WALLPAPER)) {
|
||||
std::string currentWallpaper;
|
||||
// First get the current wallpaper if it exists
|
||||
for (auto& [loadedPath, target] : g_pHyprpaper->m_mWallpaperTargets) {
|
||||
if (std::filesystem::path(loadedPath).parent_path() == std::filesystem::path(WALLPAPER)) {
|
||||
currentWallpaper = loadedPath;
|
||||
g_pHyprpaper->unloadWallpaper(loadedPath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> images;
|
||||
for (auto& entry : std::filesystem::directory_iterator(WALLPAPER)) {
|
||||
if (entry.is_regular_file()) {
|
||||
auto ext = entry.path().extension().string();
|
||||
if (ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".webp" || ext == ".jxl") {
|
||||
if (entry.path().string() != currentWallpaper) {
|
||||
images.push_back(entry.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (images.empty()) {
|
||||
if (!currentWallpaper.empty()) {
|
||||
images.push_back(currentWallpaper);
|
||||
} else {
|
||||
result.setError("No valid images in directory");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
WALLPAPER = images[CRandomGenerator::get().getRandomIndex(images.size())].string();
|
||||
}
|
||||
|
||||
if (!g_pHyprpaper->isPreloaded(WALLPAPER)) {
|
||||
if (std::any_cast<Hyprlang::INT>(g_pConfigManager->config->getConfigValue("defer_preload"))) {
|
||||
g_pHyprpaper->m_mWallpaperTargets[WALLPAPER] = CWallpaperTarget();
|
||||
g_pHyprpaper->m_mWallpaperTargets[WALLPAPER].create(WALLPAPER);
|
||||
} else {
|
||||
result.setError("wallpaper failed (not preloaded)");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
g_pHyprpaper->clearWallpaperFromMonitor(MONITOR);
|
||||
g_pHyprpaper->m_mMonitorActiveWallpapers[MONITOR] = WALLPAPER;
|
||||
g_pHyprpaper->m_mMonitorActiveWallpapers[MONITOR] = WALLPAPER;
|
||||
g_pHyprpaper->m_mMonitorWallpaperRenderData[MONITOR].contain = contain;
|
||||
g_pHyprpaper->m_mMonitorWallpaperRenderData[MONITOR].tile = tile;
|
||||
|
||||
|
|
@ -57,7 +104,7 @@ static Hyprlang::CParseResult handleWallpaper(const char* C, const char* V) {
|
|||
for (auto& m : g_pHyprpaper->m_vMonitors) {
|
||||
if (!m->hasATarget || m->wildcard) {
|
||||
g_pHyprpaper->clearWallpaperFromMonitor(m->name);
|
||||
g_pHyprpaper->m_mMonitorActiveWallpapers[m->name] = WALLPAPER;
|
||||
g_pHyprpaper->m_mMonitorActiveWallpapers[m->name] = WALLPAPER;
|
||||
g_pHyprpaper->m_mMonitorWallpaperRenderData[m->name].contain = contain;
|
||||
g_pHyprpaper->m_mMonitorWallpaperRenderData[m->name].tile = tile;
|
||||
}
|
||||
|
|
@ -76,13 +123,10 @@ static Hyprlang::CParseResult handlePreload(const char* C, const char* V) {
|
|||
const std::string VALUE = V;
|
||||
auto WALLPAPER = VALUE;
|
||||
|
||||
if (WALLPAPER[0] == '~') {
|
||||
static const char* const ENVHOME = getenv("HOME");
|
||||
WALLPAPER = std::string(ENVHOME) + WALLPAPER.substr(1);
|
||||
}
|
||||
// Use the helper function for tilde expansion.
|
||||
WALLPAPER = expandTilde(WALLPAPER);
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
if (!std::filesystem::exists(WALLPAPER, ec)) {
|
||||
Hyprlang::CParseResult result;
|
||||
result.setError(((ec ? ec.message() : std::string{"no such file"}) + std::string{": "} + WALLPAPER).c_str());
|
||||
|
|
@ -130,10 +174,8 @@ static Hyprlang::CParseResult handleUnload(const char* C, const char* V) {
|
|||
if (VALUE == "all" || VALUE == "unused")
|
||||
return handleUnloadAll(C, V);
|
||||
|
||||
if (WALLPAPER[0] == '~') {
|
||||
static const char* const ENVHOME = getenv("HOME");
|
||||
WALLPAPER = std::string(ENVHOME) + WALLPAPER.substr(1);
|
||||
}
|
||||
// Use the helper function for tilde expansion.
|
||||
WALLPAPER = expandTilde(WALLPAPER);
|
||||
|
||||
g_pHyprpaper->unloadWallpaper(WALLPAPER);
|
||||
|
||||
|
|
@ -189,6 +231,7 @@ CConfigManager::CConfigManager() {
|
|||
config->addConfigValue("splash", Hyprlang::INT{0L});
|
||||
config->addConfigValue("splash_offset", Hyprlang::FLOAT{2.F});
|
||||
config->addConfigValue("splash_color", Hyprlang::INT{0x55ffffff});
|
||||
config->addConfigValue("defer_preload", Hyprlang::INT{0L});
|
||||
|
||||
config->registerHandler(&handleWallpaper, "wallpaper", {.allowFlags = false});
|
||||
config->registerHandler(&handleUnload, "unload", {.allowFlags = false});
|
||||
|
|
|
|||
34
src/helpers/RandomGenerator.hpp
Normal file
34
src/helpers/RandomGenerator.hpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
#include <random>
|
||||
|
||||
class CRandomGenerator {
|
||||
public:
|
||||
static CRandomGenerator& get() {
|
||||
static CRandomGenerator instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Generate random index for vector size
|
||||
size_t getRandomIndex(size_t size) {
|
||||
if (size == 0) return 0;
|
||||
std::uniform_int_distribution<size_t> dis(0, size - 1);
|
||||
return dis(m_Generator);
|
||||
}
|
||||
|
||||
// Get raw generator if needed
|
||||
std::mt19937& getGenerator() {
|
||||
return m_Generator;
|
||||
}
|
||||
|
||||
private:
|
||||
CRandomGenerator() : m_Generator(m_RandomDevice()) {}
|
||||
|
||||
std::random_device m_RandomDevice;
|
||||
std::mt19937 m_Generator;
|
||||
|
||||
// Delete copy/move to ensure singleton
|
||||
CRandomGenerator(const CRandomGenerator&) = delete;
|
||||
CRandomGenerator& operator=(const CRandomGenerator&) = delete;
|
||||
CRandomGenerator(CRandomGenerator&&) = delete;
|
||||
CRandomGenerator& operator=(CRandomGenerator&&) = delete;
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue