deine cousine
This commit is contained in:
@@ -59,6 +59,8 @@ class HyprlandService {
|
|||||||
int fd = -1;
|
int fd = -1;
|
||||||
std::map<int, Monitor> monitors;
|
std::map<int, Monitor> monitors;
|
||||||
std::map<int, WorkspaceState *> workspaces;
|
std::map<int, WorkspaceState *> workspaces;
|
||||||
|
// persistent buffer for socket reads to handle partial messages
|
||||||
|
std::string socket_buffer;
|
||||||
|
|
||||||
std::string get_socket_path();
|
std::string get_socket_path();
|
||||||
bool on_socket_read(Glib::IOCondition condition);
|
bool on_socket_read(Glib::IOCondition condition);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <gtkmm/box.h>
|
#include <gtkmm/box.h>
|
||||||
#include <gtkmm/label.h>
|
#include <gtkmm/label.h>
|
||||||
|
#include <gtkmm/gestureclick.h>
|
||||||
#include <sigc++/connection.h>
|
#include <sigc++/connection.h>
|
||||||
|
|
||||||
#include "services/hyprland.hpp"
|
#include "services/hyprland.hpp"
|
||||||
@@ -17,9 +18,10 @@ class WorkspaceIndicator : public Gtk::Box {
|
|||||||
sigc::connection workspaceConnection;
|
sigc::connection workspaceConnection;
|
||||||
sigc::connection monitorConnection;
|
sigc::connection monitorConnection;
|
||||||
std::map<int, Gtk::Label *> workspaceLabels;
|
std::map<int, Gtk::Label *> workspaceLabels;
|
||||||
|
std::map<int, Glib::RefPtr<Gtk::GestureClick>> workspaceGestures;
|
||||||
|
|
||||||
void rebuild();
|
void rebuild();
|
||||||
void on_workspace_update();
|
void on_workspace_update();
|
||||||
void on_monitor_update();
|
void on_monitor_update();
|
||||||
void refreshLabel(Gtk::Label *label, HyprlandService::WorkspaceState state);
|
void refreshLabel(Gtk::Label *label, const HyprlandService::WorkspaceState &state);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,7 +30,11 @@ window {
|
|||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
color: #1e1e1e;
|
color: #1e1e1e;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-bottom: #89b4fa 2px;
|
box-shadow: 0 0 6px rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-pill-focused:hover {
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-pill-active {
|
.workspace-pill-active {
|
||||||
@@ -40,6 +44,24 @@ window {
|
|||||||
.workspace-pill-urgent {
|
.workspace-pill-urgent {
|
||||||
background-color: #ff5555;
|
background-color: #ff5555;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
|
/* base glow (will be animated) */
|
||||||
|
animation: workspace-blink 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* blinking animation for urgent workspaces */
|
||||||
|
@keyframes workspace-blink {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-pill:last-child {
|
.workspace-pill:last-child {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ Bar::Bar(GdkMonitor *monitor, HyprlandService &hyprlandService,
|
|||||||
gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, true);
|
gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, true);
|
||||||
gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_LEFT, true);
|
gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_LEFT, true);
|
||||||
gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, true);
|
gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, true);
|
||||||
|
gtk_layer_set_layer(this->gobj(), GTK_LAYER_SHELL_LAYER_TOP);
|
||||||
|
|
||||||
gtk_layer_auto_exclusive_zone_enable(this->gobj());
|
gtk_layer_auto_exclusive_zone_enable(this->gobj());
|
||||||
|
|
||||||
@@ -62,15 +63,14 @@ void Bar::setup_ui() {
|
|||||||
|
|
||||||
left_box.set_valign(Gtk::Align::CENTER);
|
left_box.set_valign(Gtk::Align::CENTER);
|
||||||
|
|
||||||
// Don't expand the center box — keep it centered by alignment
|
|
||||||
center_box.set_hexpand(false);
|
center_box.set_hexpand(false);
|
||||||
center_box.set_valign(Gtk::Align::CENTER);
|
center_box.set_valign(Gtk::Align::CENTER);
|
||||||
center_box.set_halign(Gtk::Align::CENTER);
|
center_box.set_halign(Gtk::Align::CENTER);
|
||||||
|
|
||||||
right_box.set_valign(Gtk::Align::CENTER);
|
right_box.set_valign(Gtk::Align::CENTER);
|
||||||
|
|
||||||
workspaceIndicator =
|
workspaceIndicator = Gtk::make_managed<WorkspaceIndicator>(hyprlandService, monitorId);
|
||||||
Gtk::make_managed<WorkspaceIndicator>(hyprlandService, monitorId);
|
|
||||||
left_box.append(*workspaceIndicator);
|
left_box.append(*workspaceIndicator);
|
||||||
|
|
||||||
clock.set_name("clock-label");
|
clock.set_name("clock-label");
|
||||||
@@ -82,7 +82,7 @@ void Bar::setup_ui() {
|
|||||||
|
|
||||||
trayWidget = Gtk::make_managed<TrayWidget>(trayService);
|
trayWidget = Gtk::make_managed<TrayWidget>(trayService);
|
||||||
right_box.append(*trayWidget);
|
right_box.append(*trayWidget);
|
||||||
// right_box.append(homeAssistant);
|
right_box.append(homeAssistant);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bar::load_css() {
|
void Bar::load_css() {
|
||||||
|
|||||||
@@ -21,17 +21,25 @@ HyprlandService::~HyprlandService() {
|
|||||||
close(fd);
|
close(fd);
|
||||||
fd = -1;
|
fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// free allocated workspace pointers
|
||||||
|
for (auto &p : this->workspaces) {
|
||||||
|
delete p.second;
|
||||||
|
}
|
||||||
|
this->workspaces.clear();
|
||||||
|
this->monitors.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprlandService::on_hyprland_event(std::string event, std::string data) {
|
void HyprlandService::on_hyprland_event(std::string event, std::string data) {
|
||||||
if (event == "monitoradded" || event == "monitorremoved") {
|
|
||||||
refresh_monitors();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event == "urgent") {
|
if (event == "urgent") {
|
||||||
onUrgentEvent(data);
|
onUrgentEvent(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event == "activewindowv2") {
|
||||||
|
onActiveWindowEvent(data);
|
||||||
|
}
|
||||||
|
|
||||||
if (event == "workspace" || event == "movewindow") {
|
if (event == "workspace" || event == "movewindow") {
|
||||||
refresh_workspaces();
|
refresh_workspaces();
|
||||||
}
|
}
|
||||||
@@ -39,8 +47,8 @@ void HyprlandService::on_hyprland_event(std::string event, std::string data) {
|
|||||||
// use for
|
// use for
|
||||||
// event == "focusedmon"
|
// event == "focusedmon"
|
||||||
|
|
||||||
if (event == "activewindowv2") {
|
if (event == "monitoradded" || event == "monitorremoved") {
|
||||||
onActiveWindowEvent(data);
|
refresh_monitors();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,31 +89,38 @@ void HyprlandService::start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool HyprlandService::on_socket_read(Glib::IOCondition condition) {
|
bool HyprlandService::on_socket_read(Glib::IOCondition condition) {
|
||||||
const auto error_mask =
|
const auto error_mask = Glib::IOCondition::IO_HUP | Glib::IOCondition::IO_ERR;
|
||||||
Glib::IOCondition::IO_HUP | Glib::IOCondition::IO_ERR;
|
|
||||||
|
|
||||||
if (static_cast<int>(condition & error_mask) != 0) {
|
if (static_cast<int>(condition & error_mask) != 0) {
|
||||||
std::cerr << "[Hyprland] Socket disconnected." << std::endl;
|
std::cerr << "[Hyprland] Socket disconnected." << std::endl;
|
||||||
close(fd);
|
if (fd != -1) {
|
||||||
fd = -1;
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string buffer;
|
|
||||||
char temp_buffer[4096];
|
char temp_buffer[4096];
|
||||||
const ssize_t bytes_read = read(fd, temp_buffer, sizeof(temp_buffer) - 1);
|
const ssize_t bytes_read = read(fd, temp_buffer, sizeof(temp_buffer));
|
||||||
|
|
||||||
if (bytes_read > 0) {
|
if (bytes_read <= 0) {
|
||||||
temp_buffer[bytes_read] = '\0';
|
// peer closed or error
|
||||||
buffer.append(temp_buffer);
|
std::cerr << "[Hyprland] Socket read returned " << bytes_read << std::endl;
|
||||||
|
if (fd != -1) {
|
||||||
size_t pos = 0;
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
while ((pos = buffer.find('\n')) != std::string::npos) {
|
|
||||||
const std::string line = buffer.substr(0, pos);
|
|
||||||
parse_message(line);
|
|
||||||
buffer.erase(0, pos + 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;
|
return true;
|
||||||
@@ -134,8 +149,12 @@ std::string HyprlandService::get_socket_path() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HyprlandService::refresh_monitors() {
|
void HyprlandService::refresh_monitors() {
|
||||||
this->monitors.clear();
|
// free any previously allocated WorkspaceState objects before rebuilding
|
||||||
|
for (auto &p : this->workspaces) {
|
||||||
|
delete p.second;
|
||||||
|
}
|
||||||
this->workspaces.clear();
|
this->workspaces.clear();
|
||||||
|
this->monitors.clear();
|
||||||
|
|
||||||
std::string output = SystemHelper::get_command_output(kMonitorCommand);
|
std::string output = SystemHelper::get_command_output(kMonitorCommand);
|
||||||
auto monitorsJson = nlohmann::json::parse(output, nullptr, false);
|
auto monitorsJson = nlohmann::json::parse(output, nullptr, false);
|
||||||
@@ -160,10 +179,12 @@ void HyprlandService::refresh_monitors() {
|
|||||||
wsState.label = std::to_string(slot);
|
wsState.label = std::to_string(slot);
|
||||||
wsState.monitorId = monitor.id;
|
wsState.monitorId = monitor.id;
|
||||||
|
|
||||||
int id = slot + monitor.id * HyprlandService::kWorkspaceSlotCount;
|
int id = slot + monitor.id * HyprlandService::kWorkspaceSlotCount;
|
||||||
wsState.hyprId = id;
|
wsState.hyprId = id;
|
||||||
this->workspaces[id] = new WorkspaceState(wsState);
|
this->workspaces[id] = new WorkspaceState(wsState);
|
||||||
this->monitors[monitor.id].workspaceStates[slot] = this->workspaces[id];
|
if (monitor.id >= 0) {
|
||||||
|
this->monitors[monitor.id].workspaceStates[slot] = this->workspaces[id];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,17 +212,28 @@ void HyprlandService::refresh_workspaces() {
|
|||||||
const int monitorId = monitorJson.value("id", -1);
|
const int monitorId = monitorJson.value("id", -1);
|
||||||
const int focusedWorkspaceId = monitorJson["activeWorkspace"].value("id", -1);
|
const int focusedWorkspaceId = monitorJson["activeWorkspace"].value("id", -1);
|
||||||
|
|
||||||
auto monitor = this->monitors[monitorId];
|
// write into the stored monitor (use reference)
|
||||||
monitor.focusedWorkspaceId = focusedWorkspaceId;
|
auto it = this->monitors.find(monitorId);
|
||||||
|
if (it != this->monitors.end()) {
|
||||||
|
it->second.focusedWorkspaceId = focusedWorkspaceId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &workspaceJson : workspacesJson) {
|
for (const auto &workspaceJson : workspacesJson) {
|
||||||
const int workspaceId = workspaceJson.value("id", -1);
|
const int workspaceId = workspaceJson.value("id", -1);
|
||||||
std::map<int, WorkspaceState *>::iterator workspaceStateIt = this->workspaces.find(workspaceId);
|
auto workspaceStateIt = this->workspaces.find(workspaceId);
|
||||||
WorkspaceState *workspaceState = workspaceStateIt->second;
|
if (workspaceStateIt == this->workspaces.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
workspaceState->focused = monitors[workspaceState->monitorId].focusedWorkspaceId == workspaceId;
|
WorkspaceState *workspaceState = workspaceStateIt->second;
|
||||||
workspaceState->active = true;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
workspaceStateChanged.emit();
|
workspaceStateChanged.emit();
|
||||||
@@ -229,12 +261,16 @@ void HyprlandService::onUrgentEvent(std::string windowAddress) {
|
|||||||
if (addr == "0x" + windowAddress) {
|
if (addr == "0x" + windowAddress) {
|
||||||
int workspaceId = clientJson["workspace"].value("id", -1);
|
int workspaceId = clientJson["workspace"].value("id", -1);
|
||||||
|
|
||||||
WorkspaceState *ws = this->workspaces[workspaceId];
|
auto it = this->workspaces.find(workspaceId);
|
||||||
|
if (it != this->workspaces.end() && it->second) {
|
||||||
|
|
||||||
|
if (std::find(it->second->urgentWindows.begin(),
|
||||||
|
it->second->urgentWindows.end(), windowAddress) ==
|
||||||
|
it->second->urgentWindows.end()) {
|
||||||
|
it->second->urgentWindows.push_back(windowAddress);
|
||||||
|
workspaceStateChanged.emit();
|
||||||
|
}
|
||||||
|
|
||||||
// todo: maybee better method of access
|
|
||||||
if (ws) {
|
|
||||||
ws->urgentWindows.push_back(windowAddress);
|
|
||||||
workspaceStateChanged.emit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -250,13 +286,13 @@ void HyprlandService::onActiveWindowEvent(std::string windowAddress) {
|
|||||||
const std::string addr = clientJson.value("address", "");
|
const std::string addr = clientJson.value("address", "");
|
||||||
|
|
||||||
if (addr == "0x" + windowAddress) {
|
if (addr == "0x" + windowAddress) {
|
||||||
int workspaceId = clientJson["workspace"]["id"];
|
int workspaceId = clientJson["workspace"]["id"];
|
||||||
WorkspaceState *ws = this->workspaces[workspaceId];
|
auto it = this->workspaces.find(workspaceId);
|
||||||
|
if (it != this->workspaces.end() && it->second) {
|
||||||
if (ws) {
|
WorkspaceState *ws = it->second;
|
||||||
auto it = std::find(ws->urgentWindows.begin(), ws->urgentWindows.end(), windowAddress);
|
auto uit = std::find(ws->urgentWindows.begin(), ws->urgentWindows.end(), windowAddress);
|
||||||
if (it != ws->urgentWindows.end()) {
|
if (uit != ws->urgentWindows.end()) {
|
||||||
ws->urgentWindows.erase(it);
|
ws->urgentWindows.erase(uit);
|
||||||
workspaceStateChanged.emit();
|
workspaceStateChanged.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,14 +22,16 @@ WorkspaceIndicator::WorkspaceIndicator(HyprlandService &service, int monitorId)
|
|||||||
auto label = Gtk::make_managed<Gtk::Label>(std::to_string(i));
|
auto label = Gtk::make_managed<Gtk::Label>(std::to_string(i));
|
||||||
label->add_css_class("workspace-pill");
|
label->add_css_class("workspace-pill");
|
||||||
|
|
||||||
|
// create and store a gesture controller once to avoid duplicate handlers
|
||||||
auto gesture = Gtk::GestureClick::create();
|
auto gesture = Gtk::GestureClick::create();
|
||||||
gesture->set_button(GDK_BUTTON_PRIMARY);
|
gesture->set_button(GDK_BUTTON_PRIMARY);
|
||||||
gesture->signal_released().connect(
|
gesture->signal_released().connect(
|
||||||
[this, i, &service](int, double, double) {
|
[this, i](int, double, double) {
|
||||||
service.switchToWorkspace(
|
this->service.switchToWorkspace(
|
||||||
i + this->monitorId * HyprlandService::kWorkspaceSlotCount);
|
i + this->monitorId * HyprlandService::kWorkspaceSlotCount);
|
||||||
});
|
});
|
||||||
label->add_controller(gesture);
|
label->add_controller(gesture);
|
||||||
|
workspaceGestures[i] = gesture;
|
||||||
|
|
||||||
workspaceLabels[i] = label;
|
workspaceLabels[i] = label;
|
||||||
append(*label);
|
append(*label);
|
||||||
@@ -54,19 +56,12 @@ void WorkspaceIndicator::on_workspace_update() {
|
|||||||
|
|
||||||
void WorkspaceIndicator::on_monitor_update() { rebuild(); }
|
void WorkspaceIndicator::on_monitor_update() { rebuild(); }
|
||||||
|
|
||||||
void WorkspaceIndicator::refreshLabel(Gtk::Label *label, HyprlandService::WorkspaceState state) {
|
void WorkspaceIndicator::refreshLabel(Gtk::Label *label, const HyprlandService::WorkspaceState &state) {
|
||||||
label->remove_css_class("workspace-pill-active");
|
label->remove_css_class("workspace-pill-active");
|
||||||
label->remove_css_class("workspace-pill-focused");
|
label->remove_css_class("workspace-pill-focused");
|
||||||
label->remove_css_class("workspace-pill-urgent");
|
label->remove_css_class("workspace-pill-urgent");
|
||||||
|
|
||||||
auto gesture = Gtk::GestureClick::create();
|
// controller created once in constructor and reused
|
||||||
gesture->set_button(GDK_BUTTON_PRIMARY);
|
|
||||||
gesture->signal_released().connect(
|
|
||||||
[this, state](int, double, double) {
|
|
||||||
service.switchToWorkspace(state.hyprId);
|
|
||||||
});
|
|
||||||
label->add_controller(gesture);
|
|
||||||
|
|
||||||
if (state.urgentWindows.size() > 0) {
|
if (state.urgentWindows.size() > 0) {
|
||||||
label->add_css_class("workspace-pill-urgent");
|
label->add_css_class("workspace-pill-urgent");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user