Skip to content

Commit 2de4b13

Browse files
committed
Add the ability to turn monitors on and off
1 parent 0b4f160 commit 2de4b13

File tree

6 files changed

+136
-2
lines changed

6 files changed

+136
-2
lines changed

backlight_ipc/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub enum BacklightCommand {
2323
SetBrightness(u8),
2424
IncreaseBrightness(u8),
2525
DecreaseBrightness(u8),
26+
TurnOffMonitors,
27+
TurnOnMonitors,
2628
Refresh,
2729
SetMode(BacklightMode),
2830
GetInfo,

backlightctl/src/main.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ struct BacklightctlCli {
1515
#[clap(short, long, default_value_t = false)]
1616
auto: bool,
1717

18+
/// Turn off monitors
19+
#[clap(long, default_value_t = false)]
20+
turn_off: bool,
21+
22+
/// Turn on monitors
23+
#[clap(long, default_value_t = false)]
24+
turn_on: bool,
25+
1826
/// Refresh the list of known monitors (called by the udev rule)
1927
#[clap(short, long, default_value_t = false)]
2028
refresh: bool,
@@ -40,6 +48,15 @@ fn main() {
4048
.exit();
4149
}
4250

51+
if cli.turn_on && cli.turn_off {
52+
BacklightctlCli::command()
53+
.error(
54+
ErrorKind::ArgumentConflict,
55+
"You cannot use both --turn-on and --turn-off",
56+
)
57+
.exit();
58+
}
59+
4360
let brightness_cmd = if let Some(brightness) = cli.brightness {
4461
if brightness.chars().last().is_some_and(|c| c != '%') {
4562
BacklightctlCli::command()
@@ -163,10 +180,20 @@ fn main() {
163180
eprintln!("{err}");
164181
exit(1);
165182
}
183+
} else if let Some(brightness_cmd) = brightness_cmd {
184+
if let Err(err) = brightness_cmd.serialize_into(&stream) {
185+
eprintln!("{err}");
186+
exit(1);
187+
}
166188
}
167189

168-
if let Some(brightness_cmd) = brightness_cmd {
169-
if let Err(err) = brightness_cmd.serialize_into(&stream) {
190+
if cli.turn_off {
191+
if let Err(err) = BacklightCommand::TurnOffMonitors.serialize_into(&stream) {
192+
eprintln!("{err}");
193+
exit(1);
194+
}
195+
} else if cli.turn_on {
196+
if let Err(err) = BacklightCommand::TurnOnMonitors.serialize_into(&stream) {
170197
eprintln!("{err}");
171198
exit(1);
172199
}

backlightd/src/acpi.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,12 @@ impl BacklightDevice for BacklightAcpiDevice {
6565
// It's ok to unwrap here, if there is no filename it means the developer did something wrong.
6666
self.path.file_name().unwrap().to_string_lossy().to_string()
6767
}
68+
69+
fn turn_off(&mut self) -> anyhow::Result<()> {
70+
todo!()
71+
}
72+
73+
fn turn_on(&mut self) -> anyhow::Result<()> {
74+
todo!()
75+
}
6876
}

backlightd/src/ddc.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/// The bible for DDC:
2+
/// https://milek7.pl/ddcbacklight/mccs.pdf
13
use std::error::Error;
24

35
use anyhow::bail;
@@ -6,6 +8,9 @@ use ddc_hi::{Ddc, Display, FeatureCode};
68
use crate::monitors::BacklightDevice;
79

810
const VCP_FEATURE_BRIGHTNESS: FeatureCode = 0x10;
11+
const VCP_FEATURE_POWER: FeatureCode = 0xD6;
12+
const VCP_VALUE_POWER_ON: u16 = 0x1;
13+
const VCP_VALUE_POWER_OFF: u16 = 0x4;
914

1015
pub(crate) struct BacklightDdcDevice {
1116
display: Display,
@@ -57,4 +62,28 @@ impl BacklightDevice for BacklightDdcDevice {
5762
.clone()
5863
.unwrap_or(String::from("Unknown"))
5964
}
65+
66+
fn turn_off(&mut self) -> anyhow::Result<()> {
67+
if let Err(err) = self
68+
.display
69+
.handle
70+
.set_vcp_feature(VCP_FEATURE_POWER, VCP_VALUE_POWER_OFF)
71+
{
72+
bail!("{}: {err}", self.name());
73+
}
74+
75+
Ok(())
76+
}
77+
78+
fn turn_on(&mut self) -> anyhow::Result<()> {
79+
if let Err(err) = self
80+
.display
81+
.handle
82+
.set_vcp_feature(VCP_FEATURE_POWER, VCP_VALUE_POWER_ON)
83+
{
84+
bail!("{}: {err}", self.name());
85+
}
86+
87+
Ok(())
88+
}
6089
}

backlightd/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ fn handle_client(
161161
Ok(())
162162
}
163163
BacklightCommand::NotifyShutdown => break,
164+
BacklightCommand::TurnOffMonitors => monitors::turn_off(),
165+
BacklightCommand::TurnOnMonitors => monitors::turn_on(),
164166
};
165167

166168
if let Err(err) = result {

backlightd/src/monitors.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ pub(crate) trait BacklightDevice {
2424
fn name(&self) -> String;
2525
fn set_brightness(&mut self, percent: u8) -> anyhow::Result<()>;
2626
fn get_brightness(&self) -> u8;
27+
fn turn_off(&mut self) -> anyhow::Result<()>;
28+
fn turn_on(&mut self) -> anyhow::Result<()>;
2729
}
2830

2931
pub(crate) fn auto_refresh_monitors_list() -> ! {
@@ -164,6 +166,70 @@ pub(crate) fn decrease_brightness_percent(percent: u8) -> anyhow::Result<()> {
164166
}
165167
}
166168

169+
pub(crate) fn turn_off() -> anyhow::Result<()> {
170+
let mut last_error = None;
171+
172+
for monitor in MONITORS.lock().unwrap().iter_mut() {
173+
if let Err(err) = monitor.turn_off() {
174+
log::error!("Unable to turn OFF monitor: {err}");
175+
last_error = Some(err);
176+
}
177+
}
178+
179+
if last_error.is_some() {
180+
log::info!("Trying to refresh monitors list to fix the error and retry...");
181+
refresh_monitors_list();
182+
183+
last_error = None;
184+
for monitor in MONITORS.lock().unwrap().iter_mut() {
185+
if let Err(err) = monitor.turn_off() {
186+
log::error!("Unable to turn OFF monitor: {err}");
187+
last_error = Some(err);
188+
}
189+
}
190+
191+
if let Some(err) = last_error {
192+
Err(err)
193+
} else {
194+
Ok(())
195+
}
196+
} else {
197+
Ok(())
198+
}
199+
}
200+
201+
pub(crate) fn turn_on() -> anyhow::Result<()> {
202+
let mut last_error = None;
203+
204+
for monitor in MONITORS.lock().unwrap().iter_mut() {
205+
if let Err(err) = monitor.turn_on() {
206+
log::error!("Unable to turn ON monitor: {err}");
207+
last_error = Some(err);
208+
}
209+
}
210+
211+
if last_error.is_some() {
212+
log::info!("Trying to refresh monitors list to fix the error and retry...");
213+
refresh_monitors_list();
214+
215+
last_error = None;
216+
for monitor in MONITORS.lock().unwrap().iter_mut() {
217+
if let Err(err) = monitor.turn_on() {
218+
log::error!("Unable to turn ON monitor: {err}");
219+
last_error = Some(err);
220+
}
221+
}
222+
223+
if let Some(err) = last_error {
224+
Err(err)
225+
} else {
226+
Ok(())
227+
}
228+
} else {
229+
Ok(())
230+
}
231+
}
232+
167233
pub(crate) fn get_average_brightness() -> u8 {
168234
let monitors = MONITORS.lock().unwrap();
169235
let mut sum: usize = 0;

0 commit comments

Comments
 (0)