use std::{fmt, fs, path::Path};
#[cfg(not(feature = "dbus"))]
use std::{fs::OpenOptions, io::prelude::*};
use super::BACKLIGHT_DIR;
use crate::{
error::Error,
utils::{read_sys_backlight, SysBacklightInterface},
};
#[cfg(feature = "dbus")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "dbus")]
use zbus::Connection;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "dbus", derive(Serialize, Deserialize))]
pub struct MonitorDevice {
pub device: String,
pub bl_power: u32,
pub brightness: u32,
pub actual_brightness: u32,
pub max_brightness: u32,
pub bl_type: BackLightType,
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "dbus", derive(Serialize, Deserialize))]
pub enum BackLightType {
FirmWare,
PlatForm,
Raw,
}
impl From<&BackLightType> for &str {
fn from(val: &BackLightType) -> &'static str {
match val {
BackLightType::FirmWare => "Firmware",
BackLightType::PlatForm => "Platform",
BackLightType::Raw => "Raw",
}
}
}
impl From<BackLightType> for &str {
fn from(val: BackLightType) -> &'static str {
match val {
BackLightType::FirmWare => "Firmware",
BackLightType::PlatForm => "Platform",
BackLightType::Raw => "Raw",
}
}
}
impl From<&BackLightType> for String {
fn from(val: &BackLightType) -> String {
match val {
BackLightType::FirmWare => String::from("Firmware"),
BackLightType::PlatForm => String::from("Platform"),
BackLightType::Raw => String::from("Raw"),
}
}
}
impl From<BackLightType> for String {
fn from(val: BackLightType) -> String {
match val {
BackLightType::FirmWare => String::from("Firmware"),
BackLightType::PlatForm => String::from("Platform"),
BackLightType::Raw => String::from("Raw"),
}
}
}
impl fmt::Display for BackLightType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
BackLightType::FirmWare => write!(f, "Firmware"),
BackLightType::PlatForm => write!(f, "Platform"),
BackLightType::Raw => write!(f, "Raw"),
}
}
}
impl MonitorDevice {
pub fn get_monitor_device(device: String) -> Result<MonitorDevice, Error> {
if Path::new(format!("{}/{}", BACKLIGHT_DIR, &device).as_str()).is_dir() {
let bl_power =
read_sys_backlight(&device, SysBacklightInterface::Power)?.parse::<u32>()?;
let brightness =
read_sys_backlight(&device, SysBacklightInterface::Brightness)?.parse::<u32>()?;
let actual_brightness =
read_sys_backlight(&device, SysBacklightInterface::ActualBrightness)?
.parse::<u32>()?;
let max_brightness = read_sys_backlight(&device, SysBacklightInterface::MaxBrightness)?
.parse::<u32>()?;
let bl_type = match read_sys_backlight(&device, SysBacklightInterface::Type)?.as_str() {
"firmware" => BackLightType::FirmWare,
"platform" => BackLightType::PlatForm,
"raw" => BackLightType::Raw,
_ => unreachable!(),
};
Ok(MonitorDevice {
device,
bl_power,
brightness,
actual_brightness,
max_brightness,
bl_type,
})
} else {
Err(Error::InvalidDeviceName { device })
}
}
pub fn get_all_monitor_devices() -> Result<Vec<MonitorDevice>, Error> {
let mut monitors = Vec::with_capacity(1);
if Path::new(BACKLIGHT_DIR).is_dir() {
for device in fs::read_dir(BACKLIGHT_DIR)? {
let device = device?;
let device_name = device.file_name().into_string().unwrap();
match MonitorDevice::get_monitor_device(device_name) {
Ok(dev) => monitors.push(dev),
Err(e) => return Err(e),
}
}
}
Ok(monitors)
}
pub fn get_device_name(&self) -> &str {
&self.device
}
pub fn get_power(&self) -> u32 {
self.bl_power
}
pub fn get_brightness(&self) -> u32 {
self.brightness
}
pub fn get_actual_brightness(&self) -> u32 {
self.actual_brightness
}
pub fn get_max_brightness(&self) -> u32 {
self.max_brightness
}
pub fn get_type(&self) -> BackLightType {
self.bl_type
}
#[cfg(feature = "dbus")]
pub fn set_brightness(&self, level: u32) -> Result<(), Error> {
if level <= self.max_brightness {
let sd_bus = Connection::new_system().unwrap();
match sd_bus.call_method(
Some("org.freedesktop.login1"),
"/org/freedesktop/login1/session/auto",
Some("org.freedesktop.login1.Session"),
"SetBrightness",
&("backlight", &self.device, level),
) {
Ok(_) => Ok(()),
Err(e) => Err(Error::SetBrightnessDBusError(e)),
}
} else {
Err(Error::InvalidBrightnessLevel {
given: level,
max: self.max_brightness,
})
}
}
#[cfg(not(feature = "dbus"))]
pub fn set_brightness(&self, level: u32) -> Result<(), Error> {
if level <= self.max_brightness {
let mut brightness = OpenOptions::new()
.write(true)
.open(format!("{}/{}/brightness", BACKLIGHT_DIR, &self.device))?;
match brightness.write_all(level.to_string().as_bytes()) {
Ok(_) => Ok(()),
Err(e) => Err(Error::Io(e)),
}
} else {
Err(Error::InvalidBrightnessLevel {
given: level,
max: self.max_brightness,
})
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::format_monitor_device;
#[test]
fn get_all_monitor_devices() {
let monitors = MonitorDevice::get_all_monitor_devices().unwrap();
for monitor in monitors {
format_monitor_device(monitor)
}
}
#[test]
fn set_monitor() {
let mut monitors = MonitorDevice::get_all_monitor_devices().unwrap();
if !monitors.is_empty() {
let first_device = monitors.remove(0);
let device_name = first_device.get_device_name();
let starting_brightness = first_device.get_actual_brightness();
let max_brightness = first_device.get_max_brightness();
let new_brightness: u32 = if starting_brightness > (max_brightness / 2) {
0
} else {
max_brightness
};
let _set_brightness_1 =
MonitorDevice::set_brightness(&first_device.clone(), new_brightness);
let udpated_device =
MonitorDevice::get_monitor_device(String::from(device_name)).unwrap();
let updated_brightness = udpated_device.get_actual_brightness();
assert_ne!(starting_brightness, updated_brightness);
assert_eq!(updated_brightness, new_brightness);
let _set_brightness_2 =
MonitorDevice::set_brightness(&udpated_device, starting_brightness);
let final_device =
MonitorDevice::get_monitor_device(String::from(device_name)).unwrap();
let final_brightness = final_device.get_actual_brightness();
assert_ne!(final_brightness, updated_brightness);
assert_eq!(final_brightness, starting_brightness);
}
}
}