/**
rexbacklight
Copyright (C) 2018 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 .
*/
#include
#include
#include
#include "cmd.h"
#include "common.h"
#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 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 backlight values"
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(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))
#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},
{RESTORE_LONG_OPT, RESTORE_SHORT_OPT, RESTORE_XBACK_OPT, RESTORE_DESC},
{HELP_LONG_OPT, HELP_SHORT_OPT, HELP_XBACK_OPT, HELP_DESC},
{VERSION_LONG_OPT, NO_OPT, VERSION_XBACK_OPT, VERSION_DESC}
};
#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},
{RESTORE_LONG_OPT, RESTORE_SHORT_OPT, RESTORE_DESC},
{HELP_LONG_OPT, HELP_SHORT_OPT, HELP_DESC},
{VERSION_LONG_OPT, NO_OPT, VERSION_DESC}
};
#endif //XBACKLIGHT_COMPAT_OPTIONS
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)
return;
free_cmd_args(a->next);
free(a->next);
}
#define CHECK_NEXT_ARG() \
do{ \
if(i == argc - 1){ \
fprintf(stderr, "Missing argument to '%s'\n\n", argv[i]); \
free_cmd_args(&ret); \
return_value = RETVAL_MISSING_OPTION; \
return (struct arg_values){.operation = OP_USAGE}; \
} \
}while(0)
#define UNRECOGNIZED_OPTION() \
do{ \
fprintf(stderr, "Unrecognized command line option '%s'\n\n", argv[i]); \
free_cmd_args(&ret); \
return_value = RETVAL_UNRECOGNIZED_OPTION; \
return (struct arg_values){.operation = OP_USAGE}; \
}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};
}
//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 i;
//Skip argv[0]
for(i = 1;i < argc;++i){
//Check for switches
if(CHECK_OPTION(GET, argv[i])){
curr->operation |= OP_GET;
continue;
}else if(CHECK_OPTION(FADE, argv[i])){
CHECK_NEXT_ARG();
curr->fade_duration = strtol(argv[++i], NULL, 0);
continue;
}
else if(CHECK_OPTION(STEPS, argv[i])){
CHECK_NEXT_ARG();
curr->fade_steps = strtol(argv[++i], NULL, 0);
continue;
}
else if(CHECK_OPTION(DEVICE, argv[i])){
CHECK_NEXT_ARG();
curr->next = malloc(sizeof(struct arg_values));
if(!curr->next){
mem_error();
free_cmd_args(&ret);
return_value = RETVAL_MEM_ERROR;
return (struct arg_values){.operation = OP_USAGE};
}
*(curr->next) = init_arg_values();
curr = curr->next;
curr->device = argv[++i];
continue;
}
else if(CHECK_OPTION(HELP, argv[i])){
free_cmd_args(&ret);
return (struct arg_values){.operation = OP_USAGE};
}
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])){
free_cmd_args(&ret);
ret.operation = OP_LIST;
ret.next = NULL;
return ret;
}
else if(CHECK_OPTION(RESTORE, argv[i])){
ret.operation = OP_RESTORE;
}
else if(!strcmp(argv[i], "max")){
curr->operation = OP_SET;
curr->delta = 100;
}
else if(!strcmp(argv[i], "min")){
curr->operation = OP_SET;
curr->delta = 0.1;
}
else if(!strcmp(argv[i], "off")){
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;
curr->delta = atof(argv[i] + 1);
}
else if(argv[i][0] == '+'){
curr->operation = OP_INC;
curr->delta = atof(argv[i] + 1);
}
else if(argv[i][0] == '-'){
//If we get a '-' followed by not a number, it's not a known option
if(argv[i][1] < '0' || argv[i][1] > '9'){
UNRECOGNIZED_OPTION();
//If we get a '-' followed by a number, it's a decrement request
}else{
curr->operation = OP_DEC;
curr->delta = atof(argv[i] + 1);
}
}
else{
//check for null terminator second to properly handle empty arguments
int j;
for(j = 0; j < 3;++j){
if(argv[i][j] < '0' || argv[i][j] > '9')
UNRECOGNIZED_OPTION();
if(argv[i][j] == '\0')
break;
}
curr->operation = OP_SET;
curr->delta = atof(argv[i]);
}
}
//If there isn't an operation defined in the global context and there is no specified device, there's nothing to do
if(!ret.operation){
if(!ret.next){
fprintf(stderr, "No operation requested!\n\n");
return_value = RETVAL_MISSING_OPTION;
return (struct arg_values){.operation = OP_USAGE};
}
//if there is a device specified, make sure each one has a requested operation
for(curr = ret.next;curr;curr = curr->next){
if(!curr->operation){
fprintf(stderr, "No operation requested for device '%s'!\n\n", curr->device);
free_cmd_args(&ret);
return_value = RETVAL_MISSING_OPTION;
return (struct arg_values){.operation = OP_USAGE};
}
}
//If there is a globally defined operation, apply it to the devices with no operation request
}else{
for(curr = ret.next;curr;curr = curr->next){
if(curr->operation == OP_NONE){
curr->operation = ret.operation;
if(!curr->delta)
curr->delta = ret.delta;
}
}
}
return ret;
}
#undef CHECK_OPTION
#undef UNRECOGNIZED_OPTION
//Process an operation
int process_op(struct arg_values* arg, float min, float current, float max){
//Amount to inc/dec
int delta = max * arg->delta / 100.0;
switch(arg->operation){
case OP_SET:
if(delta >= max)
return max;
else if(delta <= min)
return min;
else
return delta;
case OP_INC:
delta += current;
if(delta >= max)
return max;
else
return delta;
case OP_DEC:
delta = current - delta;
if(delta <= min)
return min;
else
return delta;
default:
fprintf(stderr, "Unrecognized operation requested!\n");
fprintf(stderr, "Internal error, plea1se fix for me ;)\n");
return_value = RETVAL_INTERNAL_ERROR;
}
return current;
}