From 95f9b0aadb82c477bce404c77c243a7b8d88c193 Mon Sep 17 00:00:00 2001 From: scorpion-26 Date: Fri, 15 Mar 2024 19:18:47 +0100 Subject: [PATCH] Handle monitor changes Since there is no way to map a connector to a Gdk Window ID and window ID's are not static per monitor, the entire Window and Wayland system has been rewritten to store a "target monitor" connector name, which is the name of the monitor we want to be on. Every time a monitor is removed or added, it is checked against the monitors of our Wayland backend to match connector to Gdk ID and then the Window is created/destroyed on the window(The target monitor is preferred, but if it doesn't exist, other monitors are used too). This is not perfect, since the matching of connector name to Gdk ID is only an approximation of the Gdk behaviour (Especially rough on hyprland with the headless monitor. A hack is needed to handle that). Additionally the monitor can now be specified by connector name when starting up (e.g. "gBar bar DP-1"). --- example/main.cpp | 3 +- src/AudioFlyin.cpp | 2 +- src/AudioFlyin.h | 2 +- src/Bar.cpp | 9 ++- src/Bar.h | 2 +- src/BluetoothDevices.cpp | 2 +- src/BluetoothDevices.h | 2 +- src/Common.h | 16 ++-- src/Plugin.cpp | 12 +-- src/Plugin.h | 2 +- src/System.cpp | 2 +- src/System.h | 2 +- src/Wayland.cpp | 109 +++++++++++++++++--------- src/Wayland.h | 11 ++- src/Window.cpp | 160 +++++++++++++++++++++++++++++++-------- src/Window.h | 21 ++++- src/Workspaces.cpp | 28 +++---- src/Workspaces.h | 2 +- src/gBar.cpp | 112 ++++++++++++++++----------- 19 files changed, 341 insertions(+), 158 deletions(-) diff --git a/example/main.cpp b/example/main.cpp index 2fd363d..942f5b8 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -1,12 +1,11 @@ #include #include -void Create(Window& window, int32_t monitor) +void Create(Window& window, const std::string& monitor) { auto mainWidget = Widget::Create(); mainWidget->SetText("Hello, World!"); - window = Window(monitor); window.SetMainWidget(std::move(mainWidget)); } diff --git a/src/AudioFlyin.cpp b/src/AudioFlyin.cpp index 094acc9..0d314ef 100644 --- a/src/AudioFlyin.cpp +++ b/src/AudioFlyin.cpp @@ -130,7 +130,7 @@ namespace AudioFlyin parent.AddChild(std::move(icon)); } - void Create(Window& window, UNUSED int32_t monitor, Type type) + void Create(Window& window, UNUSED const std::string& monitor, Type type) { DynCtx::win = &window; DynCtx::type = type; diff --git a/src/AudioFlyin.h b/src/AudioFlyin.h index 55389c1..2fe45b6 100644 --- a/src/AudioFlyin.h +++ b/src/AudioFlyin.h @@ -9,5 +9,5 @@ namespace AudioFlyin Speaker, Microphone }; - void Create(Window& window, int32_t monitor, Type type); + void Create(Window& window, const std::string& monitor, Type type); } diff --git a/src/Bar.cpp b/src/Bar.cpp index 90b9856..46502ae 100644 --- a/src/Bar.cpp +++ b/src/Bar.cpp @@ -43,7 +43,7 @@ namespace Bar return (Config::Get().location == 'L' || Config::Get().location == 'R') && Config::Get().iconsAlwaysUp; } - static int32_t monitorID; + static std::string monitor; namespace DynCtx { @@ -393,7 +393,7 @@ namespace Bar static std::vector workspaces; TimerResult UpdateWorkspaces(Box&) { - System::PollWorkspaces((uint32_t)monitorID, workspaces.size()); + System::PollWorkspaces(monitor, workspaces.size()); for (size_t i = 0; i < workspaces.size(); i++) { switch (System::GetWorkspaceStatus(i + 1)) @@ -1187,9 +1187,10 @@ namespace Bar "VRAM, GPU, RAM, CPU, Battery, Power"); } - void Create(Window& window, int32_t monitor) + void Create(Window& window, const std::string& monitorName) { - monitorID = monitor; + ASSERT(!window.GetName().empty(), "Error: The bar requires a specified monitor. Use 'gBar bar ' instead!"); + monitor = monitorName; auto mainWidget = Widget::Create(); mainWidget->SetOrientation(Utils::GetOrientation()); diff --git a/src/Bar.h b/src/Bar.h index 9a52f78..150db5b 100644 --- a/src/Bar.h +++ b/src/Bar.h @@ -4,5 +4,5 @@ namespace Bar { - void Create(Window& window, int32_t monitor); + void Create(Window& window, const std::string& monitor); } diff --git a/src/BluetoothDevices.cpp b/src/BluetoothDevices.cpp index 6425bd2..0ebdfec 100644 --- a/src/BluetoothDevices.cpp +++ b/src/BluetoothDevices.cpp @@ -286,7 +286,7 @@ namespace BluetoothDevices parentWidget.AddChild(std::move(bodyBox)); } - void Create(Window& window, UNUSED int32_t monitor) + void Create(Window& window, UNUSED const std::string& monitor) { DynCtx::win = &window; auto mainWidget = Widget::Create(); diff --git a/src/BluetoothDevices.h b/src/BluetoothDevices.h index dd01214..5392b18 100644 --- a/src/BluetoothDevices.h +++ b/src/BluetoothDevices.h @@ -4,5 +4,5 @@ namespace BluetoothDevices { - void Create(Window& window, int32_t monitor); + void Create(Window& window, const std::string& monitor); } diff --git a/src/Common.h b/src/Common.h index 3fd93b3..552ba48 100644 --- a/src/Common.h +++ b/src/Common.h @@ -190,12 +190,12 @@ inline Process OpenProcess(const std::string& command) #include "Window.h" #define DL_VERSION 1 -#define DEFINE_PLUGIN(fun) \ - extern "C" int32_t Plugin_GetVersion() \ - { \ - return DL_VERSION; \ - }; \ - extern "C" void Plugin_InvokeCreateFun(void* window, int32_t monitor) \ - { \ - fun(*(Window*)window, monitor); \ +#define DEFINE_PLUGIN(fun) \ + extern "C" int32_t Plugin_GetVersion() \ + { \ + return DL_VERSION; \ + }; \ + extern "C" void Plugin_InvokeCreateFun(void* window, void* monitor) \ + { \ + fun(*(Window*)window, *(const std::string&*)monitor); \ } diff --git a/src/Plugin.cpp b/src/Plugin.cpp index ed9a5e5..39a9060 100644 --- a/src/Plugin.cpp +++ b/src/Plugin.cpp @@ -5,7 +5,7 @@ #include -void Plugin::LoadWidgetFromPlugin(const std::string& pluginName, Window& window, int32_t monitor) +void Plugin::LoadWidgetFromPlugin(const std::string& pluginName, Window& window, const std::string& monitor) { std::string home = std::getenv("HOME"); std::array paths = {home + "/.local/lib/gBar", "/usr/local/lib/gBar", "/usr/lib/gBar"}; @@ -18,19 +18,19 @@ void Plugin::LoadWidgetFromPlugin(const std::string& pluginName, Window& window, if (dl) break; } - ASSERT(dl, "Error: Cannot find plugin \"" << pluginName << "\"!\n" - "Note: Did you mean to run \"gBar bar\" instead?"); + ASSERT(dl, "Error: Cannot find plugin \"" << pluginName + << "\"!\n" + "Note: Did you mean to run \"gBar bar\" instead?"); - typedef void (*PFN_InvokeCreateFun)(void*, int32_t); + typedef void (*PFN_InvokeCreateFun)(void*, void*); typedef int32_t (*PFN_GetVersion)(); auto getVersion = (PFN_GetVersion)dlsym(dl, "Plugin_GetVersion"); ASSERT(getVersion, "DL is not a valid gBar plugin!"); ASSERT(getVersion() == DL_VERSION, "Mismatching version, please recompile your plugin!"); - typedef void (*PFN_InvokeCreateFun)(void*, int32_t); auto invokeCreateFun = (PFN_InvokeCreateFun)dlsym(dl, "Plugin_InvokeCreateFun"); ASSERT(invokeCreateFun, "DL is not a valid gBar plugin!"); // Execute - invokeCreateFun(&window, monitor); + invokeCreateFun(&window, (void*)&monitor); } diff --git a/src/Plugin.h b/src/Plugin.h index 314d41d..cbf3ffa 100644 --- a/src/Plugin.h +++ b/src/Plugin.h @@ -4,5 +4,5 @@ namespace Plugin { - void LoadWidgetFromPlugin(const std::string& pluginName, Window& window, int32_t monitor); + void LoadWidgetFromPlugin(const std::string& pluginName, Window& window, const std::string& monitor); } diff --git a/src/System.cpp b/src/System.cpp index a9d8a97..c627840 100644 --- a/src/System.cpp +++ b/src/System.cpp @@ -485,7 +485,7 @@ namespace System } #ifdef WITH_WORKSPACES - void PollWorkspaces(uint32_t monitor, uint32_t numWorkspaces) + void PollWorkspaces(const std::string& monitor, uint32_t numWorkspaces) { Workspaces::PollStatus(monitor, numWorkspaces); } diff --git a/src/System.h b/src/System.h index af03a48..89f6b64 100644 --- a/src/System.h +++ b/src/System.h @@ -95,7 +95,7 @@ namespace System Current, Active }; - void PollWorkspaces(uint32_t monitor, uint32_t numWorkspaces); + void PollWorkspaces(const std::string& monitor, uint32_t numWorkspaces); WorkspaceStatus GetWorkspaceStatus(uint32_t workspace); void GotoWorkspace(uint32_t workspace); // direction: + or - diff --git a/src/Wayland.cpp b/src/Wayland.cpp index f28a843..484bf02 100644 --- a/src/Wayland.cpp +++ b/src/Wayland.cpp @@ -8,17 +8,15 @@ namespace Wayland { // There's probably a better way to avoid the LUTs - static std::unordered_map monitors; + static std::unordered_map monitors; static std::unordered_map workspaceGroups; static std::unordered_map workspaces; - static uint32_t curID = 0; - static wl_display* display; static wl_registry* registry; static zext_workspace_manager_v1* workspaceManager; - static bool registeredMonitors = false; + static bool registeredMonitor = false; static bool registeredGroup = false; static bool registeredWorkspace = false; static bool registeredWorkspaceInfo = false; @@ -87,22 +85,14 @@ namespace Wayland // Workspace Group callbacks static void OnWSGroupOutputEnter(void*, zext_workspace_group_handle_v1* group, wl_output* output) { - auto monitor = std::find_if(monitors.begin(), monitors.end(), - [&](const std::pair& mon) - { - return mon.second.output == output; - }); + auto monitor = monitors.find(output); ASSERT(monitor != monitors.end(), "Wayland: Registered WS group before monitor!"); LOG("Wayland: Added group to monitor"); monitor->second.workspaceGroup = group; } static void OnWSGroupOutputLeave(void*, zext_workspace_group_handle_v1*, wl_output* output) { - auto monitor = std::find_if(monitors.begin(), monitors.end(), - [&](const std::pair& mon) - { - return mon.second.output == output; - }); + auto monitor = monitors.find(output); ASSERT(monitor != monitors.end(), "Wayland: Registered WS group before monitor!"); LOG("Wayland: Added group to monitor"); monitor->second.workspaceGroup = nullptr; @@ -140,24 +130,22 @@ namespace Wayland // Output Callbacks // Very bloated, indeed static void OnOutputGeometry(void*, wl_output*, int32_t, int32_t, int32_t, int32_t, int32_t, const char*, const char*, int32_t) {} - static void OnOutputMode(void*, wl_output*, uint32_t, int32_t, int32_t, int32_t) {} - static void OnOutputDone(void*, wl_output*) {} + static void OnOutputMode(void*, wl_output* output, uint32_t, int32_t width, int32_t height, int32_t) + { + auto it = monitors.find(output); + ASSERT(it != monitors.end(), "Error: OnOutputMode called on unknown monitor"); + it->second.width = width; + it->second.height = height; + } + static void OnOutputDone(void*, wl_output* output) {} static void OnOutputScale(void*, wl_output*, int32_t) {} static void OnOutputName(void*, wl_output* output, const char* name) { - std::string nameStr = name; - auto it = monitors.find(nameStr); - if (it == monitors.end()) - { - LOG("Wayland: Registering monitor " << name << " at ID " << curID); - registeredMonitors = true; - Monitor mon = {nameStr, output, nullptr, curID++}; - monitors.try_emplace(nameStr, mon); - } - else - { - LOG("Wayland: Recovering monitor " << name << " at ID " << curID); - } + auto it = monitors.find(output); + ASSERT(it != monitors.end(), "Error: OnOutputName called on unknown monitor"); + it->second.name = name; + LOG("Wayland: Monitor at ID " << it->second.ID << " got name " << name); + registeredMonitor = true; } static void OnOutputDescription(void*, wl_output*, const char*) {} wl_output_listener outputListener = {OnOutputGeometry, OnOutputMode, OnOutputDone, OnOutputScale, OnOutputName, OnOutputDescription}; @@ -168,6 +156,10 @@ namespace Wayland if (strcmp(interface, "wl_output") == 0) { wl_output* output = (wl_output*)wl_registry_bind(registry, name, &wl_output_interface, 4); + Monitor mon = Monitor{"", name, 0, 0, nullptr, (uint32_t)monitors.size()}; + monitors.emplace(output, mon); + + LOG("Wayland: Register at ID " << mon.ID); wl_output_add_listener(output, &outputListener, nullptr); } if (strcmp(interface, "zext_workspace_manager_v1") == 0 && !Config::Get().useHyprlandIPC) @@ -176,7 +168,31 @@ namespace Wayland zext_workspace_manager_v1_add_listener(workspaceManager, &workspaceManagerListener, nullptr); } } - static void OnRegistryRemove(void*, wl_registry*, uint32_t) {} + static void OnRegistryRemove(void*, wl_registry*, uint32_t name) + { + auto it = std::find_if(monitors.begin(), monitors.end(), + [&](const std::pair& elem) + { + return elem.second.wlName == name; + }); + if (it != monitors.end()) + { + LOG("Wayland: Removing monitor " << it->second.name << " at ID " << it->second.ID); + // Monitor has been removed. Update the ids of the other accordingly + for (auto& mon : monitors) + { + if (mon.second.ID > it->second.ID) + { + mon.second.ID -= 1; + auto name = mon.second.name.empty() ? "" : mon.second.name; + LOG("Wayland: " << name << " got new ID " << mon.second.ID); + } + } + registeredMonitor = true; + monitors.erase(it); + } + } + wl_registry_listener registryListener = {OnRegistryAdd, OnRegistryRemove}; // Dispatch events. @@ -201,8 +217,8 @@ namespace Wayland wl_registry_add_listener(registry, ®istryListener, nullptr); wl_display_roundtrip(display); - WaitFor(registeredMonitors); - registeredMonitors = false; + WaitFor(registeredMonitor); + registeredMonitor = false; if (!workspaceManager && !Config::Get().useHyprlandIPC) { @@ -255,7 +271,6 @@ namespace Wayland registeredGroup = false; registeredWorkspace = false; registeredWorkspaceInfo = false; - return; } void Shutdown() @@ -264,7 +279,33 @@ namespace Wayland wl_display_disconnect(display); } - const std::unordered_map& GetMonitors() + std::string GtkMonitorIDToName(int32_t monitorID) + { + auto it = std::find_if(monitors.begin(), monitors.end(), + [&](const std::pair& el) + { + return el.second.ID == (uint32_t)monitorID; + }); + if (it == monitors.end()) + { + LOG("Wayland: No monitor registered with ID " << monitorID); + return ""; + } + return it->second.name; + } + int32_t NameToGtkMonitorID(const std::string& name) + { + auto it = std::find_if(monitors.begin(), monitors.end(), + [&](const std::pair& el) + { + return el.second.name == name; + }); + if (it == monitors.end()) + return -1; + return it->second.ID; + } + + const std::unordered_map& GetMonitors() { return monitors; } diff --git a/src/Wayland.h b/src/Wayland.h index a656bc9..9f92e22 100644 --- a/src/Wayland.h +++ b/src/Wayland.h @@ -9,8 +9,11 @@ namespace Wayland struct Monitor { std::string name; - wl_output* output; + uint32_t wlName; + int32_t width; + int32_t height; zext_workspace_group_handle_v1* workspaceGroup; + // The Gdk monitor index. This is only a hacky approximation, since there is no way to get the wl_output from a GdkMonitor uint32_t ID; }; @@ -29,9 +32,13 @@ namespace Wayland void Init(); void PollEvents(); - const std::unordered_map& GetMonitors(); + const std::unordered_map& GetMonitors(); const std::unordered_map& GetWorkspaceGroups(); const std::unordered_map& GetWorkspaces(); + // Returns the connector name of the monitor + std::string GtkMonitorIDToName(int32_t monitorID); + int32_t NameToGtkMonitorID(const std::string& name); + void Shutdown(); } diff --git a/src/Window.cpp b/src/Window.cpp index 4d7b3b8..2f52cf7 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -1,14 +1,15 @@ #include "Window.h" #include "Common.h" #include "CSS.h" +#include "Wayland.h" -#include -#include +#include #include #include -Window::Window(int32_t monitor) : m_MonitorID(monitor) {} +Window::Window(int32_t monitor) : m_MonitorName(Wayland::GtkMonitorIDToName(monitor)) {} +Window::Window(const std::string& monitor) : m_MonitorName(monitor) {} Window::~Window() { @@ -21,6 +22,8 @@ Window::~Window() void Window::Init(const std::string& overideConfigLocation) { + m_TargetMonitor = m_MonitorName; + gtk_init(NULL, NULL); // Style @@ -30,13 +33,14 @@ void Window::Init(const std::string& overideConfigLocation) GdkDisplay* defaultDisplay = gdk_display_get_default(); ASSERT(defaultDisplay != nullptr, "Cannot get display!"); - if (m_MonitorID != -1) + if (!m_MonitorName.empty()) { - m_Monitor = gdk_display_get_monitor(defaultDisplay, m_MonitorID); - ASSERT(m_Monitor, "Cannot get monitor!"); + m_Monitor = gdk_display_get_monitor(defaultDisplay, Wayland::NameToGtkMonitorID(m_MonitorName)); + ASSERT(m_Monitor, "Cannot get monitor \"" << m_MonitorName << "\"!"); } else { + LOG("Window: Requested monitor not found. Falling back to current monitor!") m_Monitor = gdk_display_get_primary_monitor(defaultDisplay); } @@ -55,12 +59,87 @@ void Window::Init(const std::string& overideConfigLocation) void Window::Run() { - ASSERT(m_MainWidget, "Main Widget not set!"); + Create(); + while (gtk_main_iteration()) + { + if (bHandleMonitorChanges) + { + // Flush the event loop + while (gtk_events_pending()) + { + if (!gtk_main_iteration()) + break; + } + LOG("Window: Handling monitor changes"); + bHandleMonitorChanges = false; + + if (m_MonitorName == m_TargetMonitor) + { + // Don't care + continue; + } + // Process Wayland + Wayland::PollEvents(); + + GdkDisplay* display = gdk_display_get_default(); + auto& mons = Wayland::GetMonitors(); + + // HACK: Discrepancies are mostly caused by the HEADLESS monitor. Assume that. + bool bGotHeadless = (size_t)gdk_display_get_n_monitors(display) != mons.size(); + if (bGotHeadless) + { + LOG("Window: Found discrepancy between GDK and Wayland!"); + } + + // Try to find our target monitor + auto it = std::find_if(mons.begin(), mons.end(), + [&](const std::pair& mon) + { + return mon.second.name == m_TargetMonitor; + }); + if (it != mons.end()) + { + // Found target monitor, snap back. + if (m_MainWidget) + Destroy(); + m_MonitorName = m_TargetMonitor; + m_Monitor = gdk_display_get_monitor(display, bGotHeadless ? it->second.ID + 1 : it->second.ID); + Create(); + continue; + } + + // We haven't yet created, check if we can. + if (m_MainWidget == nullptr) + { + // Find a non-headless monitor + auto it = std::find_if(mons.begin(), mons.end(), + [&](const std::pair& mon) + { + return mon.second.name.find("HEADLESS") == std::string::npos; + }); + if (it == mons.end()) + continue; + m_MonitorName = it->second.name; + m_Monitor = gdk_display_get_monitor(display, bGotHeadless ? it->second.ID + 1 : it->second.ID); + Create(); + continue; + } + } + } +} + +void Window::Create() +{ + LOG("Window: Create on monitor " << m_MonitorName); m_Window = (GtkWindow*)gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_layer_init_for_window(m_Window); + // Notify our main method, that we want to init + OnWidget(); + + ASSERT(m_MainWidget, "Main Widget not set!"); + switch (m_Layer) { case Layer::Top: gtk_layer_set_layer(m_Window, GTK_LAYER_SHELL_LAYER_TOP); break; @@ -94,13 +173,18 @@ void Window::Run() Widget::CreateAndAddWidget(m_MainWidget.get(), (GtkWidget*)m_Window); gtk_widget_show_all((GtkWidget*)m_Window); +} - gtk_main(); +void Window::Destroy() +{ + LOG("Window: Destroy"); + m_MainWidget = nullptr; + gtk_widget_destroy((GtkWidget*)m_Window); } void Window::Close() { - gtk_widget_hide((GtkWidget*)m_Window); + Destroy(); gtk_main_quit(); } @@ -159,42 +243,52 @@ void Window::SetMargin(Anchor anchor, int32_t margin) int Window::GetWidth() const { - GdkRectangle rect{}; + // gdk_monitor_get_geometry is really unreliable for some reason. + // Use our wayland backend instead + + /*GdkRectangle rect{}; gdk_monitor_get_geometry(m_Monitor, &rect); - return rect.width; + return rect.width;*/ + auto& mons = Wayland::GetMonitors(); + auto it = std::find_if(mons.begin(), mons.end(), + [&](const std::pair& mon) + { + return mon.second.name == m_MonitorName; + }); + ASSERT(it != mons.end(), "Window: Couldn't find monitor"); + return it->second.width; } int Window::GetHeight() const { - GdkRectangle rect{}; + /*GdkRectangle rect{}; gdk_monitor_get_geometry(m_Monitor, &rect); - return rect.height; + return rect.height;*/ + + auto& mons = Wayland::GetMonitors(); + auto it = std::find_if(mons.begin(), mons.end(), + [&](const std::pair& mon) + { + return mon.second.name == m_MonitorName; + }); + ASSERT(it != mons.end(), "Window: Couldn't find monitor"); + return it->second.width; } -void Window::MonitorAdded(GdkDisplay* display, GdkMonitor* mon) +void Window::MonitorAdded(GdkDisplay*, GdkMonitor*) { - LOG("Window: Monitor added: " << mon); - if (!m_Monitor) - { - LOG("Window: Activating window"); - gtk_layer_set_monitor(m_Window, mon); - m_Monitor = mon; - gtk_widget_show_all((GtkWidget*)m_Window); - } + bHandleMonitorChanges = true; } -void Window::MonitorRemoved(GdkDisplay* display, GdkMonitor* mon) +void Window::MonitorRemoved(GdkDisplay*, GdkMonitor* mon) { - LOG("Window: Monitor removed: " << mon); + bHandleMonitorChanges = true; + // Immediately react if (mon == m_Monitor) { - // Hide the window, so it doesn't get rendered on an invalid monitor - gtk_widget_hide((GtkWidget*)m_Window); - // Notify gtk layer shell and redisplay window - gtk_layer_set_monitor(m_Window, nullptr); - m_Monitor = gtk_layer_get_monitor(m_Window); - LOG("Window: New Monitor: " << m_Monitor); - if (m_Monitor) - gtk_widget_show_all((GtkWidget*)m_Window); + LOG("Window: Current monitor removed!") + m_Monitor = nullptr; + m_MonitorName = ""; + Destroy(); } } diff --git a/src/Window.h b/src/Window.h index e7914bd..0ce5d87 100644 --- a/src/Window.h +++ b/src/Window.h @@ -23,6 +23,7 @@ class Window public: Window() = default; Window(int32_t monitor); + Window(const std::string& monitor); Window(Window&& window) noexcept = default; Window& operator=(Window&& other) noexcept = default; ~Window(); @@ -35,14 +36,23 @@ public: void SetAnchor(Anchor anchor) { m_Anchor = anchor; } void SetMargin(Anchor anchor, int32_t margin); void SetExclusive(bool exclusive) { m_Exclusive = exclusive; } - void SetLayer(Layer layer) { m_Layer = layer;} + void SetLayer(Layer layer) { m_Layer = layer; } void SetMainWidget(std::unique_ptr&& mainWidget); int GetWidth() const; int GetHeight() const; + // Returns the connector name of the currnet monitor + std::string GetName() const { return m_MonitorName; } + + // Callback when the widget should be recreated + std::function OnWidget; + private: + void Create(); + void Destroy(); + void UpdateMargin(); void LoadCSS(GtkCssProvider* provider); @@ -60,6 +70,13 @@ private: bool m_Exclusive = true; Layer m_Layer = Layer::Top; - int32_t m_MonitorID; + // The monitor we are currently on. + std::string m_MonitorName; + + // The monitor we want to be on. + std::string m_TargetMonitor; + GdkMonitor* m_Monitor = nullptr; + + bool bHandleMonitorChanges = false; }; diff --git a/src/Workspaces.cpp b/src/Workspaces.cpp index 249b771..9f10f3b 100644 --- a/src/Workspaces.cpp +++ b/src/Workspaces.cpp @@ -12,19 +12,19 @@ namespace Workspaces using WaylandWorkspaceGroup = ::Wayland::WorkspaceGroup; using WaylandWorkspace = ::Wayland::Workspace; - static uint32_t lastPolledMonitor; - void PollStatus(uint32_t monitorID, uint32_t) + static std::string lastPolledMonitor; + void PollStatus(const std::string& monitor, uint32_t) { ::Wayland::PollEvents(); - lastPolledMonitor = monitorID; + lastPolledMonitor = monitor; } System::WorkspaceStatus GetStatus(uint32_t workspaceId) { auto& mons = ::Wayland::GetMonitors(); auto it = std::find_if(mons.begin(), mons.end(), - [&](const std::pair& mon) + [&](const std::pair& mon) { - return mon.second.ID == workspaceId; + return mon.second.name == lastPolledMonitor; }); if (it == mons.end()) { @@ -147,7 +147,7 @@ namespace Workspaces static std::vector workspaceStati; - void PollStatus(uint32_t monitorID, uint32_t numWorkspaces) + void PollStatus(const std::string& monitor, uint32_t numWorkspaces) { if (RuntimeConfig::Get().hasWorkspaces == false) { @@ -182,11 +182,11 @@ namespace Workspaces parseIdx = 0; while ((parseIdx = monitors.find("Monitor ", parseIdx)) != std::string::npos) { - // Goto ( and remove ID (=Advance 4 spaces, 1 for (, two for ID, one for space) - size_t begMonNum = monitors.find('(', parseIdx) + 4; - size_t endMonNum = monitors.find(')', begMonNum); + // Query monitor name + // Format: Monitor (ID ) + size_t begMonNum = monitors.find(' ', parseIdx) + 1; + size_t endMonNum = monitors.find(' ', begMonNum); std::string mon = monitors.substr(begMonNum, endMonNum - begMonNum); - int32_t monIdx = std::atoi(mon.c_str()); // Parse active workspace parseIdx = monitors.find("active workspace: ", parseIdx); @@ -205,7 +205,7 @@ namespace Workspaces if (wsId >= 1 && wsId <= (int32_t)numWorkspaces) { - if ((uint32_t)monIdx == monitorID) + if (mon == monitor) { if (focused) { @@ -248,16 +248,16 @@ namespace Workspaces #endif } - void PollStatus(uint32_t monitorID, uint32_t numWorkspaces) + void PollStatus(const std::string& monitor, uint32_t numWorkspaces) { #ifdef WITH_HYPRLAND if (Config::Get().useHyprlandIPC) { - Hyprland::PollStatus(monitorID, numWorkspaces); + Hyprland::PollStatus(monitor, numWorkspaces); return; } #endif - Wayland::PollStatus(monitorID, numWorkspaces); + Wayland::PollStatus(monitor, numWorkspaces); } System::WorkspaceStatus GetStatus(uint32_t workspaceId) diff --git a/src/Workspaces.h b/src/Workspaces.h index 6981667..884ad11 100644 --- a/src/Workspaces.h +++ b/src/Workspaces.h @@ -16,7 +16,7 @@ namespace Workspaces { void Init(); - void PollStatus(uint32_t monitorID, uint32_t numWorkspaces); + void PollStatus(const std::string& monitor, uint32_t numWorkspaces); System::WorkspaceStatus GetStatus(uint32_t workspaceId); diff --git a/src/gBar.cpp b/src/gBar.cpp index 16c1f09..ddeca96 100644 --- a/src/gBar.cpp +++ b/src/gBar.cpp @@ -16,7 +16,7 @@ const char* bluetoothTmpFilePath = "/tmp/gBar__bluetooth"; static bool tmpFileOpen = false; -void OpenAudioFlyin(Window& window, int32_t monitor, AudioFlyin::Type type) +void OpenAudioFlyin(Window& window, const std::string& monitor, AudioFlyin::Type type) { tmpFileOpen = true; if (access(audioTmpFilePath, F_OK) != 0) @@ -71,10 +71,57 @@ void PrintHelp() "\t[plugin] \tTries to open and run the plugin lib[plugin].so\n"); } +void CreateWidget(const std::string& widget, Window& window) +{ + if (widget == "bar") + { + Bar::Create(window, window.GetName()); + } + else if (widget == "audio") + { + OpenAudioFlyin(window, window.GetName(), AudioFlyin::Type::Speaker); + } + else if (widget == "mic") + { + OpenAudioFlyin(window, window.GetName(), AudioFlyin::Type::Microphone); + } +#ifdef WITH_BLUEZ + else if (widget == "bluetooth") + { + if (RuntimeConfig::Get().hasBlueZ) + { + if (access(bluetoothTmpFilePath, F_OK) != 0) + { + tmpFileOpen = true; + FILE* bluetoothTmpFile = fopen(bluetoothTmpFilePath, "w"); + BluetoothDevices::Create(window, window.GetName()); + fclose(bluetoothTmpFile); + } + else + { + // Already open, close + LOG("Bluetooth widget already open (/tmp/gBar__bluetooth exists)! Exiting..."); + exit(0); + } + } + else + { + LOG("Blutooth disabled, cannot open bluetooth widget!"); + exit(1); + } + } +#endif + else + { + Plugin::LoadWidgetFromPlugin(widget, window, window.GetName()); + } +} + int main(int argc, char** argv) { std::string widget; int32_t monitor = -1; + std::string monitorName; std::string overrideConfigLocation = ""; // Arg parsing @@ -83,20 +130,27 @@ int main(int argc, char** argv) std::string arg = argv[i]; if (arg.size() < 1 || arg[0] != '-') { - // This must be the widget selection + // This must be the widget selection. widget = arg; if (i + 1 < argc) { std::string mon = argv[i + 1]; - if (mon.size() < 1 || mon[0] != '-') + + // Check if a monitor was supplied + if (mon.empty() || mon[0] == '-') + continue; + + if (std::isdigit(mon[0])) { - // Next comes the monitor + // Monitor using ID monitor = std::stoi(mon); i += 1; } else { - // Not the monitor + // Monitor using connector name + monitorName = std::move(mon); + i += 1; continue; } } @@ -133,51 +187,21 @@ int main(int argc, char** argv) signal(SIGINT, CloseTmpFiles); System::Init(overrideConfigLocation); - Window window(monitor); - window.Init(overrideConfigLocation); - if (widget == "bar") + Window window; + if (monitor != -1) { - Bar::Create(window, monitor); + window = Window(monitor); } - else if (widget == "audio") - { - OpenAudioFlyin(window, monitor, AudioFlyin::Type::Speaker); - } - else if (widget == "mic") - { - OpenAudioFlyin(window, monitor, AudioFlyin::Type::Microphone); - } -#ifdef WITH_BLUEZ - else if (widget == "bluetooth") - { - if (RuntimeConfig::Get().hasBlueZ) - { - if (access(bluetoothTmpFilePath, F_OK) != 0) - { - tmpFileOpen = true; - FILE* bluetoothTmpFile = fopen(bluetoothTmpFilePath, "w"); - BluetoothDevices::Create(window, monitor); - fclose(bluetoothTmpFile); - } - else - { - // Already open, close - LOG("Bluetooth widget already open (/tmp/gBar__bluetooth exists)! Exiting..."); - exit(0); - } - } - else - { - LOG("Blutooth disabled, cannot open bluetooth widget!"); - exit(1); - } - } -#endif else { - Plugin::LoadWidgetFromPlugin(widget, window, monitor); + window = Window(monitorName); } + window.Init(overrideConfigLocation); + window.OnWidget = [&]() + { + CreateWidget(widget, window); + }; window.Run(); System::FreeResources();