Compare commits
No commits in common. "c3" and "master" have entirely different histories.
6 changed files with 264 additions and 300 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,3 @@
|
||||||
ambientd
|
ambientd
|
||||||
!sv/**
|
!sv/**
|
||||||
.ccls-cache
|
.ccls-cache
|
||||||
.zed
|
|
||||||
|
|
|
||||||
5
Makefile
5
Makefile
|
|
@ -1,7 +1,8 @@
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -Wall -Wextra -pedantic -std=c99 -O2
|
||||||
PREFIX = /usr/local
|
PREFIX = /usr/local
|
||||||
|
|
||||||
ambientd: ambientd.c3
|
ambientd: ambientd.c
|
||||||
c3c compile -O1 -g0 --optsize=small --fp-math=fast --x86cpu=native ambientd.c3
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
cp ambientd ${PREFIX}/bin/
|
cp ambientd ${PREFIX}/bin/
|
||||||
|
|
|
||||||
10
TODO
Normal file
10
TODO
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[ ] Add a config file
|
||||||
|
[ ] Use a better filter than a simple median, maybe a kalman filter
|
||||||
|
[ ] Add a way to customise the brightness curve
|
||||||
|
Percieved brightness is not linear, on low light you need a slightly higher
|
||||||
|
brightness and on high light you can get away with a lower brightness
|
||||||
|
than the max, the goal is to find the right curve for the screen
|
||||||
|
[ ] Automatically detect backlight device, or at least make it configurable
|
||||||
|
[ ] Add signal handlers
|
||||||
|
[ ] SIGUSR1 to reset the user offset
|
||||||
|
[ ] Handle kill by closing all file descriptors
|
||||||
178
ambientd.c
Normal file
178
ambientd.c
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||||
|
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||||
|
#define ABS(x) ((x) < 0 ? -(x) : (x))
|
||||||
|
#define MEDIAN_SAMPLES 4
|
||||||
|
|
||||||
|
const char *LUMINANCE_DEV = "/sys/bus/iio/devices/iio:device0/in_illuminance_raw";
|
||||||
|
const char *MAX_BRIGHTNESS_DEV = "/sys/class/backlight/intel_backlight/max_brightness";
|
||||||
|
const char *BRIGHTNESS_DEV = "/sys/class/backlight/intel_backlight/brightness";
|
||||||
|
const char *ACTUAL_BRIGHTNESS_DEV = "/sys/class/backlight/intel_backlight/actual_brightness";
|
||||||
|
|
||||||
|
char STR_BUFFER[128] = {0};
|
||||||
|
|
||||||
|
// file pointer to the luminance device
|
||||||
|
FILE *luminance_fp = NULL;
|
||||||
|
|
||||||
|
// max luminance that the display can output
|
||||||
|
int max_luminance = 550;
|
||||||
|
|
||||||
|
// minimum display brightness in percent
|
||||||
|
float min_brightness_percent = 0.05;
|
||||||
|
|
||||||
|
// returns the luminance value in percentage between 0 and 1
|
||||||
|
int get_luminance_raw (void)
|
||||||
|
{
|
||||||
|
freopen(NULL, "r", luminance_fp);
|
||||||
|
const char *x = fgets(STR_BUFFER, sizeof(STR_BUFFER), luminance_fp);
|
||||||
|
if (x == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int luminance = strtol(STR_BUFFER, NULL, 10);
|
||||||
|
|
||||||
|
return luminance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read a string from a file and convert it to int, should only be used for
|
||||||
|
// positive numbers since negative numbers mean an error
|
||||||
|
int read_int(FILE *fp)
|
||||||
|
{
|
||||||
|
freopen(NULL, "r", fp);
|
||||||
|
const char *p = fgets(STR_BUFFER, sizeof(STR_BUFFER), fp);
|
||||||
|
if (p == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
long r = strtol(STR_BUFFER, NULL, 10);
|
||||||
|
if (r == LONG_MIN || r == LONG_MAX) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
float bezier(float t)
|
||||||
|
{
|
||||||
|
const float B = 0.66;
|
||||||
|
const float D = 0.28;
|
||||||
|
return 3*B*t+(3*D-6*B)*t*t+(1+3*B-3*D)*t*t*t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add some command line options like:
|
||||||
|
// -v: verbose/logging
|
||||||
|
// -c file; specify a config file
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
// open the luminance device
|
||||||
|
luminance_fp = fopen(LUMINANCE_DEV, "r");
|
||||||
|
if (luminance_fp == NULL) {
|
||||||
|
fprintf(stderr, "Could not open luminance device '%s': %s\n",
|
||||||
|
LUMINANCE_DEV, strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get max and min brightness
|
||||||
|
FILE *fp = fopen(MAX_BRIGHTNESS_DEV, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
fprintf(stderr, "Could not open max_brightness file '%s': %s\n",
|
||||||
|
MAX_BRIGHTNESS_DEV, strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int max_brightness = read_int(fp);
|
||||||
|
if (max_brightness < 0) {
|
||||||
|
fprintf(stderr, "Could not read max brightness value: %s\n", strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int min_brightness = max_brightness * min_brightness_percent;
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
|
||||||
|
// open brightness device
|
||||||
|
FILE *brightness_fp = fopen(BRIGHTNESS_DEV, "w");
|
||||||
|
if (brightness_fp == NULL) {
|
||||||
|
fprintf(stderr, "Could not open brightness device '%s': %s\n",
|
||||||
|
BRIGHTNESS_DEV, strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// open actual brightness device
|
||||||
|
FILE *actual_brightness_fp = fopen(ACTUAL_BRIGHTNESS_DEV, "r");
|
||||||
|
if (brightness_fp == NULL) {
|
||||||
|
fprintf(stderr, "Could not open actual brightness device '%s': %s\n",
|
||||||
|
ACTUAL_BRIGHTNESS_DEV, strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x = get_luminance_raw();
|
||||||
|
if (x < 0) {
|
||||||
|
fprintf(stderr, "Could not read luminance value: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
int luminance_samples[MEDIAN_SAMPLES] = {x, x, x, x};
|
||||||
|
int idx = 0;
|
||||||
|
int user_offset = 0;
|
||||||
|
int brightness_target = min_brightness;
|
||||||
|
int brightness_old = min_brightness;
|
||||||
|
int brightness_adjusted = min_brightness;
|
||||||
|
int brightness_delta = 0;
|
||||||
|
while (1) {
|
||||||
|
static float luminance;
|
||||||
|
static float luminance_percent;
|
||||||
|
|
||||||
|
x = get_luminance_raw();
|
||||||
|
if (x >= 0) luminance_samples[idx] = x;
|
||||||
|
idx = (idx + 1) % MEDIAN_SAMPLES;
|
||||||
|
|
||||||
|
luminance = 0;
|
||||||
|
for (int i = 0; i < MEDIAN_SAMPLES; i++) {
|
||||||
|
luminance += luminance_samples[i] / MEDIAN_SAMPLES;
|
||||||
|
}
|
||||||
|
luminance_percent = bezier(MIN(luminance/max_luminance, 1.0));
|
||||||
|
|
||||||
|
brightness_old = brightness_target;
|
||||||
|
brightness_target = MAX((int)(luminance_percent * max_brightness), min_brightness);
|
||||||
|
brightness_delta = ABS((float)(brightness_target - brightness_old)/brightness_old);
|
||||||
|
|
||||||
|
// if the brightness changes by more than 10% reduce the user offset, scaling
|
||||||
|
// with the delta, this accounts for large changes in brightness no longer
|
||||||
|
// corresponding to the adjustments that the user made to the brightness
|
||||||
|
if (brightness_delta > 0.1) {
|
||||||
|
user_offset = user_offset * MAX(MIN(1-(brightness_delta*1.2), 1), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
brightness_adjusted = MIN(MAX(brightness_target + user_offset, min_brightness), max_brightness);
|
||||||
|
// TODO: put this in write_int() function
|
||||||
|
x = snprintf(STR_BUFFER, sizeof(STR_BUFFER), "%d", brightness_adjusted);
|
||||||
|
if (x <= 0) {
|
||||||
|
fprintf(stderr, "Error in snprintf(): %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
if (fputs(STR_BUFFER, brightness_fp) == EOF) {
|
||||||
|
fprintf(stderr, "error writing brightness: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
rewind(brightness_fp);
|
||||||
|
|
||||||
|
sleep(3);
|
||||||
|
|
||||||
|
// read actual brightness to get the user offset
|
||||||
|
int actual_brightness = 0;
|
||||||
|
int off = 0;
|
||||||
|
actual_brightness = read_int(actual_brightness_fp);
|
||||||
|
if (actual_brightness < 0) {
|
||||||
|
fprintf(stderr, "error reading actual brightness: %s\n", strerror(errno));
|
||||||
|
actual_brightness = brightness_adjusted;
|
||||||
|
}
|
||||||
|
|
||||||
|
off = actual_brightness - brightness_target;
|
||||||
|
if (brightness_target != max_brightness || off < 0){
|
||||||
|
user_offset = off;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: option to turn off logging
|
||||||
|
printf("brightness target: %d; user offset: %d; brightness adjusted: %d\n", brightness_target, user_offset, brightness_adjusted);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
297
ambientd.c3
297
ambientd.c3
|
|
@ -1,297 +0,0 @@
|
||||||
module ambientd;
|
|
||||||
|
|
||||||
import std::io;
|
|
||||||
import std::io::path;
|
|
||||||
import std::os::env;
|
|
||||||
import std::os::posix;
|
|
||||||
import std::collections::list;
|
|
||||||
import std::collections::object;
|
|
||||||
import std::encoding::ini;
|
|
||||||
import std::thread;
|
|
||||||
import std::time;
|
|
||||||
import std::math;
|
|
||||||
import libc;
|
|
||||||
|
|
||||||
|
|
||||||
alias StrList = List{String};
|
|
||||||
|
|
||||||
|
|
||||||
const String DEF_LIGHT_SENSOR_PATH = "/sys/bus/iio/devices/iio:device0/in_illuminance_raw";
|
|
||||||
const String DEF_BACKLIGHT_PATH = "/sys/class/backlight/intel_backlight";
|
|
||||||
const float DEF_SCREEN_NIT = 400.0;
|
|
||||||
const float DEF_SENSOR_K = 0.98;
|
|
||||||
const int DEF_UPDATE_INTERVAL_MS = 200;
|
|
||||||
|
|
||||||
const String[*] CONFIG_PATHS = {
|
|
||||||
"$XDG_CONFIG_HOME/ambientd/ambientd.ini",
|
|
||||||
"$HOME/.config/ambientd/ambientd.ini",
|
|
||||||
"/etc/ambientd/ambientd.ini",
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
String light_sensor_path = DEF_LIGHT_SENSOR_PATH;
|
|
||||||
String backlight_path = DEF_BACKLIGHT_PATH;
|
|
||||||
float backlight_max_luminance = DEF_SCREEN_NIT;
|
|
||||||
float light_sensor_k = DEF_SENSOR_K;
|
|
||||||
int update_interval_ms = DEF_UPDATE_INTERVAL_MS;
|
|
||||||
|
|
||||||
|
|
||||||
struct LuminanceFilter {
|
|
||||||
float x; // estimate
|
|
||||||
float p; // error
|
|
||||||
float r; // mesurement noise
|
|
||||||
float q; // process noise
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn float LuminanceFilter.update(&self, int reading, float dt)
|
|
||||||
{
|
|
||||||
float x_1 = self.x;
|
|
||||||
float p_1 = self.p + self.q * dt;
|
|
||||||
|
|
||||||
float k = p_1 / (p_1 + self.r);
|
|
||||||
|
|
||||||
self.x = x_1 + k * ((float)reading - x_1);
|
|
||||||
self.p = (1.0 - k) * p_1;
|
|
||||||
|
|
||||||
return self.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn void LuminanceFilter.init(&self, int reading)
|
|
||||||
{
|
|
||||||
self.p = 0.0;
|
|
||||||
self.x = (float)reading;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: this should be configurable
|
|
||||||
LuminanceFilter filter = {.r = 5.0, .q = 0.1};
|
|
||||||
|
|
||||||
|
|
||||||
fn String? String.to_expanded_path(&str, Allocator allocator, String[] extra_env = {})
|
|
||||||
{
|
|
||||||
@pool() {
|
|
||||||
StrList l;
|
|
||||||
l.tinit();
|
|
||||||
Path str_path = str.to_tpath()!;
|
|
||||||
|
|
||||||
l.push(str_path.basename());
|
|
||||||
Path? p = str_path.parent();
|
|
||||||
while (true) {
|
|
||||||
Path? x;
|
|
||||||
if (try p) {
|
|
||||||
String n = p.basename();
|
|
||||||
l.push("/");
|
|
||||||
|
|
||||||
// env variable
|
|
||||||
if (n.starts_with("$")) {
|
|
||||||
String? e = env::tget_var(n[1..]);
|
|
||||||
if (catch e) {
|
|
||||||
// var is not in regular environment, check extra_env
|
|
||||||
bool in_extra = false;
|
|
||||||
foreach_r (j: extra_env) {
|
|
||||||
if (j.starts_with(n[1..])) {
|
|
||||||
l.push(j.strip_suffix(n[1..])[1..]);
|
|
||||||
in_extra = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!in_extra) {
|
|
||||||
return NOT_FOUND~;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
l.push(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
l.push(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
x = p.parent();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p = x;
|
|
||||||
}
|
|
||||||
l.reverse();
|
|
||||||
String path;
|
|
||||||
path = string::tjoin(l.array_view(), path);
|
|
||||||
|
|
||||||
return (String)path::new(allocator, path);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn void update_config()
|
|
||||||
{
|
|
||||||
bool config_found = false;
|
|
||||||
foreach (c: CONFIG_PATHS) {
|
|
||||||
String? p = c.to_expanded_path(tmem);
|
|
||||||
if (catch e = p) {
|
|
||||||
io::eprintfn("Error expanding path %s: %s", c, e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!file::exists(p)) {
|
|
||||||
io::printfn("%s not present", p);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read file
|
|
||||||
File f = file::open(p, "r")!!;
|
|
||||||
Object*? o = ini::load_temp(&f).get(ini::GLOBAL);
|
|
||||||
if (catch e = o) {
|
|
||||||
io::eprintfn("Error reading config file %s: %s", p, e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read backlight device folder
|
|
||||||
String? backlight = o.get_string("backlight");
|
|
||||||
if (catch e = backlight) {
|
|
||||||
io::eprintfn("Error reading backlight path from config: %s", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!file::is_dir(backlight)) {
|
|
||||||
io::eprintfn("Error setting the backlight path: path should be a directory");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
backlight_path = backlight;
|
|
||||||
|
|
||||||
// Read light sensor device path
|
|
||||||
String? sensor = o.get_string("sensor");
|
|
||||||
if (catch e = sensor) {
|
|
||||||
io::eprintfn("Error reading luminance sensor path from config: %s", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
light_sensor_path = sensor;
|
|
||||||
|
|
||||||
// Optional configurations
|
|
||||||
|
|
||||||
// Read the max screen brightness
|
|
||||||
if (try x = o.get_string("max_brightness").to_float()) {
|
|
||||||
backlight_max_luminance = (float)x;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the light sensor factor
|
|
||||||
if (try x = o.get_string("sensor_factor").to_float()) {
|
|
||||||
light_sensor_k = (float)x;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the light sensor factor
|
|
||||||
if (try x = o.get_string("update_interval").to_int()) {
|
|
||||||
update_interval_ms = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
config_found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config_found) {
|
|
||||||
io::eprintfn("Keping previous configuration");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
macro float @bezier(float t)
|
|
||||||
{
|
|
||||||
const float B = 0.66;
|
|
||||||
const float D = 0.28;
|
|
||||||
return 3*B*t+(3*D-6*B)*t*t+(1+3*B-3*D)*t*t*t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// https://docs.kernel.org/admin-guide/abi-stable-files.html#abi-file-stable-sysfs-class-backlight
|
|
||||||
macro int? @read_int_from_file(String path) => ((String)file::load_temp(path)[..^2]).to_int();
|
|
||||||
macro void @write_int_to_file(String path, int i) => (void)file::save(path, string::tformat("%d\n", i));
|
|
||||||
macro int? @get_luminance() => @read_int_from_file(light_sensor_path);
|
|
||||||
macro int? @get_max_backlight() => @read_int_from_file(backlight_path.tconcat("/max_brightness"));
|
|
||||||
macro int? @get_backlight() => @read_int_from_file(backlight_path.tconcat("/brightness"));
|
|
||||||
macro void @set_backlight(int v) => @write_int_to_file(backlight_path.tconcat("/brightness"), v);
|
|
||||||
macro bool? @is_screen_on() => @read_int_from_file(backlight_path.tconcat("/bl_power")) == 0;
|
|
||||||
|
|
||||||
macro @retry_every(#f, int ms = 100)
|
|
||||||
{
|
|
||||||
const sz TRIES = 10;
|
|
||||||
for (sz i = 0; i < TRIES; i++) {
|
|
||||||
if (try x = #f) return x;
|
|
||||||
thread::sleep_ms(ms);
|
|
||||||
}
|
|
||||||
io::eprintfn("ERROR: tried to execute '%s' but it failed too many times (%d)", $stringify(#f), TRIES);
|
|
||||||
std::os::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn void sigusr1_handler(CInt sig, void* act, void* oldact)
|
|
||||||
{
|
|
||||||
(void)sig;
|
|
||||||
(void)act;
|
|
||||||
(void)oldact;
|
|
||||||
update_config();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: add command line arguments, like -h, -v and -d
|
|
||||||
// TODO: daemonization
|
|
||||||
// TODO: override config file with arguments
|
|
||||||
fn void main(String[] args)
|
|
||||||
{
|
|
||||||
bool verbose = false;
|
|
||||||
foreach (a : args[1..]) {
|
|
||||||
switch (a) {
|
|
||||||
case "-v":
|
|
||||||
verbose = true;
|
|
||||||
break;
|
|
||||||
case "-h":
|
|
||||||
io::printfn("ambientd [-hv]");
|
|
||||||
std::os::exit(0);
|
|
||||||
default:
|
|
||||||
io::eprintfn("Unrecognized argument: '%s'", a);
|
|
||||||
std::os::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update_config();
|
|
||||||
|
|
||||||
posix::install_signal_handler(libc::SIGUSR1, &sigusr1_handler);
|
|
||||||
|
|
||||||
int _l = @get_luminance() ?? 0;
|
|
||||||
filter.init(_l);
|
|
||||||
float luminance_old = filter.update(_l, 0.0);
|
|
||||||
int brightness_set = @retry_every(@get_backlight());
|
|
||||||
int user_offset = 0;
|
|
||||||
int backlight_max = @get_max_backlight() ?? 0;
|
|
||||||
Time start = time::now();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (!@is_screen_on() ?? false) {
|
|
||||||
thread::sleep_ms(5000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Check user offset
|
|
||||||
user_offset = @retry_every(@get_backlight(), update_interval_ms/2) - brightness_set;
|
|
||||||
int _lx = @retry_every(@get_luminance(), update_interval_ms/2);
|
|
||||||
|
|
||||||
Time now = time::now();
|
|
||||||
Time dt = (Time)(now - start);
|
|
||||||
|
|
||||||
// Filtered luminance, depends heavily on the type of sensor, for the CF-SV7
|
|
||||||
// seems to be in lux
|
|
||||||
float luminance = filter.update(_lx, (float)dt.to_seconds()) * light_sensor_k;
|
|
||||||
// Percent of the luminance received compared to what the panel can output
|
|
||||||
float p = min((luminance/backlight_max_luminance), 1.0f);
|
|
||||||
|
|
||||||
// int new_brightness = (int)(@bezier(p)*backlight_max);
|
|
||||||
// The panel should output slightly more light than the ambient, moreover the
|
|
||||||
// relationship between a percent increase in the backlight power and the
|
|
||||||
// perceived luminance is non-linear, I think the inverse square law is correct
|
|
||||||
int new_brightness = (int)(math::pow(p*1.1f, 0.5f)*backlight_max);
|
|
||||||
brightness_set = new_brightness;
|
|
||||||
@set_backlight(max(brightness_set + user_offset, 0));
|
|
||||||
|
|
||||||
if (verbose) io::printfn("%f, %d, %d", luminance, brightness_set, user_offset);
|
|
||||||
|
|
||||||
start = now;
|
|
||||||
thread::sleep_ms(update_interval_ms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
73
ambientd.py
Executable file
73
ambientd.py
Executable file
|
|
@ -0,0 +1,73 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
# Simple daemon to change brightness based on luminance
|
||||||
|
# TODO: add a config
|
||||||
|
# TODO: add debug option
|
||||||
|
# TODO: add dry-run
|
||||||
|
# TODO automatically find the right brightness path and do not assume intel_backlight
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
# Open the luminance sensor device file
|
||||||
|
try:
|
||||||
|
luminance_file = open('/sys/bus/iio/devices/iio:device0/in_illuminance_raw', 'r')
|
||||||
|
except OSError as err:
|
||||||
|
print(err)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# max luminance of the screen - 100 (maybe)
|
||||||
|
max_luminance = 400
|
||||||
|
|
||||||
|
# get luminance value, on fail return the max
|
||||||
|
def get_luminance():
|
||||||
|
try:
|
||||||
|
l = int(luminance_file.read())
|
||||||
|
luminance_file.seek(0)
|
||||||
|
except:
|
||||||
|
l = max_luminance
|
||||||
|
return l
|
||||||
|
|
||||||
|
# Get the max brightness
|
||||||
|
max_brightness = 100
|
||||||
|
with open('/sys/class/backlight/intel_backlight/max_brightness', 'r') as f:
|
||||||
|
max_brightness = int(f.read())
|
||||||
|
min_brightness = int(max_brightness * 0.05)
|
||||||
|
|
||||||
|
# loop delay in seconds
|
||||||
|
delay = 3
|
||||||
|
|
||||||
|
lum = [get_luminance()]*4
|
||||||
|
user_offset = 0
|
||||||
|
brightness_target = min_brightness
|
||||||
|
brightness_old = min_brightness
|
||||||
|
while True:
|
||||||
|
lum.append(lum.pop(0))
|
||||||
|
lum[0] = get_luminance()
|
||||||
|
|
||||||
|
luminance = sum(lum)/len(lum)
|
||||||
|
luminance_percent = min(luminance/max_luminance, 1.0)
|
||||||
|
brightness_old = brightness_target
|
||||||
|
brightness_target = max(int(luminance_percent * max_brightness), min_brightness)
|
||||||
|
brightness_delta = abs((brightness_target - brightness_old)/brightness_old)
|
||||||
|
|
||||||
|
# if the brightness changes by more than 10% reduce the user offset, scaling
|
||||||
|
# with the delta, this accounts for large changes in brightness no longer
|
||||||
|
# corresponding to the adjustments that the user made to the brightness
|
||||||
|
if brightness_delta > 0.1:
|
||||||
|
user_offset = int(user_offset * max(min(1-(brightness_delta*1.2), 1), 0))
|
||||||
|
|
||||||
|
brightness_adjusted = min(max(brightness_target + user_offset, min_brightness), max_brightness)
|
||||||
|
b = open('/sys/class/backlight/intel_backlight/brightness', 'w')
|
||||||
|
b.write(str(brightness_adjusted))
|
||||||
|
b.close()
|
||||||
|
|
||||||
|
time.sleep(delay)
|
||||||
|
|
||||||
|
# if the value of the brightness changed then update the user offset
|
||||||
|
a = open('/sys/class/backlight/intel_backlight/actual_brightness')
|
||||||
|
x = int(a.read()) - brightness_target
|
||||||
|
a.close()
|
||||||
|
if brightness_target != max_brightness or x < 0:
|
||||||
|
user_offset = x
|
||||||
|
|
||||||
|
print("brightness target: ", brightness_target, "user offset: ", user_offset, "brightness adjusted: ", brightness_adjusted)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue