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").
This commit is contained in:
scorpion-26 2024-03-15 19:18:47 +01:00
parent f205a26fcc
commit 95f9b0aadb
19 changed files with 341 additions and 158 deletions

View file

@ -1,12 +1,11 @@
#include <gBar/Common.h>
#include <gBar/Window.h>
void Create(Window& window, int32_t monitor)
void Create(Window& window, const std::string& monitor)
{
auto mainWidget = Widget::Create<Text>();
mainWidget->SetText("Hello, World!");
window = Window(monitor);
window.SetMainWidget(std::move(mainWidget));
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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<Button*> 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 <monitor>' instead!");
monitor = monitorName;
auto mainWidget = Widget::Create<Box>();
mainWidget->SetOrientation(Utils::GetOrientation());

View file

@ -4,5 +4,5 @@
namespace Bar
{
void Create(Window& window, int32_t monitor);
void Create(Window& window, const std::string& monitor);
}

View file

@ -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<Box>();

View file

@ -4,5 +4,5 @@
namespace BluetoothDevices
{
void Create(Window& window, int32_t monitor);
void Create(Window& window, const std::string& monitor);
}

View file

@ -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); \
}

View file

@ -5,7 +5,7 @@
#include <dlfcn.h>
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<std::string, 3> 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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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 -

View file

@ -8,17 +8,15 @@
namespace Wayland
{
// There's probably a better way to avoid the LUTs
static std::unordered_map<std::string, Monitor> monitors;
static std::unordered_map<wl_output*, Monitor> monitors;
static std::unordered_map<zext_workspace_group_handle_v1*, WorkspaceGroup> workspaceGroups;
static std::unordered_map<zext_workspace_handle_v1*, Workspace> 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<std::string, Monitor>& 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<std::string, Monitor>& 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 <pending> 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<wl_output*, const Monitor&>& 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() ? "<pending>" : 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, &registryListener, 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<std::string, Monitor>& GetMonitors()
std::string GtkMonitorIDToName(int32_t monitorID)
{
auto it = std::find_if(monitors.begin(), monitors.end(),
[&](const std::pair<wl_output*, Monitor>& 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<wl_output*, Monitor>& el)
{
return el.second.name == name;
});
if (it == monitors.end())
return -1;
return it->second.ID;
}
const std::unordered_map<wl_output*, Monitor>& GetMonitors()
{
return monitors;
}

View file

@ -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<std::string, Monitor>& GetMonitors();
const std::unordered_map<wl_output*, Monitor>& GetMonitors();
const std::unordered_map<zext_workspace_group_handle_v1*, WorkspaceGroup>& GetWorkspaceGroups();
const std::unordered_map<zext_workspace_handle_v1*, Workspace>& GetWorkspaces();
// Returns the connector name of the monitor
std::string GtkMonitorIDToName(int32_t monitorID);
int32_t NameToGtkMonitorID(const std::string& name);
void Shutdown();
}

View file

@ -1,14 +1,15 @@
#include "Window.h"
#include "Common.h"
#include "CSS.h"
#include "Wayland.h"
#include <tuple>
#include <fstream>
#include <thread>
#include <gtk/gtk.h>
#include <gtk-layer-shell.h>
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<wl_output*, Wayland::Monitor>& 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<wl_output*, Wayland::Monitor>& 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<wl_output*, Wayland::Monitor>& 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<wl_output*, Wayland::Monitor>& 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();
}
}

View file

@ -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<Widget>&& 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<void()> 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;
};

View file

@ -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<std::string, ::Wayland::Monitor>& mon)
[&](const std::pair<wl_output*, ::Wayland::Monitor>& mon)
{
return mon.second.ID == workspaceId;
return mon.second.name == lastPolledMonitor;
});
if (it == mons.end())
{
@ -147,7 +147,7 @@ namespace Workspaces
static std::vector<System::WorkspaceStatus> 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 <name> (ID <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)

View file

@ -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);

View file

@ -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();