frontend: Update crash handling and log upload functionality

Updates include:

* Use of CrashHandler to provide automatic uploads of the most recent
crash log if an unclean shutdown was detected and it has not been
uploaded yet.
* Detection and handling of unclean shutdowns is delegated entirely to
the CrashHandler class
* Use of OBSLogReply has been replaced with the LogUploadDialog, which
asks for confirmation before new uploads of log files (confirmation is
skipped for files with available upload URLs already - only available
for crash logs with this change)

Architectural changes:
* OBSApp is the layer responsible for application launch and shutdown
states, as well as crash logs and application logs
* The actual handling is delegated to purpose-made classes which OBSApp
owns instances of
* OBSBasic in turn refers to OBSApp for all this functionality, and can
subscribe/connect to appropriate events exposed by OBSApp to this
purpose
* Implementation details (like the existence of the CrashHandler class)
are not exposed to OBSBasic or the LogUploadDialog

The amount of changes for normal log file upload have been purposefully
limited. A proper refactoring of the application log file handling will
move this code out of OBSBasic as well.
This commit is contained in:
PatTheMav
2025-08-22 15:38:12 -04:00
committed by Ryan Foster
parent e4e3035661
commit de997b1e2f
12 changed files with 245 additions and 380 deletions
+154 -30
View File
@@ -18,6 +18,8 @@
#include "OBSApp.hpp"
#include <components/Multiview.hpp>
#include <dialogs/LogUploadDialog.hpp>
#include <utility/CrashHandler.hpp>
#include <utility/OBSEventFilter.hpp>
#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)
#include <utility/models/branches.hpp>
@@ -32,6 +34,8 @@
#endif
#include <qt-wrappers.hpp>
#include <QCheckBox>
#include <QDesktopServices>
#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)
#include <QFile>
#endif
@@ -42,6 +46,8 @@
#include <QSocketNotifier>
#endif
#include <chrono>
#ifdef _WIN32
#include <sstream>
#define WIN32_LEAN_AND_MEAN
@@ -61,6 +67,7 @@ string lastCrashLogFile;
extern bool portable_mode;
extern bool safe_mode;
extern bool multi;
extern bool disable_3p_plugins;
extern bool opt_disable_updater;
extern bool opt_disable_missing_files_check;
@@ -79,6 +86,66 @@ extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 1;
extern "C" __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
#endif
namespace {
typedef struct UncleanLaunchAction {
bool useSafeMode = false;
bool sendCrashReport = false;
} UncleanLaunchAction;
UncleanLaunchAction handleUncleanShutdown(bool enableCrashUpload)
{
UncleanLaunchAction launchAction;
blog(LOG_WARNING, "Crash or unclean shutdown detected");
QMessageBox crashWarning;
crashWarning.setIcon(QMessageBox::Warning);
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
crashWarning.setOption(QMessageBox::Option::DontUseNativeDialog);
#endif
crashWarning.setWindowTitle(QTStr("CrashHandling.Dialog.Title"));
crashWarning.setText(QTStr("CrashHandling.Labels.Text"));
if (enableCrashUpload) {
crashWarning.setInformativeText(QTStr("CrashHandling.Labels.PrivacyNotice"));
QCheckBox *sendCrashReportCheckbox = new QCheckBox(QTStr("CrashHandling.Checkbox.SendReport"));
crashWarning.setCheckBox(sendCrashReportCheckbox);
}
QPushButton *launchSafeButton =
crashWarning.addButton(QTStr("CrashHandling.Buttons.LaunchSafe"), QMessageBox::AcceptRole);
QPushButton *launchNormalButton =
crashWarning.addButton(QTStr("CrashHandling.Buttons.LaunchNormal"), QMessageBox::RejectRole);
crashWarning.setDefaultButton(launchNormalButton);
crashWarning.exec();
bool useSafeMode = crashWarning.clickedButton() == launchSafeButton;
if (useSafeMode) {
launchAction.useSafeMode = true;
blog(LOG_INFO, "[Safe Mode] Safe mode launch selected, loading third-party plugins is disabled");
} else {
blog(LOG_WARNING, "[Safe Mode] Normal launch selected, loading third-party plugins is enabled");
}
bool sendCrashReport = (enableCrashUpload) ? crashWarning.checkBox()->isChecked() : false;
if (sendCrashReport) {
launchAction.sendCrashReport = true;
blog(LOG_INFO, "User selected to send crash report");
}
return launchAction;
}
} // namespace
QObject *CreateShortcutFilter()
{
return new OBSEventFilter([](QObject *obj, QEvent *event) {
@@ -771,7 +838,8 @@ std::vector<UpdateBranch> OBSApp::GetBranches()
OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store)
: QApplication(argc, argv),
profilerNameStore(store)
profilerNameStore(store),
appLaunchUUID_(QUuid::createUuid())
{
/* fix float handling */
#if defined(Q_OS_UNIX)
@@ -787,6 +855,11 @@ OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store)
#else
connect(qApp, &QGuiApplication::commitDataRequest, this, &OBSApp::commitData);
#endif
if (multi) {
crashHandler_ = std::make_unique<OBS::CrashHandler>();
} else {
crashHandler_ = std::make_unique<OBS::CrashHandler>(appLaunchUUID_);
}
sleepInhibitor = os_inhibit_sleep_create("OBS Video/audio");
@@ -799,29 +872,10 @@ OBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store)
OBSApp::~OBSApp()
{
#ifdef _WIN32
bool disableAudioDucking = config_get_bool(appConfig, "Audio", "DisableAudioDucking");
if (disableAudioDucking)
DisableAudioDucking(false);
#else
delete snInt;
close(sigintFd[0]);
close(sigintFd[1]);
#endif
#ifdef __APPLE__
bool vsyncDisabled = config_get_bool(appConfig, "Video", "DisableOSXVSync");
bool resetVSync = config_get_bool(appConfig, "Video", "ResetOSXVSyncOnExit");
if (vsyncDisabled && resetVSync)
EnableOSXVSync(true);
#endif
os_inhibit_sleep_set_active(sleepInhibitor, false);
os_inhibit_sleep_destroy(sleepInhibitor);
if (libobs_initialized)
obs_shutdown();
}
if (libobs_initialized) {
applicationShutdown();
}
};
static void move_basic_to_profiles(void)
{
@@ -982,6 +1036,22 @@ void OBSApp::AppInit()
throw "Failed to create profile directories";
}
void OBSApp::checkForUncleanShutdown()
{
bool hasUncleanShutdown = crashHandler_->hasUncleanShutdown();
bool hasNewCrashLog = crashHandler_->hasNewCrashLog();
if (hasUncleanShutdown) {
UncleanLaunchAction launchAction = handleUncleanShutdown(hasNewCrashLog);
safe_mode = launchAction.useSafeMode;
if (launchAction.sendCrashReport) {
crashHandler_->uploadLastCrashLog();
}
}
}
const char *OBSApp::GetRenderModule() const
{
const char *renderer = config_get_string(appConfig, "Video", "Renderer");
@@ -1130,6 +1200,15 @@ bool OBSApp::OBSInit()
connect(this, &QGuiApplication::applicationStateChanged,
[this](Qt::ApplicationState state) { ResetHotkeyState(state == Qt::ApplicationActive); });
ResetHotkeyState(applicationState() == Qt::ApplicationActive);
connect(crashHandler_.get(), &OBS::CrashHandler::crashLogUploadFailed, this,
[this](const QString &errorMessage) {
emit this->logUploadFailed(OBS::LogFileType::CrashLog, errorMessage);
});
connect(crashHandler_.get(), &OBS::CrashHandler::crashLogUploadFinished, this,
[this](const QString &fileUrl) { emit this->logUploadFinished(OBS::LogFileType::CrashLog, fileUrl); });
return true;
}
@@ -1208,30 +1287,47 @@ const char *OBSApp::GetCurrentLog() const
return currentLogFile.c_str();
}
const char *OBSApp::GetLastCrashLog() const
void OBSApp::openCrashLogDirectory() const
{
return lastCrashLogFile.c_str();
std::filesystem::path crashLogDirectory = crashHandler_->getCrashLogDirectory();
if (crashLogDirectory.empty()) {
return;
}
QString crashLogDirectoryString = QString::fromStdString(crashLogDirectory.u8string());
QUrl crashLogDirectoryURL = QUrl::fromLocalFile(crashLogDirectoryString);
QDesktopServices::openUrl(crashLogDirectoryURL);
}
void OBSApp::uploadLastAppLog() const
{
return;
OBSBasic *basicWindow = static_cast<OBSBasic *>(GetMainWindow());
basicWindow->UploadLog("obs-studio/logs", GetLastLog(), OBS::LogFileType::LastAppLog);
}
void OBSApp::uploadCurrentAppLog() const
{
return;
OBSBasic *basicWindow = static_cast<OBSBasic *>(GetMainWindow());
basicWindow->UploadLog("obs-studio/logs", GetCurrentLog(), OBS::LogFileType::CurrentAppLog);
}
void OBSApp::uploadLastCrashLog()
{
return;
crashHandler_->uploadLastCrashLog();
}
OBS::LogFileState OBSApp::getLogFileState(OBS::LogFileType type) const
{
switch (type) {
case OBS::LogFileType::CrashLog:
case OBS::LogFileType::CrashLog: {
bool hasNewCrashLog = crashHandler_->hasNewCrashLog();
return (hasNewCrashLog) ? OBS::LogFileState::New : OBS::LogFileState::Uploaded;
}
case OBS::LogFileType::CurrentAppLog:
case OBS::LogFileType::LastAppLog:
return OBS::LogFileState::New;
@@ -1633,3 +1729,31 @@ void OBSApp::commitData(QSessionManager &manager)
}
}
#endif
void OBSApp::applicationShutdown() noexcept
{
#ifdef _WIN32
bool disableAudioDucking = config_get_bool(appConfig, "Audio", "DisableAudioDucking");
if (disableAudioDucking)
DisableAudioDucking(false);
#else
delete snInt;
close(sigintFd[0]);
close(sigintFd[1]);
#endif
#ifdef __APPLE__
bool vsyncDisabled = config_get_bool(appConfig, "Video", "DisableOSXVSync");
bool resetVSync = config_get_bool(appConfig, "Video", "ResetOSXVSyncOnExit");
if (vsyncDisabled && resetVSync)
EnableOSXVSync(true);
#endif
os_inhibit_sleep_set_active(sleepInhibitor, false);
os_inhibit_sleep_destroy(sleepInhibitor);
if (libobs_initialized) {
obs_shutdown();
libobs_initialized = false;
}
}
+9 -1
View File
@@ -28,6 +28,7 @@
#include <QApplication>
#include <QPalette>
#include <QPointer>
#include <QUuid>
#include <deque>
#include <functional>
@@ -42,6 +43,8 @@ class QFileSystemWatcher;
class QSocketNotifier;
namespace OBS {
class CrashHandler;
enum class LogFileType { NoType, CurrentAppLog, LastAppLog, CrashLog };
enum class LogFileState { NoState, New, Uploaded };
} // namespace OBS
@@ -58,6 +61,9 @@ class OBSApp : public QApplication {
Q_OBJECT
private:
QUuid appLaunchUUID_;
std::unique_ptr<OBS::CrashHandler> crashHandler_;
std::string locale;
ConfigFile appConfig;
@@ -114,12 +120,14 @@ private slots:
private slots:
void themeFileChanged(const QString &);
void applicationShutdown() noexcept;
public:
OBSApp(int &argc, char **argv, profiler_name_store_t *store);
~OBSApp();
void AppInit();
void checkForUncleanShutdown();
bool OBSInit();
void UpdateHotkeyFocusSetting(bool reset = true);
@@ -157,7 +165,7 @@ public:
const char *GetLastLog() const;
const char *GetCurrentLog() const;
const char *GetLastCrashLog() const;
void openCrashLogDirectory() const;
void uploadLastAppLog() const;
void uploadCurrentAppLog() const;
void uploadLastCrashLog();
-2
View File
@@ -29,8 +29,6 @@ target_sources(
dialogs/OBSBasicTransform.hpp
dialogs/OBSBasicVCamConfig.cpp
dialogs/OBSBasicVCamConfig.hpp
dialogs/OBSLogReply.cpp
dialogs/OBSLogReply.hpp
dialogs/OBSLogViewer.cpp
dialogs/OBSLogViewer.hpp
dialogs/OBSMissingFiles.cpp
+2 -4
View File
@@ -30,7 +30,6 @@ target_sources(
forms/AutoConfigTestPage.ui
forms/AutoConfigVideoPage.ui
forms/ColorSelect.ui
forms/obs.qrc
forms/LogUploadDialog.ui
forms/OBSAbout.ui
forms/OBSAdvAudio.ui
@@ -44,10 +43,10 @@ target_sources(
forms/OBSBasicVCamConfig.ui
forms/OBSExtraBrowsers.ui
forms/OBSImporter.ui
forms/OBSLogReply.ui
forms/OBSLogReply.ui
forms/OBSMissingFiles.ui
forms/OBSRemux.ui
forms/StatusBarWidget.ui
forms/obs.qrc
forms/source-toolbar/browser-source-toolbar.ui
forms/source-toolbar/color-source-toolbar.ui
forms/source-toolbar/device-select-toolbar.ui
@@ -55,5 +54,4 @@ target_sources(
forms/source-toolbar/image-source-toolbar.ui
forms/source-toolbar/media-controls.ui
forms/source-toolbar/text-source-toolbar.ui
forms/StatusBarWidget.ui
)
+8 -21
View File
@@ -128,15 +128,6 @@ AlreadyRunning.Title="OBS is already running"
AlreadyRunning.Text="OBS is already running! Unless you meant to do this, please shut down any existing instances of OBS before trying to run a new instance. If you have OBS set to minimize to the system tray, please check to see if it's still running there."
AlreadyRunning.LaunchAnyway="Launch Anyway"
# warning if auto Safe Mode has engaged
AutoSafeMode.Title="Safe Mode"
AutoSafeMode.Text="OBS did not shut down properly during your last session.\n\nWould you like to start in Safe Mode (third-party plugins, scripting, and WebSockets disabled)?"
AutoSafeMode.LaunchSafe="Run in Safe Mode"
AutoSafeMode.LaunchNormal="Run Normally"
## Restart Option
SafeMode.Restart="Do you want to restart OBS in Safe Mode (third-party plugins, scripting, and WebSockets disabled)?"
SafeMode.RestartNormal="Do you want to restart OBS in Normal Mode?"
# Prior Crash Detection
CrashHandling.Dialog.Title="OBS Studio Crash Detected"
CrashHandling.Labels.Text="OBS Studio did not properly shut down.\n\nRun in Safe Mode (third-party plugins, scripting, and WebSockets disabled)?"
@@ -147,6 +138,10 @@ CrashHandling.Buttons.LaunchNormal="Run in Normal Mode"
CrashHandling.Errors.UploadJSONError="An error occurred while trying to upload the most recent crash log. Please try again later."
CrashHandling.Errors.Title="Crash Log Upload Error"
# Safe Mode Restart Option
SafeMode.Restart="Do you want to restart OBS in Safe Mode (third-party plugins, scripting, and WebSockets disabled)?"
SafeMode.RestartNormal="Do you want to restart OBS in Normal Mode?"
ChromeOS.Title="Unsupported Platform"
ChromeOS.Text="OBS appears to be running inside a ChromeOS container. This platform is unsupported."
@@ -460,24 +455,16 @@ Output.NoBroadcast.Text="You need to set up a broadcast before you can start str
Output.BroadcastStartFailed="Failed to start broadcast"
Output.BroadcastStopFailed="Failed to stop broadcast"
# log upload dialog text and messages
LogReturnDialog="Log Upload Successful"
LogReturnDialog.Description="Your log file has been uploaded. You can now share the URL for debugging or support purposes."
LogReturnDialog.Description.Crash="Your crash report has been uploaded. You can now share the URL for debugging purposes."
LogReturnDialog.CopyURL="Copy URL"
LogReturnDialog.AnalyzeURL="Analyze"
LogReturnDialog.ErrorUploadingLog="Error uploading log file"
# Log Upload Dialog for Application- and Crash-Logs
# Log Upload Dialog for Application and Crash Logs
LogUploadDialog.Title="OBS Studio Log File Upload"
LogUploadDialog.Labels.PrivacyNotice="Please read the <a href='https://obsproject.com/privacy-policy'>Privacy Policy</a> and its section regarding file uploads before uploading any files."
LogUploadDialog.Labels.Progress="Log upload in progress - please wait..."
LogUploadDialog.Labels.Progress="Log upload in progress. Please wait..."
LogUploadDialog.Labels.Description.AppLog="Your log file has been uploaded. You can now share the URL for debugging or support purposes."
LogUploadDialog.Labels.Description.CrashLog="Your crash report has been uploaded. You can now share the URL for debugging purposes."
LogUploadDialog.Buttons.ConfirmUpload="Continue Upload..."
LogUploadDialog.Buttons.ConfirmUpload="Upload"
LogUploadDialog.Buttons.CopyURL="Copy Log URL"
LogUploadDialog.Buttons.AnalyzeURL="Analyze Log File"
LogUploadDialog.Buttons.RetryButton="Retry Upload..."
LogUploadDialog.Buttons.RetryButton="Retry"
LogUploadDialog.Errors.Template="An error occurred while trying to upload the file:\n\n%1"
LogUploadDialog.Errors.NoLogFile="No file to upload found or file was empty."
-56
View File
@@ -1,56 +0,0 @@
/******************************************************************************
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "OBSLogReply.hpp"
#include <OBSApp.hpp>
#include <QClipboard>
#include <QDesktopServices>
#include <QUrlQuery>
#include "moc_OBSLogReply.cpp"
OBSLogReply::OBSLogReply(QWidget *parent, const QString &url, const bool crash)
: QDialog(parent),
ui(new Ui::OBSLogReply)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
ui->setupUi(this);
ui->urlEdit->setText(url);
if (crash) {
ui->analyzeURL->hide();
ui->description->setText(Str("LogReturnDialog.Description.Crash"));
}
installEventFilter(CreateShortcutFilter());
}
void OBSLogReply::on_copyURL_clicked()
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(ui->urlEdit->text());
}
void OBSLogReply::on_analyzeURL_clicked()
{
QUrlQuery param;
param.addQueryItem("log_url", QUrl::toPercentEncoding(ui->urlEdit->text()));
QUrl url("https://obsproject.com/tools/analyzer", QUrl::TolerantMode);
url.setQuery(param);
QDesktopServices::openUrl(url);
}
-36
View File
@@ -1,36 +0,0 @@
/******************************************************************************
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#pragma once
#include "ui_OBSLogReply.h"
#include <QDialog>
class OBSLogReply : public QDialog {
Q_OBJECT
private:
std::unique_ptr<Ui::OBSLogReply> ui;
public:
OBSLogReply(QWidget *parent, const QString &url, const bool crash);
private slots:
void on_copyURL_clicked();
void on_analyzeURL_clicked();
};
-102
View File
@@ -1,102 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OBSLogReply</class>
<widget class="QDialog" name="OBSLogReply">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>96</height>
</rect>
</property>
<property name="windowTitle">
<string>LogReturnDialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="description">
<property name="text">
<string>LogReturnDialog.Description</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="urlEdit">
<property name="text">
<string notr="true"/>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="copyURL">
<property name="text">
<string>LogReturnDialog.CopyURL</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="analyzeURL">
<property name="text">
<string>LogReturnDialog.AnalyzeURL</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
<property name="centerButtons">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>OBSLogReply</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>OBSLogReply</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+2 -64
View File
@@ -53,15 +53,13 @@ static log_handler_t def_log_handler;
extern string currentLogFile;
extern string lastLogFile;
extern string lastCrashLogFile;
bool portable_mode = false;
bool steam = false;
bool safe_mode = false;
bool disable_3p_plugins = false;
static bool unclean_shutdown = false;
static bool disable_shutdown_check = false;
static bool multi = false;
bool multi = false;
static bool log_verbose = false;
static bool unfiltered_log = false;
bool opt_start_streaming = false;
@@ -405,9 +403,6 @@ static void create_log_file(fstream &logFile)
stringstream dst;
get_last_log(false, "obs-studio/logs", lastLogFile);
#ifdef _WIN32
get_last_log(true, "obs-studio/crashes", lastCrashLogFile);
#endif
currentLogFile = GenerateTimeDateFilename("txt");
dst << "obs-studio/logs/" << currentLogFile.c_str();
@@ -625,26 +620,7 @@ static int run_program(fstream &logFile, int argc, char *argv[])
if (!created_log)
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.");
}
}
program.checkForUncleanShutdown();
qInstallMessageHandler([](QtMsgType type, const QMessageLogContext &, const QString &message) {
switch (type) {
@@ -842,36 +818,6 @@ static inline bool arg_is(const char *arg, const char *long_form, const char *sh
return (long_form && strcmp(arg, long_form) == 0) || (short_form && strcmp(arg, short_form) == 0);
}
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 = GetAppConfigPathPtr("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)
{
#ifndef NDEBUG
return;
#else
BPtr sentinelPath = GetAppConfigPathPtr("obs-studio/safe_mode");
os_unlink(sentinelPath);
#endif
}
#ifdef _WIN32
static constexpr char vcRunErrorTitle[] = "Outdated Visual C++ Runtime";
static constexpr char vcRunErrorMsg[] = "OBS Studio requires a newer version of the Microsoft Visual C++ "
@@ -965,7 +911,6 @@ int main(int argc, char *argv[])
for (int i = 1; i < argc; i++) {
if (arg_is(argv[i], "--multi", "-m")) {
multi = true;
disable_shutdown_check = true;
#if ALLOW_PORTABLE_MODE
} else if (arg_is(argv[i], "--portable", "-p")) {
@@ -981,10 +926,6 @@ int main(int argc, char *argv[])
} 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)) {
opt_always_on_top = true;
@@ -1091,8 +1032,6 @@ int main(int argc, char *argv[])
}
#endif
check_safe_mode_sentinel();
fstream logFile;
curl_global_init(CURL_GLOBAL_ALL);
@@ -1109,7 +1048,6 @@ int main(int argc, char *argv[])
log_blocked_dlls();
#endif
delete_safe_mode_sentinel();
blog(LOG_INFO, "Number of memory leaks: %ld", bnum_allocs());
base_set_log_handler(nullptr, nullptr);
+13 -4
View File
@@ -1233,16 +1233,16 @@ void OBSBasic::OBSInit()
ui->sources->UpdateIcons();
#if !defined(_WIN32)
delete ui->actionRepair;
ui->actionRepair = nullptr;
#if !defined(__APPLE__)
delete ui->actionShowCrashLogs;
delete ui->actionUploadLastCrashLog;
delete ui->menuCrashLogs;
delete ui->actionRepair;
delete ui->actionCheckForUpdates;
ui->actionShowCrashLogs = nullptr;
ui->actionUploadLastCrashLog = nullptr;
ui->menuCrashLogs = nullptr;
ui->actionRepair = nullptr;
#if !defined(__APPLE__)
delete ui->actionCheckForUpdates;
ui->actionCheckForUpdates = nullptr;
#endif
#endif
@@ -1323,6 +1323,13 @@ void OBSBasic::OnFirstLoad()
}
OBSBasic::~OBSBasic()
{
if (!handledShutdown) {
applicationShutdown();
}
}
void OBSBasic::applicationShutdown() noexcept
{
/* clear out UI event queue */
QApplication::sendPostedEvents(nullptr);
@@ -1420,6 +1427,8 @@ OBSBasic::~OBSBasic()
delete cef;
cef = nullptr;
#endif
handledShutdown = true;
}
static inline int AttemptToResetVideo(struct obs_video_info *ovi)
+13 -4
View File
@@ -64,6 +64,7 @@ struct QuickTransition;
namespace OBS {
class SceneCollection;
struct Rect;
enum class LogFileType;
} // namespace OBS
#define DESKTOP_AUDIO_1 Str("DesktopAudioDevice1")
@@ -259,6 +260,7 @@ private:
bool loaded = false;
bool closing = false;
bool handledShutdown = false;
// TODO: Remove, orphaned variable
bool copyVisible = true;
@@ -300,6 +302,7 @@ private:
public slots:
void UpdatePatronJson(const QString &text, const QString &error);
void UpdateEditMenu();
void applicationShutdown() noexcept;
public:
/* `undo_s` needs to be declared after `ui` to prevent an uninitialized
@@ -579,7 +582,6 @@ private:
QList<QPoint> visDlgPositions;
void UploadLog(const char *subdir, const char *file, const bool crash);
void CloseDialogs();
void EnumDialogs();
@@ -631,9 +633,7 @@ private slots:
void on_resetUI_triggered();
void logUploadFinished(const QString &text, const QString &error);
void crashUploadFinished(const QString &text, const QString &error);
void openLogDialog(const QString &text, const bool crash);
void logUploadFinished(const QString &text, const QString &error, OBS::LogFileType uploadType);
void updateCheckFinished();
@@ -645,6 +645,15 @@ public:
void CreateEditTransformWindow(obs_sceneitem_t *item);
void CreatePropertiesWindow(obs_source_t *source);
void UploadLog(const char *subdir, const char *file, OBS::LogFileType uploadType);
/* -------------------------------------
* MARK: - OBSBasic_MainMenu
* -------------------------------------
*/
private:
void setupMenuItemStateHandlers();
/* -------------------------------------
* MARK: - OBSBasic_OutputHandler
* -------------------------------------
+44 -56
View File
@@ -20,6 +20,7 @@
#include "OBSBasic.hpp"
#include "OBSBasicStats.hpp"
#include <dialogs/LogUploadDialog.hpp>
#include <dialogs/OBSAbout.hpp>
#include <dialogs/OBSBasicAdvAudio.hpp>
#include <dialogs/OBSBasicFilters.hpp>
@@ -27,7 +28,6 @@
#include <dialogs/OBSBasicProperties.hpp>
#include <dialogs/OBSBasicTransform.hpp>
#include <dialogs/OBSLogViewer.hpp>
#include <dialogs/OBSLogReply.hpp>
#ifdef __APPLE__
#include <dialogs/OBSPermissions.hpp>
#endif
@@ -62,6 +62,8 @@ extern QCef *cef;
extern QCefCookieManager *panel_cookies;
using namespace std;
using LogUploadDialog = OBS::LogUploadDialog;
using LogUploadType = OBS::LogFileType;
void OBSBasic::CreateInteractionWindow(obs_source_t *source)
{
@@ -256,20 +258,17 @@ static BPtr<char> ReadLogFile(const char *subdir, const char *log)
return file;
}
void OBSBasic::UploadLog(const char *subdir, const char *file, const bool crash)
void OBSBasic::UploadLog(const char *subdir, const char *file, const LogUploadType uploadType)
{
BPtr<char> fileString{ReadLogFile(subdir, file)};
if (!fileString)
return;
if (!*fileString)
if (!fileString || !*fileString) {
OBSApp *app = App();
emit app->logUploadFailed(uploadType, QTStr("LogUploadDialog.Errors.NoLogFile"));
return;
}
ui->menuLogFiles->setEnabled(false);
#if defined(_WIN32)
ui->menuCrashLogs->setEnabled(false);
#endif
stringstream ss;
ss << "OBS " << App()->GetVersionString(false) << " log file uploaded at " << CurrentDateTimeString() << "\n\n"
@@ -282,11 +281,11 @@ void OBSBasic::UploadLog(const char *subdir, const char *file, const bool crash)
RemoteTextThread *thread = new RemoteTextThread("https://obsproject.com/logs/upload", "text/plain", ss.str());
logUploadThread.reset(thread);
if (crash) {
connect(thread, &RemoteTextThread::Result, this, &OBSBasic::crashUploadFinished);
} else {
connect(thread, &RemoteTextThread::Result, this, &OBSBasic::logUploadFinished);
}
connect(thread, &RemoteTextThread::Result, this, [this, uploadType](const QString &text, const QString &error) {
logUploadFinished(text, error, uploadType);
});
logUploadThread->start();
}
@@ -302,12 +301,24 @@ void OBSBasic::on_actionShowLogs_triggered()
void OBSBasic::on_actionUploadCurrentLog_triggered()
{
UploadLog("obs-studio/logs", App()->GetCurrentLog(), false);
ui->menuLogFiles->setEnabled(false);
LogUploadDialog uploadDialog{this, LogUploadType::CurrentAppLog};
uploadDialog.exec();
ui->menuLogFiles->setEnabled(true);
}
void OBSBasic::on_actionUploadLastLog_triggered()
{
UploadLog("obs-studio/logs", App()->GetLastLog(), false);
ui->menuLogFiles->setEnabled(false);
LogUploadDialog uploadDialog{this, LogUploadType::LastAppLog};
uploadDialog.exec();
ui->menuLogFiles->setEnabled(true);
}
void OBSBasic::on_actionViewCurrentLog_triggered()
@@ -323,17 +334,18 @@ void OBSBasic::on_actionViewCurrentLog_triggered()
void OBSBasic::on_actionShowCrashLogs_triggered()
{
char logDir[512];
if (GetAppConfigPath(logDir, sizeof(logDir), "obs-studio/crashes") <= 0)
return;
QUrl url = QUrl::fromLocalFile(QT_UTF8(logDir));
QDesktopServices::openUrl(url);
App()->openCrashLogDirectory();
}
void OBSBasic::on_actionUploadLastCrashLog_triggered()
{
UploadLog("obs-studio/crashes", App()->GetLastCrashLog(), true);
ui->menuCrashLogs->setEnabled(false);
LogUploadDialog uploadDialog{this, LogUploadType::CrashLog};
uploadDialog.exec();
ui->menuCrashLogs->setEnabled(true);
}
void OBSBasic::on_actionCheckForUpdates_triggered()
@@ -367,43 +379,19 @@ void OBSBasic::on_actionRestartSafe_triggered()
}
}
void OBSBasic::logUploadFinished(const QString &text, const QString &error)
void OBSBasic::logUploadFinished(const QString &text, const QString &error, LogUploadType uploadType)
{
ui->menuLogFiles->setEnabled(true);
#if defined(_WIN32)
ui->menuCrashLogs->setEnabled(true);
#endif
OBSApp *app = App();
if (text.isEmpty()) {
OBSMessageBox::critical(this, QTStr("LogReturnDialog.ErrorUploadingLog"), error);
return;
emit app->logUploadFailed(uploadType, error);
} else {
OBSDataAutoRelease returnData = obs_data_create_from_json(QT_TO_UTF8(text));
string resURL = obs_data_get_string(returnData, "url");
QString logURL = resURL.c_str();
emit app->logUploadFinished(uploadType, logURL);
}
openLogDialog(text, false);
}
void OBSBasic::crashUploadFinished(const QString &text, const QString &error)
{
ui->menuLogFiles->setEnabled(true);
#if defined(_WIN32)
ui->menuCrashLogs->setEnabled(true);
#endif
if (text.isEmpty()) {
OBSMessageBox::critical(this, QTStr("LogReturnDialog.ErrorUploadingLog"), error);
return;
}
openLogDialog(text, true);
}
void OBSBasic::openLogDialog(const QString &text, const bool crash)
{
OBSDataAutoRelease returnData = obs_data_create_from_json(QT_TO_UTF8(text));
string resURL = obs_data_get_string(returnData, "url");
QString logURL = resURL.c_str();
OBSLogReply logDialog(this, logURL, crash);
logDialog.exec();
}
void OBSBasic::on_actionHelpPortal_triggered()