mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-08-24 10:14:13 -05:00
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:
+154
-30
@@ -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
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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."
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
* -------------------------------------
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user