/* brightness.c - Because xbacklight doesn't work for me.
* Features: Cubic ease-in-out interpolation of brightness over time.
*
* Currently works with a single target device from /sys/class/backlight.
* Previously only worked on intel_backlight, has since been adapted to search
* for the proper controller.
*
* Ethan McTague <ethan@tague.me> - January 28, 2018
* Public domain - free to use/modify without any restrictions.
*
* gcc brightness.c -o brightness -lm
* sudo install -m a=rxs brightness /usr/local/bin
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <dirent.h>
#define TARGET_DIR "/sys/class/backlight/"
#define BUF_SIZE 500
// Cubic bezier (ctrl points c1, c2 + anchor points a1, a2 + time u).
float
cubic_bezier (float u, float c1, float c2, float a1, float a2)
{
return pow(u, 3) * (a2 + 3 * (c1 - c2) - a1) + 3 * pow(u, 2)
* (a1 - 2 * c1 + c2) + 3 * u * (c1 - a1) + a1;
}
// Ease-in-out bezier (range: 0 to 1).
float
ease_in_out (float u)
{
return cubic_bezier(u, .42, .58, 0, 1);
}
// Interpolate linearly between two values.
float
interpolate (float u, float a, float b) {
return a + u * (b - a);
}
int
main (int argc, char* argv[])
{
// Complain if no value provided.
if (argc != 2)
{
fprintf(stderr,
"Usage: %s [+/-][value]\n\n"
"Ethan McTague <ethan@tague.me> - January 28, 2018\n"
"Public domain - free to use/modify without any restrictions.\n",
argv[0]);
return 1;
}
// Read the backlights directory to find the appropriate backlight controller.
DIR* dir = opendir(TARGET_DIR);
if (dir == NULL)
{
fprintf(stderr, "Could not open %s for file listing.\n", TARGET_DIR);
return 2;
}
// Skip over '.' and '..'.
readdir(dir); readdir(dir);
// Complain if there aren't any backlights to control.
struct dirent *ent = readdir(dir);
if (ent == NULL || (ent->d_type != DT_DIR && ent->d_type != DT_LNK))
{
fprintf(stderr, "No subdirectories in %s.\n", TARGET_DIR);
return 3;
}
// Write the target paths.
char target_out[BUF_SIZE];
char target_max[BUF_SIZE];
snprintf(target_out, BUF_SIZE, TARGET_DIR "%s/brightness", ent->d_name);
snprintf(target_max, BUF_SIZE, TARGET_DIR "%s/max_brightness", ent->d_name);
closedir(dir);
// Get value from first argument.
int newval = atoi(argv[1]);
// Get original value for smooth and relative.
int orig;
FILE* cur = fopen(target_out, "r");
if (cur == NULL)
{
fprintf(stderr, "Could not open %s for reading.\n", target_out);
return 2;
}
fscanf(cur, "%d", &orig);
fclose(cur);
// Relative positioning if '+' or '-' prefix is used.
if (argv[1][0] == '+' || argv[1][0] == '-')
newval = orig + newval;
// Read maximum brightness.
int max;
FILE* maxin = fopen(target_max, "r");
if (maxin == NULL)
{
fprintf(stderr, "Could not open %s for reading.\n", target_max);
return 3;
}
fscanf(maxin, "%d", &max);
fclose(maxin);
// Clamp within valid range (and avoid 0 despite it being valid.)
if (newval < 1) newval = 1;
if (newval > max) newval = max;
// Repeatedly quickly adjust brightness over time for a nice smooth change.
for (float n = 0; n < 1; n += 0.01)
{
usleep(5000);
// Save to output file.
FILE* out = fopen(target_out, "w");
if (out == NULL)
{
fprintf(stderr, "Could not open %s for writing.\n", target_out);
return 4;
}
float ival = interpolate(ease_in_out(n), (float)orig, (float)newval);
fprintf(out, "%d", (int)ival);
fclose(out);
}
return 0;
}