From b71f5cc4fcb324c84b63dd099d5f9b73cf7c8f38 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Sat, 13 Aug 2016 09:36:17 -0500 Subject: [PATCH] UI: Add system tray capability This adds a system tray when enabled. There is also a option to minimize to the system tray when the app is started. Closes jp9000/obs-studio#595 --- obs/data/locale/en-US.ini | 9 ++ obs/forms/OBSBasicSettings.ui | 37 ++++++- obs/forms/images/tray_active.png | Bin 0 -> 38441 bytes obs/forms/obs.qrc | 1 + obs/obs-app.cpp | 4 + obs/window-basic-main-outputs.cpp | 20 ++-- obs/window-basic-main.cpp | 172 ++++++++++++++++++++++++++++-- obs/window-basic-main.hpp | 22 ++++ obs/window-basic-settings.cpp | 20 ++++ obs/window-basic-status-bar.cpp | 34 ++++-- obs/window-basic-status-bar.hpp | 1 + 11 files changed, 298 insertions(+), 22 deletions(-) create mode 100644 obs/forms/images/tray_active.png diff --git a/obs/data/locale/en-US.ini b/obs/data/locale/en-US.ini index 5be0dc155..f3467f4d2 100644 --- a/obs/data/locale/en-US.ini +++ b/obs/data/locale/en-US.ini @@ -383,6 +383,8 @@ Basic.Settings.General.SourceSnapping="Snap Sources to other sources" Basic.Settings.General.SnapDistance="Snap Sensitivity" Basic.Settings.General.RecordWhenStreaming="Automatically record when streaming" Basic.Settings.General.KeepRecordingWhenStreamStops="Keep recording when stream stops" +Basic.Settings.General.SysTrayEnabled="Enable system tray icon" +Basic.Settings.General.SysTrayWhenStarted="Minimize to system tray when started" # basic mode 'stream' settings Basic.Settings.Stream="Stream" @@ -550,6 +552,13 @@ Basic.Hotkeys.StartRecording="Start Recording" Basic.Hotkeys.StopRecording="Stop Recording" Basic.Hotkeys.SelectScene="Switch to scene" +# system tray +Basic.SystemTray.Show="Show" +Basic.SystemTray.Hide="Hide" + +# system tray messages +Basic.SystemTray.Message.Reconnecting="Disconnected. Reconnecting..." + # hotkeys that may lack translation on certain operating systems Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" diff --git a/obs/forms/OBSBasicSettings.ui b/obs/forms/OBSBasicSettings.ui index be10b2f20..64ce0fd1c 100644 --- a/obs/forms/OBSBasicSettings.ui +++ b/obs/forms/OBSBasicSettings.ui @@ -192,14 +192,31 @@ - + + + + Basic.Settings.General.SysTrayEnabled + + + + + + + false + + + Basic.Settings.General.SysTrayWhenStarted + + + + Qt::Horizontal - + true @@ -3753,5 +3770,21 @@ + + systemTrayEnabled + toggled(bool) + systemTrayWhenStarted + setEnabled(bool) + + + 404 + 245 + + + 404 + 271 + + + diff --git a/obs/forms/images/tray_active.png b/obs/forms/images/tray_active.png new file mode 100644 index 0000000000000000000000000000000000000000..d8da1c5f92937dd4dd35c032148a75978638a329 GIT binary patch literal 38441 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 Q07VI%J8fZHatcrSKl@xD!~g&Q literal 0 HcmV?d00001 diff --git a/obs/forms/obs.qrc b/obs/forms/obs.qrc index 60ab59a4b..361fef4ae 100644 --- a/obs/forms/obs.qrc +++ b/obs/forms/obs.qrc @@ -13,6 +13,7 @@ images/properties.png images/up.png images/obs.png + images/tray_active.png images/settings/advanced.png diff --git a/obs/obs-app.cpp b/obs/obs-app.cpp index 232f8c85f..0584e18eb 100644 --- a/obs/obs-app.cpp +++ b/obs/obs-app.cpp @@ -360,6 +360,10 @@ bool OBSApp::InitGlobalConfigDefaults() "RecordWhenStreaming", false); config_set_default_bool(globalConfig, "BasicWindow", "KeepRecordingWhenStreamStops", false); + config_set_default_bool(globalConfig, "BasicWindow", + "SysTrayEnabled", true); + config_set_default_bool(globalConfig, "BasicWindow", + "SysTrayWhenStarted", false); config_set_default_bool(globalConfig, "BasicWindow", "ShowTransitions", true); config_set_default_bool(globalConfig, "BasicWindow", diff --git a/obs/window-basic-main-outputs.cpp b/obs/window-basic-main-outputs.cpp index e94503b3b..2415d75d6 100644 --- a/obs/window-basic-main-outputs.cpp +++ b/obs/window-basic-main-outputs.cpp @@ -604,9 +604,13 @@ bool SimpleOutput::StartRecording() os_dir_t *dir = path ? os_opendir(path) : nullptr; if (!dir) { - QMessageBox::information(main, - QTStr("Output.BadPath.Title"), - QTStr("Output.BadPath.Text")); + if (main->isVisible()) + QMessageBox::information(main, + QTStr("Output.BadPath.Title"), + QTStr("Output.BadPath.Text")); + else + main->SysTrayNotify(QTStr("Output.BadPath.Text"), + QSystemTrayIcon::Warning); return false; } @@ -1147,9 +1151,13 @@ bool AdvancedOutput::StartRecording() os_dir_t *dir = path ? os_opendir(path) : nullptr; if (!dir) { - QMessageBox::information(main, - QTStr("Output.BadPath.Title"), - QTStr("Output.BadPath.Text")); + if (main->isVisible()) + QMessageBox::information(main, + QTStr("Output.BadPath.Title"), + QTStr("Output.BadPath.Text")); + else + main->SysTrayNotify(QTStr("Output.BadPath.Text"), + QSystemTrayIcon::Warning); return false; } diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index b12246342..d2aecbac3 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -1181,6 +1181,8 @@ void OBSBasic::OBSInit() } ui->mainSplitter->setSizes(defSizes); + + SystemTray(true); } void OBSBasic::InitHotkeys() @@ -1447,8 +1449,11 @@ OBSBasic::~OBSBasic() QList splitterSizes = ui->mainSplitter->sizes(); bool alwaysOnTop = IsAlwaysOnTop(this); - config_set_string(App()->GlobalConfig(), "BasicWindow", "geometry", - saveGeometry().toBase64().constData()); + if (isVisible()) + config_set_string(App()->GlobalConfig(), + "BasicWindow", "geometry", + saveGeometry().toBase64().constData()); + config_set_int(App()->GlobalConfig(), "BasicWindow", "splitterTop", splitterSizes[0]); config_set_int(App()->GlobalConfig(), "BasicWindow", "splitterBottom", @@ -2610,6 +2615,8 @@ void OBSBasic::closeEvent(QCloseEvent *event) blog(LOG_INFO, SHUTDOWN_SEPARATOR); if (outputHandler && outputHandler->Active()) { + SetShowing(true); + QMessageBox::StandardButton button = QMessageBox::question( this, QTStr("ConfirmExit.Title"), QTStr("ConfirmExit.Text")); @@ -2668,6 +2675,7 @@ void OBSBasic::on_action_Settings_triggered() { OBSBasicSettings settings(this); settings.exec(); + SystemTray(false); } void OBSBasic::on_actionAdvAudioProperties_triggered() @@ -3533,10 +3541,14 @@ void OBSBasic::StartStreaming() ui->streamButton->setEnabled(false); ui->streamButton->setText(QTStr("Basic.Main.Connecting")); + sysTrayStream->setEnabled(false); + sysTrayStream->setText(ui->streamButton->text()); if (!outputHandler->StartStreaming(service)) { ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setEnabled(true); + sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setEnabled(true); } bool recordWhenStreaming = config_get_bool(GetGlobalConfig(), @@ -3572,6 +3584,8 @@ inline void OBSBasic::OnActivate() ui->profileMenu->setEnabled(false); App()->IncrementSleepInhibition(); UpdateProcessPriority(); + + trayIcon->setIcon(QIcon(":/res/images/tray_active.png")); } } @@ -3581,6 +3595,8 @@ inline void OBSBasic::OnDeactivate() ui->profileMenu->setEnabled(true); App()->DecrementSleepInhibition(); ClearProcessPriority(); + + trayIcon->setIcon(QIcon(":/res/images/obs.png")); } } @@ -3622,6 +3638,8 @@ void OBSBasic::StreamDelayStarting(int sec) { ui->streamButton->setText(QTStr("Basic.Main.StopStreaming")); ui->streamButton->setEnabled(true); + sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setEnabled(true); if (!startStreamMenu.isNull()) startStreamMenu->deleteLater(); @@ -3642,6 +3660,8 @@ void OBSBasic::StreamDelayStopping(int sec) { ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setEnabled(true); + sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setEnabled(true); if (!startStreamMenu.isNull()) startStreamMenu->deleteLater(); @@ -3661,6 +3681,8 @@ void OBSBasic::StreamingStart() ui->streamButton->setText(QTStr("Basic.Main.StopStreaming")); ui->streamButton->setEnabled(true); ui->statusbar->StreamStarted(outputHandler->streamOutput); + sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setEnabled(true); OnActivate(); @@ -3670,6 +3692,7 @@ void OBSBasic::StreamingStart() void OBSBasic::StreamStopping() { ui->streamButton->setText(QTStr("Basic.Main.StoppingStreaming")); + sysTrayStream->setText(ui->streamButton->text()); } void OBSBasic::StreamingStop(int code) @@ -3704,15 +3727,20 @@ void OBSBasic::StreamingStop(int code) ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setEnabled(true); + sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setEnabled(true); OnDeactivate(); blog(LOG_INFO, STREAMING_STOP); - if (code != OBS_OUTPUT_SUCCESS) + if (code != OBS_OUTPUT_SUCCESS && isVisible()) { QMessageBox::information(this, QTStr("Output.ConnectFail.Title"), QT_UTF8(errorMessage)); + } else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) { + SysTrayNotify(QT_UTF8(errorMessage), QSystemTrayIcon::Warning); + } if (!startStreamMenu.isNull()) { ui->streamButton->setMenu(nullptr); @@ -3733,6 +3761,7 @@ void OBSBasic::StartRecording() void OBSBasic::RecordStopping() { ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording")); + sysTrayRecord->setText(ui->recordButton->text()); } void OBSBasic::StopRecording() @@ -3749,6 +3778,7 @@ void OBSBasic::RecordingStart() { ui->statusbar->RecordingStarted(outputHandler->fileOutput); ui->recordButton->setText(QTStr("Basic.Main.StopRecording")); + sysTrayRecord->setText(ui->recordButton->text()); OnActivate(); @@ -3759,22 +3789,35 @@ void OBSBasic::RecordingStop(int code) { ui->statusbar->RecordingStopped(); ui->recordButton->setText(QTStr("Basic.Main.StartRecording")); + sysTrayRecord->setText(ui->recordButton->text()); blog(LOG_INFO, RECORDING_STOP); - if (code == OBS_OUTPUT_UNSUPPORTED) { + if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) { QMessageBox::information(this, QTStr("Output.RecordFail.Title"), QTStr("Output.RecordFail.Unsupported")); - } else if (code == OBS_OUTPUT_NO_SPACE) { + } else if (code == OBS_OUTPUT_NO_SPACE && isVisible()) { QMessageBox::information(this, QTStr("Output.RecordNoSpace.Title"), QTStr("Output.RecordNoSpace.Msg")); - } else if (code != OBS_OUTPUT_SUCCESS) { + } else if (code != OBS_OUTPUT_SUCCESS && isVisible()) { QMessageBox::information(this, QTStr("Output.RecordError.Title"), QTStr("Output.RecordError.Msg")); + + } else if (code == OBS_OUTPUT_UNSUPPORTED && !isVisible()) { + SysTrayNotify(QTStr("Output.RecordFail.Unsupported"), + QSystemTrayIcon::Warning); + + } else if (code == OBS_OUTPUT_NO_SPACE && !isVisible()) { + SysTrayNotify(QTStr("Output.RecordNoSpace.Msg"), + QSystemTrayIcon::Warning); + + } else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) { + SysTrayNotify(QTStr("Output.RecordError.Msg"), + QSystemTrayIcon::Warning); } OnDeactivate(); @@ -3786,7 +3829,7 @@ void OBSBasic::on_streamButton_clicked() bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow", "WarnBeforeStoppingStream"); - if (confirm) { + if (confirm && isVisible()) { QMessageBox::StandardButton button = QMessageBox::question(this, QTStr("ConfirmStop.Title"), @@ -3801,7 +3844,7 @@ void OBSBasic::on_streamButton_clicked() bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow", "WarnBeforeStartingStream"); - if (confirm) { + if (confirm && isVisible()) { QMessageBox::StandardButton button = QMessageBox::question(this, QTStr("ConfirmStart.Title"), @@ -3827,6 +3870,7 @@ void OBSBasic::on_settingsButton_clicked() { OBSBasicSettings settings(this); settings.exec(); + SystemTray(false); } void OBSBasic::on_actionWebsite_triggered() @@ -4389,3 +4433,115 @@ void OBSBasic::on_actionLockPreview_triggered() ui->preview->ToggleLocked(); ui->actionLockPreview->setChecked(ui->preview->Locked()); } + +void OBSBasic::SetShowing(bool showing) +{ + if (!showing && isVisible()) { + config_set_string(App()->GlobalConfig(), + "BasicWindow", "geometry", + saveGeometry().toBase64().constData()); + + showHide->setText(QTStr("Basic.SystemTray.Show")); + QTimer::singleShot(250, this, SLOT(hide())); + + if (previewEnabled) + EnablePreviewDisplay(false); + + setVisible(false); + + } else if (showing && !isVisible()) { + showHide->setText(QTStr("Basic.SystemTray.Hide")); + QTimer::singleShot(250, this, SLOT(show())); + + if (previewEnabled) + EnablePreviewDisplay(true); + + setVisible(true); + } +} + +void OBSBasic::SystemTrayInit() { + trayIcon = new QSystemTrayIcon(QIcon(":/res/images/obs.png"), + this); + trayIcon->setToolTip("OBS Studio"); + + showHide = new QAction(QTStr("Basic.SystemTray.Show"), + trayIcon); + sysTrayStream = new QAction(QTStr("Basic.Main.StartStreaming"), + trayIcon); + sysTrayRecord = new QAction(QTStr("Basic.Main.StartRecording"), + trayIcon); + exit = new QAction(QTStr("Exit"), + trayIcon); + + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, + SLOT(IconActivated(QSystemTrayIcon::ActivationReason))); + connect(showHide, SIGNAL(triggered()), + this, SLOT(ToggleShowHide())); + connect(sysTrayStream, SIGNAL(triggered()), + this, SLOT(on_streamButton_clicked())); + connect(sysTrayRecord, SIGNAL(triggered()), + this, SLOT(on_recordButton_clicked())); + connect(exit, SIGNAL(triggered()), + this, SLOT(close())); + + trayMenu = new QMenu; + trayMenu->addAction(showHide); + trayMenu->addAction(sysTrayStream); + trayMenu->addAction(sysTrayRecord); + trayMenu->addAction(exit); + trayIcon->setContextMenu(trayMenu); +} + +void OBSBasic::IconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::Trigger) + ToggleShowHide(); +} + +void OBSBasic::SysTrayNotify(const QString &text, + QSystemTrayIcon::MessageIcon n) +{ + if (QSystemTrayIcon::supportsMessages()) { + QSystemTrayIcon::MessageIcon icon = + QSystemTrayIcon::MessageIcon(n); + trayIcon->showMessage("OBS Studio", text, icon, 10000); + } +} + +void OBSBasic::SystemTray(bool firstStarted) +{ + if (!QSystemTrayIcon::isSystemTrayAvailable()) + return; + + bool sysTrayWhenStarted = config_get_bool(GetGlobalConfig(), + "BasicWindow", "SysTrayWhenStarted"); + bool sysTrayEnabled = config_get_bool(GetGlobalConfig(), + "BasicWindow", "SysTrayEnabled"); + + if (firstStarted) + SystemTrayInit(); + + if (!sysTrayWhenStarted && !sysTrayEnabled) { + trayIcon->hide(); + } else if (sysTrayWhenStarted && sysTrayEnabled) { + trayIcon->show(); + if (firstStarted) { + QTimer::singleShot(50, this, SLOT(hide())); + EnablePreviewDisplay(false); + setVisible(false); + } + } else if (sysTrayEnabled) { + trayIcon->show(); + } else if (!sysTrayEnabled) { + trayIcon->hide(); + } else if (!sysTrayWhenStarted && sysTrayEnabled) { + trayIcon->hide(); + } + + if (isVisible()) + showHide->setText(QTStr("Basic.SystemTray.Hide")); + else + showHide->setText(QTStr("Basic.SystemTray.Show")); +} diff --git a/obs/window-basic-main.hpp b/obs/window-basic-main.hpp index 4fedeee6c..c67a762cf 100644 --- a/obs/window-basic-main.hpp +++ b/obs/window-basic-main.hpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -135,6 +136,14 @@ private: QPointer startStreamMenu; + QSystemTrayIcon *trayIcon; + QMenu *trayMenu; + QAction *sysTrayStream; + QAction *sysTrayRecord; + QAction *showHide; + QAction *showPreview; + QAction *exit; + void DrawBackdrop(float cx, float cy); void SetupEncoders(); @@ -340,6 +349,14 @@ private slots: void SetScaleFilter(); + void IconActivated(QSystemTrayIcon::ActivationReason reason); + void SetShowing(bool showing); + + inline void ToggleShowHide() + { + SetShowing(!isVisible()); + } + private: /* OBS Callbacks */ static void SceneReordered(void *data, calldata_t *params); @@ -366,6 +383,8 @@ private: public: OBSScene GetCurrentScene(); + void SysTrayNotify(const QString &text, QSystemTrayIcon::MessageIcon n); + inline OBSSource GetCurrentSceneSource() { OBSScene curScene = GetCurrentScene(); @@ -414,6 +433,9 @@ public: void UpdateTitleBar(); void UpdateSceneSelection(OBSSource source); + void SystemTrayInit(); + void SystemTray(bool firstStarted); + protected: virtual void closeEvent(QCloseEvent *event) override; virtual void changeEvent(QEvent *event) override; diff --git a/obs/window-basic-settings.cpp b/obs/window-basic-settings.cpp index fef022bf7..3bd064881 100644 --- a/obs/window-basic-settings.cpp +++ b/obs/window-basic-settings.cpp @@ -274,6 +274,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->hideProjectorCursor, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->recordWhenStreaming, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->keepRecordStreamStops,CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->systemTrayEnabled, CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->systemTrayWhenStarted,CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->snappingEnabled, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->screenSnapping, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->centerSnapping, CHECK_CHANGED, GENERAL_CHANGED); @@ -845,6 +847,14 @@ void OBSBasicSettings::LoadGeneralSettings() "BasicWindow", "KeepRecordingWhenStreamStops"); ui->keepRecordStreamStops->setChecked(keepRecordStreamStops); + bool systemTrayEnabled = config_get_bool(GetGlobalConfig(), + "BasicWindow", "SysTrayEnabled"); + ui->systemTrayEnabled->setChecked(systemTrayEnabled); + + bool systemTrayWhenStarted = config_get_bool(GetGlobalConfig(), + "BasicWindow", "SysTrayWhenStarted"); + ui->systemTrayWhenStarted->setChecked(systemTrayWhenStarted); + bool snappingEnabled = config_get_bool(GetGlobalConfig(), "BasicWindow", "SnappingEnabled"); ui->snappingEnabled->setChecked(snappingEnabled); @@ -2235,6 +2245,16 @@ void OBSBasicSettings::SaveGeneralSettings() config_set_bool(GetGlobalConfig(), "BasicWindow", "KeepRecordingWhenStreamStops", ui->keepRecordStreamStops->isChecked()); + + if (WidgetChanged(ui->systemTrayEnabled)) + config_set_bool(GetGlobalConfig(), "BasicWindow", + "SysTrayEnabled", + ui->systemTrayEnabled->isChecked()); + + if (WidgetChanged(ui->systemTrayWhenStarted)) + config_set_bool(GetGlobalConfig(), "BasicWindow", + "SysTrayWhenStarted", + ui->systemTrayWhenStarted->isChecked()); } void OBSBasicSettings::SaveStream1Settings() diff --git a/obs/window-basic-status-bar.cpp b/obs/window-basic-status-bar.cpp index 64015efca..4c37b0e3f 100644 --- a/obs/window-basic-status-bar.cpp +++ b/obs/window-basic-status-bar.cpp @@ -72,6 +72,7 @@ void OBSBasicStatusBar::Deactivate() delaySecStopping = 0; reconnectTimeout = 0; active = false; + overloadedNotify = true; } } @@ -165,9 +166,10 @@ void OBSBasicStatusBar::UpdateSessionTime() sessionTime->setMinimumWidth(sessionTime->width()); if (reconnectTimeout > 0) { - QString msg = QTStr("Basic.StatusBar.Reconnecting"); - showMessage(msg.arg(QString::number(retries), - QString::number(reconnectTimeout))); + QString msg = QTStr("Basic.StatusBar.Reconnecting") + .arg(QString::number(retries), + QString::number(reconnectTimeout)); + showMessage(msg); reconnectTimeout--; } else if (retries > 0) { @@ -224,12 +226,20 @@ void OBSBasicStatusBar::OBSOutputReconnectSuccess(void *data, calldata_t *params void OBSBasicStatusBar::Reconnect(int seconds) { - retries++; + OBSBasic *main = qobject_cast(parent()); + + if (!retries) + main->SysTrayNotify( + QTStr("Basic.SystemTray.Message.Reconnecting"), + QSystemTrayIcon::Warning); + reconnectTimeout = seconds; if (streamOutput) { delaySecTotal = obs_output_get_active_delay(streamOutput); UpdateDelayMsg(); + + retries++; } } @@ -246,7 +256,11 @@ void OBSBasicStatusBar::ReconnectClear() void OBSBasicStatusBar::ReconnectSuccess() { - showMessage(QTStr("Basic.StatusBar.ReconnectSuccessful"), 4000); + OBSBasic *main = qobject_cast(parent()); + + QString msg = QTStr("Basic.StatusBar.ReconnectSuccessful"); + showMessage(msg, 4000); + main->SysTrayNotify(msg, QSystemTrayIcon::Information); ReconnectClear(); if (streamOutput) { @@ -257,6 +271,8 @@ void OBSBasicStatusBar::ReconnectSuccess() void OBSBasicStatusBar::UpdateStatusBar() { + OBSBasic *main = qobject_cast(parent()); + UpdateBandwidth(); UpdateSessionTime(); UpdateDroppedFrames(); @@ -270,8 +286,14 @@ void OBSBasicStatusBar::UpdateStatusBar() int diff = skipped - lastSkippedFrameCount; double percentage = double(skipped) / double(total) * 100.0; - if (diff > 10 && percentage >= 0.1f) + if (diff > 10 && percentage >= 0.1f) { showMessage(QTStr("HighResourceUsage"), 4000); + if (!main->isVisible() && overloadedNotify) { + main->SysTrayNotify(QTStr("HighResourceUsage"), + QSystemTrayIcon::Warning); + overloadedNotify = false; + } + } lastSkippedFrameCount = skipped; } diff --git a/obs/window-basic-status-bar.hpp b/obs/window-basic-status-bar.hpp index 3f9bc8fa0..e0c7a0d0a 100644 --- a/obs/window-basic-status-bar.hpp +++ b/obs/window-basic-status-bar.hpp @@ -21,6 +21,7 @@ private: obs_output_t *streamOutput = nullptr; obs_output_t *recordOutput = nullptr; bool active = false; + bool overloadedNotify = true; int retries = 0; int totalSeconds = 0;