brightness/lib.rs
1// Copyright (C) 2022 The brightness project authors. Distributed under the 0BSD license.
2
3//! # Overview
4//! - [📦 crates.io](https://crates.io/crates/brightness)
5//! - [📖 Documentation](https://docs.rs/brightness)
6//! - [âš– 0BSD license](https://spdx.org/licenses/0BSD.html)
7//!
8//! This crate provides definitions to get and set display brightness.
9//!
10//! Linux and Windows are supported.
11//!
12//! # Example
13//!
14//! ```rust
15//! # #[cfg(feature = "async")]
16//! # mod doctest {
17//! use brightness::Brightness;
18//! use futures::TryStreamExt;
19//!
20//! async fn show_brightness() -> Result<(), brightness::Error> {
21//! brightness::brightness_devices().try_for_each(|dev| async move {
22//! let name = dev.device_name().await?;
23//! let value = dev.get().await?;
24//! println!("Brightness of device {} is {}%", name, value);
25//! Ok(())
26//! }).await
27//! }
28//! # }
29//! ```
30//!
31//! # Linux
32//!
33//! This crate interacts with devices found at `/sys/class/backlight`. This means that the
34//! [ddcci-backlight](https://gitlab.com/ddcci-driver-linux/ddcci-driver-linux#ddcci-backlight-monitor-backlight-driver)
35//! kernel driver is required to control external displays (via DDC/CI).
36//!
37//! Setting brightness is attempted using D-Bus and logind, which requires
38//! [systemd 243 or newer](https://github.com/systemd/systemd/blob/877aa0bdcc2900712b02dac90856f181b93c4e40/NEWS#L262).
39//! If this fails because the method is not available, the desired brightness is written to
40//! `/sys/class/backlight/$DEVICE/brightness`, which requires permission (`udev` rules can help with
41//! that).
42//!
43//! # Contribute
44//!
45//! All contributions shall be licensed under the [0BSD license](https://spdx.org/licenses/0BSD.html).
46
47#![deny(warnings)]
48#![deny(missing_docs)]
49#![cfg_attr(doc_cfg, feature(doc_cfg))]
50
51use std::error::Error as StdError;
52use thiserror::Error;
53
54pub mod blocking;
55
56#[cfg(feature = "async")]
57#[cfg_attr(doc_cfg, doc(cfg(feature = "async")))]
58cfg_if::cfg_if! {
59 if #[cfg(target_os = "linux")] {
60 mod linux;
61 use self::linux as platform;
62 } else if #[cfg(windows)] {
63 pub mod windows;
64 use self::windows as platform;
65 } else {
66 compile_error!("unsupported platform");
67 }
68}
69
70#[cfg(feature = "async")]
71#[cfg_attr(doc_cfg, doc(cfg(feature = "async")))]
72mod r#async {
73 use super::{platform, Error};
74 use async_trait::async_trait;
75 use futures::{Stream, StreamExt};
76
77 /// Async interface to get and set brightness.
78 #[async_trait]
79 pub trait Brightness {
80 /// Returns the device name.
81 async fn device_name(&self) -> Result<String, Error>;
82
83 /// Returns the current brightness as a percentage.
84 async fn get(&self) -> Result<u32, Error>;
85
86 /// Sets the brightness as a percentage.
87 async fn set(&mut self, percentage: u32) -> Result<(), Error>;
88 }
89
90 /// Async brightness device.
91 #[derive(Debug)]
92 pub struct BrightnessDevice(pub(crate) platform::AsyncDeviceImpl);
93
94 #[async_trait]
95 impl Brightness for BrightnessDevice {
96 async fn device_name(&self) -> Result<String, Error> {
97 self.0.device_name().await
98 }
99
100 async fn get(&self) -> Result<u32, Error> {
101 self.0.get().await
102 }
103
104 async fn set(&mut self, percentage: u32) -> Result<(), Error> {
105 self.0.set(percentage).await
106 }
107 }
108
109 /// Returns all brightness devices on the running system.
110 pub fn brightness_devices() -> impl Stream<Item = Result<BrightnessDevice, Error>> {
111 platform::brightness_devices().map(|r| r.map(BrightnessDevice).map_err(Into::into))
112 }
113}
114
115#[cfg(feature = "async")]
116pub use r#async::{brightness_devices, Brightness, BrightnessDevice};
117
118/// Errors used in this API
119#[derive(Debug, Error)]
120#[non_exhaustive]
121pub enum Error {
122 /// Getting a list of brightness devices failed
123 #[error("Failed to list brightness devices")]
124 ListingDevicesFailed(#[source] Box<dyn StdError + Send + Sync>),
125
126 /// Getting device information failed
127 #[error("Failed to get brightness device {device} information")]
128 GettingDeviceInfoFailed {
129 /// Device name
130 device: String,
131 /// Cause
132 source: Box<dyn StdError + Send + Sync>,
133 },
134
135 /// Setting brightness failed
136 #[error("Setting brightness failed for device {device}")]
137 SettingBrightnessFailed {
138 /// Device name
139 device: String,
140 /// Cause
141 source: Box<dyn StdError + Send + Sync>,
142 },
143}