diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index b94b03b8d..041422232 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -421,6 +421,8 @@ elseif(OS_MACOS) endif() target_sources(obs PRIVATE platform-osx.mm) + target_sources(obs PRIVATE forms/OBSPermissions.ui window-permissions.cpp + window-permissions.hpp) set_source_files_properties(platform-osx.mm PROPERTIES COMPILE_FLAGS -fobjc-arc) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 3179cc363..cb45ecd5a 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -471,6 +471,23 @@ MissingFiles.AutoSearchText="OBS has found additional matches for missing files MissingFiles.NoMissing.Title="Missing Files Check" MissingFiles.NoMissing.Text="No files appear to be missing." +# macOS permissions dialog +MacPermissions.Title="Review App Permissions" +MacPermissions.Description="OBS Studio requires your permission to be able to provide certain features. It is recommended to enable these permissions, but they are not required to use the app. You can always enable them later." +MacPermissions.Description.OpenDialog="You can re-open this dialog via the OBS Studio menu." +MacPermissions.AccessGranted="Access Granted" +MacPermissions.RequestAccess="Request Access" +MacPermissions.OpenPreferences="Open %1 Preferences" +MacPermissions.Item.ScreenRecording="Screen Recording" +MacPermissions.Item.ScreenRecording.Details="OBS requires this permission to be able to capture your screen." +MacPermissions.Item.Camera="Camera" +MacPermissions.Item.Camera.Details="This permission is needed in order to capture content from a webcam or capture card." +MacPermissions.Item.Microphone="Microphone" +MacPermissions.Item.Microphone.Details="OBS requires this permission if you want to capture your microphone." +MacPermissions.Item.Accessibility="Accessibility" +MacPermissions.Item.Accessibility.Details="For keyboard shortcuts (hotkeys) to work while other apps are focused, please enable this permission." +MacPermissions.Continue="Continue" + # update dialog UpdateAvailable="New Update Available" UpdateAvailable.Text="Version %1.%2.%3 is now available. Click here to download" diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index 435970950..2ddeb7bce 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -535,6 +535,7 @@ + @@ -1966,6 +1967,11 @@ Basic.MainMenu.Help.CheckForUpdates + + + MacPermissions.Title + + Basic.MainMenu.Help.Repair diff --git a/UI/forms/OBSPermissions.ui b/UI/forms/OBSPermissions.ui new file mode 100644 index 000000000..043297ea8 --- /dev/null +++ b/UI/forms/OBSPermissions.ui @@ -0,0 +1,389 @@ + + + OBSPermissions + + + + 0 + 0 + 692 + 550 + + + + + 0 + 0 + + + + + 0 + 0 + + + + MacPermissions.Title + + + + :/res/images/obs.png:/res/images/obs.png + + + + + + + + + 0 + + + QLayout::SetNoConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + MacPermissions.Description + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + MacPermissions.Continue + + + + + + + MacPermissions.Description.OpenDialog + + + true + + + 8 + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + QFormLayout::WrapAllRows + + + 22 + + + 0 + + + + + + true + + + + MacPermissions.Item.ScreenRecording + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + MacPermissions.Item.ScreenRecording.Details + + + + + + + 8 + + + 25 + + + + + + + + + 0 + 0 + + + + + 300 + 0 + + + + + 300 + 16777215 + + + + + 300 + 0 + + + + + + + + + + + true + + + + MacPermissions.Item.Camera + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + MacPermissions.Item.Camera.Details + + + true + + + + + + + 8 + + + 25 + + + + + + + + + 0 + 0 + + + + + 300 + 0 + + + + + 300 + 16777215 + + + + + 300 + 0 + + + + + + + + + + + true + + + + MacPermissions.Item.Microphone + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + MacPermissions.Item.Microphone.Details + + + + + + + 8 + + + 25 + + + + + + + + + 0 + 0 + + + + + 300 + 0 + + + + + 300 + 16777215 + + + + + 300 + 0 + + + + + + + + + + + true + + + + MacPermissions.Item.Accessibility + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + MacPermissions.Item.Accessibility.Details + + + + + + + QLayout::SetDefaultConstraint + + + 8 + + + 25 + + + + + + + + + 0 + 0 + + + + + 300 + 0 + + + + + 300 + 16777215 + + + + + 300 + 0 + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index cd2885c4e..91a7196f8 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -43,6 +43,9 @@ #include "log-viewer.hpp" #include "slider-ignorewheel.hpp" #include "window-basic-main.hpp" +#ifdef __APPLE__ +#include "window-permissions.hpp" +#endif #include "window-basic-settings.hpp" #include "crash-report.hpp" #include "platform.hpp" @@ -2289,6 +2292,17 @@ static int run_program(fstream &logFile, int argc, char *argv[]) MacPermissionStatus screen_permission = CheckPermission(kScreenCapture); + int permissionsDialogLastShown = + config_get_int(GetGlobalConfig(), "General", + "MacOSPermissionsDialogLastShown"); + if (permissionsDialogLastShown < + MACOS_PERMISSIONS_DIALOG_VERSION) { + OBSPermissions *check = new OBSPermissions( + nullptr, screen_permission, video_permission, + audio_permission, accessibility_permission); + check->exec(); + } + bool rosettaTranslated = os_get_emulation_status(); blog(LOG_INFO, "Rosetta translation used: %s", rosettaTranslated ? "true" : "false"); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 891ecebbc..be17dceda 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -53,6 +53,9 @@ #include "window-basic-stats.hpp" #include "window-basic-main-outputs.hpp" #include "window-log-reply.hpp" +#ifdef __APPLE__ +#include "window-permissions.hpp" +#endif #include "window-projector.hpp" #include "window-remux.hpp" #if YOUTUBE_ENABLED @@ -369,6 +372,8 @@ OBSBasic::OBSBasic(QWidget *parent) ui->actionCheckForUpdates->setMenuRole(QAction::AboutQtRole); ui->action_Settings->setMenuRole(QAction::PreferencesRole); + ui->actionShowMacPermissions->setMenuRole( + QAction::ApplicationSpecificRole); ui->actionE_xit->setMenuRole(QAction::QuitRole); #else renameScene->setShortcut({Qt::Key_F2}); @@ -2069,6 +2074,10 @@ void OBSBasic::OBSInit() /* Remove OBS' Fullscreen Interface menu in favor of the one macOS adds by default */ delete ui->actionFullscreenInterface; ui->actionFullscreenInterface = nullptr; +#else + /* Don't show menu to raise macOS-only permissions dialog */ + delete ui->actionShowMacPermissions; + ui->actionShowMacPermissions = nullptr; #endif #if defined(_WIN32) || defined(__APPLE__) @@ -4915,6 +4924,18 @@ void OBSBasic::on_action_Settings_triggered() } } +void OBSBasic::on_actionShowMacPermissions_triggered() +{ +#ifdef __APPLE__ + OBSPermissions *check = + new OBSPermissions(this, CheckPermission(kScreenCapture), + CheckPermission(kVideoDeviceAccess), + CheckPermission(kAudioDeviceAccess), + CheckPermission(kAccessibility)); + check->exec(); +#endif +} + void OBSBasic::ShowMissingFilesDialog(obs_missing_files_t *files) { if (obs_missing_files_count(files) > 0) { diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 3bdb0bff7..90374c3be 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -185,6 +185,7 @@ class OBSBasic : public OBSMainWindow { friend class DeviceToolbarPropertiesThread; friend class OBSBasicSourceSelect; friend class OBSYoutubeActions; + friend class OBSPermissions; friend struct BasicOutputHandler; friend struct OBSStudioAPI; @@ -993,6 +994,7 @@ private slots: void on_actionShow_Recordings_triggered(); void on_actionRemux_triggered(); void on_action_Settings_triggered(); + void on_actionShowMacPermissions_triggered(); void on_actionShowMissingFiles_triggered(); void on_actionAdvAudioProperties_triggered(); void on_actionMixerToolbarAdvAudio_triggered(); diff --git a/UI/window-permissions.cpp b/UI/window-permissions.cpp new file mode 100644 index 000000000..c36e52497 --- /dev/null +++ b/UI/window-permissions.cpp @@ -0,0 +1,102 @@ +/****************************************************************************** + Copyright (C) 2014 by Hugh 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 +#include "window-permissions.hpp" +#include "obs-app.hpp" + +OBSPermissions::OBSPermissions(QWidget *parent, MacPermissionStatus capture, + MacPermissionStatus video, + MacPermissionStatus audio, + MacPermissionStatus accessibility) + : QDialog(parent), ui(new Ui::OBSPermissions) +{ + ui->setupUi(this); + SetStatus(ui->capturePermissionButton, capture, + QTStr("MacPermissions.Item.ScreenRecording")); + SetStatus(ui->videoPermissionButton, video, + QTStr("MacPermissions.Item.Camera")); + SetStatus(ui->audioPermissionButton, audio, + QTStr("MacPermissions.Item.Microphone")); + SetStatus(ui->accessibilityPermissionButton, accessibility, + QTStr("MacPermissions.Item.Accessibility")); +} + +void OBSPermissions::SetStatus(QPushButton *btn, MacPermissionStatus status, + const QString &preference) +{ + if (status == kPermissionAuthorized) { + btn->setText(QTStr("MacPermissions.AccessGranted")); + } else if (status == kPermissionNotDetermined) { + btn->setText(QTStr("MacPermissions.RequestAccess")); + } else { + btn->setText( + QTStr("MacPermissions.OpenPreferences").arg(preference)); + } + btn->setEnabled(status != kPermissionAuthorized); + btn->setProperty("status", status); +} + +void OBSPermissions::on_capturePermissionButton_clicked() +{ + OpenMacOSPrivacyPreferences("ScreenCapture"); + RequestPermission(kScreenCapture); +} + +void OBSPermissions::on_videoPermissionButton_clicked() +{ + MacPermissionStatus status = + (MacPermissionStatus)ui->videoPermissionButton + ->property("status") + .toInt(); + if (status == kPermissionNotDetermined) { + status = RequestPermission(kVideoDeviceAccess); + SetStatus(ui->videoPermissionButton, status, + QTStr("MacPermissions.Item.Camera")); + } else { + OpenMacOSPrivacyPreferences("Camera"); + } +} + +void OBSPermissions::on_audioPermissionButton_clicked() +{ + MacPermissionStatus status = + (MacPermissionStatus)ui->audioPermissionButton + ->property("status") + .toInt(); + if (status == kPermissionNotDetermined) { + status = RequestPermission(kAudioDeviceAccess); + SetStatus(ui->audioPermissionButton, status, + QTStr("MacPermissions.Item.Microphone")); + } else { + OpenMacOSPrivacyPreferences("Microphone"); + } +} + +void OBSPermissions::on_accessibilityPermissionButton_clicked() +{ + OpenMacOSPrivacyPreferences("Accessibility"); + RequestPermission(kAccessibility); +} + +void OBSPermissions::on_continueButton_clicked() +{ + config_set_int(GetGlobalConfig(), "General", + "MacOSPermissionsDialogLastShown", + MACOS_PERMISSIONS_DIALOG_VERSION); + close(); +} diff --git a/UI/window-permissions.hpp b/UI/window-permissions.hpp new file mode 100644 index 000000000..6d0efaa45 --- /dev/null +++ b/UI/window-permissions.hpp @@ -0,0 +1,44 @@ +/****************************************************************************** + Copyright (C) 2014 by Hugh 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_OBSPermissions.h" +#include "platform.hpp" + +#define MACOS_PERMISSIONS_DIALOG_VERSION 1 + +class OBSPermissions : public QDialog { + Q_OBJECT + +private: + std::unique_ptr ui; + void SetStatus(QPushButton *btn, MacPermissionStatus status, + const QString &preference); + +public: + OBSPermissions(QWidget *parent, MacPermissionStatus capture, + MacPermissionStatus video, MacPermissionStatus audio, + MacPermissionStatus accessibility); + +private slots: + void on_capturePermissionButton_clicked(); + void on_videoPermissionButton_clicked(); + void on_audioPermissionButton_clicked(); + void on_accessibilityPermissionButton_clicked(); + void on_continueButton_clicked(); +};