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

Make zbus Optional, Remove anyhow, Fix notify Example #40

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
18 changes: 10 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,18 @@ readme = "README.md"
build = "build.rs"

[dependencies]
futures = "0.3.30"
anyhow = "1.0.79"
futures = "0.3"

[dev-dependencies]
tokio = { version = "1.23.0", features = ["full"] }
tokio = { version = "1.40.0", features = ["full"] }

[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))'.dependencies]
detect-desktop-environment = "1.0.0"
detect-desktop-environment = "1.1.0"
dconf_rs = "0.3"
zbus = "3.0"
rust-ini = "0.20"
ashpd = "0.7.0"
xdg = "2.4.1"
zbus = { version = "4.4", optional = true }
ashpd = { version = "0.9", optional = true }
rust-ini = "0.21"
xdg = "2.5"

[target.'cfg(windows)'.dependencies]
winreg = "0.52.0"
Expand All @@ -32,3 +31,6 @@ objc = "0.2"

[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = { version = "0.3", features = ["MediaQueryList", "Window"] }

[features]
zbus = ["dep:zbus", "dep:ashpd"]
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ fn main() {
// Light mode
dark_light::Mode::Light => {},
// Unspecified
dark_light::Mode::Default => {},
dark_light::Mode::NoPreference => {},
}
}
```

On platforms which make use of xdg-desktop-portals, by default this crate uses the `dbus-send` and `dbus-monitor` commands to avoid heavy dependencies.
If you already depend on `zbus` or `ashpd`, you should enable the `zbus` feature.

## Example

```
Expand All @@ -35,5 +38,3 @@ Licensed under either of
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)

at your option.


7 changes: 5 additions & 2 deletions examples/notify.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::error::Error;

use futures::StreamExt;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
while let Some(mode) = dark_light::subscribe().await?.next().await {
async fn main() -> Result<(), Box<dyn Error>> {
let mut stream = dark_light::subscribe().await?;
while let Some(mode) = stream.next().await {
println!("System theme changed: {:?}", mode);
}

Expand Down
4 changes: 2 additions & 2 deletions src/freedesktop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ fn get_freedesktop_color_scheme() -> Option<Mode> {
if theme.is_err() {
return None;
}

match theme.unwrap() {
1 => Some(Mode::Dark),
2 => Some(Mode::Light),
Expand Down Expand Up @@ -86,7 +86,7 @@ pub fn detect() -> Mode {
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::Default,
_ => Mode::NoPreference,
},
}
}
18 changes: 14 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
//! // Light mode
//! dark_light::Mode::Light => {},
//! // Unspecified
//! dark_light::Mode::Default => {},
//! dark_light::Mode::NoPreference => {},
//! }
//! ```

mod platforms;

use platforms::platform;

mod utils;
Expand All @@ -29,14 +30,15 @@ mod utils;
use utils::rgb::Rgb;

/// Enum representing dark mode, light mode, or unspecified.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub enum Mode {
/// Dark mode
Dark,
/// Light mode
Light,
/// Unspecified
Default,
#[default]
NoPreference,
}

impl Mode {
Expand Down Expand Up @@ -65,9 +67,17 @@ impl Mode {
Self::Light
}
}

fn concrete(self) -> Option<Self> {
match self {
Mode::Dark => Some(Mode::Dark),
Mode::Light => Some(Mode::Light),
Mode::NoPreference => None,
}
}
}

/// Detect if light mode or dark mode is enabled. If the mode can’t be detected, fall back to [`Mode::Default`].
/// Detect if light mode or dark mode is enabled. If the mode can’t be detected, fall back to [`Mode::NoPreference`].
pub use platform::detect::detect;
/// Notifies the user if the system theme has been changed.
pub use platform::notify::subscribe;
42 changes: 35 additions & 7 deletions src/platforms/freedesktop/detect.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::process::Command;

use detect_desktop_environment::DesktopEnvironment;

use crate::Mode;
Expand All @@ -6,6 +8,8 @@ use super::{dconf_detect, kde_detect, CINNAMON, GNOME, MATE};

pub fn detect() -> Mode {
NonFreeDesktop::detect()
.concrete()
.unwrap_or_else(FreeDesktop::detect)
}

/// Detects the color scheme on a platform.
Expand All @@ -22,7 +26,34 @@ struct NonFreeDesktop;
/// Detects the color scheme on FreeDesktop platforms. It makes use of the DBus interface.
impl ColorScheme for FreeDesktop {
fn detect() -> Mode {
todo!()
let Ok(output) = Command::new("dbus-send")
.args([
"--print-reply=literal",
"--dest=org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Settings.Read",
"string:org.freedesktop.appearance",
"string:color-scheme",
])
.output()
.map(|output| output.stdout)
else {
return Mode::NoPreference;
};
const PREFIX: &[u8] = b"uint32 ";
if let Some(index) = output
.windows(PREFIX.len())
.position(|bytes| bytes == PREFIX)
{
match output.get(index + PREFIX.len()) {
Some(b'0') => Mode::NoPreference,
Some(b'1') => Mode::Dark,
Some(b'2') => Mode::Light,
_ => Mode::NoPreference,
}
} else {
Mode::NoPreference
}
}
}

Expand All @@ -31,17 +62,14 @@ impl ColorScheme for NonFreeDesktop {
fn detect() -> Mode {
match DesktopEnvironment::detect() {
Some(mode) => match mode {
DesktopEnvironment::Kde => match kde_detect() {
Ok(mode) => mode,
Err(_) => Mode::Default,
},
DesktopEnvironment::Kde => kde_detect(),
DesktopEnvironment::Cinnamon => dconf_detect(CINNAMON),
DesktopEnvironment::Gnome => dconf_detect(GNOME),
DesktopEnvironment::Mate => dconf_detect(MATE),
DesktopEnvironment::Unity => dconf_detect(GNOME),
_ => Mode::Default,
_ => Mode::NoPreference,
},
None => Mode::Default,
None => Mode::NoPreference,
}
}
}
31 changes: 14 additions & 17 deletions src/platforms/freedesktop/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::str::FromStr;

use anyhow::Context;
use ini::Ini;

use crate::{utils::rgb::Rgb, Mode};
Expand All @@ -21,30 +20,28 @@ fn dconf_detect(path: &str) -> Mode {
Mode::Light
}
}
Err(_) => Mode::Default,
Err(_) => Mode::NoPreference,
}
}

fn kde_detect() -> anyhow::Result<Mode> {
let xdg = xdg::BaseDirectories::new()?;
let path = xdg
.find_config_file("kdeglobals")
.context("Path not found")?;
let cfg = Ini::load_from_file(path)?;
let properties = cfg
.section(Some("Colors:Window"))
.context("Failed to get section Colors:Window")?;
let background = properties
.get("BackgroundNormal")
.context("Failed to get BackgroundNormal inside Colors:Window")?;
let rgb = Rgb::from_str(background).unwrap();
Ok(Mode::from_rgb(rgb))
fn kde_detect() -> Mode {
fn kde_detect() -> Option<Mode> {
let xdg = xdg::BaseDirectories::new().ok()?;
let path = xdg.find_config_file("kdeglobals")?;
let cfg = Ini::load_from_file(path).ok()?;
let properties = cfg.section(Some("Colors:Window"))?;
let background = properties.get("BackgroundNormal")?;
let rgb = Rgb::from_str(background).ok()?;
Some(Mode::from_rgb(rgb))
}
kde_detect().unwrap_or_default()
}

#[cfg(feature = "zbus")]
impl From<ashpd::desktop::settings::ColorScheme> for Mode {
fn from(value: ashpd::desktop::settings::ColorScheme) -> Self {
match value {
ashpd::desktop::settings::ColorScheme::NoPreference => Mode::Default,
ashpd::desktop::settings::ColorScheme::NoPreference => Mode::NoPreference,
ashpd::desktop::settings::ColorScheme::PreferDark => Mode::Dark,
ashpd::desktop::settings::ColorScheme::PreferLight => Mode::Light,
}
Expand Down
57 changes: 44 additions & 13 deletions src/platforms/freedesktop/notify.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,46 @@
use ashpd::desktop::settings::{ColorScheme, Settings};
use futures::{stream, Stream, StreamExt};
use std::task::Poll;
use crate::Mode;
use futures::Stream;
use std::error::Error;

use crate::{detect, Mode};
#[cfg(not(feature = "zbus"))]
pub async fn subscribe() -> Result<impl Stream<Item = Mode> + Send, Box<dyn Error>> {
use futures::stream;
use std::{
io::{BufRead, BufReader},
process::{Command, Stdio},
};
let mut process = Command::new("dbus-monitor")
.arg(
"type='signal',\
sender='org.freedesktop.portal.Desktop',\
path='/org/freedesktop/portal/desktop',\
interface='org.freedesktop.portal.Settings',\
member='SettingChanged',\
arg0='org.freedesktop.appearance',\
arg1='color-scheme'",
)
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()?;
let stdout = process.stdout.take().unwrap();
let lines = BufReader::new(stdout).lines();
Ok(stream::iter(lines.filter_map(
|line| match line.ok()?.chars().last()? {
'0' => Some(Mode::NoPreference),
'1' => Some(Mode::Dark),
'2' => Some(Mode::Light),
_ => None,
},
)))
}

pub async fn subscribe() -> anyhow::Result<impl Stream<Item = Mode> + Send> {
#[cfg(feature = "zbus")]
pub async fn subscribe() -> Result<impl Stream<Item = Mode> + Send, Box<dyn Error>> {
use crate::detect;
use futures::{stream, StreamExt};
use std::task::Poll;
let stream = if get_freedesktop_color_scheme().await.is_ok() {
let proxy = Settings::new().await?;
let proxy = ashpd::desktop::settings::Settings::new().await?;
proxy
.receive_color_scheme_changed()
.await?
Expand All @@ -30,13 +64,10 @@ pub async fn subscribe() -> anyhow::Result<impl Stream<Item = Mode> + Send> {
Ok(stream)
}

async fn get_freedesktop_color_scheme() -> anyhow::Result<Mode> {
let proxy = Settings::new().await?;
#[cfg(feature = "zbus")]
async fn get_freedesktop_color_scheme() -> Result<Mode, Box<dyn Error>> {
let proxy = ashpd::desktop::settings::Settings::new().await?;
let color_scheme = proxy.color_scheme().await?;
let mode = match color_scheme {
ColorScheme::PreferDark => Mode::Dark,
ColorScheme::PreferLight => Mode::Light,
ColorScheme::NoPreference => Mode::Default,
};
let mode = color_scheme.into();
Ok(mode)
}
3 changes: 2 additions & 1 deletion src/platforms/macos/notify.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::error::Error;
use std::task::Poll;

use futures::{stream, Stream};

use crate::{detect, Mode};

pub async fn subscribe() -> anyhow::Result<impl Stream<Item = Mode> + Send> {
pub async fn subscribe() -> Result<impl Stream<Item = Mode> + Send, Box<dyn Error>> {
let mut last_mode = detect();

let stream = stream::poll_fn(move |ctx| -> Poll<Option<Mode>> {
Expand Down
2 changes: 1 addition & 1 deletion src/platforms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ pub use websys as platform;
)))]
pub mod platform {
pub fn detect() -> crate::Mode {
super::Mode::Light
super::Mode::NoPreference
}
}
3 changes: 2 additions & 1 deletion src/platforms/websys/notify.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::error::Error;
use std::task::Poll;

use futures::{stream, Stream};

use crate::{detect, Mode};

pub async fn subscribe() -> anyhow::Result<impl Stream<Item = Mode> + Send> {
pub async fn subscribe() -> Result<impl Stream<Item = Mode> + Send, Box<dyn Error>> {
let mut last_mode = detect();

let stream = stream::poll_fn(move |ctx| -> Poll<Option<Mode>> {
Expand Down
3 changes: 2 additions & 1 deletion src/platforms/windows/notify.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::error::Error;
use std::task::Poll;

use futures::{stream, Stream};

use crate::{detect, Mode};

pub async fn subscribe() -> anyhow::Result<impl Stream<Item = Mode> + Send> {
pub async fn subscribe() -> Result<impl Stream<Item = Mode> + Send, Box<dyn Error>> {
let mut last_mode = detect();

let stream = stream::poll_fn(move |ctx| -> Poll<Option<Mode>> {
Expand Down
Loading
Loading