bar can now toggle bluetooth power and discovery

This commit is contained in:
2025-12-22 01:41:01 +01:00
parent 0101ea1ec0
commit 5a429a3b8b
31 changed files with 507 additions and 114 deletions

View File

@@ -1,3 +1,12 @@
IndentWidth: 4 IndentWidth: 4
ColumnLimit: 0 ColumnLimit: 0
AlignConsecutiveAssignments: true AlignConsecutiveAssignments: true
IncludeCategories:
- Regex: '^(<.+>)$'
Priority: 1
- Regex: '^"(.+\.hpp)"$'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeBlocks: Regroup

View File

@@ -34,9 +34,11 @@ 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/notifications.cpp
src/services/bluetooth.cpp
src/widgets/tray.cpp src/widgets/tray.cpp
src/widgets/todoPopover.cpp src/widgets/todo.cpp
src/widgets/bluetooth.cpp
src/components/popover.cpp src/components/popover.cpp
src/components/todoEntry.cpp src/components/todoEntry.cpp

View File

@@ -3,12 +3,14 @@
#include <vector> #include <vector>
#include "bar/bar.hpp" #include "bar/bar.hpp"
#include "glibmm/refptr.h" #include "services/bluetooth.hpp"
#include "gtkmm/application.h"
#include "services/hyprland.hpp" #include "services/hyprland.hpp"
#include "services/notifications.hpp" #include "services/notifications.hpp"
#include "services/tray.hpp" #include "services/tray.hpp"
#include "glibmm/refptr.h"
#include "gtkmm/application.h"
class App { class App {
public: public:
App(); App();
@@ -21,6 +23,7 @@ class App {
HyprlandService hyprlandService; HyprlandService hyprlandService;
NotificationService notificationService; NotificationService notificationService;
TrayService trayService; TrayService trayService;
BluetoothService bluetoothService;
void setupServices(); void setupServices();
}; };

View File

@@ -2,9 +2,12 @@
#include <gtk4-layer-shell/gtk4-layer-shell.h> #include <gtk4-layer-shell/gtk4-layer-shell.h>
#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"
@@ -14,7 +17,7 @@
class Bar : public Gtk::Window { class Bar : public Gtk::Window {
public: public:
Bar(GdkMonitor *monitor, HyprlandService &hyprlandService, TrayService &trayService, int monitorId); Bar(GdkMonitor *monitor, HyprlandService &hyprlandService, TrayService &trayService, BluetoothService &bluetoothService, int monitorId);
protected: protected:
Gtk::CenterBox main_box{}; Gtk::CenterBox main_box{};
@@ -30,11 +33,13 @@ class Bar : public Gtk::Window {
WebWidget homeAssistant{ICON_HOME, "Home Assistant", "https://home.rivercry.com"}; WebWidget homeAssistant{ICON_HOME, "Home Assistant", "https://home.rivercry.com"};
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,18 +2,17 @@
#include <gtkmm/button.h> #include <gtkmm/button.h>
#include <gtkmm/popover.h> #include <gtkmm/popover.h>
#include <string> #include <string>
class Popover: public Gtk::Button { class Popover : public Gtk::Button {
public: public:
Popover(std::string icon, std::string name); Popover(std::string icon, std::string name);
~Popover() override; ~Popover() override;
protected: protected:
void on_toggle_window(); void on_toggle_window();
Gtk::Popover* popover = nullptr; Gtk::Popover *popover = nullptr;
void set_popover_child(Gtk::Widget& child) { void set_popover_child(Gtk::Widget &child) {
gtk_popover_set_child(popover->gobj(), child.gobj()); gtk_popover_set_child(popover->gobj(), child.gobj());
} }
}; };

View File

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

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
class IUpdatable { class IUpdatable {
public: public:
virtual ~IUpdatable() = default; virtual ~IUpdatable() = default;
virtual bool onUpdate() = 0; virtual bool onUpdate() = 0;
}; };

View File

@@ -0,0 +1,35 @@
#pragma once
#include <gio/gio.h>
#include <gtk/gtk.h>
#include "sigc++/signal.h"
class BluetoothService {
public:
BluetoothService();
sigc::signal<void(bool)> powerStateChangedSignal;
sigc::signal<void(bool)> isDiscoveringChangedSignal;
bool getPowerState();
void setPowerState(bool state);
bool getIsDiscovering();
void setIsDiscovering(bool state);
private:
GDBusProxy *adapter_proxy = nullptr;
bool powerState = false;
bool isDiscovering = false;
void onPropertyChanged(GDBusProxy *proxy,
GVariant *changed_properties,
const gchar *const *invalidated_properties,
gpointer user_data);
static void onPropertyChangedStatic(GDBusProxy *proxy,
GVariant *changed_properties,
const gchar *const *invalidated_properties,
gpointer user_data);
};

View File

@@ -3,18 +3,18 @@
#include <gio/gio.h> #include <gio/gio.h>
class NotificationService { class NotificationService {
public: public:
void intialize(); void intialize();
NotificationService() = default; NotificationService() = default;
~NotificationService(); ~NotificationService();
guint32 allocateNotificationId(guint32 replacesId); guint32 allocateNotificationId(guint32 replacesId);
GDBusConnection* getConnection() const { return connection; } GDBusConnection *getConnection() const { return connection; }
private: private:
GDBusConnection* connection = nullptr; GDBusConnection *connection = nullptr;
guint registrationId = 0; guint registrationId = 0;
GDBusNodeInfo* nodeInfo = nullptr; GDBusNodeInfo *nodeInfo = nullptr;
guint32 nextNotificationId = 1; guint32 nextNotificationId = 1;
}; };

View File

@@ -1,9 +1,10 @@
#pragma once #pragma once
#include <memory>
#include <string>
#include "components/todoEntry.hpp" #include "components/todoEntry.hpp"
#include "services/todoAdapter.hpp" #include "services/todoAdapter.hpp"
#include <memory>
#include <string>
class TodoService { class TodoService {
public: public:

View File

@@ -12,10 +12,10 @@ class ITodoAdapter {
public: public:
virtual ~ITodoAdapter() = default; virtual ~ITodoAdapter() = default;
virtual bool init() = 0; virtual bool init() = 0;
virtual std::vector<TodoRecord> listTodos() = 0; virtual std::vector<TodoRecord> listTodos() = 0;
virtual int addTodo(const std::string &text) = 0; virtual int addTodo(const std::string &text) = 0;
virtual bool removeTodo(int id) = 0; virtual bool removeTodo(int id) = 0;
virtual bool updateTodo(int id, const std::string &text) = 0; virtual bool updateTodo(int id, const std::string &text) = 0;
}; };

View File

@@ -1,18 +1,17 @@
#pragma once #pragma once
#include <gdkmm/memorytexture.h> #include <gdkmm/memorytexture.h>
#include <gio/gio.h>
#include <giomm/actiongroup.h> #include <giomm/actiongroup.h>
#include <giomm/dbusconnection.h> #include <giomm/dbusconnection.h>
#include <giomm/init.h> #include <giomm/init.h>
#include <giomm/menumodel.h> #include <giomm/menumodel.h>
#include <glibmm/bytes.h> #include <glibmm/bytes.h>
#include <glibmm/refptr.h> #include <glibmm/refptr.h>
#include <sigc++/sigc++.h>
#include <gio/gio.h>
#include <map> #include <map>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <sigc++/sigc++.h>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -49,8 +48,8 @@ class TrayService {
struct MenuNode { struct MenuNode {
int id = 0; int id = 0;
std::string label; std::string label;
bool enabled = true; bool enabled = true;
bool visible = true; bool visible = true;
bool separator = false; bool separator = false;
std::vector<MenuNode> children; std::vector<MenuNode> children;
}; };
@@ -65,7 +64,7 @@ class TrayService {
struct TrackedItem { struct TrackedItem {
Item publicData; Item publicData;
guint signalSubscriptionId = 0; guint signalSubscriptionId = 0;
guint ownerWatchId = 0; guint ownerWatchId = 0;
Glib::RefPtr<Gio::MenuModel> menuModel; Glib::RefPtr<Gio::MenuModel> menuModel;
Glib::RefPtr<Gio::ActionGroup> menuActions; Glib::RefPtr<Gio::ActionGroup> menuActions;
}; };
@@ -74,9 +73,9 @@ class TrayService {
Glib::RefPtr<Gio::DBus::NodeInfo> nodeInfo; Glib::RefPtr<Gio::DBus::NodeInfo> nodeInfo;
Gio::DBus::InterfaceVTable vtable; Gio::DBus::InterfaceVTable vtable;
guint nameOwnerId = 0; guint nameOwnerId = 0;
guint registrationId = 0; guint registrationId = 0;
bool hostRegistered = false; bool hostRegistered = false;
std::map<std::string, std::unique_ptr<TrackedItem>> items; std::map<std::string, std::unique_ptr<TrackedItem>> items;

View File

@@ -0,0 +1,31 @@
#pragma once
#include <gtkmm/box.h>
#include "components/popover.hpp"
#include "gtkmm/button.h"
class BluetoothWidget : public Popover {
public:
BluetoothWidget(std::string icon, std::string title);
void setPowerState(bool state);
void setIsDiscovering(bool state);
sigc::signal<void(bool)> powerStateChangedSignal;
sigc::signal<void(bool)> isDiscoveringChangedSignal;
void update();
private:
bool isPowered = false;
bool isDiscovering = false;
Gtk::Box container;
Gtk::Box statusArea;
Gtk::Box *deviceList = nullptr;
Gtk::Button *scanButton = nullptr;
Gtk::Button *toggleButton = nullptr;
};

View File

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

View File

@@ -11,7 +11,6 @@
#include <gtkmm/image.h> #include <gtkmm/image.h>
#include <gtkmm/picture.h> #include <gtkmm/picture.h>
#include <gtkmm/popovermenu.h> #include <gtkmm/popovermenu.h>
#include <map> #include <map>
#include <memory> #include <memory>
#include <string> #include <string>
@@ -37,8 +36,8 @@ class TrayIconWidget : public Gtk::Button {
Glib::RefPtr<Gio::MenuModel> menuModel; Glib::RefPtr<Gio::MenuModel> menuModel;
sigc::connection menuChangedConnection; sigc::connection menuChangedConnection;
bool menuPopupPending = false; bool menuPopupPending = false;
double pendingX = 0.0; double pendingX = 0.0;
double pendingY = 0.0; double pendingY = 0.0;
void on_primary_released(int n_press, double x, double y); void on_primary_released(int n_press, double x, double y);
void on_secondary_released(int n_press, double x, double y); void on_secondary_released(int n_press, double x, double y);

View File

@@ -1,11 +1,13 @@
#pragma once #pragma once
#include "components/popover.hpp"
#include <gtkmm/button.h> #include <gtkmm/button.h>
#include <gtkmm/popover.h> #include <gtkmm/popover.h>
#include "components/popover.hpp"
class WebWidget : public Popover { class WebWidget : public Popover {
public: public:
WebWidget(std::string icon, std::string title, std::string url); WebWidget(std::string icon, std::string title, std::string url);
private:
private:
}; };

View File

@@ -1,8 +1,8 @@
#pragma once #pragma once
#include <gtkmm/box.h> #include <gtkmm/box.h>
#include <gtkmm/label.h>
#include <gtkmm/gestureclick.h> #include <gtkmm/gestureclick.h>
#include <gtkmm/label.h>
#include <sigc++/connection.h> #include <sigc++/connection.h>
#include "services/hyprland.hpp" #include "services/hyprland.hpp"
@@ -18,7 +18,7 @@ class WorkspaceIndicator : public Gtk::Box {
sigc::connection workspaceConnection; sigc::connection workspaceConnection;
sigc::connection monitorConnection; sigc::connection monitorConnection;
std::map<int, Gtk::Label *> workspaceLabels; std::map<int, Gtk::Label *> workspaceLabels;
std::map<int, Glib::RefPtr<Gtk::GestureClick>> workspaceGestures; std::map<int, Glib::RefPtr<Gtk::GestureClick>> workspaceGestures;
void rebuild(); void rebuild();
void on_workspace_update(); void on_workspace_update();

View File

@@ -2,7 +2,6 @@
all: unset; all: unset;
} }
window { window {
background-color: rgba(30, 30, 30, 0.8); background-color: rgba(30, 30, 30, 0.8);
color: #ffffff; color: #ffffff;
@@ -73,6 +72,7 @@ button {
background-color: transparent; background-color: transparent;
color: #ffffff; color: #ffffff;
border: none; border: none;
font-size: 20px;
} }
button:hover { button:hover {
@@ -88,9 +88,10 @@ popover {
background-color: rgb(30, 30, 30); background-color: rgb(30, 30, 30);
color: #ffffff; color: #ffffff;
font-family: "IBMPlexSans-Regular", sans-serif; font-family: "IBMPlexSans-Regular", sans-serif;
padding: 10px; padding: 5px;
border-radius: 8px; border-radius: 8px;
border: 1px solid #444444; border: 1px solid #444444;
margin-top: 5px;
} }
tooltip { tooltip {
@@ -119,4 +120,19 @@ tooltip {
border: 1px solid #555555; border: 1px solid #555555;
background-color: #222222; background-color: #222222;
color: #ffffff; color: #ffffff;
}
.toggle-button {
border-radius: 4px;
}
/* use background to highlight using same dark colors as in file */
.toggle-button-on {
background-color: rgba(0, 150, 136, 0.2);
border: 1px solid #009688;
}
.toggle-button-off {
background-color: rgba(244, 67, 54, 0.2);
border: 1px solid #f44336;
} }

View File

@@ -29,8 +29,10 @@ App::App() {
continue; continue;
} }
auto bar = new Bar(monitor->gobj(), this->hyprlandService, auto bar = new Bar(monitor->gobj(), this->hyprlandService,
this->trayService, hyprlandMonitor->id); this->trayService, this->bluetoothService, hyprlandMonitor->id);
bar->set_application(app); bar->set_application(app);
bar->show(); bar->show();

View File

@@ -1,26 +1,24 @@
#include "bar/bar.hpp" #include "bar/bar.hpp"
#include "gtk/gtk.h"
#include "widgets/date.hpp"
#include "widgets/spacer.hpp"
#include "widgets/todoPopover.hpp"
#include "widgets/volumeWidget.hpp"
#include "widgets/workspaceIndicator.hpp"
#include "helpers/systemHelper.hpp"
#include <filesystem> #include <filesystem>
#include <gtkmm/enums.h> #include <gtkmm/enums.h>
#include <gtkmm/label.h> #include <gtkmm/label.h>
#include <gtkmm/window.h> #include <gtkmm/window.h>
#include "helpers/systemHelper.hpp"
#include "widgets/date.hpp"
#include "widgets/spacer.hpp"
#include "widgets/todo.hpp"
#include "widgets/volumeWidget.hpp"
#include "widgets/workspaceIndicator.hpp"
#include "glibmm/main.h" #include "glibmm/main.h"
#include "gtk/gtk.h"
#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, int monitorId) TrayService &trayService, BluetoothService &bluetoothService, int monitorId)
: hyprlandService(hyprlandService), trayService(trayService), : hyprlandService(hyprlandService), trayService(trayService), bluetoothService(bluetoothService),
monitorId(monitorId) { monitorId(monitorId) {
set_name("bar-window"); set_name("bar-window");
@@ -39,20 +37,30 @@ Bar::Bar(GdkMonitor *monitor, HyprlandService &hyprlandService,
set_child(main_box); set_child(main_box);
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->powerStateChangedSignal.connect(
sigc::mem_fun(bluetoothService, &BluetoothService::setPowerState));
bluetoothWidget->setPowerState(bluetoothService.getPowerState());
bluetoothService.isDiscoveringChangedSignal.connect(
sigc::mem_fun(*this->bluetoothWidget, &BluetoothWidget::setIsDiscovering));
bluetoothWidget->isDiscoveringChangedSignal.connect(
sigc::mem_fun(bluetoothService, &BluetoothService::setIsDiscovering));
bluetoothWidget->setIsDiscovering(bluetoothService.getIsDiscovering());
load_css(); load_css();
setup_ui(); setup_ui();
clock.onUpdate(); clock.onUpdate();
Glib::signal_timeout().connect(sigc::mem_fun(clock, &Clock::onUpdate),
1000);
date.onUpdate(); date.onUpdate();
Glib::signal_timeout().connect(sigc::mem_fun(clock, &Clock::onUpdate), 1000);
Glib::signal_timeout().connect(sigc::mem_fun(date, &Date::onUpdate), 1000); Glib::signal_timeout().connect(sigc::mem_fun(date, &Date::onUpdate), 1000);
} }
@@ -83,8 +91,9 @@ void Bar::setup_center_box() {
} }
void Bar::setup_right_box() { void Bar::setup_right_box() {
right_box.append(*trayWidget); right_box.append(*this->trayWidget);
right_box.append(homeAssistant); right_box.append(this->homeAssistant);
right_box.append(*this->bluetoothWidget);
} }
void Bar::load_css() { void Bar::load_css() {

View File

@@ -1,15 +1,16 @@
#include "components/todoEntry.hpp" #include "components/todoEntry.hpp"
#include <gtkmm/label.h> #include <gtkmm/label.h>
#include <string>
#include "gtkmm/button.h" #include "gtkmm/button.h"
TodoEntry::TodoEntry(int id, std::string text, sigc::signal<void(int)> signal_dismissed) TodoEntry::TodoEntry(int id, std::string text, sigc::signal<void(int)> signal_dismissed, sigc::signal<void(int, std::string)> signal_edited)
: Gtk::Box(Gtk::Orientation::HORIZONTAL) { : Gtk::Box(Gtk::Orientation::HORIZONTAL) {
this->id = id; this->id = id;
this->text = text; this->text = text;
this->signal_dismissed = signal_dismissed; this->signal_dismissed = signal_dismissed;
this->signal_edited = signal_edited;
auto box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL); auto box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
box->set_hexpand(true); box->set_hexpand(true);
@@ -35,6 +36,10 @@ TodoEntry::TodoEntry(int id, std::string text, sigc::signal<void(int)> signal_di
dismissButton->signal_clicked().connect(sigc::mem_fun(*this, &TodoEntry::on_dismiss_clicked)); dismissButton->signal_clicked().connect(sigc::mem_fun(*this, &TodoEntry::on_dismiss_clicked));
auto editButton = Gtk::make_managed<Gtk::Button>("\uf044");
editButton->set_valign(Gtk::Align::CENTER);
buttonBox->append(*editButton);
buttonBox->append(*dismissButton); buttonBox->append(*dismissButton);
} }

185
src/services/bluetooth.cpp Normal file
View File

@@ -0,0 +1,185 @@
#include "services/bluetooth.hpp"
#include <cassert>
#include <iostream>
#include "glib.h"
#include "sigc++/signal.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);
} else {
std::cout << "Bluetooth adapter proxy created successfully." << std::endl;
}
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::setPowerState(bool state) {
GError *error = nullptr;
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::setIsDiscovering(bool state) {
const bool currently_discovering = this->getIsDiscovering();
this->isDiscovering = currently_discovering;
if (currently_discovering == state) {
return;
}
GError *error = nullptr;
const char *method = state ? "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 = state;
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)) {
this->setIsDiscovering(false);
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);
}
}
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);
}
}

View File

@@ -2,6 +2,7 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <iostream>
#include <iterator> #include <iterator>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <stdexcept> #include <stdexcept>
@@ -10,8 +11,6 @@
#include <sys/un.h> #include <sys/un.h>
#include <unistd.h> #include <unistd.h>
#include <iostream>
#include "helpers/systemHelper.hpp" #include "helpers/systemHelper.hpp"
HyprlandService::HyprlandService() = default; HyprlandService::HyprlandService() = default;

View File

@@ -1,10 +1,10 @@
#include "services/todoAdapter.hpp"
#include <filesystem> #include <filesystem>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <sqlite3.h> #include <sqlite3.h>
#include "services/todoAdapter.hpp"
namespace { namespace {
std::string getDbPath() { std::string getDbPath() {
const char *homeDir = getenv("HOME"); const char *homeDir = getenv("HOME");
@@ -31,7 +31,6 @@ class SqliteTodoAdapter final : public ITodoAdapter {
bool init() override { bool init() override {
std::string dbPath = getDbPath(); std::string dbPath = getDbPath();
std::cout << "Opening database at: " << dbPath << std::endl;
int rc = sqlite3_open(dbPath.c_str(), &db_); int rc = sqlite3_open(dbPath.c_str(), &db_);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
@@ -42,8 +41,8 @@ class SqliteTodoAdapter final : public ITodoAdapter {
const char *sql = "CREATE TABLE IF NOT EXISTS todos (" const char *sql = "CREATE TABLE IF NOT EXISTS todos ("
"id INTEGER PRIMARY KEY AUTOINCREMENT," "id INTEGER PRIMARY KEY AUTOINCREMENT,"
"text TEXT NOT NULL);"; "text TEXT NOT NULL);";
char *zErrMsg = nullptr; char *zErrMsg = nullptr;
rc = sqlite3_exec(db_, sql, nullptr, nullptr, &zErrMsg); rc = sqlite3_exec(db_, sql, nullptr, nullptr, &zErrMsg);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
std::cerr << "SQL error (create table): " << (zErrMsg ? zErrMsg : "") << std::endl; std::cerr << "SQL error (create table): " << (zErrMsg ? zErrMsg : "") << std::endl;
sqlite3_free(zErrMsg); sqlite3_free(zErrMsg);

View File

@@ -1,8 +1,10 @@
#include "services/todo.hpp" #include "services/todo.hpp"
#include "components/todoEntry.hpp"
#include "gtkmm/object.h"
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include <gtkmm/object.h>
#include "components/todoEntry.hpp"
std::unique_ptr<ITodoAdapter> makeSqliteTodoAdapter(); std::unique_ptr<ITodoAdapter> makeSqliteTodoAdapter();
@@ -62,9 +64,13 @@ TodoEntry *TodoService::addTodo(std::string text, bool emitSignal, bool persist)
id = newId; id = newId;
} }
auto signal = sigc::signal<void(int)>(); auto dismissSignal = sigc::signal<void(int)>();
signal.connect(sigc::mem_fun(*this, &TodoService::removeTodo)); dismissSignal.connect(sigc::mem_fun(*this, &TodoService::removeTodo));
TodoEntry *todo = Gtk::make_managed<TodoEntry>(id, text, signal);
auto editSignal = sigc::signal<void(int, std::string)>();
editSignal.connect(sigc::mem_fun(*this, &TodoService::updateTodo));
TodoEntry *todo = Gtk::make_managed<TodoEntry>(id, text, dismissSignal, editSignal);
todos[id] = todo; todos[id] = todo;
@@ -79,14 +85,24 @@ TodoEntry *TodoService::addTodo(std::string text, bool emitSignal, bool persist)
return todo; return todo;
} }
void TodoService::updateTodo(int id, std::string text) {} void TodoService::updateTodo(int id, std::string text) {
if (todos.find(id) == todos.end()) {
std::cerr << "Todo with id " << id << " not found!" << std::endl;
assert(false);
}
if (this->adapter) {
this->adapter->updateTodo(id, text);
}
this->refreshSignal.emit();
}
void TodoService::load() { void TodoService::load() {
if (!this->adapter) { if (!this->adapter) {
return; return;
} }
std::cout << "Loading todos from database..." << std::endl;
auto rows = this->adapter->listTodos(); auto rows = this->adapter->listTodos();
int count = 0; int count = 0;
@@ -96,14 +112,17 @@ void TodoService::load() {
int id = row.id; int id = row.id;
std::string text = row.text; std::string text = row.text;
auto signal = sigc::signal<void(int)>(); auto dismissSignal = sigc::signal<void(int)>();
signal.connect(sigc::mem_fun(*this, &TodoService::removeTodo)); dismissSignal.connect(sigc::mem_fun(*this, &TodoService::removeTodo));
TodoEntry *todo = Gtk::make_managed<TodoEntry>(id, text, signal);
auto editSignal = sigc::signal<void(int, std::string)>();
editSignal.connect(sigc::mem_fun(*this, &TodoService::updateTodo));
TodoEntry *todo = Gtk::make_managed<TodoEntry>(id, text, dismissSignal, editSignal);
todos[id] = todo; todos[id] = todo;
if (id >= nextId) { if (id >= nextId) {
nextId = id + 1; nextId = id + 1;
} }
} }
std::cout << "Loaded " << count << " todos." << std::endl;
} }

View File

@@ -1,5 +1,7 @@
#include "services/tray.hpp" #include "services/tray.hpp"
#include <algorithm>
#include <cstring>
#include <gdkmm/pixbuf.h> #include <gdkmm/pixbuf.h>
#include <gdkmm/texture.h> #include <gdkmm/texture.h>
#include <gio/gdbusmenumodel.h> #include <gio/gdbusmenumodel.h>
@@ -7,9 +9,6 @@
#include <giomm/dbusactiongroup.h> #include <giomm/dbusactiongroup.h>
#include <giomm/dbusownname.h> #include <giomm/dbusownname.h>
#include <giomm/menumodel.h> #include <giomm/menumodel.h>
#include <algorithm>
#include <cstring>
#include <iostream> #include <iostream>
#include <tuple> #include <tuple>
#include <vector> #include <vector>

70
src/widgets/bluetooth.cpp Normal file
View File

@@ -0,0 +1,70 @@
#include "widgets/bluetooth.hpp"
BluetoothWidget::BluetoothWidget(std::string icon, std::string title) : Popover(icon, title) {
this->popover->set_size_request(100, -1);
set_popover_child(this->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.set_hexpand(true);
this->statusArea.set_halign(Gtk::Align::FILL);
this->container.append(this->statusArea);
this->toggleButton = Gtk::make_managed<Gtk::Button>("\ue1a8");
this->toggleButton->add_css_class("bluetooth-toggle-button");
this->toggleButton->set_tooltip_text("Turn Bluetooth Off");
this->toggleButton->signal_clicked().connect([this]() {
const bool newState = !isPowered;
setPowerState(newState);
powerStateChangedSignal.emit(newState);
});
this->toggleButton->add_css_class("toggle-button");
this->scanButton = Gtk::make_managed<Gtk::Button>("\ue1aa");
this->scanButton->set_tooltip_text("Scan for Devices");
this->scanButton->add_css_class("bluetooth-scan-button");
this->scanButton->signal_clicked().connect([this]() {
const bool newState = !isDiscovering;
setIsDiscovering(newState);
isDiscoveringChangedSignal.emit(newState);
});
this->statusArea.append(*this->toggleButton);
this->statusArea.append(*this->scanButton);
}
void BluetoothWidget::setPowerState(bool state) {
this->isPowered = state;
if (state) {
this->toggleButton->set_label("\ue1a8");
this->toggleButton->add_css_class("toggle-button-on");
this->toggleButton->remove_css_class("toggle-button-off");
} else {
this->toggleButton->set_label("\ue1a9");
this->toggleButton->add_css_class("toggle-button-off");
this->toggleButton->remove_css_class("toggle-button-on");
}
this->setIsDiscovering(false);
}
void BluetoothWidget::setIsDiscovering(bool state) {
this->isDiscovering = state;
if (state) {
this->scanButton->add_css_class("toggle-button-on");
this->scanButton->remove_css_class("toggle-button-off");
} else {
this->scanButton->add_css_class("toggle-button-off");
this->scanButton->remove_css_class("toggle-button-on");
}
}
void BluetoothWidget::update() {
setPowerState(isPowered);
setIsDiscovering(isDiscovering);
}

View File

@@ -1,4 +1,4 @@
#include "widgets/todoPopover.hpp" #include "widgets/todo.hpp"
#include <gtkmm/box.h> #include <gtkmm/box.h>
#include <gtkmm/entry.h> #include <gtkmm/entry.h>
@@ -12,24 +12,23 @@ TodoPopover::TodoPopover(std::string icon, std::string title) : Popover(icon, ti
container.set_spacing(10); 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>();
entry->set_placeholder_text("Enter your to-do item..."); entry->set_placeholder_text("Enter your to-do item...");
entry->add_css_class("todo-input"); entry->add_css_class("todo-input");
entry->set_hexpand(true); entry->set_hexpand(true);
entry->set_halign(Gtk::Align::FILL); entry->set_halign(Gtk::Align::FILL);
entry->set_valign(Gtk::Align::START); entry->set_valign(Gtk::Align::START);
inputArea.append(*entry); inputArea.append(*entry);
inputArea.add_css_class("todo-input-area"); inputArea.add_css_class("todo-input-area");
inputArea.set_hexpand(true); inputArea.set_hexpand(true);
inputArea.set_halign(Gtk::Align::FILL); inputArea.set_halign(Gtk::Align::FILL);
entry->signal_activate().connect([this, entry]() { entry->signal_activate().connect([this, entry]() {
std::string text = entry->get_text(); std::string text = entry->get_text();
if (!text.empty()) { if (!text.empty()) {
this->todoService->addTodo(text); this->todoService->addTodo(text);
entry->set_text(""); entry->set_text("");
} }
}); });
@@ -39,12 +38,12 @@ TodoPopover::TodoPopover(std::string icon, std::string title) : Popover(icon, ti
this->todoList = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL); this->todoList = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
container.append(*todoList); container.append(*todoList);
todoList->add_css_class("todo-list"); todoList->add_css_class("todo-list");
auto signal = sigc::signal<void()>(); auto signal = sigc::signal<void()>();
this->todoService = new TodoService(signal); this->todoService = new TodoService(signal);
signal.connect(sigc::mem_fun(*this, &TodoPopover::update)); signal.connect(sigc::mem_fun(*this, &TodoPopover::update));
this->set_popover_child(this->container); this->set_popover_child(this->container);
this->update(); this->update();
} }
@@ -52,23 +51,23 @@ TodoPopover::TodoPopover(std::string icon, std::string title) : Popover(icon, ti
void TodoPopover::update() { void TodoPopover::update() {
auto todos = this->todoService->getTodos(); auto todos = this->todoService->getTodos();
Gtk::Widget* child = todoList->get_first_child(); Gtk::Widget *child = todoList->get_first_child();
while (child) { while (child) {
Gtk::Widget* next = child->get_next_sibling(); Gtk::Widget *next = child->get_next_sibling();
bool found = false; bool found = false;
for (auto& [id, todo] : todos) { for (auto &[id, todo] : todos) {
if (child == todo) { if (child == todo) {
found = true; found = true;
break; break;
} }
} }
if (!found) { if (!found) {
todoList->remove(*child); todoList->remove(*child);
} }
child = next; child = next;
} }

View File

@@ -1,12 +1,12 @@
#include "widgets/volumeWidget.hpp" #include "widgets/volumeWidget.hpp"
#include "helpers/systemHelper.hpp"
#include <cmath> #include <cmath>
#include <iostream> #include <iostream>
#include <regex> #include <regex>
#include <sigc++/functors/mem_fun.h> #include <sigc++/functors/mem_fun.h>
#include "helpers/systemHelper.hpp"
VolumeWidget::VolumeWidget() : Gtk::Box(Gtk::Orientation::HORIZONTAL) { VolumeWidget::VolumeWidget() : Gtk::Box(Gtk::Orientation::HORIZONTAL) {
set_valign(Gtk::Align::CENTER); set_valign(Gtk::Align::CENTER);
set_halign(Gtk::Align::CENTER); set_halign(Gtk::Align::CENTER);

View File

@@ -1,4 +1,5 @@
#include "widgets/webWidget.hpp" #include "widgets/webWidget.hpp"
#include <gtkmm/label.h> #include <gtkmm/label.h>
#include <webkit/webkit.h> #include <webkit/webkit.h>

View File

@@ -1,5 +1,4 @@
#include "widgets/workspaceIndicator.hpp" #include "widgets/workspaceIndicator.hpp"
#include "services/hyprland.hpp"
#include <cassert> #include <cassert>
#include <gdk/gdk.h> #include <gdk/gdk.h>
@@ -7,6 +6,8 @@
#include <gtkmm/widget.h> #include <gtkmm/widget.h>
#include <sigc++/functors/mem_fun.h> #include <sigc++/functors/mem_fun.h>
#include "services/hyprland.hpp"
WorkspaceIndicator::WorkspaceIndicator(HyprlandService &service, int monitorId) WorkspaceIndicator::WorkspaceIndicator(HyprlandService &service, int monitorId)
: Gtk::Box(Gtk::Orientation::HORIZONTAL), service(service), : Gtk::Box(Gtk::Orientation::HORIZONTAL), service(service),
monitorId(monitorId) { monitorId(monitorId) {