mirror of
https://github.com/scorpion-26/gBar.git
synced 2024-11-21 10:42:51 +00:00
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:
parent
f205a26fcc
commit
95f9b0aadb
19 changed files with 341 additions and 158 deletions
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
|
||||
namespace Bar
|
||||
{
|
||||
void Create(Window& window, int32_t monitor);
|
||||
void Create(Window& window, const std::string& monitor);
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
|
||||
namespace BluetoothDevices
|
||||
{
|
||||
void Create(Window& window, int32_t monitor);
|
||||
void Create(Window& window, const std::string& monitor);
|
||||
}
|
||||
|
|
16
src/Common.h
16
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); \
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 -
|
||||
|
|
109
src/Wayland.cpp
109
src/Wayland.cpp
|
@ -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, ®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<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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
160
src/Window.cpp
160
src/Window.cpp
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
21
src/Window.h
21
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<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;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
112
src/gBar.cpp
112
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();
|
||||
|
|
Loading…
Reference in a new issue