/** 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; }