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/Common.h>
#include <gBar/Window.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>(); auto mainWidget = Widget::Create<Text>();
mainWidget->SetText("Hello, World!"); mainWidget->SetText("Hello, World!");
window = Window(monitor);
window.SetMainWidget(std::move(mainWidget)); window.SetMainWidget(std::move(mainWidget));
} }

View file

@ -130,7 +130,7 @@ namespace AudioFlyin
parent.AddChild(std::move(icon)); 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::win = &window;
DynCtx::type = type; DynCtx::type = type;

View file

@ -9,5 +9,5 @@ namespace AudioFlyin
Speaker, Speaker,
Microphone 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; return (Config::Get().location == 'L' || Config::Get().location == 'R') && Config::Get().iconsAlwaysUp;
} }
static int32_t monitorID; static std::string monitor;
namespace DynCtx namespace DynCtx
{ {
@ -393,7 +393,7 @@ namespace Bar
static std::vector<Button*> workspaces; static std::vector<Button*> workspaces;
TimerResult UpdateWorkspaces(Box&) TimerResult UpdateWorkspaces(Box&)
{ {
System::PollWorkspaces((uint32_t)monitorID, workspaces.size()); System::PollWorkspaces(monitor, workspaces.size());
for (size_t i = 0; i < workspaces.size(); i++) for (size_t i = 0; i < workspaces.size(); i++)
{ {
switch (System::GetWorkspaceStatus(i + 1)) switch (System::GetWorkspaceStatus(i + 1))
@ -1187,9 +1187,10 @@ namespace Bar
"VRAM, GPU, RAM, CPU, Battery, Power"); "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>(); auto mainWidget = Widget::Create<Box>();
mainWidget->SetOrientation(Utils::GetOrientation()); mainWidget->SetOrientation(Utils::GetOrientation());

View file

@ -4,5 +4,5 @@
namespace Bar 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)); 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; DynCtx::win = &window;
auto mainWidget = Widget::Create<Box>(); auto mainWidget = Widget::Create<Box>();

View file

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

View file

@ -195,7 +195,7 @@ inline Process OpenProcess(const std::string& command)
{ \ { \
return DL_VERSION; \ return DL_VERSION; \
}; \ }; \
extern "C" void Plugin_InvokeCreateFun(void* window, int32_t monitor) \ extern "C" void Plugin_InvokeCreateFun(void* window, void* monitor) \
{ \ { \
fun(*(Window*)window, monitor); \ fun(*(Window*)window, *(const std::string&*)monitor); \
} }

View file

@ -5,7 +5,7 @@
#include <dlfcn.h> #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::string home = std::getenv("HOME");
std::array<std::string, 3> paths = {home + "/.local/lib/gBar", "/usr/local/lib/gBar", "/usr/lib/gBar"}; 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) if (dl)
break; break;
} }
ASSERT(dl, "Error: Cannot find plugin \"" << pluginName << "\"!\n" ASSERT(dl, "Error: Cannot find plugin \"" << pluginName
<< "\"!\n"
"Note: Did you mean to run \"gBar bar\" instead?"); "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)(); typedef int32_t (*PFN_GetVersion)();
auto getVersion = (PFN_GetVersion)dlsym(dl, "Plugin_GetVersion"); auto getVersion = (PFN_GetVersion)dlsym(dl, "Plugin_GetVersion");
ASSERT(getVersion, "DL is not a valid gBar plugin!"); ASSERT(getVersion, "DL is not a valid gBar plugin!");
ASSERT(getVersion() == DL_VERSION, "Mismatching version, please recompile your 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"); auto invokeCreateFun = (PFN_InvokeCreateFun)dlsym(dl, "Plugin_InvokeCreateFun");
ASSERT(invokeCreateFun, "DL is not a valid gBar plugin!"); ASSERT(invokeCreateFun, "DL is not a valid gBar plugin!");
// Execute // Execute
invokeCreateFun(&window, monitor); invokeCreateFun(&window, (void*)&monitor);
} }

View file

@ -4,5 +4,5 @@
namespace Plugin 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 #ifdef WITH_WORKSPACES
void PollWorkspaces(uint32_t monitor, uint32_t numWorkspaces) void PollWorkspaces(const std::string& monitor, uint32_t numWorkspaces)
{ {
Workspaces::PollStatus(monitor, numWorkspaces); Workspaces::PollStatus(monitor, numWorkspaces);
} }

View file

@ -95,7 +95,7 @@ namespace System
Current, Current,
Active Active
}; };
void PollWorkspaces(uint32_t monitor, uint32_t numWorkspaces); void PollWorkspaces(const std::string& monitor, uint32_t numWorkspaces);
WorkspaceStatus GetWorkspaceStatus(uint32_t workspace); WorkspaceStatus GetWorkspaceStatus(uint32_t workspace);
void GotoWorkspace(uint32_t workspace); void GotoWorkspace(uint32_t workspace);
// direction: + or - // direction: + or -

View file

@ -8,17 +8,15 @@
namespace Wayland namespace Wayland
{ {
// There's probably a better way to avoid the LUTs // 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_group_handle_v1*, WorkspaceGroup> workspaceGroups;
static std::unordered_map<zext_workspace_handle_v1*, Workspace> workspaces; static std::unordered_map<zext_workspace_handle_v1*, Workspace> workspaces;
static uint32_t curID = 0;
static wl_display* display; static wl_display* display;
static wl_registry* registry; static wl_registry* registry;
static zext_workspace_manager_v1* workspaceManager; static zext_workspace_manager_v1* workspaceManager;
static bool registeredMonitors = false; static bool registeredMonitor = false;
static bool registeredGroup = false; static bool registeredGroup = false;
static bool registeredWorkspace = false; static bool registeredWorkspace = false;
static bool registeredWorkspaceInfo = false; static bool registeredWorkspaceInfo = false;
@ -87,22 +85,14 @@ namespace Wayland
// Workspace Group callbacks // Workspace Group callbacks
static void OnWSGroupOutputEnter(void*, zext_workspace_group_handle_v1* group, wl_output* output) static void OnWSGroupOutputEnter(void*, zext_workspace_group_handle_v1* group, wl_output* output)
{ {
auto monitor = std::find_if(monitors.begin(), monitors.end(), auto monitor = monitors.find(output);
[&](const std::pair<std::string, Monitor>& mon)
{
return mon.second.output == output;
});
ASSERT(monitor != monitors.end(), "Wayland: Registered WS group before monitor!"); ASSERT(monitor != monitors.end(), "Wayland: Registered WS group before monitor!");
LOG("Wayland: Added group to monitor"); LOG("Wayland: Added group to monitor");
monitor->second.workspaceGroup = group; monitor->second.workspaceGroup = group;
} }
static void OnWSGroupOutputLeave(void*, zext_workspace_group_handle_v1*, wl_output* output) static void OnWSGroupOutputLeave(void*, zext_workspace_group_handle_v1*, wl_output* output)
{ {
auto monitor = std::find_if(monitors.begin(), monitors.end(), auto monitor = monitors.find(output);
[&](const std::pair<std::string, Monitor>& mon)
{
return mon.second.output == output;
});
ASSERT(monitor != monitors.end(), "Wayland: Registered WS group before monitor!"); ASSERT(monitor != monitors.end(), "Wayland: Registered WS group before monitor!");
LOG("Wayland: Added group to monitor"); LOG("Wayland: Added group to monitor");
monitor->second.workspaceGroup = nullptr; monitor->second.workspaceGroup = nullptr;
@ -140,24 +130,22 @@ namespace Wayland
// Output Callbacks // Output Callbacks
// Very bloated, indeed // 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 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 OnOutputMode(void*, wl_output* output, uint32_t, int32_t width, int32_t height, int32_t)
static void OnOutputDone(void*, wl_output*) {} {
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 OnOutputScale(void*, wl_output*, int32_t) {}
static void OnOutputName(void*, wl_output* output, const char* name) static void OnOutputName(void*, wl_output* output, const char* name)
{ {
std::string nameStr = name; auto it = monitors.find(output);
auto it = monitors.find(nameStr); ASSERT(it != monitors.end(), "Error: OnOutputName called on unknown monitor");
if (it == monitors.end()) it->second.name = name;
{ LOG("Wayland: Monitor at ID " << it->second.ID << " got name " << name);
LOG("Wayland: Registering monitor " << name << " at ID " << curID); registeredMonitor = true;
registeredMonitors = true;
Monitor mon = {nameStr, output, nullptr, curID++};
monitors.try_emplace(nameStr, mon);
}
else
{
LOG("Wayland: Recovering monitor " << name << " at ID " << curID);
}
} }
static void OnOutputDescription(void*, wl_output*, const char*) {} static void OnOutputDescription(void*, wl_output*, const char*) {}
wl_output_listener outputListener = {OnOutputGeometry, OnOutputMode, OnOutputDone, OnOutputScale, OnOutputName, OnOutputDescription}; wl_output_listener outputListener = {OnOutputGeometry, OnOutputMode, OnOutputDone, OnOutputScale, OnOutputName, OnOutputDescription};
@ -168,6 +156,10 @@ namespace Wayland
if (strcmp(interface, "wl_output") == 0) if (strcmp(interface, "wl_output") == 0)
{ {
wl_output* output = (wl_output*)wl_registry_bind(registry, name, &wl_output_interface, 4); 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); wl_output_add_listener(output, &outputListener, nullptr);
} }
if (strcmp(interface, "zext_workspace_manager_v1") == 0 && !Config::Get().useHyprlandIPC) 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); 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}; wl_registry_listener registryListener = {OnRegistryAdd, OnRegistryRemove};
// Dispatch events. // Dispatch events.
@ -201,8 +217,8 @@ namespace Wayland
wl_registry_add_listener(registry, &registryListener, nullptr); wl_registry_add_listener(registry, &registryListener, nullptr);
wl_display_roundtrip(display); wl_display_roundtrip(display);
WaitFor(registeredMonitors); WaitFor(registeredMonitor);
registeredMonitors = false; registeredMonitor = false;
if (!workspaceManager && !Config::Get().useHyprlandIPC) if (!workspaceManager && !Config::Get().useHyprlandIPC)
{ {
@ -255,7 +271,6 @@ namespace Wayland
registeredGroup = false; registeredGroup = false;
registeredWorkspace = false; registeredWorkspace = false;
registeredWorkspaceInfo = false; registeredWorkspaceInfo = false;
return;
} }
void Shutdown() void Shutdown()
@ -264,7 +279,33 @@ namespace Wayland
wl_display_disconnect(display); 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; return monitors;
} }

View file

@ -9,8 +9,11 @@ namespace Wayland
struct Monitor struct Monitor
{ {
std::string name; std::string name;
wl_output* output; uint32_t wlName;
int32_t width;
int32_t height;
zext_workspace_group_handle_v1* workspaceGroup; 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; uint32_t ID;
}; };
@ -29,9 +32,13 @@ namespace Wayland
void Init(); void Init();
void PollEvents(); 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_group_handle_v1*, WorkspaceGroup>& GetWorkspaceGroups();
const std::unordered_map<zext_workspace_handle_v1*, Workspace>& GetWorkspaces(); 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(); void Shutdown();
} }

View file

@ -1,14 +1,15 @@
#include "Window.h" #include "Window.h"
#include "Common.h" #include "Common.h"
#include "CSS.h" #include "CSS.h"
#include "Wayland.h"
#include <tuple> #include <thread>
#include <fstream>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <gtk-layer-shell.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() Window::~Window()
{ {
@ -21,6 +22,8 @@ Window::~Window()
void Window::Init(const std::string& overideConfigLocation) void Window::Init(const std::string& overideConfigLocation)
{ {
m_TargetMonitor = m_MonitorName;
gtk_init(NULL, NULL); gtk_init(NULL, NULL);
// Style // Style
@ -30,13 +33,14 @@ void Window::Init(const std::string& overideConfigLocation)
GdkDisplay* defaultDisplay = gdk_display_get_default(); GdkDisplay* defaultDisplay = gdk_display_get_default();
ASSERT(defaultDisplay != nullptr, "Cannot get display!"); ASSERT(defaultDisplay != nullptr, "Cannot get display!");
if (m_MonitorID != -1) if (!m_MonitorName.empty())
{ {
m_Monitor = gdk_display_get_monitor(defaultDisplay, m_MonitorID); m_Monitor = gdk_display_get_monitor(defaultDisplay, Wayland::NameToGtkMonitorID(m_MonitorName));
ASSERT(m_Monitor, "Cannot get monitor!"); ASSERT(m_Monitor, "Cannot get monitor \"" << m_MonitorName << "\"!");
} }
else else
{ {
LOG("Window: Requested monitor not found. Falling back to current monitor!")
m_Monitor = gdk_display_get_primary_monitor(defaultDisplay); m_Monitor = gdk_display_get_primary_monitor(defaultDisplay);
} }
@ -55,12 +59,87 @@ void Window::Init(const std::string& overideConfigLocation)
void Window::Run() 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); m_Window = (GtkWindow*)gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_layer_init_for_window(m_Window); 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) switch (m_Layer)
{ {
case Layer::Top: gtk_layer_set_layer(m_Window, GTK_LAYER_SHELL_LAYER_TOP); break; 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); Widget::CreateAndAddWidget(m_MainWidget.get(), (GtkWidget*)m_Window);
gtk_widget_show_all((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() void Window::Close()
{ {
gtk_widget_hide((GtkWidget*)m_Window); Destroy();
gtk_main_quit(); gtk_main_quit();
} }
@ -159,42 +243,52 @@ void Window::SetMargin(Anchor anchor, int32_t margin)
int Window::GetWidth() const 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); 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 int Window::GetHeight() const
{ {
GdkRectangle rect{}; /*GdkRectangle rect{};
gdk_monitor_get_geometry(m_Monitor, &rect); gdk_monitor_get_geometry(m_Monitor, &rect);
return rect.height; return rect.height;*/
}
void Window::MonitorAdded(GdkDisplay* display, GdkMonitor* mon) auto& mons = Wayland::GetMonitors();
{ auto it = std::find_if(mons.begin(), mons.end(),
LOG("Window: Monitor added: " << mon); [&](const std::pair<wl_output*, Wayland::Monitor>& mon)
if (!m_Monitor)
{ {
LOG("Window: Activating window"); return mon.second.name == m_MonitorName;
gtk_layer_set_monitor(m_Window, mon); });
m_Monitor = mon; ASSERT(it != mons.end(), "Window: Couldn't find monitor");
gtk_widget_show_all((GtkWidget*)m_Window); return it->second.width;
}
} }
void Window::MonitorRemoved(GdkDisplay* display, GdkMonitor* mon) void Window::MonitorAdded(GdkDisplay*, GdkMonitor*)
{ {
LOG("Window: Monitor removed: " << mon); bHandleMonitorChanges = true;
}
void Window::MonitorRemoved(GdkDisplay*, GdkMonitor* mon)
{
bHandleMonitorChanges = true;
// Immediately react
if (mon == m_Monitor) if (mon == m_Monitor)
{ {
// Hide the window, so it doesn't get rendered on an invalid monitor LOG("Window: Current monitor removed!")
gtk_widget_hide((GtkWidget*)m_Window); m_Monitor = nullptr;
// Notify gtk layer shell and redisplay window m_MonitorName = "";
gtk_layer_set_monitor(m_Window, nullptr); Destroy();
m_Monitor = gtk_layer_get_monitor(m_Window);
LOG("Window: New Monitor: " << m_Monitor);
if (m_Monitor)
gtk_widget_show_all((GtkWidget*)m_Window);
} }
} }

View file

@ -23,6 +23,7 @@ class Window
public: public:
Window() = default; Window() = default;
Window(int32_t monitor); Window(int32_t monitor);
Window(const std::string& monitor);
Window(Window&& window) noexcept = default; Window(Window&& window) noexcept = default;
Window& operator=(Window&& other) noexcept = default; Window& operator=(Window&& other) noexcept = default;
~Window(); ~Window();
@ -35,14 +36,23 @@ public:
void SetAnchor(Anchor anchor) { m_Anchor = anchor; } void SetAnchor(Anchor anchor) { m_Anchor = anchor; }
void SetMargin(Anchor anchor, int32_t margin); void SetMargin(Anchor anchor, int32_t margin);
void SetExclusive(bool exclusive) { m_Exclusive = exclusive; } 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); void SetMainWidget(std::unique_ptr<Widget>&& mainWidget);
int GetWidth() const; int GetWidth() const;
int GetHeight() 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: private:
void Create();
void Destroy();
void UpdateMargin(); void UpdateMargin();
void LoadCSS(GtkCssProvider* provider); void LoadCSS(GtkCssProvider* provider);
@ -60,6 +70,13 @@ private:
bool m_Exclusive = true; bool m_Exclusive = true;
Layer m_Layer = Layer::Top; 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; GdkMonitor* m_Monitor = nullptr;
bool bHandleMonitorChanges = false;
}; };

View file

@ -12,19 +12,19 @@ namespace Workspaces
using WaylandWorkspaceGroup = ::Wayland::WorkspaceGroup; using WaylandWorkspaceGroup = ::Wayland::WorkspaceGroup;
using WaylandWorkspace = ::Wayland::Workspace; using WaylandWorkspace = ::Wayland::Workspace;
static uint32_t lastPolledMonitor; static std::string lastPolledMonitor;
void PollStatus(uint32_t monitorID, uint32_t) void PollStatus(const std::string& monitor, uint32_t)
{ {
::Wayland::PollEvents(); ::Wayland::PollEvents();
lastPolledMonitor = monitorID; lastPolledMonitor = monitor;
} }
System::WorkspaceStatus GetStatus(uint32_t workspaceId) System::WorkspaceStatus GetStatus(uint32_t workspaceId)
{ {
auto& mons = ::Wayland::GetMonitors(); auto& mons = ::Wayland::GetMonitors();
auto it = std::find_if(mons.begin(), mons.end(), 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()) if (it == mons.end())
{ {
@ -147,7 +147,7 @@ namespace Workspaces
static std::vector<System::WorkspaceStatus> workspaceStati; 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) if (RuntimeConfig::Get().hasWorkspaces == false)
{ {
@ -182,11 +182,11 @@ namespace Workspaces
parseIdx = 0; parseIdx = 0;
while ((parseIdx = monitors.find("Monitor ", parseIdx)) != std::string::npos) while ((parseIdx = monitors.find("Monitor ", parseIdx)) != std::string::npos)
{ {
// Goto ( and remove ID (=Advance 4 spaces, 1 for (, two for ID, one for space) // Query monitor name
size_t begMonNum = monitors.find('(', parseIdx) + 4; // Format: Monitor <name> (ID <id>)
size_t endMonNum = monitors.find(')', begMonNum); size_t begMonNum = monitors.find(' ', parseIdx) + 1;
size_t endMonNum = monitors.find(' ', begMonNum);
std::string mon = monitors.substr(begMonNum, endMonNum - begMonNum); std::string mon = monitors.substr(begMonNum, endMonNum - begMonNum);
int32_t monIdx = std::atoi(mon.c_str());
// Parse active workspace // Parse active workspace
parseIdx = monitors.find("active workspace: ", parseIdx); parseIdx = monitors.find("active workspace: ", parseIdx);
@ -205,7 +205,7 @@ namespace Workspaces
if (wsId >= 1 && wsId <= (int32_t)numWorkspaces) if (wsId >= 1 && wsId <= (int32_t)numWorkspaces)
{ {
if ((uint32_t)monIdx == monitorID) if (mon == monitor)
{ {
if (focused) if (focused)
{ {
@ -248,16 +248,16 @@ namespace Workspaces
#endif #endif
} }
void PollStatus(uint32_t monitorID, uint32_t numWorkspaces) void PollStatus(const std::string& monitor, uint32_t numWorkspaces)
{ {
#ifdef WITH_HYPRLAND #ifdef WITH_HYPRLAND
if (Config::Get().useHyprlandIPC) if (Config::Get().useHyprlandIPC)
{ {
Hyprland::PollStatus(monitorID, numWorkspaces); Hyprland::PollStatus(monitor, numWorkspaces);
return; return;
} }
#endif #endif
Wayland::PollStatus(monitorID, numWorkspaces); Wayland::PollStatus(monitor, numWorkspaces);
} }
System::WorkspaceStatus GetStatus(uint32_t workspaceId) System::WorkspaceStatus GetStatus(uint32_t workspaceId)

View file

@ -16,7 +16,7 @@ namespace Workspaces
{ {
void Init(); 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); System::WorkspaceStatus GetStatus(uint32_t workspaceId);

View file

@ -16,7 +16,7 @@ const char* bluetoothTmpFilePath = "/tmp/gBar__bluetooth";
static bool tmpFileOpen = false; 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; tmpFileOpen = true;
if (access(audioTmpFilePath, F_OK) != 0) 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"); "\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) int main(int argc, char** argv)
{ {
std::string widget; std::string widget;
int32_t monitor = -1; int32_t monitor = -1;
std::string monitorName;
std::string overrideConfigLocation = ""; std::string overrideConfigLocation = "";
// Arg parsing // Arg parsing
@ -83,20 +130,27 @@ int main(int argc, char** argv)
std::string arg = argv[i]; std::string arg = argv[i];
if (arg.size() < 1 || arg[0] != '-') if (arg.size() < 1 || arg[0] != '-')
{ {
// This must be the widget selection // This must be the widget selection.
widget = arg; widget = arg;
if (i + 1 < argc) if (i + 1 < argc)
{ {
std::string mon = argv[i + 1]; 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); monitor = std::stoi(mon);
i += 1; i += 1;
} }
else else
{ {
// Not the monitor // Monitor using connector name
monitorName = std::move(mon);
i += 1;
continue; continue;
} }
} }
@ -133,51 +187,21 @@ int main(int argc, char** argv)
signal(SIGINT, CloseTmpFiles); signal(SIGINT, CloseTmpFiles);
System::Init(overrideConfigLocation); System::Init(overrideConfigLocation);
Window window(monitor); Window window;
window.Init(overrideConfigLocation); if (monitor != -1)
if (widget == "bar")
{ {
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 else
{ {
// Already open, close window = Window(monitorName);
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.Init(overrideConfigLocation);
window.OnWidget = [&]()
{
CreateWidget(widget, window);
};
window.Run(); window.Run();
System::FreeResources(); System::FreeResources();