summaryrefslogtreecommitdiff
path: root/src/main.c
blob: d78a40d1cf1a1b86136806a9c007440f8625ee1e (plain)
    1 /**
    2  * Copyright (C) 2017 Aaron Ball <nullspoon@oper.io>
    3  *
    4  * Luminous is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation, either version 3 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * Luminous is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with noteless.  If not, see <http://www.gnu.org/licenses/>.
   16  */
   17 #include <stdio.h>
   18 #include <unistd.h>
   19 #include <errno.h>
   20 #include <sys/file.h>
   21 
   22 #include "config.h"
   23 
   24 #define CONF "/etc/luminous.conf"
   25 #define STATE "/tmp/luminous.state"
   26 
   27 // For string macros
   28 #define xstr(s) str(s)
   29 #define str(s) #s
   30 
   31 
   32 struct props {
   33   int lvl;      // Destination brightness
   34   int max;      // Maximum brightness of screen device
   35   int cur;      // Current brightness prior to fade operation
   36   int duration; // Duration to fade through
   37   char filebrightness[512]; // Path to the sysfs ent for brightness
   38   char filemax_bright[512]; // Path to the sysfs ent for max brightness
   39 };
   40 
   41 
   42 void props_new(struct props* p) {
   43   p->lvl      = 200;
   44   p->max      = -1;
   45   p->cur      = -1;
   46   p->duration = 500;
   47   p->filebrightness[0] = '\0';
   48   p->filemax_bright[0] = '\0';
   49 }
   50 
   51 
   52 /**
   53  * version_print:
   54  * @fd File descriptor to print version information to
   55  *
   56  * Prints version information for the current build to the specified file
   57  * descriptor.
   58  * NOTE: Version information is pulled from a macro defined by the Makefile.
   59  */
   60 void version_print(FILE* fd) {
   61   fprintf(fd, "luminous %s (compiled: %s)\n", xstr(VERSTR), xstr(DATESTR));
   62 }
   63 
   64 
   65 /**
   66  * fgeti:
   67  * @path Path to the file from which to read the integer
   68  *
   69  * Reads integer from the specified file. If the file does not contain only an
   70  * integer, returns -1.
   71  *
   72  * returns: Integer read from file, otherwise -1.
   73  */
   74 int fgeti(char* path) {
   75   FILE* fd;
   76   char buf[64];
   77 
   78   fd = fopen(path, "r");
   79   if(! fd)
   80     return -1;
   81 
   82   fgets(buf, 64, fd);
   83   fclose(fd);
   84   return atoi(buf);
   85 }
   86 
   87 
   88 /**
   89  * write_state:
   90  * Writes the current brightness level to a text file.
   91  *
   92  * @path   Path to the file to contain the current brightness
   93  */
   94 int write_state(char* path, int cur) {
   95   FILE* fd;
   96   fd = fopen(path, "w");
   97 
   98   if(!fd)
   99     return errno;
  100 
  101   fprintf(fd, "%d", cur);
  102   fclose(fd);
  103   return 0;
  104 }
  105 
  106 
  107 /**
  108  * fade:
  109  * @props Props struct containing runtime parameters
  110  *
  111  * Fades brightness up or down, based on positive (fade up) or negative value
  112  * (fade down) contained in `step` property. Rate of fade is controled by
  113  * `props->rate`.
  114  *
  115  * returns: Success (0) or failure (-1)
  116  */
  117 int fade(struct props* props) {
  118   int direction = 0;             // Direction of fade. 1 for up, -1 for down
  119   int step = props->max / 300;   // Calculate what 1/300 of max is for step
  120   int rate = 0;                  // Duration / step count
  121   int steps = 0;                 // How many steps to cover the distance
  122 
  123   if(step < 1)
  124     step = 1;
  125 
  126   // Make sure we can open the file
  127   FILE* fd = fopen(props->filebrightness, "w");
  128   if(! fd) {
  129     if(errno == EACCES) {
  130       fprintf(stderr, "Permission denied: %s\n", props->filebrightness);
  131     } else {
  132       fprintf(stderr, "Could not open file %s\n", props->filebrightness);
  133     }
  134     return -1;
  135   }
  136 
  137   // Determine direction
  138   if(props->lvl > props->cur) {
  139     direction = 1;
  140     steps = (props->lvl - props->cur) / step;
  141   } else if(props->lvl < props->cur) {
  142     direction = -1;
  143     steps = (props->cur - props->lvl) / step;
  144   }
  145   if(steps == 0)
  146     steps = 1;   // Ensure no divide by zero operations happen
  147   rate = (props->duration / steps) * 1000;
  148 
  149   // Lock the file descriptor so we can't have conflicting write operations
  150   if(flock(fileno(fd), LOCK_EX | LOCK_NB) != 0) {
  151     fprintf(stderr, "Could not obtain brightness file lock\n");
  152     fclose(fd);
  153     return -1;
  154   }
  155 
  156   while(steps > 0) {
  157     usleep(rate);
  158     // Write the new brightness to the brightness file
  159     fprintf(fd, "%d\n", props->cur);
  160     fflush(fd);
  161     props->cur += step * direction;
  162     steps--;
  163   }
  164   // The step loop got us very close to the destination brightness, just finish
  165   // things off since the math rarely works evenly.
  166   fprintf(fd, "%d\n", props->lvl);
  167   fclose(fd);
  168   return 0;
  169 }
  170 
  171 
  172 /**
  173  * usage:
  174  *
  175  * Nothing to see here. Just the help text printer.
  176  */
  177 void usage() {
  178   printf(
  179 "Luminous is a backlight manager that supports fading the backlight at\n"
  180 "varying rates and steps. It manages backlight via the kenerl interfaces\n"
  181 "provided in /sys to adjust screen backlight levels. Consequently, it\n"
  182 "requires write access to the brightness property in \n"
  183 "/sys/class/backlight/<device>/brightness.\n"
  184 "\n"
  185 "Usage:\n"
  186 "  luminous --rate 10 --step 8 --level [+-]400\n"
  187 "\n"
  188 "Arguments:\n"
  189 "  -h,--help     Print this helptext\n"
  190 "  -d,--duration Duration of the fade (default: 500 ms)\n"
  191 "  -l,--level    Brightness level to change to. Supports absolute values and\n"
  192 "                relative values prefixed with + and -.\n"
  193 "  -V,--version Print program version"
  194 "\n\n"
  195 );
  196 }
  197 
  198 
  199 /**
  200  * parseargs:
  201  * @out   Output properties struct
  202  * @argc  Number of arguments passed
  203  * @argv  Arguments char array
  204  *
  205  * Parses command line arguments into a props struct.
  206  *
  207  * returns: Success (0), failure (-1), or incomplete (-2)
  208  */
  209 int parseargs(struct props* out, int argc, char* argv[]) {
  210   int i;
  211   
  212   strcpy(out->filebrightness,
  213          "/sys/class/backlight/intel_backlight/brightness");
  214   strcpy(out->filemax_bright,
  215          "/sys/class/backlight/intel_backlight/max_brightness");
  216 
  217   // Read the system values for current and maximum brightness
  218   out->cur = fgeti(out->filebrightness);
  219   out->max = fgeti(out->filemax_bright);
  220 
  221   for(i = 0; i < argc; i++) {
  222     if(strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--duration") == 0) {
  223       i++;
  224       out->duration = atoi(argv[i]);
  225 
  226     } else if(strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--toggle") == 0) {
  227       FILE* fd = fopen(STATE, "r");
  228       // If somehow we are at zero and there is no statefile, set brightness to
  229       // half max.
  230       if(out->cur < out->max * .02) {
  231         // If 0, we increase brightness which means reading from statefile
  232         if(!fd)
  233           write_state(STATE, out->lvl = out->max / 2);
  234         else
  235           out->lvl = fgeti(STATE);
  236       } else {
  237         write_state(STATE, out->cur);
  238         out->lvl = 0;
  239       }
  240 
  241     } else if(strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--level") == 0) {
  242       i++;
  243       // Account for relative and absolute increments and decrements
  244       if(argv[i][0] == '+') {
  245         out->lvl = out->cur + atoi(&argv[i][1]);
  246       } else if(argv[i][0] == '-') {
  247         out->lvl = out->cur - atoi(&argv[i][1]);
  248       } else {
  249         out->lvl = atoi(argv[i]);
  250       }
  251 
  252     } else if(strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) {
  253       version_print(stdout);
  254       return -2;
  255 
  256     } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
  257       usage();
  258       return -2;
  259     }
  260   }
  261 
  262   // Ensure min and max thresholds are respected
  263   if(out->lvl > out->max)
  264     out->lvl = out->max;
  265   else if(out->lvl < 0)
  266     out->lvl = 0;
  267   return 0;
  268 }
  269 
  270 
  271 int config_to_props(char* confpath, struct props* p) {
  272   struct config c;
  273   char lvlbuf[32];       // Buffer to store config value for "level" if found
  274   char durationbuf[32];  // Buffer to store config value for "duration" if found
  275 
  276   if(config_new(&c, confpath) == 0)
  277     config_load(&c);
  278   else
  279     return -2;
  280 
  281   // Check for and load specific configs
  282   if(config_get(&c, "level", lvlbuf) == 0)
  283     p->lvl = atoi(lvlbuf);
  284   if(config_get(&c, "duration", lvlbuf) == 0)
  285     p->duration = atoi(durationbuf);
  286 
  287   // Don't forget to clean up!
  288   config_free(&c);
  289   return 0;
  290 }
  291 
  292 
  293 /**
  294  * Ye olde main
  295  */
  296 int main(int argc, char* argv[]) {
  297   int r = 0;
  298   struct props p;
  299 
  300   props_new(&p);
  301 
  302   // Load configs into properties struct
  303   config_to_props(CONF, &p);
  304 
  305   // Parse cli arguments (overriding any configs if specified at runtime)
  306   if(parseargs(&p, argc, argv) != 0)
  307     return 1;
  308 
  309   r = fade(&p);
  310 
  311   if(r < 0) {
  312     fprintf(stderr, "ERROR: Could not open brightness file\n");
  313     return 1;
  314   }
  315   return 0;
  316 }

Generated by cgit