Skip to content

Commit 6152438

Browse files
committed
Some refactoring (move stuff away from main.rs)
1 parent d08757a commit 6152438

File tree

5 files changed

+161
-122
lines changed

5 files changed

+161
-122
lines changed

backlightd/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ edition = "2021"
77
ddc-hi = "0.4.1"
88
backlight_ipc = { path = "../backlight_ipc" }
99
anyhow = "1.0.93"
10+
chrono = "0.4.38"

backlightd/src/acpi.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{fs, path::PathBuf};
22

33
use anyhow::bail;
44

5-
use crate::BacklightDevice;
5+
use crate::monitors::BacklightDevice;
66

77
pub(crate) const ACPI_DEVICES_PATH: &str = "/sys/class/backlight";
88

backlightd/src/ddc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::error::Error;
33
use anyhow::bail;
44
use ddc_hi::{Ddc, Display, FeatureCode};
55

6-
use crate::BacklightDevice;
6+
use crate::monitors::BacklightDevice;
77

88
const VCP_FEATURE_BRIGHTNESS: FeatureCode = 0x10;
99

backlightd/src/main.rs

Lines changed: 31 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,43 @@
11
use std::{
22
env,
3-
fs::{self, remove_file},
3+
fs::remove_file,
44
io,
55
os::unix::net::UnixListener,
66
process::exit,
7-
sync::{
8-
mpsc::{channel, Receiver, RecvTimeoutError},
9-
Mutex,
10-
},
7+
sync::mpsc::{channel, Receiver, Sender},
118
thread,
12-
time::Duration,
139
};
1410

15-
use acpi::{BacklightAcpiDevice, ACPI_DEVICES_PATH};
16-
use backlight_ipc::{BacklightCommand, DEFAULT_UNIX_SOCKET_PATH};
17-
use ddc::BacklightDdcDevice;
11+
use backlight_ipc::{BacklightCommand, BacklightMode, DEFAULT_UNIX_SOCKET_PATH};
12+
use monitors::auto_refresh_monitors_list;
1813

1914
mod acpi;
2015
mod ddc;
16+
mod monitors;
2117

22-
/// Holds the current backlight state of all monitors.
23-
///
24-
/// Querying/writing to backlight devices is expensive, we try to cache as many things as possible
25-
/// in order to avoid unnecessary I/O.
26-
static MONITORS: Mutex<Vec<Box<dyn BacklightDevice + Send>>> = Mutex::new(Vec::new());
27-
const MONITORS_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
28-
29-
trait BacklightDevice {
30-
fn name(&self) -> String;
31-
fn set_brightness(&mut self, percent: u8) -> anyhow::Result<()>;
32-
fn get_brightness(&self) -> u8;
33-
}
34-
35-
fn refresh_monitors_list() {
36-
// Don't modify the global MONITORS variable directly to avoid locking the mutex for too long.
37-
let mut new_monitors: Vec<Box<dyn BacklightDevice + Send>> = Vec::new();
38-
39-
for ddc_device in ddc_hi::Display::enumerate() {
40-
match BacklightDdcDevice::new(ddc_device) {
41-
Ok(monitor) => new_monitors.push(Box::new(monitor)),
42-
Err(err) => eprintln!("Failed to retrieve DDC backlight monitor: {err}"),
43-
}
44-
}
45-
46-
match fs::read_dir(ACPI_DEVICES_PATH) {
47-
Ok(dir) => {
48-
for entry in dir {
49-
match entry {
50-
Ok(file) => match BacklightAcpiDevice::new(file.path()) {
51-
Ok(monitor) => new_monitors.push(Box::new(monitor)),
52-
Err(err) => println!("Failed to retrieve ACPI backlight monitor: {err}"),
53-
},
54-
Err(err) => {
55-
eprintln!("Unable to read entry from {ACPI_DEVICES_PATH}: {err}");
56-
}
57-
}
58-
}
59-
}
60-
Err(err) => {
61-
eprintln!("{ACPI_DEVICES_PATH}: {err}");
62-
// fallthrough
63-
}
64-
}
65-
66-
let mut monitors = MONITORS.lock().unwrap();
67-
monitors.clear();
68-
monitors.extend(new_monitors);
69-
}
70-
71-
fn set_brightness_percent(percent: u8) -> anyhow::Result<()> {
72-
for monitor in MONITORS.lock().unwrap().iter_mut() {
73-
monitor.set_brightness(percent)?;
74-
}
75-
Ok(())
76-
}
77-
78-
fn increase_brightness_percent(percent: u8) -> anyhow::Result<()> {
79-
for monitor in MONITORS.lock().unwrap().iter_mut() {
80-
let mut new_brightness = monitor.get_brightness() + percent;
81-
82-
if new_brightness > 100 {
83-
new_brightness = 100;
84-
}
85-
86-
monitor.set_brightness(new_brightness)?;
87-
}
88-
Ok(())
89-
}
90-
91-
fn decrease_brightness_percent(percent: u8) -> anyhow::Result<()> {
92-
for monitor in MONITORS.lock().unwrap().iter_mut() {
93-
let mut new_brightness = monitor.get_brightness() as isize - percent as isize;
94-
95-
// Don't allow setting the brightness to 0 to prevent the monitor from being completely black.
96-
if new_brightness < 1 {
97-
new_brightness = 1;
98-
}
99-
100-
monitor.set_brightness(new_brightness as u8)?;
101-
}
102-
Ok(())
103-
}
104-
105-
fn handle_commands(cmd_receiver: Receiver<BacklightCommand>) {
18+
fn handle_commands(
19+
cmd_receiver: Receiver<BacklightCommand>,
20+
) -> ! {
10621
loop {
107-
refresh_monitors_list();
108-
109-
loop {
110-
match cmd_receiver.recv_timeout(MONITORS_REFRESH_INTERVAL) {
111-
Ok(command) => {
112-
let result = match command {
113-
BacklightCommand::SetBrightness(percent) => set_brightness_percent(percent),
114-
BacklightCommand::IncreaseBrightness(percent) => {
115-
increase_brightness_percent(percent)
116-
}
117-
BacklightCommand::DecreaseBrightness(percent) => {
118-
decrease_brightness_percent(percent)
119-
}
120-
BacklightCommand::Refresh => {
121-
refresh_monitors_list();
122-
Ok(())
123-
}
124-
};
125-
126-
if let Err(err) = result {
127-
eprintln!("Command handling failed: {err}");
128-
}
129-
}
130-
Err(err) => match err {
131-
RecvTimeoutError::Timeout => break,
132-
RecvTimeoutError::Disconnected => panic!("channel disconnected"),
133-
},
22+
let command = cmd_receiver
23+
.recv()
24+
.expect("Failed to receive command from cmd channel");
25+
let result = match command {
26+
BacklightCommand::SetBrightness(percent) => monitors::set_brightness_percent(percent),
27+
BacklightCommand::IncreaseBrightness(percent) => {
28+
monitors::increase_brightness_percent(percent)
29+
}
30+
BacklightCommand::DecreaseBrightness(percent) => {
31+
monitors::decrease_brightness_percent(percent)
32+
}
33+
BacklightCommand::Refresh => {
34+
monitors::refresh_monitors_list();
35+
Ok(())
13436
}
37+
};
38+
39+
if let Err(err) = result {
40+
eprintln!("Command handling failed: {err}");
13541
}
13642
}
13743
}
@@ -171,8 +77,13 @@ fn main() {
17177
let (cmd_sender, cmd_receiver) = channel();
17278

17379
let command_handler_thread = thread::spawn(move || handle_commands(cmd_receiver));
80+
let auto_refresh_monitors_thread = thread::spawn(move || auto_refresh_monitors_list());
17481

17582
for stream in listener.incoming() {
83+
if auto_refresh_monitors_thread.is_finished() {
84+
panic!("auto refresh monitors thread is gone");
85+
}
86+
17687
if command_handler_thread.is_finished() {
17788
panic!("command handler thread is gone");
17889
}

backlightd/src/monitors.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use std::{
2+
fs::{self},
3+
sync::Mutex,
4+
thread,
5+
time::Duration,
6+
};
7+
8+
use chrono::{DateTime, Local};
9+
10+
use crate::acpi::{BacklightAcpiDevice, ACPI_DEVICES_PATH};
11+
use crate::ddc::BacklightDdcDevice;
12+
13+
/// Holds the current backlight state of all monitors.
14+
///
15+
/// Querying/writing to backlight devices is expensive, we try to cache as many things as possible
16+
/// in order to avoid unnecessary I/O.
17+
static MONITORS: Mutex<Vec<Box<dyn BacklightDevice + Send>>> = Mutex::new(Vec::new());
18+
19+
/// The last time the list of known monitors was refreshed.
20+
static LAST_REFRESH: Mutex<Option<DateTime<Local>>> = Mutex::new(None);
21+
22+
/// The frequency at which the list of known monitors must be refreshed.
23+
const MONITORS_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
24+
25+
pub(crate) trait BacklightDevice {
26+
fn name(&self) -> String;
27+
fn set_brightness(&mut self, percent: u8) -> anyhow::Result<()>;
28+
fn get_brightness(&self) -> u8;
29+
}
30+
31+
pub(crate) fn auto_refresh_monitors_list() -> ! {
32+
loop {
33+
let must_refresh = {
34+
let last_refresh = LAST_REFRESH
35+
.lock()
36+
.expect("Unable to acquire LAST_REFRESH mutex");
37+
38+
last_refresh.is_none()
39+
|| last_refresh.is_some_and(|dt| {
40+
(Local::now() - dt)
41+
.to_std()
42+
.unwrap_or(MONITORS_REFRESH_INTERVAL + Duration::from_secs(1))
43+
> MONITORS_REFRESH_INTERVAL
44+
})
45+
};
46+
47+
if must_refresh {
48+
refresh_monitors_list();
49+
}
50+
51+
thread::sleep(Duration::from_secs(10));
52+
}
53+
}
54+
55+
pub(crate) fn refresh_monitors_list() {
56+
// Don't modify the global MONITORS variable directly to avoid locking the mutex for too long.
57+
let mut new_monitors: Vec<Box<dyn BacklightDevice + Send>> = Vec::new();
58+
59+
for ddc_device in ddc_hi::Display::enumerate() {
60+
match BacklightDdcDevice::new(ddc_device) {
61+
Ok(monitor) => new_monitors.push(Box::new(monitor)),
62+
Err(err) => eprintln!("Failed to retrieve DDC backlight monitor: {err}"),
63+
}
64+
}
65+
66+
match fs::read_dir(ACPI_DEVICES_PATH) {
67+
Ok(dir) => {
68+
for entry in dir {
69+
match entry {
70+
Ok(file) => match BacklightAcpiDevice::new(file.path()) {
71+
Ok(monitor) => new_monitors.push(Box::new(monitor)),
72+
Err(err) => println!("Failed to retrieve ACPI backlight monitor: {err}"),
73+
},
74+
Err(err) => {
75+
eprintln!("Unable to read entry from {ACPI_DEVICES_PATH}: {err}");
76+
}
77+
}
78+
}
79+
}
80+
Err(err) => {
81+
eprintln!("{ACPI_DEVICES_PATH}: {err}");
82+
// fallthrough
83+
}
84+
}
85+
86+
let mut monitors = MONITORS.lock().expect("Unable to acquire MONITORS mutex");
87+
monitors.clear();
88+
monitors.extend(new_monitors);
89+
90+
*LAST_REFRESH
91+
.lock()
92+
.expect("Unable to acquire LAST_REFRESH mutex") = Some(Local::now());
93+
}
94+
95+
pub(crate) fn set_brightness_percent(percent: u8) -> anyhow::Result<()> {
96+
for monitor in MONITORS.lock().unwrap().iter_mut() {
97+
monitor.set_brightness(percent)?;
98+
}
99+
Ok(())
100+
}
101+
102+
pub(crate) fn increase_brightness_percent(percent: u8) -> anyhow::Result<()> {
103+
for monitor in MONITORS.lock().unwrap().iter_mut() {
104+
let mut new_brightness = monitor.get_brightness() + percent;
105+
106+
if new_brightness > 100 {
107+
new_brightness = 100;
108+
}
109+
110+
monitor.set_brightness(new_brightness)?;
111+
}
112+
Ok(())
113+
}
114+
115+
pub(crate) fn decrease_brightness_percent(percent: u8) -> anyhow::Result<()> {
116+
for monitor in MONITORS.lock().unwrap().iter_mut() {
117+
let mut new_brightness = monitor.get_brightness() as isize - percent as isize;
118+
119+
// Don't allow setting the brightness to 0 to prevent the monitor from being completely black.
120+
if new_brightness < 1 {
121+
new_brightness = 1;
122+
}
123+
124+
monitor.set_brightness(new_brightness as u8)?;
125+
}
126+
Ok(())
127+
}

0 commit comments

Comments
 (0)