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:
		
							
								
								
									
										23
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								README.md
									
									
									
									
									
								
							@@ -146,3 +146,26 @@ resuming.
 | 
			
		||||
 | 
			
		||||
The settings component is a standalone GTK app that presents a settings
 | 
			
		||||
dialog, which reads from and writes to your configuration file.
 | 
			
		||||
 | 
			
		||||
#### Screen Brightness
 | 
			
		||||
 | 
			
		||||
If you enable the setting that allows `bscreensaver` to make your screen
 | 
			
		||||
brightness keys work while the screen is locked, this should hopefully
 | 
			
		||||
work without further intervention, if your display driver supports the
 | 
			
		||||
`XBACKLIGHT` protocol.  If not, `bscreensaver` will attempt to use the
 | 
			
		||||
backlight controls in sysfs, which probably will not work without extra
 | 
			
		||||
setup, as those controls are usually only accessible to root.  You can
 | 
			
		||||
give yourself (well, anyone in the `video` group) access by adding a
 | 
			
		||||
udev rules file, say at `/etc/udev/rules.d/90-backlight.rules`:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
SUBSYSTEM=="backlight", ACTION=="add", \
 | 
			
		||||
  RUN+="/bin/chgrp video /sys/class/backlight/%k/brightness", \
 | 
			
		||||
  RUN+="/bin/chmod g+w /sys/class/backlight/%k/brightness"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You may need to restart to get those settings applied, or you can just
 | 
			
		||||
run those commands yourself (replacing `%k` with whatever directories
 | 
			
		||||
happen to be located there).  Your user account will also need to be in
 | 
			
		||||
the `video` group; if it isn't, you'll probably need to log out and in
 | 
			
		||||
again before any changes take effect.
 | 
			
		||||
 
 | 
			
		||||
@@ -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,9 +367,11 @@ impl<'a> Monitor<'a> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_current_brightness(&self, backlight_control: &BacklightControl) -> anyhow::Result<Option<u32>> {
 | 
			
		||||
        match &backlight_control.location {
 | 
			
		||||
            BacklightLocation::XBacklight(property) => {
 | 
			
		||||
                let cookie = self.conn.send_request(&randr::GetOutputProperty {
 | 
			
		||||
                    output: self.properties.output,
 | 
			
		||||
            property: backlight_control.property,
 | 
			
		||||
                    property: *property,
 | 
			
		||||
                    r#type: x::ATOM_INTEGER,
 | 
			
		||||
                    long_offset: 0,
 | 
			
		||||
                    long_length: 4,
 | 
			
		||||
@@ -377,16 +385,28 @@ impl<'a> Monitor<'a> {
 | 
			
		||||
                } 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<()> {
 | 
			
		||||
        match &backlight_control.location {
 | 
			
		||||
            BacklightLocation::XBacklight(property) => {
 | 
			
		||||
                self.conn.send_and_check_request(&randr::ChangeOutputProperty {
 | 
			
		||||
                    output: self.properties.output,
 | 
			
		||||
            property: backlight_control.property,
 | 
			
		||||
                    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)?;
 | 
			
		||||
        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 {
 | 
			
		||||
                    property,
 | 
			
		||||
                        location: BacklightLocation::XBacklight(property),
 | 
			
		||||
                        min_level,
 | 
			
		||||
                        max_level,
 | 
			
		||||
                    step: cmp::min(range as u32, cmp::max(10, (max_level - min_level) / 10) as u32),
 | 
			
		||||
                        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 {
 | 
			
		||||
                    location: BacklightLocation::Sysfs(cur_path),
 | 
			
		||||
                    min_level: 0,
 | 
			
		||||
                    max_level,
 | 
			
		||||
                    step: calc_step(0, max_level),
 | 
			
		||||
                }));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(None)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user