Skip to content

Commit

Permalink
permissions requesting rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
filleduchaos committed Sep 12, 2024
1 parent 9012e6c commit 6132424
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 201 deletions.
2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1302,7 +1302,7 @@ pub fn run() {

tokio::spawn(updater::check_for_updates(app.handle().clone()));

if permissions::do_permissions_check().necessary_granted() {
if permissions::do_permissions_check(true).necessary_granted() {
open_main_window(app_handle.clone());
} else {
permissions::open_permissions_window(app);
Expand Down
183 changes: 99 additions & 84 deletions apps/desktop/src-tauri/src/permissions.rs
Original file line number Diff line number Diff line change
@@ -1,138 +1,153 @@
use std::process::Command;

use serde::{Deserialize, Serialize};
use tauri::{Manager, WebviewUrl, WebviewWindow, Wry};

#[cfg(target_os = "macos")]
use nokhwa_bindings_macos::{AVAuthorizationStatus, AVMediaType};

#[derive(Serialize, Deserialize, specta::Type)]
#[serde(rename_all = "camelCase")]
pub enum MacOSPermissionSettings {
pub enum OSPermission {
ScreenRecording,
Camera,
Microphone,
}

#[derive(Serialize, Deserialize, specta::Type)]
#[serde(rename_all = "camelCase")]
pub enum OSPermissionSettings {
MacOS(MacOSPermissionSettings),
}

#[tauri::command]
#[specta::specta]
pub fn open_permission_settings(settings: OSPermissionSettings) {
match settings {
OSPermissionSettings::MacOS(macos) => match macos {
MacOSPermissionSettings::ScreenRecording => {
pub fn open_permission_settings(permission: OSPermission) {
#[cfg(target_os = "macos")]
{
use std::process::Command;

match permission {
OSPermission::ScreenRecording => {
Command::new("open")
.arg("x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture")
.spawn()
.expect("Failed to open Screen Recording settings");
.arg("x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture")
.spawn()
.expect("Failed to open Screen Recording settings");
}
MacOSPermissionSettings::Camera => {
OSPermission::Camera => {
Command::new("open")
.arg("x-apple.systempreferences:com.apple.preference.security?Privacy_Camera")
.spawn()
.expect("Failed to open Camera settings");
}
MacOSPermissionSettings::Microphone => {
OSPermission::Microphone => {
Command::new("open")
.arg("x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone")
.spawn()
.expect("Failed to open Microphone settings");
}
},
}
}
}

#[tauri::command]
#[specta::specta]
pub async fn request_permission(permission: OSPermissionSettings) {
match permission {
OSPermissionSettings::MacOS(macos) => {
#[cfg(target_os = "macos")]
{
use objc::{runtime::*, *};
let cls = class!(AVCaptureDevice);
use nokhwa_bindings_macos::core_media::AVMediaTypeAudio;
use tauri_nspanel::block::ConcreteBlock;

match macos {
MacOSPermissionSettings::ScreenRecording => {
scap::request_permission();
}
MacOSPermissionSettings::Camera => {
nokhwa::nokhwa_initialize(|_| {});
}
MacOSPermissionSettings::Microphone => unsafe {
let wrapper = move |_: BOOL| {};

let objc_fn_block: ConcreteBlock<(BOOL,), (), _> =
ConcreteBlock::new(wrapper);
let objc_fn_pass = objc_fn_block.copy();
let _: () = msg_send![cls, requestAccessForMediaType:(AVMediaTypeAudio.clone()) completionHandler:objc_fn_pass];
},
}
pub async fn request_permission(permission: OSPermission) {
#[cfg(target_os = "macos")]
{
match permission {
OSPermission::ScreenRecording => {
scap::request_permission();
}
OSPermission::Camera => request_av_permission(AVMediaType::Video),
OSPermission::Microphone => request_av_permission(AVMediaType::Audio),
}
}
}

#[derive(Serialize, Deserialize, specta::Type)]
#[cfg(target_os = "macos")]
fn request_av_permission(media_type: AVMediaType) {
use objc::{runtime::*, *};
use tauri_nspanel::block::ConcreteBlock;

let callback = move |_: BOOL| {};
let cls = class!(AVCaptureDevice);
let objc_fn_block: ConcreteBlock<(BOOL,), (), _> = ConcreteBlock::new(callback);
let objc_fn_pass = objc_fn_block.copy();
unsafe {
let _: () = msg_send![cls, requestAccessForMediaType:media_type.into_ns_str() completionHandler:objc_fn_pass];
};
}

#[derive(Serialize, Deserialize, Debug, specta::Type)]
#[serde(rename_all = "camelCase")]
pub struct MacOSPermissionsCheck {
screen_recording: bool,
microphone: bool,
camera: bool,
pub enum OSPermissionStatus {
// This platform does not require this permission
NotNeeded,
// The user has neither granted nor denied permission
Empty,
// The user has explicitly granted permission
Granted,
// The user has denied permission, or has granted it but not yet restarted
Denied,
}

#[derive(Serialize, Deserialize, specta::Type)]
#[serde(rename_all = "camelCase", tag = "os")]
pub enum OSPermissionsCheck {
MacOS(MacOSPermissionsCheck),
Other,
impl OSPermissionStatus {
fn permitted(&self) -> bool {
match self {
Self::NotNeeded | Self::Granted => true,
_ => false,
}
}
}

#[derive(Serialize, Deserialize, Debug, specta::Type)]
#[serde(rename_all = "camelCase")]
pub struct OSPermissionsCheck {
screen_recording: OSPermissionStatus,
microphone: OSPermissionStatus,
camera: OSPermissionStatus,
}

impl OSPermissionsCheck {
pub fn necessary_granted(&self) -> bool {
match self {
Self::MacOS(macos) => macos.screen_recording && macos.microphone && macos.camera,
Self::Other => true,
}
self.screen_recording.permitted() && self.microphone.permitted() && self.camera.permitted()
}
}

#[tauri::command]
#[specta::specta]
pub fn do_permissions_check() -> OSPermissionsCheck {
pub fn do_permissions_check(initial_check: bool) -> OSPermissionsCheck {
#[cfg(target_os = "macos")]
{
OSPermissionsCheck::MacOS(MacOSPermissionsCheck {
screen_recording: scap::has_permission(),
microphone: {
use nokhwa_bindings_macos::{AVAuthorizationStatus, AVMediaType};
use objc::*;

let cls = objc::class!(AVCaptureDevice);
let status: AVAuthorizationStatus = unsafe {
msg_send![cls, authorizationStatusForMediaType:AVMediaType::Audio.into_ns_str()]
};
matches!(status, AVAuthorizationStatus::Authorized)
},
camera: {
use nokhwa_bindings_macos::{AVAuthorizationStatus, AVMediaType};
use objc::*;

let cls = objc::class!(AVCaptureDevice);
let status: AVAuthorizationStatus = unsafe {
msg_send![cls, authorizationStatusForMediaType:AVMediaType::Video.into_ns_str()]
};
matches!(status, AVAuthorizationStatus::Authorized)
OSPermissionsCheck {
screen_recording: {
let result = scap::has_permission();
match (result, initial_check) {
(true, _) => OSPermissionStatus::Granted,
(false, true) => OSPermissionStatus::Empty,
(false, false) => OSPermissionStatus::Denied,
}
},
})
microphone: check_av_permission(AVMediaType::Audio),
camera: check_av_permission(AVMediaType::Video),
}
}

#[cfg(not(target_os = "macos"))]
OSpermissiosCheck::Other
{
OSPermissionsCheck {
screen_recording: OSPermissionStatus::NotNeeded,
microphone: OSPermissionStatus::NotNeeded,
camera: OSPermissionStatus::NotNeeded,
};
}
}

#[cfg(target_os = "macos")]
pub fn check_av_permission(media_type: AVMediaType) -> OSPermissionStatus {
use objc::*;

let cls = objc::class!(AVCaptureDevice);
let status: AVAuthorizationStatus =
unsafe { msg_send![cls, authorizationStatusForMediaType:media_type.into_ns_str()] };
match status {
AVAuthorizationStatus::NotDetermined => OSPermissionStatus::Empty,
AVAuthorizationStatus::Authorized => OSPermissionStatus::Granted,
_ => OSPermissionStatus::Denied,
}
}

#[tauri::command]
Expand All @@ -145,7 +160,7 @@ pub fn open_permissions_window(app: &impl Manager<Wry>) {

WebviewWindow::builder(app, "permissions", WebviewUrl::App("/permissions".into()))
.title("Cap")
.inner_size(300.0, 325.0)
.inner_size(300.0, 256.0)
.resizable(false)
.maximized(false)
.shadow(true)
Expand Down
10 changes: 10 additions & 0 deletions apps/desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
}
}
},
"plugins": {
"updater": {
"active": false,
"endpoints": [
"https://cdn.crabnebula.app/update/cap/cap/{{target}}-{{arch}}/{{current_version}}"
],
"dialog": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUyOTAzOTdFNzJFQkRFOTMKUldTVDN1dHlmam1RNHFXb1VYTXlrQk1iMFFkcjN0YitqZlA5WnZNY0ZtQ1dvM1dxK211M3VIYUQK"
}
},
"bundle": {
"active": true,
"targets": "all",
Expand Down
6 changes: 1 addition & 5 deletions apps/desktop/src-tauri/tauri.conf.prod.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
"identifier": "so.cap.desktop",
"plugins": {
"updater": {
"endpoints": [
"https://cdn.crabnebula.app/update/cap/cap/{{target}}-{{arch}}/{{current_version}}"
],
"dialog": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUyOTAzOTdFNzJFQkRFOTMKUldTVDN1dHlmam1RNHFXb1VYTXlrQk1iMFFkcjN0YitqZlA5WnZNY0ZtQ1dvM1dxK211M3VIYUQK"
"active": true
}
},
"bundle": {
Expand Down
Loading

0 comments on commit 6132424

Please sign in to comment.