Individual device control added

This commit is contained in:
rexy712 2018-01-19 11:44:55 -08:00
parent b8df3dc3ed
commit 74fb2f11b9
3 changed files with 261 additions and 205 deletions

2
TODO
View File

@ -1,4 +1,4 @@
Individual device control #Individual device control
List devices List devices
Query backlight percentage (get) Query backlight percentage (get)
allow fade in and fade out allow fade in and fade out

View File

@ -2,7 +2,7 @@
all: rexbacklight all: rexbacklight
rexbacklight: src/rexbacklight.c rexbacklight: src/rexbacklight.c
gcc -std=c11 -g -Wall -Wextra -pedantic src/rexbacklight.c -o rexbacklight gcc -std=c11 -DDEBUG_REXBACKLIGHT -g -Wall -Wextra -pedantic src/rexbacklight.c -o rexbacklight
# strip --strip-all rexbacklight # strip --strip-all rexbacklight
.PHONY: install .PHONY: install

View File

@ -24,8 +24,6 @@
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
#define OP_INC 1
#define OP_DEC 2
#define OP_SET 4 #define OP_SET 4
#define OP_GET 128 #define OP_GET 128
#define OP_NONE 0 #define OP_NONE 0
@ -41,8 +39,40 @@
#define CHECK_OPTION(opt, arg) (!strcmp(opt##_LONG_OPT, arg) || !strcmp(opt##_SHORT_OPT, arg)) #define CHECK_OPTION(opt, arg) (!strcmp(opt##_LONG_OPT, arg) || !strcmp(opt##_SHORT_OPT, arg))
#define RETVAL_INVALID_FILE -1
#define RETVAL_INVALID_DIR -2
#define RETVAL_UNRECOGNIZED_OPTION -3
#define RETVAL_MISSING_OPTION -4
#define RETVAL_INVALID_DEVICE -5
#define RETVAL_SUCCESS EXIT_SUCCESS
struct string_array{
char** list;
int size;
};
struct arg_values{
struct arg_values* next;
//Specific device to control
//NULL means all devices
const char* device;
//What value to put in the backlight file
char* delta;
//How many seconds to transition
int fade_duration;
unsigned char operation;
};
//This is where backlight devices can be found in sysfs //This is where backlight devices can be found in sysfs
static const char* backlight_dir = "/sys/class/backlight/"; static const char* backlight_dir = "/sys/class/backlight/";
static const char* backlight_file = "brightness";
static const char* max_backlight_file = "max_brightness";
//Print out usage message and GPL message if not caused by an error //Print out usage message and GPL message if not caused by an error
void usage(int exit_val){ void usage(int exit_val){
@ -56,7 +86,7 @@ void usage(int exit_val){
fprintf(stderr, " max\n"); fprintf(stderr, " max\n");
fprintf(stderr, " min\n"); fprintf(stderr, " min\n");
if(!exit_val){ if(exit_val == RETVAL_SUCCESS){
fprintf(stderr, "\nrexbacklight Copyright (C) 2018 rexy712\n"); fprintf(stderr, "\nrexbacklight Copyright (C) 2018 rexy712\n");
fprintf(stderr, "This program comes with ABSOLUTELY NO WARRANTY.\n"); fprintf(stderr, "This program comes with ABSOLUTELY NO WARRANTY.\n");
fprintf(stderr, "This is free software, and you are welcome to redistribute it\n"); fprintf(stderr, "This is free software, and you are welcome to redistribute it\n");
@ -67,79 +97,116 @@ void usage(int exit_val){
exit(exit_val); exit(exit_val);
} }
struct string_array{ void free_cmd_args(struct arg_values* a){
char** list; if(!a->next)
int size; return;
}; free_cmd_args(a->next);
free(a->next);
}
//Get a list of backlight devices in sysfs #define CHECK_NEXT_ARG(rval) \
struct string_array get_backlight_sources(void){ do{ \
if(i == argc - 1){ \
fprintf(stderr, "Missing argument to '%s'\n\n", argv[i]); \
free_cmd_args(&ret); \
usage(rval); \
} \
}while(0)
#define UNRECOGNIZED_OPTION(rval) \
do{ \
fprintf(stderr, "Unrecognized command line option '%s'\n\n", argv[i]); \
free_cmd_args(&ret); \
usage(rval); \
}while(0);
DIR* fd; //Convert command line arguments to flags
struct dirent* dir; struct arg_values process_cmd_args(int argc, char** argv){
struct arg_values ret = {0};
struct arg_values* curr = &ret;
fd = opendir(backlight_dir); //Skip argv[0]
//If the dir can't be opened, return no backlight devices for(int i = 1;i < argc;i++){
if(!fd){
fprintf(stderr, "Unable to open '%s' for reading!\n", backlight_dir);
return (struct string_array){.list = NULL, .size = 0};
}
int i, list_size = 8; //Check for switches
char** list = malloc(sizeof(const char*) * list_size); if(CHECK_OPTION(GET, argv[i])){
curr->operation |= OP_GET;
continue;
for(i = 0;(dir = readdir(fd));){ }else if(CHECK_OPTION(FADE, argv[i])){
CHECK_NEXT_ARG(-5);
//Resize list if more than 8 backlight devices exist (not likely) curr->fade_duration = strtol(argv[++i], NULL, 0);
if(i > list_size){
char** tmp = malloc(sizeof(const char*) * (list_size += 8));
for(int j = 0;j < i;j++){
tmp[j] = list[j];
}
free(list);
list = tmp;
}
//Don't include "." or ".." in directory list
if(!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")){
continue; continue;
} }
list[i] = malloc(strlen(dir->d_name) + 1);
strcpy(list[i], dir->d_name); else if(CHECK_OPTION(DEVICE, argv[i])){
i++; CHECK_NEXT_ARG(-5);
curr->next = calloc(1, sizeof(struct arg_values));
curr = curr->next;
curr->device = argv[++i];
continue;
}
else if(CHECK_OPTION(HELP, argv[i])){
free_cmd_args(&ret);
usage(RETVAL_SUCCESS);
} }
closedir(fd); else if(!strcmp(argv[i], "max")){
curr->operation = OP_SET;
return (struct string_array){.list = list, .size = i}; curr->delta = argv[i];
}
//Delete the list of backlight devices
void free_string_array(struct string_array* s){
for(int i = 0;i < s->size;i++)
free(s->list[i]);
free(s->list);
}
//Read a float from a file
float get_brightness(const char* file){
//Static buffer because it shouldn't be able to exceed 127 chars long
char buff[127];
FILE* bright = fopen(file, "r");
if(!bright){
fprintf(stderr, "Unable to open brightness file \"%s\"!\n", file);
return 0;
} }
if(!fgets(buff, sizeof(buff), bright)){ else if(!strcmp(argv[i], "min")){
fprintf(stderr, "Unable to read file \"%s\"!\n", file); curr->operation = OP_SET;
fclose(bright); curr->delta = argv[i];
return 0; }
else if(!strcmp(argv[i], "off")){
curr->operation = OP_SET;
curr->delta = argv[i];
} }
fclose(bright);
return atof(buff);
}
int process_arg(char* arg, float min, float current, float max){ //If we get a '-' followed by not a number, it's not a known option
else if(argv[i][1] < '0' || argv[i][1] > '9'){
UNRECOGNIZED_OPTION(RETVAL_UNRECOGNIZED_OPTION);
}
//If we get a '-' followed by a number, it's a decrement request
else{
curr->operation = OP_SET;
curr->delta = argv[i];
}
}
//If there isn't an operation defined in the global context and there is no specified device, there's nothing to do
if(!ret.operation){
if(!ret.next){
fprintf(stderr, "No operation requested!\n\n");
usage(RETVAL_MISSING_OPTION);
}
//if there is a device specified, make sure each one has a requested operation
for(curr = ret.next;curr;curr = curr->next){
if(!curr->operation){
fprintf(stderr, "No operation requested for device '%s'!\n\n", curr->device);
free_cmd_args(&ret);
usage(RETVAL_MISSING_OPTION);
}
}
//If there is a globally defined operation, apply it to the devices with no operation request
}else{
for(curr = ret.next;curr;curr = curr->next){
if(curr->operation == OP_NONE){
curr->operation = ret.operation;
if(!curr->delta)
curr->delta = ret.delta;
}
}
}
return ret;
}
#undef CHECK_OPTION
#undef UNRECOGNIZED_OPTION
//Process an operation
int process_op(char* arg, float min, float current, float max){
//Amount to inc/dec //Amount to inc/dec
int delta = delta = max * atof(arg + 1) / 100.0; int delta = delta = max * atof(arg + 1) / 100.0;
@ -180,180 +247,169 @@ int process_arg(char* arg, float min, float current, float max){
//Set to maximum //Set to maximum
}else if(!strcmp("max", arg)){ }else if(!strcmp("max", arg)){
return max; return max;
//Unknown option
}else{
usage(-1);
} }
return current; return current;
} }
struct arg_values{ //Delete the list of backlight devices
struct arg_values* next; void free_string_array(struct string_array* s){
for(int i = 0;i < s->size;i++)
free(s->list[i]);
free(s->list);
}
//Specific device to control //Get a list of backlight devices in sysfs
//NULL means all devices struct string_array get_backlight_sources(void){
const char* device;
//What value to put in the backlight file DIR* fd;
int delta; struct dirent* dir;
//How many seconds to transition fd = opendir(backlight_dir);
int fade_duration; //If the dir can't be opened, return no backlight devices
if(!fd){
fprintf(stderr, "Unable to open '%s' for reading!\n", backlight_dir);
return (struct string_array){.list = NULL, .size = 0};
}
unsigned char operation; int i, list_size = 8;
}; char** list = malloc(sizeof(const char*) * list_size);
void free_cmd_args(struct arg_values* a){ for(i = 0;(dir = readdir(fd));){
if(!a->next)
//Don't include "." or ".." in directory list
if(!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")){
continue;
}
//Resize list if more than 8 backlight devices exist (not likely)
if(i > list_size){
char** tmp = malloc(sizeof(const char*) * (list_size += 8));
for(int j = 0;j < i;j++){
tmp[j] = list[j];
}
free(list);
list = tmp;
}
list[i] = malloc(strlen(dir->d_name) + 1);
strcpy(list[i], dir->d_name);
i++;
}
closedir(fd);
return (struct string_array){.list = list, .size = i};
}
//Read a float from a file
float get_brightness(const char* file){
//Static buffer because it shouldn't be able to exceed 127 chars long
char buff[127];
FILE* bright = fopen(file, "r");
if(!bright){
fprintf(stderr, "Unable to open brightness file \"%s\"!\n", file);
return 0;
}
if(!fgets(buff, sizeof(buff), bright)){
fprintf(stderr, "Unable to read file \"%s\"!\n", file);
fclose(bright);
return 0;
}
fclose(bright);
return atof(buff);
}
//Write value to backlight files
void write_delta(char* delta){
int out = process_op(delta, 0, get_brightness(backlight_file), get_brightness(max_backlight_file));
FILE* bright = fopen(backlight_file, "w+");
if(!bright){
fprintf(stderr, "Unable to open brightness file \"%s%s\" for writing!\n", backlight_dir, backlight_file);
return; return;
free_cmd_args(a->next); }
free(a->next); fprintf(bright, "%d", out);
fclose(bright);
} }
#define CHECK_NEXT_ARG(rval) \
do{ \
if(i == argc - 1){ \
fprintf(stderr, "Missing argument to '%s'\n\n", argv[i]); \
free_cmd_args(&ret); \
usage(rval); \
} \
}while(0)
#define UNRECOGNIZED_OPTION(rval) \
do{ \
fprintf(stderr, "Unrecognized command line option '%s'\n\n", argv[i]); \
free_cmd_args(&ret); \
usage(rval); \
}while(0);
struct arg_values process_cmd_args(int argc, char** argv){
struct arg_values ret = {0};
struct arg_values* curr = &ret;
//Skip argv[0]
for(int i = 1;i < argc;i++){
//Check for switches
if(CHECK_OPTION(GET, argv[i])){
curr->operation |= OP_GET;
continue;
}else if(CHECK_OPTION(FADE, argv[i])){
CHECK_NEXT_ARG(-5);
curr->fade_duration = strtol(argv[++i], NULL, 0);
continue;
}
else if(CHECK_OPTION(DEVICE, argv[i])){
CHECK_NEXT_ARG(-5);
curr->next = calloc(1, sizeof(struct arg_values));
curr = curr->next;
curr->device = argv[++i];
continue;
}
else if(CHECK_OPTION(HELP, argv[i])){
free_cmd_args(&ret);
usage(0);
}
//If we get a '-' followed by not a number, it's not a known option
else if(argv[i][1] < '0' || argv[i][1] > '9'){
UNRECOGNIZED_OPTION(-4);
}
//If we get a '-' followed by a number, it's a decrement request
else{
switch(argv[i][0]){
case '=':
curr->operation = OP_SET;
break;
case '-':
curr->operation = OP_DEC;
break;
case '+':
curr->operation = OP_INC;
break;
default:
UNRECOGNIZED_OPTION(-4);
}
curr->delta = atof(&argv[i][1]);
}
}
//If there isn't an operation defined in the global context and there is no specified device, there's nothing to do
if(!ret.operation){
if(!ret.next){
fprintf(stderr, "No operation requested!\n\n");
usage(-1);
}
//if there is a device specified, make sure each one has a requested operation
for(curr = ret.next;curr;curr = curr->next){
if(!curr->operation){
fprintf(stderr, "No operation requested for device '%s'!\n\n", curr->device);
usage(-1);
}
}
//If there is a globally defined operation, apply it to the devices with no operation request
}else{
for(curr = ret.next;curr;curr = curr->next){
if(!curr->operation)
curr->operation = ret.operation;
}
}
return ret;
}
#undef CHECK_OPTION
#undef UNRECOGNIZED_OPTION
//argv[1] shall be [+-=]<percentage>|min|max|off //argv[1] shall be [+-=]<percentage>|min|max|off
int main(int argc, char** argv){ int main(int argc, char** argv){
const char* backlight_file = "brightness";
const char* max_backlight_file = "max_brightness";
struct arg_values args = process_cmd_args(argc, argv); struct arg_values args; //A linked list of devices and the requested settings.
struct string_array backlight_names; //List of all backlight devices in sysfs.
//Acquire a list of backlight devices in sysfs args = process_cmd_args(argc, argv);
struct string_array backlight_names = get_backlight_sources(); backlight_names = get_backlight_sources();
//Macro for easy memory cleaning
#define CLEANUP() do{free_string_array(&backlight_names);free_cmd_args(&args);}while(0)
//If there are no backlights, we can't do anything
if(backlight_names.size == 0){ if(backlight_names.size == 0){
fprintf(stderr, "No backlights devices found!\n"); fprintf(stderr, "No backlights devices found!\n");
return -1; CLEANUP();
return RETVAL_INVALID_DEVICE;
}
//Make sure all explicit devices actually exist
for(struct arg_values* curr = args.next;curr;curr = curr->next){
for(int i = 0;i < backlight_names.size;i++){
if(!strcmp(curr->device, backlight_names.list[i])){
goto continue_outer;
}
}
fprintf(stderr, "No such device '%s'\n", curr->device);
CLEANUP();
return RETVAL_INVALID_DEVICE;
continue_outer:;
} }
//save our starting directory so we can change to each backlight directory (makes logic easier) //save our starting directory so we can change to each backlight directory (makes logic easier)
char* starting_dir = getcwd(NULL, 0); char* starting_dir = getcwd(NULL, 0);
//Redefine CLEANUP to include freeing of the starting_dir string
#undef CLEANUP
#define CLEANUP() do{free_string_array(&backlight_names);free_cmd_args(&args);free(starting_dir);}while(0)
//Change to the base directory for all sysfs backlights
if(chdir(backlight_dir)){ if(chdir(backlight_dir)){
fprintf(stderr, "Unable to read backlight sysfs directory!\n"); fprintf(stderr, "Unable to read backlight sysfs directory!\n");
return -2; CLEANUP();
return RETVAL_INVALID_DIR;
} }
//Set all backlight sources to the requested percentage //If args.next is not NULL, then 1+ devices were specified. Only apply change to those
if(args.next){
for(struct arg_values* curr = args.next;curr;curr = curr->next){
if(chdir(curr->device)){
fprintf(stderr, "Unable to open backlight directory \"%s%s\"!\n", backlight_dir, curr->device);
continue;
}
write_delta(curr->delta);
}
//Otherise, apply delta to all backlights
}else{
for(int i = 0;i < backlight_names.size;i++){ for(int i = 0;i < backlight_names.size;i++){
if(chdir(backlight_names.list[i])){ if(chdir(backlight_names.list[i])){
fprintf(stderr, "Unable to open backlight directory \"%s%s\"!\n", backlight_dir, backlight_names.list[i]); fprintf(stderr, "Unable to open backlight directory \"%s%s\"!\n", backlight_dir, backlight_names.list[i]);
continue; continue;
} }
write_delta(args.delta);
//Write value to backlight files
int out = process_arg(argv[1], 0, get_brightness(backlight_file), get_brightness(max_backlight_file));
FILE* bright = fopen(backlight_file, "w+");
if(!bright){
fprintf(stderr, "Unable to open brightness file \"%s%s\" for writing!\n", backlight_dir, backlight_file);
continue;
} }
fprintf(bright, "%d", out);
fclose(bright);
} }
free_string_array(&backlight_names);
free_cmd_args(&args);
//Return to start directory //Return to start directory
if(chdir(starting_dir)){ if(chdir(starting_dir)){
free(starting_dir); CLEANUP();
fprintf(stderr, "Could not return to starting directory!\nWas the directory moved/deleted?\n"); fprintf(stderr, "Could not return to starting directory!\nWas the directory moved/deleted?\n");
if(chdir(getenv("HOME")) || chdir("/")){ if(!chdir(getenv("HOME")) || !chdir("/")){
return -5; return RETVAL_INVALID_DIR;
}else{
return RETVAL_INVALID_DIR;
} }
} }
free(starting_dir); CLEANUP();
return 0; return RETVAL_SUCCESS;
} }
#undef CLEANUP