1#[derive(Debug, Copy, Clone, PartialEq, Eq)]
16#[non_exhaustive]
17pub enum Theme {
18 Default,
20
21 Dark,
23}
24
25pub fn global_default_theme() -> Option<Theme> {
29 #[cfg(target_vendor = "apple")]
30 return crate::apple::get();
31
32 #[cfg(not(target_vendor = "apple"))]
33 return None;
34}
35
36#[cfg(target_vendor = "apple")]
37mod apple {
38 use crate::Theme;
39 use core_foundation_sys::preferences::kCFPreferencesAnyApplication;
40 use core::ptr;
41 use core_foundation_sys::base::CFRelease;
42 use core_foundation_sys::base::kCFAllocatorNull;
43 use core_foundation_sys::preferences::CFPreferencesCopyAppValue;
44 use core_foundation_sys::string::CFStringCreateWithBytesNoCopy;
45 use core_foundation_sys::string::CFStringHasPrefix;
46 use core_foundation_sys::string::CFStringRef;
47 use core_foundation_sys::string::kCFStringEncodingUTF8;
48
49 fn static_cf_string(string: &'static str) -> CFStringRef {
50 unsafe {
51 CFStringCreateWithBytesNoCopy(
52 ptr::null_mut(),
53 string.as_ptr(),
54 string.len() as _,
55 kCFStringEncodingUTF8,
56 false as _,
57 kCFAllocatorNull,
58 )
59 }
60 }
61
62 pub(crate) fn get() -> Option<Theme> {
63 #[cfg(target_vendor = "apple")]
64 unsafe {
65 let interface_style = static_cf_string("AppleInterfaceStyle");
66 if interface_style.is_null() {
67 return None;
68 }
69 let dark = static_cf_string("Dark");
70
71 let value = CFPreferencesCopyAppValue(interface_style, kCFPreferencesAnyApplication);
72 let is_dark = !value.is_null() && !dark.is_null() && 0 != CFStringHasPrefix(value.cast(), dark);
73
74 CFRelease(dark.cast());
75 CFRelease(interface_style.cast());
76
77 Some(if is_dark { Theme::Dark } else { Theme::Default })
78 }
79 }
80}
81
82#[test]
83fn test() {
84 #[cfg(target_vendor = "apple")]
85 assert!(global_default_theme().is_some());
86
87 global_default_theme();
88}