Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 43542d84dc | |||
| 398146a0d8 | |||
|
|
b944ee8dec | ||
|
|
927160ba01 | ||
|
|
42dee2de78 | ||
|
|
61e36bed19 | ||
|
|
50412e0006 | ||
|
|
19d736bc84 | ||
|
|
db3036e85b | ||
|
|
26bd685296 | ||
|
|
fa187be8b0 | ||
|
|
bbce4dc8fe | ||
|
|
9c4425ff54 | ||
|
|
2e0ee84ca7 | ||
|
|
f868074f0c | ||
|
|
2fdd9fa13b | ||
|
|
40eec1fb23 | ||
|
|
42f93ba026 | ||
|
|
44ddaae28d | ||
|
|
5986fa8133 | ||
|
|
479e7c1f17 | ||
|
|
2dd52c32b7 | ||
|
|
87b7dd2503 | ||
|
|
3c2e3f35a0 | ||
|
|
00062612f5 |
@ -1,6 +1,7 @@
|
||||
include(CMakeDependentOption)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(rexbacklight)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
include(CMakeDependentOption)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(SCRIPT_DIR ${CMAKE_SOURCE_DIR}/scripts)
|
||||
|
||||
@ -23,9 +24,7 @@ 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 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}
|
||||
@ -36,9 +35,7 @@ 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 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}
|
||||
@ -65,8 +62,8 @@ endif()
|
||||
|
||||
#locate rjp library requirements
|
||||
if(ENABLE_RESTORE_FILE)
|
||||
find_library(RJP_LIB rjp)
|
||||
find_path(RJP_HEADER_DIR rjp.h)
|
||||
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")
|
||||
@ -82,11 +79,12 @@ if(BUILD_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
|
||||
if(ENABLE_RESTORE_FILE)
|
||||
target_link_libraries(rexledctl PRIVATE "${RJP_LIB}") #link with rjp
|
||||
target_include_directories(rexledctl PUBLIC "${RJP_HEADER_DIR}") #include rjp.h directory
|
||||
endif()
|
||||
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})
|
||||
@ -95,13 +93,14 @@ endif()
|
||||
#build backlight control program
|
||||
if(BUILD_REXBACKLIGHT)
|
||||
add_executable (rexbacklight src/rexbacklight.c)
|
||||
add_dependencies(rexledctl common_srcs)
|
||||
add_dependencies(rexbacklight common_srcs)
|
||||
target_compile_definitions(rexbacklight PRIVATE REXBACKLIGHT)
|
||||
if(ENABLE_RESTORE_FILE)
|
||||
target_link_libraries(rexbacklight PRIVATE "${RJP_LIB}")
|
||||
target_include_directories(rexbacklight PUBLIC "${RJP_HEADER_DIR}")
|
||||
endif()
|
||||
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})
|
||||
|
||||
@ -7,9 +7,11 @@ I've also since added a program to control LED devices, rexledctl (best name I c
|
||||
|
||||
Either program can be built alone or both together. This is configured using some cmake magic.
|
||||
|
||||
Current version is 1.3.1 as of writing this, so if the version is much newer, remind me to update this readme.
|
||||
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:
|
||||
@ -25,6 +27,8 @@ Options:
|
||||
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
|
||||
@ -38,7 +42,7 @@ Arguments:
|
||||
max
|
||||
min
|
||||
|
||||
rexbacklight Copyright (C) 2018 rexy712
|
||||
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.
|
||||
|
||||
@ -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
|
||||
@ -29,6 +29,9 @@
|
||||
#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;
|
||||
@ -48,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
|
||||
@ -60,6 +67,7 @@ struct arg_values{
|
||||
int operation;
|
||||
int num_values;
|
||||
};
|
||||
int flags;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -22,7 +22,11 @@
|
||||
#include "version.h"
|
||||
|
||||
#ifdef GIT_TAG_NAME
|
||||
#define REXBACKLIGHT_VERSION 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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -24,8 +24,9 @@
|
||||
#include "common.h"
|
||||
#include "cmd.h"
|
||||
|
||||
void save_restore_file(struct string_array* devices);
|
||||
int restore_to_delta(struct arg_values* curr, RJP_value** root, int* try_restore);
|
||||
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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -6,3 +6,5 @@ output = subprocess.run(['git', 'describe', "--always", "--dirty", "--abbrev", "
|
||||
|
||||
if output:
|
||||
print("#define GIT_COMMIT_HASH " + '"' + output + '"')
|
||||
if output.find("dirty") != -1:
|
||||
print("#define GIT_DIRTY")
|
||||
|
||||
@ -4,4 +4,7 @@ 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
|
||||
|
||||
88
src/cmd.c
88
src/cmd.c
@ -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
|
||||
@ -58,6 +58,9 @@
|
||||
#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 DEVICE_DESC "select which device to control"
|
||||
#define FADE_DESC "change brightness over time interval"
|
||||
@ -70,8 +73,9 @@
|
||||
#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"
|
||||
|
||||
int strcmp_handle_null(const char* one, const char* two){
|
||||
static inline int strcmp_handle_null(const char* one, const char* two){
|
||||
if(!one)
|
||||
return 1;
|
||||
if(!two)
|
||||
@ -79,48 +83,37 @@ int strcmp_handle_null(const char* one, const char* two){
|
||||
return strcmp(one, two);
|
||||
}
|
||||
|
||||
#define CHECK_SHORT_OPTION(opt, arg) (!strcmp(opt##_SHORT_OPT, arg))
|
||||
#define CHECK_LONG_OPTION(opt, arg) (!strcmp(opt##_LONG_OPT, arg))
|
||||
#define CHECK_XBACK_OPTION(opt, arg) (!strcmp(opt##_XBACK_OPT, arg))
|
||||
#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 CHECK_OPTION(opt, arg) (!strcmp_handle_null(opt##_LONG_OPT, arg) || !strcmp_handle_null(opt##_SHORT_OPT, arg) || !strcmp_handle_null(opt##_XBACK_OPT, arg))
|
||||
struct cmd_arg rexbacklight_args[] = {
|
||||
{DEVICE_LONG_OPT, DEVICE_SHORT_OPT, DEVICE_XBACK_OPT, DEVICE_DESC},
|
||||
{NO_OPT, NO_OPT, SET_XBACK_OPT, SET_DESC},
|
||||
{NO_OPT, NO_OPT, INC_XBACK_OPT, INC_DESC},
|
||||
{NO_OPT, NO_OPT, DEC_XBACK_OPT, DEC_DESC},
|
||||
{FADE_LONG_OPT, FADE_SHORT_OPT, FADE_XBACK_OPT, FADE_DESC},
|
||||
{STEPS_LONG_OPT, STEPS_SHORT_OPT, STEPS_XBACK_OPT, STEPS_DESC},
|
||||
{GET_LONG_OPT, GET_SHORT_OPT, GET_XBACK_OPT, GET_DESC},
|
||||
{LIST_LONG_OPT, LIST_SHORT_OPT, LIST_XBACK_OPT, LIST_DESC},
|
||||
#ifdef ENABLE_RESTORE_FILE
|
||||
{RESTORE_LONG_OPT, RESTORE_SHORT_OPT, RESTORE_XBACK_OPT, RESTORE_DESC},
|
||||
#endif
|
||||
{HELP_LONG_OPT, HELP_SHORT_OPT, HELP_XBACK_OPT, HELP_DESC},
|
||||
{VERSION_LONG_OPT, NO_OPT, VERSION_XBACK_OPT, VERSION_DESC}
|
||||
};
|
||||
|
||||
#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 CHECK_OPTION(opt, arg) (!strcmp_handle_null(opt##_LONG_OPT, arg) || !strcmp_handle_null(opt##_SHORT_OPT, arg))
|
||||
struct cmd_arg rexbacklight_args[] = {
|
||||
{DEVICE_LONG_OPT, DEVICE_SHORT_OPT, DEVICE_DESC},
|
||||
{FADE_LONG_OPT, FADE_SHORT_OPT, FADE_DESC},
|
||||
{STEPS_LONG_OPT, STEPS_SHORT_OPT, STEPS_DESC},
|
||||
{GET_LONG_OPT, GET_SHORT_OPT, GET_DESC},
|
||||
{LIST_LONG_OPT, LIST_SHORT_OPT, LIST_DESC},
|
||||
#ifdef ENABLE_RESTORE_FILE
|
||||
{RESTORE_LONG_OPT, RESTORE_SHORT_OPT, RESTORE_DESC},
|
||||
#endif
|
||||
{HELP_LONG_OPT, HELP_SHORT_OPT, HELP_DESC},
|
||||
{VERSION_LONG_OPT, NO_OPT, VERSION_DESC}
|
||||
};
|
||||
#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
|
||||
|
||||
int rexbacklight_args_length = sizeof(rexbacklight_args) / sizeof(rexbacklight_args[0]);
|
||||
struct cmd_arg rexbacklight_args[] = {
|
||||
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]);
|
||||
\
|
||||
//Clean up a cmd_arg struct
|
||||
void free_cmd_args(struct arg_values* a){
|
||||
if(!a->next)
|
||||
@ -147,7 +140,7 @@ 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
|
||||
@ -162,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])){
|
||||
@ -203,14 +196,14 @@ struct arg_values process_cmd_args(int argc, char** argv){
|
||||
}
|
||||
|
||||
else if(CHECK_OPTION(LIST, argv[i])){
|
||||
free_cmd_args(&ret);
|
||||
ret.operation = OP_LIST;
|
||||
ret.next = NULL;
|
||||
return ret;
|
||||
curr->operation = OP_LIST;
|
||||
}
|
||||
#ifdef ENABLE_RESTORE_FILE
|
||||
else if(CHECK_OPTION(RESTORE, argv[i])){
|
||||
ret.operation = OP_RESTORE;
|
||||
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")){
|
||||
@ -302,7 +295,9 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -312,6 +307,7 @@ struct arg_values process_cmd_args(int argc, char** argv){
|
||||
}
|
||||
#undef CHECK_OPTION
|
||||
#undef UNRECOGNIZED_OPTION
|
||||
#undef OPTION
|
||||
|
||||
//Process an operation
|
||||
int process_op(struct arg_values* arg, float min, float current, float max){
|
||||
|
||||
19
src/common.c
19
src/common.c
@ -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
|
||||
@ -35,6 +35,9 @@ void io_error_2(const char* error, const char* type, const char* name, const cha
|
||||
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");
|
||||
}
|
||||
|
||||
//Clean up a string array
|
||||
void free_string_array(struct string_array* s){
|
||||
@ -44,10 +47,6 @@ void free_string_array(struct string_array* s){
|
||||
free(s->list);
|
||||
}
|
||||
|
||||
void mem_error(void){
|
||||
fprintf(stderr, "Failed to allocate memory! Unable to continue!\n");
|
||||
}
|
||||
|
||||
_Noreturn void version(void){
|
||||
printf("%s version %s\n", executable_name(), REXBACKLIGHT_VERSION);
|
||||
exit(return_value);
|
||||
@ -76,11 +75,15 @@ _Noreturn void usage(int exit_val){
|
||||
if(rexbacklight_args[i].xopt){
|
||||
if(printed)
|
||||
printf("|");
|
||||
else
|
||||
printed = 1;
|
||||
printf("%s", rexbacklight_args[i].xopt);
|
||||
}
|
||||
#endif //XBACKLIGHT_COMPAT_OPTIONS
|
||||
printf("\n");
|
||||
printf(" %s\n", rexbacklight_args[i].desc);
|
||||
if(printed){
|
||||
printf("\n");
|
||||
printf(" %s\n", rexbacklight_args[i].desc);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
printf("Arguments:\n");
|
||||
@ -91,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");
|
||||
|
||||
102
src/restore.c
102
src/restore.c
@ -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
|
||||
@ -66,7 +66,6 @@ RJP_value* read_restore_file(const char* file){
|
||||
int i;
|
||||
FILE* fp = fopen(file, "r");
|
||||
if(!fp){
|
||||
fprintf(stderr, "Could not restore! No restore file found!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -78,14 +77,26 @@ RJP_value* read_restore_file(const char* file){
|
||||
i = fread(file_contents, filesize, 1, fp);
|
||||
fclose(fp);
|
||||
file_contents[filesize] = 0;
|
||||
RJP_value* root = rjp_parse(file_contents);
|
||||
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){
|
||||
for(RJP_value* curr = rjp_get_member(root);curr;curr = rjp_next_member(curr)){
|
||||
if(!strcmp(rjp_member_name(curr), name)){
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -93,55 +104,58 @@ RJP_value* find_matching_json_device(const char* name, RJP_value* root){
|
||||
}
|
||||
|
||||
//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);
|
||||
free(tmp);
|
||||
if(!*root){
|
||||
*try_restore = 0;
|
||||
return 0;
|
||||
}
|
||||
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);
|
||||
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_value_dfloat(match);
|
||||
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){
|
||||
RJP_value* root = rjp_init_json();
|
||||
for(size_t i = 0;i < devices->size;++i){
|
||||
if(chdir(devices->list[i])){
|
||||
io_error_2(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir(), devices->list[i]);
|
||||
return_value = RETVAL_INVALID_DIR;
|
||||
continue;
|
||||
}
|
||||
size_t esc_name_len = rjp_escape_strlen(devices->list[i]);
|
||||
char* esc_str = rjp_alloc(esc_name_len + 1);
|
||||
rjp_escape_strcpy(esc_str, devices->list[i]);
|
||||
|
||||
float curr = get_brightness(brightness_file());
|
||||
float max = get_brightness(max_brightness_file());
|
||||
if(return_value == RETVAL_INVALID_FILE && (!curr || !max)){
|
||||
rjp_add_member(root, 0, esc_str, 0, rjp_null());
|
||||
}else{
|
||||
rjp_add_member(root, 0, esc_str, 0, rjp_dfloat(curr/max*100));
|
||||
}
|
||||
|
||||
if(chdir(device_dir())){
|
||||
io_error(IO_ERROR_OPEN, IO_ERROR_DIR, device_dir());
|
||||
return_value = RETVAL_INVALID_DIR;
|
||||
rjp_free_value(root);
|
||||
free(esc_str);
|
||||
return;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
char* tmp = rjp_to_json(root);
|
||||
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){
|
||||
@ -152,7 +166,7 @@ void save_restore_file(struct string_array* devices){
|
||||
}
|
||||
free(rfil);
|
||||
rjp_free(tmp);
|
||||
rjp_free_value(root);
|
||||
rjp_free_value(rf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
@ -31,30 +31,47 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef ENABLE_RESTORE_FILE
|
||||
#include <rjp.h>
|
||||
#include "restore.h"
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#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
|
||||
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
|
||||
|
||||
const char* brightness_file(void){return "brightness";}
|
||||
const char* max_brightness_file(void){return "max_brightness";}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//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
|
||||
struct string_array get_device_sources(void){
|
||||
DIR* fd;
|
||||
@ -80,7 +97,7 @@ struct string_array get_device_sources(void){
|
||||
for(arr.size = 0;(dir = readdir(fd));){
|
||||
int slen;
|
||||
//ignore '.' and '..'
|
||||
if(!strncmp(dir->d_name, ".", 1) || !strncmp(dir->d_name, "..", 2)){
|
||||
if(!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")){
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -138,6 +155,7 @@ float get_brightness(const char* file){
|
||||
fclose(bright);
|
||||
return atof(buff);
|
||||
}
|
||||
|
||||
//return milliseconds since last boot
|
||||
double get_time(void){
|
||||
struct timespec t;
|
||||
@ -153,23 +171,32 @@ void sleep_for(double time){
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
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, int iv, int fv){
|
||||
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 = iv; //current value to write to file
|
||||
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)(fv - iv) / arg->fade_steps; //amount brightness needs to change per iteration
|
||||
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
|
||||
start = get_time();
|
||||
fd = fopen(brightness_file(), "w+");
|
||||
if(!fd){
|
||||
io_error_3(IO_ERROR_OPEN, IO_ERROR_FILE, device_dir(), arg->device, 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);
|
||||
@ -192,122 +219,95 @@ void fade_out(struct arg_values* arg, int iv, int fv){
|
||||
}
|
||||
fd = fopen(brightness_file(), "w+");
|
||||
if(!fd){
|
||||
io_error_3(IO_ERROR_OPEN, IO_ERROR_FILE, device_dir(), arg->device, 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
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
return return_value;
|
||||
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
|
||||
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;
|
||||
}
|
||||
float curr, max;
|
||||
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", 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;
|
||||
printf("%s\n", args->device);
|
||||
}
|
||||
|
||||
//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(a);
|
||||
break;
|
||||
case OP_GET:
|
||||
return_value = do_get(a);
|
||||
break;
|
||||
#ifdef ENABLE_RESTORE_FILE
|
||||
//convert all restore operations to set operations
|
||||
void prep_restore(struct arg_values* a){
|
||||
RJP_value* root = 0;
|
||||
int try_restore = 1;
|
||||
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)
|
||||
case OP_RESTORE:
|
||||
if(!restore_to_delta(a)){
|
||||
continue;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
case OP_INC:
|
||||
case OP_DEC:
|
||||
case OP_SET:
|
||||
if(prep_offset(a))
|
||||
break;
|
||||
++changes;
|
||||
fade_out(a);
|
||||
break;
|
||||
};
|
||||
}
|
||||
return curr_dest;
|
||||
}
|
||||
|
||||
//If devices were specified, this function will run
|
||||
void run_device_operations(struct arg_values* a){
|
||||
struct arg_values* curr;
|
||||
struct arg_values current_op_args = {0};
|
||||
|
||||
//list
|
||||
extract_operation(OP_LIST, ¤t_op_args, a);
|
||||
do_list(current_op_args.next);
|
||||
free_cmd_args(¤t_op_args);
|
||||
|
||||
//get
|
||||
extract_operation(OP_GET, ¤t_op_args, a);
|
||||
return_value = do_get(current_op_args.next);
|
||||
free_cmd_args(¤t_op_args);
|
||||
|
||||
//assignment
|
||||
curr = extract_operation(OP_RESTORE, ¤t_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(¤t_op_args);
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
//If a global operation is requested, run this to fill arg with all devices, each with the global operation
|
||||
@ -320,6 +320,7 @@ void populate_args(struct arg_values* arg, struct string_array* devices){
|
||||
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;
|
||||
@ -331,6 +332,7 @@ 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.
|
||||
int should_save = 0;
|
||||
|
||||
args = process_cmd_args(argc, argv);
|
||||
if(args.operation == OP_USAGE){
|
||||
@ -344,7 +346,7 @@ int main(int argc, char** argv){
|
||||
//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");
|
||||
#endif
|
||||
@ -375,16 +377,20 @@ int main(int argc, char** argv){
|
||||
return RETVAL_INVALID_DIR;
|
||||
}
|
||||
|
||||
//Run selected operation
|
||||
//on a global operation, populate list with all devices
|
||||
if(!args.next){
|
||||
populate_args(&args, &device_names);
|
||||
}
|
||||
run_device_operations(&args);
|
||||
should_save = (run_device_operations(&args) > 0);
|
||||
#ifdef ENABLE_RESTORE_FILE
|
||||
save_restore_file(&device_names);
|
||||
if(should_save)
|
||||
save_restore_file(&device_names, &args); //performs cleanup on args
|
||||
else
|
||||
free_cmd_args(&args);
|
||||
#else
|
||||
free_cmd_args(&args);
|
||||
#endif
|
||||
|
||||
free_string_array(&device_names);
|
||||
free_cmd_args(&args);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user