Explorar el Código

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
cg2121 hace 9 años
padre
commit
b71f5cc4fc

+ 9 - 0
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"

+ 35 - 2
obs/forms/OBSBasicSettings.ui

@@ -192,14 +192,31 @@
            </property>
           </widget>
          </item>
-         <item row="8" column="0" colspan="2">
+         <item row="8" column="1">
+          <widget class="QCheckBox" name="systemTrayEnabled">
+           <property name="text">
+            <string>Basic.Settings.General.SysTrayEnabled</string>
+           </property>
+          </widget>
+         </item>
+         <item row="9" column="1">
+          <widget class="QCheckBox" name="systemTrayWhenStarted">
+           <property name="enabled">
+            <bool>false</bool>
+           </property>
+           <property name="text">
+            <string>Basic.Settings.General.SysTrayWhenStarted</string>
+           </property>
+          </widget>
+         </item>
+         <item row="10" column="0" colspan="2">
           <widget class="Line" name="line_4">
            <property name="orientation">
             <enum>Qt::Horizontal</enum>
            </property>
           </widget>
          </item>
-         <item row="9" column="0" colspan="2">
+         <item row="11" column="0" colspan="2">
           <widget class="QGroupBox" name="groupBox_10">
            <property name="enabled">
             <bool>true</bool>
@@ -3753,5 +3770,21 @@
     </hint>
    </hints>
   </connection>
+  <connection>
+   <sender>systemTrayEnabled</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>systemTrayWhenStarted</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>404</x>
+     <y>245</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>404</x>
+     <y>271</y>
+    </hint>
+   </hints>
+  </connection>
  </connections>
 </ui>

BIN
obs/forms/images/tray_active.png


+ 1 - 0
obs/forms/obs.qrc

@@ -13,6 +13,7 @@
     <file>images/properties.png</file>
     <file>images/up.png</file>
     <file>images/obs.png</file>
+    <file>images/tray_active.png</file>
   </qresource>
   <qresource prefix="/settings">
     <file>images/settings/advanced.png</file>

+ 4 - 0
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",

+ 14 - 6
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;
 		}
 

+ 164 - 8
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<int> 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"));
+}

+ 22 - 0
obs/window-basic-main.hpp

@@ -19,6 +19,7 @@
 
 #include <QBuffer>
 #include <QAction>
+#include <QSystemTrayIcon>
 #include <obs.hpp>
 #include <vector>
 #include <memory>
@@ -135,6 +136,14 @@ private:
 
 	QPointer<QMenu> 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;

+ 20 - 0
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()

+ 28 - 6
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<OBSBasic*>(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<OBSBasic*>(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<OBSBasic*>(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;
 }

+ 1 - 0
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;