From 628a4d896c154018048d04d2b4c28b32afb30cba Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Sun, 28 Dec 2025 21:32:01 +0100 Subject: [PATCH] Add option to stop plugin in case unclean shutdown is detected --- CMakeLists.txt | 2 + data/locale/en-US.ini | 2 + lib/advanced-scene-switcher.cpp | 11 ++- lib/utils/crash-handler.cpp | 135 ++++++++++++++++++++++++++++++++ lib/utils/crash-handler.hpp | 7 ++ 5 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 lib/utils/crash-handler.cpp create mode 100644 lib/utils/crash-handler.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c55a206e..1c0a8487 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,6 +184,8 @@ target_sources( lib/utils/canvas-helpers.hpp lib/utils/condition-logic.cpp lib/utils/condition-logic.hpp + lib/utils/crash-handler.cpp + lib/utils/crash-handler.hpp lib/utils/curl-helper.cpp lib/utils/curl-helper.hpp lib/utils/cursor-shape-changer.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index b0bf8770..a8d858ab 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -1390,6 +1390,8 @@ AdvSceneSwitcher.hotkey.macro.segment.remove="Remove selected macro segment" AdvSceneSwitcher.askBackup="Detected a new version of the Advanced Scene Switcher.\nShould a backup of the old settings be created?" AdvSceneSwitcher.askForMacro="Select macro{{macroSelection}}" +AdvSceneSwitcher.crashDetected="OBS did not shut down cleanly (for example, due to a crash or freeze).\n\nThe Advanced Scene Switcher plugin would normally start automatically.\nWould you like to keep it stopped for now?" + AdvSceneSwitcher.close="Close" AdvSceneSwitcher.browse="Browse" diff --git a/lib/advanced-scene-switcher.cpp b/lib/advanced-scene-switcher.cpp index b0d2fb46..82f40ee1 100644 --- a/lib/advanced-scene-switcher.cpp +++ b/lib/advanced-scene-switcher.cpp @@ -1,5 +1,6 @@ #include "advanced-scene-switcher.hpp" #include "backup.hpp" +#include "crash-handler.hpp" #include "log-helper.hpp" #include "macro-helpers.hpp" #include "obs-module-helper.hpp" @@ -207,9 +208,15 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) switcher->LoadSettings(data); switcher->m.unlock(); - if (!switcher->stop) { - switcher->Start(); + if (switcher->stop) { + return; } + + if (ShouldSkipPluginStartOnUncleanShutdown()) { + return; + } + + switcher->Start(); } } diff --git a/lib/utils/crash-handler.cpp b/lib/utils/crash-handler.cpp new file mode 100644 index 00000000..5846be19 --- /dev/null +++ b/lib/utils/crash-handler.cpp @@ -0,0 +1,135 @@ +#include "crash-handler.hpp" +#include "log-helper.hpp" +#include "obs-module-helper.hpp" +#include "plugin-state-helpers.hpp" +#include "ui-helpers.hpp" + +#include +#include + +#include +#include + +#include + +namespace advss { + +static constexpr std::string_view sentinel = ".running"; + +#ifndef NDEBUG +static constexpr bool handleUncleanShutdown = false; +#else +static constexpr bool handleUncleanShutdown = true; +#endif + +static bool wasCleanShutdown = false; + +static void setup(); +static bool setupDone = []() { + AddPluginInitStep(setup); + return true; +}(); + +static void handleShutdown(enum obs_frontend_event event, void *) +{ + if (event != OBS_FRONTEND_EVENT_EXIT) { + return; + } + + char *sentinelFile = obs_module_config_path(sentinel.data()); + if (!sentinelFile) { + return; + } + + QFile file(sentinelFile); + if (!file.exists()) { + bfree(sentinelFile); + return; + } + + if (!file.remove()) { + blog(LOG_WARNING, "failed to remove sentinel file"); + } + + bfree(sentinelFile); +} + +static void setup() +{ + char *sentinelFile = obs_module_config_path(sentinel.data()); + if (!sentinelFile) { + return; + } + + QString dirPath = QFileInfo(sentinelFile).absolutePath(); + QDir dir(dirPath); + if (!dir.exists()) { + if (!dir.mkpath(dirPath)) { + blog(LOG_WARNING, + "failed to create directory for sentinel file"); + bfree(sentinelFile); + return; + } + } + + QFile file(sentinelFile); + + wasCleanShutdown = file.exists(); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + blog(LOG_WARNING, "failed to create sentinel file"); + bfree(sentinelFile); + return; + } + + file.write("running"); + file.close(); + bfree(sentinelFile); + + obs_frontend_add_event_callback(handleShutdown, nullptr); + return; +} + +static bool wasUncleanShutdown() +{ + static bool alreadyHandled = false; + + if (!handleUncleanShutdown || !wasCleanShutdown || alreadyHandled) { + alreadyHandled = true; + return false; + } + + alreadyHandled = true; + blog(LOG_WARNING, "unclean shutdown detected"); + return true; +} + +static bool askForStartupSkip() +{ + return DisplayMessage(obs_module_text("AdvSceneSwitcher.crashDetected"), + true, false); +} + +bool ShouldSkipPluginStartOnUncleanShutdown() +{ + if (!wasUncleanShutdown()) { + return false; + } + + std::thread t([]() { + obs_queue_task( + OBS_TASK_UI, + [](void *) { + const bool skipStart = askForStartupSkip(); + if (!skipStart) { + StartPlugin(); + } + }, + nullptr, false); + }); + t.detach(); + + return true; +} + +} // namespace advss diff --git a/lib/utils/crash-handler.hpp b/lib/utils/crash-handler.hpp new file mode 100644 index 00000000..f02fe2b3 --- /dev/null +++ b/lib/utils/crash-handler.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace advss { + +bool ShouldSkipPluginStartOnUncleanShutdown(); + +} // namespace advss