208 lines
6.7 KiB
C++
208 lines
6.7 KiB
C++
#include "Hyprpaper.hpp"
|
|
#include "../helpers/Memory.hpp"
|
|
|
|
#include <optional>
|
|
#include <format>
|
|
#include <filesystem>
|
|
#include <print>
|
|
|
|
#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 = 2;
|
|
|
|
//
|
|
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);
|
|
}
|
|
|
|
static std::expected<void, std::string> doWallpaper(const std::string_view& RHS) {
|
|
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>(PROTOCOL_VERSION_SUPPORTED);
|
|
|
|
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(), std::min(PROTOCOL_VERSION_SUPPORTED, spec->specVer())));
|
|
|
|
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;
|
|
switch (code) {
|
|
case HYPRPAPER_CORE_APPLYING_ERROR_INVALID_PATH: err = std::format("failed to set wallpaper: Invalid path", code); break;
|
|
case HYPRPAPER_CORE_APPLYING_ERROR_INVALID_MONITOR: err = std::format("failed to set wallpaper: Invalid monitor", code); break;
|
|
default: err = std::format("failed to set wallpaper: unknown error, code {}", code); break;
|
|
}
|
|
});
|
|
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 {};
|
|
}
|
|
|
|
static std::expected<void, std::string> doListActive() {
|
|
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)");
|
|
|
|
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>(PROTOCOL_VERSION_SUPPORTED);
|
|
|
|
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?!");
|
|
|
|
if (spec->specVer() < 2)
|
|
return std::unexpected("can't send: hyprpaper protocol version too low (hyprpaper too old)");
|
|
|
|
auto manager = makeShared<CCHyprpaperCoreManagerObject>(socket->bindProtocol(g_coreImpl->protocol(), std::min(PROTOCOL_VERSION_SUPPORTED, spec->specVer())));
|
|
|
|
if (!manager)
|
|
return std::unexpected("wire error: couldn't create manager");
|
|
|
|
auto status = makeShared<CCHyprpaperStatusObject>(manager->sendGetStatusObject());
|
|
|
|
status->setActiveWallpaper([](const char* mon, const char* wp) { std::println("{}: {}", mon, wp); });
|
|
|
|
socket->roundtrip();
|
|
|
|
return {};
|
|
}
|
|
|
|
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 doWallpaper(RHS);
|
|
else if (LHS == "listactive")
|
|
return doListActive();
|
|
else
|
|
return std::unexpected("invalid hyprpaper request");
|
|
|
|
return {};
|
|
}
|