fix: update laptop detection logic to be more reliable (#348)

This commit is contained in:
Jorge 2025-11-18 13:43:35 +00:00 committed by GitHub
parent c359e3ff17
commit 97f3018be0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 29 additions and 36 deletions

View file

@ -26,31 +26,23 @@ pub fn is_clamshell() -> Result<bool, String> {
Ok(stdout.contains("\"AppleClamshellState\" = Yes")) Ok(stdout.contains("\"AppleClamshellState\" = Yes"))
} }
/// Checks if the Mac has a built-in display (i.e., is a laptop) /// Checks if the Mac is a laptop by detecting battery presence
/// ///
/// This queries the macOS IORegistry for built-in displays. /// This uses pmset to check for battery information.
/// Returns true if a built-in display is found (MacBook), false otherwise (Mac Mini, Mac Studio, etc.) /// Returns true if a battery is detected (laptop), false otherwise (desktop)
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
#[tauri::command] #[tauri::command]
pub fn has_builtin_display() -> Result<bool, String> { pub fn is_laptop() -> Result<bool, String> {
let output = Command::new("ioreg") let output = Command::new("pmset")
.args(["-l", "-w", "0", "-r", "-c", "IODisplayConnect"]) .arg("-g")
.arg("batt")
.output() .output()
.map_err(|e| format!("Failed to execute ioreg: {}", e))?; .map_err(|e| e.to_string())?;
if !output.status.success() {
return Err(format!(
"ioreg command failed with status: {}",
output.status
));
}
let stdout = String::from_utf8_lossy(&output.stdout); let stdout = String::from_utf8_lossy(&output.stdout);
// Look for built-in display indicators // Check if InternalBattery is present (laptops have batteries, desktops typically don't)
// Built-in displays typically have AppleDisplay or AppleBacklightDisplay Ok(stdout.contains("InternalBattery"))
Ok(stdout.contains("AppleBacklightDisplay")
|| (stdout.contains("built-in") && stdout.contains("IODisplayConnect")))
} }
/// Stub implementation for non-macOS platforms /// Stub implementation for non-macOS platforms
@ -62,10 +54,10 @@ pub fn is_clamshell() -> Result<bool, String> {
} }
/// Stub implementation for non-macOS platforms /// Stub implementation for non-macOS platforms
/// Always returns false since built-in display detection is macOS-specific /// Always returns false since laptop detection is macOS-specific
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
#[tauri::command] #[tauri::command]
pub fn has_builtin_display() -> Result<bool, String> { pub fn is_laptop() -> Result<bool, String> {
Ok(false) Ok(false)
} }
@ -84,9 +76,11 @@ mod tests {
#[test] #[test]
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
fn test_has_builtin_display() { fn test_is_laptop() {
let result = has_builtin_display(); let result = is_laptop();
assert!(result.is_ok()); assert!(result.is_ok());
let _ = result.unwrap(); if let Ok(is_laptop) = result {
println!("Is laptop: {}", is_laptop);
}
} }
} }

View file

@ -352,7 +352,7 @@ pub fn run() {
commands::audio::set_clamshell_microphone, commands::audio::set_clamshell_microphone,
commands::audio::get_clamshell_microphone, commands::audio::get_clamshell_microphone,
helpers::clamshell::is_clamshell, helpers::clamshell::is_clamshell,
helpers::clamshell::has_builtin_display, helpers::clamshell::is_laptop,
commands::transcription::set_model_unload_timeout, commands::transcription::set_model_unload_timeout,
commands::transcription::get_model_load_status, commands::transcription::get_model_load_status,
commands::transcription::unload_model_manually, commands::transcription::unload_model_manually,

View file

@ -22,25 +22,24 @@ export const ClamshellMicrophoneSelector: React.FC<ClamshellMicrophoneSelectorPr
refreshAudioDevices, refreshAudioDevices,
} = useSettings(); } = useSettings();
const [hasBuiltinDisplay, setHasBuiltinDisplay] = useState<boolean>(false); const [isLaptop, setIsLaptop] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
// Check if the device has a built-in display (i.e., is a laptop) const checkIsLaptop = async () => {
const checkBuiltinDisplay = async () => {
try { try {
const result = await invoke<boolean>("has_builtin_display"); const result = await invoke<boolean>("is_laptop");
setHasBuiltinDisplay(result); setIsLaptop(result);
} catch (error) { } catch (error) {
console.error("Failed to check for built-in display:", error); console.error("Failed to check if device is laptop:", error);
setHasBuiltinDisplay(false); setIsLaptop(false);
} }
}; };
checkBuiltinDisplay(); checkIsLaptop();
}, []); }, []);
// Only render on devices with built-in displays (laptops) // Only render on laptops
if (!hasBuiltinDisplay) { if (!isLaptop) {
return null; return null;
} }