quick commit

This commit is contained in:
2026-01-31 11:46:12 +01:00
parent 0e613141da
commit ad5e678c5d
25 changed files with 287 additions and 622 deletions

View File

@@ -3,7 +3,6 @@
#include <gtk4-layer-shell/gtk4-layer-shell.h>
#include <gtkmm.h>
#include "icons.hpp"
#include "widgets/clock.hpp"
#include "widgets/date.hpp"
#include "widgets/tray.hpp"
@@ -26,8 +25,8 @@ class Bar : public Gtk::Window {
Clock clock;
Date date;
WebWidget homeAssistant{ICON_HOME, "Home Assistant", "https://home.rivercry.com"};
ControlCenter controlCenter{"\ue8bb", "Control Center"};
WebWidget homeAssistant{"\ue88a", "Home Assistant", "https://home.rivercry.com"};
ControlCenter controlCenter{"\ue88a", "Control Center"};
WorkspaceIndicator *workspaceIndicator = nullptr;
TrayWidget *trayWidget = nullptr;

View File

@@ -1,21 +0,0 @@
#pragma once
#include <sys/stat.h>
#include "gtkmm/box.h"
class TodoEntry : public Gtk::Box {
public:
TodoEntry(int id, std::string text, sigc::signal<void(int)> signal_dismissed, sigc::signal<void(int, std::string)> signal_edited);
int get_id() const { return id; }
std::string get_text() const { return text; }
private:
int id;
std::string text;
sigc::signal<void(int)> signal_dismissed;
sigc::signal<void(int, std::string)> signal_edited;
void on_dismiss_clicked();
};

View File

@@ -0,0 +1,31 @@
#pragma once
#include <array>
#include <memory>
#include <stdexcept>
#include <string>
#include <cstdio>
class CommandHelper {
public:
static std::string exec(const char *cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, int(*)(FILE*)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
static void execNoOutput(std::string cmd) {
std::string command = cmd + " > /dev/null 2>&1";
int ret = std::system(command.c_str());
if (ret != 0) {
throw std::runtime_error("Command failed with return code: " + std::to_string(ret));
}
}
};

69
include/helpers/hypr.hpp Normal file
View File

@@ -0,0 +1,69 @@
#pragma once
#include <cassert>
#include <nlohmann/json.hpp>
#include <string>
#include <sys/socket.h>
#include "helpers/command.hpp"
class HyprctlHelper {
public:
static nlohmann::json getMonitorData() {
std::string result = CommandHelper::exec("hyprctl -j monitors");
assert(!result.empty() && "Failed to get monitor data from hyprctl");
auto json = nlohmann::json::parse(result);
assert(json.is_array());
return json;
}
static nlohmann::json getWorkspaceData() {
std::string result = CommandHelper::exec("hyprctl -j workspaces");
assert(!result.empty() && "Failed to get workspace data from hyprctl");
auto json = nlohmann::json::parse(result);
assert(json.is_array());
return json;
}
static nlohmann::json getClientData() {
std::string result = CommandHelper::exec("hyprctl -j clients");
assert(!result.empty() && "Failed to get client data from hyprctl");
auto json = nlohmann::json::parse(result);
assert(json.is_array());
return json;
}
static void dispatchWorkspace(int workspaceNumber) {
std::string out = "hyprctl dispatch workspace " + std::to_string(workspaceNumber);
CommandHelper::execNoOutput(out.c_str());
}
};
class HyprSocketHelper {
public:
static std::string getHyprlandSocketPath() {
const char *hyprlandInstanceSignature = std::getenv("HYPRLAND_INSTANCE_SIGNATURE");
const char *xdgRuntimeDir = std::getenv("XDG_RUNTIME_DIR");
if (!xdgRuntimeDir || !hyprlandInstanceSignature) {
return std::string();
}
std::string basePath = std::string(xdgRuntimeDir) + "/hypr/" + std::string(hyprlandInstanceSignature) + "/";
std::string sock1 = basePath + ".socket2.sock";
if (access(sock1.c_str(), F_OK) == 0) {
return sock1;
}
return std::string();
}
};

View File

@@ -0,0 +1,52 @@
#pragma once
#include <cassert>
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <vector>
#include "helper/string.hpp"
class SocketHelper {
typedef struct SocketMessage {
std::string eventType;
std::string eventData;
} SocketMessage;
public:
static std::vector<SocketMessage> 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';
data = std::string(buffer);
} else if (bytesRead == 0) {
std::cerr << "Socket closed by peer" << std::endl;
} else {
std::cerr << "Error reading from socket" << std::endl;
}
auto delimiterPos = data.find(delimiter);
if (delimiterPos == std::string::npos) {
assert(false && "Delimiter not found in socket message");
}
auto splitMessages = StringHelper::split(data, '\n');
auto splitMessagesFinal = std::vector<SocketMessage>();
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);
}
return splitMessagesFinal;
}
};

View File

@@ -0,0 +1,40 @@
#pragma once
#include <string>
#include <vector>
class StringHelper {
public:
static std::vector<std::string> split(const std::string &input, char delimiter) {
std::vector<std::string> tokens;
std::string token;
for (char ch : input) {
if (ch == delimiter) {
if (!token.empty()) {
tokens.push_back(token);
token.clear();
}
} else {
token += ch;
}
}
if (!token.empty()) {
tokens.push_back(token);
}
return tokens;
}
static std::vector<std::string> split(const std::string &input, std::string delimiter) {
std::vector<std::string> tokens;
size_t start = 0;
size_t end = input.find(delimiter);
while (end != std::string::npos) {
tokens.push_back(input.substr(start, end - start));
start = end + delimiter.length();
end = input.find(delimiter, start);
}
tokens.push_back(input.substr(start));
return tokens;
}
};

View File

@@ -0,0 +1,17 @@
#pragma once
#include <fstream>
class SystemHelper {
public:
static std::string read_file_to_string(const std::string &filePath) {
std::ifstream file(filePath);
if (!file.is_open()) {
throw std::runtime_error("Could not open file: " + filePath);
}
std::string content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
file.close();
return content;
}
};

View File

@@ -1,39 +0,0 @@
#pragma once
#include <array>
#include <fstream>
#include <memory>
#include <sstream>
#include <string>
class SystemHelper {
public:
static std::string get_command_output(const char *cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, int (*)(FILE *)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
// Read the output a chunk at a time until the stream ends
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
// Read an entire file into a string. Throws std::runtime_error on failure.
static std::string read_file_to_string(const std::string &path) {
std::ifstream in(path, std::ios::in | std::ios::binary);
if (!in) {
throw std::runtime_error("Failed to open file: " + path);
}
std::ostringstream ss;
ss << in.rdbuf();
return ss.str();
}
};

View File

@@ -1,3 +0,0 @@
#pragma once
#define ICON_HOME "\ue88a"

View File

@@ -13,13 +13,6 @@ class HyprlandService {
public:
static constexpr int kWorkspaceSlotCount = 7;
const char *kMonitorCommand = "hyprctl monitors -j";
const char *kWorkspaceCommand = "hyprctl workspaces -j";
const char *kClientsCommand = "hyprctl clients -j";
struct WindowState {
int hyprId = -1;
};
struct WorkspaceState {
int hyprId = -1;
@@ -64,6 +57,10 @@ class HyprlandService {
}
private:
const char *kMonitorCommand = "hyprctl monitors -j";
const char *kWorkspaceCommand = "hyprctl workspaces -j";
const char *kClientsCommand = "hyprctl clients -j";
HyprlandService();
~HyprlandService();

View File

@@ -1,28 +0,0 @@
#pragma once
#include <memory>
#include <string>
#include "components/todoEntry.hpp"
#include "services/todoAdapter.hpp"
class TodoService {
public:
TodoService(sigc::signal<void()> refreshSignal);
~TodoService();
std::map<int, TodoEntry *> getTodos();
void init();
void removeTodo(int id);
TodoEntry *addTodo(std::string text, bool emitSignal = true, bool persist = true);
void updateTodo(int id, std::string text);
private:
void load();
int nextId = 1;
std::map<int, TodoEntry *> todos;
sigc::signal<void()> refreshSignal;
std::unique_ptr<ITodoAdapter> adapter;
};

View File

@@ -1,21 +0,0 @@
#pragma once
#include <string>
#include <vector>
struct TodoRecord {
int id;
std::string text;
};
class ITodoAdapter {
public:
virtual ~ITodoAdapter() = default;
virtual bool init() = 0;
virtual std::vector<TodoRecord> listTodos() = 0;
virtual int addTodo(const std::string &text) = 0;
virtual bool removeTodo(int id) = 0;
virtual bool updateTodo(int id, const std::string &text) = 0;
};

View File

@@ -1,21 +0,0 @@
#pragma once
#include <string>
#include "components/popover.hpp"
#include "services/todo.hpp"
class TodoPopover : public Popover {
public:
TodoPopover(std::string icon, std::string title);
void update();
private:
std::string name;
TodoService *todoService = nullptr;
Gtk::Box container;
Gtk::Box inputArea;
Gtk::Box *todoList = nullptr;
};