11#include " ConfigManager.hpp"
2+ #include < algorithm>
23#include < filesystem>
34#include < hyprlang.hpp>
45#include < hyprutils/path/Path.hpp>
6+ #include < hyprutils/utils/ScopeGuard.hpp>
57#include < string>
68#include < sys/ucontext.h>
79#include " ../helpers/Logger.hpp"
810#include " WallpaperMatcher.hpp"
911
12+ #include < magic.h>
13+
1014using namespace std ::string_literals;
1115
16+ [[nodiscard]] static bool isImage (const std::filesystem::path& path) {
17+ static constexpr std::array exts{" .jpg" , " .jpeg" , " .png" , " .bmp" , " .webp" , " .svg" };
18+
19+ auto ext = path.extension ().string ();
20+ std::transform (ext.begin (), ext.end (), ext.begin (), ::tolower);
21+ if (std::ranges::any_of (exts, [&ext](const auto & e) { return ext == e; }))
22+ return true ;
23+
24+ magic_t magic = magic_open (MAGIC_MIME_TYPE );
25+ if (magic == nullptr )
26+ return false ;
27+
28+ Hyprutils::Utils::CScopeGuard guard{[&magic] { magic_close (magic); }};
29+
30+ if (magic_load (magic, nullptr ) != 0 )
31+ return false ;
32+
33+ const auto * result = magic_file (magic, path.string ().c_str ());
34+ if (result == nullptr )
35+ return false ;
36+
37+ return std::string (result).starts_with (" image/" );
38+ }
39+
1240static std::string getMainConfigPath () {
1341 static const auto paths = Hyprutils::Path::findConfig (" hyprpaper" );
1442
@@ -30,6 +58,7 @@ void CConfigManager::init() {
3058 m_config.addSpecialConfigValue (" wallpaper" , " monitor" , Hyprlang::STRING {" " });
3159 m_config.addSpecialConfigValue (" wallpaper" , " path" , Hyprlang::STRING {" " });
3260 m_config.addSpecialConfigValue (" wallpaper" , " fit_mode" , Hyprlang::STRING {" cover" });
61+ m_config.addSpecialConfigValue (" wallpaper" , " timeout" , Hyprlang::INT {0 });
3362
3463 m_config.commence ();
3564
@@ -55,19 +84,51 @@ static std::expected<std::string, std::string> resolvePath(const std::string_vie
5584 return CAN ;
5685}
5786
58- static std::expected<std::string, std::string> getFullPath (const std::string_view& sv ) {
59- if (sv .empty ())
87+ static std::expected<std::string, std::string> getPath (const std::string_view& path ) {
88+ if (path .empty ())
6089 return std::unexpected (" empty path" );
6190
62- if (sv [0 ] == ' ~' ) {
91+ if (path [0 ] == ' ~' ) {
6392 static auto HOME = getenv (" HOME" );
6493 if (!HOME || HOME [0 ] == ' \0 ' )
6594 return std::unexpected (" home path but no $HOME" );
6695
67- return resolvePath (std::string{HOME } + " /" s + std::string{sv .substr (1 )});
96+ return resolvePath (std::string{HOME } + " /" s + std::string{path .substr (1 )});
6897 }
6998
70- return resolvePath (sv);
99+ return resolvePath (path);
100+ }
101+
102+ static std::expected<std::vector<std::string>, std::string> getFullPath (const std::string& sv) {
103+ if (sv.empty ())
104+ return std::unexpected (" empty path" );
105+
106+ static constexpr const size_t maxImagesCount{1024 };
107+
108+ std::vector<std::string> result;
109+
110+ const auto resolved = getPath (sv);
111+ if (!resolved)
112+ return std::unexpected (resolved.error ());
113+
114+ const auto resolvedPath = resolved.value ();
115+ if (!std::filesystem::exists (resolvedPath))
116+ return std::unexpected (std::format (" File '{}' does not exist" , resolvedPath));
117+
118+ if (std::filesystem::is_directory (resolvedPath))
119+ for (const auto & entry : std::filesystem::directory_iterator (resolvedPath, std::filesystem::directory_options::skip_permission_denied)) {
120+ if (entry.is_regular_file () && isImage (entry.path ()))
121+ result.push_back (entry.path ());
122+
123+ if (result.size () >= maxImagesCount)
124+ break ;
125+ }
126+ else if (isImage (resolvedPath))
127+ result.push_back (resolvedPath);
128+ else
129+ return std::unexpected (std::format (" File '{}' is neither an image nor a directory" , resolvedPath));
130+
131+ return result;
71132}
72133
73134std::vector<CConfigManager::SSetting> CConfigManager::getSettings () {
@@ -78,11 +139,13 @@ std::vector<CConfigManager::SSetting> CConfigManager::getSettings() {
78139
79140 for (auto & key : keys) {
80141 std::string monitor, fitMode, path;
142+ int timeout;
81143
82144 try {
83145 monitor = std::any_cast<Hyprlang::STRING >(m_config.getSpecialConfigValue (" wallpaper" , " monitor" , key.c_str ()));
84146 fitMode = std::any_cast<Hyprlang::STRING >(m_config.getSpecialConfigValue (" wallpaper" , " fit_mode" , key.c_str ()));
85147 path = std::any_cast<Hyprlang::STRING >(m_config.getSpecialConfigValue (" wallpaper" , " path" , key.c_str ()));
148+ timeout = std::any_cast<Hyprlang::INT >(m_config.getSpecialConfigValue (" wallpaper" , " timeout" , key.c_str ()));
86149 } catch (...) {
87150 g_logger->log (LOG_ERR , " Failed parsing wallpaper for key {}" , key);
88151 continue ;
@@ -95,7 +158,12 @@ std::vector<CConfigManager::SSetting> CConfigManager::getSettings() {
95158 continue ;
96159 }
97160
98- result.emplace_back (SSetting{.monitor = std::move (monitor), .fitMode = std::move (fitMode), .path = RESOLVE_PATH .value ()});
161+ if (RESOLVE_PATH .value ().empty ()) {
162+ g_logger->log (LOG_ERR , " Provided path(s) '{}' does not contain a valid image" , path);
163+ continue ;
164+ }
165+
166+ result.emplace_back (SSetting{.monitor = std::move (monitor), .fitMode = std::move (fitMode), .paths = RESOLVE_PATH .value (), .timeout = timeout});
99167 }
100168
101169 return result;
0 commit comments