base function of indicators work again
This commit is contained in:
@@ -27,7 +27,6 @@ target_sources(bar_lib
|
|||||||
|
|
||||||
src/widgets/clock.cpp
|
src/widgets/clock.cpp
|
||||||
src/widgets/date.cpp
|
src/widgets/date.cpp
|
||||||
src/widgets/workspaceIndicator.cpp
|
|
||||||
src/widgets/volumeWidget.cpp
|
src/widgets/volumeWidget.cpp
|
||||||
src/widgets/webWidget.cpp
|
src/widgets/webWidget.cpp
|
||||||
|
|
||||||
@@ -41,6 +40,7 @@ target_sources(bar_lib
|
|||||||
src/widgets/controlCenter.cpp
|
src/widgets/controlCenter.cpp
|
||||||
|
|
||||||
src/components/popover.cpp
|
src/components/popover.cpp
|
||||||
|
src/components/workspaceIndicator.cpp
|
||||||
src/components/base/button.cpp
|
src/components/base/button.cpp
|
||||||
)
|
)
|
||||||
include_directories(bar_lib PRIVATE
|
include_directories(bar_lib PRIVATE
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class App {
|
|||||||
private:
|
private:
|
||||||
Glib::RefPtr<Gtk::Application> app;
|
Glib::RefPtr<Gtk::Application> app;
|
||||||
std::vector<Bar *> bars;
|
std::vector<Bar *> bars;
|
||||||
HyprlandService *hyprlandService = HyprlandService::getInstance();
|
HyprlandService *hyprlandService = nullptr;
|
||||||
NotificationService notificationService;
|
NotificationService notificationService;
|
||||||
TrayService *trayService = TrayService::getInstance();
|
TrayService *trayService = TrayService::getInstance();
|
||||||
|
|
||||||
|
|||||||
@@ -2,22 +2,24 @@
|
|||||||
|
|
||||||
#include <gtk4-layer-shell/gtk4-layer-shell.h>
|
#include <gtk4-layer-shell/gtk4-layer-shell.h>
|
||||||
#include <gtkmm.h>
|
#include <gtkmm.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "widgets/clock.hpp"
|
#include "widgets/clock.hpp"
|
||||||
#include "widgets/date.hpp"
|
#include "widgets/date.hpp"
|
||||||
#include "widgets/tray.hpp"
|
#include "widgets/tray.hpp"
|
||||||
#include "widgets/volumeWidget.hpp"
|
#include "widgets/volumeWidget.hpp"
|
||||||
#include "widgets/webWidget.hpp"
|
#include "widgets/webWidget.hpp"
|
||||||
#include "widgets/workspaceIndicator.hpp"
|
|
||||||
#include "widgets/controlCenter.hpp"
|
#include "widgets/controlCenter.hpp"
|
||||||
|
|
||||||
class Bar : public Gtk::Window {
|
class Bar : public Gtk::Window {
|
||||||
public:
|
public:
|
||||||
Bar(GdkMonitor *monitor, int monitorId);
|
Bar(GdkMonitor *monitor);
|
||||||
|
|
||||||
|
void addLeftWidget(std::shared_ptr<Gtk::Widget> widget) { left_box.append(*widget); }
|
||||||
|
void addCenterWidget(std::shared_ptr<Gtk::Widget> widget) { center_box.append(*widget); }
|
||||||
|
void addRightWidget(std::shared_ptr<Gtk::Widget> widget) { right_box.append(*widget); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int monitorId;
|
|
||||||
|
|
||||||
Gtk::CenterBox main_box{};
|
Gtk::CenterBox main_box{};
|
||||||
Gtk::Box left_box{Gtk::Orientation::HORIZONTAL};
|
Gtk::Box left_box{Gtk::Orientation::HORIZONTAL};
|
||||||
Gtk::Box center_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"};
|
WebWidget homeAssistant{"\ue88a", "Home Assistant", "https://home.rivercry.com"};
|
||||||
ControlCenter controlCenter{"\ue88a", "Control Center"};
|
ControlCenter controlCenter{"\ue88a", "Control Center"};
|
||||||
|
|
||||||
WorkspaceIndicator *workspaceIndicator = nullptr;
|
std::shared_ptr<TrayWidget> trayWidget = nullptr;
|
||||||
TrayWidget *trayWidget = nullptr;
|
std::shared_ptr<VolumeWidget> volumeWidget = nullptr;
|
||||||
VolumeWidget *volumeWidget = nullptr;
|
|
||||||
|
|
||||||
|
|
||||||
void setup_ui();
|
void setup_ui();
|
||||||
|
|||||||
14
include/components/workspaceIndicator.hpp
Normal file
14
include/components/workspaceIndicator.hpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "gtkmm/box.h"
|
||||||
|
#include "gtkmm/button.h"
|
||||||
|
#include "gtkmm/overlay.h"
|
||||||
|
|
||||||
|
class WorkspaceIndicator : public Gtk::Box {
|
||||||
|
public:
|
||||||
|
WorkspaceIndicator(std::string label, sigc::slot<void()> onClick = {});
|
||||||
|
|
||||||
|
private:
|
||||||
|
Gtk::Button button;
|
||||||
|
};
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "helper/string.hpp"
|
#include "helpers/string.hpp"
|
||||||
|
|
||||||
class SocketHelper {
|
class SocketHelper {
|
||||||
typedef struct SocketMessage {
|
typedef struct SocketMessage {
|
||||||
@@ -18,6 +18,7 @@ class SocketHelper {
|
|||||||
static std::vector<SocketMessage> parseSocketMessage(int socketFd, const std::string &delimiter) {
|
static std::vector<SocketMessage> parseSocketMessage(int socketFd, const std::string &delimiter) {
|
||||||
char buffer[4096];
|
char buffer[4096];
|
||||||
std::string data;
|
std::string data;
|
||||||
|
|
||||||
ssize_t bytesRead = recv(socketFd, buffer, sizeof(buffer) - 1, 0);
|
ssize_t bytesRead = recv(socketFd, buffer, sizeof(buffer) - 1, 0);
|
||||||
if (bytesRead > 0) {
|
if (bytesRead > 0) {
|
||||||
buffer[bytesRead] = '\0';
|
buffer[bytesRead] = '\0';
|
||||||
@@ -34,17 +35,15 @@ class SocketHelper {
|
|||||||
assert(false && "Delimiter not found in socket message");
|
assert(false && "Delimiter not found in socket message");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto splitMessages = StringHelper::split(data, '\n');
|
auto splitMessages = StringHelper::split(data, '\n');
|
||||||
auto splitMessagesFinal = std::vector<SocketMessage>();
|
auto splitMessagesFinal = std::vector<SocketMessage>();
|
||||||
for (auto splitMessage : splitMessages) {
|
for (auto splitMessage : splitMessages) {
|
||||||
|
SocketMessage message;
|
||||||
|
auto messageCommandVector = StringHelper::split(splitMessage, delimiter);
|
||||||
|
|
||||||
|
message.eventType = messageCommandVector[0];
|
||||||
SocketMessage message;
|
message.eventData = messageCommandVector.size() > 1 ? messageCommandVector[1] : "";
|
||||||
auto messageCommandVector = StringHelper::split(splitMessage, ">>");
|
splitMessagesFinal.push_back(message);
|
||||||
|
|
||||||
message.eventType = messageCommandVector[0];
|
|
||||||
message.eventData = messageCommandVector.size() > 1 ? messageCommandVector[1] : "";
|
|
||||||
splitMessagesFinal.push_back(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return splitMessagesFinal;
|
return splitMessagesFinal;
|
||||||
|
|||||||
@@ -1,79 +1,70 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <glibmm.h>
|
#include <glibmm.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <sigc++/sigc++.h>
|
#include <sigc++/sigc++.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "components/workspaceIndicator.hpp"
|
||||||
|
|
||||||
|
#include "gtkmm/box.h"
|
||||||
|
|
||||||
|
#define NUM_WORKSPACES 7
|
||||||
|
|
||||||
class HyprlandService {
|
class HyprlandService {
|
||||||
inline static HyprlandService *instance = nullptr;
|
inline static HyprlandService *instance = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr int kWorkspaceSlotCount = 7;
|
|
||||||
|
|
||||||
struct WorkspaceState {
|
struct WorkspaceState {
|
||||||
int hyprId = -1;
|
int id;
|
||||||
int monitorId = -1;
|
int monitorId;
|
||||||
bool active = false;
|
bool alive = false; // window count above 0 (exists in hyprctl workspaces array)
|
||||||
bool focused = false;
|
bool presenting = false; // $(hyprctl monitors).activeWorkspace == id
|
||||||
std::vector<std::string> urgentWindows;
|
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;
|
std::string label;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Monitor {
|
struct Workspace {
|
||||||
std::map<int, WorkspaceState *> workspaceStates;
|
WorkspaceState state;
|
||||||
std::string name;
|
std::shared_ptr<WorkspaceIndicator> view;
|
||||||
int x = 0;
|
|
||||||
int y = 0;
|
|
||||||
int id = -1;
|
|
||||||
int focusedWorkspaceId = -1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void start();
|
struct Monitor {
|
||||||
void on_hyprland_event(std::string event, std::string data);
|
int id;
|
||||||
void printMonitor(const Monitor &mon) const;
|
int activeWorkspaceId;
|
||||||
|
bool focused = false;
|
||||||
sigc::signal<void(std::string, std::string)> socketEventSignal;
|
std::string name;
|
||||||
sigc::signal<void()> workspaceStateChanged;
|
std::map<int, std::shared_ptr<Workspace>> monitorWorkspaces;
|
||||||
sigc::signal<void()> monitorStateChanged;
|
};
|
||||||
|
|
||||||
Monitor *getMonitorById(int id);
|
|
||||||
Monitor *getMonitorByIndex(std::size_t index);
|
|
||||||
void switchToWorkspace(int workspaceId);
|
|
||||||
|
|
||||||
std::map<int, WorkspaceState *> getAllWorkspaces() const {
|
|
||||||
return this->workspaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HyprlandService *getInstance() {
|
static HyprlandService *getInstance() {
|
||||||
if (HyprlandService::instance == nullptr) {
|
if (!instance) {
|
||||||
HyprlandService::instance = new HyprlandService();
|
instance = new HyprlandService();
|
||||||
}
|
}
|
||||||
|
return instance;
|
||||||
return HyprlandService::instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Gtk::Box> getWorkspaceIndicatorsForMonitor(int monitorId);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char *kMonitorCommand = "hyprctl monitors -j";
|
|
||||||
const char *kWorkspaceCommand = "hyprctl workspaces -j";
|
|
||||||
const char *kClientsCommand = "hyprctl clients -j";
|
|
||||||
|
|
||||||
HyprlandService();
|
HyprlandService();
|
||||||
~HyprlandService();
|
std::map<int, std::shared_ptr<Monitor>> monitors;
|
||||||
|
std::map<int, std::shared_ptr<Workspace>> workspaces;
|
||||||
|
|
||||||
int fd = -1;
|
/// maybe refactor into reusable class
|
||||||
std::map<int, Monitor> monitors;
|
std::string socketBuffer;
|
||||||
std::map<int, WorkspaceState *> workspaces;
|
int socketFd;
|
||||||
std::string socket_buffer;
|
///
|
||||||
|
|
||||||
std::string get_socket_path();
|
std::string getSocketPath();
|
||||||
bool on_socket_read(Glib::IOCondition condition);
|
void bindSocket();
|
||||||
void parse_message(const std::string &line);
|
|
||||||
void refresh_monitors();
|
void init();
|
||||||
void refresh_workspaces();
|
|
||||||
void onUrgentEvent(std::string windowAddress);
|
void updateIndicator(Workspace &workspace, const WorkspaceState newState);
|
||||||
void onActiveWindowEvent(std::string windowAddress);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <gtkmm/box.h>
|
|
||||||
#include <gtkmm/gestureclick.h>
|
|
||||||
#include <gtkmm/label.h>
|
|
||||||
#include <sigc++/connection.h>
|
|
||||||
|
|
||||||
#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<int, Gtk::Overlay *> workspaceIndicators;
|
|
||||||
std::map<int, Glib::RefPtr<Gtk::GestureClick>> workspaceGestures;
|
|
||||||
|
|
||||||
void rebuild();
|
|
||||||
void on_workspace_update();
|
|
||||||
void on_monitor_update();
|
|
||||||
void refreshLabel(Gtk::Overlay *overlay, const HyprlandService::WorkspaceState &state);
|
|
||||||
};
|
|
||||||
27
src/app.cpp
27
src/app.cpp
@@ -5,10 +5,11 @@
|
|||||||
#include <sigc++/sigc++.h>
|
#include <sigc++/sigc++.h>
|
||||||
|
|
||||||
App::App() {
|
App::App() {
|
||||||
|
this->app = Gtk::Application::create("org.example.mybar");
|
||||||
|
|
||||||
this->setupServices();
|
this->setupServices();
|
||||||
this->notificationService.intialize();
|
this->notificationService.intialize();
|
||||||
|
this->hyprlandService = HyprlandService::getInstance();
|
||||||
this->app = Gtk::Application::create("org.example.mybar");
|
|
||||||
|
|
||||||
app->signal_activate().connect([&]() {
|
app->signal_activate().connect([&]() {
|
||||||
auto display = Gdk::Display::get_default();
|
auto display = Gdk::Display::get_default();
|
||||||
@@ -16,23 +17,15 @@ App::App() {
|
|||||||
|
|
||||||
for (guint i = 0; i < monitors->get_n_items(); ++i) {
|
for (guint i = 0; i < monitors->get_n_items(); ++i) {
|
||||||
auto monitor = std::dynamic_pointer_cast<Gdk::Monitor>(
|
auto monitor = std::dynamic_pointer_cast<Gdk::Monitor>(
|
||||||
monitors->get_object(i));
|
monitors->get_object(i)
|
||||||
|
);
|
||||||
|
|
||||||
if (monitor) {
|
if (monitor) {
|
||||||
HyprlandService::Monitor *hyprlandMonitor = nullptr;
|
auto bar = new Bar(monitor->gobj());
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
bar->set_application(app);
|
bar->set_application(app);
|
||||||
bar->show();
|
bar->show();
|
||||||
|
bar->addLeftWidget(hyprlandService->getWorkspaceIndicatorsForMonitor(i));
|
||||||
bars.push_back(bar);
|
bars.push_back(bar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,10 +43,6 @@ App::App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void App::setupServices() {
|
void App::setupServices() {
|
||||||
this->hyprlandService->socketEventSignal.connect(sigc::mem_fun(
|
|
||||||
*this->hyprlandService, &HyprlandService::on_hyprland_event));
|
|
||||||
|
|
||||||
this->hyprlandService->start();
|
|
||||||
this->trayService->start();
|
this->trayService->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,14 +9,12 @@
|
|||||||
#include "widgets/date.hpp"
|
#include "widgets/date.hpp"
|
||||||
#include "widgets/spacer.hpp"
|
#include "widgets/spacer.hpp"
|
||||||
#include "widgets/volumeWidget.hpp"
|
#include "widgets/volumeWidget.hpp"
|
||||||
#include "widgets/workspaceIndicator.hpp"
|
|
||||||
|
|
||||||
#include "glibmm/main.h"
|
#include "glibmm/main.h"
|
||||||
#include "gtk/gtk.h"
|
#include "gtk/gtk.h"
|
||||||
#include "sigc++/functors/mem_fun.h"
|
#include "sigc++/functors/mem_fun.h"
|
||||||
|
|
||||||
Bar::Bar(GdkMonitor *monitor, int monitorId)
|
Bar::Bar(GdkMonitor *monitor) {
|
||||||
: monitorId(monitorId) {
|
|
||||||
set_name("bar-window");
|
set_name("bar-window");
|
||||||
|
|
||||||
gtk_layer_init_for_window(this->gobj());
|
gtk_layer_init_for_window(this->gobj());
|
||||||
@@ -34,9 +32,8 @@ Bar::Bar(GdkMonitor *monitor, int monitorId)
|
|||||||
|
|
||||||
set_child(main_box);
|
set_child(main_box);
|
||||||
|
|
||||||
this->volumeWidget = Gtk::make_managed<VolumeWidget>();
|
this->volumeWidget = std::make_shared<VolumeWidget>();
|
||||||
this->workspaceIndicator = Gtk::make_managed<WorkspaceIndicator>(monitorId);
|
this->trayWidget = std::make_shared<TrayWidget>();
|
||||||
this->trayWidget = Gtk::make_managed<TrayWidget>();
|
|
||||||
|
|
||||||
load_css();
|
load_css();
|
||||||
setup_ui();
|
setup_ui();
|
||||||
@@ -59,7 +56,7 @@ void Bar::setup_ui() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Bar::setup_left_box() {
|
void Bar::setup_left_box() {
|
||||||
left_box.append(*workspaceIndicator);
|
// left_box.append(*workspaceIndicator);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bar::setup_center_box() {
|
void Bar::setup_center_box() {
|
||||||
|
|||||||
42
src/components/workspaceIndicator.cpp
Normal file
42
src/components/workspaceIndicator.cpp
Normal 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);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,64 +1,82 @@
|
|||||||
#include "services/hyprland.hpp"
|
#include "services/hyprland.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <glib-unix.h>
|
||||||
|
#include <glib.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iterator>
|
#include <memory>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <unordered_set>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "helpers/command.hpp"
|
|
||||||
#include "helpers/hypr.hpp"
|
#include "helpers/hypr.hpp"
|
||||||
|
#include "helpers/socket.hpp"
|
||||||
|
|
||||||
HyprlandService::HyprlandService() = default;
|
#include "gtkmm/box.h"
|
||||||
|
#include "gtkmm/button.h"
|
||||||
|
|
||||||
HyprlandService::~HyprlandService() {
|
HyprlandService::HyprlandService() {
|
||||||
if (fd != -1) {
|
auto monitorDataJson = HyprctlHelper::getMonitorData();
|
||||||
close(fd);
|
|
||||||
fd = -1;
|
|
||||||
|
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) {
|
auto workspaceDataJson = HyprctlHelper::getWorkspaceData();
|
||||||
delete p.second;
|
|
||||||
|
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();
|
bindSocket();
|
||||||
this->monitors.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprlandService::on_hyprland_event(std::string event, std::string data) {
|
void HyprlandService::init() {
|
||||||
|
|
||||||
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::start() {
|
std::string HyprlandService::getSocketPath() {
|
||||||
const std::string socket_path = get_socket_path();
|
auto sig = std::string(std::getenv("HYPRLAND_INSTANCE_SIGNATURE"));
|
||||||
if (socket_path.empty()) {
|
auto runtime = std::string(std::getenv("XDG_RUNTIME_DIR"));
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
return std::string(runtime) + "/hypr/" + sig + "/.socket2.sock";
|
||||||
if (fd == -1) {
|
}
|
||||||
|
|
||||||
|
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;
|
std::cerr << "[Hyprland] Failed to create socket" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -66,271 +84,41 @@ void HyprlandService::start() {
|
|||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
addr.sun_family = AF_UNIX;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Glib::signal_io().connect(
|
GSource *source = g_unix_fd_source_new(socketFd, static_cast<GIOCondition>(G_IO_IN | G_IO_HUP | G_IO_ERR));
|
||||||
sigc::mem_fun(*this, &HyprlandService::on_socket_read), fd,
|
|
||||||
Glib::IOCondition::IO_IN | Glib::IOCondition::IO_HUP |
|
|
||||||
Glib::IOCondition::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) {
|
std::shared_ptr<Gtk::Box> HyprlandService::getWorkspaceIndicatorsForMonitor(int monitorId) {
|
||||||
const auto error_mask = Glib::IOCondition::IO_HUP | Glib::IOCondition::IO_ERR;
|
auto box = std::make_shared<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
|
||||||
|
|
||||||
if (static_cast<int>(condition & error_mask) != 0) {
|
auto monitor = monitors[monitorId];
|
||||||
std::cerr << "[Hyprland] Socket disconnected." << std::endl;
|
|
||||||
if (fd != -1) {
|
int i = 0;
|
||||||
close(fd);
|
for (const auto &[wsId, wsPair] : monitor->monitorWorkspaces) {
|
||||||
fd = -1;
|
box->append((Gtk::Box &)*wsPair->view);
|
||||||
}
|
i++;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char temp_buffer[4096];
|
|
||||||
const ssize_t bytes_read = read(fd, temp_buffer, sizeof(temp_buffer));
|
|
||||||
|
|
||||||
if (bytes_read <= 0) {
|
return box;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user