base function of indicators work again

This commit is contained in:
2026-01-31 15:26:11 +01:00
parent ad5e678c5d
commit 7ad6f46b3c
12 changed files with 212 additions and 523 deletions

View File

@@ -1,64 +1,82 @@
#include "services/hyprland.hpp"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <glib-unix.h>
#include <glib.h>
#include <iostream>
#include <iterator>
#include <memory>
#include <nlohmann/json.hpp>
#include <stdexcept>
#include <string>
#include <sys/socket.h>
#include <sys/un.h>
#include <unordered_set>
#include <unistd.h>
#include "helpers/command.hpp"
#include "helpers/hypr.hpp"
#include "helpers/socket.hpp"
HyprlandService::HyprlandService() = default;
#include "gtkmm/box.h"
#include "gtkmm/button.h"
HyprlandService::~HyprlandService() {
if (fd != -1) {
close(fd);
fd = -1;
HyprlandService::HyprlandService() {
auto monitorDataJson = HyprctlHelper::getMonitorData();
for (const auto &item : monitorDataJson) {
auto monitorPtr = std::make_shared<Monitor>();
int monitorId = item["id"].get<int>();
monitorPtr->id = monitorId;
monitorPtr->name = item["name"].get<std::string>();
this->monitors[monitorPtr->id] = monitorPtr;
// Create inidcators for hyprsplit
for (int i = 1; i <= NUM_WORKSPACES; i++) {
WorkspaceState state;
int workspaceId = i + (NUM_WORKSPACES * monitorId);
state.id = workspaceId;
state.monitorId = monitorId;
auto view = std::make_shared<WorkspaceIndicator>(std::to_string(i));
auto workSpace = std::make_shared<Workspace>();
workSpace->state = state;
workSpace->view = view;
monitorPtr->monitorWorkspaces[workspaceId] = workSpace;
this->workspaces[workspaceId] = workSpace;
}
}
for (auto &p : this->workspaces) {
delete p.second;
auto workspaceDataJson = HyprctlHelper::getWorkspaceData();
for (const auto &workspace : workspaceDataJson) {
auto workspacePtr = std::make_shared<Workspace>();
workspacePtr->state.id = workspace["id"].get<int>();
workspacePtr->state.monitorId = workspace["monitorID"].get<int>();
workspaces[workspacePtr->state.id] = workspacePtr;
}
this->workspaces.clear();
this->monitors.clear();
bindSocket();
}
void HyprlandService::on_hyprland_event(std::string event, std::string data) {
if (event == "urgent") {
onUrgentEvent(data);
}
if (event == "activewindowv2") {
onActiveWindowEvent(data);
}
if (event == "workspace" || event == "movewindow") {
refresh_workspaces();
}
if (event == "monitoradded" || event == "monitorremoved") {
refresh_monitors();
}
void HyprlandService::init() {
}
void HyprlandService::start() {
const std::string socket_path = get_socket_path();
if (socket_path.empty()) {
return;
}
std::string HyprlandService::getSocketPath() {
auto sig = std::string(std::getenv("HYPRLAND_INSTANCE_SIGNATURE"));
auto runtime = std::string(std::getenv("XDG_RUNTIME_DIR"));
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == -1) {
return std::string(runtime) + "/hypr/" + sig + "/.socket2.sock";
}
void HyprlandService::bindSocket() {
std::string socketPath = getSocketPath();
socketFd = socket(AF_UNIX, SOCK_STREAM, 0);
if (socketFd == -1) {
std::cerr << "[Hyprland] Failed to create socket" << std::endl;
return;
}
@@ -66,271 +84,41 @@ void HyprlandService::start() {
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
std::strncpy(addr.sun_path, socket_path.c_str(), sizeof(addr.sun_path) - 1);
std::strncpy(addr.sun_path, socketPath.c_str(), sizeof(addr.sun_path) - 1);
if (connect(socketFd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) == -1) {
std::cerr << "[Hyprland] Failed to connect to " << socketPath << std::endl;
close(socketFd);
socketFd = -1;
if (connect(fd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) == -1) {
std::cerr << "[Hyprland] Failed to connect to " << socket_path
<< std::endl;
close(fd);
fd = -1;
return;
}
Glib::signal_io().connect(
sigc::mem_fun(*this, &HyprlandService::on_socket_read), fd,
Glib::IOCondition::IO_IN | Glib::IOCondition::IO_HUP |
Glib::IOCondition::IO_ERR);
GSource *source = g_unix_fd_source_new(socketFd, static_cast<GIOCondition>(G_IO_IN | G_IO_HUP | G_IO_ERR));
refresh_monitors();
auto onSocketEvent = [](gint fd, GIOCondition condition, gpointer user_data) -> gboolean {
HyprlandService *self = static_cast<HyprlandService *>(user_data);
auto messages = SocketHelper::parseSocketMessage(fd, ">>");
return G_SOURCE_CONTINUE;
};
g_source_set_callback(source, (GSourceFunc)(+onSocketEvent), this, nullptr);
g_source_attach(source, g_main_context_default());
g_source_unref(source);
}
bool HyprlandService::on_socket_read(Glib::IOCondition condition) {
const auto error_mask = Glib::IOCondition::IO_HUP | Glib::IOCondition::IO_ERR;
std::shared_ptr<Gtk::Box> HyprlandService::getWorkspaceIndicatorsForMonitor(int monitorId) {
auto box = std::make_shared<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
if (static_cast<int>(condition & error_mask) != 0) {
std::cerr << "[Hyprland] Socket disconnected." << std::endl;
if (fd != -1) {
close(fd);
fd = -1;
}
return false;
auto monitor = monitors[monitorId];
int i = 0;
for (const auto &[wsId, wsPair] : monitor->monitorWorkspaces) {
box->append((Gtk::Box &)*wsPair->view);
i++;
}
char temp_buffer[4096];
const ssize_t bytes_read = read(fd, temp_buffer, sizeof(temp_buffer));
if (bytes_read <= 0) {
// peer closed or error
std::cerr << "[Hyprland] Socket read returned " << bytes_read << std::endl;
if (fd != -1) {
close(fd);
fd = -1;
}
return false;
}
// append exactly bytes_read bytes (may contain embedded nulls)
this->socket_buffer.append(temp_buffer, static_cast<size_t>(bytes_read));
size_t pos = 0;
while ((pos = this->socket_buffer.find('\n')) != std::string::npos) {
const std::string line = this->socket_buffer.substr(0, pos);
parse_message(line);
this->socket_buffer.erase(0, pos + 1);
}
return true;
}
void HyprlandService::parse_message(const std::string &line) {
const size_t split = line.find(">>");
if (split != std::string::npos) {
const std::string event_name = line.substr(0, split);
const std::string event_data = line.substr(split + 2);
socketEventSignal.emit(event_name, event_data);
}
}
std::string HyprlandService::get_socket_path() {
const char *sig = std::getenv("HYPRLAND_INSTANCE_SIGNATURE");
const char *runtime = std::getenv("XDG_RUNTIME_DIR");
if (!sig || !runtime) {
std::cerr << "[Hyprland] Environment variables missing!" << std::endl;
return "";
}
return std::string(runtime) + "/hypr/" + sig + "/.socket2.sock";
}
void HyprlandService::refresh_monitors() {
for (auto &p : this->workspaces) {
delete p.second;
}
this->workspaces.clear();
this->monitors.clear();
auto monitorsJson = HyprctlHelper::getMonitorData();
for (const auto &monitorJson : monitorsJson) {
Monitor monitor;
monitor.id = monitorJson.value("id", -1);
monitor.name = monitorJson.value("name", "");
monitor.x = monitorJson.value("x", 0);
monitor.y = monitorJson.value("y", 0);
monitor.focusedWorkspaceId = monitorJson["activeWorkspace"].value("id", -1);
if (monitor.id >= 0) {
this->monitors[monitor.id] = monitor;
}
for (int slot = 1; slot <= HyprlandService::kWorkspaceSlotCount; ++slot) {
WorkspaceState wsState;
wsState.focused = false;
wsState.active = false;
wsState.label = std::to_string(slot);
wsState.monitorId = monitor.id;
int id = slot + monitor.id * HyprlandService::kWorkspaceSlotCount;
wsState.hyprId = id;
this->workspaces[id] = new WorkspaceState(wsState);
if (monitor.id >= 0) {
this->monitors[monitor.id].workspaceStates[slot] = this->workspaces[id];
}
}
}
this->refresh_workspaces();
monitorStateChanged.emit();
}
void HyprlandService::refresh_workspaces() {
auto workspacesJson = HyprctlHelper::getWorkspaceData();
for (auto &[id, ws] : this->workspaces) {
ws->focused = false;
ws->active = false;
}
auto monitorsJson = HyprctlHelper::getMonitorData();
for (const auto &monitorJson : monitorsJson) {
const int monitorId = monitorJson.value("id", -1);
const int focusedWorkspaceId = monitorJson["activeWorkspace"].value("id", -1);
auto it = this->monitors.find(monitorId);
if (it != this->monitors.end()) {
it->second.focusedWorkspaceId = focusedWorkspaceId;
}
}
auto clientsJson = HyprctlHelper::getClientData();
std::unordered_set<std::string> liveClientAddresses;
for (const auto &clientJson : clientsJson) {
const std::string addr = clientJson.value("address", "");
if (addr.empty()) {
continue;
}
if (addr.rfind("0x", 0) == 0) {
liveClientAddresses.insert(addr.substr(2));
} else {
liveClientAddresses.insert(addr);
}
}
for (const auto &workspaceJson : workspacesJson) {
const int workspaceId = workspaceJson.value("id", -1);
auto workspaceStateIt = this->workspaces.find(workspaceId);
if (workspaceStateIt == this->workspaces.end()) {
continue;
}
WorkspaceState *workspaceState = workspaceStateIt->second;
auto mit = this->monitors.find(workspaceState->monitorId);
if (mit != this->monitors.end()) {
workspaceState->focused = mit->second.focusedWorkspaceId == workspaceId;
} else {
workspaceState->focused = false;
}
workspaceState->active = true;
}
for (auto &[id, ws] : this->workspaces) {
auto &urgent = ws->urgentWindows;
auto newEnd = std::remove_if(urgent.begin(), urgent.end(), [&](const std::string &addr) {
return liveClientAddresses.find(addr) == liveClientAddresses.end();
});
if (newEnd != urgent.end()) {
urgent.erase(newEnd, urgent.end());
}
}
workspaceStateChanged.emit();
}
void HyprlandService::switchToWorkspace(int workspaceId) {
std::string cmd =
"hyprctl dispatch workspace " + std::to_string(workspaceId);
try {
(void)CommandHelper::execNoOutput(cmd.c_str());
} catch (const std::exception &ex) {
std::cerr << "[Hyprland] Failed to dispatch workspace command: "
<< ex.what() << " cmd=" << cmd << std::endl;
}
}
void HyprlandService::onUrgentEvent(std::string windowAddress) {
auto clientsJson = HyprctlHelper::getClientData();
for (const auto &clientJson : clientsJson) {
const std::string addr = clientJson.value("address", "");
if (addr == "0x" + windowAddress) {
int workspaceId = clientJson["workspace"].value("id", -1);
auto it = this->workspaces.find(workspaceId);
if (it != this->workspaces.end() && it->second) {
WorkspaceState *ws = it->second;
auto uit = std::find(ws->urgentWindows.begin(), ws->urgentWindows.end(), windowAddress);
if (uit == ws->urgentWindows.end()) {
ws->urgentWindows.push_back(windowAddress);
workspaceStateChanged.emit();
}
}
break;
}
}
}
void HyprlandService::onActiveWindowEvent(std::string windowAddress) {
auto clientsJson = HyprctlHelper::getClientData();
for (const auto &clientJson : clientsJson) {
const std::string addr = clientJson.value("address", "");
if (addr == "0x" + windowAddress) {
int workspaceId = clientJson["workspace"]["id"];
auto it = this->workspaces.find(workspaceId);
if (it != this->workspaces.end() && it->second) {
WorkspaceState *ws = it->second;
auto uit = std::find(ws->urgentWindows.begin(), ws->urgentWindows.end(), windowAddress);
if (uit != ws->urgentWindows.end()) {
ws->urgentWindows.erase(uit);
workspaceStateChanged.emit();
}
break;
}
}
}
}
HyprlandService::Monitor *HyprlandService::getMonitorById(int id) {
auto it = monitors.find(id);
if (it == monitors.end()) {
throw std::runtime_error("Monitor with ID " + std::to_string(id) +
" not found.");
}
return &it->second;
}
HyprlandService::Monitor *HyprlandService::getMonitorByIndex(std::size_t index) {
if (index >= monitors.size()) {
throw std::runtime_error("Monitor index out of bounds: " + std::to_string(index));
}
auto it = monitors.begin();
std::advance(it, static_cast<long>(index));
return &it->second;
}
return box;
}