From 18170ab1c70bd05b1e6d9a0340e103e7b44923ee Mon Sep 17 00:00:00 2001 From: scorpion-26 Date: Tue, 1 Aug 2023 17:09:27 +0200 Subject: [PATCH] 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 --- src/Wayland.cpp | 46 +++++++++++++++++++++++++++++++++++----------- src/Wayland.h | 3 ++- src/Window.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/Window.h | 4 ++++ src/Workspaces.cpp | 17 ++++++++++++----- 5 files changed, 93 insertions(+), 17 deletions(-) diff --git a/src/Wayland.cpp b/src/Wayland.cpp index 8bfad9a..f28a843 100644 --- a/src/Wayland.cpp +++ b/src/Wayland.cpp @@ -8,7 +8,7 @@ namespace Wayland { // There's probably a better way to avoid the LUTs - static std::unordered_map monitors; + static std::unordered_map monitors; static std::unordered_map workspaceGroups; static std::unordered_map workspaces; @@ -28,9 +28,16 @@ namespace Wayland // Workspace Callbacks static void OnWorkspaceName(void*, zext_workspace_handle_v1* workspace, const char* name) { - workspaces[workspace].id = std::stoul(name); - LOG("Workspace ID: " << workspaces[workspace].id); - registeredWorkspaceInfo = true; + 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& mon) + [&](const std::pair& 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& mon) + [&](const std::pair& mon) { return mon.second.output == output; }); @@ -131,9 +145,19 @@ namespace Wayland static void OnOutputScale(void*, wl_output*, int32_t) {} static void OnOutputName(void*, wl_output* output, const char* name) { - LOG("Wayland: Registering monitor " << name << " at ID " << curID); - registeredMonitors = true; - monitors.try_emplace(curID++, Monitor{name, output, nullptr}); + 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); + } } 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& 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& GetMonitors() + const std::unordered_map& GetMonitors() { return monitors; } diff --git a/src/Wayland.h b/src/Wayland.h index 1c2c18f..a656bc9 100644 --- a/src/Wayland.h +++ b/src/Wayland.h @@ -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& GetMonitors(); + const std::unordered_map& GetMonitors(); const std::unordered_map& GetWorkspaceGroups(); const std::unordered_map& GetWorkspaces(); diff --git a/src/Window.cpp b/src/Window.cpp index d21b096..4d7b3b8 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -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); + } +} diff --git a/src/Window.h b/src/Window.h index e11fef4..e7914bd 100644 --- a/src/Window.h +++ b/src/Window.h @@ -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; diff --git a/src/Workspaces.cpp b/src/Workspaces.cpp index 83b72ee..249b771 100644 --- a/src/Workspaces.cpp +++ b/src/Workspaces.cpp @@ -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& 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());