/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "arg.h" #ifndef BACKLIGHT_DIR # define BACKLIGHT_DIR "/sys/class/backlight" #endif char *argv0; static double brightness = 0; static int nbrightness = 0; static int cols = 80; static char *space; static char *line; static void usage(void) { fprintf(stderr, "usage: %s [-g | -s [+|-]level[%%|%%%%]] [-a | device ...]\n", argv0); exit(1); } static long int readfile(const char *path) { int fd; char buf[64]; ssize_t i, n; long int value = 0; fd = open(path, O_RDONLY); if (fd < 0) return -1; while ((n = read(fd, buf, sizeof(buf))) > 0) { for (i = 0; i < n; i++) { if (isdigit(buf[i])) value = value * 10 + (buf[i] - '0'); else if (buf[i] != '\n') goto out; } } out: close(fd); return n ? -1 : value; } static double getbrightness(const char *device, long int *out_cur, long int *out_max) { size_t n = sizeof("//max_brightness") - 1; char *path; long int max, cur; n += strlen(BACKLIGHT_DIR); n += strlen(device); path = alloca(n + 1); if (sprintf(path, "%s/%s/max_brightness", BACKLIGHT_DIR, device) != (int)n) abort(); max = readfile(path); if (max < 1) return -1; if (out_max) *out_max = max; if (sprintf(path, "%s/%s/brightness", BACKLIGHT_DIR, device) != (int)n - 4) abort(); cur = readfile(path); if (cur < 0) return -1; if (out_cur) *out_cur = cur; return (double)cur / (double)max; } static int setbrightness(const char *device, long int value) { size_t p = 0, n = sizeof("//brightness") - 1; char *path, buf[128]; ssize_t r = 0; int fd; n += strlen(BACKLIGHT_DIR); n += strlen(device); path = alloca(n + 1); if (sprintf(path, "%s/%s/brightness", BACKLIGHT_DIR, device) != (int)n) abort(); if (sprintf(buf, "%li\n", value) < 0) abort(); fd = open(path, O_WRONLY); if (fd < 0) return -1; for (n = strlen(buf); p < n; p += (size_t)r) if ((r = write(fd, &buf[p], n - p)) < 0) break; close(fd); return r < 0 ? -1 : 0; } static int adjbrightness(const char *device, double pcur, long int cur, long int max, double adj, int inc, const char *suf) { switch (strlen(suf)) { case 2: pcur = adj * pcur / 100 + pcur * inc; cur = (long int)(pcur * (double)max + 0.5); break; case 1: pcur = adj / 100 + pcur * inc; cur = (long int)(pcur * (double)max + 0.5); break; case 0: cur = (long int)adj + cur * inc; break; default: abort(); } cur = cur < 0 ? 0 : cur < max ? cur : max; return setbrightness(device, cur); } static void bars(long int max, long int init, long int cur) { long int mid = (long int)((double)cur * (double)(cols - 2) / (double)max + 0.5); printf("\033[%iD\033[6A", cols); printf("\033[2K┌%s┐\n", line); space[mid] = '\0'; printf("\033[2K│\033[47m%s\033[49m%s│\n", space, &space[mid + 1]); space[mid] = ' '; printf("\033[2K└%s┘\n", line); printf("\033[2KMaximum brightness: %li\n", max); printf("\033[2KInitial brightness: %li\n", init); printf("\033[2KCurrent brightness: %li\n", cur); fflush(stdout); } static void interactive(const char *device, long int cur, long int max) { long int step, init; int c; step = max / 200; step = step ? step : 1; init = cur; printf("\n\n\n\n\n\n"); bars(max, init, cur); while ((c = getchar()) != -1) { switch (c) { case 'q': case '\n': case 4: printf("\n"); return; case 'A': case 'C': cur += step << 1; /* fall through */ case 'B': case 'D': cur -= step; cur = cur < 0 ? 0 : cur < max ? cur : max; adjbrightness(device, 0, 0, max, (double)cur, 0, ""); bars(max, init, cur); } } } static void handle_device(const char *device, int get, int set, double adj, int inc, const char *suf) { double value; long int cur, max; value = getbrightness(device, &cur, &max); if (get) { if (value >= 0) { brightness += value; nbrightness++; } } else if (set) { adjbrightness(device, value, cur, max, adj, inc, suf); } else { interactive(device, cur, max); } } int main(int argc, char *argv[]) { int all = 0; int get = 0; char* set = NULL; char set_prefix = '='; double set_value = 0; char* set_suffix = NULL; struct winsize win; struct termios saved_stty; struct termios stty; pid_t pid = -1; DIR *dir; struct dirent *ent; int any = 0; ARGBEGIN { case 'a': all = 1; break; case 'g': get = 1; break; case 's': set = EARGF(usage()); break; default: usage(); } ARGEND; if ((get && set) || (argc && all)) usage(); /* Parse -s argument */ if (set) { if (*set == '+' || *set == '-') { set_prefix = *set; set++; } if (!isdigit(*set) && *set != '.') usage(); errno = 0; set_value = strtod(set, &set_suffix); if (errno || set_value < 0) { fprintf(stderr, "%s: strtod %s: %s\n", argv0, set, strerror(errno)); return 1; } if (*set_suffix && strcmp(set_suffix, "%") && strcmp(set_suffix, "%%")) usage(); if (set_prefix == '-') { set_value = -set_value; set_prefix = '+'; } } /* Check permissions (important because the program is installed with set-uid) */ if (getuid()) { struct group *videogrp = getgrnam("video"); if (!videogrp && errno && errno != ENOENT && errno != ESRCH && errno != EPERM) { /* Note, glibc sets errno to EIO if the group does not exist, * this is the not the specified behavour by either POSIX or * glibc, and it would be a security issue to treat it as OK. * Additionally, EBADF is not treated as OK. */ fprintf(stderr, "%s: getgrnam video: %s\n", argv0, strerror(errno)); return 1; } else if (videogrp) { long ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1; gid_t *groups; int ngroups; if (ngroups_max < 0 || ngroups_max > INT_MAX - 1) { fprintf(stderr, "%s: sysconf _SC_NGROUPS_MAX: %s\n", argv0, strerror(errno)); return 1; } groups = alloca((size_t)ngroups_max * sizeof(*groups)); ngroups = getgroups((int)ngroups_max, groups); if (ngroups < 0) { fprintf(stderr, "%s: getgroups: %s\n", argv0, strerror(errno)); return 1; } while (ngroups--) if (groups[ngroups] == videogrp->gr_gid) break; if (ngroups < 0) { fprintf(stderr, "%s: only root and members of the group 'video' may run this command\n", argv0); return 1; } } } if (!get && !set) { int i; printf("\n\n"); printf("If the program is abnormally aborted the may be some residual\n"); printf("effects on the terminal. the following commands should reset it:\n"); printf("\n"); printf(" stty sane\n"); printf(" printf '\\ec'\n"); printf("\n"); printf("\n\n\n\n"); /* Get the size of the terminal */ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == -1) fprintf(stderr, "%s: ioctl TIOCGWINSZ: %s\n", argv0, strerror(errno)); else cols = win.ws_col; line = malloc((size_t)cols * 3); space = malloc((size_t)cols); if (!line || !space) { fprintf(stderr, "%s: malloc: %s\n", argv0, strerror(ENOMEM)); return 1; } for (i = 0; i < cols; i++) { line[i * 3 + 0] = (char)(0xE2); line[i * 3 + 1] = (char)(0x94); line[i * 3 + 2] = (char)(0x80); } memset(space, ' ', (size_t)cols); space[cols - 1] = '\0'; line[(cols - 2) * 3] = '\0'; /* stty -icanon -echo */ if (tcgetattr(STDIN_FILENO, &stty)) { fprintf(stderr, "%s: tcgetattr : %s\n", argv0, strerror(errno)); return 1; } saved_stty = stty; stty.c_lflag &= (tcflag_t)~(ICANON | ECHO); if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &stty)) { fprintf(stderr, "%s: tcsetattr : %s\n", argv0, strerror(errno)); return 1; } /* Hide cursor */ printf("%s", "\033[?25l"); fflush(stdout); /* Fork to diminish risk of unclean exit */ pid = fork(); if (pid == (pid_t)-1) { fprintf(stderr, "%s: fork: %s\n", argv0, strerror(errno)); } else if (pid) { waitpid(pid, NULL, 0); goto done; } } if (argc) { for (; *argv; argv++) { if (strchr(*argv, '/')) *argv = strrchr(*argv, '/'); handle_device(*argv, get, !!set, set_value, set_prefix == '+', set_suffix); } } else { if ((dir = opendir(BACKLIGHT_DIR))) { while ((ent = readdir(dir))) { if (all || strstr(ent->d_name, "acpi_video") == ent->d_name) { if (*ent->d_name && *ent->d_name != '.') { handle_device(ent->d_name, get, !!set, set_value, set_prefix == '+', set_suffix); any = 1; } } } closedir(dir); } if (!any) fprintf(stderr, "%s: cannot find any backlight devices\n", argv0); } if (get) { if (nbrightness) { brightness *= 100; brightness /= (double)nbrightness; printf("%.2lf%%\n", brightness); fflush(stdout); } else { printf("%s\n", "100.00%"); fflush(stdout); } } done: if (!get && !set && pid) { /* Show cursor */ printf("%s", "\033[?25h"); fflush(stdout); /* `stty icanon echo` */ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty)) { fprintf(stderr, "%s: tcsetattr : %s\n", argv0, strerror(errno)); return 1; } } return 0; }