fix(windows): 校准麦克风权限探测 (#53)
Co-authored-by: Cooper-X-Oak <Cooper-X-Oak@users.noreply.github.com>
This commit is contained in:
1 parent
bb7300ee85
commit
ce0f00fd54
2 files changed
+68
-9
No files matched your search
@@ -264,6 +264,11 @@ mod platform {
|
||||
/// Windows 的麦克风权限走系统设置 → 隐私 → 麦克风;
|
||||
/// 这里用 cpal 建立一次短生命周期输入流,避免只查设备格式时误报已授权。
|
||||
pub fn check_microphone() -> PermissionStatus {
|
||||
if windows_microphone_registry_denied() {
|
||||
log::warn!("[mic] Windows microphone privacy registry is denied");
|
||||
return PermissionStatus::Denied;
|
||||
}
|
||||
|
||||
let host = cpal::default_host();
|
||||
let Some(device) = host.default_input_device() else {
|
||||
log::warn!("[mic] no default input device");
|
||||
@@ -334,6 +339,51 @@ mod platform {
|
||||
drop(stream);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn windows_microphone_registry_denied() -> bool {
|
||||
candidate_microphone_registry_paths()
|
||||
.into_iter()
|
||||
.any(|path| registry_value_is_deny(&path))
|
||||
}
|
||||
|
||||
fn candidate_microphone_registry_paths() -> Vec<String> {
|
||||
let mut paths = vec![
|
||||
r"HKCU\Software\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone".to_string(),
|
||||
r"HKCU\Software\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone\NonPackaged".to_string(),
|
||||
r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone".to_string(),
|
||||
];
|
||||
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(encoded) = exe.to_str().map(|path| path.replace('\\', "#")) {
|
||||
paths.push(format!(
|
||||
r"HKCU\Software\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone\NonPackaged\{encoded}"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
paths
|
||||
}
|
||||
|
||||
fn registry_value_is_deny(path: &str) -> bool {
|
||||
let output = match std::process::Command::new("reg")
|
||||
.args(["query", path, "/v", "Value"])
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(err) => {
|
||||
log::warn!("[mic] reg query failed for {path}: {err}");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
if !output.status.success() {
|
||||
return false;
|
||||
}
|
||||
|
||||
String::from_utf8_lossy(&output.stdout)
|
||||
.lines()
|
||||
.any(|line| line.contains("REG_SZ") && line.split_whitespace().any(|part| part == "Deny"))
|
||||
}
|
||||
}
|
||||
|
||||
pub use platform::{
|
||||
|
||||
@@ -565,37 +565,46 @@ function PermissionsSection() {
|
||||
const [hotkey, setHotkey] = useState<HotkeyStatus | null>(null);
|
||||
const { capability } = useHotkeySettings();
|
||||
|
||||
const refresh = async () => {
|
||||
const refreshPermissions = async () => {
|
||||
const [a, m] = await Promise.all([
|
||||
checkAccessibilityPermission(),
|
||||
checkMicrophonePermission(),
|
||||
]);
|
||||
setAccessibility(a);
|
||||
setMicrophone(m);
|
||||
};
|
||||
|
||||
const refreshHotkey = async () => {
|
||||
setHotkey(await getHotkeyStatus());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
refresh();
|
||||
const id = window.setInterval(refresh, 1000);
|
||||
// 用户从系统设置切回来时立刻刷新(不等下一个 1s tick)
|
||||
const onFocus = () => refresh();
|
||||
refreshPermissions();
|
||||
refreshHotkey();
|
||||
const hotkeyId = window.setInterval(refreshHotkey, 1000);
|
||||
// 麦克风检查会短暂打开输入流,避免每秒探测导致隐私指示器频繁闪烁。
|
||||
const permissionId = window.setInterval(refreshPermissions, 10000);
|
||||
const onFocus = () => {
|
||||
refreshPermissions();
|
||||
refreshHotkey();
|
||||
};
|
||||
window.addEventListener('focus', onFocus);
|
||||
return () => {
|
||||
window.clearInterval(id);
|
||||
window.clearInterval(hotkeyId);
|
||||
window.clearInterval(permissionId);
|
||||
window.removeEventListener('focus', onFocus);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const reRequestAccessibility = async () => {
|
||||
await requestAccessibilityPermission();
|
||||
refresh();
|
||||
refreshPermissions();
|
||||
};
|
||||
|
||||
const reRequestMicrophone = async () => {
|
||||
if (microphone === 'denied' || microphone === 'restricted') {
|
||||
await openSystemSettings('microphone');
|
||||
refresh();
|
||||
refreshPermissions();
|
||||
return;
|
||||
}
|
||||
const status = await requestMicrophonePermission();
|
||||
@@ -603,7 +612,7 @@ function PermissionsSection() {
|
||||
if (status === 'denied' || status === 'restricted') {
|
||||
await openSystemSettings('microphone');
|
||||
}
|
||||
refresh();
|
||||
refreshPermissions();
|
||||
};
|
||||
|
||||
const desc = capability?.requiresAccessibilityPermission
|
||||
|
||||
Reference in new issue
Block a user