9 Commits
v1.3 ... v1.3.1

Author SHA1 Message Date
rexy712
eab07e49cf Fix readme 2018-12-15 10:06:43 -08:00
rexy712
94ce061013 Updated Readme 2018-12-15 10:01:59 -08:00
rexy712
80eda899e7 made python optional for no real reason 2018-12-14 18:42:59 -08:00
Rexy712
cf45749af2 Change makefile output for version.h generation 2018-12-11 13:35:14 -08:00
Rexy712
a394ade61e Fixed missing config.h file issue 2018-12-11 13:32:02 -08:00
Rexy712
9a1856ace5 Changed version detection to work at compile time instead of configure time 2018-12-11 13:29:05 -08:00
rexy712
b5815149a3 changed version output to print branch and commit when not on a precise version tag 2018-12-10 16:33:14 -08:00
Rexy712
1d5a5d9d69 Refactored how operations are carried out 2018-12-10 11:19:15 -08:00
rexy712
04dcd0684a Some cleanup work 2018-12-09 04:28:21 -08:00
17 changed files with 313 additions and 228 deletions

2
.gitignore vendored
View File

@@ -4,4 +4,4 @@ rexbacklight
rexledctl
*.swp
build
include/config.h
include/version.h

View File

@@ -1,15 +1,52 @@
include(CMakeDependentOption)
cmake_minimum_required(VERSION 3.0.2)
cmake_minimum_required(VERSION 3.1)
project(rexbacklight)
#setup common defines for C files
set(rexbacklight_VERSION_MAJOR 1)
set(rexbacklight_VERSION_MINOR 3)
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
set(SCRIPT_DIR ${CMAKE_SOURCE_DIR}/scripts)
#set project include directory
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
include_directories("${INCLUDE_PATH}")
set(GIT_VERSION_FILE ${CMAKE_SOURCE_DIR}/include/version.h)
set(GIT_VERSION_TMP_FILE ${CMAKE_SOURCE_DIR}/include/verison.h.tmp)
#require python on windows
#i know this program can't be built on windows, but i wanted to figure out how to do this
if(WIN32)
find_package(PythonInterp 3 REQUIRED)
else()
find_package(PythonInterp 3 QUIET)
endif()
if(PYTHONINTERP_FOUND)
add_custom_command(
OUTPUT ${GIT_VERSION_TMP_FILE}
COMMAND ${CMAKE_COMMAND} -E echo "//File generated by CMake. Do not edit!" > ${GIT_VERSION_TMP_FILE}
COMMAND ${PYTHON_EXECUTABLE} ${SCRIPT_DIR}/git_branch_name.py >> ${GIT_VERSION_TMP_FILE}
COMMAND ${PYTHON_EXECUTABLE} ${SCRIPT_DIR}/git_commit_hash.py >> ${GIT_VERSION_TMP_FILE}
COMMAND ${PYTHON_EXECUTABLE} ${SCRIPT_DIR}/git_tag_name.py >> ${GIT_VERSION_TMP_FILE}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GIT_VERSION_TMP_FILE} ${GIT_VERSION_FILE}
COMMAND ${CMAKE_COMMAND} -E remove ${GIT_VERSION_TMP_FILE}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Regenerating version.h"
VERBATIM
)
elseif(UNIX)
add_custom_command(
OUTPUT ${GIT_VERSION_TMP_FILE}
COMMAND ${CMAKE_COMMAND} -E echo "//File generated by CMake. Do not edit!" > ${GIT_VERSION_TMP_FILE}
COMMAND ${SCRIPT_DIR}/git_branch_name.sh >> ${GIT_VERSION_TMP_FILE}
COMMAND ${SCRIPT_DIR}/git_commit_hash.sh >> ${GIT_VERSION_TMP_FILE}
COMMAND ${SCRIPT_DIR}/git_tag_name.sh >> ${GIT_VERSION_TMP_FILE}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${GIT_VERSION_TMP_FILE} ${GIT_VERSION_FILE}
COMMAND ${CMAKE_COMMAND} -E remove ${GIT_VERSION_TMP_FILE}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Regenerating version.h"
VERBATIM
)
endif()
#setup cmake options
option(BUILD_REXLEDCTL "Build led control program" ON)
option(BUILD_REXBACKLIGHT "Build backlight control program" ON)
@@ -23,7 +60,7 @@ set(UDEV_DIR "/etc/udev/rules.d" CACHE STRING "Set the output directory for udev
mark_as_advanced(UDEV_DIR)
if(XBACKLIGHT_COMPAT)
add_definitions(-DXBACKLIGHT_COMPAT_OPTIONS)
add_definitions("-DXBACKLIGHT_COMPAT_OPTIONS")
endif()
#locate rjp library requirements
@@ -32,17 +69,15 @@ if(ENABLE_RESTORE_FILE)
find_path(RJP_HEADER_DIR rjp.h)
#temporary library (no actual library generated)
add_library(common_srcs OBJECT src/cmd.c src/common.c src/restore.c)
set(enable_RESTORE_FILE "#define ENABLE_RESTORE_FILE")
add_definitions("-DENABLE_RESTORE_FILE")
else()
add_library(common_srcs OBJECT src/cmd.c src/common.c)
set(enable_RESTORE_FILE "")
endif()
target_sources(common_srcs PUBLIC ${GIT_VERSION_TMP_FILE})
configure_file(
"${INCLUDE_PATH}/config.h.in"
"${INCLUDE_PATH}/config.h"
)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DREXBACKLIGHT_DEBUG")
#build led control program
if(BUILD_REXLEDCTL)
add_executable (rexledctl src/rexbacklight.c)
add_dependencies(rexledctl common_srcs) #force common_srcs to be built first
@@ -57,6 +92,7 @@ if(BUILD_REXLEDCTL)
install(FILES ${CMAKE_SOURCE_DIR}/rules/91-leds.rules DESTINATION ${UDEV_DIR})
endif()
endif()
#build backlight control program
if(BUILD_REXBACKLIGHT)
add_executable (rexbacklight src/rexbacklight.c)
add_dependencies(rexledctl common_srcs)

View File

@@ -1,31 +1,33 @@
# README
## About
rexbacklight is a program designed to operate similarly to xbacklight, though it's not a drop in replacement (possibly adding drop in replacement configure option at some point). Unlike xbacklight, this program does not use X11 to control the backlight devices. rexbacklight directly interacts with sysfs to change backlight levels, read their current status, and find their max output level. This has the advantage of allowing rexbacklight to run without the X server running and will work even if X doesn't recognize your backlight device, which was my original inspiration to make this project.
rexbacklight is a program designed to operate similarly to xbacklight. There is even a configure option to enable xbacklight arguments to make this a drop in replacement. However unlike xbacklight, this program does not use X11 to control the backlight devices. rexbacklight directly interacts with sysfs to change backlight levels, read their current status, and find their max output level. This has the advantage of allowing rexbacklight to run without the X server running and will work even if X doesn't recognize your backlight device, which was my original inspiration to make this project.
I've also since added a program to control LED devices, rexledctl (best name I could think of). This one was inspired by the fact that my laptop's keyboard backlight wasn't controllable by any program I had and the hardware button didn't work either. So now I have my own program which can control it. It was so similar in function to rexbacklight that I decided to just add some compile time `#define`'s and make a few things more modular in the code and have the same source files make 2 different programs.
Either program can be built alone or both together. This is configured using some cmake magic.
Current version is 1.0 as of writing this, so if the version is much newer, remind me to update this readme.
Current version is 1.3.1 as of writing this, so if the version is much newer, remind me to update this readme.
```
Usage: rexbacklight [argument] [options] [argument]
Options:
--device|-d
--device|-d
select which device to control
--fade|-f
--fade|-f
change brightness over time interval
--steps|-s
--steps|-s
number of steps over which to fade
--get|-g
--get|-g
print current brightness level to stdout
--list|-l
--list|-l
print device names to stdout and exit
--help|-h
--restore|-R
reassign previously saved device values
--help|-h
print this help message and exit
--version
--version
print program version and exit
Arguments:
@@ -44,7 +46,8 @@ A copy of the GPLv3 is available with the source in the file 'LICENSE'
```
## Dependencies
Requires the rjp (rexy's json parser) library for reading restore file.
Requires the rjp ([rexy's json parser][rjp]) library for reading restore file, though this can be disabled via cmake configuration.
Either python3 or sh compatible shell to run some build scripts.
## Building
##### Run the following commands
@@ -72,3 +75,5 @@ sudo udevadm trigger
```
sudo make uninstall
```
[rjp]: https://gitlab.com/rexy712/rjp

View File

@@ -56,7 +56,10 @@ struct arg_values{
int fade_steps;
unsigned char operation;
union{
int operation;
int num_values;
};
};

View File

@@ -19,6 +19,12 @@
#ifndef REXBACKLIGHT_COMMON_H
#define REXBACKLIGHT_COMMON_H
#ifdef REXBACKLIGHT_DEBUG
#define DEBUG_PRINT(s, ...) printf(s, __VA_ARGS__)
#else
#define DEBUG_PRINT(s, ...)
#endif
#define RETVAL_INVALID_FILE 1
#define RETVAL_INVALID_DIR 2
#define RETVAL_UNRECOGNIZED_OPTION 3
@@ -34,19 +40,19 @@
#define IO_ERROR_READ "read"
#define IO_ERROR_WRITE "write to"
void io_error(const char* error, const char* type, const char* name);
void io_error_2(const char* error, const char* type, const char* name, const char* name2);
void io_error_3(const char* error, const char* type, const char* name, const char* name2, const char* name3);
extern int return_value;
struct string_array{
char** list;
int size;
};
void free_string_array(struct string_array* s);
void io_error(const char* error, const char* type, const char* name);
void io_error_2(const char* error, const char* type, const char* name, const char* name2);
void io_error_3(const char* error, const char* type, const char* name, const char* name2, const char* name3);
void mem_error(void);
_Noreturn void version(void);
_Noreturn void usage(int exit_val);

View File

@@ -19,10 +19,13 @@
#ifndef CONFIG_H
#define CONFIG_H
#define REXBACKLIGHT_VERSION_MAJOR @rexbacklight_VERSION_MAJOR@
#define REXBACKLIGHT_VERSION_MINOR @rexbacklight_VERSION_MINOR@
#include "version.h"
@enable_RESTORE_FILE@
#ifdef GIT_TAG_NAME
#define REXBACKLIGHT_VERSION GIT_TAG_NAME
#else
#define REXBACKLIGHT_VERSION GIT_BRANCH_NAME "-" GIT_COMMIT_HASH
#endif
#endif

View File

@@ -25,8 +25,7 @@
#include "cmd.h"
void save_restore_file(struct string_array* devices);
void all_restore(void);
int individual_restore(struct arg_values* curr, RJP_value** root, int* try_restore);
int restore_to_delta(struct arg_values* curr, RJP_value** root, int* try_restore);
RJP_value* find_matching_json_device(const char* name, RJP_value* root);
RJP_value* read_restore_file(const char* file);

8
scripts/git_branch_name.py Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/python
import subprocess
output = subprocess.run(['git', 'rev-parse', "--abbrev-ref", "HEAD"], stdout=subprocess.PIPE).stdout.decode('utf-8').rstrip()
if output:
print("#define GIT_BRANCH_NAME " + '"' + output + '"')

7
scripts/git_branch_name.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
output="$(git rev-parse --abbrev-ref HEAD)"
if test -n "$output";then
echo "#define GIT_BRANCH_NAME \"$output\" //test"
fi

8
scripts/git_commit_hash.py Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/python
import subprocess
output = subprocess.run(['git', 'describe', "--always", "--dirty", "--abbrev", "--match=\"NeVeRmAtCh\""], stdout=subprocess.PIPE).stdout.decode('utf-8').rstrip()
if output:
print("#define GIT_COMMIT_HASH " + '"' + output + '"')

7
scripts/git_commit_hash.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
output="$(git describe --always --dirty --abbrev --match="NeVeRmAtCh")"
if test -n output;then
echo "#define GIT_COMMIT_HASH \"$output\""
fi

9
scripts/git_tag_name.py Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/python
import subprocess
output = subprocess.run(['git', 'tag', "--points-at", "HEAD"], stdout=subprocess.PIPE).stdout.decode('utf-8').rstrip()
if output:
if output[0] == 'v' or output[0] == 'V':
print("#define GIT_TAG_NAME " + '"' + output[1:] + '"')

9
scripts/git_tag_name.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/sh
output="$(git tag --points-at HEAD)"
if test -n "$output";then
if test "${output:0:1}" = "v" || test "${output:0:1}" = "V";then
echo "#define GIT_TAG_NAME \"${output:1}\""
fi
fi

View File

@@ -69,7 +69,7 @@
#define SET_DESC "set backlight device to specified value"
#define INC_DESC "increase backlight device by specified value"
#define DEC_DESC "decrease backlight device by specified value"
#define RESTORE_DESC "reassign previously saved backlight values"
#define RESTORE_DESC "reassign previously saved device values"
int strcmp_handle_null(const char* one, const char* two){
if(!one)
@@ -119,7 +119,6 @@ struct cmd_arg rexbacklight_args[] = {
};
#endif //XBACKLIGHT_COMPAT_OPTIONS
int rexbacklight_args_length = sizeof(rexbacklight_args) / sizeof(rexbacklight_args[0]);
//Clean up a cmd_arg struct
@@ -155,6 +154,7 @@ struct arg_values init_arg_values(void){
struct arg_values process_cmd_args(int argc, char** argv){
struct arg_values ret = init_arg_values();
struct arg_values* curr = &ret;
int nvals = 0;
int i;
//Skip argv[0]
@@ -189,6 +189,7 @@ struct arg_values process_cmd_args(int argc, char** argv){
*(curr->next) = init_arg_values();
curr = curr->next;
curr->device = argv[++i];
++nvals;
continue;
}
@@ -305,6 +306,8 @@ struct arg_values process_cmd_args(int argc, char** argv){
}
}
}
if(ret.next)
ret.num_values = nvals;
return ret;
}
#undef CHECK_OPTION

View File

@@ -36,19 +36,26 @@ void io_error_3(const char* error, const char* type, const char* name, const cha
fprintf(stderr, "Unable to %s %s '%s%s%s'!\n", error, type, name, name2, name3);
}
//Clean up a string array
void free_string_array(struct string_array* s){
int i;
for(i = 0;i < s->size;++i)
free(s->list[i]);
free(s->list);
}
void mem_error(void){
fprintf(stderr, "Failed to allocate memory! Unable to continue!\n");
}
_Noreturn void version(void){
printf("%s version %d.%d\n", executable_name(), REXBACKLIGHT_VERSION_MAJOR, REXBACKLIGHT_VERSION_MINOR);
printf("%s version %s\n", executable_name(), REXBACKLIGHT_VERSION);
exit(return_value);
}
_Noreturn void usage(int exit_val){
int i;
printf("%s version %d.%d\n\n", executable_name(), REXBACKLIGHT_VERSION_MAJOR, REXBACKLIGHT_VERSION_MINOR);
printf("%s version %s\n\n", executable_name(), REXBACKLIGHT_VERSION);
printf("Usage: %s [argument] [options] [argument]\n\n", executable_name());
printf("Options:\n");

View File

@@ -92,8 +92,8 @@ RJP_value* find_matching_json_device(const char* name, RJP_value* root){
return NULL;
}
//run restore on individual device
int individual_restore(struct arg_values* curr, RJP_value** root, int* try_restore){
//convert a restoration operation to a set operation
int restore_to_delta(struct arg_values* curr, RJP_value** root, int* try_restore){
if(!*root && *try_restore){
char* tmp = restore_file();
*root = read_restore_file(tmp);
@@ -113,24 +113,6 @@ int individual_restore(struct arg_values* curr, RJP_value** root, int* try_resto
return 1;
}
//run restore on all devices
void all_restore(void){
char* rfil = restore_file();
RJP_value* root = read_restore_file(rfil);
free(rfil);
if(!root)
return;
struct arg_values tmp = {.next = NULL,
.fade_duration = 0,
.fade_steps = 1,
.operation = OP_SET};
for(RJP_value* curr = rjp_get_member(root);curr;curr = rjp_next_member(curr)){
tmp.device = rjp_member_name(curr);
tmp.delta = rjp_value_dfloat(curr);
individual_device_op(&tmp);
}
rjp_free_value(root);
}
void save_restore_file(struct string_array* devices){
RJP_value* root = rjp_init_json();
for(size_t i = 0;i < devices->size;++i){

View File

@@ -35,12 +35,13 @@
#include "restore.h"
#endif
//name of the program being run so that we print the correct name in the usage
#ifdef REXBACKLIGHT
//This is where backlight devices can be found in sysfs
const char* device_dir(void){return "/sys/class/backlight/";}
//name of the program being run so that we print the correct name in the usage
const char* executable_name(void){return "rexbacklight";}
//location of the restore file in the home directory
const char* restore_file_suffix(void){return "/.config/rexbacklight.json";}
#elif defined(REXLEDCTL)
//This is where led devices can be found in sysfs
@@ -53,13 +54,6 @@ const char* restore_file_suffix(void){return "/.config/rexledctl.json";}
const char* brightness_file(void){return "brightness";}
const char* max_brightness_file(void){return "max_brightness";}
//Clean up a string array
void free_string_array(struct string_array* s){
int i;
for(i = 0;i < s->size;++i)
free(s->list[i]);
free(s->list);
}
//Get a list of led/backlight devices in sysfs
struct string_array get_device_sources(void){
@@ -83,23 +77,24 @@ struct string_array get_device_sources(void){
return arr;
}
#define MEMORY_ERROR() do{mem_error();return_value = RETVAL_MEM_ERROR;free_string_array(&arr);closedir(fd);}while(0)
for(arr.size = 0;(dir = readdir(fd));){
int slen;
//ignore '.' and '..'
if(!strncmp(dir->d_name, ".", 1) || !strncmp(dir->d_name, "..", 2)){
continue;
}
//Resize list if more than 8 led/backlight devices exist (not likely)
if(arr.size >= list_size){
int j;
char** tmp = malloc(sizeof(const char*) * (list_size += 8));
if(!tmp){
MEMORY_ERROR();
mem_error();
return_value = RETVAL_MEM_ERROR;
free_string_array(&arr);
closedir(fd);
return (struct string_array){0};
}
for(j = 0;j < arr.size;++j){
for(int j = 0;j < arr.size;++j){
tmp[j] = arr.list[j];
}
free(arr.list);
@@ -109,16 +104,17 @@ struct string_array get_device_sources(void){
slen = strlen(dir->d_name);
arr.list[arr.size] = malloc(slen + 1);
if(!arr.list[arr.size]){
MEMORY_ERROR();
mem_error();
return_value = RETVAL_MEM_ERROR;
free_string_array(&arr);
closedir(fd);
return (struct string_array){0};
}
strcpy(arr.list[arr.size], dir->d_name);
strncpy(arr.list[arr.size], dir->d_name, slen);
arr.list[arr.size][slen] = 0;
++arr.size;
}
#undef MEMORY_ERROR
closedir(fd);
return arr;
}
@@ -142,42 +138,53 @@ float get_brightness(const char* file){
fclose(bright);
return atof(buff);
}
//return milliseconds since last boot
double get_time(void){
struct timespec t;
clock_gettime(CLOCK_BOOTTIME, &t);
return ((t.tv_sec * 1000.0) + (t.tv_nsec * 0.0000001));
}
//sleep thread for milliseconds
void sleep_for(double time){
DEBUG_PRINT("sleeping for %lf milliseconds\n", time);
struct timespec ts;
ts.tv_sec = (long)(time*0.0001);
ts.tv_nsec = ((time) - ts.tv_sec*1000) * 1000*1000;
nanosleep(&ts, NULL);
}
//update brightness incrementally over requested millisecond time interval
void fade_out(const char* device_name, int iv, int fv, int ms_duration, int steps){
void fade_out(struct arg_values* arg, int iv, int fv){
FILE* fd;
struct timeval start, end;
double tdelta = 0; //amount of time that has passed in ms
double start, end;
double tdelta = 0; //amount of time that has passed in ms
float value = iv; //current value to write to file
int step_delta = ms_duration / steps;
int step_inc = step_delta;
float brdelta = (float)(fv - iv) / steps; //amount brightness needs to change per iteration
double step_delta = (double)arg->fade_duration / arg->fade_steps;
double step_inc = step_delta;
float brdelta = (float)(fv - iv) / arg->fade_steps; //amount brightness needs to change per iteration
while(ms_duration > tdelta){
while(arg->fade_duration > tdelta){
//write
gettimeofday(&start, NULL);
start = get_time();
fd = fopen(brightness_file(), "w+");
if(!fd){
io_error_3(IO_ERROR_OPEN, IO_ERROR_FILE, device_dir(), device_name, brightness_file());
io_error_3(IO_ERROR_OPEN, IO_ERROR_FILE, device_dir(), arg->device, brightness_file());
return_value = RETVAL_INVALID_FILE;
return;
}
fprintf(fd, "%d", (int)value);
fclose(fd);
gettimeofday(&end, NULL);
end = get_time();
//calc time delta
tdelta += (((end.tv_sec * 1000.0) + (end.tv_usec / 1000.0)) - ((start.tv_sec * 1000.0) + (start.tv_usec / 1000.0)));
tdelta += end - start;
//calc next value to write
value += brdelta;
//waste excess time until next step
if(step_delta > tdelta){
struct timespec ts;
ts.tv_sec = (long)((step_delta - tdelta)/1000);
ts.tv_nsec = ((step_delta - tdelta) - ts.tv_sec*1000) * 1000*1000;
nanosleep(&ts, NULL);
sleep_for(step_delta - tdelta);
tdelta = step_delta;
}
//calc end of next step
@@ -185,7 +192,7 @@ void fade_out(const char* device_name, int iv, int fv, int ms_duration, int step
}
fd = fopen(brightness_file(), "w+");
if(!fd){
io_error_3(IO_ERROR_OPEN, IO_ERROR_FILE, device_dir(), device_name, brightness_file());
io_error_3(IO_ERROR_OPEN, IO_ERROR_FILE, device_dir(), arg->device, brightness_file());
return_value = RETVAL_INVALID_FILE;
return;
}
@@ -195,144 +202,136 @@ void fade_out(const char* device_name, int iv, int fv, int ms_duration, int step
}
//Write value to device files
void do_assignment(struct arg_values* arg, const char* device){
int start = get_brightness(brightness_file());
int out = process_op(arg, 0, start, get_brightness(max_brightness_file()));
fade_out(device, start, out, arg->fade_duration, arg->fade_steps);
}
//Run get operation
void do_get(const char* device){
float curr, max;
curr = get_brightness(brightness_file());
max = get_brightness(max_brightness_file());
if(return_value == RETVAL_INVALID_FILE && (!curr || !max)){
fprintf(stdout, "%s", device);
}else{
fprintf(stdout, "%-25s %.2f\n", device, curr / max * 100);
}
}
//Run list operation
void do_list(struct string_array* names){
int i;
for(i = 0;i < names->size;++i)
fprintf(stdout, "%s\n", names->list[i]);
}
int individual_device_op(struct arg_values* curr){
if(chdir(curr->device)){
io_error_2(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir(), curr->device);
return RETVAL_INVALID_DIR;
}
switch(curr->operation){
case OP_SET:
case OP_INC:
case OP_DEC:
do_assignment(curr, curr->device);
break;
case OP_GET:
do_get(curr->device);
break;
}
if(chdir(device_dir())){
io_error(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir());
return RETVAL_INTERNAL_ERROR;
int do_assignment(struct arg_values* args){
for(;args;args = args->next){
if(chdir(args->device)){
io_error_2(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir(), args->device);
return_value = RETVAL_INVALID_DIR;
continue;
}
int start = get_brightness(brightness_file());
int out = process_op(args, 0, start, get_brightness(max_brightness_file()));
fade_out(args, start, out);
if(chdir(device_dir())){
io_error(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir());
return RETVAL_INVALID_DIR;
}
}
return return_value;
}
//Run get operation
int do_get(struct arg_values* args){
for(;args;args = args->next){
float curr, max;
if(chdir(args->device)){
io_error_2(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir(), args->device);
return_value = RETVAL_INVALID_DIR;
continue;
}
curr = get_brightness(brightness_file());
max = get_brightness(max_brightness_file());
if(return_value == RETVAL_INVALID_FILE && (!curr || !max)){
fprintf(stdout, "%s", args->device);
}else{
fprintf(stdout, "%-25s %.2f\n", args->device, curr / max * 100);
}
if(chdir(device_dir())){
io_error(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir());
return RETVAL_INVALID_DIR;
}
}
return return_value;
}
//Run list operation
void do_list(struct arg_values* args){
for(;args;args = args->next){
printf("%s\n", args->device);
}
}
//move next arg in src list to next arg in dest list
void extract_next_arg(struct arg_values* dest, struct arg_values* src){
dest->next = src->next;
src->next = src->next->next;
dest->next->next = NULL;
}
#ifdef ENABLE_RESTORE_FILE
//If devices were specified, this function will run
void individual_device_loop(struct arg_values* a){
struct arg_values* curr;
//convert all restore operations to set operations
void prep_restore(struct arg_values* a){
RJP_value* root = 0;
int try_restore = 1;
for(curr = a->next;curr;curr = curr->next){
if(curr->operation == OP_RESTORE){
if(!individual_restore(curr, &root, &try_restore))
continue;
}
return_value = individual_device_op(curr);
if(return_value == RETVAL_INTERNAL_ERROR){
rjp_free_value(root);
return;
}
for(struct arg_values* curr = a;curr;curr = curr->next){
restore_to_delta(curr, &root, &try_restore);
}
rjp_free_value(root);
}
#else
#define prep_restore(x)
#endif
//remove all args in src which have the specified operation and append them to dest
struct arg_values* extract_operation(int operation, struct arg_values* dest, struct arg_values* src){
struct arg_values* curr_dest = dest;
dest->next = NULL;
for(struct arg_values* curr_src = src;curr_src->next;){
if(curr_src->next->operation & operation){
extract_next_arg(curr_dest, curr_src);
curr_dest = curr_dest->next;
}else{
curr_src = curr_src->next;
}
}
return curr_dest;
}
//If devices were specified, this function will run
void individual_device_loop(struct arg_values* a){
void run_device_operations(struct arg_values* a){
struct arg_values* curr;
int try_restore = 1;
for(curr = a->next;curr;curr = curr->next){
return_value = individual_device_op(curr);
if(return_value == RETVAL_INTERNAL_ERROR)
return;
}
}
#endif
struct arg_values current_op_args = {0};
//list
extract_operation(OP_LIST, &current_op_args, a);
do_list(current_op_args.next);
free_cmd_args(&current_op_args);
//get
extract_operation(OP_GET, &current_op_args, a);
return_value = do_get(current_op_args.next);
free_cmd_args(&current_op_args);
//assignment
curr = extract_operation(OP_RESTORE, &current_op_args, a);
prep_restore(current_op_args.next);
extract_operation(OP_SET | OP_INC | OP_DEC, curr, a);
return_value = do_assignment(current_op_args.next);
free_cmd_args(&current_op_args);
//If no devices were specified, this function will run
void all_device_loop(struct string_array* device_names, struct arg_values* args){
int i;
switch(args->operation){
case OP_SET:
case OP_INC:
case OP_DEC:
for(i = 0;i < device_names->size;++i){
if(chdir(device_names->list[i])){
io_error_2(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir(), device_names->list[i]);
return_value = RETVAL_INVALID_DIR;
continue;
}
do_assignment(args, device_names->list[i]);
if(chdir(device_dir())){
io_error(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir());
return_value = RETVAL_INVALID_DIR;
return;
}
}
#ifdef ENABLE_RESTORE_FILE
save_restore_file(device_names);
#endif
break;
case OP_LIST:
do_list(device_names);
break;
case OP_GET:
for(i = 0;i < device_names->size;++i){
if(chdir(device_names->list[i])){
io_error_2(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir(), device_names->list[i]);
return_value = RETVAL_INVALID_DIR;
continue;
}
do_get(device_names->list[i]);
if(chdir(device_dir())){
io_error(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir());
return_value = RETVAL_INVALID_DIR;
return;
}
}
break;
#ifdef ENABLE_RESTORE_FILE
case OP_RESTORE:;
all_restore();
break;
#endif
default:
fprintf(stderr, "Internal processing error!\n");
break;
}
}
//If a global operation is requested, run this to fill arg with all devices, each with the global operation
void populate_args(struct arg_values* arg, struct string_array* devices){
struct arg_values* curr = arg;
for(size_t i = 0;i < devices->size;++i){
curr->next = malloc(sizeof(struct arg_values));
curr->next->device = devices->list[i];
curr->next->delta = arg->delta;
curr->next->fade_duration = arg->fade_duration;
curr->next->fade_steps = arg->fade_steps;
curr->next->operation = arg->operation;
curr = curr->next;
}
curr->next = NULL;
arg->num_values = devices->size;
}
int main(int argc, char** argv){
struct arg_values args; //A linked list of devices and the requested settings.
struct string_array device_names; //List of all led/backlight devices in sysfs.
struct arg_values* curr;
args = process_cmd_args(argc, argv);
if(args.operation == OP_USAGE){
usage(return_value);
@@ -342,34 +341,28 @@ int main(int argc, char** argv){
device_names = get_device_sources();
//Macro for easy memory cleaning
#define CLEANUP() do{free_string_array(&device_names);free_cmd_args(&args);}while(0)
//If there are no led/backlights, we can't do anything
if(device_names.size == 0){
#ifdef REXBACKLIGHT
fprintf(stderr, "No backlights devices found!\n");
#elif defined(REXLEDCTL)
fprintf(stderr, "No led devices found!\n");
#else
#error "UNDEFINED PROGRAM NAME!"
#endif
CLEANUP();
free_string_array(&device_names);
free_cmd_args(&args);
return RETVAL_INVALID_DEVICE;
}
//Make sure all explicit devices actually exist
for(curr = args.next;curr;curr = curr->next){
int i;
for(i = 0;i < device_names.size;++i){
for(struct arg_values* curr = args.next;curr;curr = curr->next){
for(int i = 0;i < device_names.size;++i){
if(!strcmp(curr->device, device_names.list[i])){
goto continue_outer;
}
}
fprintf(stderr, "No such device '%s'\n", curr->device);
CLEANUP();
free_string_array(&device_names);
free_cmd_args(&args);
return RETVAL_INVALID_DEVICE;
continue_outer:;
}
@@ -377,21 +370,21 @@ int main(int argc, char** argv){
//Change to the base directory for all sysfs leds/backlights
if(chdir(device_dir())){
io_error(IO_ERROR_READ, IO_ERROR_DIR, device_dir());
CLEANUP();
free_string_array(&device_names);
free_cmd_args(&args);
return RETVAL_INVALID_DIR;
}
//Run selected operation
if(args.next){
individual_device_loop(&args);
if(!args.next){
populate_args(&args, &device_names);
}
run_device_operations(&args);
#ifdef ENABLE_RESTORE_FILE
save_restore_file(&device_names);
#endif
}else{
all_device_loop(&device_names, &args);
}
CLEANUP();
free_string_array(&device_names);
free_cmd_args(&args);
return return_value;
}
#undef CLEANUP