From 83d5ba42f8a98fc0df177efef4333f74a0481f61 Mon Sep 17 00:00:00 2001 From: pants Date: Mon, 21 Sep 2026 14:18:06 -0700 Subject: init --- src/canvas/wayland/config/hyprland.cpp | 151 +++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 src/canvas/wayland/config/hyprland.cpp (limited to 'src/canvas/wayland/config/hyprland.cpp') diff --git a/src/canvas/wayland/config/hyprland.cpp b/src/canvas/wayland/config/hyprland.cpp new file mode 100644 index 0000000..42387fb --- /dev/null +++ b/src/canvas/wayland/config/hyprland.cpp @@ -0,0 +1,151 @@ +// Display images inside a terminal +// Copyright (C) 2023 JustKidding +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "hyprland.hpp" +#include "os.hpp" +#include "tmux.hpp" +#include "util/socket.hpp" + +#include +#include +#include +#include + +using njson = nlohmann::json; +namespace fs = std::filesystem; + +HyprlandSocket::HyprlandSocket(const std::string_view signature) + : logger(spdlog::get("wayland")) +{ + const auto socket_base_dir = os::getenv("XDG_RUNTIME_DIR").value_or("/tmp"); + const auto socket_rel_path = fmt::format("hypr/{}/.socket.sock", signature); + socket_path = fmt::format("{}/{}", socket_base_dir, socket_rel_path); + // XDG_RUNTIME_DIR set but hyprland < 0.40 + if (!fs::exists(socket_path)) { + socket_path = fmt::format("/tmp/{}", socket_rel_path); + } + + logger->info("Using hyprland socket {}", socket_path); + const auto active = request_result("j/activewindow"); + address = active.at("address"); + set_active_monitor(); +} + +void HyprlandSocket::set_active_monitor() +{ + const auto monitors = request_result("j/monitors"); + for (const auto &monitor : monitors) { + bool focused = monitor.at("focused"); + if (focused) { + output_name = monitor.at("name"); + output_scale = monitor.at("scale"); + break; + } + } +} + +auto HyprlandSocket::get_focused_output_name() -> std::string +{ + return output_name; +} + +auto HyprlandSocket::request_result(const std::string_view payload) -> nlohmann::json +{ + const UnixSocket socket{socket_path}; + socket.write(payload.data(), payload.size()); + const std::string result = socket.read_until_empty(); + return njson::parse(result); +} + +void HyprlandSocket::request(const std::string_view payload) +{ + const UnixSocket socket{socket_path}; + logger->debug("Running socket command {}", payload); + socket.write(payload.data(), payload.length()); +} + +auto HyprlandSocket::get_active_window() -> nlohmann::json +{ + // recalculate address in case it changed + if (tmux::is_used()) { + const auto active = request_result("j/activewindow"); + address = active.at("address"); + } + const auto clients = request_result("j/clients"); + const auto client = ranges::find_if(clients, [this](const njson &json) { return json.at("address") == address; }); + if (client == clients.end()) { + throw std::runtime_error("Active window not found"); + } + return *client; +} + +auto HyprlandSocket::get_window_info() -> struct WaylandWindowGeometry { + const auto terminal = get_active_window(); + const auto &sizes = terminal.at("size"); + const auto &coords = terminal.at("at"); + + return { + .width = sizes.at(0), + .height = sizes.at(1), + .x = coords.at(0), + .y = coords.at(1), + }; +} + +void HyprlandSocket::initial_setup(const std::string_view appid) +{ + disable_focus(appid); + enable_floating(appid); + remove_borders(appid); + remove_rounding(appid); +} + +void HyprlandSocket::remove_rounding(const std::string_view appid) +{ + const auto payload = fmt::format("/keyword windowrulev2 rounding 0,title:{}", appid); + request(payload); +} + +void HyprlandSocket::disable_focus(const std::string_view appid) +{ + const auto payload = fmt::format("/keyword windowrulev2 nofocus,title:{}", appid); + request(payload); +} + +void HyprlandSocket::enable_floating(const std::string_view appid) +{ + const auto payload = fmt::format("/keyword windowrulev2 float,title:{}", appid); + request(payload); +} + +void HyprlandSocket::remove_borders(const std::string_view appid) +{ + const auto payload = fmt::format("/keyword windowrulev2 noborder,title:{}", appid); + request(payload); +} + +void HyprlandSocket::move_window(const std::string_view appid, int xcoord, int ycoord) +{ + int res_x = xcoord; + int res_y = ycoord; + if (output_scale > 1.0F) { + const int offset = 10; + res_x = res_x / 2 + offset; + res_y = res_y / 2 + offset; + } + const auto payload = fmt::format("/dispatch movewindowpixel exact {} {},title:{}", res_x, res_y, appid); + request(payload); +} -- cgit v1.2.3