diff --git a/CMakeLists.txt b/CMakeLists.txt index b6488e3..bcd3560 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,8 @@ set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED 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) @@ -22,28 +23,25 @@ link_directories(${GTKMM_LIBRARY_DIRS} ${LAYERSHELL_LIBRARY_DIRS} ${WEBKIT_LIBRA add_library(bar_lib) target_sources(bar_lib PUBLIC - src/app.cpp - src/bar/bar.cpp + src/app.cpp + src/bar/bar.cpp - src/widgets/clock.cpp - src/widgets/date.cpp - src/widgets/volumeWidget.cpp - src/widgets/webWidget.cpp - - src/services/hyprland.cpp - src/services/tray.cpp - src/services/notifications.cpp - src/services/bluetooth.cpp + src/widgets/clock.cpp + src/widgets/date.cpp + src/widgets/volumeWidget.cpp + src/widgets/webWidget.cpp - src/widgets/tray.cpp - src/widgets/bluetooth.cpp - src/widgets/controlCenter.cpp - - src/components/popover.cpp - src/components/workspaceIndicator.cpp - src/components/base/button.cpp + src/services/hyprland.cpp + src/services/tray.cpp + + src/widgets/tray.cpp + src/widgets/controlCenter.cpp + + src/components/popover.cpp + src/components/workspaceIndicator.cpp + src/components/base/button.cpp ) -include_directories(bar_lib PRIVATE +include_directories(bar_lib PRIVATE include ) diff --git a/include/app.hpp b/include/app.hpp index 39fb0ac..f67ba9c 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -4,8 +4,6 @@ #include "bar/bar.hpp" #include "services/hyprland.hpp" -#include "services/notifications.hpp" -#include "services/tray.hpp" #include "glibmm/refptr.h" #include "gtkmm/application.h" @@ -18,9 +16,8 @@ class App { private: Glib::RefPtr app; - std::vector bars; + std::vector> bars; HyprlandService *hyprlandService = nullptr; - NotificationService notificationService; TrayService *trayService = TrayService::getInstance(); void setupServices(); diff --git a/include/components/workspaceIndicator.hpp b/include/components/workspaceIndicator.hpp index f6b5614..42bc525 100644 --- a/include/components/workspaceIndicator.hpp +++ b/include/components/workspaceIndicator.hpp @@ -1,14 +1,35 @@ #pragma once #include + #include "gtkmm/box.h" #include "gtkmm/button.h" #include "gtkmm/overlay.h" class WorkspaceIndicator : public Gtk::Box { - public: - WorkspaceIndicator(std::string label, sigc::slot onClick = {}); + public: - private: - Gtk::Button button; + enum InidicatorState { + EMPTY, + ALIVE, + FOCUSED, + PRESENTING, + URGENT, + }; + + // meh, Maybe try WorkspaceState struct later + WorkspaceIndicator(int id, std::string label, sigc::slot onClick); + void setIndicatorState(InidicatorState state); + + private: + void clearCssClass(); + InidicatorState currentState = EMPTY; + std::shared_ptr overlay; + std::map stateToCssClass = { + {FOCUSED, "workspace-pill-focused"}, + {URGENT, "workspace-pill-urgent"}, + {ALIVE, "workspace-pill-alive"}, + {PRESENTING, "workspace-pill-presenting"}, + {EMPTY, "workspace-pill-empty"}, // Default class + }; }; \ No newline at end of file diff --git a/include/helpers/socket.hpp b/include/helpers/socket.hpp index 45953e9..0caafb7 100644 --- a/include/helpers/socket.hpp +++ b/include/helpers/socket.hpp @@ -9,12 +9,13 @@ #include "helpers/string.hpp" class SocketHelper { + + public: typedef struct SocketMessage { std::string eventType; std::string eventData; } SocketMessage; - public: static std::vector parseSocketMessage(int socketFd, const std::string &delimiter) { char buffer[4096]; std::string data; diff --git a/include/services/hyprland.hpp b/include/services/hyprland.hpp index dd6d122..388e0d7 100644 --- a/include/services/hyprland.hpp +++ b/include/services/hyprland.hpp @@ -3,43 +3,50 @@ #include #include #include +#include #include #include #include -#include +#include "bar/bar.hpp" #include "components/workspaceIndicator.hpp" +#include "helpers/socket.hpp" #include "gtkmm/box.h" #define NUM_WORKSPACES 7 class HyprlandService { -inline static HyprlandService *instance = nullptr; + inline static HyprlandService *instance = nullptr; public: - struct WorkspaceState { - int id; - int monitorId; - bool alive = false; // window count above 0 (exists in hyprctl workspaces array) - 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 Client { + std::string address; + int workspaceId; + std::string title; + }; + struct WorkspaceData { + int id; + std::string monitorName; std::string label; + + std::map> clients; + std::set urgentClients; }; struct Workspace { - WorkspaceState state; + std::shared_ptr state; std::shared_ptr view; }; struct Monitor { int id; int activeWorkspaceId; - bool focused = false; + bool focused; std::string name; std::map> monitorWorkspaces; + std::shared_ptr bar; }; static HyprlandService *getInstance() { @@ -49,22 +56,57 @@ inline static HyprlandService *instance = nullptr; return instance; } - std::shared_ptr getWorkspaceIndicatorsForMonitor(int monitorId); + std::shared_ptr getWorkspaceIndicatorsForMonitor(std::string monitorName); + + void addBar(std::shared_ptr bar, std::string monitorName); private: + enum SocketEventType { + WORKSPACE_CHANGED, + + ACTIVE_WINDOW, + OPEN_WINDOW, + CLOSE_WINDOW, + URGENT, + + FOCUSED_MONITOR, + MONITOR_REMOVED, + }; + + std::map 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(); - std::map> monitors; + std::map> monitors; std::map> workspaces; + std::map> clients; /// maybe refactor into reusable class std::string socketBuffer; int socketFd; /// - std::string getSocketPath(); void bindSocket(); void init(); - void updateIndicator(Workspace &workspace, const WorkspaceState newState); + void switchToWorkspace(int workspaceId); + void refreshIndicator(std::shared_ptr workspace); + + void handleSocketMessage(SocketHelper::SocketMessage message); }; diff --git a/include/services/notifications.hpp b/include/services/notifications.hpp deleted file mode 100644 index e13c8e5..0000000 --- a/include/services/notifications.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -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; -}; diff --git a/include/widgets/controlCenter.hpp b/include/widgets/controlCenter.hpp index bc0ec06..201c748 100644 --- a/include/widgets/controlCenter.hpp +++ b/include/widgets/controlCenter.hpp @@ -1,8 +1,6 @@ #pragma once #include "components/popover.hpp" -#include "services/bluetooth.hpp" -#include "widgets/bluetooth.hpp" #include "gtkmm/box.h" class ControlCenter : public Popover { @@ -11,7 +9,4 @@ class ControlCenter : public Popover { private: Gtk::Box container; - - BluetoothWidget *bluetoothWidget = nullptr; - BluetoothService *bluetoothService = BluetoothService::getInstance(); }; \ No newline at end of file diff --git a/main.cpp b/main.cpp index 78f6555..94e5720 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,6 @@ #include "app.hpp" -int main(int argc, char *argv[]) +int main() { App app; diff --git a/resources/bar.css b/resources/bar.css index 60990f0..543c22d 100644 --- a/resources/bar.css +++ b/resources/bar.css @@ -74,8 +74,14 @@ button { color: #ffffff; } -.workspace-pill-active { +.workspace-pill-alive { + background-color: rgba(255, 255, 255, 0.153); +} + +.workspace-pill-presenting { background-color: #666666; + color: #ffffff; + /* animation: workspace-updown 1.2s ease-in-out infinite; */ } .workspace-pill-focused { @@ -136,35 +142,3 @@ button { 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; -} \ No newline at end of file diff --git a/src/app.cpp b/src/app.cpp index e860987..b9096ef 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -6,9 +6,7 @@ App::App() { this->app = Gtk::Application::create("org.example.mybar"); - this->setupServices(); - this->notificationService.intialize(); this->hyprlandService = HyprlandService::getInstance(); app->signal_activate().connect([&]() { @@ -21,11 +19,15 @@ App::App() { ); if (monitor) { - auto bar = new Bar(monitor->gobj()); + auto bar = std::make_shared(monitor->gobj()); bar->set_application(app); 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); } } @@ -33,16 +35,12 @@ App::App() { app->signal_shutdown().connect([&]() { - for (auto bar : bars) { - delete bar; - } - bars.clear(); - this->trayService->stop(); }); } void App::setupServices() { + this->trayService->start(); } diff --git a/src/components/popover.cpp b/src/components/popover.cpp index ffcaa14..f4c1d6f 100644 --- a/src/components/popover.cpp +++ b/src/components/popover.cpp @@ -3,6 +3,8 @@ Popover::Popover(const std::string icon, std::string name): Button(icon) { signal_clicked().connect(sigc::mem_fun(*this, &Popover::on_toggle_window)); + set_name(name); + popover = new Gtk::Popover(); popover->set_parent(*this); popover->set_autohide(true); diff --git a/src/components/workspaceIndicator.cpp b/src/components/workspaceIndicator.cpp index 4b8359b..d6464b7 100644 --- a/src/components/workspaceIndicator.cpp +++ b/src/components/workspaceIndicator.cpp @@ -1,42 +1,52 @@ #include "components/workspaceIndicator.hpp" - #include -#include "gtkmm/button.h" +#include "gtkmm/gestureclick.h" #include "gtkmm/label.h" +#include "gtkmm/overlay.h" -WorkspaceIndicator::WorkspaceIndicator(std::string label, sigc::slot onClick) +WorkspaceIndicator::WorkspaceIndicator(int id, std::string label, sigc::slot onClick) : Gtk::Box(Gtk::Orientation::HORIZONTAL) { - auto overlay = Gtk::make_managed(); - auto numLabel = Gtk::make_managed(label); - auto pillContainer = Gtk::make_managed(Gtk::Orientation::HORIZONTAL); + overlay = std::make_shared(); + auto numLabel = Gtk::make_managed(label); + auto pillContainer = Gtk::make_managed(Gtk::Orientation::HORIZONTAL); - // auto gesture = Gtk::GestureClick::create(); - // gesture->set_button(GDK_BUTTON_PRIMARY); - // gesture->signal_released().connect([this, i](int, double, double) { - // this->service->switchToWorkspace( - // i + this->monitorId * HyprlandService::kWorkspaceSlotCount); - // }); + auto gesture = Gtk::GestureClick::create(); + gesture->set_button(GDK_BUTTON_PRIMARY); + gesture->signal_released().connect([this, id, onClick](int, double, double) { + onClick( + id); + }); + overlay->add_controller(gesture); + overlay->add_css_class("workspace-pill"); - // overlay->add_controller(gesture); - overlay->add_css_class("workspace-pill"); + if (id == 6 || id == 7) { + auto indicator = Gtk::make_managed(id == 6 ? "🫱🏻" : "🫲🏻"); + indicator->add_css_class(id == 6 ? "workspace-pill-six" : "workspace-pill-seven"); + indicator->set_valign(Gtk::Align::END); + overlay->set_child(*indicator); + overlay->add_overlay(*numLabel); + pillContainer->append(*overlay); + } else { + overlay->set_child(*numLabel); + pillContainer->append(*overlay); + } - // if (i == 6 || i == 7) { - // auto indicator = Gtk::make_managed(i == 6 ? "🫱🏻" : "🫲🏻"); - // indicator->add_css_class(i == 6 ? "workspace-pill-six" : "workspace-pill-seven"); - // indicator->set_valign(Gtk::Align::END); - // overlay->set_child(*indicator); - // overlay->add_overlay(*numLabel); - // pillContainer->append(*overlay); - // } else { - overlay->set_child(*numLabel); - 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); + } } \ No newline at end of file diff --git a/src/services/bluetooth.cpp b/src/services/bluetooth.cpp deleted file mode 100644 index c1baaed..0000000 --- a/src/services/bluetooth.cpp +++ /dev/null @@ -1,237 +0,0 @@ -#include "services/bluetooth.hpp" - -#include -#include -#include -#include - -#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(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 BluetoothService::getDeviceObjectPaths() { - std::vector 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; -} \ No newline at end of file diff --git a/src/services/hyprland.cpp b/src/services/hyprland.cpp index 89b32a2..3d43610 100644 --- a/src/services/hyprland.cpp +++ b/src/services/hyprland.cpp @@ -13,34 +13,38 @@ #include #include "helpers/hypr.hpp" -#include "helpers/socket.hpp" +#include "helpers/string.hpp" #include "gtkmm/box.h" -#include "gtkmm/button.h" 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) { auto monitorPtr = std::make_shared(); - int monitorId = item["id"].get(); + std::string monitorName = item["name"].get(); + int monitorId = item["id"].get(); monitorPtr->id = monitorId; - monitorPtr->name = item["name"].get(); - this->monitors[monitorPtr->id] = monitorPtr; + monitorPtr->name = monitorName; + monitorPtr->activeWorkspaceId = item["activeWorkspace"]["id"].get(); + monitorPtr->focused = item["focused"].get(); + this->monitors[monitorPtr->name] = monitorPtr; - // Create inidcators for hyprsplit for (int i = 1; i <= NUM_WORKSPACES; i++) { - WorkspaceState state; - int workspaceId = i + (NUM_WORKSPACES * monitorId); - - state.id = workspaceId; - state.monitorId = monitorId; - - auto view = std::make_shared(std::to_string(i)); + std::shared_ptr state = std::make_shared(); + int workspaceId = i + (NUM_WORKSPACES * monitorId); + state->id = workspaceId; + state->monitorName = monitorName; + auto view = std::make_shared(workspaceId, std::to_string(i), onClick); auto workSpace = std::make_shared(); workSpace->state = state; workSpace->view = view; @@ -50,30 +54,38 @@ HyprlandService::HyprlandService() { } } + auto clientsDataJson = HyprctlHelper::getClientData(); + + for (const auto &client : clientsDataJson) { + auto clientPtr = std::make_shared(); + clientPtr->address = client["address"].get(); + clientPtr->workspaceId = client["workspace"]["id"].get(); + clientPtr->title = client["title"].get(); + this->clients[clientPtr->address] = clientPtr; + + auto workspacePtr = workspaces[clientPtr->workspaceId]; + workspacePtr->state->clients[clientPtr->address] = clientPtr; + + if (client.contains("urgent") && client["urgent"].get()) { + workspacePtr->state->urgentClients.insert(clientPtr->address); + } + } + auto workspaceDataJson = HyprctlHelper::getWorkspaceData(); for (const auto &workspace : workspaceDataJson) { - auto workspacePtr = std::make_shared(); - workspacePtr->state.id = workspace["id"].get(); - workspacePtr->state.monitorId = workspace["monitorID"].get(); - workspaces[workspacePtr->state.id] = workspacePtr; + auto workspacePtr = workspaces[workspace["id"].get()]; + auto state = workspacePtr->state; + + state->id = workspace["id"].get(); + state->monitorName = workspace["monitor"].get(); + + 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() { - std::string socketPath = getSocketPath(); + std::string socketPath = HyprSocketHelper::getHyprlandSocketPath(); socketFd = socket(AF_UNIX, SOCK_STREAM, 0); if (socketFd == -1) { @@ -96,29 +108,220 @@ void HyprlandService::bindSocket() { GSource *source = g_unix_fd_source_new(socketFd, static_cast(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(user_data); auto messages = SocketHelper::parseSocketMessage(fd, ">>"); + for (const auto &message : messages) { + self->handleSocketMessage(message); + } + return G_SOURCE_CONTINUE; }; - g_source_set_callback(source, (GSourceFunc)(+onSocketEvent), this, nullptr); + g_source_set_callback(source, reinterpret_cast(reinterpret_cast(+onSocketEvent)), this, nullptr); g_source_attach(source, g_main_context_default()); g_source_unref(source); } -std::shared_ptr HyprlandService::getWorkspaceIndicatorsForMonitor(int monitorId) { - auto box = std::make_shared(Gtk::Orientation::HORIZONTAL); +void HyprlandService::onWorkspaceChanged(int workspaceId) { + 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; - for (const auto &[wsId, wsPair] : monitor->monitorWorkspaces) { - box->append((Gtk::Box &)*wsPair->view); - i++; + monitorPtr->activeWorkspaceId = workspaceId; + + refreshIndicator(newActive); + 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(); + 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 HyprlandService::getWorkspaceIndicatorsForMonitor(std::string monitorName) { + auto box = std::make_shared(Gtk::Orientation::HORIZONTAL); + auto monitor = monitors[monitorName]; + + for (const auto &[wsId, wsPair] : monitor->monitorWorkspaces) { + box->append((Gtk::Box &)*wsPair->view); + } return box; +} + +void HyprlandService::switchToWorkspace(int workspaceId) { + HyprctlHelper::dispatchWorkspace(workspaceId); +} + +void HyprlandService::refreshIndicator(std::shared_ptr 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, std::string monitorName) { + this->monitors[monitorName]->bar = bar; } \ No newline at end of file diff --git a/src/services/notifications.cpp b/src/services/notifications.cpp deleted file mode 100644 index efea285..0000000 --- a/src/services/notifications.cpp +++ /dev/null @@ -1,305 +0,0 @@ -#include "services/notifications.hpp" - -#include -#include - -static constexpr const char *kNotificationsObjectPath = "/org/freedesktop/Notifications"; -static constexpr const char *kNotificationsInterface = "org.freedesktop.Notifications"; - -static const char *kNotificationsIntrospectionXml = R"XML( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -)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(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( - 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; - } -} diff --git a/src/widgets/bluetooth.cpp b/src/widgets/bluetooth.cpp deleted file mode 100644 index 44d8486..0000000 --- a/src/widgets/bluetooth.cpp +++ /dev/null @@ -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