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"))
}
/// 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.
/// Returns true if a built-in display is found (MacBook), false otherwise (Mac Mini, Mac Studio, etc.)
/// This uses pmset to check for battery information.
/// Returns true if a battery is detected (laptop), false otherwise (desktop)
#[cfg(target_os = "macos")]
#[tauri::command]
pub fn has_builtin_display() -> Result<bool, String> {
let output = Command::new("ioreg")
.args(["-l", "-w", "0", "-r", "-c", "IODisplayConnect"])
pub fn is_laptop() -> Result<bool, String> {
let output = Command::new("pmset")
.arg("-g")
.arg("batt")
.output()
.map_err(|e| format!("Failed to execute ioreg: {}", e))?;
if !output.status.success() {
return Err(format!(
"ioreg command failed with status: {}",
output.status
));
}
.map_err(|e| e.to_string())?;
let stdout = String::from_utf8_lossy(&output.stdout);
// Look for built-in display indicators
// Built-in displays typically have AppleDisplay or AppleBacklightDisplay
Ok(stdout.contains("AppleBacklightDisplay")
|| (stdout.contains("built-in") && stdout.contains("IODisplayConnect")))
// Check if InternalBattery is present (laptops have batteries, desktops typically don't)
Ok(stdout.contains("InternalBattery"))
}
/// Stub implementation for non-macOS platforms
@ -62,10 +54,10 @@ pub fn is_clamshell() -> Result<bool, String> {
}
/// 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"))]
#[tauri::command]
pub fn has_builtin_display() -> Result<bool, String> {
pub fn is_laptop() -> Result<bool, String> {
Ok(false)
}
@ -84,9 +76,11 @@ mod tests {
#[test]
#[cfg(target_os = "macos")]
fn test_has_builtin_display() {
let result = has_builtin_display();
fn test_is_laptop() {
let result = is_laptop();
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::get_clamshell_microphone,
helpers::clamshell::is_clamshell,
helpers::clamshell::has_builtin_display,
helpers::clamshell::is_laptop,
commands::transcription::set_model_unload_timeout,
commands::transcription::get_model_load_status,
commands::transcription::unload_model_manually,

View file

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