diff options
Diffstat (limited to 'hyprctl/src/hyprpaper/Hyprpaper.cpp')
| -rw-r--r-- | hyprctl/src/hyprpaper/Hyprpaper.cpp | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/hyprctl/src/hyprpaper/Hyprpaper.cpp b/hyprctl/src/hyprpaper/Hyprpaper.cpp new file mode 100644 index 00000000..afa7f653 --- /dev/null +++ b/hyprctl/src/hyprpaper/Hyprpaper.cpp @@ -0,0 +1,148 @@ +#include "Hyprpaper.hpp" +#include "../helpers/Memory.hpp" + +#include <optional> +#include <format> +#include <filesystem> + +#include <hyprpaper_core-client.hpp> + +#include <hyprutils/string/VarList2.hpp> +using namespace Hyprutils::String; + +using namespace std::string_literals; + +constexpr const char* SOCKET_NAME = ".hyprpaper.sock"; +static SP<CCHyprpaperCoreImpl> g_coreImpl; + +constexpr const uint32_t PROTOCOL_VERSION_SUPPORTED = 1; + +// +static hyprpaperCoreWallpaperFitMode fitFromString(const std::string_view& sv) { + if (sv == "contain") + return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_CONTAIN; + if (sv == "fit" || sv == "stretch") + return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_STRETCH; + if (sv == "tile") + return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_TILE; + return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_COVER; +} + +static std::expected<std::string, std::string> resolvePath(const std::string_view& sv) { + std::error_code ec; + auto can = std::filesystem::canonical(sv, ec); + + if (ec) + return std::unexpected(std::format("invalid path: {}", ec.message())); + + return can; +} + +static std::expected<std::string, std::string> getFullPath(const std::string_view& sv) { + if (sv.empty()) + return std::unexpected("empty path"); + + if (sv[0] == '~') { + static auto HOME = getenv("HOME"); + if (!HOME || HOME[0] == '\0') + return std::unexpected("home path but no $HOME"); + + return resolvePath(std::string{HOME} + "/"s + std::string{sv.substr(1)}); + } + + return resolvePath(sv); +} + +std::expected<void, std::string> Hyprpaper::makeHyprpaperRequest(const std::string_view& rq) { + if (!rq.contains(' ')) + return std::unexpected("Invalid request"); + + if (!rq.starts_with("/hyprpaper ")) + return std::unexpected("Invalid request"); + + std::string_view LHS, RHS; + auto spacePos = rq.find(' ', 12); + LHS = rq.substr(11, spacePos - 11); + RHS = rq.substr(spacePos + 1); + + if (LHS != "wallpaper") + return std::unexpected("Unknown hyprpaper request"); + + CVarList2 args(std::string{RHS}, 0, ','); + + const std::string MONITOR = std::string{args[0]}; + const auto& PATH_RAW = args[1]; + const auto& FIT = args[2]; + + if (PATH_RAW.empty()) + return std::unexpected("not enough args"); + + const auto RTDIR = getenv("XDG_RUNTIME_DIR"); + + if (!RTDIR || RTDIR[0] == '\0') + return std::unexpected("can't send: no XDG_RUNTIME_DIR"); + + const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE"); + + if (!HIS || HIS[0] == '\0') + return std::unexpected("can't send: no HYPRLAND_INSTANCE_SIGNATURE (not running under hyprland)"); + + const auto PATH = getFullPath(PATH_RAW); + + if (!PATH) + return std::unexpected(std::format("bad path: {}", PATH_RAW)); + + auto socketPath = RTDIR + "/hypr/"s + HIS + "/"s + SOCKET_NAME; + + auto socket = Hyprwire::IClientSocket::open(socketPath); + + if (!socket) + return std::unexpected("can't send: failed to connect to hyprpaper (is it running?)"); + + g_coreImpl = makeShared<CCHyprpaperCoreImpl>(1); + + socket->addImplementation(g_coreImpl); + + if (!socket->waitForHandshake()) + return std::unexpected("can't send: wire handshake failed"); + + auto spec = socket->getSpec(g_coreImpl->protocol()->specName()); + + if (!spec) + return std::unexpected("can't send: hyprpaper doesn't have the spec?!"); + + auto manager = makeShared<CCHyprpaperCoreManagerObject>(socket->bindProtocol(g_coreImpl->protocol(), PROTOCOL_VERSION_SUPPORTED)); + + if (!manager) + return std::unexpected("wire error: couldn't create manager"); + + auto wallpaper = makeShared<CCHyprpaperWallpaperObject>(manager->sendGetWallpaperObject()); + + if (!wallpaper) + return std::unexpected("wire error: couldn't create wallpaper object"); + + bool canExit = false; + std::optional<std::string> err; + + wallpaper->setFailed([&canExit, &err](uint32_t code) { + canExit = true; + err = std::format("failed to set wallpaper, code {}", code); + }); + wallpaper->setSuccess([&canExit]() { canExit = true; }); + + wallpaper->sendPath(PATH->c_str()); + wallpaper->sendMonitorName(MONITOR.c_str()); + if (!FIT.empty()) + wallpaper->sendFitMode(fitFromString(FIT)); + + wallpaper->sendApply(); + + while (!canExit) { + socket->dispatchEvents(true); + } + + if (err) + return std::unexpected(*err); + + return {}; +}
\ No newline at end of file |
