Don't crash on monitor remove

gBar no longer crashes when it's monitor is removed and is instead
evacuated onto another monitor or the next monitor added. We don't
change any monitor indices etc., which results in some glitchy results
This commit is contained in:
scorpion-26 2023-08-01 17:09:27 +02:00
parent d3f46bc5f0
commit f205a26fcc
5 changed files with 93 additions and 17 deletions

View file

@ -8,7 +8,7 @@
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<uint32_t, Monitor> monitors; static std::unordered_map<std::string, 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;
@ -28,9 +28,16 @@ namespace Wayland
// Workspace Callbacks // Workspace Callbacks
static void OnWorkspaceName(void*, zext_workspace_handle_v1* workspace, const char* name) static void OnWorkspaceName(void*, zext_workspace_handle_v1* workspace, const char* name)
{ {
workspaces[workspace].id = std::stoul(name); try
LOG("Workspace ID: " << workspaces[workspace].id); {
registeredWorkspaceInfo = true; workspaces[workspace].id = std::stoul(name);
LOG("Workspace ID: " << workspaces[workspace].id);
registeredWorkspaceInfo = true;
}
catch (const std::invalid_argument&)
{
LOG("Wayland: Invalid WS name: " << name);
}
} }
static void OnWorkspaceGeometry(void*, zext_workspace_handle_v1*, wl_array*) {} static void OnWorkspaceGeometry(void*, zext_workspace_handle_v1*, wl_array*) {}
static void OnWorkspaceState(void*, zext_workspace_handle_v1* ws, wl_array* arrState) static void OnWorkspaceState(void*, zext_workspace_handle_v1* ws, wl_array* arrState)
@ -67,6 +74,13 @@ namespace Wayland
workspaces.erase(ws); workspaces.erase(ws);
LOG("Wayland: Removed workspace!"); LOG("Wayland: Removed workspace!");
if (group.lastActiveWorkspace == ws)
{
if (group.workspaces.size())
group.lastActiveWorkspace = group.workspaces[0];
else
group.lastActiveWorkspace = nullptr;
}
} }
zext_workspace_handle_v1_listener workspaceListener = {OnWorkspaceName, OnWorkspaceGeometry, OnWorkspaceState, OnWorkspaceRemove}; zext_workspace_handle_v1_listener workspaceListener = {OnWorkspaceName, OnWorkspaceGeometry, OnWorkspaceState, OnWorkspaceRemove};
@ -74,7 +88,7 @@ namespace Wayland
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 = std::find_if(monitors.begin(), monitors.end(),
[&](const std::pair<uint32_t, Monitor>& mon) [&](const std::pair<std::string, Monitor>& mon)
{ {
return mon.second.output == output; return mon.second.output == output;
}); });
@ -85,7 +99,7 @@ namespace Wayland
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 = std::find_if(monitors.begin(), monitors.end(),
[&](const std::pair<uint32_t, Monitor>& mon) [&](const std::pair<std::string, Monitor>& mon)
{ {
return mon.second.output == output; return mon.second.output == output;
}); });
@ -131,9 +145,19 @@ namespace Wayland
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)
{ {
LOG("Wayland: Registering monitor " << name << " at ID " << curID); std::string nameStr = name;
registeredMonitors = true; auto it = monitors.find(nameStr);
monitors.try_emplace(curID++, Monitor{name, output, nullptr}); 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);
}
} }
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};
@ -198,7 +222,7 @@ namespace Wayland
auto workspaceIt = std::find_if(workspaces.begin(), workspaces.end(), auto workspaceIt = std::find_if(workspaces.begin(), workspaces.end(),
[&](const std::pair<zext_workspace_handle_v1*, Workspace>& ws) [&](const std::pair<zext_workspace_handle_v1*, Workspace>& ws)
{ {
return ws.second.id == monitor.first + 1; return ws.second.id == monitor.second.ID + 1;
}); });
if (workspaceIt != workspaces.end()) if (workspaceIt != workspaces.end())
{ {
@ -240,7 +264,7 @@ namespace Wayland
wl_display_disconnect(display); wl_display_disconnect(display);
} }
const std::unordered_map<uint32_t, Monitor>& GetMonitors() const std::unordered_map<std::string, Monitor>& GetMonitors()
{ {
return monitors; return monitors;
} }

View file

@ -11,6 +11,7 @@ namespace Wayland
std::string name; std::string name;
wl_output* output; wl_output* output;
zext_workspace_group_handle_v1* workspaceGroup; zext_workspace_group_handle_v1* workspaceGroup;
uint32_t ID;
}; };
struct Workspace struct Workspace
@ -28,7 +29,7 @@ namespace Wayland
void Init(); void Init();
void PollEvents(); void PollEvents();
const std::unordered_map<uint32_t, Monitor>& GetMonitors(); const std::unordered_map<std::string, 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();

View file

@ -39,6 +39,18 @@ void Window::Init(const std::string& overideConfigLocation)
{ {
m_Monitor = gdk_display_get_primary_monitor(defaultDisplay); m_Monitor = gdk_display_get_primary_monitor(defaultDisplay);
} }
// Register monitor added/removed callbacks
auto monAdded = [](GdkDisplay* display, GdkMonitor* mon, void* window)
{
((Window*)window)->MonitorAdded(display, mon);
};
g_signal_connect(defaultDisplay, "monitor-added", G_CALLBACK(+monAdded), this);
auto monRemoved = [](GdkDisplay* display, GdkMonitor* mon, void* window)
{
((Window*)window)->MonitorRemoved(display, mon);
};
g_signal_connect(defaultDisplay, "monitor-removed", G_CALLBACK(+monRemoved), this);
} }
void Window::Run() void Window::Run()
@ -158,3 +170,31 @@ int Window::GetHeight() const
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)
{
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);
}
}
void Window::MonitorRemoved(GdkDisplay* display, GdkMonitor* mon)
{
LOG("Window: Monitor removed: " << mon);
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);
}
}

View file

@ -41,11 +41,15 @@ public:
int GetWidth() const; int GetWidth() const;
int GetHeight() const; int GetHeight() const;
private: private:
void UpdateMargin(); void UpdateMargin();
void LoadCSS(GtkCssProvider* provider); void LoadCSS(GtkCssProvider* provider);
void MonitorAdded(GdkDisplay* display, GdkMonitor* monitor);
void MonitorRemoved(GdkDisplay* display, GdkMonitor* monitor);
GtkWindow* m_Window = nullptr; GtkWindow* m_Window = nullptr;
GtkApplication* m_App = nullptr; GtkApplication* m_App = nullptr;

View file

@ -20,12 +20,18 @@ namespace Workspaces
} }
System::WorkspaceStatus GetStatus(uint32_t workspaceId) System::WorkspaceStatus GetStatus(uint32_t workspaceId)
{ {
const WaylandMonitor& monitor = ::Wayland::GetMonitors().at(lastPolledMonitor); auto& mons = ::Wayland::GetMonitors();
if (!monitor.output) auto it = std::find_if(mons.begin(), mons.end(),
[&](const std::pair<std::string, ::Wayland::Monitor>& mon)
{
return mon.second.ID == workspaceId;
});
if (it == mons.end())
{ {
LOG("Polled monitor doesn't exist!"); LOG("Polled monitor doesn't exist!");
return System::WorkspaceStatus::Dead; return System::WorkspaceStatus::Dead;
} }
const ::Wayland::Monitor& monitor = it->second;
auto& workspaces = ::Wayland::GetWorkspaces(); auto& workspaces = ::Wayland::GetWorkspaces();
auto workspaceIt = std::find_if(workspaces.begin(), workspaces.end(), auto workspaceIt = std::find_if(workspaces.begin(), workspaces.end(),
@ -156,9 +162,10 @@ namespace Workspaces
std::string workspaces = DispatchIPC("/workspaces"); std::string workspaces = DispatchIPC("/workspaces");
while ((parseIdx = workspaces.find("workspace ID ", parseIdx)) != std::string::npos) while ((parseIdx = workspaces.find("workspace ID ", parseIdx)) != std::string::npos)
{ {
// Goto ( // Advance two spaces
size_t begWSNum = workspaces.find('(', parseIdx) + 1; size_t begWSNum = workspaces.find(' ', parseIdx) + 1;
size_t endWSNum = workspaces.find(')', begWSNum); begWSNum = workspaces.find(' ', begWSNum) + 1;
size_t endWSNum = workspaces.find(' ', begWSNum);
std::string ws = workspaces.substr(begWSNum, endWSNum - begWSNum); std::string ws = workspaces.substr(begWSNum, endWSNum - begWSNum);
int32_t wsId = std::atoi(ws.c_str()); int32_t wsId = std::atoi(ws.c_str());