diff --git a/include/cmd.h b/include/cmd.h new file mode 100644 index 0000000..1e860a8 --- /dev/null +++ b/include/cmd.h @@ -0,0 +1,69 @@ +/** + 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 . +*/ + +#ifndef RECBACKLIGHT_CMD_H +#define REXBACKLIGHT_CMD_H + +#define OP_SET 4 +#define OP_LIST 8 +#define OP_GET 128 +#define OP_NONE 0 + +#define GET_LONG_OPT "--get" +#define GET_SHORT_OPT "-g" +#define FADE_LONG_OPT "--fade" +#define FADE_SHORT_OPT "-f" +#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" + +struct cmd_arg{ + const char* lopt; + const char* sopt; + const char* desc; +}; + +extern struct cmd_arg rexbacklight_args[]; +extern int rexbacklight_args_length; + +struct arg_values{ + struct arg_values* next; + + //Specific device to control + //NULL means all devices + const char* device; + + //What value to put in the backlight file + char* delta; + + //How many seconds to transition + int fade_duration; + + unsigned char operation; +}; + + + +void free_cmd_args(struct arg_values* a); +struct arg_values process_cmd_args(int argc, char** argv); +int process_op(char* arg, float min, float current, float max); + +#endif diff --git a/include/common.h b/include/common.h new file mode 100644 index 0000000..c49bb5c --- /dev/null +++ b/include/common.h @@ -0,0 +1,34 @@ +/** + 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 + +#ifndef REXBACKLIGHT_COMMON_H +#define REXBACKLIGHT_COMMON_H + +#define RETVAL_INVALID_FILE 1 +#define RETVAL_INVALID_DIR 2 +#define RETVAL_UNRECOGNIZED_OPTION 3 +#define RETVAL_MISSING_OPTION 4 +#define RETVAL_INVALID_DEVICE -5 +#define RETVAL_SUCCESS EXIT_SUCCESS + +_Noreturn void usage(int exit_val); + + +#endif diff --git a/makefile b/makefile index 61e9be7..52474c1 100644 --- a/makefile +++ b/makefile @@ -1,25 +1,72 @@ -.PHONY: all -all: rexbacklight +#Copyright 2018 rexy712 of Rexy & Co +#Makefile for project 'rexbacklight' -rexbacklight: src/rexbacklight.c - gcc -std=c11 -g -Wall -Wextra -pedantic src/rexbacklight.c -o rexbacklight +#Project directory and file setup +SOURCE_DIRS:=src +OBJDIR:=obj +DEPDIR:=obj/dep +INCLUDE_DIRS:=include +EXT:=c +MAIN_EXECUTABLE:=rexbacklight + +#Compiler/Linker setup +CC:=gcc +CFLAGS:=-g -std=c11 -Wall -pedantic -Wextra +release: CFLAGS:=-std=c11 -Wall -pedantic -Wextra -O2 +LDFLAGS= +LDLIBS:= +STRIP:=strip + +#Windows workarounds +ifeq ($(OS),Windows_NT) + mkdir=mkdir $(subst /,\,$(1)) > NUL 2>&1 + rm=del /F $(1) > NUL 2>&1 + rmdir=rd /s /q $(1) > NUL 2>&1 + move=move /y $(subst /,\,$(1)) $(subst /,\,$(2)) > NUL 2>&1 + MAIN_EXECUTABLE:=$(MAIN_EXECUTABLE).exe + LDLIBS:= +else + mkdir=mkdir -p $(1) + rm=rm -f $(1) + rmdir=rm -rf $(1) + move=mv $(1) $(2) +endif + +#Internal management of sources/objects and additional cflags +INTERNAL_CFLAGS=-c $(foreach dir,$(INCLUDE_DIRS),-I"$(dir)") -MMD -MP -MF"$(DEPDIR)/$*.d" +SOURCES:=$(foreach source,$(SOURCE_DIRS),$(foreach ext,$(EXT),$(wildcard $(source)/*.$(ext)))) +OBJECTS:=$(addprefix $(OBJDIR)/,$(subst \,.,$(subst /,.,$(addsuffix .o,$(SOURCES))))) + +all: $(MAIN_EXECUTABLE) + +$(MAIN_EXECUTABLE): $(OBJECTS) + $(CC) $(LDFLAGS) $^ -o "$(basename $@)" $(LDLIBS) .PHONY: release -release: src/rexbacklight.c - gcc -std=c11 -O2 -Wall -Wextra -pedantic src/rexbacklight.c -o rexbacklight - strip --strip-all rexbacklight +release: $(OBJECTS) + $(CC) $(LDFLAGS) $^ -o "$(basename $(MAIN_EXECUTABLE))" $(LDLIBS) + $(STRIP) --strip-all "$(MAIN_EXECUTABLE)" -.PHONY: install -install: rexbacklight - install -m755 -o root -g root -s rexbacklight /usr/bin/rexbacklight - install -m600 -o root -g root rules/91-backlight.rules /etc/udev/rules.d/91-backlight.rules - install -m755 -o root -g root rules/backlight /etc/init.d/backlight +define GENERATE_OBJECTS + +$$(OBJDIR)/$(subst \,.,$(subst /,.,$(1))).%.o: $(1)/% + $$(CC) $$(CFLAGS) $$(INTERNAL_CFLAGS) "$$<" -o "$$@" + +endef + +$(foreach dir,$(SOURCE_DIRS),$(eval $(call GENERATE_OBJECTS,$(dir)))) +$(OBJECTS): | $(OBJDIR) $(DEPDIR) + +$(OBJDIR): + $(call mkdir,"$@") +$(DEPDIR): + $(call mkdir,"$@") -.PHONY: uninstall -uninstall: - rm /usr/bin/rexbacklight - rm /etc/udev/rules.d/91-backlight.rules - rm /etc/init.d/backlight .PHONY: clean clean: - rm rexbacklight + $(call rmdir,"$(DEPDIR)") + $(call rmdir,"$(OBJDIR)") + $(call rm,"$(MAIN_EXECUTABLE)") +-include $(wildcard $(DEPDIR)/*.d) + + diff --git a/src/cmd.c b/src/cmd.c new file mode 100644 index 0000000..54ce680 --- /dev/null +++ b/src/cmd.c @@ -0,0 +1,196 @@ +/** + 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 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 = argv[i]; + } + else if(!strcmp(argv[i], "min")){ + curr->operation = OP_SET; + curr->delta = argv[i]; + } + else if(!strcmp(argv[i], "off")){ + curr->operation = OP_SET; + curr->delta = argv[i]; + } + + //If we get a '-' followed by not a number, it's not a known option + else 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_SET; + curr->delta = 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(char* arg, float min, float current, float max){ + //Amount to inc/dec + int delta = delta = max * atof(arg + 1) / 100.0; + + //Increment + if(arg[0] == '+'){ + delta = delta + current; + if(delta >= max) + return max; + else{ + return delta; + } + + //Decrement + }else if(arg[0] == '-'){ + delta = current - delta; + if(delta <= min) + return min; + else + return delta; + + //Directly set + }else if(arg[0] == '='){ + if(delta >= max) + return max; + else if(delta <= min) + return min; + else + return delta; + + //Set to minimum plus one (so backlight is actually on!) + }else if(!strcmp("min", arg)){ + return min + 1; + + //Turn off backlight + }else if(!strcmp("off", arg)){ + return min; + + //Set to maximum + }else if(!strcmp("max", arg)){ + return max; + } + return current; +} + diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..6e5c228 --- /dev/null +++ b/src/common.c @@ -0,0 +1,50 @@ +/** + 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 "common.h" +#include "cmd.h" + +#include + +_Noreturn void usage(int exit_val){ + fprintf(stderr, "Usage: rexbacklight [argument] [options] [argument]\n\n"); + + fprintf(stderr, "Options:\n"); + for(int i = 0;i < rexbacklight_args_length;i++){ + fprintf(stderr, " %s|%s\n", rexbacklight_args[i].lopt, rexbacklight_args[i].sopt); + fprintf(stderr, " %s\n", rexbacklight_args[i].desc); + } + fprintf(stderr, "\n"); + fprintf(stderr, "Arguments:\n"); + fprintf(stderr, " =\n"); + fprintf(stderr, " -\n"); + fprintf(stderr, " +\n"); + fprintf(stderr, " off\n"); + fprintf(stderr, " max\n"); + fprintf(stderr, " min\n"); + + if(exit_val == RETVAL_SUCCESS){ + fprintf(stderr, "\nrexbacklight Copyright (C) 2018 rexy712\n"); + fprintf(stderr, "This program comes with ABSOLUTELY NO WARRANTY.\n"); + fprintf(stderr, "This is free software, and you are welcome to redistribute it\n"); + fprintf(stderr, "under certain conditions; see the GNU GPLv3 for details.\n"); + fprintf(stderr, "A copy of the GPLv3 is available with the source in the file 'LICENSE'\n"); + } + + exit(exit_val); +} diff --git a/src/rexbacklight.c b/src/rexbacklight.c index b4232ff..b9ba1e5 100644 --- a/src/rexbacklight.c +++ b/src/rexbacklight.c @@ -19,247 +19,20 @@ #include #include #include -#include -#include #include -#include - -#define OP_SET 4 -#define OP_GET 128 -#define OP_NONE 0 - -#define GET_LONG_OPT "--get" -#define GET_SHORT_OPT "-g" -#define FADE_LONG_OPT "--fade" -#define FADE_SHORT_OPT "-f" -#define DEVICE_LONG_OPT "--device" -#define DEVICE_SHORT_OPT "-d" -#define HELP_LONG_OPT "--help" -#define HELP_SHORT_OPT "-h" - -#define CHECK_OPTION(opt, arg) (!strcmp(opt##_LONG_OPT, arg) || !strcmp(opt##_SHORT_OPT, arg)) - -#define RETVAL_INVALID_FILE -1 -#define RETVAL_INVALID_DIR -2 -#define RETVAL_UNRECOGNIZED_OPTION -3 -#define RETVAL_MISSING_OPTION -4 -#define RETVAL_INVALID_DEVICE -5 -#define RETVAL_SUCCESS EXIT_SUCCESS - -struct string_array{ - char** list; - int size; -}; - -struct arg_values{ - struct arg_values* next; - - //Specific device to control - //NULL means all devices - const char* device; - - //What value to put in the backlight file - char* delta; - - //How many seconds to transition - int fade_duration; - - unsigned char operation; -}; - +#include "common.h" +#include "cmd.h" //This is where backlight devices can be found in sysfs static const char* backlight_dir = "/sys/class/backlight/"; static const char* backlight_file = "brightness"; static const char* max_backlight_file = "max_brightness"; -//Print out usage message and GPL message if not caused by an error -_Noreturn void usage(int exit_val){ - fprintf(stderr, "Usage: rexbacklight [argument] [options] [argument]\n\n"); - - fprintf(stderr, "Options:\n"); - fprintf(stderr, " %s|%s\n", DEVICE_SHORT_OPT, DEVICE_LONG_OPT); - fprintf(stderr, " select which device to control\n"); - fprintf(stderr, " %s|%s\n", FADE_SHORT_OPT, FADE_LONG_OPT); - fprintf(stderr, " TODO: change brightness over time interval\n"); - fprintf(stderr, " %s|%s\n", GET_SHORT_OPT, GET_LONG_OPT); - fprintf(stderr, " TODO: print the current brightness level to stdout\n"); - fprintf(stderr, " %s|%s\n", HELP_SHORT_OPT, HELP_LONG_OPT); - fprintf(stderr, " print this help message and exit\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Arguments:\n"); - fprintf(stderr, " =\n"); - fprintf(stderr, " -\n"); - fprintf(stderr, " +\n"); - fprintf(stderr, " off\n"); - fprintf(stderr, " max\n"); - fprintf(stderr, " min\n"); - - if(exit_val == RETVAL_SUCCESS){ - fprintf(stderr, "\nrexbacklight Copyright (C) 2018 rexy712\n"); - fprintf(stderr, "This program comes with ABSOLUTELY NO WARRANTY.\n"); - fprintf(stderr, "This is free software, and you are welcome to redistribute it\n"); - fprintf(stderr, "under certain conditions; see the GNU GPLv3 for details.\n"); - fprintf(stderr, "A copy of the GPLv3 is available with the source in the file 'LICENSE'\n"); - } - - exit(exit_val); -} - -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(HELP, argv[i])){ - free_cmd_args(&ret); - usage(RETVAL_SUCCESS); - } - - else if(!strcmp(argv[i], "max")){ - curr->operation = OP_SET; - curr->delta = argv[i]; - } - else if(!strcmp(argv[i], "min")){ - curr->operation = OP_SET; - curr->delta = argv[i]; - } - else if(!strcmp(argv[i], "off")){ - curr->operation = OP_SET; - curr->delta = argv[i]; - } - - //If we get a '-' followed by not a number, it's not a known option - else 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_SET; - curr->delta = 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(char* arg, float min, float current, float max){ - //Amount to inc/dec - int delta = delta = max * atof(arg + 1) / 100.0; - - //Increment - if(arg[0] == '+'){ - delta = delta + current; - if(delta >= max) - return max; - else{ - return delta; - } - - //Decrement - }else if(arg[0] == '-'){ - delta = current - delta; - if(delta <= min) - return min; - else - return delta; - - //Directly set - }else if(arg[0] == '='){ - if(delta >= max) - return max; - else if(delta <= min) - return min; - else - return delta; - - //Set to minimum plus one (so backlight is actually on!) - }else if(!strcmp("min", arg)){ - return min + 1; - - //Turn off backlight - }else if(!strcmp("off", arg)){ - return min; - - //Set to maximum - }else if(!strcmp("max", arg)){ - return max; - } - return current; -} +struct string_array{ + char** list; + int size; +}; //Delete the list of backlight devices void free_string_array(struct string_array* s){