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 <stdlib.h>
19 #include <string.h>
20 #include <time.h>
21 #include <errno.h>
22 #include <sys/file.h>
23
24 #include "config.h"
25
26 #define CONF "/etc/luminous.conf"
27
28 // For string macros
29 #define xstr(s) str(s)
30 #define str(s) #s
31
32
33 struct props {
34 int lvl; // Destination brightness
35 int step; // Step size
36 int rate; // Time in milliseconds between steps
37 int max; // Maximum brightness of screen device
38 int cur; // Current brightness prior to fade operation
39 char filebrightness[512]; // Path to the sysfs ent for brightness
40 char filemax_bright[512]; // Path to the sysfs ent for max brightness
41 };
42
43
44 void props_new(struct props* p) {
45 p->lvl = -1;
46 p->step = -1;
47 p->rate = -1;
48 p->max = -1;
49 p->cur = -1;
50 p->filebrightness[0] = '\0';
51 p->filemax_bright[0] = '\0';
52 }
53
54
55 /**
56 * version_print:
57 * @fd File descriptor to print version information to
58 *
59 * Prints version information for the current build to the specified file
60 * descriptor.
61 * NOTE: Version information is pulled from a macro defined by the Makefile.
62 */
63 void version_print(FILE* fd) {
64 fprintf(fd, "luminous %s (compiled: %s)\n", xstr(VERSTR), xstr(DATESTR));
65 }
66
67
68 /**
69 * fgeti:
70 * @path Path to the file from which to read the integer
71 *
72 * Reads integer from the specified file. If the file does not contain only an
73 * integer, returns -1.
74 *
75 * returns: Integer read from file, otherwise -1.
76 */
77 int fgeti(char* path) {
78 FILE* fd;
79 char buf[64];
80
81 fd = fopen(path, "r");
82 if(! fd)
83 return -1;
84
85 fgets(buf, 64, fd);
86 fclose(fd);
87 return atoi(buf);
88 }
89
90
91 /**
92 * fade:
93 * @props Props struct containing runtime parameters
94 *
95 * Fades brightness up or down, based on positive (fade up) or negative value
96 * (fade down) contained in `step` property. Rate of fade is controled by
97 * `props->rate`.
98 *
99 * returns: Success (0) or failure (-1)
100 */
101 int fade(struct props* props) {
102 struct timespec tim, tim2;
103 int sec; // Number of seconds to sleep
104 int msec; // Number of milliseconds to sleep (less than 1 second)
105 int direction = 0; // Direction of fade. Positive for up, negative for down
106 int opcount = 0;
107
108 sec = props->rate / 1000;
109 msec = props->rate - (sec * 1000);
110
111 tim.tv_sec = sec;
112 tim.tv_nsec = 1000000 * msec;
113
114 // Determine direction
115 if(props->lvl > props->cur) {
116 // Dest brightness is higher than current
117 direction = 1;
118 opcount = (props->lvl - props->cur) / props->step;
119 } else if(props->lvl < props->cur) {
120 // Dest brightness is lower than current
121 direction = -1;
122 opcount = (props->cur - props->lvl) / props->step;
123 }
124
125 FILE* fd = fopen(props->filebrightness, "w");
126 if(! fd) {
127 if(errno == EACCES) {
128 fprintf(stderr, "Permission denied: %s\n", props->filebrightness);
129 } else {
130 fprintf(stderr, "Could not open file %s\n", props->filebrightness);
131 }
132 return -1;
133 }
134
135 // Lock the file descriptor so we can't have conflicting brightness change
136 // operations
137 if(flock(fileno(fd), LOCK_EX | LOCK_NB) != 0) {
138 fprintf(stderr, "Could not obtain brightness file lock\n");
139 fclose(fd);
140 return -1;
141 }
142
143 while(opcount > 0) {
144 nanosleep(&tim, &tim2);
145
146 // Write the new brightness to the brightness file
147 fprintf(fd, "%d\n", props->cur);
148
149 // Flush the toil...er...buffer!
150 fflush(fd);
151
152 props->cur += props->step * direction;
153 opcount--;
154 }
155
156 // Do not need to explicitly release the file lock
157 fclose(fd);
158 return 0;
159 }
160
161
162 /**
163 * usage:
164 *
165 * Nothing to see here. Just the help text printer.
166 */
167 void usage() {
168 printf(
169 "Luminous is a backlight manager that supports fading the backlight at\n"
170 "varying rates and steps. It manages backlight via the kenerl interfaces\n"
171 "provided in /sys to adjust screen backlight levels. Consequently, it\n"
172 "requires write access to the brightness property in \n"
173 "/sys/class/backlight/<device>/brightness.\n"
174 "\n"
175 "Usage:\n"
176 " luminous --rate 10 --step 8 --level [+-]400\n"
177 "\n"
178 "Arguments:\n"
179 " -h,--help Print this helptext\n"
180 " -s,--step Fade step (default: 6)\n"
181 " -r,--rate Time in milliseconds between fade steps (default: 10)\n"
182 " -l,--level Brightness level to change to. Supports absolute values and\n"
183 " relative values prefixed with + and -.\n"
184 " -V,--version Print program version"
185 "\n\n"
186 );
187 }
188
189
190 /**
191 * parseargs:
192 * @out Output properties struct
193 * @argc Number of arguments passed
194 * @argv Arguments char array
195 *
196 * Parses command line arguments into a props struct.
197 *
198 * returns: Success (0), failure (-1), or incomplete (-2)
199 */
200 int parseargs(struct props* out, int argc, char* argv[]) {
201 int i;
202
203 strcpy(out->filebrightness,
204 "/sys/class/backlight/intel_backlight/brightness");
205 strcpy(out->filemax_bright,
206 "/sys/class/backlight/intel_backlight/max_brightness");
207
208 // Read the system values for current and maximum brightness
209 out->cur = fgeti(out->filebrightness);
210 out->max = fgeti(out->filemax_bright);
211
212 for(i = 0; i < argc; i++) {
213 if(strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--rate") == 0) {
214 i++;
215 out->rate = atoi(argv[i]);
216 } else if(strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--step") == 0) {
217 i++;
218 out->step = atoi(argv[i]);
219 } else if(strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--level") == 0) {
220 i++;
221 // Account for relative and absolute increments and decrements
222 if(argv[i][0] == '+') {
223 out->lvl = out->cur + atoi(&argv[i][1]);
224 } else if(argv[i][0] == '-') {
225 out->lvl = out->cur - atoi(&argv[i][1]);
226 } else {
227 out->lvl = atoi(argv[i]);
228 }
229 } else if(strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) {
230 version_print(stdout);
231 return -2;
232 } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
233 usage();
234 return -2;
235 }
236 }
237
238 // Some defaults, if no values are set yet
239 if(out->rate < 0)
240 out->rate = 20;
241 if(out->step < 0)
242 out->step = 12;
243 if(out->lvl == -1)
244 out->lvl = 200;
245
246 // Ensure min and max thresholds are respected
247 if(out->lvl > out->max) {
248 out->lvl = out->max;
249 } else if(out->lvl < 0) {
250 out->lvl = 0;
251 }
252 return 0;
253 }
254
255
256 int config_to_props(char* confpath, struct props* p) {
257 struct config c;
258 char lvlbuf[32]; // Buffer to store config value for "level" if found
259 char ratebuf[32]; // Buffer to store config value for "rate" if found
260 char stepbuf[32]; // Buffer to store config value for "step" if found
261
262 if(config_new(&c, confpath) == 0) {
263 config_load(&c);
264 } else {
265 // Config file not found, return -2 code
266 return -2;
267 }
268
269 // If the config specifies a "level" key, set it to the props object
270 if(config_get(&c, "level", lvlbuf) == 0)
271 p->lvl = atoi(lvlbuf);
272
273 // If the config specifies a "step" key, set it to the props object
274 if(config_get(&c, "step", stepbuf) == 0)
275 p->step = atoi(stepbuf);
276
277 // If the config specifies a "rate" key, set it to the props object
278 if(config_get(&c, "rate", ratebuf) == 0)
279 p->rate = atoi(ratebuf);
280
281 // Don't forget to clean up!
282 config_free(&c);
283
284 return 0;
285 }
286
287
288 /**
289 * Ye olde main
290 */
291 int main(int argc, char* argv[]) {
292 int r = 0;
293 struct props p;
294
295 props_new(&p);
296
297 // Load configs into properties struct
298 config_to_props(CONF, &p);
299
300 // Parse cli arguments (overriding any configs if specified at runtime)
301 if(parseargs(&p, argc, argv) != 0)
302 return 1;
303
304 // Determine if we should fade up or fade down
305 if(p.lvl < 0) {
306 printf("%d\n", p.cur);
307 } else {
308 r = fade(&p);
309 }
310
311 if(r < 0) {
312 fprintf(stderr, "ERROR: Could not open brightness file\n");
313 return 1;
314 }
315 return 0;
316 }
|