mirror of
https://github.com/hyprwm/hyprpaper.git
synced 2025-12-20 04:30:02 +01:00
Merge bf38a16940 into f827dc3197
This commit is contained in:
commit
ff95921bdf
6 changed files with 180 additions and 46 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,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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});
|
||||
|
|
|
|||
33
src/helpers/ImagePicker.cpp
Normal file
33
src/helpers/ImagePicker.cpp
Normal 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();
|
||||
}
|
||||
7
src/helpers/ImagePicker.hpp
Normal file
7
src/helpers/ImagePicker.hpp
Normal 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 = "");
|
||||
27
src/helpers/RandomGenerator.hpp
Normal file
27
src/helpers/RandomGenerator.hpp
Normal 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;
|
||||
Loading…
Add table
Reference in a new issue