gitkraft_core/features/theme/
system.rs1pub const SYSTEM_THEME_NAME: &str = "System";
5
6pub const SYSTEM_DARK_INDEX: usize = 0; pub const SYSTEM_LIGHT_INDEX: usize = 1; pub fn is_system_name(name: &str) -> bool {
13 name.eq_ignore_ascii_case(SYSTEM_THEME_NAME)
14}
15
16pub fn system_theme_index(dark: bool) -> usize {
18 if dark {
19 SYSTEM_DARK_INDEX
20 } else {
21 SYSTEM_LIGHT_INDEX
22 }
23}
24
25pub fn parse_macos(out: &str) -> bool {
27 out.trim().eq_ignore_ascii_case("dark")
28}
29
30pub fn parse_gsettings(out: &str) -> Option<bool> {
32 let v = out.trim().trim_matches('\'');
33 match v {
34 "prefer-dark" => Some(true),
35 "prefer-light" | "default" => Some(false),
36 _ => None,
37 }
38}
39
40pub fn parse_windows(out: &str) -> Option<bool> {
42 let v = out.split_whitespace().last()?;
43 match v {
44 "0x0" => Some(true),
45 "0x1" => Some(false),
46 _ => None,
47 }
48}
49
50#[allow(dead_code)]
51fn run(cmd: &str, args: &[&str]) -> Option<String> {
52 let o = std::process::Command::new(cmd).args(args).output().ok()?;
53 Some(String::from_utf8_lossy(&o.stdout).into_owned())
54}
55
56pub fn detect_system_dark() -> Option<bool> {
58 #[cfg(target_os = "macos")]
59 {
60 Some(parse_macos(
62 &run("defaults", &["read", "-g", "AppleInterfaceStyle"]).unwrap_or_default(),
63 ))
64 }
65 #[cfg(target_os = "windows")]
66 {
67 parse_windows(&run(
68 "reg",
69 &[
70 "query",
71 r"HKCU\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
72 "/v",
73 "AppsUseLightTheme",
74 ],
75 )?)
76 }
77 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
78 {
79 if let Some(d) = run(
80 "gsettings",
81 &["get", "org.gnome.desktop.interface", "color-scheme"],
82 )
83 .and_then(|o| parse_gsettings(&o))
84 {
85 return Some(d);
86 }
87 std::env::var("GTK_THEME")
88 .ok()
89 .map(|t| t.to_ascii_lowercase().contains("dark"))
90 }
91}
92
93pub fn current_system_theme_index() -> usize {
95 system_theme_index(detect_system_dark().unwrap_or(true))
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn system_name_is_case_insensitive_and_not_a_preset() {
104 assert!(is_system_name("System") && is_system_name("system"));
105 assert!(!is_system_name("Nord"));
106 assert!(!super::super::THEME_NAMES.iter().any(|n| is_system_name(n)));
107 }
108
109 #[test]
110 fn dark_and_light_map_to_matching_presets() {
111 assert!(super::super::theme_by_index(system_theme_index(true)).is_dark);
112 assert!(!super::super::theme_by_index(system_theme_index(false)).is_dark);
113 }
114
115 #[test]
116 fn parsers() {
117 assert!(parse_macos("Dark\n"));
118 assert!(!parse_macos(""));
119 assert_eq!(parse_gsettings("'prefer-dark'\n"), Some(true));
120 assert_eq!(parse_gsettings("'default'"), Some(false));
121 assert_eq!(parse_gsettings("garbage"), None);
122 assert_eq!(
123 parse_windows(" AppsUseLightTheme REG_DWORD 0x0\r\n"),
124 Some(true)
125 );
126 assert_eq!(
127 parse_windows("AppsUseLightTheme REG_DWORD 0x1"),
128 Some(false)
129 );
130 assert_eq!(parse_windows(""), None);
131 }
132}