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
{
// 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_handle_v1*, Workspace> workspaces;
@ -27,11 +27,18 @@ namespace Wayland
// Workspace Callbacks
static void OnWorkspaceName(void*, zext_workspace_handle_v1* workspace, const char* name)
{
try
{
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 OnWorkspaceState(void*, zext_workspace_handle_v1* ws, wl_array* arrState)
{
@ -67,6 +74,13 @@ namespace Wayland
workspaces.erase(ws);
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};
@ -74,7 +88,7 @@ namespace Wayland
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<uint32_t, Monitor>& mon)
[&](const std::pair<std::string, Monitor>& mon)
{
return mon.second.output == output;
});
@ -85,7 +99,7 @@ namespace Wayland
static void OnWSGroupOutputLeave(void*, zext_workspace_group_handle_v1*, wl_output* output)
{
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;
});
@ -130,10 +144,20 @@ namespace Wayland
static void OnOutputDone(void*, wl_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;
monitors.try_emplace(curID++, Monitor{name, output, nullptr});
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*) {}
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(),
[&](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())
{
@ -240,7 +264,7 @@ namespace Wayland
wl_display_disconnect(display);
}
const std::unordered_map<uint32_t, Monitor>& GetMonitors()
const std::unordered_map<std::string, Monitor>& GetMonitors()
{
return monitors;
}

View file

@ -11,6 +11,7 @@ namespace Wayland
std::string name;
wl_output* output;
zext_workspace_group_handle_v1* workspaceGroup;
uint32_t ID;
};
struct Workspace
@ -28,7 +29,7 @@ namespace Wayland
void Init();
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_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);
}
// 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()
@ -158,3 +170,31 @@ int Window::GetHeight() const
gdk_monitor_get_geometry(m_Monitor, &rect);
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 GetHeight() const;
private:
void UpdateMargin();
void LoadCSS(GtkCssProvider* provider);
void MonitorAdded(GdkDisplay* display, GdkMonitor* monitor);
void MonitorRemoved(GdkDisplay* display, GdkMonitor* monitor);
GtkWindow* m_Window = nullptr;
GtkApplication* m_App = nullptr;

View file

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