203 lines
5.2 KiB
C

/**
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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cmd.h"
#include "common.h"
#define CHECK_OPTION(opt, arg) (!strcmp(opt##_LONG_OPT, arg) || !strcmp(opt##_SHORT_OPT, arg))
struct cmd_arg rexbacklight_args[] = {
{DEVICE_LONG_OPT, DEVICE_SHORT_OPT, "select which device to control"},
{FADE_LONG_OPT, FADE_SHORT_OPT, "TODO: change brightness over time interval"},
{GET_LONG_OPT, GET_SHORT_OPT, "TODO: print current brightness level to stdout"},
{LIST_LONG_OPT, LIST_SHORT_OPT, "TODO: print device names to stdout and exit"},
{HELP_LONG_OPT, HELP_SHORT_OPT, "print this help message and exit"}
};
int rexbacklight_args_length = sizeof(rexbacklight_args) / sizeof(rexbacklight_args[0]);
void free_cmd_args(struct arg_values* a){
if(!a->next)
return;
free_cmd_args(a->next);
free(a->next);
}
#define CHECK_NEXT_ARG(rval) \
do{ \
if(i == argc - 1){ \
fprintf(stderr, "Missing argument to '%s'\n\n", argv[i]); \
free_cmd_args(&ret); \
usage(rval); \
} \
}while(0)
#define UNRECOGNIZED_OPTION(rval) \
do{ \
fprintf(stderr, "Unrecognized command line option '%s'\n\n", argv[i]); \
free_cmd_args(&ret); \
usage(rval); \
}while(0);
//Convert command line arguments to flags
struct arg_values process_cmd_args(int argc, char** argv){
struct arg_values ret = {0};
struct arg_values* curr = &ret;
//Skip argv[0]
for(int i = 1;i < argc;i++){
//Check for switches
if(CHECK_OPTION(GET, argv[i])){
curr->operation |= OP_GET;
continue;
}else if(CHECK_OPTION(FADE, argv[i])){
CHECK_NEXT_ARG(-5);
curr->fade_duration = strtol(argv[++i], NULL, 0);
continue;
}
else if(CHECK_OPTION(DEVICE, argv[i])){
CHECK_NEXT_ARG(-5);
curr->next = calloc(1, sizeof(struct arg_values));
curr = curr->next;
curr->device = argv[++i];
continue;
}
else if(CHECK_OPTION(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);
usage(RETVAL_SUCCESS);
}
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;
}
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(RETVAL_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{
for(int j = 0; j < 3;j++){
if(argv[i][j] == '\0')
break;
if(argv[i][j] < '0' || argv[i][j] > '9')
UNRECOGNIZED_OPTION(RETVAL_UNRECOGNIZED_OPTION);
}
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");
usage(RETVAL_MISSING_OPTION);
}
//if there is a device specified, make sure each one has a requested operation
for(curr = ret.next;curr;curr = curr->next){
if(!curr->operation){
fprintf(stderr, "No operation requested for device '%s'!\n\n", curr->device);
free_cmd_args(&ret);
usage(RETVAL_MISSING_OPTION);
}
}
//If there is a globally defined operation, apply it to the devices with no operation request
}else{
for(curr = ret.next;curr;curr = curr->next){
if(curr->operation == OP_NONE){
curr->operation = ret.operation;
if(!curr->delta)
curr->delta = ret.delta;
}
}
}
return ret;
}
#undef CHECK_OPTION
#undef UNRECOGNIZED_OPTION
//Process an operation
int process_op(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, "Internal error, please fix for me ;)\n");
exit(-99);
}
return current;
}