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

@@ -5,10 +5,11 @@
#include <sigc++/sigc++.h>
App::App() {
this->app = Gtk::Application::create("org.example.mybar");
this->setupServices();
this->notificationService.intialize();
this->app = Gtk::Application::create("org.example.mybar");
this->hyprlandService = HyprlandService::getInstance();
app->signal_activate().connect([&]() {
auto display = Gdk::Display::get_default();
@@ -16,23 +17,15 @@ App::App() {
for (guint i = 0; i < monitors->get_n_items(); ++i) {
auto monitor = std::dynamic_pointer_cast<Gdk::Monitor>(
monitors->get_object(i));
monitors->get_object(i)
);
if (monitor) {
HyprlandService::Monitor *hyprlandMonitor = nullptr;
try {
hyprlandMonitor =
this->hyprlandService->getMonitorByIndex(i);
} catch (const std::exception &ex) {
std::cerr << "[App] Failed to fetch Hyprland monitor: "
<< ex.what() << std::endl;
continue;
}
auto bar = new Bar(monitor->gobj(), hyprlandMonitor->id);
auto bar = new Bar(monitor->gobj());
bar->set_application(app);
bar->show();
bar->addLeftWidget(hyprlandService->getWorkspaceIndicatorsForMonitor(i));
bars.push_back(bar);
}
}
@@ -50,10 +43,6 @@ App::App() {
}
void App::setupServices() {
this->hyprlandService->socketEventSignal.connect(sigc::mem_fun(
*this->hyprlandService, &HyprlandService::on_hyprland_event));
this->hyprlandService->start();
this->trayService->start();
}

View File

@@ -9,14 +9,12 @@
#include "widgets/date.hpp"
#include "widgets/spacer.hpp"
#include "widgets/volumeWidget.hpp"
#include "widgets/workspaceIndicator.hpp"
#include "glibmm/main.h"
#include "gtk/gtk.h"
#include "sigc++/functors/mem_fun.h"
Bar::Bar(GdkMonitor *monitor, int monitorId)
: monitorId(monitorId) {
Bar::Bar(GdkMonitor *monitor) {
set_name("bar-window");
gtk_layer_init_for_window(this->gobj());
@@ -34,9 +32,8 @@ Bar::Bar(GdkMonitor *monitor, int monitorId)
set_child(main_box);
this->volumeWidget = Gtk::make_managed<VolumeWidget>();
this->workspaceIndicator = Gtk::make_managed<WorkspaceIndicator>(monitorId);
this->trayWidget = Gtk::make_managed<TrayWidget>();
this->volumeWidget = std::make_shared<VolumeWidget>();
this->trayWidget = std::make_shared<TrayWidget>();
load_css();
setup_ui();
@@ -59,7 +56,7 @@ void Bar::setup_ui() {
}
void Bar::setup_left_box() {
left_box.append(*workspaceIndicator);
// left_box.append(*workspaceIndicator);
}
void Bar::setup_center_box() {

View File

@@ -0,0 +1,42 @@
#include "components/workspaceIndicator.hpp"
#include <iostream>
#include "gtkmm/button.h"
#include "gtkmm/label.h"
WorkspaceIndicator::WorkspaceIndicator(std::string label, sigc::slot<void()> onClick)
: Gtk::Box(Gtk::Orientation::HORIZONTAL) {
auto overlay = Gtk::make_managed<Gtk::Overlay>();
auto numLabel = Gtk::make_managed<Gtk::Label>(label);
auto pillContainer = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
// auto gesture = Gtk::GestureClick::create();
// gesture->set_button(GDK_BUTTON_PRIMARY);
// gesture->signal_released().connect([this, i](int, double, double) {
// this->service->switchToWorkspace(
// i + this->monitorId * HyprlandService::kWorkspaceSlotCount);
// });
// overlay->add_controller(gesture);
overlay->add_css_class("workspace-pill");
// if (i == 6 || i == 7) {
// auto indicator = Gtk::make_managed<Gtk::Label>(i == 6 ? "🫱🏻" : "🫲🏻");
// indicator->add_css_class(i == 6 ? "workspace-pill-six" : "workspace-pill-seven");
// indicator->set_valign(Gtk::Align::END);
// overlay->set_child(*indicator);
// overlay->add_overlay(*numLabel);
// pillContainer->append(*overlay);
// } else {
overlay->set_child(*numLabel);
pillContainer->append(*overlay);
// }
append(*pillContainer);
}

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

View File

@@ -1,104 +0,0 @@
#include "widgets/workspaceIndicator.hpp"
#include <cassert>
#include <gdk/gdk.h>
#include <gtkmm/gestureclick.h>
#include <gtkmm/overlay.h>
#include <gtkmm/widget.h>
#include <sigc++/functors/mem_fun.h>
#include "services/hyprland.hpp"
#include "gtkmm/box.h"
#include "gtkmm/label.h"
WorkspaceIndicator::WorkspaceIndicator(int monitorId)
: Gtk::Box(Gtk::Orientation::HORIZONTAL),
monitorId(monitorId) {
set_margin_top(2);
set_margin_bottom(2);
workspaceConnection = service->workspaceStateChanged.connect(
sigc::mem_fun(*this, &WorkspaceIndicator::on_workspace_update));
monitorConnection = service->monitorStateChanged.connect(
sigc::mem_fun(*this, &WorkspaceIndicator::on_monitor_update));
for (int i = 1; i <= HyprlandService::kWorkspaceSlotCount; ++i) {
auto overlay = Gtk::make_managed<Gtk::Overlay>();
auto numLabel = Gtk::make_managed<Gtk::Label>(std::to_string(i));
auto pillContainer = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
auto gesture = Gtk::GestureClick::create();
gesture->set_button(GDK_BUTTON_PRIMARY);
gesture->signal_released().connect([this, i](int, double, double) {
this->service->switchToWorkspace(
i + this->monitorId * HyprlandService::kWorkspaceSlotCount);
});
workspaceGestures[i] = gesture;
workspaceIndicators[i] = overlay;
overlay->add_controller(gesture);
overlay->add_css_class("workspace-pill");
if (i == 6 || i == 7) {
auto indicator = Gtk::make_managed<Gtk::Label>(i == 6 ? "🫱🏻" : "🫲🏻");
indicator->add_css_class(i == 6 ? "workspace-pill-six" : "workspace-pill-seven");
indicator->set_valign(Gtk::Align::END);
overlay->set_child(*indicator);
overlay->add_overlay(*numLabel);
pillContainer->append(*overlay);
} else {
overlay->set_child(*numLabel);
pillContainer->append(*overlay);
}
append(*pillContainer);
}
rebuild();
}
WorkspaceIndicator::~WorkspaceIndicator() {
if (workspaceConnection.connected()) {
workspaceConnection.disconnect();
}
if (monitorConnection.connected()) {
monitorConnection.disconnect();
}
}
void WorkspaceIndicator::on_workspace_update() {
rebuild();
}
void WorkspaceIndicator::on_monitor_update() {
rebuild();
}
void WorkspaceIndicator::refreshLabel(Gtk::Overlay *overlay, const HyprlandService::WorkspaceState &state) {
overlay->remove_css_class("workspace-pill-active");
overlay->remove_css_class("workspace-pill-focused");
overlay->remove_css_class("workspace-pill-urgent");
// controller created once in constructor and reused
if (state.urgentWindows.size() > 0) {
overlay->add_css_class("workspace-pill-urgent");
} else {
if (state.focused) {
overlay->add_css_class("workspace-pill-focused");
} else if (state.active) {
overlay->add_css_class("workspace-pill-active");
}
}
}
void WorkspaceIndicator::rebuild() {
HyprlandService::Monitor *mon = service->getMonitorById(this->monitorId);
for (auto [id, workspaceState] : mon->workspaceStates) {
Gtk::Overlay *overlay = workspaceIndicators[id];
this->refreshLabel(overlay, *workspaceState);
}
}