|
@@ -87,6 +87,10 @@ static string lastCrashLogFile;
|
|
|
|
|
|
bool portable_mode = false;
|
|
bool portable_mode = false;
|
|
bool steam = false;
|
|
bool steam = false;
|
|
|
|
+bool safe_mode = false;
|
|
|
|
+bool disable_3p_plugins = false;
|
|
|
|
+bool unclean_shutdown = false;
|
|
|
|
+bool disable_shutdown_check = false;
|
|
static bool multi = false;
|
|
static bool multi = false;
|
|
static bool log_verbose = false;
|
|
static bool log_verbose = false;
|
|
static bool unfiltered_log = false;
|
|
static bool unfiltered_log = false;
|
|
@@ -105,6 +109,7 @@ string opt_starting_profile;
|
|
string opt_starting_scene;
|
|
string opt_starting_scene;
|
|
|
|
|
|
bool restart = false;
|
|
bool restart = false;
|
|
|
|
+bool restart_safe = false;
|
|
|
|
|
|
QPointer<OBSLogViewer> obsLogViewer;
|
|
QPointer<OBSLogViewer> obsLogViewer;
|
|
|
|
|
|
@@ -1715,6 +1720,12 @@ bool OBSApp::OBSInit()
|
|
QT_VERSION_STR);
|
|
QT_VERSION_STR);
|
|
blog(LOG_INFO, "Portable mode: %s", portable_mode ? "true" : "false");
|
|
blog(LOG_INFO, "Portable mode: %s", portable_mode ? "true" : "false");
|
|
|
|
|
|
|
|
+ if (safe_mode) {
|
|
|
|
+ blog(LOG_WARNING, "Safe Mode enabled.");
|
|
|
|
+ } else if (disable_3p_plugins) {
|
|
|
|
+ blog(LOG_WARNING, "Third-party plugins disabled.");
|
|
|
|
+ }
|
|
|
|
+
|
|
setQuitOnLastWindowClosed(false);
|
|
setQuitOnLastWindowClosed(false);
|
|
|
|
|
|
mainWindow = new OBSBasic();
|
|
mainWindow = new OBSBasic();
|
|
@@ -2429,6 +2440,10 @@ static int run_program(fstream &logFile, int argc, char *argv[])
|
|
blog(LOG_WARNING, "================================");
|
|
blog(LOG_WARNING, "================================");
|
|
blog(LOG_WARNING, "User is now running multiple "
|
|
blog(LOG_WARNING, "User is now running multiple "
|
|
"instances of OBS!");
|
|
"instances of OBS!");
|
|
|
|
+ /* Clear unclean_shutdown flag as multiple instances
|
|
|
|
+ * running from the same config will lead to a
|
|
|
|
+ * false-positive detection.*/
|
|
|
|
+ unclean_shutdown = false;
|
|
}
|
|
}
|
|
|
|
|
|
/* --------------------------------------- */
|
|
/* --------------------------------------- */
|
|
@@ -2453,6 +2468,34 @@ static int run_program(fstream &logFile, int argc, char *argv[])
|
|
if (!created_log)
|
|
if (!created_log)
|
|
create_log_file(logFile);
|
|
create_log_file(logFile);
|
|
|
|
|
|
|
|
+ if (unclean_shutdown) {
|
|
|
|
+ blog(LOG_WARNING,
|
|
|
|
+ "[Safe Mode] Unclean shutdown detected!");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (unclean_shutdown && !safe_mode) {
|
|
|
|
+ QMessageBox mb(QMessageBox::Warning,
|
|
|
|
+ QTStr("AutoSafeMode.Title"),
|
|
|
|
+ QTStr("AutoSafeMode.Text"));
|
|
|
|
+ QPushButton *launchSafeButton =
|
|
|
|
+ mb.addButton(QTStr("AutoSafeMode.LaunchSafe"),
|
|
|
|
+ QMessageBox::AcceptRole);
|
|
|
|
+ QPushButton *launchNormalButton =
|
|
|
|
+ mb.addButton(QTStr("AutoSafeMode.LaunchNormal"),
|
|
|
|
+ QMessageBox::RejectRole);
|
|
|
|
+ mb.setDefaultButton(launchNormalButton);
|
|
|
|
+ mb.exec();
|
|
|
|
+
|
|
|
|
+ safe_mode = mb.clickedButton() == launchSafeButton;
|
|
|
|
+ if (safe_mode) {
|
|
|
|
+ blog(LOG_INFO,
|
|
|
|
+ "[Safe Mode] User has launched in Safe Mode.");
|
|
|
|
+ } else {
|
|
|
|
+ blog(LOG_WARNING,
|
|
|
|
+ "[Safe Mode] User elected to launch normally.");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
qInstallMessageHandler([](QtMsgType type,
|
|
qInstallMessageHandler([](QtMsgType type,
|
|
const QMessageLogContext &,
|
|
const QMessageLogContext &,
|
|
const QString &message) {
|
|
const QString &message) {
|
|
@@ -2540,9 +2583,18 @@ static int run_program(fstream &logFile, int argc, char *argv[])
|
|
OBSErrorBox(nullptr, "%s", error);
|
|
OBSErrorBox(nullptr, "%s", error);
|
|
}
|
|
}
|
|
|
|
|
|
- if (restart)
|
|
|
|
- QProcess::startDetached(qApp->arguments()[0],
|
|
|
|
- qApp->arguments());
|
|
|
|
|
|
+ if (restart || restart_safe) {
|
|
|
|
+ auto args = qApp->arguments();
|
|
|
|
+ auto executable = args[0];
|
|
|
|
+
|
|
|
|
+ if (restart_safe) {
|
|
|
|
+ args.append("--safe-mode");
|
|
|
|
+ } else {
|
|
|
|
+ args.removeAll("--safe-mode");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ QProcess::startDetached(executable, args);
|
|
|
|
+ }
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -3194,6 +3246,32 @@ static void upgrade_settings(void)
|
|
os_closedir(dir);
|
|
os_closedir(dir);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void check_safe_mode_sentinel(void)
|
|
|
|
+{
|
|
|
|
+#ifndef NDEBUG
|
|
|
|
+ /* Safe Mode detection is disabled in Debug builds to keep developers
|
|
|
|
+ * somewhat sane. */
|
|
|
|
+ return;
|
|
|
|
+#else
|
|
|
|
+ if (disable_shutdown_check)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ BPtr sentinelPath = GetConfigPathPtr("obs-studio/safe_mode");
|
|
|
|
+ if (os_file_exists(sentinelPath)) {
|
|
|
|
+ unclean_shutdown = true;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ os_quick_write_utf8_file(sentinelPath, nullptr, 0, false);
|
|
|
|
+#endif
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void delete_safe_mode_sentinel(void)
|
|
|
|
+{
|
|
|
|
+ BPtr sentinelPath = GetConfigPathPtr("obs-studio/safe_mode");
|
|
|
|
+ os_unlink(sentinelPath);
|
|
|
|
+}
|
|
|
|
+
|
|
#ifndef _WIN32
|
|
#ifndef _WIN32
|
|
void OBSApp::SigIntSignalHandler(int s)
|
|
void OBSApp::SigIntSignalHandler(int s)
|
|
{
|
|
{
|
|
@@ -3281,6 +3359,17 @@ int main(int argc, char *argv[])
|
|
} else if (arg_is(argv[i], "--verbose", nullptr)) {
|
|
} else if (arg_is(argv[i], "--verbose", nullptr)) {
|
|
log_verbose = true;
|
|
log_verbose = true;
|
|
|
|
|
|
|
|
+ } else if (arg_is(argv[i], "--safe-mode", nullptr)) {
|
|
|
|
+ safe_mode = true;
|
|
|
|
+
|
|
|
|
+ } else if (arg_is(argv[i], "--only-bundled-plugins", nullptr)) {
|
|
|
|
+ disable_3p_plugins = true;
|
|
|
|
+
|
|
|
|
+ } else if (arg_is(argv[i], "--disable-shutdown-check",
|
|
|
|
+ nullptr)) {
|
|
|
|
+ /* This exists mostly to bypass the dialog during development. */
|
|
|
|
+ disable_shutdown_check = true;
|
|
|
|
+
|
|
} else if (arg_is(argv[i], "--always-on-top", nullptr)) {
|
|
} else if (arg_is(argv[i], "--always-on-top", nullptr)) {
|
|
opt_always_on_top = true;
|
|
opt_always_on_top = true;
|
|
|
|
|
|
@@ -3347,6 +3436,9 @@ int main(int argc, char *argv[])
|
|
"--portable, -p: Use portable mode.\n"
|
|
"--portable, -p: Use portable mode.\n"
|
|
#endif
|
|
#endif
|
|
"--multi, -m: Don't warn when launching multiple instances.\n\n"
|
|
"--multi, -m: Don't warn when launching multiple instances.\n\n"
|
|
|
|
+ "--safe-mode: Run in Safe Mode (disables third-party plugins, scripting, and websockets).\n"
|
|
|
|
+ "--only-bundled-plugins: Only load included (first-party) plugins\n"
|
|
|
|
+ "--disable-shutdown-check: Disable unclean shutdown detection.\n"
|
|
"--verbose: Make log more verbose.\n"
|
|
"--verbose: Make log more verbose.\n"
|
|
"--always-on-top: Start in 'always on top' mode.\n\n"
|
|
"--always-on-top: Start in 'always on top' mode.\n\n"
|
|
"--unfiltered_log: Make log unfiltered.\n\n"
|
|
"--unfiltered_log: Make log unfiltered.\n\n"
|
|
@@ -3393,6 +3485,7 @@ int main(int argc, char *argv[])
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
+ check_safe_mode_sentinel();
|
|
upgrade_settings();
|
|
upgrade_settings();
|
|
|
|
|
|
fstream logFile;
|
|
fstream logFile;
|
|
@@ -3412,6 +3505,7 @@ int main(int argc, char *argv[])
|
|
log_blocked_dlls();
|
|
log_blocked_dlls();
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
+ delete_safe_mode_sentinel();
|
|
blog(LOG_INFO, "Number of memory leaks: %ld", bnum_allocs());
|
|
blog(LOG_INFO, "Number of memory leaks: %ld", bnum_allocs());
|
|
base_set_log_handler(nullptr, nullptr);
|
|
base_set_log_handler(nullptr, nullptr);
|
|
return ret;
|
|
return ret;
|