thread_local! {
static ACCESSIBILITY_PREFS: std::cell::RefCell<AccessibilityPreferences> =
std::cell::RefCell::new(AccessibilityPreferences::default());
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct AccessibilityPreferences {
pub reduce_motion: bool,
pub reduce_transparency: bool,
pub increase_contrast: bool,
}
impl AccessibilityPreferences {
pub fn detect_from_system() -> Self {
#[cfg(target_os = "macos")]
{
let reduce_motion = std::process::Command::new("defaults")
.args(["read", "-g", "com.apple.universalaccess", "reduceMotion"])
.output()
.ok()
.and_then(|o| String::from_utf8(o.stdout).ok())
.map(|s| s.trim() == "1")
.unwrap_or(false);
let reduce_transparency = std::process::Command::new("defaults")
.args([
"read",
"-g",
"com.apple.universalaccess",
"reduceTransparency",
])
.output()
.ok()
.and_then(|o| String::from_utf8(o.stdout).ok())
.map(|s| s.trim() == "1")
.unwrap_or(false);
let increase_contrast = std::process::Command::new("defaults")
.args([
"read",
"-g",
"com.apple.universalaccess",
"increaseContrast",
])
.output()
.ok()
.and_then(|o| String::from_utf8(o.stdout).ok())
.map(|s| s.trim() == "1")
.unwrap_or(false);
Self {
reduce_motion,
reduce_transparency,
increase_contrast,
}
}
#[cfg(target_os = "linux")]
{
let reduce_motion = std::env::var("GTK_A11Y")
.map(|v| v.to_lowercase().contains("reduce-motion"))
.unwrap_or(false)
|| {
std::process::Command::new("gsettings")
.args([
"get",
"org.gnome.desktop.interface",
"enable-animations",
])
.output()
.ok()
.and_then(|o| String::from_utf8(o.stdout).ok())
.map(|s| s.trim() == "'false'" || s.trim() == "false")
.unwrap_or(false)
};
let reduce_transparency = false;
let increase_contrast = std::env::var("GTK_THEME")
.map(|v| v.to_lowercase().contains("highcontrast"))
.unwrap_or(false);
Self {
reduce_motion,
reduce_transparency,
increase_contrast,
}
}
#[cfg(target_os = "windows")]
{
use std::process::Command;
fn reg_query(key: &str, value_name: &str) -> Option<String> {
Command::new("reg")
.args(["query", key, "/v", value_name])
.output()
.ok()
.and_then(|o| {
if o.status.success() {
String::from_utf8(o.stdout).ok()
} else {
None
}
})
.and_then(|s| {
s.lines()
.last()?
.split_whitespace()
.last()
.map(String::from)
})
}
let reduce_motion = reg_query(
"HKCU\\Control Panel\\Accessibility\\EffectsAnimationEfficiency",
"EffectsAnimationEfficiency",
)
.map(|v| v == "1")
.unwrap_or(false);
let reduce_transparency = reg_query(
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
"EnableTransparency",
)
.map(|v| v == "0")
.unwrap_or(false);
let increase_contrast = reg_query(
"HKCU\\Control Panel\\Accessibility\\HighContrast",
"HighContrast",
)
.map(|v| v == "1")
.unwrap_or(false);
Self {
reduce_motion,
reduce_transparency,
increase_contrast,
}
}
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
{
Self::default()
}
}
pub fn min_alpha(&self, requested: f32) -> f32 {
if self.increase_contrast {
requested.max(0.5)
} else {
requested
}
}
pub fn should_disable_glass(&self) -> bool {
self.reduce_transparency
}
pub fn should_reduce_motion(&self) -> bool {
self.reduce_motion
}
pub fn should_increase_contrast(&self) -> bool {
self.increase_contrast
}
}
pub fn accessibility_preferences() -> AccessibilityPreferences {
ACCESSIBILITY_PREFS.with(|p| *p.borrow())
}
pub fn set_accessibility_preferences(prefs: AccessibilityPreferences) {
ACCESSIBILITY_PREFS.with(|p| {
*p.borrow_mut() = prefs;
});
}