base function of indicators work again
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user