Compare commits

...

47 Commits
v1.0 ... master

Author SHA1 Message Date
43542d84dc Merge branch 'master' of ssh://rexy712-server:1995/var/git/repos/rexy712/thinkpad_backlight 2022-01-30 20:43:09 -08:00
398146a0d8 Fix parsing of restore file 2022-01-30 20:42:12 -08:00
rexy712
b944ee8dec Version bump 2021-01-17 09:35:35 -08:00
rexy712
927160ba01 Fix restore file not working 2021-01-17 09:34:21 -08:00
rexy712
42dee2de78 Update to version 1.5.2 2021-01-03 10:01:25 -08:00
rexy712
61e36bed19 Fix error output on invalid permissions to write to brightness file 2021-01-03 09:54:49 -08:00
rexy712
50412e0006 Fix CMakeLists.txt 2020-04-09 15:22:05 -07:00
rexy712
19d736bc84 Fix usage of older rjp parse flag 2020-04-09 14:56:02 -07:00
rexy712
db3036e85b Merge branch 'master' of ssh://rexy712-server:1995/var/git/repos/rexy712/thinkpad_backlight 2020-04-09 14:39:01 -07:00
rexy712
26bd685296 Update to new rjp api 2020-04-09 14:38:52 -07:00
rexy712
fa187be8b0 Update CMakeLists to use rjp's pkg-config files 2020-03-07 13:19:34 -08:00
rexy712
bbce4dc8fe Updated version 2020-02-23 10:27:13 -08:00
rexy712
9c4425ff54 Update to new rjp api 2020-02-23 10:24:10 -08:00
rexy712
2e0ee84ca7 Add support for new rjp api 2019-11-09 21:18:00 -08:00
rexy712
f868074f0c please 2019-01-14 16:17:57 -08:00
rexy712
2fdd9fa13b fixed typo 2019-01-14 16:11:53 -08:00
rexy712
40eec1fb23 hope this fixes it... 2019-01-14 16:07:33 -08:00
rexy712
42f93ba026 really fixed version this time 2019-01-14 16:04:09 -08:00
rexy712
44ddaae28d fixed version output 2019-01-14 16:01:49 -08:00
rexy712
5986fa8133 Fixed version number 2019-01-14 15:55:33 -08:00
rexy712
479e7c1f17 Removed the cool git versioning thing because it won't work when distributed 2019-01-14 15:54:47 -08:00
rexy712
2dd52c32b7 Fixed memory leak when restore file is disabled and prevent restore file parsing when no write is performed 2018-12-16 15:42:22 -08:00
rexy712
87b7dd2503 Added option to not write to restore file and fixed a lot of restore file issues 2018-12-16 14:27:58 -08:00
rexy712
3c2e3f35a0 Version now includes dirty status on exact version tags too 2018-12-16 07:18:31 -08:00
rexy712
00062612f5 Cleaned up option management in cmd.c and moved a restore operation from rexbacklight.c to restore.c 2018-12-16 06:56:18 -08:00
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
rexy712
dbfa9b9d12 Made restoration files optional 2018-12-06 13:37:01 -08:00
rexy712
8244ebb50b moved restore file to user's home directory 2018-12-06 12:59:25 -08:00
rexy712
abb03dcd40 Moved restore file logic into its own file 2018-12-04 13:13:51 -08:00
rexy712
82369b4d67 changed restore file signature 2018-12-01 10:17:41 -08:00
rexy712
38793bf575 writing to restore file 2018-12-01 10:10:40 -08:00
rexy712
904abe3294 Now can read from predefined restore file at /var/tmp/rexbacklight.json 2018-12-01 08:36:56 -08:00
rexy712
e1b2bb8ce9 Update readme 2018-11-24 10:10:57 -08:00
rexy712
37ec303118 Improved cmake build efficiency 2018-11-24 09:41:09 -08:00
Rexy712
9d511f4635 updated version 2018-05-26 16:07:25 -07:00
Rexy712
4fd9f43e51 changed method of processing arguments to be a bit easier to maintain 2018-05-26 16:06:25 -07:00
Rexy712
0df81aa5ed fixed a small whitespace issue in a couple file headers 2018-05-26 09:36:46 -07:00
Rexy712
f475f18ce8 added xbacklight argument compatability option 2018-05-26 09:23:12 -07:00
Rexy712
078b9bf1e3 Fixed issue in help output. added readme 2018-05-18 14:03:41 -07:00
21 changed files with 947 additions and 254 deletions

2
.gitignore vendored
View File

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

View File

@ -1,17 +1,54 @@
include(CMakeDependentOption)
cmake_minimum_required(VERSION 3.0.2)
project(rexbacklight)
set(rexbacklight_VERSION_MAJOR 1)
set(rexbacklight_VERSION_MINOR 0)
cmake_minimum_required(VERSION 3.1)
include(CMakeDependentOption)
include(GNUInstallDirs)
set(SCRIPT_DIR ${CMAKE_SOURCE_DIR}/scripts)
#set project include directory
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
configure_file(
"${INCLUDE_PATH}/config.h.in"
"${INCLUDE_PATH}/config.h"
)
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 ${CMAKE_COMMAND} -E echo "#define GIT_TAG_NAME \"v1.5.3\"" > ${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 ${CMAKE_COMMAND} -E echo "#define GIT_TAG_NAME \"v1.5.3\"" > ${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)
option(ENABLE_RESTORE_FILE "Enable backlight restoration from generated save file" ON)
option(XBACKLIGHT_COMPAT "Use xbacklight style options (eg -get -inc -dec)" OFF)
CMAKE_DEPENDENT_OPTION(INSTALL_UDEV_LED_RULE "Install the udev rule to allow users of video group to control led devices" ON
"BUILD_REXLEDCTL" OFF)
CMAKE_DEPENDENT_OPTION(INSTALL_UDEV_BACKLIGHT_RULE "Install the udev rule to allow users of video group to control backlight devices" ON
@ -19,21 +56,56 @@ CMAKE_DEPENDENT_OPTION(INSTALL_UDEV_BACKLIGHT_RULE "Install the udev rule to all
set(UDEV_DIR "/etc/udev/rules.d" CACHE STRING "Set the output directory for udev rules")
mark_as_advanced(UDEV_DIR)
if(XBACKLIGHT_COMPAT)
add_definitions("-DXBACKLIGHT_COMPAT_OPTIONS")
endif()
#locate rjp library requirements
if(ENABLE_RESTORE_FILE)
find_package(PkgConfig REQUIRED)
pkg_check_modules(RJP REQUIRED rjp)
#temporary library (no actual library generated)
add_library(common_srcs OBJECT src/cmd.c src/common.c src/restore.c)
add_definitions("-DENABLE_RESTORE_FILE")
else()
add_library(common_srcs OBJECT src/cmd.c src/common.c)
endif()
target_sources(common_srcs PUBLIC ${GIT_VERSION_TMP_FILE})
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DREXBACKLIGHT_DEBUG")
#build led control program
if(BUILD_REXLEDCTL)
add_executable (rexledctl src/rexbacklight.c src/cmd.c src/common.c)
target_compile_definitions(rexledctl PRIVATE REXLEDCTL)
add_executable (rexledctl src/rexbacklight.c)
add_dependencies(rexledctl common_srcs) #force common_srcs to be built first
target_compile_definitions(rexledctl PRIVATE REXLEDCTL) #define REXLEDCTL in C files
target_link_libraries(rexledctl PRIVATE $<TARGET_OBJECTS:common_srcs>) #link with the common_srcs "library"
if(ENABLE_RESTORE_FILE)
target_link_libraries(rexledctl PRIVATE "${RJP_LIBRARIES}") #link with rjp
target_include_directories(rexledctl PUBLIC "${RJP_INCLUDE_DIRS}") #include rjp.h directory
target_compile_options(rexledctl PUBLIC ${RJP_CFLAGS_OTHER})
endif()
install(TARGETS rexledctl RUNTIME DESTINATION bin)
if(INSTALL_UDEV_LED_RULE)
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 src/cmd.c src/common.c)
add_executable (rexbacklight src/rexbacklight.c)
add_dependencies(rexbacklight common_srcs)
target_compile_definitions(rexbacklight PRIVATE REXBACKLIGHT)
target_link_libraries(rexbacklight PRIVATE $<TARGET_OBJECTS:common_srcs>)
if(ENABLE_RESTORE_FILE)
target_link_libraries(rexbacklight PRIVATE "${RJP_LIBRARIES}")
target_include_directories(rexbacklight PUBLIC "${RJP_INCLUDE_DIRS}")
target_compile_options(rexbacklight PUBLIC ${RJP_CFLAGS_OTHER})
endif()
install(TARGETS rexbacklight RUNTIME DESTINATION bin)
if(INSTALL_UDEV_BACKLIGHT_RULE)
install(FILES ${CMAKE_SOURCE_DIR}/rules/91-backlight.rules DESTINATION ${UDEV_DIR})
endif()
endif()
#uninstall target
add_custom_target(uninstall cat install_manifest.txt | xargs rm)

83
README.md Normal file
View File

@ -0,0 +1,83 @@
# README
## About
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.5.2 as of writing this, so if the version is much newer, remind me to update this readme.
```
rexbacklight version v1.5.2
Usage: rexbacklight [argument] [options] [argument]
Options:
--device|-d
select which device to control
--fade|-f
change brightness over time interval
--steps|-s
number of steps over which to fade
--get|-g
print current brightness level to stdout
--list|-l
print device names to stdout and exit
--restore|-R
reassign previously saved device values
--no-save|-N
do not write any data to the restore file
--help|-h
print this help message and exit
--version
print program version and exit
Arguments:
=<percentage>
-<percentage>
+<percentage>
off
max
min
rexbacklight Copyright (C) 2018-2021 rexy712
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions; see the GNU GPLv3 for details.
A copy of the GPLv3 is available with the source in the file 'LICENSE'
```
## Dependencies
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
```
mkdir build
cd build
ccmake ..
#or just 'cmake ..' and specify options on command line
make
```
## Installing
##### Run the following commands from the build directory after building
```
sudo make install
```
##### Reload the udev rules for them to take effect, otherwise just reboot
```
sudo udevadm control --reload-rules
sudo udevadm trigger
```
## Uninstalling
##### Run the following commands from the build directory
```
sudo make uninstall
```
[rjp]: https://gitlab.com/rexy712/rjp

2
TODO
View File

@ -1,7 +1,7 @@
#Individual device control
#List devices
#Query backlight percentage (get)
xbacklight compat option (-get/-set/-inc/-dec/-help/-time/-steps)
#xbacklight compat option (-get/-set/-inc/-dec/-help/-time/-steps)
#allow fade in and fade out
a lot of cleanup
#rename "backlight" things to "device" things because led/backlight ambiguity

View File

@ -1,6 +1,6 @@
/**
rexbacklight
Copyright (C) 2018 rexy712
Copyright (C) 2018-2021 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,21 +16,28 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RECBACKLIGHT_CMD_H
#ifndef REXBACKLIGHT_CMD_H
#define REXBACKLIGHT_CMD_H
#define OP_INC 1
#define OP_DEC 2
#define OP_SET 4
#define OP_LIST 8
#define OP_RESTORE 16
#define OP_GET 128
#define OP_NONE 0
#define OP_VERSION 254
#define OP_USAGE 255
#define ARG_FLAG_NONE 0
#define ARG_FLAG_NO_SAVE 1
struct cmd_arg{
const char* lopt;
const char* sopt;
#ifdef XBACKLIGHT_COMPAT_OPTIONS
const char* xopt;
#endif
const char* desc;
};
@ -44,7 +51,11 @@ struct arg_values{
//NULL means all devices
const char* device;
//What value to put in the backlight file
//starting brightness
int act_start;
//value to write in backlight file
int act_delta;
//output percentage
float delta;
//How many seconds to transition
@ -52,7 +63,11 @@ struct arg_values{
int fade_steps;
unsigned char operation;
union{
int operation;
int num_values;
};
int flags;
};

View File

@ -1,6 +1,6 @@
/**
rexbacklight
Copyright (C) 2018 rexy712
Copyright (C) 2018-2021 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -9,18 +9,22 @@
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program, If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#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
@ -29,11 +33,27 @@
#define RETVAL_MEM_ERROR 6
#define RETVAL_INTERNAL_ERROR 7
#define RETVAL_SUCCESS EXIT_SUCCESS
#define IO_ERROR_DIR "directory"
#define IO_ERROR_FILE "file"
#define IO_ERROR_OPEN "open"
#define IO_ERROR_READ "read"
#define IO_ERROR_WRITE "write to"
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);
#endif

35
include/config.h Normal file
View File

@ -0,0 +1,35 @@
/**
rexbacklight
Copyright (C) 2018-2021 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONFIG_H
#define CONFIG_H
#include "version.h"
#ifdef GIT_TAG_NAME
#ifdef GIT_DIRTY
#define REXBACKLIGHT_VERSION GIT_TAG_NAME "-dirty"
#else
#define REXBACKLIGHT_VERSION GIT_TAG_NAME
#endif
#else
#define REXBACKLIGHT_VERSION GIT_BRANCH_NAME "-" GIT_COMMIT_HASH
#endif
#endif

View File

@ -1,2 +0,0 @@
#define REXBACKLIGHT_VERSION_MAJOR 1
#define REXBACKLIGHT_VERSION_MINOR 0

28
include/globals.h Normal file
View File

@ -0,0 +1,28 @@
/**
rexbacklight
Copyright (C) 2018-2021 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLOBALS_H
#define GLOBALS_H
const char* device_dir(void);
const char* executable_name(void);
const char* restore_file_suffix(void);
const char* brightness_file(void);
const char* max_brightness_file(void);
#endif

33
include/restore.h Normal file
View File

@ -0,0 +1,33 @@
/**
rexbacklight
Copyright (C) 2018-2021 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RESTORE_H
#define RESTORE_H
#include <rjp.h>
#include "common.h"
#include "cmd.h"
void save_restore_file(struct string_array* devices, struct arg_values* args);
int restore_to_delta(struct arg_values* curr);
void prep_restore(struct arg_values* a);
RJP_value* find_matching_json_device(const char* name, RJP_value* root);
RJP_value* read_restore_file(const char* file);
#endif

27
include/rexbacklight.h Normal file
View File

@ -0,0 +1,27 @@
/**
rexbacklight
Copyright (C) 2018-2021 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REXBACKLIGHT_H
#define REXBACKLIGHT_H
#include "cmd.h"
float get_brightness(const char* file);
int individual_device_op(struct arg_values* curr);
#endif

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

10
scripts/git_commit_hash.py Executable file
View File

@ -0,0 +1,10 @@
#!/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 + '"')
if output.find("dirty") != -1:
print("#define GIT_DIRTY")

10
scripts/git_commit_hash.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/sh
output="$(git describe --always --dirty --abbrev --match="NeVeRmAtCh")"
if test -n output;then
echo "#define GIT_COMMIT_HASH \"$output\""
if grep -q "dirty" <<< "$output";then
echo "#define GIT_DIRTY"
fi
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

174
src/cmd.c
View File

@ -1,6 +1,6 @@
/**
rexbacklight
Copyright (C) 2018 rexy712
Copyright (C) 2018-2021 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,45 +16,104 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h> //fprintf
#include <string.h> //strcmp
#include <stdlib.h> //malloc, free, strtol, atof
#include "cmd.h"
#include "common.h"
#include "config.h"
#define NO_OPT NULL
#define GET_LONG_OPT "--get"
#define GET_SHORT_OPT "-g"
#define FADE_LONG_OPT "--fade"
#define FADE_SHORT_OPT "-f"
#define STEPS_LONG_OPT "--steps"
#define STEPS_SHORT_OPT "-s"
#define DEVICE_LONG_OPT "--device"
#define DEVICE_SHORT_OPT "-d"
#define LIST_LONG_OPT "--list"
#define LIST_SHORT_OPT "-l"
#define HELP_LONG_OPT "--help"
#define HELP_SHORT_OPT "-h"
#define HELP_LONG_OPT "--help"
#define HELP_SHORT_OPT "-h"
#define VERSION_LONG_OPT "--version"
#define NO_OPT NULL
#define GET_LONG_OPT "--get"
#define GET_SHORT_OPT "-g"
#define GET_XBACK_OPT "-get"
#define FADE_LONG_OPT "--fade"
#define FADE_SHORT_OPT "-f"
#define FADE_XBACK_OPT "-time"
#define STEPS_LONG_OPT "--steps"
#define STEPS_SHORT_OPT "-s"
#define STEPS_XBACK_OPT "-steps"
#define DEVICE_LONG_OPT "--device"
#define DEVICE_SHORT_OPT "-d"
#define DEVICE_XBACK_OPT "-display"
#define LIST_LONG_OPT "--list"
#define LIST_SHORT_OPT "-l"
#define LIST_XBACK_OPT NO_OPT
#define HELP_LONG_OPT "--help"
#define HELP_SHORT_OPT "-h"
#define HELP_XBACK_OPT "-help"
#define VERSION_LONG_OPT "--version"
#define VERSION_SHORT_OPT NO_OPT
#define VERSION_XBACK_OPT "-version"
#define SET_LONG_OPT NO_OPT
#define SET_SHORT_OPT NO_OPT
#define SET_XBACK_OPT "-set"
#define INC_LONG_OPT NO_OPT
#define INC_SHORT_OPT NO_OPT
#define INC_XBACK_OPT "-inc"
#define DEC_LONG_OPT NO_OPT
#define DEC_SHORT_OPT NO_OPT
#define DEC_XBACK_OPT "-dec"
#define RESTORE_LONG_OPT "--restore"
#define RESTORE_SHORT_OPT "-R"
#define RESTORE_XBACK_OPT NO_OPT
#define NO_SAVE_LONG_OPT "--no-save"
#define NO_SAVE_SHORT_OPT "-N"
#define NO_SAVE_XBACK_OPT NO_OPT
#define CHECK_SHORT_OPTION(opt, arg) (!strcmp(opt##_SHORT_OPT, arg))
#define CHECK_LONG_OPTION(opt, arg) (!strcmp(opt##_LONG_OPT, arg))
#define CHECK_OPTION(opt, arg) (!strcmp(opt##_LONG_OPT, arg) || !strcmp(opt##_SHORT_OPT, arg))
#define DEVICE_DESC "select which device to control"
#define FADE_DESC "change brightness over time interval"
#define STEPS_DESC "number of steps over which to fade"
#define GET_DESC "print current brightness level to stdout"
#define LIST_DESC "print device names to stdout and exit"
#define HELP_DESC "print this help message and exit"
#define VERSION_DESC "print program version and exit"
#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 device values"
#define NO_SAVE_DESC "do not write any data to the restore file"
static inline int strcmp_handle_null(const char* one, const char* two){
if(!one)
return 1;
if(!two)
return -1;
return strcmp(one, two);
}
#define CHECK_SHORT_OPTION(opt, arg) (!strcmp_handle_null(opt##_SHORT_OPT, arg))
#define CHECK_LONG_OPTION(opt, arg) (!strcmp_handle_null(opt##_LONG_OPT, arg))
#define CHECK_XBACK_OPTION(opt, arg) (!strcmp_handle_null(opt##_XBACK_OPT, arg))
#ifdef XBACKLIGHT_COMPAT_OPTIONS
#define OPTION(x) {x##_LONG_OPT, x##_SHORT_OPT, x##_XBACK_OPT, x##_DESC}
#define CHECK_OPTION(opt, arg) (CHECK_LONG_OPTION(opt, arg) || CHECK_SHORT_OPTION(opt, arg) || CHECK_XBACK_OPTION(opt, arg))
#else //XBACKLIGHT_COMPAT_OPTIONS
#define OPTION(x) {x##_LONG_OPT, x##_SHORT_OPT, x##_DESC}
#define CHECK_OPTION(opt, arg) (CHECK_LONG_OPTION(opt, arg) || CHECK_SHORT_OPTION(opt, arg))
#endif //XBACKLIGHT_COMPAT_OPTIONS
struct cmd_arg rexbacklight_args[] = {
{DEVICE_LONG_OPT, DEVICE_SHORT_OPT, "select which device to control"},
{FADE_LONG_OPT, FADE_SHORT_OPT, "change brightness over time interval"},
{STEPS_LONG_OPT, STEPS_SHORT_OPT, "number of steps over which to fade"},
{GET_LONG_OPT, GET_SHORT_OPT, "print current brightness level to stdout"},
{LIST_LONG_OPT, LIST_SHORT_OPT, "print device names to stdout and exit"},
{HELP_LONG_OPT, HELP_SHORT_OPT, "print this help message and exit"},
{VERSION_LONG_OPT, NO_OPT, "print program version and exit"}
OPTION(DEVICE),
OPTION(SET),
OPTION(INC),
OPTION(DEC),
OPTION(FADE),
OPTION(STEPS),
OPTION(GET),
OPTION(LIST),
#ifdef ENABLE_RESTORE_FILE
OPTION(RESTORE),
OPTION(NO_SAVE),
#endif
OPTION(HELP),
OPTION(VERSION)
};
int rexbacklight_args_length = sizeof(rexbacklight_args) / sizeof(rexbacklight_args[0]);
int rexbacklight_args_length = sizeof(rexbacklight_args) / sizeof(rexbacklight_args[0]);
\
//Clean up a cmd_arg struct
void free_cmd_args(struct arg_values* a){
if(!a->next)
@ -81,13 +140,14 @@ void free_cmd_args(struct arg_values* a){
}while(0);
struct arg_values init_arg_values(void){
return (struct arg_values){.next = NULL, .device = NULL, .delta = 0, .fade_duration = 0, .fade_steps = 20, .operation = 0};
return (struct arg_values){.next = NULL, .device = NULL, .delta = 0, .fade_duration = 0, .fade_steps = 20, .operation = 0, .flags = 0};
}
//Convert command line arguments to flags
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]
@ -95,7 +155,7 @@ struct arg_values process_cmd_args(int argc, char** argv){
//Check for switches
if(CHECK_OPTION(GET, argv[i])){
curr->operation |= OP_GET;
curr->operation = OP_GET;
continue;
}else if(CHECK_OPTION(FADE, argv[i])){
@ -122,23 +182,30 @@ struct arg_values process_cmd_args(int argc, char** argv){
*(curr->next) = init_arg_values();
curr = curr->next;
curr->device = argv[++i];
++nvals;
continue;
}
else if(CHECK_OPTION(LIST, argv[i])){
free_cmd_args(&ret);
ret.operation = OP_LIST;
ret.next = NULL;
return ret;
}
else if(CHECK_OPTION(HELP, argv[i])){
free_cmd_args(&ret);
return (struct arg_values){.operation = OP_USAGE};
}
else if(CHECK_LONG_OPTION(VERSION, argv[i])){
else if(CHECK_OPTION(VERSION, argv[i])){
free_cmd_args(&ret);
return (struct arg_values){.operation = OP_VERSION};
}
else if(CHECK_OPTION(LIST, argv[i])){
curr->operation = OP_LIST;
}
#ifdef ENABLE_RESTORE_FILE
else if(CHECK_OPTION(RESTORE, argv[i])){
curr->operation = OP_RESTORE;
}
else if(CHECK_OPTION(NO_SAVE, argv[i])){
curr->flags |= ARG_FLAG_NO_SAVE;
}
#endif
else if(!strcmp(argv[i], "max")){
curr->operation = OP_SET;
curr->delta = 100;
@ -151,6 +218,25 @@ struct arg_values process_cmd_args(int argc, char** argv){
curr->operation = OP_SET;
curr->delta = 0;
}
#ifdef XBACKLIGHT_COMPAT_OPTIONS
else if(CHECK_OPTION(SET, argv[i])){
CHECK_NEXT_ARG();
curr->operation = OP_SET;
curr->delta = atof(argv[++i]);
}
else if(CHECK_OPTION(INC, argv[i])){
CHECK_NEXT_ARG();
curr->operation = OP_INC;
curr->delta = atof(argv[++i]);
}
else if(CHECK_OPTION(DEC, argv[i])){
CHECK_NEXT_ARG();
curr->operation = OP_DEC;
curr->delta = atof(argv[++i]);
}
#endif //XBACKLIGHT_COMPAT_OPTIONS
else if(argv[i][0] == '='){
curr->operation = OP_SET;
@ -209,14 +295,19 @@ struct arg_values process_cmd_args(int argc, char** argv){
curr->operation = ret.operation;
if(!curr->delta)
curr->delta = ret.delta;
}
if(curr->flags == ARG_FLAG_NONE){
curr->flags = ret.flags;
}
}
}
if(ret.next)
ret.num_values = nvals;
return ret;
}
#undef CHECK_OPTION
#undef UNRECOGNIZED_OPTION
#undef OPTION
//Process an operation
int process_op(struct arg_values* arg, float min, float current, float max){
@ -251,3 +342,4 @@ int process_op(struct arg_values* arg, float min, float current, float max){
return current;
}

View File

@ -1,6 +1,6 @@
/**
rexbacklight
Copyright (C) 2018 rexy712
Copyright (C) 2018-2021 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,32 +19,71 @@
#include "config.h"
#include "common.h"
#include "cmd.h"
#include "globals.h"
#include <stdio.h>
#include <stdio.h> //printf
#include <stdlib.h> //exit
int return_value = RETVAL_SUCCESS;
void io_error(const char* error, const char* type, const char* name){
fprintf(stderr, "Unable to %s %s '%s'!\n", error, type, name);
}
void io_error_2(const char* error, const char* type, const char* name, const char* name2){
fprintf(stderr, "Unable to %s %s '%s%s'!\n", error, type, name, name2);
}
void io_error_3(const char* error, const char* type, const char* name, const char* name2, const char* name3){
fprintf(stderr, "Unable to %s %s '%s%s%s'!\n", error, type, name, name2, name3);
}
void mem_error(void){
fprintf(stderr, "Failed to allocate memory! Unable to continue!\n");
}
//name of the program being run so that we print the correct name in the usage
extern const char* executable_name;
//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);
}
_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("Usage: %s [argument] [options] [argument]\n\n", executable_name);
printf("%s version %s\n\n", executable_name(), REXBACKLIGHT_VERSION);
printf("Usage: %s [argument] [options] [argument]\n\n", executable_name());
printf("Options:\n");
for(i = 0;i < rexbacklight_args_length;++i){
printf(" %s|%s\n", rexbacklight_args[i].lopt, rexbacklight_args[i].sopt);
printf(" %s\n", rexbacklight_args[i].desc);
int printed = 0;
if(rexbacklight_args[i].lopt){
printf("%s", rexbacklight_args[i].lopt);
printed = 1;
}
if(rexbacklight_args[i].sopt){
if(printed)
printf("|");
else
printed = 1;
printf("%s", rexbacklight_args[i].sopt);
}
#ifdef XBACKLIGHT_COMPAT_OPTIONS
if(rexbacklight_args[i].xopt){
if(printed)
printf("|");
else
printed = 1;
printf("%s", rexbacklight_args[i].xopt);
}
#endif //XBACKLIGHT_COMPAT_OPTIONS
if(printed){
printf("\n");
printf(" %s\n", rexbacklight_args[i].desc);
}
}
printf("\n");
printf("Arguments:\n");
@ -55,7 +94,7 @@ _Noreturn void usage(int exit_val){
printf(" max\n");
printf(" min\n");
printf("\n%s Copyright (C) 2018 rexy712\n", executable_name);
printf("\n%s Copyright (C) 2018-2021 rexy712\n", executable_name());
printf("This program comes with ABSOLUTELY NO WARRANTY.\n");
printf("This is free software, and you are welcome to redistribute it\n");
printf("under certain conditions; see the GNU GPLv3 for details.\n");

173
src/restore.c Normal file
View File

@ -0,0 +1,173 @@
/**
rexbacklight
Copyright (C) 2018-2021 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h> //printf
#include <string.h> //strcmp, strlen
#include <rjp.h>
#include <sys/types.h> //getpwnam
#include <pwd.h> //getpwnam
#include <unistd.h> //chdir
#include <stdlib.h> //malloc, free
#include "restore.h"
#include "common.h"
#include "cmd.h"
#include "globals.h"
#include "rexbacklight.h"
static char* get_home_folder(void){
char* user = getenv("SUDO_USER");
char* home = getenv("HOME");
if(!home || user){
if(!user){
return NULL;
}
struct passwd* pw_entry = getpwnam(user);
if(!pw_entry){
return NULL;
}
home = pw_entry->pw_dir;
}
return home;
}
static char* restore_file(void){
char* home_folder = get_home_folder();
if(!home_folder)
return NULL;
//"/.config/rexbacklight.json"
size_t home_folder_len = strlen(home_folder);
size_t conf_len = strlen(restore_file_suffix());
char* rest_file = malloc(home_folder_len + conf_len + 1);
strncpy(rest_file, home_folder, home_folder_len);
strncpy(rest_file+home_folder_len, restore_file_suffix(), conf_len);
rest_file[home_folder_len+conf_len] = 0;
return rest_file;
}
RJP_value* read_restore_file(const char* file){
size_t filesize;
char* file_contents;
int i;
FILE* fp = fopen(file, "r");
if(!fp){
return NULL;
}
fseek(fp, 0, SEEK_END);
filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);
file_contents = malloc(filesize+1);
i = fread(file_contents, filesize, 1, fp);
fclose(fp);
file_contents[filesize] = 0;
RJP_value* root = rjp_parse(file_contents, RJP_PARSE_ALLOW_COMMENTS, NULL);
free(file_contents);
return root;
}
static RJP_value* restore_file_handle(void){
static RJP_value* rf = NULL;
if(!rf){
char* f = restore_file();
rf = read_restore_file(f);
free(f);
}
return rf;
}
RJP_value* find_matching_json_device(const char* name, RJP_value* root){
RJP_object_iterator it;
rjp_init_object_iterator(&it, root);
for(RJP_value* curr = rjp_object_iterator_current(&it);curr;curr = rjp_object_iterator_next(&it)){
if(!strcmp(rjp_member_key(curr)->value, name)){
return curr;
}
}
return NULL;
}
//convert a restoration operation to a set operation
int restore_to_delta(struct arg_values* curr){
RJP_value* root = restore_file_handle();
if(!root){
fprintf(stderr, "Could not restore! No restore file found!\n");
return 0;
}
RJP_value* match = find_matching_json_device(curr->device, root);
if(!match){
fprintf(stderr, "No matching device '%s' in restore file!\n", curr->device);
return 0;
}
curr->operation = OP_SET;
curr->delta = rjp_get_float(match);
return 1;
}
void prep_restore(struct arg_values* a){
for(struct arg_values* curr = a;curr;curr = curr->next){
restore_to_delta(curr);
}
}
void save_restore_file(struct string_array* devices, struct arg_values* args){
RJP_object_iterator it;
RJP_value* rf = restore_file_handle();
if(!rf)
rf = rjp_new_object();
rjp_init_object_iterator(&it, rf);
for(RJP_value* mem = rjp_object_iterator_current(&it);mem;mem = rjp_object_iterator_next(&it)){
for(struct arg_values* curr = args->next, *prev = args;curr;prev = curr, curr = curr->next){
if(curr->operation != OP_SET || curr->flags & ARG_FLAG_NO_SAVE){
prev->next = curr->next;
free(curr);
curr = prev;
continue;
}
if(!strcmp(rjp_member_key(mem)->value, curr->device)){
rjp_set_float(mem, curr->delta);
prev->next = curr->next;
free(curr);
break;
}
}
}
for(struct arg_values* curr = args->next;curr;curr = curr->next){
if(curr->operation == OP_SET){
RJP_value* newmem = rjp_new_member(rf, curr->device, 0);
rjp_set_float(newmem, curr->delta);
}
}
free_cmd_args(args);
char* tmp = rjp_to_json(rf, RJP_FORMAT_NONE);
char* rfil = restore_file();
FILE* restf = fopen(rfil, "w");
if(!restf){
io_error(IO_ERROR_OPEN, IO_ERROR_FILE, rfil);
}else{
fprintf(restf, "//File generated by %s\n%s\n", executable_name(), tmp);
fclose(restf);
}
free(rfil);
rjp_free(tmp);
rjp_free_value(rf);
}

View File

@ -1,6 +1,6 @@
/**
rexbacklight
Copyright (C) 2018 rexy712
Copyright (C) 2018-2021 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,64 +16,60 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//Needed for usleep to work
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h> //fprintf
#include <stdlib.h> //malloc, free
#include <string.h> //strlen, strcpy
#include <unistd.h> //chdir
#include <sys/types.h> //opendir
#include <dirent.h> //opendir
#include <sys/time.h> //gettimeofday
#include <time.h> //nanosleep
#include "common.h"
#include "cmd.h"
#include "globals.h"
#include "config.h"
#ifdef ENABLE_RESTORE_FILE
#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
static const char* device_dir = "/sys/class/backlight/";
const char* executable_name = "rexbacklight";
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
static const char* device_dir = "/sys/class/leds";
const char* executable_name = "rexledctl";
const char* device_dir(void){return "/sys/class/leds/";}
const char* executable_name(void){return "rexledctl";}
const char* restore_file_suffix(void){return "/.config/rexledctl.json";}
#else
#error "UNDEFINED PROGRAM NAME!"
#endif
static const char* brightness_file = "brightness";
static const char* max_brightness_file = "max_brightness";
//Used to store a dynamic array of dynamic strings and number of strings
struct string_array{
char** list;
int size;
};
const char* brightness_file(void){return "brightness";}
const char* max_brightness_file(void){return "max_brightness";}
#define IO_ERROR_DIR "directory"
#define IO_ERROR_FILE "file"
#define IO_ERROR_OPEN "open"
#define IO_ERROR_READ "read"
#define IO_ERROR_WRITE "write to"
void io_error(const char* error, const char* type, const char* name){
fprintf(stderr, "Unable to %s %s '%s'!\n", error, type, name);
}
void io_error_2(const char* error, const char* type, const char* name, const char* name2){
fprintf(stderr, "Unable to %s %s '%s/%s'!\n", error, type, name, name2);
}
void io_error_3(const char* error, const char* type, const char* name, const char* name2, const char* name3){
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);
//create a copy of a string and append a '/' character to the end
char* add_slash_to(const char* name, size_t namelen){
char* newname = malloc(namelen + 2);
memcpy(newname, name, namelen);
newname[namelen++] = '/';
newname[namelen] = 0;
return newname;
}
//Get a list of led/backlight devices in sysfs
@ -83,9 +79,9 @@ struct string_array get_device_sources(void){
struct string_array arr = {0};
int list_size = 8;
fd = opendir(device_dir);
fd = opendir(device_dir());
if(!fd){
io_error(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir);
io_error(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir());
return_value = RETVAL_INVALID_DIR;
return arr;
}
@ -98,23 +94,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;
if(!strncmp(dir->d_name, ".", 1) || !strncmp(dir->d_name, "..", 2)){
//ignore '.' and '..'
if(!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")){
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);
@ -124,16 +121,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;
}
@ -158,150 +156,175 @@ float get_brightness(const char* file){
return atof(buff);
}
//update brightness incrementally over requested millisecond time interval
void fade_out(const char* device_name, int iv, int fv, int ms_duration, int steps){
FILE* fd;
struct timeval 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
//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);
}
while(ms_duration > tdelta){
void return_to_root_dir(void){
if(chdir(device_dir())){
io_error(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir());
return_value = RETVAL_INVALID_DIR;
}
}
//update brightness incrementally over requested millisecond time interval
void fade_out(struct arg_values* arg){
FILE* fd;
double start, end;
double tdelta = 0; //amount of time that has passed in ms
float value = arg->act_start; //current value to write to file
double step_delta = (double)arg->fade_duration / arg->fade_steps;
double step_inc = step_delta;
float brdelta = (float)(arg->act_delta - arg->act_start) / arg->fade_steps; //amount brightness needs to change per iteration
while(arg->fade_duration > tdelta){
//write
gettimeofday(&start, NULL);
fd = fopen(brightness_file, "w+");
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);
char* devname = add_slash_to(arg->device, strlen(arg->device));
io_error_3(IO_ERROR_OPEN, IO_ERROR_FILE, device_dir(), devname, brightness_file());
return_value = RETVAL_INVALID_FILE;
free(devname);
return_to_root_dir();
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
step_delta += step_inc;
}
fd = fopen(brightness_file, "w+");
fd = fopen(brightness_file(), "w+");
if(!fd){
io_error_3(IO_ERROR_OPEN, IO_ERROR_FILE, device_dir, device_name, brightness_file);
char* devname = add_slash_to(arg->device, strlen(arg->device));
io_error_3(IO_ERROR_OPEN, IO_ERROR_FILE, device_dir(), devname, brightness_file());
return_value = RETVAL_INVALID_FILE;
free(devname);
return_to_root_dir();
return;
}
fprintf(fd, "%d", fv);
fprintf(fd, "%d", arg->act_delta);
fclose(fd);
return_to_root_dir();
}
//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);
int prep_offset(struct arg_values* args){
if(chdir(args->device)){
io_error_2(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir(), args->device);
return RETVAL_INVALID_DIR;
}
args->act_start = get_brightness(brightness_file());
float act_max = get_brightness(max_brightness_file());
args->act_delta = process_op(args, 0, args->act_start, act_max);
float start_pec = (args->act_start / act_max) * 100.0;
if(args->operation == OP_INC){
args->delta = start_pec + args->delta;
}else if(args->operation == OP_DEC){
args->delta = start_pec - args->delta;
}
if(args->delta > 100)
args->delta = 100;
else if(args->delta < 0)
args->delta = 0;
args->operation = OP_SET;
//chdir back in do_assignment
return 0;
}
//Run get operation
void do_get(const char* device){
int do_get(struct arg_values* args){
float curr, max;
curr = get_brightness(brightness_file);
max = get_brightness(max_brightness_file);
if(chdir(args->device)){
io_error_2(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir(), args->device);
return RETVAL_INVALID_DIR;
}
curr = get_brightness(brightness_file());
max = get_brightness(max_brightness_file());
if(return_value == RETVAL_INVALID_FILE && (!curr || !max)){
fprintf(stdout, "%s", device);
fprintf(stdout, "%s", args->device);
}else{
fprintf(stdout, "%-25s %.2f\n", device, get_brightness(brightness_file) / get_brightness(max_brightness_file) * 100);
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 string_array* names){
int i;
for(i = 0;i < names->size;++i)
fprintf(stdout, "%s\n", names->list[i]);
void do_list(struct arg_values* args){
printf("%s\n", args->device);
}
//If devices were specified, this function will run
void individual_device_loop(struct arg_values* a){
struct arg_values* curr;
for(curr = a->next;curr;curr = curr->next){
if(chdir(curr->device)){
io_error_2(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir, curr->device);
return_value = RETVAL_INVALID_DIR;
continue;
}
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_value = RETVAL_INVALID_DIR;
return;
}
}
}
//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;
}
}
break;
//Run requested operation on a per device basis
int run_device_operations(struct arg_values* a){
int changes = 0;
for(a = a->next;a;a = a->next){
switch(a->operation){
case OP_LIST:
do_list(device_names);
do_list(a);
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;
}
}
return_value = do_get(a);
break;
#ifdef ENABLE_RESTORE_FILE
case OP_RESTORE:
if(!restore_to_delta(a)){
continue;
}
#endif
case OP_INC:
case OP_DEC:
case OP_SET:
if(prep_offset(a))
break;
++changes;
fade_out(a);
break;
};
}
return changes;
}
//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->next->flags = arg->flags;
curr = curr->next;
}
curr->next = NULL;
arg->num_values = devices->size;
}
@ -309,7 +332,8 @@ 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;
int should_save = 0;
args = process_cmd_args(argc, argv);
if(args.operation == OP_USAGE){
usage(return_value);
@ -319,53 +343,54 @@ 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");
fprintf(stderr, "No backlight 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:;
}
//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();
if(chdir(device_dir())){
io_error(IO_ERROR_READ, IO_ERROR_DIR, device_dir());
free_string_array(&device_names);
free_cmd_args(&args);
return RETVAL_INVALID_DIR;
}
//Run selected operation
if(args.next){
individual_device_loop(&args);
}else{
all_device_loop(&device_names, &args);
//on a global operation, populate list with all devices
if(!args.next){
populate_args(&args, &device_names);
}
should_save = (run_device_operations(&args) > 0);
#ifdef ENABLE_RESTORE_FILE
if(should_save)
save_restore_file(&device_names, &args); //performs cleanup on args
else
free_cmd_args(&args);
#else
free_cmd_args(&args);
#endif
CLEANUP();
free_string_array(&device_names);
return return_value;
}
#undef CLEANUP