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 | The settings component is a standalone GTK app that presents a settings | ||||||
| dialog, which reads from and writes to your configuration file. | 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 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 xcb::{x, randr, Xid}; | ||||||
|  |  | ||||||
| use bscreensaver_util::{BSCREENSAVER_WM_CLASS, create_atom, destroy_cursor, destroy_pixmap, destroy_gc, destroy_window}; | 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_ATOM_NAME: &[u8] = b"Backlight"; | ||||||
| const BACKLIGHT_FALLBACK_ATOM_NAME: &[u8] = b"BACKLIGHT"; | const BACKLIGHT_FALLBACK_ATOM_NAME: &[u8] = b"BACKLIGHT"; | ||||||
|  |  | ||||||
|  | enum BacklightLocation { | ||||||
|  |     XBacklight(x::Atom), | ||||||
|  |     Sysfs(PathBuf), | ||||||
|  | } | ||||||
|  |  | ||||||
| struct BacklightControl { | struct BacklightControl { | ||||||
|     property: x::Atom, |     location: BacklightLocation, | ||||||
|     min_level: i32, |     min_level: i32, | ||||||
|     max_level: i32, |     max_level: i32, | ||||||
|     step: u32, |     step: u32, | ||||||
| @@ -361,32 +367,46 @@ impl<'a> Monitor<'a> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn get_current_brightness(&self, backlight_control: &BacklightControl) -> anyhow::Result<Option<u32>> { |     fn get_current_brightness(&self, backlight_control: &BacklightControl) -> anyhow::Result<Option<u32>> { | ||||||
|         let cookie = self.conn.send_request(&randr::GetOutputProperty { |         match &backlight_control.location { | ||||||
|             output: self.properties.output, |             BacklightLocation::XBacklight(property) => { | ||||||
|             property: backlight_control.property, |                 let cookie = self.conn.send_request(&randr::GetOutputProperty { | ||||||
|             r#type: x::ATOM_INTEGER, |                     output: self.properties.output, | ||||||
|             long_offset: 0, |                     property: *property, | ||||||
|             long_length: 4, |                     r#type: x::ATOM_INTEGER, | ||||||
|             delete: false, |                     long_offset: 0, | ||||||
|             pending: false, |                     long_length: 4, | ||||||
|         }); |                     delete: false, | ||||||
|         let reply = self.conn.wait_for_reply(cookie)?; |                     pending: false, | ||||||
|         let data = reply.data::<u32>(); |                 }); | ||||||
|         if data.len() == 1 { |                 let reply = self.conn.wait_for_reply(cookie)?; | ||||||
|             Ok(Some(data[0])) |                 let data = reply.data::<u32>(); | ||||||
|         } else { |                 if data.len() == 1 { | ||||||
|             Ok(None) |                     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<()> { |     fn set_brightness(&self, backlight_control: &BacklightControl, level: i32) -> anyhow::Result<()> { | ||||||
|         self.conn.send_and_check_request(&randr::ChangeOutputProperty { |         match &backlight_control.location { | ||||||
|             output: self.properties.output, |             BacklightLocation::XBacklight(property) => { | ||||||
|             property: backlight_control.property, |                 self.conn.send_and_check_request(&randr::ChangeOutputProperty { | ||||||
|             r#type: x::ATOM_INTEGER, |                     output: self.properties.output, | ||||||
|             mode: x::PropMode::Replace, |                     property: *property, | ||||||
|             data: &[level as u32], |                     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(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -513,28 +533,57 @@ fn create_blank_cursor(conn: &xcb::Connection, root: x::Window) -> xcb::Result<x | |||||||
|     Ok(blank_cursor) |     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] { |     for prop_name in [BACKLIGHT_ATOM_NAME, BACKLIGHT_FALLBACK_ATOM_NAME] { | ||||||
|         let property = create_atom(conn, prop_name)?; |         let property = create_atom(conn, prop_name)?; | ||||||
|         let cookie = conn.send_request(&randr::QueryOutputProperty { |         let cookie = conn.send_request(&randr::QueryOutputProperty { | ||||||
|             output, |             output, | ||||||
|             property, |             property, | ||||||
|         }); |         }); | ||||||
|         let reply = conn.wait_for_reply(cookie)?; |         if let Ok(reply) = conn.wait_for_reply(cookie) { | ||||||
|         let values = reply.valid_values(); |             let values = reply.valid_values(); | ||||||
|         if reply.range() && values.len() == 2 { |             if reply.range() && values.len() == 2 { | ||||||
|             let min_level = values[0]; |                 let min_level = values[0]; | ||||||
|             let max_level = values[1]; |                 let max_level = values[1]; | ||||||
|             let range = max_level - min_level; |                 let range = max_level - min_level; | ||||||
|             if range > 0 { |                 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 { |                 return Ok(Some(BacklightControl { | ||||||
|                     property, |                     location: BacklightLocation::Sysfs(cur_path), | ||||||
|                     min_level, |                     min_level: 0, | ||||||
|                     max_level, |                     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) |     Ok(None) | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user