Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions hyprctl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,28 @@ target_include_directories(hyprctl PRIVATE "hw-protocols")

# Hyprwire

function(hyprprotocol protoPath protoName)
set(path ${CMAKE_CURRENT_SOURCE_DIR}/${protoPath})
pkg_get_variable(HYPRWIRE_PROTOCOLS_DIR hyprwire-protocols pkgdatadir)
message(STATUS "Found hyprwire-protocols at ${HYPRWIRE_PROTOCOLS_DIR}")

function(hyprprotocol protoPath protoName external)
if(external)
set(path ${protoPath})
else()
set(path ${HYPRWIRE_PROTOCOLS_DIR}/${protoPath})
endif()
add_custom_command(
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-client.cpp
${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-client.hpp
${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-spec.hpp
COMMAND hyprwire-scanner --client ${path}/${protoName}.xml
${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
target_sources(hyprctl PRIVATE hw-protocols/${protoName}-client.cpp
hw-protocols/${protoName}-client.hpp
hw-protocols/${protoName}-spec.hpp)
)
endfunction()

hyprprotocol(hw-protocols hyprpaper_core)
hyprprotocol(hw-protocols hyprpaper_core true)
hyprprotocol(hyprtavern hp_hyprtavern_core_v1 false)

# binary
install(TARGETS hyprctl)
Expand Down
181 changes: 132 additions & 49 deletions hyprctl/src/hyprpaper/Hyprpaper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@
#include <filesystem>

#include <hyprpaper_core-client.hpp>
#include <hp_hyprtavern_core_v1-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 char* SOCKET_NAME = ".hyprpaper.sock";
static SP<CCHyprpaperCoreImpl> g_coreImpl;
static SP<CCHpHyprtavernCoreV1Impl> g_tavernImpl;

constexpr const uint32_t PROTOCOL_VERSION_SUPPORTED = 1;
constexpr const uint32_t HYPRPAPER_PROTOCOL_VERSION_SUPPORTED = 1;
constexpr const uint32_t HYPRTAVERN_PROTOCOL_VERSION_SUPPORTED = 1;

//
static hyprpaperCoreWallpaperFitMode fitFromString(const std::string_view& sv) {
Expand Down Expand Up @@ -53,6 +56,128 @@ static std::expected<std::string, std::string> getFullPath(const std::string_vie
return resolvePath(sv);
}

static std::expected<void, std::string> makeRequestToSocket(SP<Hyprwire::IClientSocket> socket, const std::string& PATH, const std::string& MONITOR, const std::string_view& FIT) {
g_coreImpl = makeShared<CCHyprpaperCoreImpl>(HYPRPAPER_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(), HYPRPAPER_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");

std::optional<std::string> err;

wallpaper->setFailed([&err](uint32_t code) { err = std::format("failed to set wallpaper, code {}", code); });

wallpaper->sendPath(PATH.c_str());
wallpaper->sendMonitorName(MONITOR.c_str());
if (!FIT.empty())
wallpaper->sendFitMode(fitFromString(FIT));

wallpaper->sendApply();

socket->roundtrip();

if (err)
return std::unexpected(*err);

return {};
}

static bool attemptHyprtavern(const std::string& PATH, const std::string& MONITOR, const std::string_view& FIT) {
// try to connect to hyprtavern
g_tavernImpl = makeShared<CCHpHyprtavernCoreV1Impl>(HYPRTAVERN_PROTOCOL_VERSION_SUPPORTED);

const auto RTDIR = getenv("XDG_RUNTIME_DIR");

auto socketPath = RTDIR + "/hyprtavern/ht.sock"s;

auto socket = Hyprwire::IClientSocket::open(socketPath);

if (!socket)
return false;

socket->addImplementation(g_tavernImpl);

if (!socket->waitForHandshake())
return false;

auto spec = socket->getSpec(g_tavernImpl->protocol()->specName());

if (!spec)
return false;

auto manager = makeShared<CCHpHyprtavernCoreManagerV1Object>(socket->bindProtocol(g_tavernImpl->protocol(), HYPRTAVERN_PROTOCOL_VERSION_SUPPORTED));

if (!manager)
return false;

// run a query, find all possible candidates
const auto WAYLAND_DISPLAY = getenv("WAYLAND_DISPLAY");

if (!WAYLAND_DISPLAY)
return false;

std::string propStr = std::format("GLOBAL:WAYLAND_DISPLAY={}", WAYLAND_DISPLAY);

std::vector<const char*> props = {propStr.c_str()};
std::vector<const char*> protos = {"hyprpaper_core"};

auto query = makeShared<CCHpHyprtavernBusQueryV1Object>(
manager->sendGetQueryObject(protos, HP_HYPRTAVERN_CORE_V1_BUS_QUERY_FILTER_MODE_ALL, props, HP_HYPRTAVERN_CORE_V1_BUS_QUERY_FILTER_MODE_ALL));

uint32_t candidateId = 0;

query->setResults([&candidateId](const std::vector<uint32_t>& vec) {
if (vec.empty())
return;

candidateId = vec.at(0);
});

socket->roundtrip();

if (candidateId == 0)
return false; // no hyprpaper-compatible thing in the tavern

auto handle = makeShared<CCHpHyprtavernBusObjectHandleV1Object>(manager->sendGetObjectHandle(candidateId));

if (!handle)
return false;

int newSocketFd = -1;

handle->setSocket([&newSocketFd](int fd) { newSocketFd = fd; });

handle->sendConnect();

socket->roundtrip();

if (newSocketFd < 0)
return false;

auto newSocket = Hyprwire::IClientSocket::open(newSocketFd);

if (!newSocket)
return false;

return !!makeRequestToSocket(newSocket, PATH, MONITOR, FIT);
}

std::expected<void, std::string> Hyprpaper::makeHyprpaperRequest(const std::string_view& rq) {
if (!rq.contains(' '))
return std::unexpected("Invalid request");
Expand Down Expand Up @@ -92,57 +217,15 @@ std::expected<void, std::string> Hyprpaper::makeHyprpaperRequest(const std::stri
if (!PATH)
return std::unexpected(std::format("bad path: {}", PATH_RAW));

if (attemptHyprtavern(*PATH, MONITOR, FIT))
return {};

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 {};
return makeRequestToSocket(socket, *PATH, MONITOR, FIT);
}
Loading