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 }
|