diff --git a/README.md b/README.md index e57632e..8bc2d1a 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,12 @@ $ busctl --user introspect rs.wl-gammarelay / rs.wl.gammarelay NAME TYPE SIGNATURE RESULT/VALUE FLAGS .ToggleInverted method - - - .UpdateBrightness method d - - +.UpdateGamma method d - - .UpdateTemperature method n - - .Brightness property d 1 emits-change writable +.Gamma property d 1 emits-change writable .Inverted property b false emits-change writable -.Temperature property q 4500 emits-change writable +.Temperature property q 6500 emits-change writable ``` ## Installation @@ -42,6 +44,12 @@ i3status-rust hueshift block has the builtin support for this backend since 0.21 "on-scroll-up": "busctl --user -- call rs.wl-gammarelay / rs.wl.gammarelay UpdateBrightness d +0.02", "on-scroll-down": "busctl --user -- call rs.wl-gammarelay / rs.wl.gammarelay UpdateBrightness d -0.02" } + "custom/wl-gammarelay-gamma": { + "format": "{}% γ", + "exec": "wl-gammarelay-rs watch {g}", + "on-scroll-up": "busctl --user -- call rs.wl-gammarelay / rs.wl.gammarelay UpdateGamma d +0.02", + "on-scroll-down": "busctl --user -- call rs.wl-gammarelay / rs.wl.gammarelay UpdateGamma d -0.02" + } ``` ## Watch for changes @@ -86,4 +94,13 @@ busctl --user call rs.wl-gammarelay / rs.wl.gammarelay UpdateBrightness d 0.1 # Decrease the brightness by `10%`: busctl --user -- call rs.wl-gammarelay / rs.wl.gammarelay UpdateBrightness d -0.1 + +# Set display gamma to `1.0`: +busctl --user set-property rs.wl-gammarelay / rs.wl.gammarelay Gamma d 1 + +# Increase gamma by `0.1`: +busctl --user call rs.wl-gammarelay / rs.wl.gammarelay UpdateGamma d 0.1 + +# Decrease gamma by `0.1`: +busctl --user -- call rs.wl-gammarelay / rs.wl.gammarelay UpdateGamma d -0.1 ``` diff --git a/src/color.rs b/src/color.rs index a17533b..d55da76 100644 --- a/src/color.rs +++ b/src/color.rs @@ -2,6 +2,7 @@ #[derive(Debug, Clone, Copy, PartialEq)] pub struct Color { pub temp: u16, + pub gamma: f64, pub brightness: f64, pub inverted: bool, } @@ -10,12 +11,24 @@ impl Default for Color { fn default() -> Self { Self { temp: 6500, + gamma: 1.0, brightness: 1.0, inverted: false, } } } +fn map_intensity(v: f64, white: f64, color: Color, v_max_gamma: f64) -> u16 { + // A gamma ramp is computed as f(x) = x^γ, for x ∈ [0,1]. + // Multiple gamma adjustments can reasonably be combined as + // f(x) = x^(γ₁γ₂) = (x^γ₁)^γ₂. + // Here, x^γ₁ ≡ (v * white) is the color-temperature-adjusted intensity, + // and γ₂ is the overall gamma correction. + // The factor v_max_gamma adjusts for the fact that generally v ∉ [0,1]: + // (v/v_max)^γ * v_max = v^γ * v_max^(1-γ) + ((v * white).powf(color.gamma) * v_max_gamma) as u16 +} + pub fn colorramp_fill(r: &mut [u16], g: &mut [u16], b: &mut [u16], ramp_size: usize, color: Color) { let color_i = ((color.temp as usize - 1000) / 100) * 3; let [white_r, white_g, white_b] = interpolate_color( @@ -24,17 +37,19 @@ pub fn colorramp_fill(r: &mut [u16], g: &mut [u16], b: &mut [u16], ramp_size: us &BLACKBODY_COLOR[(color_i + 3)..], ); - let step = u16::MAX as f64 * color.brightness / (ramp_size - 1) as f64; + let v_max = u16::MAX as f64 * color.brightness; + let v_max_gamma = v_max.powf(1.0 - color.gamma); + let step = v_max / (ramp_size - 1) as f64; for i in 0..ramp_size { let v = step * i as f64; if !color.inverted { - r[i] = (v * white_r) as u16; - g[i] = (v * white_g) as u16; - b[i] = (v * white_b) as u16; + r[i] = map_intensity(v, white_r, color, v_max_gamma); + g[i] = map_intensity(v, white_g, color, v_max_gamma); + b[i] = map_intensity(v, white_b, color, v_max_gamma); } else { - r[i] = u16::MAX - (v * white_r) as u16; - g[i] = u16::MAX - (v * white_g) as u16; - b[i] = u16::MAX - (v * white_b) as u16; + r[ramp_size - 1 - i] = map_intensity(v, white_r, color, v_max_gamma); + g[ramp_size - 1 - i] = map_intensity(v, white_g, color, v_max_gamma); + b[ramp_size - 1 - i] = map_intensity(v, white_b, color, v_max_gamma); } } } diff --git a/src/dbus_client.rs b/src/dbus_client.rs index c6af4d0..2258194 100644 --- a/src/dbus_client.rs +++ b/src/dbus_client.rs @@ -6,15 +6,20 @@ pub async fn watch_dbus(format: &str) -> Result<()> { let conn = zbus::ConnectionBuilder::session()?.build().await?; let proxy = BusInterfaceProxy::new(&conn).await?; let mut temperature = proxy.temperature().await?; + let mut gamma = proxy.gamma().await?; let mut brightness = proxy.brightness().await?; let mut t_stream = proxy.receive_temperature_changed().await; + let mut g_stream = proxy.receive_gamma_changed().await; let mut b_stream = proxy.receive_brightness_changed().await; loop { - print_formatted(format, temperature, brightness); + print_formatted(format, temperature, gamma, brightness); tokio::select! { Some(t) = t_stream.next() => { temperature = t.get().await?; } + Some(g) = g_stream.next() => { + gamma = g.get().await?; + } Some(b) = b_stream.next() => { brightness = b.get().await?; } @@ -22,11 +27,12 @@ pub async fn watch_dbus(format: &str) -> Result<()> { } } -fn print_formatted(format: &str, temperature: u16, brightness: f64) { +fn print_formatted(format: &str, temperature: u16, gamma: f64, brightness: f64) { println!( "{}", format .replace("{t}", &temperature.to_string()) + .replace("{g}", &format!("{:.2}", gamma)) .replace("{b}", &format!("{:.2}", brightness)) .replace("{bp}", &format!("{:.0}", brightness * 100.)) ); @@ -41,6 +47,9 @@ trait BusInterface { /// UpdateBrightness method fn update_brightness(&self, delta_brightness: f64) -> zbus::Result<()>; + /// UpdateGamma method + fn update_gamma(&self, delta_gamma: f64) -> zbus::Result<()>; + /// UpdateTemperature method fn update_temperature(&self, delta_temp: i16) -> zbus::Result<()>; @@ -50,6 +59,12 @@ trait BusInterface { #[dbus_proxy(property)] fn set_brightness(&self, value: f64) -> zbus::Result<()>; + /// Gamma property + #[dbus_proxy(property)] + fn gamma(&self) -> zbus::Result; + #[dbus_proxy(property)] + fn set_gamma(&self, value: f64) -> zbus::Result<()>; + /// Temperature property #[dbus_proxy(property)] fn temperature(&self) -> zbus::Result; diff --git a/src/dbus_server.rs b/src/dbus_server.rs index 336e7cc..d9e97dd 100644 --- a/src/dbus_server.rs +++ b/src/dbus_server.rs @@ -66,6 +66,35 @@ impl Server { Ok(()) } + #[dbus_interface(property)] + async fn gamma(&self) -> f64 { + self.color.gamma + } + + #[dbus_interface(property)] + async fn set_gamma(&mut self, gamma: f64) -> Result<(), zbus::fdo::Error> { + if gamma > 0.0 { + self.color.gamma = gamma; + self.send_color().await; + Ok(()) + } else { + Err(zbus::fdo::Error::InvalidArgs( + "gamma must be greater than zero".into(), + )) + } + } + + async fn update_gamma( + &mut self, + #[zbus(signal_context)] cx: zbus::SignalContext<'_>, + delta_gamma: f64, + ) -> Result<(), zbus::fdo::Error> { + self.color.gamma = (self.color.gamma + delta_gamma).max(0.0); + self.send_color().await; + self.gamma_changed(&cx).await?; + Ok(()) + } + #[dbus_interface(property)] async fn brightness(&self) -> f64 { self.color.brightness @@ -89,7 +118,7 @@ impl Server { #[zbus(signal_context)] cx: zbus::SignalContext<'_>, delta_brightness: f64, ) -> Result<(), zbus::fdo::Error> { - self.color.brightness = (self.color.brightness + delta_brightness).clamp(0.0, 1.0) as _; + self.color.brightness = (self.color.brightness + delta_brightness).clamp(0.0, 1.0); self.send_color().await; self.brightness_changed(&cx).await?; Ok(()) diff --git a/toggle-invert-display.sh b/toggle-invert-display.sh new file mode 100755 index 0000000..c922ee8 --- /dev/null +++ b/toggle-invert-display.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# The human eye is more sensitive to contrast at high than at low +# levels of intensity. Thus, contast at high levels of intensity is +# lost if the display is inverted. This script compensates for this by +# gamma correction. The value of γ=0.6 is not based on any theory; +# adjust it to your liking. + +dbus="rs.wl-gammarelay / rs.wl.gammarelay" + +if [ "$(busctl --user get-property $dbus Inverted)" = "b false" ]; then + busctl --user set-property $dbus Inverted b true + busctl --user set-property $dbus Gamma d 0.6 +else + busctl --user set-property $dbus Inverted b false + busctl --user set-property $dbus Gamma d 1.0 +fi