1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61#include "libbb.h"
62#include "common_bufsiz.h"
63#include <linux/fb.h>
64
65
66#define DEBUG 0
67
68#define ESC "\033"
69
70struct globals {
71#if DEBUG
72 bool bdebug_messages;
73 FILE *logfile_fd;
74#endif
75 unsigned char *addr;
76 unsigned ns[9];
77 const char *image_filename;
78 struct fb_var_screeninfo scr_var;
79 struct fb_fix_screeninfo scr_fix;
80 unsigned bytes_per_pixel;
81
82 unsigned red_shift;
83 unsigned green_shift;
84 unsigned blue_shift;
85};
86#define G (*ptr_to_globals)
87#define INIT_G() do { \
88 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
89} while (0)
90
91#define nbar_width ns[0]
92#define nbar_height ns[1]
93#define nbar_posx ns[2]
94#define nbar_posy ns[3]
95#define nbar_colr ns[4]
96#define nbar_colg ns[5]
97#define nbar_colb ns[6]
98#define img_posx ns[7]
99#define img_posy ns[8]
100
101#if DEBUG
102#define DEBUG_MESSAGE(strMessage, args...) \
103 if (G.bdebug_messages) { \
104 fprintf(G.logfile_fd, "[%s][%s] - %s\n", \
105 __FILE__, __FUNCTION__, strMessage); \
106 }
107#else
108#define DEBUG_MESSAGE(...) ((void)0)
109#endif
110
111
112
113
114static void fb_setpal(int fd)
115{
116 struct fb_cmap cmap;
117
118 unsigned short red[256], green[256], blue[256];
119 unsigned i;
120
121
122 for (i = 0; i < 256; i++) {
123
124
125
126
127
128
129
130
131
132
133 red[i] = (( i >> 5 ) * 0x9249) >> 2;
134 green[i] = (((i >> 2) & 0x7) * 0x9249) >> 2;
135
136 blue[i] = ( i & 0x3) * 0x5555;
137 }
138
139 cmap.start = 0;
140 cmap.len = 256;
141 cmap.red = red;
142 cmap.green = green;
143 cmap.blue = blue;
144 cmap.transp = 0;
145
146 xioctl(fd, FBIOPUTCMAP, &cmap);
147}
148
149
150
151
152
153static void fb_open(const char *strfb_device)
154{
155 int fbfd = xopen(strfb_device, O_RDWR);
156
157
158 xioctl(fbfd, FBIOGET_VSCREENINFO, &G.scr_var);
159 xioctl(fbfd, FBIOGET_FSCREENINFO, &G.scr_fix);
160
161 switch (G.scr_var.bits_per_pixel) {
162 case 8:
163 fb_setpal(fbfd);
164 break;
165
166 case 16:
167 case 24:
168 case 32:
169 break;
170
171 default:
172 bb_error_msg_and_die("unsupported %u bpp", (int)G.scr_var.bits_per_pixel);
173 break;
174 }
175
176 G.red_shift = 8 - G.scr_var.red.length;
177 G.green_shift = 8 - G.scr_var.green.length;
178 G.blue_shift = 8 - G.scr_var.blue.length;
179 G.bytes_per_pixel = (G.scr_var.bits_per_pixel + 7) >> 3;
180
181
182 G.addr = mmap(NULL,
183 (G.scr_var.yres_virtual ?: G.scr_var.yres) * G.scr_fix.line_length,
184 PROT_WRITE, MAP_SHARED, fbfd, 0);
185 if (G.addr == MAP_FAILED)
186 bb_perror_msg_and_die("mmap");
187
188
189 G.addr += G.scr_var.yoffset * G.scr_fix.line_length + G.scr_var.xoffset * G.bytes_per_pixel;
190 close(fbfd);
191}
192
193
194
195
196
197
198static unsigned fb_pixel_value(unsigned r, unsigned g, unsigned b)
199{
200
201
202 if (G.bytes_per_pixel == 1) {
203 r = r & 0xe0;
204 g = (g >> 3) & 0x1c;
205 b = b >> 6;
206 return r + g + b;
207 }
208 if (G.bytes_per_pixel == 2) {
209
210
211
212
213 r = r >> G.red_shift;
214 g = g >> G.green_shift;
215 b = b >> G.blue_shift;
216
217 return (r << G.scr_var.red.offset) +
218 (g << G.scr_var.green.offset) +
219 (b << G.scr_var.blue.offset);
220 }
221
222 return b + (g << 8) + (r << 16);
223}
224
225
226
227
228static void fb_write_pixel(unsigned char *addr, unsigned pixel)
229{
230 switch (G.bytes_per_pixel) {
231 case 1:
232 *addr = pixel;
233 break;
234 case 2:
235 *(uint16_t *)addr = pixel;
236 break;
237 case 4:
238 *(uint32_t *)addr = pixel;
239 break;
240 default:
241 addr[0] = pixel;
242 addr[1] = pixel >> 8;
243 addr[2] = pixel >> 16;
244 }
245}
246
247
248
249
250
251static void fb_drawrectangle(void)
252{
253 int cnt;
254 unsigned thispix;
255 unsigned char *ptr1, *ptr2;
256 unsigned char nred = G.nbar_colr/2;
257 unsigned char ngreen = G.nbar_colg/2;
258 unsigned char nblue = G.nbar_colb/2;
259
260 thispix = fb_pixel_value(nred, ngreen, nblue);
261
262
263 ptr1 = G.addr + G.nbar_posy * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
264 ptr2 = G.addr + (G.nbar_posy + G.nbar_height - 1) * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
265 cnt = G.nbar_width - 1;
266 do {
267 fb_write_pixel(ptr1, thispix);
268 fb_write_pixel(ptr2, thispix);
269 ptr1 += G.bytes_per_pixel;
270 ptr2 += G.bytes_per_pixel;
271 } while (--cnt >= 0);
272
273
274 ptr1 = G.addr + G.nbar_posy * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
275 ptr2 = G.addr + G.nbar_posy * G.scr_fix.line_length + (G.nbar_posx + G.nbar_width - 1) * G.bytes_per_pixel;
276 cnt = G.nbar_height - 1;
277 do {
278 fb_write_pixel(ptr1, thispix);
279 fb_write_pixel(ptr2, thispix);
280 ptr1 += G.scr_fix.line_length;
281 ptr2 += G.scr_fix.line_length;
282 } while (--cnt >= 0);
283}
284
285
286
287
288
289
290
291
292static void fb_drawfullrectangle(int nx1pos, int ny1pos, int nx2pos, int ny2pos,
293 unsigned char nred, unsigned char ngreen, unsigned char nblue)
294{
295 int cnt1, cnt2, nypos;
296 unsigned thispix;
297 unsigned char *ptr;
298
299 thispix = fb_pixel_value(nred, ngreen, nblue);
300
301 cnt1 = ny2pos - ny1pos;
302 nypos = ny1pos;
303 do {
304 ptr = G.addr + nypos * G.scr_fix.line_length + nx1pos * G.bytes_per_pixel;
305 cnt2 = nx2pos - nx1pos;
306 do {
307 fb_write_pixel(ptr, thispix);
308 ptr += G.bytes_per_pixel;
309 } while (--cnt2 >= 0);
310
311 nypos++;
312 } while (--cnt1 >= 0);
313}
314
315
316
317
318
319
320static void fb_drawprogressbar(unsigned percent)
321{
322 int left_x, top_y, pos_x;
323 unsigned width, height;
324
325
326 left_x = G.nbar_posx;
327 top_y = G.nbar_posy;
328 width = G.nbar_width - 1;
329 height = G.nbar_height - 1;
330 if ((int)(height | width) < 0)
331 return;
332
333 fb_drawrectangle();
334
335
336 left_x++;
337 top_y++;
338 width -= 2;
339 height -= 2;
340 if ((int)(height | width) < 0)
341 return;
342
343 pos_x = left_x;
344 if (percent > 0) {
345 int i, y;
346
347
348 pos_x += (unsigned)(width * percent) / 100;
349
350 y = top_y;
351 i = height;
352 if (height == 0)
353 height++;
354 while (i >= 0) {
355
356
357 unsigned gray_level = 100 + (unsigned)i*100 / height;
358 fb_drawfullrectangle(
359 left_x, y, pos_x, y,
360 gray_level, gray_level, gray_level);
361 y++;
362 i--;
363 }
364 }
365
366 fb_drawfullrectangle(
367 pos_x, top_y,
368 left_x + width, top_y + height,
369 G.nbar_colr, G.nbar_colg, G.nbar_colb);
370}
371
372
373
374
375
376static void fb_drawimage(void)
377{
378 FILE *theme_file;
379 char *read_ptr;
380 unsigned char *pixline;
381 unsigned i, j, width, height, line_size;
382
383 if (LONE_DASH(G.image_filename)) {
384 theme_file = stdin;
385 } else {
386 int fd = open_zipped(G.image_filename, 0);
387 if (fd < 0)
388 bb_simple_perror_msg_and_die(G.image_filename);
389 theme_file = xfdopen_for_read(fd);
390 }
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405#define concat_buf bb_common_bufsiz1
406 setup_common_bufsiz();
407
408 read_ptr = concat_buf;
409 while (1) {
410 int w, h, max_color_val;
411 int rem = concat_buf + COMMON_BUFSIZE - read_ptr;
412 if (rem < 2
413 || fgets(read_ptr, rem, theme_file) == NULL
414 ) {
415 bb_error_msg_and_die("bad PPM file '%s'", G.image_filename);
416 }
417 read_ptr = strchrnul(read_ptr, '#');
418 *read_ptr = '\0';
419 if (sscanf(concat_buf, "P6 %u %u %u", &w, &h, &max_color_val) == 3
420 && max_color_val <= 255
421 ) {
422 width = w;
423 height = h;
424 break;
425 }
426 }
427
428 line_size = width*3;
429 pixline = xmalloc(line_size);
430
431 if ((width + G.img_posx) > G.scr_var.xres)
432 width = G.scr_var.xres - G.img_posx;
433 if ((height + G.img_posy) > G.scr_var.yres)
434 height = G.scr_var.yres - G.img_posy;
435 for (j = 0; j < height; j++) {
436 unsigned char *pixel;
437 unsigned char *src;
438
439 if (fread(pixline, 1, line_size, theme_file) != line_size)
440 bb_error_msg_and_die("bad PPM file '%s'", G.image_filename);
441 pixel = pixline;
442 src = G.addr + (G.img_posy + j) * G.scr_fix.line_length + G.img_posx * G.bytes_per_pixel;
443 for (i = 0; i < width; i++) {
444 unsigned thispix = fb_pixel_value(pixel[0], pixel[1], pixel[2]);
445 fb_write_pixel(src, thispix);
446 src += G.bytes_per_pixel;
447 pixel += 3;
448 }
449 }
450 free(pixline);
451 fclose(theme_file);
452}
453
454
455
456
457
458
459static void init(const char *cfg_filename)
460{
461 static const char param_names[] ALIGN1 =
462 "BAR_WIDTH\0" "BAR_HEIGHT\0"
463 "BAR_LEFT\0" "BAR_TOP\0"
464 "BAR_R\0" "BAR_G\0" "BAR_B\0"
465 "IMG_LEFT\0" "IMG_TOP\0"
466#if DEBUG
467 "DEBUG\0"
468#endif
469 ;
470 char *token[2];
471 parser_t *parser = config_open2(cfg_filename, xfopen_stdin);
472 while (config_read(parser, token, 2, 2, "#=",
473 (PARSE_NORMAL | PARSE_MIN_DIE) & ~(PARSE_TRIM | PARSE_COLLAPSE))) {
474 unsigned val = xatoi_positive(token[1]);
475 int i = index_in_strings(param_names, token[0]);
476 if (i < 0)
477 bb_error_msg_and_die("syntax error: %s", token[0]);
478 if (i >= 0 && i < 9)
479 G.ns[i] = val;
480#if DEBUG
481 if (i == 9) {
482 G.bdebug_messages = val;
483 if (G.bdebug_messages)
484 G.logfile_fd = xfopen_for_write("/tmp/fbsplash.log");
485 }
486#endif
487 }
488 config_close(parser);
489}
490
491
492int fbsplash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
493int fbsplash_main(int argc UNUSED_PARAM, char **argv)
494{
495 const char *fb_device, *cfg_filename, *fifo_filename;
496 FILE *fp = fp;
497 char *num_buf;
498 unsigned num;
499 bool bCursorOff;
500
501 INIT_G();
502
503
504 fb_device = "/dev/fb0";
505 cfg_filename = NULL;
506 fifo_filename = NULL;
507 bCursorOff = 1 & getopt32(argv, "cs:d:i:f:",
508 &G.image_filename, &fb_device, &cfg_filename, &fifo_filename);
509
510
511 if (cfg_filename)
512 init(cfg_filename);
513
514
515 if (!G.image_filename)
516 bb_show_usage();
517
518 fb_open(fb_device);
519
520 if (fifo_filename && bCursorOff) {
521
522 full_write(STDOUT_FILENO, ESC"[?25l", 6);
523 }
524
525 fb_drawimage();
526
527 if (!fifo_filename)
528 return EXIT_SUCCESS;
529
530 fp = xfopen_stdin(fifo_filename);
531 if (fp != stdin) {
532
533
534
535
536
537
538
539
540
541
542
543 open(fifo_filename, O_WRONLY);
544 }
545
546 fb_drawprogressbar(0);
547
548
549
550
551 while ((num_buf = xmalloc_fgetline(fp)) != NULL) {
552 if (is_prefixed_with(num_buf, "exit")) {
553 DEBUG_MESSAGE("exit");
554 break;
555 }
556 num = atoi(num_buf);
557 if (isdigit(num_buf[0]) && (num <= 100)) {
558#if DEBUG
559 DEBUG_MESSAGE(itoa(num));
560#endif
561 fb_drawprogressbar(num);
562 }
563 free(num_buf);
564 }
565
566 if (bCursorOff)
567 full_write(STDOUT_FILENO, ESC"[?25h", 6);
568
569 return EXIT_SUCCESS;
570}
571