UI: Add macOS permissions window

Co-Authored-By: Matt Gajownik <git@wizardcm.com>
This commit is contained in:
gxalpha 2022-07-26 01:31:12 +02:00 committed by Sebastian Beckmann
parent b7a20cd5e4
commit 37526e9ac9
9 changed files with 597 additions and 0 deletions

View File

@ -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)

View File

@ -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. <a href='%4'>Click here to download</a>"

View File

@ -535,6 +535,7 @@
<addaction name="actionRepair"/>
<addaction name="actionCheckForUpdates"/>
<addaction name="actionShowAbout"/>
<addaction name="actionShowMacPermissions"/>
<addaction name="separator"/>
</widget>
<widget class="QMenu" name="menuBasic_MainMenu_Edit">
@ -1966,6 +1967,11 @@
<string>Basic.MainMenu.Help.CheckForUpdates</string>
</property>
</action>
<action name="actionShowMacPermissions">
<property name="text">
<string>MacPermissions.Title</string>
</property>
</action>
<action name="actionRepair">
<property name="text">
<string>Basic.MainMenu.Help.Repair</string>

389
UI/forms/OBSPermissions.ui Normal file
View File

@ -0,0 +1,389 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OBSPermissions</class>
<widget class="QDialog" name="OBSPermissions">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>692</width>
<height>550</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>MacPermissions.Title</string>
</property>
<property name="windowIcon">
<iconset resource="obs.qrc">
<normaloff>:/res/images/obs.png</normaloff>:/res/images/obs.png</iconset>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>MacPermissions.Description</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="continueButton">
<property name="text">
<string>MacPermissions.Continue</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>MacPermissions.Description.OpenDialog</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="bottomMargin">
<number>8</number>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="rowWrapPolicy">
<enum>QFormLayout::WrapAllRows</enum>
</property>
<property name="topMargin">
<number>22</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>MacPermissions.Item.ScreenRecording</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_5">
<property name="text">
<string>MacPermissions.Item.ScreenRecording.Details</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>25</number>
</property>
<item>
<widget class="QWidget"/>
</item>
<item>
<widget class="QPushButton" name="capturePermissionButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="font">
<font>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>MacPermissions.Item.Camera</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_8">
<property name="text">
<string>MacPermissions.Item.Camera.Details</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="topMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>25</number>
</property>
<item>
<widget class="QWidget"/>
</item>
<item>
<widget class="QPushButton" name="videoPermissionButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_9">
<property name="font">
<font>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>MacPermissions.Item.Microphone</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_10">
<property name="text">
<string>MacPermissions.Item.Microphone.Details</string>
</property>
</widget>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="topMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>25</number>
</property>
<item>
<widget class="QWidget"/>
</item>
<item>
<widget class="QPushButton" name="audioPermissionButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>MacPermissions.Item.Accessibility</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_13">
<property name="text">
<string>MacPermissions.Item.Accessibility.Details</string>
</property>
</widget>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="topMargin">
<number>8</number>
</property>
<property name="bottomMargin">
<number>25</number>
</property>
<item>
<widget class="QWidget"/>
</item>
<item>
<widget class="QPushButton" name="accessibilityPermissionButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="obs.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -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");

View File

@ -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) {

View File

@ -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();

102
UI/window-permissions.cpp Normal file
View File

@ -0,0 +1,102 @@
/******************************************************************************
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.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 <QLabel>
#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();
}

44
UI/window-permissions.hpp Normal file
View File

@ -0,0 +1,44 @@
/******************************************************************************
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.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_OBSPermissions.h"
#include "platform.hpp"
#define MACOS_PERMISSIONS_DIALOG_VERSION 1
class OBSPermissions : public QDialog {
Q_OBJECT
private:
std::unique_ptr<Ui::OBSPermissions> 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();
};