// 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);
}