diff --git a/frontend/OBSApp.cpp b/frontend/OBSApp.cpp index dd7407a1f..2fc0488a6 100644 --- a/frontend/OBSApp.cpp +++ b/frontend/OBSApp.cpp @@ -18,6 +18,8 @@ #include "OBSApp.hpp" #include +#include +#include #include #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER) #include @@ -32,6 +34,8 @@ #endif #include +#include +#include #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER) #include #endif @@ -42,6 +46,8 @@ #include #endif +#include + #ifdef _WIN32 #include #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 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(); + } else { + crashHandler_ = std::make_unique(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(GetMainWindow()); + + basicWindow->UploadLog("obs-studio/logs", GetLastLog(), OBS::LogFileType::LastAppLog); } void OBSApp::uploadCurrentAppLog() const { - return; + OBSBasic *basicWindow = static_cast(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; + } +} diff --git a/frontend/OBSApp.hpp b/frontend/OBSApp.hpp index 834f8d8d0..94e82bb4a 100644 --- a/frontend/OBSApp.hpp +++ b/frontend/OBSApp.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -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 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(); diff --git a/frontend/cmake/ui-dialogs.cmake b/frontend/cmake/ui-dialogs.cmake index b8083b8bb..de490a383 100644 --- a/frontend/cmake/ui-dialogs.cmake +++ b/frontend/cmake/ui-dialogs.cmake @@ -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 diff --git a/frontend/cmake/ui-qt.cmake b/frontend/cmake/ui-qt.cmake index e8991f60b..72437ad2a 100644 --- a/frontend/cmake/ui-qt.cmake +++ b/frontend/cmake/ui-qt.cmake @@ -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 ) diff --git a/frontend/data/locale/en-US.ini b/frontend/data/locale/en-US.ini index 247cafaef..cc5724f6d 100644 --- a/frontend/data/locale/en-US.ini +++ b/frontend/data/locale/en-US.ini @@ -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 Privacy Policy 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." diff --git a/frontend/dialogs/OBSLogReply.cpp b/frontend/dialogs/OBSLogReply.cpp deleted file mode 100644 index 1000372a5..000000000 --- a/frontend/dialogs/OBSLogReply.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/****************************************************************************** - Copyright (C) 2023 by Lain Bailey - - 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 . -******************************************************************************/ - -#include "OBSLogReply.hpp" - -#include - -#include -#include -#include - -#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); -} diff --git a/frontend/dialogs/OBSLogReply.hpp b/frontend/dialogs/OBSLogReply.hpp deleted file mode 100644 index 260b827f7..000000000 --- a/frontend/dialogs/OBSLogReply.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/****************************************************************************** - Copyright (C) 2023 by Lain Bailey - - 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 . -******************************************************************************/ - -#pragma once - -#include "ui_OBSLogReply.h" - -#include - -class OBSLogReply : public QDialog { - Q_OBJECT - -private: - std::unique_ptr ui; - -public: - OBSLogReply(QWidget *parent, const QString &url, const bool crash); - -private slots: - void on_copyURL_clicked(); - void on_analyzeURL_clicked(); -}; diff --git a/frontend/forms/OBSLogReply.ui b/frontend/forms/OBSLogReply.ui deleted file mode 100644 index 544018b7d..000000000 --- a/frontend/forms/OBSLogReply.ui +++ /dev/null @@ -1,102 +0,0 @@ - - - OBSLogReply - - - - 0 - 0 - 600 - 96 - - - - LogReturnDialog - - - - - - LogReturnDialog.Description - - - - - - - - - - - - true - - - - - - - LogReturnDialog.CopyURL - - - - - - - LogReturnDialog.AnalyzeURL - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Ok - - - true - - - - - - - - - buttonBox - accepted() - OBSLogReply - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - OBSLogReply - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/frontend/obs-main.cpp b/frontend/obs-main.cpp index 69fcf9d68..5b5c3e7ca 100644 --- a/frontend/obs-main.cpp +++ b/frontend/obs-main.cpp @@ -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); diff --git a/frontend/widgets/OBSBasic.cpp b/frontend/widgets/OBSBasic.cpp index ffe42be30..8d1fad371 100644 --- a/frontend/widgets/OBSBasic.cpp +++ b/frontend/widgets/OBSBasic.cpp @@ -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) diff --git a/frontend/widgets/OBSBasic.hpp b/frontend/widgets/OBSBasic.hpp index 65dbe5ece..28210980e 100644 --- a/frontend/widgets/OBSBasic.hpp +++ b/frontend/widgets/OBSBasic.hpp @@ -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 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 * ------------------------------------- diff --git a/frontend/widgets/OBSBasic_MainControls.cpp b/frontend/widgets/OBSBasic_MainControls.cpp index e47e7c007..b0418a842 100644 --- a/frontend/widgets/OBSBasic_MainControls.cpp +++ b/frontend/widgets/OBSBasic_MainControls.cpp @@ -20,6 +20,7 @@ #include "OBSBasic.hpp" #include "OBSBasicStats.hpp" +#include #include #include #include @@ -27,7 +28,6 @@ #include #include #include -#include #ifdef __APPLE__ #include #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 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 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()