no compile errors, fully functional workspace indicators
This commit is contained in:
@@ -5,7 +5,8 @@ set(CMAKE_CXX_STANDARD 23)
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -DNDEBUG -Wall -Wextra -Wpedantic -Werror")
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG -Wall -Wextra -Wpedantic -Werror")
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
@@ -32,11 +33,8 @@ target_sources(bar_lib
|
|||||||
|
|
||||||
src/services/hyprland.cpp
|
src/services/hyprland.cpp
|
||||||
src/services/tray.cpp
|
src/services/tray.cpp
|
||||||
src/services/notifications.cpp
|
|
||||||
src/services/bluetooth.cpp
|
|
||||||
|
|
||||||
src/widgets/tray.cpp
|
src/widgets/tray.cpp
|
||||||
src/widgets/bluetooth.cpp
|
|
||||||
src/widgets/controlCenter.cpp
|
src/widgets/controlCenter.cpp
|
||||||
|
|
||||||
src/components/popover.cpp
|
src/components/popover.cpp
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
#include "bar/bar.hpp"
|
#include "bar/bar.hpp"
|
||||||
#include "services/hyprland.hpp"
|
#include "services/hyprland.hpp"
|
||||||
#include "services/notifications.hpp"
|
|
||||||
#include "services/tray.hpp"
|
|
||||||
|
|
||||||
#include "glibmm/refptr.h"
|
#include "glibmm/refptr.h"
|
||||||
#include "gtkmm/application.h"
|
#include "gtkmm/application.h"
|
||||||
@@ -18,9 +16,8 @@ class App {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Glib::RefPtr<Gtk::Application> app;
|
Glib::RefPtr<Gtk::Application> app;
|
||||||
std::vector<Bar *> bars;
|
std::vector<std::shared_ptr<Bar>> bars;
|
||||||
HyprlandService *hyprlandService = nullptr;
|
HyprlandService *hyprlandService = nullptr;
|
||||||
NotificationService notificationService;
|
|
||||||
TrayService *trayService = TrayService::getInstance();
|
TrayService *trayService = TrayService::getInstance();
|
||||||
|
|
||||||
void setupServices();
|
void setupServices();
|
||||||
|
|||||||
@@ -1,14 +1,35 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "gtkmm/box.h"
|
#include "gtkmm/box.h"
|
||||||
#include "gtkmm/button.h"
|
#include "gtkmm/button.h"
|
||||||
#include "gtkmm/overlay.h"
|
#include "gtkmm/overlay.h"
|
||||||
|
|
||||||
class WorkspaceIndicator : public Gtk::Box {
|
class WorkspaceIndicator : public Gtk::Box {
|
||||||
public:
|
public:
|
||||||
WorkspaceIndicator(std::string label, sigc::slot<void()> onClick = {});
|
|
||||||
|
enum InidicatorState {
|
||||||
|
EMPTY,
|
||||||
|
ALIVE,
|
||||||
|
FOCUSED,
|
||||||
|
PRESENTING,
|
||||||
|
URGENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
// meh, Maybe try WorkspaceState struct later
|
||||||
|
WorkspaceIndicator(int id, std::string label, sigc::slot<void(int)> onClick);
|
||||||
|
void setIndicatorState(InidicatorState state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Gtk::Button button;
|
void clearCssClass();
|
||||||
|
InidicatorState currentState = EMPTY;
|
||||||
|
std::shared_ptr<Gtk::Overlay> overlay;
|
||||||
|
std::map<InidicatorState, std::string> stateToCssClass = {
|
||||||
|
{FOCUSED, "workspace-pill-focused"},
|
||||||
|
{URGENT, "workspace-pill-urgent"},
|
||||||
|
{ALIVE, "workspace-pill-alive"},
|
||||||
|
{PRESENTING, "workspace-pill-presenting"},
|
||||||
|
{EMPTY, "workspace-pill-empty"}, // Default class
|
||||||
|
};
|
||||||
};
|
};
|
||||||
@@ -9,12 +9,13 @@
|
|||||||
#include "helpers/string.hpp"
|
#include "helpers/string.hpp"
|
||||||
|
|
||||||
class SocketHelper {
|
class SocketHelper {
|
||||||
|
|
||||||
|
public:
|
||||||
typedef struct SocketMessage {
|
typedef struct SocketMessage {
|
||||||
std::string eventType;
|
std::string eventType;
|
||||||
std::string eventData;
|
std::string eventData;
|
||||||
} SocketMessage;
|
} SocketMessage;
|
||||||
|
|
||||||
public:
|
|
||||||
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;
|
||||||
|
|||||||
@@ -3,12 +3,14 @@
|
|||||||
#include <glibmm.h>
|
#include <glibmm.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
#include <sigc++/sigc++.h>
|
#include <sigc++/sigc++.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
#include "bar/bar.hpp"
|
||||||
#include "components/workspaceIndicator.hpp"
|
#include "components/workspaceIndicator.hpp"
|
||||||
|
#include "helpers/socket.hpp"
|
||||||
|
|
||||||
#include "gtkmm/box.h"
|
#include "gtkmm/box.h"
|
||||||
|
|
||||||
@@ -18,28 +20,33 @@ class HyprlandService {
|
|||||||
inline static HyprlandService *instance = nullptr;
|
inline static HyprlandService *instance = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct WorkspaceState {
|
struct Client {
|
||||||
int id;
|
std::string address;
|
||||||
int monitorId;
|
int workspaceId;
|
||||||
bool alive = false; // window count above 0 (exists in hyprctl workspaces array)
|
std::string title;
|
||||||
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
|
|
||||||
|
|
||||||
|
struct WorkspaceData {
|
||||||
|
int id;
|
||||||
|
std::string monitorName;
|
||||||
std::string label;
|
std::string label;
|
||||||
|
|
||||||
|
std::map<std::string, std::shared_ptr<Client>> clients;
|
||||||
|
std::set<std::string> urgentClients;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Workspace {
|
struct Workspace {
|
||||||
WorkspaceState state;
|
std::shared_ptr<WorkspaceData> state;
|
||||||
std::shared_ptr<WorkspaceIndicator> view;
|
std::shared_ptr<WorkspaceIndicator> view;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Monitor {
|
struct Monitor {
|
||||||
int id;
|
int id;
|
||||||
int activeWorkspaceId;
|
int activeWorkspaceId;
|
||||||
bool focused = false;
|
bool focused;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::map<int, std::shared_ptr<Workspace>> monitorWorkspaces;
|
std::map<int, std::shared_ptr<Workspace>> monitorWorkspaces;
|
||||||
|
std::shared_ptr<Bar> bar;
|
||||||
};
|
};
|
||||||
|
|
||||||
static HyprlandService *getInstance() {
|
static HyprlandService *getInstance() {
|
||||||
@@ -49,22 +56,57 @@ inline static HyprlandService *instance = nullptr;
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Gtk::Box> getWorkspaceIndicatorsForMonitor(int monitorId);
|
std::shared_ptr<Gtk::Box> getWorkspaceIndicatorsForMonitor(std::string monitorName);
|
||||||
|
|
||||||
|
void addBar(std::shared_ptr<Bar> bar, std::string monitorName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum SocketEventType {
|
||||||
|
WORKSPACE_CHANGED,
|
||||||
|
|
||||||
|
ACTIVE_WINDOW,
|
||||||
|
OPEN_WINDOW,
|
||||||
|
CLOSE_WINDOW,
|
||||||
|
URGENT,
|
||||||
|
|
||||||
|
FOCUSED_MONITOR,
|
||||||
|
MONITOR_REMOVED,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<std::string, SocketEventType> socketEventTypeMap = {
|
||||||
|
{"workspace", WORKSPACE_CHANGED},
|
||||||
|
{"activewindowv2", ACTIVE_WINDOW},
|
||||||
|
{"openwindow", OPEN_WINDOW},
|
||||||
|
{"closewindow", CLOSE_WINDOW},
|
||||||
|
{"urgent", URGENT},
|
||||||
|
{"focusedmon", FOCUSED_MONITOR},
|
||||||
|
{"monitorremoved", MONITOR_REMOVED},
|
||||||
|
};
|
||||||
|
void onWorkspaceChanged(int workspaceId);
|
||||||
|
void onFocusedMonitorChanged(std::string monitorData);
|
||||||
|
void onOpenWindow(std::string windowData);
|
||||||
|
void onCloseWindow(std::string windowData);
|
||||||
|
void onUrgent(std::string windowAddress);
|
||||||
|
void onActiveWindowChanged(std::string windowAddress);
|
||||||
|
void onMonitorRemoved(std::string monitorName);
|
||||||
|
// void onMonitorAdded(std::string monitorName);
|
||||||
|
|
||||||
HyprlandService();
|
HyprlandService();
|
||||||
std::map<int, std::shared_ptr<Monitor>> monitors;
|
std::map<std::string, std::shared_ptr<Monitor>> monitors;
|
||||||
std::map<int, std::shared_ptr<Workspace>> workspaces;
|
std::map<int, std::shared_ptr<Workspace>> workspaces;
|
||||||
|
std::map<std::string, std::shared_ptr<Client>> clients;
|
||||||
|
|
||||||
/// maybe refactor into reusable class
|
/// maybe refactor into reusable class
|
||||||
std::string socketBuffer;
|
std::string socketBuffer;
|
||||||
int socketFd;
|
int socketFd;
|
||||||
///
|
///
|
||||||
|
|
||||||
std::string getSocketPath();
|
|
||||||
void bindSocket();
|
void bindSocket();
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void updateIndicator(Workspace &workspace, const WorkspaceState newState);
|
void switchToWorkspace(int workspaceId);
|
||||||
|
void refreshIndicator(std::shared_ptr<Workspace> workspace);
|
||||||
|
|
||||||
|
void handleSocketMessage(SocketHelper::SocketMessage message);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <gio/gio.h>
|
|
||||||
|
|
||||||
class NotificationService {
|
|
||||||
public:
|
|
||||||
void intialize();
|
|
||||||
|
|
||||||
NotificationService() = default;
|
|
||||||
~NotificationService();
|
|
||||||
|
|
||||||
guint32 allocateNotificationId(guint32 replacesId);
|
|
||||||
GDBusConnection *getConnection() const { return connection; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
GDBusConnection *connection = nullptr;
|
|
||||||
guint registrationId = 0;
|
|
||||||
GDBusNodeInfo *nodeInfo = nullptr;
|
|
||||||
guint32 nextNotificationId = 1;
|
|
||||||
};
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "components/popover.hpp"
|
#include "components/popover.hpp"
|
||||||
#include "services/bluetooth.hpp"
|
|
||||||
#include "widgets/bluetooth.hpp"
|
|
||||||
#include "gtkmm/box.h"
|
#include "gtkmm/box.h"
|
||||||
|
|
||||||
class ControlCenter : public Popover {
|
class ControlCenter : public Popover {
|
||||||
@@ -11,7 +9,4 @@ class ControlCenter : public Popover {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Gtk::Box container;
|
Gtk::Box container;
|
||||||
|
|
||||||
BluetoothWidget *bluetoothWidget = nullptr;
|
|
||||||
BluetoothService *bluetoothService = BluetoothService::getInstance();
|
|
||||||
};
|
};
|
||||||
2
main.cpp
2
main.cpp
@@ -1,6 +1,6 @@
|
|||||||
#include "app.hpp"
|
#include "app.hpp"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main()
|
||||||
{
|
{
|
||||||
App app;
|
App app;
|
||||||
|
|
||||||
|
|||||||
@@ -74,8 +74,14 @@ button {
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-pill-active {
|
.workspace-pill-alive {
|
||||||
|
background-color: rgba(255, 255, 255, 0.153);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-pill-presenting {
|
||||||
background-color: #666666;
|
background-color: #666666;
|
||||||
|
color: #ffffff;
|
||||||
|
/* animation: workspace-updown 1.2s ease-in-out infinite; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-pill-focused {
|
.workspace-pill-focused {
|
||||||
@@ -136,35 +142,3 @@ button {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.todo-tag-area {
|
|
||||||
min-height: 40px;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-button {
|
|
||||||
background-color: #444444;
|
|
||||||
color: #ffffff;
|
|
||||||
padding: 2px 8px;
|
|
||||||
margin: 2px;
|
|
||||||
border-radius: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-family: "Hack Nerd Font Mono", sans-serif;
|
|
||||||
min-height: 24px;
|
|
||||||
min-width: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-button:hover {
|
|
||||||
background-color: #555555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-button.suggested-action {
|
|
||||||
background-color: #3498db;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.todo-entry-box {
|
|
||||||
margin-top: 5px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
16
src/app.cpp
16
src/app.cpp
@@ -6,9 +6,7 @@
|
|||||||
|
|
||||||
App::App() {
|
App::App() {
|
||||||
this->app = Gtk::Application::create("org.example.mybar");
|
this->app = Gtk::Application::create("org.example.mybar");
|
||||||
|
|
||||||
this->setupServices();
|
this->setupServices();
|
||||||
this->notificationService.intialize();
|
|
||||||
this->hyprlandService = HyprlandService::getInstance();
|
this->hyprlandService = HyprlandService::getInstance();
|
||||||
|
|
||||||
app->signal_activate().connect([&]() {
|
app->signal_activate().connect([&]() {
|
||||||
@@ -21,11 +19,15 @@ App::App() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (monitor) {
|
if (monitor) {
|
||||||
auto bar = new Bar(monitor->gobj());
|
auto bar = std::make_shared<Bar>(monitor->gobj());
|
||||||
|
|
||||||
bar->set_application(app);
|
bar->set_application(app);
|
||||||
bar->show();
|
bar->show();
|
||||||
bar->addLeftWidget(hyprlandService->getWorkspaceIndicatorsForMonitor(i));
|
|
||||||
|
std::string monitorName = monitor->get_connector();
|
||||||
|
bar->addLeftWidget(hyprlandService->getWorkspaceIndicatorsForMonitor(monitorName));
|
||||||
|
hyprlandService->addBar(bar, monitorName);
|
||||||
|
|
||||||
bars.push_back(bar);
|
bars.push_back(bar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,16 +35,12 @@ App::App() {
|
|||||||
|
|
||||||
|
|
||||||
app->signal_shutdown().connect([&]() {
|
app->signal_shutdown().connect([&]() {
|
||||||
for (auto bar : bars) {
|
|
||||||
delete bar;
|
|
||||||
}
|
|
||||||
bars.clear();
|
|
||||||
|
|
||||||
this->trayService->stop();
|
this->trayService->stop();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::setupServices() {
|
void App::setupServices() {
|
||||||
|
|
||||||
this->trayService->start();
|
this->trayService->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
Popover::Popover(const std::string icon, std::string name): Button(icon) {
|
Popover::Popover(const std::string icon, std::string name): Button(icon) {
|
||||||
signal_clicked().connect(sigc::mem_fun(*this, &Popover::on_toggle_window));
|
signal_clicked().connect(sigc::mem_fun(*this, &Popover::on_toggle_window));
|
||||||
|
|
||||||
|
set_name(name);
|
||||||
|
|
||||||
popover = new Gtk::Popover();
|
popover = new Gtk::Popover();
|
||||||
popover->set_parent(*this);
|
popover->set_parent(*this);
|
||||||
popover->set_autohide(true);
|
popover->set_autohide(true);
|
||||||
|
|||||||
@@ -1,42 +1,52 @@
|
|||||||
#include "components/workspaceIndicator.hpp"
|
#include "components/workspaceIndicator.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "gtkmm/button.h"
|
#include "gtkmm/gestureclick.h"
|
||||||
#include "gtkmm/label.h"
|
#include "gtkmm/label.h"
|
||||||
|
#include "gtkmm/overlay.h"
|
||||||
|
|
||||||
WorkspaceIndicator::WorkspaceIndicator(std::string label, sigc::slot<void()> onClick)
|
WorkspaceIndicator::WorkspaceIndicator(int id, std::string label, sigc::slot<void(int)> onClick)
|
||||||
: Gtk::Box(Gtk::Orientation::HORIZONTAL) {
|
: Gtk::Box(Gtk::Orientation::HORIZONTAL) {
|
||||||
|
|
||||||
auto overlay = Gtk::make_managed<Gtk::Overlay>();
|
overlay = std::make_shared<Gtk::Overlay>();
|
||||||
auto numLabel = Gtk::make_managed<Gtk::Label>(label);
|
auto numLabel = Gtk::make_managed<Gtk::Label>(label);
|
||||||
auto pillContainer = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
|
auto pillContainer = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
|
||||||
|
|
||||||
// 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([this, i](int, double, double) {
|
gesture->signal_released().connect([this, id, onClick](int, double, double) {
|
||||||
// this->service->switchToWorkspace(
|
onClick(
|
||||||
// i + this->monitorId * HyprlandService::kWorkspaceSlotCount);
|
id);
|
||||||
// });
|
});
|
||||||
|
|
||||||
|
overlay->add_controller(gesture);
|
||||||
// overlay->add_controller(gesture);
|
|
||||||
overlay->add_css_class("workspace-pill");
|
overlay->add_css_class("workspace-pill");
|
||||||
|
|
||||||
// if (i == 6 || i == 7) {
|
if (id == 6 || id == 7) {
|
||||||
// auto indicator = Gtk::make_managed<Gtk::Label>(i == 6 ? "🫱🏻" : "🫲🏻");
|
auto indicator = Gtk::make_managed<Gtk::Label>(id == 6 ? "🫱🏻" : "🫲🏻");
|
||||||
// indicator->add_css_class(i == 6 ? "workspace-pill-six" : "workspace-pill-seven");
|
indicator->add_css_class(id == 6 ? "workspace-pill-six" : "workspace-pill-seven");
|
||||||
// indicator->set_valign(Gtk::Align::END);
|
indicator->set_valign(Gtk::Align::END);
|
||||||
// overlay->set_child(*indicator);
|
overlay->set_child(*indicator);
|
||||||
// overlay->add_overlay(*numLabel);
|
overlay->add_overlay(*numLabel);
|
||||||
// pillContainer->append(*overlay);
|
pillContainer->append(*overlay);
|
||||||
// } else {
|
} else {
|
||||||
overlay->set_child(*numLabel);
|
overlay->set_child(*numLabel);
|
||||||
pillContainer->append(*overlay);
|
pillContainer->append(*overlay);
|
||||||
// }
|
}
|
||||||
|
|
||||||
append(*pillContainer);
|
append(*pillContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceIndicator::setIndicatorState(InidicatorState state) {
|
||||||
|
this->clearCssClass();
|
||||||
|
this->currentState = state;
|
||||||
|
auto cssClass = this->stateToCssClass[state];
|
||||||
|
|
||||||
|
this->overlay->add_css_class(cssClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceIndicator::clearCssClass() {
|
||||||
|
for (const auto &[_, cssClass] : stateToCssClass) {
|
||||||
|
this->overlay->remove_css_class(cssClass);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
#include "services/bluetooth.hpp"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "glib.h"
|
|
||||||
|
|
||||||
BluetoothService::BluetoothService() {
|
|
||||||
GError *error = nullptr;
|
|
||||||
|
|
||||||
this->adapter_proxy = g_dbus_proxy_new_for_bus_sync(
|
|
||||||
G_BUS_TYPE_SYSTEM,
|
|
||||||
G_DBUS_PROXY_FLAGS_NONE,
|
|
||||||
nullptr,
|
|
||||||
"org.bluez",
|
|
||||||
"/org/bluez/hci0",
|
|
||||||
"org.bluez.Adapter1",
|
|
||||||
nullptr,
|
|
||||||
&error);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "Error creating Bluetooth adapter proxy: "
|
|
||||||
<< error->message << std::endl;
|
|
||||||
g_error_free(error);
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->powerState = this->getPowerState();
|
|
||||||
this->isDiscovering = this->getIsDiscovering();
|
|
||||||
|
|
||||||
g_signal_connect(
|
|
||||||
this->adapter_proxy,
|
|
||||||
"g-properties-changed",
|
|
||||||
G_CALLBACK(BluetoothService::onPropertyChangedStatic),
|
|
||||||
this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BluetoothService::getPowerState() {
|
|
||||||
GVariant *result = g_dbus_proxy_get_cached_property(
|
|
||||||
this->adapter_proxy,
|
|
||||||
"Powered");
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
std::cerr << "Error getting Powered property." << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean powered;
|
|
||||||
g_variant_get(result, "b", &powered);
|
|
||||||
g_variant_unref(result);
|
|
||||||
|
|
||||||
return powered;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BluetoothService::getIsDiscovering() {
|
|
||||||
GVariant *result = g_dbus_proxy_get_cached_property(
|
|
||||||
this->adapter_proxy,
|
|
||||||
"Discovering");
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
std::cerr << "Error getting Discovering property." << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean discovering;
|
|
||||||
g_variant_get(result, "b", &discovering);
|
|
||||||
g_variant_unref(result);
|
|
||||||
|
|
||||||
return discovering;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BluetoothService::togglePowerState() {
|
|
||||||
GError *error = nullptr;
|
|
||||||
bool state = !this->powerState;
|
|
||||||
|
|
||||||
GDBusConnection *connection = g_dbus_proxy_get_connection(this->adapter_proxy);
|
|
||||||
GVariant *reply = g_dbus_connection_call_sync(
|
|
||||||
connection,
|
|
||||||
"org.bluez",
|
|
||||||
"/org/bluez/hci0",
|
|
||||||
"org.freedesktop.DBus.Properties",
|
|
||||||
"Set",
|
|
||||||
g_variant_new(
|
|
||||||
"(ssv)",
|
|
||||||
"org.bluez.Adapter1",
|
|
||||||
"Powered",
|
|
||||||
g_variant_new_boolean(state)),
|
|
||||||
nullptr,
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
nullptr,
|
|
||||||
&error);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "Error setting Powered property: "
|
|
||||||
<< error->message << std::endl;
|
|
||||||
g_error_free(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reply) {
|
|
||||||
g_variant_unref(reply);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!error) {
|
|
||||||
this->powerState = state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BluetoothService::toggleIsDiscovering() {
|
|
||||||
bool newState = !this->isDiscovering;
|
|
||||||
GError *error = nullptr;
|
|
||||||
|
|
||||||
const char *method = newState ? "StartDiscovery" : "StopDiscovery";
|
|
||||||
GVariant *reply = g_dbus_proxy_call_sync(
|
|
||||||
this->adapter_proxy,
|
|
||||||
method,
|
|
||||||
nullptr,
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
nullptr,
|
|
||||||
&error);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "Error calling " << method << ": "
|
|
||||||
<< error->message << std::endl;
|
|
||||||
g_error_free(error);
|
|
||||||
|
|
||||||
if (reply) {
|
|
||||||
g_variant_unref(reply);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->isDiscovering = newState;
|
|
||||||
|
|
||||||
if (reply) {
|
|
||||||
g_variant_unref(reply);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BluetoothService::onPropertyChanged(GDBusProxy *proxy,
|
|
||||||
GVariant *changed_properties,
|
|
||||||
const gchar *const *invalidated_properties,
|
|
||||||
gpointer user_data) {
|
|
||||||
|
|
||||||
gboolean is_powered = FALSE;
|
|
||||||
gboolean is_discovering = FALSE;
|
|
||||||
|
|
||||||
if (g_variant_lookup(changed_properties, "Powered", "b", &is_powered)) {
|
|
||||||
|
|
||||||
if (!is_powered) {
|
|
||||||
this->isDiscovering = is_discovering;
|
|
||||||
isDiscoveringChangedSignal.emit(isDiscovering);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->powerState = is_powered;
|
|
||||||
powerStateChangedSignal.emit(powerState);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_variant_lookup(changed_properties, "Discovering", "b", &is_discovering)) {
|
|
||||||
this->isDiscovering = is_discovering;
|
|
||||||
isDiscoveringChangedSignal.emit(isDiscovering);
|
|
||||||
getDeviceObjectPaths();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BluetoothService::onPropertyChangedStatic(GDBusProxy *proxy,
|
|
||||||
GVariant *changed_properties,
|
|
||||||
const gchar *const *invalidated_properties,
|
|
||||||
gpointer user_data) {
|
|
||||||
BluetoothService *service = static_cast<BluetoothService *>(user_data);
|
|
||||||
|
|
||||||
if (service) {
|
|
||||||
service->onPropertyChanged(proxy, changed_properties, invalidated_properties, user_data);
|
|
||||||
} else {
|
|
||||||
std::cerr << "Error: BluetoothService instance is null in static callback." << std::endl;
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> BluetoothService::getDeviceObjectPaths() {
|
|
||||||
std::vector<std::string> device_paths;
|
|
||||||
|
|
||||||
GError *error = nullptr;
|
|
||||||
GDBusConnection *connection = g_dbus_proxy_get_connection(this->adapter_proxy);
|
|
||||||
GVariant *reply = g_dbus_connection_call_sync(
|
|
||||||
connection,
|
|
||||||
"org.bluez",
|
|
||||||
"/",
|
|
||||||
"org.freedesktop.DBus.ObjectManager",
|
|
||||||
"GetManagedObjects",
|
|
||||||
nullptr,
|
|
||||||
G_VARIANT_TYPE("(a{oa{sa{sv}}})"),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
nullptr,
|
|
||||||
&error);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "Error calling GetManagedObjects: " << error->message << std::endl;
|
|
||||||
g_error_free(error);
|
|
||||||
return device_paths;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!reply) {
|
|
||||||
return device_paths;
|
|
||||||
}
|
|
||||||
|
|
||||||
GVariant *objects = g_variant_get_child_value(reply, 0);
|
|
||||||
g_variant_unref(reply);
|
|
||||||
|
|
||||||
if (!objects) {
|
|
||||||
return device_paths;
|
|
||||||
}
|
|
||||||
|
|
||||||
GVariantIter iter;
|
|
||||||
g_variant_iter_init(&iter, objects);
|
|
||||||
|
|
||||||
const gchar *object_path = nullptr;
|
|
||||||
GVariant *interfaces = nullptr;
|
|
||||||
while (g_variant_iter_next(&iter, "{&o@a{sa{sv}}}", &object_path, &interfaces)) {
|
|
||||||
GVariant *device_props = nullptr;
|
|
||||||
if (g_variant_lookup(interfaces, "org.bluez.Device1", "@a{sv}", &device_props)) {
|
|
||||||
device_paths.emplace_back(object_path);
|
|
||||||
g_variant_unref(device_props);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_variant_unref(interfaces);
|
|
||||||
interfaces = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_variant_unref(objects);
|
|
||||||
return device_paths;
|
|
||||||
}
|
|
||||||
@@ -13,34 +13,38 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "helpers/hypr.hpp"
|
#include "helpers/hypr.hpp"
|
||||||
#include "helpers/socket.hpp"
|
#include "helpers/string.hpp"
|
||||||
|
|
||||||
#include "gtkmm/box.h"
|
#include "gtkmm/box.h"
|
||||||
#include "gtkmm/button.h"
|
|
||||||
|
|
||||||
HyprlandService::HyprlandService() {
|
HyprlandService::HyprlandService() {
|
||||||
auto monitorDataJson = HyprctlHelper::getMonitorData();
|
init();
|
||||||
|
bindSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandService::init() {
|
||||||
|
auto monitorDataJson = HyprctlHelper::getMonitorData();
|
||||||
|
auto onClick = sigc::mem_fun(*this, &HyprlandService::switchToWorkspace);
|
||||||
|
|
||||||
for (const auto &item : monitorDataJson) {
|
for (const auto &item : monitorDataJson) {
|
||||||
auto monitorPtr = std::make_shared<Monitor>();
|
auto monitorPtr = std::make_shared<Monitor>();
|
||||||
|
|
||||||
|
std::string monitorName = item["name"].get<std::string>();
|
||||||
int monitorId = item["id"].get<int>();
|
int monitorId = item["id"].get<int>();
|
||||||
|
|
||||||
monitorPtr->id = monitorId;
|
monitorPtr->id = monitorId;
|
||||||
monitorPtr->name = item["name"].get<std::string>();
|
monitorPtr->name = monitorName;
|
||||||
this->monitors[monitorPtr->id] = monitorPtr;
|
monitorPtr->activeWorkspaceId = item["activeWorkspace"]["id"].get<int>();
|
||||||
|
monitorPtr->focused = item["focused"].get<bool>();
|
||||||
|
this->monitors[monitorPtr->name] = monitorPtr;
|
||||||
|
|
||||||
// Create inidcators for hyprsplit
|
|
||||||
for (int i = 1; i <= NUM_WORKSPACES; i++) {
|
for (int i = 1; i <= NUM_WORKSPACES; i++) {
|
||||||
WorkspaceState state;
|
std::shared_ptr<WorkspaceData> state = std::make_shared<WorkspaceData>();
|
||||||
int workspaceId = i + (NUM_WORKSPACES * monitorId);
|
int workspaceId = i + (NUM_WORKSPACES * monitorId);
|
||||||
|
|
||||||
state.id = workspaceId;
|
state->id = workspaceId;
|
||||||
state.monitorId = monitorId;
|
state->monitorName = monitorName;
|
||||||
|
auto view = std::make_shared<WorkspaceIndicator>(workspaceId, std::to_string(i), onClick);
|
||||||
auto view = std::make_shared<WorkspaceIndicator>(std::to_string(i));
|
|
||||||
|
|
||||||
auto workSpace = std::make_shared<Workspace>();
|
auto workSpace = std::make_shared<Workspace>();
|
||||||
workSpace->state = state;
|
workSpace->state = state;
|
||||||
workSpace->view = view;
|
workSpace->view = view;
|
||||||
@@ -50,30 +54,38 @@ HyprlandService::HyprlandService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto clientsDataJson = HyprctlHelper::getClientData();
|
||||||
|
|
||||||
|
for (const auto &client : clientsDataJson) {
|
||||||
|
auto clientPtr = std::make_shared<Client>();
|
||||||
|
clientPtr->address = client["address"].get<std::string>();
|
||||||
|
clientPtr->workspaceId = client["workspace"]["id"].get<int>();
|
||||||
|
clientPtr->title = client["title"].get<std::string>();
|
||||||
|
this->clients[clientPtr->address] = clientPtr;
|
||||||
|
|
||||||
|
auto workspacePtr = workspaces[clientPtr->workspaceId];
|
||||||
|
workspacePtr->state->clients[clientPtr->address] = clientPtr;
|
||||||
|
|
||||||
|
if (client.contains("urgent") && client["urgent"].get<bool>()) {
|
||||||
|
workspacePtr->state->urgentClients.insert(clientPtr->address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto workspaceDataJson = HyprctlHelper::getWorkspaceData();
|
auto workspaceDataJson = HyprctlHelper::getWorkspaceData();
|
||||||
|
|
||||||
for (const auto &workspace : workspaceDataJson) {
|
for (const auto &workspace : workspaceDataJson) {
|
||||||
auto workspacePtr = std::make_shared<Workspace>();
|
auto workspacePtr = workspaces[workspace["id"].get<int>()];
|
||||||
workspacePtr->state.id = workspace["id"].get<int>();
|
auto state = workspacePtr->state;
|
||||||
workspacePtr->state.monitorId = workspace["monitorID"].get<int>();
|
|
||||||
workspaces[workspacePtr->state.id] = workspacePtr;
|
state->id = workspace["id"].get<int>();
|
||||||
|
state->monitorName = workspace["monitor"].get<std::string>();
|
||||||
|
|
||||||
|
refreshIndicator(workspacePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bindSocket();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HyprlandService::init() {
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string HyprlandService::getSocketPath() {
|
|
||||||
auto sig = std::string(std::getenv("HYPRLAND_INSTANCE_SIGNATURE"));
|
|
||||||
auto runtime = std::string(std::getenv("XDG_RUNTIME_DIR"));
|
|
||||||
|
|
||||||
return std::string(runtime) + "/hypr/" + sig + "/.socket2.sock";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprlandService::bindSocket() {
|
void HyprlandService::bindSocket() {
|
||||||
std::string socketPath = getSocketPath();
|
std::string socketPath = HyprSocketHelper::getHyprlandSocketPath();
|
||||||
|
|
||||||
socketFd = socket(AF_UNIX, SOCK_STREAM, 0);
|
socketFd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
if (socketFd == -1) {
|
if (socketFd == -1) {
|
||||||
@@ -96,29 +108,220 @@ void HyprlandService::bindSocket() {
|
|||||||
|
|
||||||
GSource *source = g_unix_fd_source_new(socketFd, static_cast<GIOCondition>(G_IO_IN | G_IO_HUP | G_IO_ERR));
|
GSource *source = g_unix_fd_source_new(socketFd, static_cast<GIOCondition>(G_IO_IN | G_IO_HUP | G_IO_ERR));
|
||||||
|
|
||||||
auto onSocketEvent = [](gint fd, GIOCondition condition, gpointer user_data) -> gboolean {
|
auto onSocketEvent = [](gint fd, GIOCondition , gpointer user_data) -> gboolean {
|
||||||
HyprlandService *self = static_cast<HyprlandService *>(user_data);
|
HyprlandService *self = static_cast<HyprlandService *>(user_data);
|
||||||
auto messages = SocketHelper::parseSocketMessage(fd, ">>");
|
auto messages = SocketHelper::parseSocketMessage(fd, ">>");
|
||||||
|
|
||||||
|
for (const auto &message : messages) {
|
||||||
|
self->handleSocketMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
};
|
};
|
||||||
|
|
||||||
g_source_set_callback(source, (GSourceFunc)(+onSocketEvent), this, nullptr);
|
g_source_set_callback(source, reinterpret_cast<GSourceFunc>(reinterpret_cast<void*>(+onSocketEvent)), this, nullptr);
|
||||||
g_source_attach(source, g_main_context_default());
|
g_source_attach(source, g_main_context_default());
|
||||||
g_source_unref(source);
|
g_source_unref(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Gtk::Box> HyprlandService::getWorkspaceIndicatorsForMonitor(int monitorId) {
|
void HyprlandService::onWorkspaceChanged(int workspaceId) {
|
||||||
auto box = std::make_shared<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
|
auto newActive = workspaces[workspaceId];
|
||||||
|
auto state = newActive->state;
|
||||||
|
auto monitorPtr = monitors[state->monitorName];
|
||||||
|
|
||||||
auto monitor = monitors[monitorId];
|
int oldActiveWorkspaceId = monitorPtr->activeWorkspaceId;
|
||||||
|
auto oldActive = workspaces[oldActiveWorkspaceId];
|
||||||
|
|
||||||
int i = 0;
|
monitorPtr->activeWorkspaceId = workspaceId;
|
||||||
for (const auto &[wsId, wsPair] : monitor->monitorWorkspaces) {
|
|
||||||
box->append((Gtk::Box &)*wsPair->view);
|
refreshIndicator(newActive);
|
||||||
i++;
|
refreshIndicator(oldActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HyprlandService::onFocusedMonitorChanged(std::string monitorData) {
|
||||||
|
std::string monitorName = StringHelper::split(monitorData, ',')[0];
|
||||||
|
|
||||||
|
for (const auto &[_, monitorPtr] : monitors) {
|
||||||
|
std::string itMonitorName = monitorPtr->name;
|
||||||
|
|
||||||
|
if (itMonitorName == monitorName) {
|
||||||
|
monitorPtr->focused = true;
|
||||||
|
|
||||||
|
int activeWorkspaceId = monitorPtr->activeWorkspaceId;
|
||||||
|
auto activeWorkspace = workspaces[activeWorkspaceId];
|
||||||
|
refreshIndicator(activeWorkspace);
|
||||||
|
} else {
|
||||||
|
monitorPtr->focused = false;
|
||||||
|
|
||||||
|
int activeWorkspaceId = monitorPtr->activeWorkspaceId;
|
||||||
|
auto activeWorkspace = workspaces[activeWorkspaceId];
|
||||||
|
refreshIndicator(activeWorkspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandService::onMonitorRemoved(std::string monitorName) {
|
||||||
|
auto monitorPtr = this->monitors[monitorName];
|
||||||
|
|
||||||
|
for (const auto &[wsId, wsPtr] : monitorPtr->monitorWorkspaces) {
|
||||||
|
this->workspaces.erase(wsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
monitorPtr->monitorWorkspaces.clear();
|
||||||
|
monitorPtr->bar->close();
|
||||||
|
|
||||||
|
this->monitors.erase(monitorName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// void HyprlandService::onMonitorAdded(std::string monitorName) {
|
||||||
|
// // this->signalMonitorAdded.emit();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
void HyprlandService::onOpenWindow(std::string windowData) {
|
||||||
|
auto parts = StringHelper::split(windowData, ',');
|
||||||
|
std::string addr = "0x" + parts[0];
|
||||||
|
int workspaceId = std::stoi(parts[1]);
|
||||||
|
std::string title = parts[2];
|
||||||
|
|
||||||
|
auto clientPtr = std::make_shared<Client>();
|
||||||
|
clientPtr->address = addr;
|
||||||
|
clientPtr->workspaceId = workspaceId;
|
||||||
|
clientPtr->title = title;
|
||||||
|
this->clients[clientPtr->address] = clientPtr;
|
||||||
|
auto workspacePtr = workspaces[clientPtr->workspaceId];
|
||||||
|
workspacePtr->state->clients[clientPtr->address] = clientPtr;
|
||||||
|
|
||||||
|
refreshIndicator(workspacePtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandService::onCloseWindow(std::string windowData) {
|
||||||
|
auto parts = StringHelper::split(windowData, ',');
|
||||||
|
std::string addr = "0x" + parts[0];
|
||||||
|
|
||||||
|
if (this->clients.find(addr) == this->clients.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto clientPtr = this->clients[addr];
|
||||||
|
int workspaceId = clientPtr->workspaceId;
|
||||||
|
|
||||||
|
auto workspacePtr = workspaces[workspaceId];
|
||||||
|
workspacePtr->state->clients.erase(addr);
|
||||||
|
this->clients.erase(addr);
|
||||||
|
|
||||||
|
refreshIndicator(workspacePtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandService::handleSocketMessage(SocketHelper::SocketMessage message) {
|
||||||
|
if (socketEventTypeMap.find(message.eventType) == socketEventTypeMap.end()) {
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketEventType eventType = socketEventTypeMap[message.eventType];
|
||||||
|
std::string eventData = message.eventData;
|
||||||
|
|
||||||
|
switch (eventType) {
|
||||||
|
case FOCUSED_MONITOR: {
|
||||||
|
onFocusedMonitorChanged(eventData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WORKSPACE_CHANGED: {
|
||||||
|
this->onWorkspaceChanged(std::stoi(eventData));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OPEN_WINDOW: {
|
||||||
|
this->onOpenWindow(eventData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLOSE_WINDOW: {
|
||||||
|
this->onCloseWindow(eventData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case URGENT: {
|
||||||
|
this->onUrgent(eventData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ACTIVE_WINDOW: {
|
||||||
|
this->onActiveWindowChanged(eventData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MONITOR_REMOVED: {
|
||||||
|
this->onMonitorRemoved(eventData);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void HyprlandService::onUrgent(std::string windowAddress) {
|
||||||
|
std::string addr = "0x" + windowAddress;
|
||||||
|
if (this->clients.find(addr) == this->clients.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto clientPtr = this->clients[addr];
|
||||||
|
int workspaceId = clientPtr->workspaceId;
|
||||||
|
|
||||||
|
auto workspacePtr = workspaces[workspaceId];
|
||||||
|
workspacePtr->state->urgentClients.insert(addr);
|
||||||
|
|
||||||
|
refreshIndicator(workspacePtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandService::onActiveWindowChanged(std::string windowAddress) {
|
||||||
|
std::string addr = "0x" + windowAddress;
|
||||||
|
if (this->clients.find(addr) == this->clients.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto clientPtr = this->clients[addr];
|
||||||
|
int workspaceId = clientPtr->workspaceId;
|
||||||
|
|
||||||
|
auto workspacePtr = workspaces[workspaceId];
|
||||||
|
workspacePtr->state->urgentClients.erase(addr);
|
||||||
|
|
||||||
|
refreshIndicator(workspacePtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Gtk::Box> HyprlandService::getWorkspaceIndicatorsForMonitor(std::string monitorName) {
|
||||||
|
auto box = std::make_shared<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
|
||||||
|
auto monitor = monitors[monitorName];
|
||||||
|
|
||||||
|
for (const auto &[wsId, wsPair] : monitor->monitorWorkspaces) {
|
||||||
|
box->append((Gtk::Box &)*wsPair->view);
|
||||||
|
}
|
||||||
|
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HyprlandService::switchToWorkspace(int workspaceId) {
|
||||||
|
HyprctlHelper::dispatchWorkspace(workspaceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandService::refreshIndicator(std::shared_ptr<Workspace> workspace) {
|
||||||
|
auto view = workspace->view;
|
||||||
|
|
||||||
|
auto state = workspace->state;
|
||||||
|
auto monitorsPtr = monitors[state->monitorName];
|
||||||
|
|
||||||
|
bool isUrgent = !state->urgentClients.empty();
|
||||||
|
bool isEmpty = state->clients.empty();
|
||||||
|
bool isPreseneting = state->id == monitorsPtr->activeWorkspaceId;
|
||||||
|
bool isFocused = monitorsPtr->focused && isPreseneting;
|
||||||
|
|
||||||
|
if (isUrgent) {
|
||||||
|
view->setIndicatorState(WorkspaceIndicator::URGENT);
|
||||||
|
} else if (isFocused) {
|
||||||
|
view->setIndicatorState(WorkspaceIndicator::FOCUSED);
|
||||||
|
} else if (isPreseneting) {
|
||||||
|
view->setIndicatorState(WorkspaceIndicator::PRESENTING);
|
||||||
|
} else if (!isEmpty) {
|
||||||
|
view->setIndicatorState(WorkspaceIndicator::ALIVE);
|
||||||
|
} else {
|
||||||
|
view->setIndicatorState(WorkspaceIndicator::EMPTY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandService::addBar(std::shared_ptr<Bar> bar, std::string monitorName) {
|
||||||
|
this->monitors[monitorName]->bar = bar;
|
||||||
|
}
|
||||||
@@ -1,305 +0,0 @@
|
|||||||
#include "services/notifications.hpp"
|
|
||||||
|
|
||||||
#include <gio/gio.h>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
static constexpr const char *kNotificationsObjectPath = "/org/freedesktop/Notifications";
|
|
||||||
static constexpr const char *kNotificationsInterface = "org.freedesktop.Notifications";
|
|
||||||
|
|
||||||
static const char *kNotificationsIntrospectionXml = R"XML(
|
|
||||||
<node>
|
|
||||||
<interface name="org.freedesktop.Notifications">
|
|
||||||
<method name="Notify">
|
|
||||||
<arg type="s" direction="in"/>
|
|
||||||
<arg type="u" direction="in"/>
|
|
||||||
<arg type="s" direction="in"/>
|
|
||||||
<arg type="s" direction="in"/>
|
|
||||||
<arg type="s" direction="in"/>
|
|
||||||
<arg type="as" direction="in"/>
|
|
||||||
<arg type="a{sv}" direction="in"/>
|
|
||||||
<arg type="i" direction="in"/>
|
|
||||||
<arg type="u" direction="out"/>
|
|
||||||
</method>
|
|
||||||
<method name="CloseNotification">
|
|
||||||
<arg type="u" direction="in"/>
|
|
||||||
</method>
|
|
||||||
<method name="GetCapabilities">
|
|
||||||
<arg type="as" direction="out"/>
|
|
||||||
</method>
|
|
||||||
<method name="GetServerInformation">
|
|
||||||
<arg type="s" direction="out"/>
|
|
||||||
<arg type="s" direction="out"/>
|
|
||||||
<arg type="s" direction="out"/>
|
|
||||||
<arg type="s" direction="out"/>
|
|
||||||
</method>
|
|
||||||
<signal name="NotificationClosed">
|
|
||||||
<arg type="u"/>
|
|
||||||
<arg type="u"/>
|
|
||||||
</signal>
|
|
||||||
<signal name="ActionInvoked">
|
|
||||||
<arg type="u"/>
|
|
||||||
<arg type="s"/>
|
|
||||||
</signal>
|
|
||||||
<signal name="ActivationToken">
|
|
||||||
<arg type="u"/>
|
|
||||||
<arg type="s"/>
|
|
||||||
</signal>
|
|
||||||
</interface>
|
|
||||||
</node>)XML";
|
|
||||||
|
|
||||||
static void on_method_call(GDBusConnection * /*connection*/,
|
|
||||||
const gchar * /*sender*/,
|
|
||||||
const gchar * /*object_path*/,
|
|
||||||
const gchar *interface_name,
|
|
||||||
const gchar *method_name,
|
|
||||||
GVariant *parameters,
|
|
||||||
GDBusMethodInvocation *invocation,
|
|
||||||
gpointer user_data) {
|
|
||||||
auto *self = static_cast<NotificationService *>(user_data);
|
|
||||||
|
|
||||||
if (g_strcmp0(interface_name, kNotificationsInterface) != 0) {
|
|
||||||
g_dbus_method_invocation_return_dbus_error(
|
|
||||||
invocation,
|
|
||||||
"org.freedesktop.DBus.Error.UnknownInterface",
|
|
||||||
"Unknown interface");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_strcmp0(method_name, "Notify") == 0) {
|
|
||||||
const gchar *app_name = "";
|
|
||||||
guint32 replaces_id = 0;
|
|
||||||
const gchar *app_icon = "";
|
|
||||||
const gchar *summary = "";
|
|
||||||
const gchar *body = "";
|
|
||||||
GVariant *actions = nullptr;
|
|
||||||
GVariant *hints = nullptr;
|
|
||||||
gint32 expire_timeout = -1;
|
|
||||||
|
|
||||||
g_variant_get(parameters, "(&su&s&s&s@as@a{sv}i)",
|
|
||||||
&app_name,
|
|
||||||
&replaces_id,
|
|
||||||
&app_icon,
|
|
||||||
&summary,
|
|
||||||
&body,
|
|
||||||
&actions,
|
|
||||||
&hints,
|
|
||||||
&expire_timeout);
|
|
||||||
|
|
||||||
std::cout << "--- Notification ---" << std::endl;
|
|
||||||
std::cout << "App: " << (app_name ? app_name : "") << std::endl;
|
|
||||||
std::cout << "Title: " << (summary ? summary : "") << std::endl;
|
|
||||||
std::cout << "Body: " << (body ? body : "") << std::endl;
|
|
||||||
|
|
||||||
if (actions)
|
|
||||||
g_variant_unref(actions);
|
|
||||||
if (hints)
|
|
||||||
g_variant_unref(hints);
|
|
||||||
|
|
||||||
guint32 id = self->allocateNotificationId(replaces_id);
|
|
||||||
g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", id));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_strcmp0(method_name, "GetCapabilities") == 0) {
|
|
||||||
// Advertise common capabilities so clients don't disable notifications.
|
|
||||||
// (Many apps probe this first and may skip Notify if it's empty.)
|
|
||||||
const gchar *caps[] = {
|
|
||||||
"body",
|
|
||||||
"actions",
|
|
||||||
"body-markup",
|
|
||||||
"icon-static",
|
|
||||||
"persistence",
|
|
||||||
nullptr};
|
|
||||||
GVariant *capsV = g_variant_new_strv(caps, -1);
|
|
||||||
g_dbus_method_invocation_return_value(invocation, g_variant_new("(@as)", capsV));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_strcmp0(method_name, "GetServerInformation") == 0) {
|
|
||||||
g_dbus_method_invocation_return_value(
|
|
||||||
invocation,
|
|
||||||
g_variant_new("(ssss)", "bar", "bar", "0.1", "1.2"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_strcmp0(method_name, "CloseNotification") == 0) {
|
|
||||||
guint32 id = 0;
|
|
||||||
g_variant_get(parameters, "(u)", &id);
|
|
||||||
|
|
||||||
// reason: 3 = closed by call to CloseNotification
|
|
||||||
if (self && self->getConnection()) {
|
|
||||||
g_dbus_connection_emit_signal(
|
|
||||||
self->getConnection(),
|
|
||||||
nullptr,
|
|
||||||
kNotificationsObjectPath,
|
|
||||||
kNotificationsInterface,
|
|
||||||
"NotificationClosed",
|
|
||||||
g_variant_new("(uu)", id, 3u),
|
|
||||||
nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_dbus_method_invocation_return_value(invocation, nullptr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_dbus_method_invocation_return_dbus_error(
|
|
||||||
invocation,
|
|
||||||
"org.freedesktop.DBus.Error.UnknownMethod",
|
|
||||||
"Unknown method");
|
|
||||||
}
|
|
||||||
|
|
||||||
guint32 NotificationService::allocateNotificationId(guint32 replacesId) {
|
|
||||||
if (replacesId != 0)
|
|
||||||
return replacesId;
|
|
||||||
return this->nextNotificationId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const GDBusInterfaceVTable kVTable = {
|
|
||||||
.method_call = on_method_call,
|
|
||||||
.get_property = nullptr,
|
|
||||||
.set_property = nullptr,
|
|
||||||
};
|
|
||||||
|
|
||||||
NotificationService::~NotificationService() {
|
|
||||||
if (this->connection) {
|
|
||||||
// Best-effort release of the well-known name.
|
|
||||||
{
|
|
||||||
GError *error = nullptr;
|
|
||||||
GVariant *releaseResult = g_dbus_connection_call_sync(
|
|
||||||
this->connection,
|
|
||||||
"org.freedesktop.DBus",
|
|
||||||
"/org/freedesktop/DBus",
|
|
||||||
"org.freedesktop.DBus",
|
|
||||||
"ReleaseName",
|
|
||||||
g_variant_new("(s)", "org.freedesktop.Notifications"),
|
|
||||||
G_VARIANT_TYPE("(u)"),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
nullptr,
|
|
||||||
&error);
|
|
||||||
if (releaseResult)
|
|
||||||
g_variant_unref(releaseResult);
|
|
||||||
if (error)
|
|
||||||
g_error_free(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->registrationId != 0) {
|
|
||||||
g_dbus_connection_unregister_object(this->connection, this->registrationId);
|
|
||||||
this->registrationId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_object_unref(this->connection);
|
|
||||||
this->connection = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->nodeInfo) {
|
|
||||||
g_dbus_node_info_unref(this->nodeInfo);
|
|
||||||
this->nodeInfo = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationService::intialize() {
|
|
||||||
GError *error = nullptr;
|
|
||||||
|
|
||||||
if (this->connection) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gchar *address = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, nullptr, &error);
|
|
||||||
if (!address) {
|
|
||||||
std::cerr << "Failed to get session bus address: " << (error ? error->message : "unknown error") << std::endl;
|
|
||||||
if (error)
|
|
||||||
g_error_free(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
g_error_free(error);
|
|
||||||
error = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->connection = g_dbus_connection_new_for_address_sync(
|
|
||||||
address,
|
|
||||||
static_cast<GDBusConnectionFlags>(
|
|
||||||
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
|
|
||||||
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION),
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
&error);
|
|
||||||
g_free(address);
|
|
||||||
|
|
||||||
if (!this->connection) {
|
|
||||||
std::cerr << "Failed to connect to session bus: " << (error ? error->message : "unknown error") << std::endl;
|
|
||||||
if (error)
|
|
||||||
g_error_free(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
g_error_free(error);
|
|
||||||
error = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->nodeInfo = g_dbus_node_info_new_for_xml(kNotificationsIntrospectionXml, &error);
|
|
||||||
if (!this->nodeInfo) {
|
|
||||||
std::cerr << "Failed to create introspection data: " << (error ? error->message : "unknown error") << std::endl;
|
|
||||||
if (error)
|
|
||||||
g_error_free(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GDBusInterfaceInfo *iface = g_dbus_node_info_lookup_interface(this->nodeInfo, kNotificationsInterface);
|
|
||||||
if (!iface) {
|
|
||||||
std::cerr << "Missing interface info for org.freedesktop.Notifications" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->registrationId = g_dbus_connection_register_object(
|
|
||||||
this->connection,
|
|
||||||
kNotificationsObjectPath,
|
|
||||||
iface,
|
|
||||||
&kVTable,
|
|
||||||
this,
|
|
||||||
nullptr,
|
|
||||||
&error);
|
|
||||||
|
|
||||||
if (this->registrationId == 0) {
|
|
||||||
std::cerr << "Failed to register notifications object: " << (error ? error->message : "unknown error") << std::endl;
|
|
||||||
if (error)
|
|
||||||
g_error_free(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request the well-known name synchronously so we can detect conflicts.
|
|
||||||
// Reply codes: 1=PRIMARY_OWNER, 2=IN_QUEUE, 3=EXISTS, 4=ALREADY_OWNER
|
|
||||||
GVariant *requestResult = g_dbus_connection_call_sync(
|
|
||||||
this->connection,
|
|
||||||
"org.freedesktop.DBus",
|
|
||||||
"/org/freedesktop/DBus",
|
|
||||||
"org.freedesktop.DBus",
|
|
||||||
"RequestName",
|
|
||||||
g_variant_new("(su)", "org.freedesktop.Notifications", 0u),
|
|
||||||
G_VARIANT_TYPE("(u)"),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
nullptr,
|
|
||||||
&error);
|
|
||||||
|
|
||||||
if (!requestResult) {
|
|
||||||
std::cerr << "Failed to RequestName(org.freedesktop.Notifications): "
|
|
||||||
<< (error ? error->message : "unknown error") << std::endl;
|
|
||||||
if (error)
|
|
||||||
g_error_free(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
guint32 reply = 0;
|
|
||||||
g_variant_get(requestResult, "(u)", &reply);
|
|
||||||
g_variant_unref(requestResult);
|
|
||||||
|
|
||||||
if (reply != 1u && reply != 4u) {
|
|
||||||
std::cerr << "org.freedesktop.Notifications is already owned (RequestName reply=" << reply
|
|
||||||
<< "). Stop your existing notification daemon (e.g. dunst/mako/swaync) or allow replacement." << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
#include "widgets/bluetooth.hpp"
|
|
||||||
#include "gtkmm/label.h"
|
|
||||||
|
|
||||||
|
|
||||||
BluetoothWidget::BluetoothWidget() : Gtk::Box() {
|
|
||||||
this->set_orientation(Gtk::Orientation::VERTICAL);
|
|
||||||
this->add_css_class("bluetooth-popover-container");
|
|
||||||
|
|
||||||
this->statusArea.add_css_class("bluetooth-status-area");
|
|
||||||
this->statusArea.set_hexpand(true);
|
|
||||||
this->statusArea.set_halign(Gtk::Align::FILL);
|
|
||||||
this->append(this->statusArea);
|
|
||||||
|
|
||||||
this->powerButton = Gtk::make_managed<Button>("\ue1a8");
|
|
||||||
this->powerButton->set_tooltip_text("Turn Bluetooth Off");
|
|
||||||
this->powerButton->signal_clicked().connect(sigc::mem_fun(*this, &BluetoothWidget::onPowerButtonClicked));
|
|
||||||
this->powerButton->add_css_class("toggle-button");
|
|
||||||
|
|
||||||
this->scanButton = Gtk::make_managed<Button>("\ue1aa");
|
|
||||||
this->scanButton->set_tooltip_text("Scan for Devices");
|
|
||||||
this->scanButton->add_css_class("toggle-button");
|
|
||||||
this->scanButton->signal_clicked().connect(sigc::mem_fun(*this, &BluetoothWidget::onScanButtonClicked));
|
|
||||||
|
|
||||||
this->statusArea.append(*this->powerButton);
|
|
||||||
this->statusArea.append(*this->scanButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BluetoothWidget::onPowerButtonClicked() {
|
|
||||||
onPowerStateButtonClickedSignal.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BluetoothWidget::onScanButtonClicked() {
|
|
||||||
onIsDiscoveringButtonClickedSignal.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BluetoothWidget::toggleButton(Button *button, bool state) {
|
|
||||||
if (state) {
|
|
||||||
button->add_css_class("toggle-button-on");
|
|
||||||
button->remove_css_class("toggle-button-off");
|
|
||||||
} else {
|
|
||||||
button->add_css_class("toggle-button-off");
|
|
||||||
button->remove_css_class("toggle-button-on");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BluetoothWidget::setPowerState(bool state) {
|
|
||||||
this->isPowered = state;
|
|
||||||
|
|
||||||
this->scanButton->set_sensitive(state);
|
|
||||||
|
|
||||||
if (!state) {
|
|
||||||
this->scanButton->add_css_class("toggle-button-disabled");
|
|
||||||
// this->add_css_class("disabled-popover-icon");
|
|
||||||
this->setIsDiscovering(false);
|
|
||||||
} else {
|
|
||||||
this->scanButton->remove_css_class("toggle-button-disabled");
|
|
||||||
// this->remove_css_class("disabled-popover-icon");
|
|
||||||
}
|
|
||||||
|
|
||||||
this->toggleButton(this->powerButton, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BluetoothWidget::setIsDiscovering(bool state) {
|
|
||||||
this->isDiscovering = state;
|
|
||||||
|
|
||||||
this->toggleButton(this->scanButton, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BluetoothWidget::update() {
|
|
||||||
setPowerState(isPowered);
|
|
||||||
setIsDiscovering(isDiscovering);
|
|
||||||
}
|
|
||||||
@@ -5,20 +5,4 @@ ControlCenter::ControlCenter(std::string icon, std::string name)
|
|||||||
this->popover->set_size_request(200, -1);
|
this->popover->set_size_request(200, -1);
|
||||||
set_popover_child(this->container);
|
set_popover_child(this->container);
|
||||||
|
|
||||||
this->bluetoothWidget = Gtk::make_managed<BluetoothWidget>();
|
|
||||||
this->container.append(*this->bluetoothWidget);
|
|
||||||
|
|
||||||
bluetoothService->powerStateChangedSignal.connect(
|
|
||||||
sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setPowerState));
|
|
||||||
|
|
||||||
bluetoothWidget->onPowerStateButtonClickedSignal.connect(
|
|
||||||
sigc::mem_fun(*this->bluetoothService, &BluetoothService::togglePowerState));
|
|
||||||
|
|
||||||
bluetoothWidget->setPowerState(bluetoothService->getPowerState());
|
|
||||||
|
|
||||||
bluetoothService->isDiscoveringChangedSignal.connect(
|
|
||||||
sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setIsDiscovering));
|
|
||||||
bluetoothWidget->onIsDiscoveringButtonClickedSignal.connect(
|
|
||||||
sigc::mem_fun(*this->bluetoothService, &BluetoothService::toggleIsDiscovering));
|
|
||||||
bluetoothWidget->setIsDiscovering(bluetoothService->getIsDiscovering());
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user