Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linux & BSD support #6

Merged
merged 10 commits into from
Jan 17, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ repository = "https://github.com/frewsxcv/rust-dark-light"
description = "Detect if dark mode or light mode is enabled"
readme = "README.md"

[dependencies]
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies]
detect-desktop-environment = "0.2.0"
dconf_rs = "0.3.0"
dirs = "4.0.0"
zbus = "2.0.0"
zvariant = "3.0.0"
rust-ini = "0.17"

[target.'cfg(windows)'.dependencies]
winreg = "0.8.0"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# rust-dark-light

Rust crate to detect if dark mode or light mode is enabled. Supports macOS and Windows.
Rust crate to detect if dark mode or light mode is enabled. Supports macOS, Windows, Linux, and BSDs. On Linux and BSDs, first the XDG Desktop Portal dbus API is checked for the `color-scheme` preference, which works in Flatpak sandboxes without needing filesystem access. If that does not work, fallback methods are used for KDE, GNOME, Cinnamon, MATE, XFCE, and Unity.

[API Documentation](https://docs.rs/dark-light/)

Expand Down
96 changes: 96 additions & 0 deletions src/freedesktop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use detect_desktop_environment::DesktopEnvironment;
use ini::Ini;
use std::path::{Path, PathBuf};
use zbus::blocking::Connection;
use zvariant::Value;

use crate::Mode;

const XDG_KDEGLOBALS: &str = "/etc/xdg/kdeglobals";

fn get_freedesktop_color_scheme() -> Option<Mode> {
let conn = Connection::session();
if conn.is_err() {
return None;
}
let reply = conn.unwrap().call_method(
Some("org.freedesktop.portal.Desktop"),
"/org/freedesktop/portal/desktop",
Some("org.freedesktop.portal.Settings"),
"Read",
&("org.freedesktop.appearance", "color-scheme"),
);
if let Ok(reply) = &reply {
let theme = reply.body::<Value>();
if theme.is_err() {
return None;
}
let theme = theme.unwrap().downcast::<u32>();
match theme.unwrap() {
1 => Some(Mode::Dark),
2 => Some(Mode::Light),
_ => None,
}
} else {
None
}
}

fn detect_gtk(pattern: &str) -> Mode {
match dconf_rs::get_string(pattern) {
Ok(theme) => Mode::from(theme.to_lowercase().contains("dark")),
Err(_) => Mode::Light,
}
}

fn detect_kde(path: &str) -> Mode {
match Ini::load_from_file(path) {
Ok(cfg) => {
let section = match cfg.section(Some("Colors:Window")) {
Some(section) => section,
None => return Mode::Light,
};
let values = match section.get("BackgroundNormal") {
Some(string) => string,
None => return Mode::Light,
};
let rgb = values
.split(',')
.map(|s| s.parse::<u32>().unwrap_or(255))
.collect::<Vec<u32>>();
let rgb = if rgb.len() > 2 {
rgb
} else {
vec![255, 255, 255]
};
let (r, g, b) = (rgb[0], rgb[1], rgb[2]);
Mode::rgb(r, g, b)
}
Err(e) => {
eprintln!("{:?}", e);
Mode::Light
}
}
}

pub fn detect() -> Mode {
match get_freedesktop_color_scheme() {
Some(mode) => mode,
// Other desktop environments are still being worked on, fow now, only the following implementations work.
None => match DesktopEnvironment::detect() {
DesktopEnvironment::Kde => {
let path = if Path::new(XDG_KDEGLOBALS).exists() {
PathBuf::from(XDG_KDEGLOBALS)
} else {
dirs::home_dir().unwrap().join(".config/kdeglobals")
};
detect_kde(path.to_str().unwrap())
}
DesktopEnvironment::Cinnamon => detect_gtk("/org/cinnamon/desktop/interface/gtk-theme"),
DesktopEnvironment::Gnome => detect_gtk("/org/gnome/desktop/interface/gtk-theme"),
DesktopEnvironment::Mate => detect_gtk("/org/mate/desktop/interface/gtk-theme"),
DesktopEnvironment::Unity => detect_gtk("/org/gnome/desktop/interface/gtk-theme"),
_ => Mode::Light,
},
}
}
57 changes: 49 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
//! # Examples
//!
//! ```
//! fn main() {
//! let mode = dark_light::detect();
//!let mode = dark_light::detect();
//!
//! match mode {
//! dark_light::Mode::Dark => {},
//! dark_light::Mode::Light => {},
//! }
//! }
//!match mode {
//! dark_light::Mode::Dark => {},
//! dark_light::Mode::Light => {},
//!}
edfloreshz marked this conversation as resolved.
Show resolved Hide resolved
//! ```

#[cfg(target_os = "macos")]
Expand All @@ -23,7 +21,32 @@ mod windows;
#[cfg(target_os = "windows")]
use windows as platform;

#[cfg(not(any(target_os = "macos", target_os = "windows")))]
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd"
))]
mod freedesktop;
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd"
))]
use freedesktop as platform;

#[cfg(not(any(
target_os = "macos",
target_os = "windows",
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd"
)))]
mod platform {
pub fn detect() -> crate::Mode {
Mode::Light
Expand All @@ -36,6 +59,24 @@ pub enum Mode {
Light,
}

impl Mode {
fn from(b: bool) -> Self {
if b {
Mode::Dark
} else {
Mode::Light
}
}
fn rgb(r: u32, g: u32, b: u32) -> Self {
let window_background_gray = (r * 11 + g * 16 + b * 5) / 32;
if window_background_gray < 192 {
Self::Dark
} else {
Self::Light
}
}
}

/// Detect if light mode or dark mode is enabled. If the mode can’t be detected, fall back to [`Mode::Light`].
pub fn detect() -> Mode {
platform::detect()
Expand Down
15 changes: 4 additions & 11 deletions src/macos.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
use crate::Mode;
use std::process;

fn is_dark_mode_enabled() -> bool {
pub fn detect() -> Mode {
if let Ok(output) = process::Command::new("defaults")
.arg("read")
.arg("-g")
.arg("AppleInterfaceStyle")
.output()
{
output.stdout.starts_with(b"Dark")
Mode::from(output.stdout.starts_with(b"Dark"))
} else {
false
}
}

pub fn detect() -> crate::Mode {
if is_dark_mode_enabled() {
crate::Mode::Dark
} else {
crate::Mode::Light
Mode::Light
}
}
17 changes: 5 additions & 12 deletions src/windows.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
use crate::Mode;
use winreg::RegKey;

fn is_dark_mode_enabled() -> bool {
pub fn detect() -> Mode {
let hkcu = RegKey::predef(winreg::enums::HKEY_CURRENT_USER);
if let Ok(subkey) =
hkcu.open_subkey("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize")
{
if let Ok(dword) = subkey.get_value::<u32, _>("AppsUseLightTheme") {
(dword == 0)
Mode::from(dword == 0)
} else {
false
Mode::Light
}
} else {
false
}
}

pub fn detect() -> crate::Mode {
if is_dark_mode_enabled() {
crate::Mode::Dark
} else {
crate::Mode::Light
Mode::Light
}
}