Add sysfs fallback for screen brightness

Some drivers/displays don't support the xbacklight stuff, so fall back
to sysfs if needed.  This usually will require some extra permissions
setup on the user's part.
This commit is contained in:
2022-09-06 14:07:33 -07:00
parent 1ace254163
commit 742ff7b92c
2 changed files with 107 additions and 35 deletions

View File

@ -1,5 +1,6 @@
use log::{debug, error, warn};
use std::{cmp, thread, time::Duration};
use nix::unistd::{access, AccessFlags};
use std::{cmp, thread, time::Duration, path::PathBuf, fs, io::Write};
use xcb::{x, randr, Xid};
use bscreensaver_util::{BSCREENSAVER_WM_CLASS, create_atom, destroy_cursor, destroy_pixmap, destroy_gc, destroy_window};
@ -7,8 +8,13 @@ use bscreensaver_util::{BSCREENSAVER_WM_CLASS, create_atom, destroy_cursor, dest
const BACKLIGHT_ATOM_NAME: &[u8] = b"Backlight";
const BACKLIGHT_FALLBACK_ATOM_NAME: &[u8] = b"BACKLIGHT";
enum BacklightLocation {
XBacklight(x::Atom),
Sysfs(PathBuf),
}
struct BacklightControl {
property: x::Atom,
location: BacklightLocation,
min_level: i32,
max_level: i32,
step: u32,
@ -361,32 +367,46 @@ impl<'a> Monitor<'a> {
}
fn get_current_brightness(&self, backlight_control: &BacklightControl) -> anyhow::Result<Option<u32>> {
let cookie = self.conn.send_request(&randr::GetOutputProperty {
output: self.properties.output,
property: backlight_control.property,
r#type: x::ATOM_INTEGER,
long_offset: 0,
long_length: 4,
delete: false,
pending: false,
});
let reply = self.conn.wait_for_reply(cookie)?;
let data = reply.data::<u32>();
if data.len() == 1 {
Ok(Some(data[0]))
} else {
Ok(None)
match &backlight_control.location {
BacklightLocation::XBacklight(property) => {
let cookie = self.conn.send_request(&randr::GetOutputProperty {
output: self.properties.output,
property: *property,
r#type: x::ATOM_INTEGER,
long_offset: 0,
long_length: 4,
delete: false,
pending: false,
});
let reply = self.conn.wait_for_reply(cookie)?;
let data = reply.data::<u32>();
if data.len() == 1 {
Ok(Some(data[0]))
} else {
Ok(None)
}
},
BacklightLocation::Sysfs(path) => Ok(Some(fs::read_to_string(path)?.trim().parse::<u32>()?)),
}
}
fn set_brightness(&self, backlight_control: &BacklightControl, level: i32) -> anyhow::Result<()> {
self.conn.send_and_check_request(&randr::ChangeOutputProperty {
output: self.properties.output,
property: backlight_control.property,
r#type: x::ATOM_INTEGER,
mode: x::PropMode::Replace,
data: &[level as u32],
})?;
match &backlight_control.location {
BacklightLocation::XBacklight(property) => {
self.conn.send_and_check_request(&randr::ChangeOutputProperty {
output: self.properties.output,
property: *property,
r#type: x::ATOM_INTEGER,
mode: x::PropMode::Replace,
data: &[level as u32],
})?
},
BacklightLocation::Sysfs(path) => {
let mut f = fs::OpenOptions::new().write(true).open(path)?;
let level_str = level.to_string();
f.write_all(level_str.as_bytes())?;
},
}
Ok(())
}
}
@ -513,28 +533,57 @@ fn create_blank_cursor(conn: &xcb::Connection, root: x::Window) -> xcb::Result<x
Ok(blank_cursor)
}
fn find_backlight_control(conn: &xcb::Connection, output: randr::Output) -> xcb::Result<Option<BacklightControl>> {
fn find_backlight_control(conn: &xcb::Connection, output: randr::Output) -> anyhow::Result<Option<BacklightControl>> {
fn calc_step(min: i32, max: i32) -> u32 {
cmp::min((max - min) as u32, cmp::max(10, (max - min) / 10) as u32)
}
for prop_name in [BACKLIGHT_ATOM_NAME, BACKLIGHT_FALLBACK_ATOM_NAME] {
let property = create_atom(conn, prop_name)?;
let cookie = conn.send_request(&randr::QueryOutputProperty {
output,
property,
});
let reply = conn.wait_for_reply(cookie)?;
let values = reply.valid_values();
if reply.range() && values.len() == 2 {
let min_level = values[0];
let max_level = values[1];
let range = max_level - min_level;
if range > 0 {
if let Ok(reply) = conn.wait_for_reply(cookie) {
let values = reply.valid_values();
if reply.range() && values.len() == 2 {
let min_level = values[0];
let max_level = values[1];
let range = max_level - min_level;
if range > 0 {
debug!("Found xbacklight control");
return Ok(Some(BacklightControl {
location: BacklightLocation::XBacklight(property),
min_level,
max_level,
step: calc_step(min_level, max_level),
}));
}
}
}
}
if let Ok(entries) = fs::read_dir("/sys/class/backlight") {
for entry in entries {
let entry = entry?;
let mut path = entry.path();
path.push("brightness");
if path.exists() && access(&path, AccessFlags::W_OK).is_ok() {
let mut max_path = entry.path();
max_path.push("max_brightness");
let max_level = fs::read_to_string(max_path)?.trim().parse::<i32>()?;
let mut cur_path = entry.path();
cur_path.push("brightness");
debug!("Found sysfs backlight control at {:?}", cur_path);
return Ok(Some(BacklightControl {
property,
min_level,
location: BacklightLocation::Sysfs(cur_path),
min_level: 0,
max_level,
step: cmp::min(range as u32, cmp::max(10, (max_level - min_level) / 10) as u32),
step: calc_step(0, max_level),
}));
}
}
}
Ok(None)
}