diff --git a/CMakeLists.txt b/CMakeLists.txt index ed3f62b..b6488e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,6 @@ target_sources(bar_lib src/widgets/clock.cpp src/widgets/date.cpp - src/widgets/workspaceIndicator.cpp src/widgets/volumeWidget.cpp src/widgets/webWidget.cpp @@ -41,6 +40,7 @@ target_sources(bar_lib src/widgets/controlCenter.cpp src/components/popover.cpp + src/components/workspaceIndicator.cpp src/components/base/button.cpp ) include_directories(bar_lib PRIVATE diff --git a/include/app.hpp b/include/app.hpp index b0bff5d..39fb0ac 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -19,7 +19,7 @@ class App { private: Glib::RefPtr app; std::vector bars; - HyprlandService *hyprlandService = HyprlandService::getInstance(); + HyprlandService *hyprlandService = nullptr; NotificationService notificationService; TrayService *trayService = TrayService::getInstance(); diff --git a/include/bar/bar.hpp b/include/bar/bar.hpp index 4173277..a739e20 100644 --- a/include/bar/bar.hpp +++ b/include/bar/bar.hpp @@ -2,22 +2,24 @@ #include #include +#include #include "widgets/clock.hpp" #include "widgets/date.hpp" #include "widgets/tray.hpp" #include "widgets/volumeWidget.hpp" #include "widgets/webWidget.hpp" -#include "widgets/workspaceIndicator.hpp" #include "widgets/controlCenter.hpp" class Bar : public Gtk::Window { public: - Bar(GdkMonitor *monitor, int monitorId); + Bar(GdkMonitor *monitor); + + void addLeftWidget(std::shared_ptr widget) { left_box.append(*widget); } + void addCenterWidget(std::shared_ptr widget) { center_box.append(*widget); } + void addRightWidget(std::shared_ptr widget) { right_box.append(*widget); } private: - int monitorId; - Gtk::CenterBox main_box{}; Gtk::Box left_box{Gtk::Orientation::HORIZONTAL}; Gtk::Box center_box{Gtk::Orientation::HORIZONTAL}; @@ -28,9 +30,8 @@ class Bar : public Gtk::Window { WebWidget homeAssistant{"\ue88a", "Home Assistant", "https://home.rivercry.com"}; ControlCenter controlCenter{"\ue88a", "Control Center"}; - WorkspaceIndicator *workspaceIndicator = nullptr; - TrayWidget *trayWidget = nullptr; - VolumeWidget *volumeWidget = nullptr; + std::shared_ptr trayWidget = nullptr; + std::shared_ptr volumeWidget = nullptr; void setup_ui(); diff --git a/include/components/workspaceIndicator.hpp b/include/components/workspaceIndicator.hpp new file mode 100644 index 0000000..f6b5614 --- /dev/null +++ b/include/components/workspaceIndicator.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "gtkmm/box.h" +#include "gtkmm/button.h" +#include "gtkmm/overlay.h" + +class WorkspaceIndicator : public Gtk::Box { + public: + WorkspaceIndicator(std::string label, sigc::slot onClick = {}); + + private: + Gtk::Button button; +}; \ No newline at end of file diff --git a/include/helpers/socket.hpp b/include/helpers/socket.hpp index 0345c5d..45953e9 100644 --- a/include/helpers/socket.hpp +++ b/include/helpers/socket.hpp @@ -6,7 +6,7 @@ #include #include -#include "helper/string.hpp" +#include "helpers/string.hpp" class SocketHelper { typedef struct SocketMessage { @@ -18,6 +18,7 @@ class SocketHelper { static std::vector parseSocketMessage(int socketFd, const std::string &delimiter) { char buffer[4096]; std::string data; + ssize_t bytesRead = recv(socketFd, buffer, sizeof(buffer) - 1, 0); if (bytesRead > 0) { buffer[bytesRead] = '\0'; @@ -34,17 +35,15 @@ class SocketHelper { assert(false && "Delimiter not found in socket message"); } - auto splitMessages = StringHelper::split(data, '\n'); + auto splitMessages = StringHelper::split(data, '\n'); auto splitMessagesFinal = std::vector(); for (auto splitMessage : splitMessages) { - - - SocketMessage message; - auto messageCommandVector = StringHelper::split(splitMessage, ">>"); - - message.eventType = messageCommandVector[0]; - message.eventData = messageCommandVector.size() > 1 ? messageCommandVector[1] : ""; - splitMessagesFinal.push_back(message); + SocketMessage message; + auto messageCommandVector = StringHelper::split(splitMessage, delimiter); + + message.eventType = messageCommandVector[0]; + message.eventData = messageCommandVector.size() > 1 ? messageCommandVector[1] : ""; + splitMessagesFinal.push_back(message); } return splitMessagesFinal; diff --git a/include/services/hyprland.hpp b/include/services/hyprland.hpp index 5bd057d..dd6d122 100644 --- a/include/services/hyprland.hpp +++ b/include/services/hyprland.hpp @@ -1,79 +1,70 @@ #pragma once -#include #include #include +#include #include #include #include #include +#include "components/workspaceIndicator.hpp" + +#include "gtkmm/box.h" + +#define NUM_WORKSPACES 7 + class HyprlandService { - inline static HyprlandService *instance = nullptr; +inline static HyprlandService *instance = nullptr; public: - static constexpr int kWorkspaceSlotCount = 7; - struct WorkspaceState { - int hyprId = -1; - int monitorId = -1; - bool active = false; - bool focused = false; - std::vector urgentWindows; + int id; + int monitorId; + bool alive = false; // window count above 0 (exists in hyprctl workspaces array) + bool presenting = false; // $(hyprctl monitors).activeWorkspace == id + bool focused = false; // $(hyprctl monitors).activeWorkspace == id && (hyprctl monitors).focused + bool urgent = false; // check ctl clients, and find ws with urgent window + std::string label; }; - struct Monitor { - std::map workspaceStates; - std::string name; - int x = 0; - int y = 0; - int id = -1; - int focusedWorkspaceId = -1; + struct Workspace { + WorkspaceState state; + std::shared_ptr view; }; - void start(); - void on_hyprland_event(std::string event, std::string data); - void printMonitor(const Monitor &mon) const; - - sigc::signal socketEventSignal; - sigc::signal workspaceStateChanged; - sigc::signal monitorStateChanged; - - Monitor *getMonitorById(int id); - Monitor *getMonitorByIndex(std::size_t index); - void switchToWorkspace(int workspaceId); - - std::map getAllWorkspaces() const { - return this->workspaces; - } + struct Monitor { + int id; + int activeWorkspaceId; + bool focused = false; + std::string name; + std::map> monitorWorkspaces; + }; static HyprlandService *getInstance() { - if (HyprlandService::instance == nullptr) { - HyprlandService::instance = new HyprlandService(); + if (!instance) { + instance = new HyprlandService(); } - - return HyprlandService::instance; + return instance; } + std::shared_ptr getWorkspaceIndicatorsForMonitor(int monitorId); + private: - const char *kMonitorCommand = "hyprctl monitors -j"; - const char *kWorkspaceCommand = "hyprctl workspaces -j"; - const char *kClientsCommand = "hyprctl clients -j"; - HyprlandService(); - ~HyprlandService(); + std::map> monitors; + std::map> workspaces; - int fd = -1; - std::map monitors; - std::map workspaces; - std::string socket_buffer; + /// maybe refactor into reusable class + std::string socketBuffer; + int socketFd; + /// - std::string get_socket_path(); - bool on_socket_read(Glib::IOCondition condition); - void parse_message(const std::string &line); - void refresh_monitors(); - void refresh_workspaces(); - void onUrgentEvent(std::string windowAddress); - void onActiveWindowEvent(std::string windowAddress); + std::string getSocketPath(); + void bindSocket(); + + void init(); + + void updateIndicator(Workspace &workspace, const WorkspaceState newState); }; diff --git a/include/widgets/workspaceIndicator.hpp b/include/widgets/workspaceIndicator.hpp deleted file mode 100644 index 555d929..0000000 --- a/include/widgets/workspaceIndicator.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "services/hyprland.hpp" -#include "gtkmm/overlay.h" - -class WorkspaceIndicator : public Gtk::Box { - public: - WorkspaceIndicator(int monitorId); - ~WorkspaceIndicator() override; - - private: - HyprlandService *service = HyprlandService::getInstance(); - int monitorId; - sigc::connection workspaceConnection; - sigc::connection monitorConnection; - std::map workspaceIndicators; - std::map> workspaceGestures; - - void rebuild(); - void on_workspace_update(); - void on_monitor_update(); - void refreshLabel(Gtk::Overlay *overlay, const HyprlandService::WorkspaceState &state); -}; diff --git a/src/app.cpp b/src/app.cpp index 0d13ed8..e860987 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -5,10 +5,11 @@ #include 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( - 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(); } diff --git a/src/bar/bar.cpp b/src/bar/bar.cpp index 6cba594..a4027b0 100644 --- a/src/bar/bar.cpp +++ b/src/bar/bar.cpp @@ -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(); - this->workspaceIndicator = Gtk::make_managed(monitorId); - this->trayWidget = Gtk::make_managed(); + this->volumeWidget = std::make_shared(); + this->trayWidget = std::make_shared(); 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() { diff --git a/src/components/workspaceIndicator.cpp b/src/components/workspaceIndicator.cpp new file mode 100644 index 0000000..4b8359b --- /dev/null +++ b/src/components/workspaceIndicator.cpp @@ -0,0 +1,42 @@ +#include "components/workspaceIndicator.hpp" + +#include + +#include "gtkmm/button.h" +#include "gtkmm/label.h" + +WorkspaceIndicator::WorkspaceIndicator(std::string label, sigc::slot onClick) + : Gtk::Box(Gtk::Orientation::HORIZONTAL) { + + auto overlay = Gtk::make_managed(); + auto numLabel = Gtk::make_managed(label); + auto pillContainer = Gtk::make_managed(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(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); + + + +} \ No newline at end of file diff --git a/src/services/hyprland.cpp b/src/services/hyprland.cpp index 2061cc6..89b32a2 100644 --- a/src/services/hyprland.cpp +++ b/src/services/hyprland.cpp @@ -1,64 +1,82 @@ #include "services/hyprland.hpp" -#include #include #include +#include +#include #include -#include +#include #include -#include #include #include #include -#include #include -#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(); + + int monitorId = item["id"].get(); + + monitorPtr->id = monitorId; + monitorPtr->name = item["name"].get(); + 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(std::to_string(i)); + + auto workSpace = std::make_shared(); + 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(); + workspacePtr->state.id = workspace["id"].get(); + workspacePtr->state.monitorId = workspace["monitorID"].get(); + 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(&addr), sizeof(addr)) == -1) { + std::cerr << "[Hyprland] Failed to connect to " << socketPath << std::endl; + close(socketFd); + socketFd = -1; - if (connect(fd, reinterpret_cast(&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(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(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 HyprlandService::getWorkspaceIndicatorsForMonitor(int monitorId) { + auto box = std::make_shared(Gtk::Orientation::HORIZONTAL); - if (static_cast(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(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 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(index)); - - return &it->second; -} + return box; +} \ No newline at end of file diff --git a/src/widgets/workspaceIndicator.cpp b/src/widgets/workspaceIndicator.cpp deleted file mode 100644 index 6142649..0000000 --- a/src/widgets/workspaceIndicator.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "widgets/workspaceIndicator.hpp" - -#include -#include -#include -#include -#include -#include - -#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(); - auto numLabel = Gtk::make_managed(std::to_string(i)); - auto pillContainer = Gtk::make_managed(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(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); - } -} \ No newline at end of file