make bluetooth service singleton

This commit is contained in:
2025-12-23 19:57:52 +01:00
parent 8613024f8d
commit a06c96f648
14 changed files with 148 additions and 65 deletions

View File

@@ -39,6 +39,7 @@ target_sources(bar_lib
src/widgets/tray.cpp src/widgets/tray.cpp
src/widgets/todo.cpp src/widgets/todo.cpp
src/widgets/bluetooth.cpp src/widgets/bluetooth.cpp
src/widgets/controlCenter.cpp
src/components/popover.cpp src/components/popover.cpp
src/components/todoEntry.cpp src/components/todoEntry.cpp

View File

@@ -3,7 +3,6 @@
#include <vector> #include <vector>
#include "bar/bar.hpp" #include "bar/bar.hpp"
#include "services/bluetooth.hpp"
#include "services/hyprland.hpp" #include "services/hyprland.hpp"
#include "services/notifications.hpp" #include "services/notifications.hpp"
#include "services/tray.hpp" #include "services/tray.hpp"
@@ -23,7 +22,6 @@ class App {
HyprlandService hyprlandService; HyprlandService hyprlandService;
NotificationService notificationService; NotificationService notificationService;
TrayService trayService; TrayService trayService;
BluetoothService bluetoothService;
void setupServices(); void setupServices();
}; };

View File

@@ -4,20 +4,19 @@
#include <gtkmm.h> #include <gtkmm.h>
#include "icons.hpp" #include "icons.hpp"
#include "services/bluetooth.hpp"
#include "services/hyprland.hpp" #include "services/hyprland.hpp"
#include "services/tray.hpp" #include "services/tray.hpp"
#include "widgets/bluetooth.hpp"
#include "widgets/clock.hpp" #include "widgets/clock.hpp"
#include "widgets/date.hpp" #include "widgets/date.hpp"
#include "widgets/tray.hpp" #include "widgets/tray.hpp"
#include "widgets/volumeWidget.hpp" #include "widgets/volumeWidget.hpp"
#include "widgets/webWidget.hpp" #include "widgets/webWidget.hpp"
#include "widgets/workspaceIndicator.hpp" #include "widgets/workspaceIndicator.hpp"
#include "widgets/controlCenter.hpp"
class Bar : public Gtk::Window { class Bar : public Gtk::Window {
public: public:
Bar(GdkMonitor *monitor, HyprlandService &hyprlandService, TrayService &trayService, BluetoothService &bluetoothService, int monitorId); Bar(GdkMonitor *monitor, HyprlandService &hyprlandService, TrayService &trayService, int monitorId);
protected: protected:
Gtk::CenterBox main_box{}; Gtk::CenterBox main_box{};
@@ -31,15 +30,14 @@ class Bar : public Gtk::Window {
Clock clock; Clock clock;
Date date; Date date;
WebWidget homeAssistant{ICON_HOME, "Home Assistant", "https://home.rivercry.com"}; WebWidget homeAssistant{ICON_HOME, "Home Assistant", "https://home.rivercry.com"};
ControlCenter controlCenter{"\ue8bb", "Control Center"};
WorkspaceIndicator *workspaceIndicator = nullptr; WorkspaceIndicator *workspaceIndicator = nullptr;
TrayWidget *trayWidget = nullptr; TrayWidget *trayWidget = nullptr;
VolumeWidget *volumeWidget = nullptr; VolumeWidget *volumeWidget = nullptr;
BluetoothWidget *bluetoothWidget = nullptr;
TrayService &trayService; TrayService &trayService;
HyprlandService &hyprlandService; HyprlandService &hyprlandService;
BluetoothService &bluetoothService;
void setup_ui(); void setup_ui();
void setup_left_box(); void setup_left_box();

View File

@@ -2,14 +2,17 @@
#include <gio/gio.h> #include <gio/gio.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <string>
#include <vector>
#include "sigc++/signal.h" #include "sigc++/signal.h"
class BluetoothService { class BluetoothService {
public: static BluetoothService *instance;
BluetoothService();
public:
sigc::signal<void(bool)> powerStateChangedSignal; sigc::signal<void(bool)> powerStateChangedSignal;
sigc::signal<void(bool)> isDiscoveringChangedSignal; sigc::signal<void(bool)> isDiscoveringChangedSignal;
bool getPowerState(); bool getPowerState();
@@ -19,8 +22,20 @@ class BluetoothService {
void togglePowerState(); void togglePowerState();
void toggleIsDiscovering(); void toggleIsDiscovering();
private: static BluetoothService *getInstance() {
if (BluetoothService::instance == nullptr) {
BluetoothService::instance = new BluetoothService();
}
return BluetoothService::instance;
}
private:
BluetoothService();
GDBusProxy *adapter_proxy = nullptr; GDBusProxy *adapter_proxy = nullptr;
std::vector<std::string> getDeviceObjectPaths();
bool powerState = false; bool powerState = false;
bool isDiscovering = false; bool isDiscovering = false;

View File

@@ -1,14 +1,11 @@
#pragma once #pragma once
#include <gtkmm/box.h> #include <gtkmm/box.h>
#include <gtkmm/button.h>
#include "components/popover.hpp" class BluetoothWidget : public Gtk::Box {
#include "gtkmm/button.h"
class BluetoothWidget : public Popover {
public: public:
BluetoothWidget(std::string icon, std::string title); BluetoothWidget();
void setPowerState(bool state); void setPowerState(bool state);
void setIsDiscovering(bool state); void setIsDiscovering(bool state);
@@ -17,19 +14,16 @@ class BluetoothWidget : public Popover {
sigc::signal<void()> onIsDiscoveringButtonClickedSignal; sigc::signal<void()> onIsDiscoveringButtonClickedSignal;
void update(); void update();
private: private:
bool isPowered = false; bool isPowered = false;
bool isDiscovering = false; bool isDiscovering = false;
Gtk::Box container;
Gtk::Box statusArea; Gtk::Box statusArea;
Gtk::Box *deviceList = nullptr; Gtk::Box *deviceList = nullptr;
Gtk::Button *scanButton = nullptr; Gtk::Button *scanButton = nullptr;
Gtk::Button *powerButton = nullptr; Gtk::Button *powerButton = nullptr;
void onPowerButtonClicked(); void onPowerButtonClicked();
void onScanButtonClicked(); void onScanButtonClicked();

View File

@@ -0,0 +1,17 @@
#pragma once
#include "components/popover.hpp"
#include "services/bluetooth.hpp"
#include "widgets/bluetooth.hpp"
#include "gtkmm/box.h"
class ControlCenter : public Popover {
public:
ControlCenter(std::string icon, std::string name);
private:
Gtk::Box container;
BluetoothWidget *bluetoothWidget = nullptr;
BluetoothService *bluetoothService = BluetoothService::getInstance();
};

View File

@@ -85,20 +85,14 @@ button:hover {
} }
popover { popover {
background-color: rgb(30, 30, 30); background-color: rgba(30, 30, 30, 0.3);
color: #ffffff; color: #ffffff;
font-family: "IBMPlexSans-Regular", sans-serif; font-family: "IBMPlexSans-Regular", sans-serif;
padding: 5px; padding: 5px;
border-radius: 8px; border-radius: 8px;
border: 1px solid #444444; border: 1px solid #444444;
margin-top: 5px; margin-top: 5px;
} filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5));
tooltip {
background-color: rgba(50, 50, 50, 0.9);
color: #ffffff;
font-family: "IBMPlexSans-Regular", sans-serif;
padding: 5px 10px;
} }
.icon-label { .icon-label {
@@ -128,17 +122,17 @@ tooltip {
/* use background to highlight using same dark colors as in file */ /* use background to highlight using same dark colors as in file */
.toggle-button-on { .toggle-button-on {
background-color: rgba(0, 150, 136, 0.2); background-color: rgba(0, 150, 136, 0.9);
border: 1px solid #009688; border: 1px solid #009688;
} }
.toggle-button-off { .toggle-button-off {
background-color: rgba(244, 67, 54, 0.2); background-color: rgba(244, 67, 54, 0.9);
border: 1px solid #f44336; border: 1px solid #f44336;
} }
.toggle-button-disabled { .toggle-button-disabled {
background-color: rgba(100, 100, 100, 0.2); background-color: rgba(100, 100, 100, 0.9);
border: 1px solid #666666; border: 1px solid #666666;
color: #888888; color: #888888;
} }

View File

@@ -32,7 +32,7 @@ App::App() {
auto bar = new Bar(monitor->gobj(), this->hyprlandService, auto bar = new Bar(monitor->gobj(), this->hyprlandService,
this->trayService, this->bluetoothService, hyprlandMonitor->id); this->trayService, hyprlandMonitor->id);
bar->set_application(app); bar->set_application(app);
bar->show(); bar->show();

View File

@@ -17,8 +17,8 @@
#include "sigc++/functors/mem_fun.h" #include "sigc++/functors/mem_fun.h"
Bar::Bar(GdkMonitor *monitor, HyprlandService &hyprlandService, Bar::Bar(GdkMonitor *monitor, HyprlandService &hyprlandService,
TrayService &trayService, BluetoothService &bluetoothService, int monitorId) TrayService &trayService, int monitorId)
: hyprlandService(hyprlandService), trayService(trayService), bluetoothService(bluetoothService), : hyprlandService(hyprlandService), trayService(trayService),
monitorId(monitorId) { monitorId(monitorId) {
set_name("bar-window"); set_name("bar-window");
@@ -40,19 +40,7 @@ Bar::Bar(GdkMonitor *monitor, HyprlandService &hyprlandService,
this->volumeWidget = Gtk::make_managed<VolumeWidget>(); this->volumeWidget = Gtk::make_managed<VolumeWidget>();
this->workspaceIndicator = Gtk::make_managed<WorkspaceIndicator>(hyprlandService, monitorId); this->workspaceIndicator = Gtk::make_managed<WorkspaceIndicator>(hyprlandService, monitorId);
this->trayWidget = Gtk::make_managed<TrayWidget>(trayService); this->trayWidget = Gtk::make_managed<TrayWidget>(trayService);
this->bluetoothWidget = Gtk::make_managed<BluetoothWidget>("\ue8bb", "Bluetooth");
bluetoothService.powerStateChangedSignal.connect(
sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setPowerState));
bluetoothWidget->onPowerStateButtonClickedSignal.connect(
sigc::mem_fun(bluetoothService, &BluetoothService::togglePowerState));
bluetoothWidget->setPowerState(bluetoothService.getPowerState());
bluetoothService.isDiscoveringChangedSignal.connect(
sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setIsDiscovering));
bluetoothWidget->onIsDiscoveringButtonClickedSignal.connect(
sigc::mem_fun(bluetoothService, &BluetoothService::toggleIsDiscovering));
bluetoothWidget->setIsDiscovering(bluetoothService.getIsDiscovering());
load_css(); load_css();
setup_ui(); setup_ui();
@@ -93,7 +81,7 @@ void Bar::setup_center_box() {
void Bar::setup_right_box() { void Bar::setup_right_box() {
right_box.append(*this->trayWidget); right_box.append(*this->trayWidget);
right_box.append(this->homeAssistant); right_box.append(this->homeAssistant);
right_box.append(*this->bluetoothWidget); right_box.append(this->controlCenter);
} }
void Bar::load_css() { void Bar::load_css() {

View File

@@ -7,8 +7,7 @@ Popover::Popover(std::string icon, std::string name) {
auto label = Gtk::make_managed<Gtk::Label>(icon); auto label = Gtk::make_managed<Gtk::Label>(icon);
label->add_css_class("icon-label"); label->add_css_class("icon-label");
set_child(*label); set_child(*label);
signal_clicked().connect( signal_clicked().connect(sigc::mem_fun(*this, &Popover::on_toggle_window));
sigc::mem_fun(*this, &Popover::on_toggle_window));
popover = new Gtk::Popover(); popover = new Gtk::Popover();
popover->set_parent(*this); popover->set_parent(*this);

View File

@@ -2,10 +2,14 @@
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include <string>
#include <vector>
#include "glib.h" #include "glib.h"
#include "sigc++/signal.h" #include "sigc++/signal.h"
BluetoothService *BluetoothService::instance = nullptr;
BluetoothService::BluetoothService() { BluetoothService::BluetoothService() {
GError *error = nullptr; GError *error = nullptr;
@@ -164,6 +168,7 @@ void BluetoothService::onPropertyChanged(GDBusProxy *proxy,
if (g_variant_lookup(changed_properties, "Discovering", "b", &is_discovering)) { if (g_variant_lookup(changed_properties, "Discovering", "b", &is_discovering)) {
this->isDiscovering = is_discovering; this->isDiscovering = is_discovering;
isDiscoveringChangedSignal.emit(isDiscovering); isDiscoveringChangedSignal.emit(isDiscovering);
// getDeviceObjectPaths();
} }
} }
@@ -180,3 +185,60 @@ void BluetoothService::onPropertyChangedStatic(GDBusProxy *proxy,
assert(false); 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)) {
std::cout << "Found device: " << object_path << std::endl;
device_paths.emplace_back(object_path);
g_variant_unref(device_props);
}
g_variant_unref(interfaces);
interfaces = nullptr;
}
g_variant_unref(objects);
return device_paths;
}

View File

@@ -1,17 +1,13 @@
#include "widgets/bluetooth.hpp" #include "widgets/bluetooth.hpp"
BluetoothWidget::BluetoothWidget(std::string icon, std::string title) : Popover(icon, title) { BluetoothWidget::BluetoothWidget() : Gtk::Box() {
this->popover->set_size_request(100, -1); this->set_orientation(Gtk::Orientation::VERTICAL);
set_popover_child(this->container); this->add_css_class("bluetooth-popover-container");
this->container.set_orientation(Gtk::Orientation::VERTICAL);
this->container.set_spacing(10);
this->container.add_css_class("bluetooth-popover-container");
this->statusArea.add_css_class("bluetooth-status-area"); this->statusArea.add_css_class("bluetooth-status-area");
this->statusArea.set_hexpand(true); this->statusArea.set_hexpand(true);
this->statusArea.set_halign(Gtk::Align::FILL); this->statusArea.set_halign(Gtk::Align::FILL);
this->container.append(this->statusArea); this->append(this->statusArea);
this->powerButton = Gtk::make_managed<Gtk::Button>("\ue1a8"); this->powerButton = Gtk::make_managed<Gtk::Button>("\ue1a8");
this->powerButton->set_tooltip_text("Turn Bluetooth Off"); this->powerButton->set_tooltip_text("Turn Bluetooth Off");
@@ -52,11 +48,11 @@ void BluetoothWidget::setPowerState(bool state) {
if (!state) { if (!state) {
this->scanButton->add_css_class("toggle-button-disabled"); this->scanButton->add_css_class("toggle-button-disabled");
this->add_css_class("disabled-popover-icon"); // this->add_css_class("disabled-popover-icon");
this->setIsDiscovering(false); this->setIsDiscovering(false);
} else { } else {
this->scanButton->remove_css_class("toggle-button-disabled"); this->scanButton->remove_css_class("toggle-button-disabled");
this->remove_css_class("disabled-popover-icon"); // this->remove_css_class("disabled-popover-icon");
} }
this->toggleButton(this->powerButton, state); this->toggleButton(this->powerButton, state);

View File

@@ -0,0 +1,22 @@
#include "widgets/controlCenter.hpp"
ControlCenter::ControlCenter(std::string icon, std::string name)
: Popover(icon, name) {
this->popover->set_size_request(200, -1);
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());
}

View File

@@ -9,7 +9,6 @@ TodoPopover::TodoPopover(std::string icon, std::string title) : Popover(icon, ti
this->popover->set_size_request(300, -1); this->popover->set_size_request(300, -1);
container.set_orientation(Gtk::Orientation::VERTICAL); container.set_orientation(Gtk::Orientation::VERTICAL);
container.set_spacing(10);
container.add_css_class("todo-popover-container"); container.add_css_class("todo-popover-container");
auto entry = Gtk::make_managed<Gtk::Entry>(); auto entry = Gtk::make_managed<Gtk::Entry>();