This commit is contained in:
Sami Ansari 2025-02-06 08:03:56 -05:00 committed by GitHub
commit ff95921bdf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 180 additions and 46 deletions

View file

@ -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
```

View file

@ -3,6 +3,8 @@
#include <fstream>
#include <signal.h>
#include <sys/types.h>
#include "helpers/RandomGenerator.hpp"
#include "helpers/ImagePicker.hpp"
CHyprpaper::CHyprpaper() = default;
@ -152,35 +154,66 @@ 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);
}
// Use our helper to pick a random image.
std::string randomImage = getRandomImageFromDirectory(wp);
if (randomImage.empty()) {
Debug::log(LOG, "No valid images in directory {}", wp);
continue;
}
wp = randomImage;
}
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();
}
@ -274,15 +307,15 @@ SMonitor* CHyprpaper::getMonitorFromName(const std::string& monname) {
void CHyprpaper::ensurePoolBuffersPresent() {
bool anyNewBuffers = false;
for (auto& [file, wt] : m_mWallpaperTargets) {
for (auto& [path, wt] : m_mWallpaperTargets) {
for (auto& m : m_vMonitors) {
if (m->size == Vector2D())
continue;
auto it = std::find_if(m_vBuffers.begin(), m_vBuffers.end(), [wt = &wt, &m](const std::unique_ptr<SPoolBuffer>& el) {
auto it = std::find_if(m_vBuffers.begin(), m_vBuffers.end(), [&](const std::unique_ptr<SPoolBuffer>& el) {
auto scale = std::round((m->pCurrentLayerSurface && m->pCurrentLayerSurface->pFractionalScaleInfo ? m->pCurrentLayerSurface->fScale : m->scale) * 120.0) / 120.0;
return el->target == wt->m_szPath && vectorDeltaLessThan(el->pixelSize, m->size * scale, 1);
return el->target == path && vectorDeltaLessThan(el->pixelSize, m->size * scale, 1);
});
if (it == m_vBuffers.end()) {
@ -291,9 +324,9 @@ void CHyprpaper::ensurePoolBuffersPresent() {
auto scale = std::round((m->pCurrentLayerSurface && m->pCurrentLayerSurface->pFractionalScaleInfo ? m->pCurrentLayerSurface->fScale : m->scale) * 120.0) / 120.0;
createBuffer(PBUFFER, m->size.x * scale, m->size.y * scale, WL_SHM_FORMAT_ARGB8888);
PBUFFER->target = wt.m_szPath;
PBUFFER->target = path;
Debug::log(LOG, "Buffer created for target {}, Shared Memory usage: {:.1f}MB", wt.m_szPath, PBUFFER->size / 1000000.f);
Debug::log(LOG, "Buffer created for target {}, Shared Memory usage: {:.1f}MB", path, PBUFFER->size / 1000000.f);
anyNewBuffers = true;
}

View file

@ -2,54 +2,87 @@
#include "../Hyprpaper.hpp"
#include <hyprutils/path/Path.hpp>
#include <filesystem>
#include "../helpers/RandomGenerator.hpp"
#include "../helpers/ImagePicker.hpp"
// 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;
}
}
// Use our helper with an exclusion to avoid reselecting the same image.
std::string randomImage = getRandomImageFromDirectory(WALLPAPER, currentWallpaper);
if (randomImage.empty()) {
result.setError("No valid images in directory");
return result;
}
WALLPAPER = randomImage;
}
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 +90,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 +109,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 +160,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 +217,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});

View file

@ -0,0 +1,33 @@
#include "ImagePicker.hpp"
#include "RandomGenerator.hpp"
#include <filesystem>
#include <vector>
#include <algorithm>
std::string getRandomImageFromDirectory(const std::string &dirPath, const std::string &ignore) {
std::vector<std::filesystem::path> images;
for (const auto &entry : std::filesystem::directory_iterator(dirPath)) {
if (!entry.is_regular_file())
continue;
auto ext = entry.path().extension().string();
if (ext != ".png" && ext != ".jpg" && ext != ".jpeg" &&
ext != ".webp" && ext != ".jxl")
continue;
if (!ignore.empty() && entry.path().string() == ignore)
continue;
images.push_back(entry.path());
}
if (images.empty()) {
// If no alternative was found but the ignored file exists, return it.
if (!ignore.empty() && std::filesystem::exists(ignore))
return ignore;
return "";
}
std::shuffle(images.begin(), images.end(), CRandomGenerator::get().getGenerator());
return images.front().string();
}

View file

@ -0,0 +1,7 @@
#pragma once
#include <string>
// Returns a random image path from the given directory.
// If 'ignore' is provided, that file (if present) will be excluded from the selection.
// Returns an empty string if no valid image is found.
std::string getRandomImageFromDirectory(const std::string &dirPath, const std::string &ignore = "");

View file

@ -0,0 +1,27 @@
#pragma once
#include <random>
#include <memory>
class CRandomGenerator {
public:
CRandomGenerator() : mGenerator(mRandomDevice()) {}
// Generate random index for vector size
size_t getRandomIndex(size_t size) {
if (size == 0) return 0;
std::uniform_int_distribution<size_t> distribution(0, size - 1);
return distribution(mGenerator);
}
// Get raw generator if needed
std::mt19937& getGenerator() {
return mGenerator;
}
private:
std::random_device mRandomDevice;
std::mt19937 mGenerator;
};
// Global pointer to random generator
inline std::unique_ptr<CRandomGenerator> gRandomGenerator;