add hyprland socket support
This commit is contained in:
22
.clang-format
Normal file
22
.clang-format
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
BasedOnStyle: LLVM
|
||||||
|
IndentWidth: 4
|
||||||
|
AlignConsecutiveAssignments: true
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignTrailingComments: Always
|
||||||
|
BreakBeforeBraces: Allman
|
||||||
|
ColumnLimit: 0
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
InsertNewlineAtEOF: true
|
||||||
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
|
BinPackArguments: false
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 1000
|
||||||
|
ContinuationIndentWidth: 4 # Adjust the indent width for continuation lines
|
||||||
|
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^(<.+>)$'
|
||||||
|
Priority: 1
|
||||||
|
- Regex: '^"(.+\.hpp)"$'
|
||||||
|
Priority: 2
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 3
|
||||||
|
IncludeBlocks: Regroup
|
||||||
@@ -20,6 +20,7 @@ target_sources(bar_lib
|
|||||||
PUBLIC
|
PUBLIC
|
||||||
src/bar.cpp
|
src/bar.cpp
|
||||||
src/widgets/clock.cpp
|
src/widgets/clock.cpp
|
||||||
|
src/services/hyprland.cpp
|
||||||
)
|
)
|
||||||
include_directories(bar_lib PRIVATE
|
include_directories(bar_lib PRIVATE
|
||||||
include
|
include
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gtkmm.h>
|
|
||||||
#include <gtk4-layer-shell/gtk4-layer-shell.h>
|
#include <gtk4-layer-shell/gtk4-layer-shell.h>
|
||||||
|
#include <gtkmm.h>
|
||||||
|
|
||||||
|
#include "services/hyprland.hpp"
|
||||||
#include "widgets/clock.hpp"
|
#include "widgets/clock.hpp"
|
||||||
|
|
||||||
|
class Bar : public Gtk::Window
|
||||||
class Bar : public Gtk::Window {
|
{
|
||||||
public:
|
public:
|
||||||
Bar();
|
Bar();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Clock clock;
|
Clock clock;
|
||||||
|
HyprlandService hyprland;
|
||||||
Gtk::CenterBox main_box{};
|
Gtk::CenterBox main_box{};
|
||||||
|
|
||||||
void setup_ui();
|
void setup_ui();
|
||||||
|
|||||||
28
include/services/hyprland.hpp
Normal file
28
include/services/hyprland.hpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glibmm.h>
|
||||||
|
#include <sigc++/sigc++.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class HyprlandService
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HyprlandService();
|
||||||
|
~HyprlandService();
|
||||||
|
|
||||||
|
// Setup the connection
|
||||||
|
void start();
|
||||||
|
|
||||||
|
// Signal that emits (EventName, EventData)
|
||||||
|
// Example: emits ("workspace", "1")
|
||||||
|
sigc::signal<void(std::string, std::string)> on_event;
|
||||||
|
void on_hyprland_event(std::string event, std::string data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_fd = -1; // File descriptor for the socket
|
||||||
|
std::string m_buffer; // Buffer to handle partial socket reads
|
||||||
|
|
||||||
|
bool on_socket_read(Glib::IOCondition condition);
|
||||||
|
void parse_message(const std::string &line);
|
||||||
|
std::string get_socket_path();
|
||||||
|
};
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gtkmm/label.h>
|
|
||||||
#include <gtkmm.h>
|
|
||||||
#include <gtk4-layer-shell/gtk4-layer-shell.h>
|
#include <gtk4-layer-shell/gtk4-layer-shell.h>
|
||||||
|
#include <gtkmm.h>
|
||||||
|
#include <gtkmm/label.h>
|
||||||
|
|
||||||
#include "interface/updateable.ipp"
|
#include "interface/updateable.ipp"
|
||||||
|
|
||||||
class Clock : public Gtk::Label, public IUpdatable {
|
class Clock : public Gtk::Label, public IUpdatable
|
||||||
public:
|
{
|
||||||
|
public:
|
||||||
bool onUpdate();
|
bool onUpdate();
|
||||||
};
|
};
|
||||||
|
|||||||
3
main.cpp
3
main.cpp
@@ -1,6 +1,7 @@
|
|||||||
#include "bar.hpp"
|
#include "bar.hpp"
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
auto app = Gtk::Application::create("org.example.mybar");
|
auto app = Gtk::Application::create("org.example.mybar");
|
||||||
|
|
||||||
return app->make_window_and_run<Bar>(argc, argv);
|
return app->make_window_and_run<Bar>(argc, argv);
|
||||||
|
|||||||
42
src/bar.cpp
42
src/bar.cpp
@@ -1,14 +1,16 @@
|
|||||||
#include <ctime>
|
#include "bar.hpp"
|
||||||
|
|
||||||
#include <gtkmm/enums.h>
|
#include <gtkmm/enums.h>
|
||||||
#include <gtkmm/label.h>
|
#include <gtkmm/label.h>
|
||||||
|
|
||||||
#include "bar.hpp"
|
#include "services/hyprland.hpp"
|
||||||
#include "glibmm/main.h"
|
|
||||||
#include "sigc++/functors/mem_fun.h"
|
|
||||||
#include "widgets/clock.hpp"
|
#include "widgets/clock.hpp"
|
||||||
|
|
||||||
Bar::Bar() {
|
#include "glibmm/main.h"
|
||||||
|
#include "sigc++/functors/mem_fun.h"
|
||||||
|
|
||||||
|
Bar::Bar()
|
||||||
|
{
|
||||||
gtk_layer_init_for_window(this->gobj());
|
gtk_layer_init_for_window(this->gobj());
|
||||||
|
|
||||||
gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, true);
|
gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, true);
|
||||||
@@ -26,18 +28,22 @@ Bar::Bar() {
|
|||||||
|
|
||||||
Glib::signal_timeout().connect(
|
Glib::signal_timeout().connect(
|
||||||
sigc::mem_fun(
|
sigc::mem_fun(
|
||||||
this->clock,
|
this->clock,
|
||||||
&Clock::onUpdate
|
&Clock::onUpdate),
|
||||||
),
|
1000);
|
||||||
1000
|
|
||||||
);
|
hyprland.on_event.connect(
|
||||||
|
sigc::mem_fun(this->hyprland, &HyprlandService::on_hyprland_event));
|
||||||
|
|
||||||
|
hyprland.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bar::setup_ui() {
|
void Bar::setup_ui()
|
||||||
|
{
|
||||||
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};
|
||||||
Gtk::Box right_box{Gtk::Orientation::HORIZONTAL};
|
Gtk::Box right_box{Gtk::Orientation::HORIZONTAL};
|
||||||
|
|
||||||
main_box.set_start_widget(left_box);
|
main_box.set_start_widget(left_box);
|
||||||
main_box.set_center_widget(center_box);
|
main_box.set_center_widget(center_box);
|
||||||
main_box.set_end_widget(right_box);
|
main_box.set_end_widget(right_box);
|
||||||
@@ -45,24 +51,22 @@ void Bar::setup_ui() {
|
|||||||
Gtk::Label labelLeft("labelLeft");
|
Gtk::Label labelLeft("labelLeft");
|
||||||
Gtk::Label labelRight("labelRight");
|
Gtk::Label labelRight("labelRight");
|
||||||
|
|
||||||
|
|
||||||
left_box.append(labelLeft);
|
left_box.append(labelLeft);
|
||||||
center_box.append(clock);
|
center_box.append(clock);
|
||||||
right_box.append(labelRight);
|
right_box.append(labelRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bar::load_css() {
|
void Bar::load_css()
|
||||||
|
{
|
||||||
auto css_provider = Gtk::CssProvider::create();
|
auto css_provider = Gtk::CssProvider::create();
|
||||||
|
|
||||||
css_provider->load_from_data(R"(
|
css_provider->load_from_data(R"(
|
||||||
window { background-color: #222; color: #fff; }
|
window { background-color: #222; color: #fff; }
|
||||||
#clock-label { font-weight: bold; font-family: monospace; }
|
#clock-label { font-weight: bold; font-family: monospace; }
|
||||||
)");
|
)");
|
||||||
|
|
||||||
Gtk::StyleContext::add_provider_for_display(
|
Gtk::StyleContext::add_provider_for_display(
|
||||||
Gdk::Display::get_default(),
|
Gdk::Display::get_default(),
|
||||||
css_provider,
|
css_provider,
|
||||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
134
src/services/hyprland.cpp
Normal file
134
src/services/hyprland.cpp
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
#include "services/hyprland.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
HyprlandService::HyprlandService() {}
|
||||||
|
|
||||||
|
HyprlandService::~HyprlandService()
|
||||||
|
{
|
||||||
|
if (m_fd != -1)
|
||||||
|
{
|
||||||
|
close(m_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandService::on_hyprland_event(std::string event, std::string data)
|
||||||
|
{
|
||||||
|
if (event == "workspace")
|
||||||
|
{
|
||||||
|
std::cout << "Switched to workspace: " << data << std::endl;
|
||||||
|
// Update your UI here...
|
||||||
|
}
|
||||||
|
else if (event == "activewindow")
|
||||||
|
{
|
||||||
|
std::cout << "Active window changed" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandService::start()
|
||||||
|
{
|
||||||
|
std::string socket_path = get_socket_path();
|
||||||
|
if (socket_path.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 1. Create Socket
|
||||||
|
m_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (m_fd == -1)
|
||||||
|
{
|
||||||
|
std::cerr << "[Hyprland] Failed to create socket" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Connect
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
strncpy(addr.sun_path, socket_path.c_str(), sizeof(addr.sun_path) - 1);
|
||||||
|
|
||||||
|
if (connect(m_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
|
||||||
|
{
|
||||||
|
std::cerr << "[Hyprland] Failed to connect to " << socket_path << std::endl;
|
||||||
|
close(m_fd);
|
||||||
|
m_fd = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "[Hyprland] Connected to event socket." << std::endl;
|
||||||
|
|
||||||
|
// 3. Register with GLib Main Loop
|
||||||
|
// This tells GTK to call 'on_socket_read' whenever there is data to read
|
||||||
|
Glib::signal_io().connect(
|
||||||
|
sigc::mem_fun(*this, &HyprlandService::on_socket_read),
|
||||||
|
m_fd,
|
||||||
|
Glib::IOCondition::IO_IN | Glib::IOCondition::IO_HUP | Glib::IOCondition::IO_ERR);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HyprlandService::on_socket_read(Glib::IOCondition condition)
|
||||||
|
{
|
||||||
|
// Handle disconnection or errors
|
||||||
|
auto error_mask = Glib::IOCondition::IO_HUP | Glib::IOCondition::IO_ERR;
|
||||||
|
|
||||||
|
// 2. Perform the bitwise AND, then cast to int to check if non-zero
|
||||||
|
if (static_cast<int>(condition & error_mask) != 0)
|
||||||
|
{
|
||||||
|
std::cerr << "[Hyprland] Socket disconnected." << std::endl;
|
||||||
|
close(m_fd);
|
||||||
|
m_fd = -1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Read data
|
||||||
|
char buffer[4096];
|
||||||
|
ssize_t bytes_read = read(m_fd, buffer, sizeof(buffer) - 1);
|
||||||
|
|
||||||
|
if (bytes_read > 0)
|
||||||
|
{
|
||||||
|
buffer[bytes_read] = '\0';
|
||||||
|
m_buffer.append(buffer);
|
||||||
|
|
||||||
|
// Process line by line
|
||||||
|
size_t pos = 0;
|
||||||
|
while ((pos = m_buffer.find('\n')) != std::string::npos)
|
||||||
|
{
|
||||||
|
std::string line = m_buffer.substr(0, pos);
|
||||||
|
parse_message(line);
|
||||||
|
m_buffer.erase(0, pos + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // Continue listening
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandService::parse_message(const std::string &line)
|
||||||
|
{
|
||||||
|
// Hyprland events look like: "event>>data"
|
||||||
|
// Example: "workspace>>1" or "activewindow>>550a12,firefox"
|
||||||
|
size_t split = line.find(">>");
|
||||||
|
if (split != std::string::npos)
|
||||||
|
{
|
||||||
|
std::string event_name = line.substr(0, split);
|
||||||
|
std::string event_data = line.substr(split + 2);
|
||||||
|
|
||||||
|
// Emit the signal
|
||||||
|
on_event.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 "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path format: $XDG_RUNTIME_DIR/hypr/$SIGNATURE/.socket2.sock
|
||||||
|
return std::string(runtime) + "/hypr/" + sig + "/.socket2.sock";
|
||||||
|
}
|
||||||
@@ -3,13 +3,14 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
bool Clock::onUpdate() {
|
bool Clock::onUpdate()
|
||||||
|
{
|
||||||
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << std::put_time(std::localtime(&now), "%H:%M:%S");
|
ss << std::put_time(std::localtime(&now), "%H:%M:%S");
|
||||||
|
|
||||||
set_text(ss.str());
|
set_text(ss.str());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user