~mmach/netext73/busybox

« back to all changes in this revision

Viewing changes to .pc/platform-linux.diff/miscutils/fbsplash.c

  • Committer: mmach
  • Date: 2023-07-06 04:40:25 UTC
  • Revision ID: netbit73@gmail.com-20230706044025-2ia9985i8wzdn2a7
1.36.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* vi: set sw=4 ts=4: */
 
2
/*
 
3
 * Copyright (C) 2008 Michele Sanges <michele.sanges@gmail.com>
 
4
 *
 
5
 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 
6
 *
 
7
 * Usage:
 
8
 * - use kernel option 'vga=xxx' or otherwise enable framebuffer device.
 
9
 * - put somewhere fbsplash.cfg file and an image in .ppm format.
 
10
 * - run applet: $ setsid fbsplash [params] &
 
11
 *      -c: hide cursor
 
12
 *      -d /dev/fbN: framebuffer device (if not /dev/fb0)
 
13
 *      -s path_to_image_file (can be "-" for stdin)
 
14
 *      -i path_to_cfg_file
 
15
 *      -f path_to_fifo (can be "-" for stdin)
 
16
 * - if you want to run it only in presence of a kernel parameter
 
17
 *   (for example fbsplash=on), use:
 
18
 *   grep -q "fbsplash=on" </proc/cmdline && setsid fbsplash [params]
 
19
 * - commands for fifo:
 
20
 *   "NN" (ASCII decimal number) - percentage to show on progress bar.
 
21
 *   "exit" (or just close fifo) - well you guessed it.
 
22
 */
 
23
//config:config FBSPLASH
 
24
//config:       bool "fbsplash (26 kb)"
 
25
//config:       default y
 
26
//config:       help
 
27
//config:       Shows splash image and progress bar on framebuffer device.
 
28
//config:       Can be used during boot phase of an embedded device.
 
29
//config:       Usage:
 
30
//config:       - use kernel option 'vga=xxx' or otherwise enable fb device.
 
31
//config:       - put somewhere fbsplash.cfg file and an image in .ppm format.
 
32
//config:       - $ setsid fbsplash [params] &
 
33
//config:           -c: hide cursor
 
34
//config:           -d /dev/fbN: framebuffer device (if not /dev/fb0)
 
35
//config:           -s path_to_image_file (can be "-" for stdin)
 
36
//config:           -i path_to_cfg_file (can be "-" for stdin)
 
37
//config:           -f path_to_fifo (can be "-" for stdin)
 
38
//config:       - if you want to run it only in presence of kernel parameter:
 
39
//config:           grep -q "fbsplash=on" </proc/cmdline && setsid fbsplash [params] &
 
40
//config:       - commands for fifo:
 
41
//config:           "NN" (ASCII decimal number) - percentage to show on progress bar
 
42
//config:           "exit" - well you guessed it
 
43
 
 
44
//applet:IF_FBSPLASH(APPLET(fbsplash, BB_DIR_SBIN, BB_SUID_DROP))
 
45
 
 
46
//kbuild:lib-$(CONFIG_FBSPLASH) += fbsplash.o
 
47
 
 
48
//usage:#define fbsplash_trivial_usage
 
49
//usage:       "-s IMGFILE [-c] [-d DEV] [-i INIFILE] [-f CMD]"
 
50
//usage:#define fbsplash_full_usage "\n\n"
 
51
//usage:       "        -s      Image"
 
52
//usage:     "\n        -c      Hide cursor"
 
53
//usage:     "\n        -d      Framebuffer device (default /dev/fb0)"
 
54
//usage:     "\n        -i      Config file (var=value):"
 
55
//usage:     "\n                        BAR_LEFT,BAR_TOP,BAR_WIDTH,BAR_HEIGHT"
 
56
//usage:     "\n                        BAR_R,BAR_G,BAR_B,IMG_LEFT,IMG_TOP"
 
57
//usage:     "\n        -f      Control pipe (else exit after drawing image)"
 
58
//usage:     "\n                        commands: 'NN' (% for progress bar) or 'exit'"
 
59
 
 
60
#include "libbb.h"
 
61
#include "common_bufsiz.h"
 
62
#include <linux/fb.h>
 
63
 
 
64
/* If you want logging messages on /tmp/fbsplash.log... */
 
65
#define DEBUG 0
 
66
 
 
67
#define ESC "\033"
 
68
 
 
69
struct globals {
 
70
#if DEBUG
 
71
        bool bdebug_messages;   // enable/disable logging
 
72
        FILE *logfile_fd;       // log file
 
73
#endif
 
74
        unsigned char *addr;    // pointer to framebuffer memory
 
75
        unsigned ns[9];         // n-parameters
 
76
        const char *image_filename;
 
77
        struct fb_var_screeninfo scr_var;
 
78
        struct fb_fix_screeninfo scr_fix;
 
79
        unsigned bytes_per_pixel;
 
80
        // cached (8 - scr_var.COLOR.length):
 
81
        unsigned red_shift;
 
82
        unsigned green_shift;
 
83
        unsigned blue_shift;
 
84
};
 
85
#define G (*ptr_to_globals)
 
86
#define INIT_G() do { \
 
87
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
 
88
} while (0)
 
89
 
 
90
#define nbar_width      ns[0]   // progress bar width
 
91
#define nbar_height     ns[1]   // progress bar height
 
92
#define nbar_posx       ns[2]   // progress bar horizontal position
 
93
#define nbar_posy       ns[3]   // progress bar vertical position
 
94
#define nbar_colr       ns[4]   // progress bar color red component
 
95
#define nbar_colg       ns[5]   // progress bar color green component
 
96
#define nbar_colb       ns[6]   // progress bar color blue component
 
97
#define img_posx        ns[7]   // image horizontal position
 
98
#define img_posy        ns[8]   // image vertical position
 
99
 
 
100
#if DEBUG
 
101
#define DEBUG_MESSAGE(strMessage, args...) \
 
102
        if (G.bdebug_messages) { \
 
103
                fprintf(G.logfile_fd, "[%s][%s] - %s\n", \
 
104
                __FILE__, __FUNCTION__, strMessage);    \
 
105
        }
 
106
#else
 
107
#define DEBUG_MESSAGE(...) ((void)0)
 
108
#endif
 
109
 
 
110
/**
 
111
 * Configure palette for RGB:332
 
112
 */
 
113
static void fb_setpal(int fd)
 
114
{
 
115
        struct fb_cmap cmap;
 
116
        /* fb colors are 16 bit */
 
117
        unsigned short red[256], green[256], blue[256];
 
118
        unsigned i;
 
119
 
 
120
        /* RGB:332 */
 
121
        for (i = 0; i < 256; i++) {
 
122
                /* Color is encoded in pixel value as rrrgggbb.
 
123
                 * 3-bit color is mapped to 16-bit one as:
 
124
                 * 000 -> 00000000 00000000
 
125
                 * 001 -> 00100100 10010010
 
126
                 * ...
 
127
                 * 011 -> 01101101 10110110
 
128
                 * 100 -> 10010010 01001001
 
129
                 * ...
 
130
                 * 111 -> 11111111 11111111
 
131
                 */
 
132
                red[i]   = (( i >> 5       ) * 0x9249) >> 2; // rrr * 00 10010010 01001001 >> 2
 
133
                green[i] = (((i >> 2) & 0x7) * 0x9249) >> 2; // ggg * 00 10010010 01001001 >> 2
 
134
                /* 2-bit color is easier: */
 
135
                blue[i]  =  ( i       & 0x3) * 0x5555; // bb * 01010101 01010101
 
136
        }
 
137
 
 
138
        cmap.start = 0;
 
139
        cmap.len   = 256;
 
140
        cmap.red   = red;
 
141
        cmap.green = green;
 
142
        cmap.blue  = blue;
 
143
        cmap.transp = 0;
 
144
 
 
145
        xioctl(fd, FBIOPUTCMAP, &cmap);
 
146
}
 
147
 
 
148
/**
 
149
 * Open and initialize the framebuffer device
 
150
 * \param *strfb_device pointer to framebuffer device
 
151
 */
 
152
static void fb_open(const char *strfb_device)
 
153
{
 
154
        int fbfd = xopen(strfb_device, O_RDWR);
 
155
 
 
156
        // framebuffer properties
 
157
        xioctl(fbfd, FBIOGET_VSCREENINFO, &G.scr_var);
 
158
        xioctl(fbfd, FBIOGET_FSCREENINFO, &G.scr_fix);
 
159
 
 
160
        switch (G.scr_var.bits_per_pixel) {
 
161
        case 8:
 
162
                fb_setpal(fbfd);
 
163
                break;
 
164
 
 
165
        case 16:
 
166
        case 24:
 
167
        case 32:
 
168
                break;
 
169
 
 
170
        default:
 
171
                bb_error_msg_and_die("unsupported %u bpp", (int)G.scr_var.bits_per_pixel);
 
172
                break;
 
173
        }
 
174
 
 
175
        G.red_shift   = 8 - G.scr_var.red.length;
 
176
        G.green_shift = 8 - G.scr_var.green.length;
 
177
        G.blue_shift  = 8 - G.scr_var.blue.length;
 
178
        G.bytes_per_pixel = (G.scr_var.bits_per_pixel + 7) >> 3;
 
179
 
 
180
        // map the device in memory
 
181
        G.addr = mmap(NULL,
 
182
                        (G.scr_var.yres_virtual ?: G.scr_var.yres) * G.scr_fix.line_length,
 
183
                        PROT_WRITE, MAP_SHARED, fbfd, 0);
 
184
        if (G.addr == MAP_FAILED)
 
185
                bb_simple_perror_msg_and_die("mmap");
 
186
 
 
187
        // point to the start of the visible screen
 
188
        G.addr += G.scr_var.yoffset * G.scr_fix.line_length + G.scr_var.xoffset * G.bytes_per_pixel;
 
189
        close(fbfd);
 
190
}
 
191
 
 
192
 
 
193
/**
 
194
 * Return pixel value of the passed RGB color.
 
195
 * This is performance critical fn.
 
196
 */
 
197
static unsigned fb_pixel_value(unsigned r, unsigned g, unsigned b)
 
198
{
 
199
        /* We assume that the r,g,b values are <= 255 */
 
200
 
 
201
        if (G.bytes_per_pixel == 1) {
 
202
                r = r        & 0xe0; // 3-bit red
 
203
                g = (g >> 3) & 0x1c; // 3-bit green
 
204
                b =  b >> 6;         // 2-bit blue
 
205
                return r + g + b;
 
206
        }
 
207
        if (G.bytes_per_pixel == 2) {
 
208
                // ARM PL110 on Integrator/CP has RGBA5551 bit arrangement.
 
209
                // We want to support bit locations like that.
 
210
                //
 
211
                // First shift out unused bits
 
212
                r = r >> G.red_shift;
 
213
                g = g >> G.green_shift;
 
214
                b = b >> G.blue_shift;
 
215
                // Then shift the remaining bits to their offset
 
216
                return (r << G.scr_var.red.offset) +
 
217
                        (g << G.scr_var.green.offset) +
 
218
                        (b << G.scr_var.blue.offset);
 
219
        }
 
220
        // RGB 888
 
221
        return b + (g << 8) + (r << 16);
 
222
}
 
223
 
 
224
/**
 
225
 * Draw pixel on framebuffer
 
226
 */
 
227
static void fb_write_pixel(unsigned char *addr, unsigned pixel)
 
228
{
 
229
        switch (G.bytes_per_pixel) {
 
230
        case 1:
 
231
                *addr = pixel;
 
232
                break;
 
233
        case 2:
 
234
                *(uint16_t *)addr = pixel;
 
235
                break;
 
236
        case 4:
 
237
                *(uint32_t *)addr = pixel;
 
238
                break;
 
239
        default: // 24 bits per pixel
 
240
                addr[0] = pixel;
 
241
                addr[1] = pixel >> 8;
 
242
                addr[2] = pixel >> 16;
 
243
        }
 
244
}
 
245
 
 
246
 
 
247
/**
 
248
 * Draw hollow rectangle on framebuffer
 
249
 */
 
250
static void fb_drawrectangle(void)
 
251
{
 
252
        int cnt;
 
253
        unsigned thispix;
 
254
        unsigned char *ptr1, *ptr2;
 
255
        unsigned char nred = G.nbar_colr/2;
 
256
        unsigned char ngreen =  G.nbar_colg/2;
 
257
        unsigned char nblue = G.nbar_colb/2;
 
258
 
 
259
        thispix = fb_pixel_value(nred, ngreen, nblue);
 
260
 
 
261
        // horizontal lines
 
262
        ptr1 = G.addr + G.nbar_posy * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
 
263
        ptr2 = G.addr + (G.nbar_posy + G.nbar_height - 1) * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
 
264
        cnt = G.nbar_width - 1;
 
265
        do {
 
266
                fb_write_pixel(ptr1, thispix);
 
267
                fb_write_pixel(ptr2, thispix);
 
268
                ptr1 += G.bytes_per_pixel;
 
269
                ptr2 += G.bytes_per_pixel;
 
270
        } while (--cnt >= 0);
 
271
 
 
272
        // vertical lines
 
273
        ptr1 = G.addr + G.nbar_posy * G.scr_fix.line_length + G.nbar_posx * G.bytes_per_pixel;
 
274
        ptr2 = G.addr + G.nbar_posy * G.scr_fix.line_length + (G.nbar_posx + G.nbar_width - 1) * G.bytes_per_pixel;
 
275
        cnt = G.nbar_height - 1;
 
276
        do {
 
277
                fb_write_pixel(ptr1, thispix);
 
278
                fb_write_pixel(ptr2, thispix);
 
279
                ptr1 += G.scr_fix.line_length;
 
280
                ptr2 += G.scr_fix.line_length;
 
281
        } while (--cnt >= 0);
 
282
}
 
283
 
 
284
 
 
285
/**
 
286
 * Draw filled rectangle on framebuffer
 
287
 * \param nx1pos,ny1pos upper left position
 
288
 * \param nx2pos,ny2pos down right position
 
289
 * \param nred,ngreen,nblue rgb color
 
290
 */
 
291
static void fb_drawfullrectangle(int nx1pos, int ny1pos, int nx2pos, int ny2pos,
 
292
        unsigned char nred, unsigned char ngreen, unsigned char nblue)
 
293
{
 
294
        int cnt1, cnt2, nypos;
 
295
        unsigned thispix;
 
296
        unsigned char *ptr;
 
297
 
 
298
        thispix = fb_pixel_value(nred, ngreen, nblue);
 
299
 
 
300
        cnt1 = ny2pos - ny1pos;
 
301
        nypos = ny1pos;
 
302
        do {
 
303
                ptr = G.addr + nypos * G.scr_fix.line_length + nx1pos * G.bytes_per_pixel;
 
304
                cnt2 = nx2pos - nx1pos;
 
305
                do {
 
306
                        fb_write_pixel(ptr, thispix);
 
307
                        ptr += G.bytes_per_pixel;
 
308
                } while (--cnt2 >= 0);
 
309
 
 
310
                nypos++;
 
311
        } while (--cnt1 >= 0);
 
312
}
 
313
 
 
314
 
 
315
/**
 
316
 * Draw a progress bar on framebuffer
 
317
 * \param percent percentage of loading
 
318
 */
 
319
static void fb_drawprogressbar(unsigned percent)
 
320
{
 
321
        int left_x, top_y, pos_x;
 
322
        unsigned width, height;
 
323
 
 
324
        // outer box
 
325
        left_x = G.nbar_posx;
 
326
        top_y = G.nbar_posy;
 
327
        width = G.nbar_width - 1;
 
328
        height = G.nbar_height - 1;
 
329
        if ((int)(height | width) < 0)
 
330
                return;
 
331
        // NB: "width" of 1 actually makes rect with width of 2!
 
332
        fb_drawrectangle();
 
333
 
 
334
        // inner "empty" rectangle
 
335
        left_x++;
 
336
        top_y++;
 
337
        width -= 2;
 
338
        height -= 2;
 
339
        if ((int)(height | width) < 0)
 
340
                return;
 
341
 
 
342
        pos_x = left_x;
 
343
        if (percent > 0) {
 
344
                int i, y;
 
345
 
 
346
                // actual progress bar
 
347
                pos_x += (unsigned)(width * percent) / 100;
 
348
 
 
349
                y = top_y;
 
350
                i = height;
 
351
                if (height == 0)
 
352
                        height++; // divide by 0 is bad
 
353
                while (i >= 0) {
 
354
                        // draw one-line thick "rectangle"
 
355
                        // top line will have gray lvl 200, bottom one 100
 
356
                        unsigned gray_level = 100 + (unsigned)i*100 / height;
 
357
                        fb_drawfullrectangle(
 
358
                                        left_x, y, pos_x, y,
 
359
                                        gray_level, gray_level, gray_level);
 
360
                        y++;
 
361
                        i--;
 
362
                }
 
363
        }
 
364
 
 
365
        fb_drawfullrectangle(
 
366
                        pos_x, top_y,
 
367
                        left_x + width, top_y + height,
 
368
                        G.nbar_colr, G.nbar_colg, G.nbar_colb);
 
369
}
 
370
 
 
371
 
 
372
/**
 
373
 * Draw image from PPM file
 
374
 */
 
375
static void fb_drawimage(void)
 
376
{
 
377
        FILE *theme_file;
 
378
        char *read_ptr;
 
379
        unsigned char *pixline;
 
380
        unsigned i, j, width, height, line_size;
 
381
 
 
382
        if (LONE_DASH(G.image_filename)) {
 
383
                theme_file = stdin;
 
384
        } else {
 
385
                int fd = open_zipped(G.image_filename, /*fail_if_not_compressed:*/ 0);
 
386
                if (fd < 0)
 
387
                        bb_simple_perror_msg_and_die(G.image_filename);
 
388
                theme_file = xfdopen_for_read(fd);
 
389
        }
 
390
 
 
391
        /* Parse ppm header:
 
392
         * - Magic: two characters "P6".
 
393
         * - Whitespace (blanks, TABs, CRs, LFs).
 
394
         * - A width, formatted as ASCII characters in decimal.
 
395
         * - Whitespace.
 
396
         * - A height, ASCII decimal.
 
397
         * - Whitespace.
 
398
         * - The maximum color value, ASCII decimal, in 0..65535
 
399
         * - Newline or other single whitespace character.
 
400
         *   (we support newline only)
 
401
         * - A raster of Width * Height pixels in triplets of rgb
 
402
         *   in pure binary by 1 or 2 bytes. (we support only 1 byte)
 
403
         */
 
404
#define concat_buf bb_common_bufsiz1
 
405
        setup_common_bufsiz();
 
406
 
 
407
        read_ptr = concat_buf;
 
408
        while (1) {
 
409
                int w, h, max_color_val;
 
410
                int rem = concat_buf + COMMON_BUFSIZE - read_ptr;
 
411
                if (rem < 2
 
412
                 || fgets(read_ptr, rem, theme_file) == NULL
 
413
                ) {
 
414
                        bb_error_msg_and_die("bad PPM file '%s'", G.image_filename);
 
415
                }
 
416
                read_ptr = strchrnul(read_ptr, '#');
 
417
                *read_ptr = '\0'; /* ignore #comments */
 
418
                if (sscanf(concat_buf, "P6 %u %u %u", &w, &h, &max_color_val) == 3
 
419
                 && max_color_val <= 255
 
420
                ) {
 
421
                        width = w; /* w is on stack, width may be in register */
 
422
                        height = h;
 
423
                        break;
 
424
                }
 
425
        }
 
426
 
 
427
        line_size = width*3;
 
428
        pixline = xmalloc(line_size);
 
429
 
 
430
        if ((width + G.img_posx) > G.scr_var.xres)
 
431
                width = G.scr_var.xres - G.img_posx;
 
432
        if ((height + G.img_posy) > G.scr_var.yres)
 
433
                height = G.scr_var.yres - G.img_posy;
 
434
        for (j = 0; j < height; j++) {
 
435
                unsigned char *pixel;
 
436
                unsigned char *src;
 
437
 
 
438
                if (fread(pixline, 1, line_size, theme_file) != line_size)
 
439
                        bb_error_msg_and_die("bad PPM file '%s'", G.image_filename);
 
440
                pixel = pixline;
 
441
                src = G.addr + (G.img_posy + j) * G.scr_fix.line_length + G.img_posx * G.bytes_per_pixel;
 
442
                for (i = 0; i < width; i++) {
 
443
                        unsigned thispix = fb_pixel_value(pixel[0], pixel[1], pixel[2]);
 
444
                        fb_write_pixel(src, thispix);
 
445
                        src += G.bytes_per_pixel;
 
446
                        pixel += 3;
 
447
                }
 
448
        }
 
449
        free(pixline);
 
450
        fclose(theme_file);
 
451
}
 
452
 
 
453
 
 
454
/**
 
455
 * Parse configuration file
 
456
 * \param *cfg_filename name of the configuration file
 
457
 */
 
458
static void init(const char *cfg_filename)
 
459
{
 
460
        static const char param_names[] ALIGN1 =
 
461
                "BAR_WIDTH\0" "BAR_HEIGHT\0"
 
462
                "BAR_LEFT\0" "BAR_TOP\0"
 
463
                "BAR_R\0" "BAR_G\0" "BAR_B\0"
 
464
                "IMG_LEFT\0" "IMG_TOP\0"
 
465
#if DEBUG
 
466
                "DEBUG\0"
 
467
#endif
 
468
                ;
 
469
        char *token[2];
 
470
        parser_t *parser = config_open2(cfg_filename, xfopen_stdin);
 
471
        while (config_read(parser, token, 2, 2, "#=",
 
472
                                (PARSE_NORMAL | PARSE_MIN_DIE) & ~(PARSE_TRIM | PARSE_COLLAPSE))) {
 
473
                unsigned val = xatoi_positive(token[1]);
 
474
                int i = index_in_strings(param_names, token[0]);
 
475
                if (i < 0)
 
476
                        bb_error_msg_and_die("syntax error: %s", token[0]);
 
477
                if (i >= 0 && i < 9)
 
478
                        G.ns[i] = val;
 
479
#if DEBUG
 
480
                if (i == 9) {
 
481
                        G.bdebug_messages = val;
 
482
                        if (G.bdebug_messages)
 
483
                                G.logfile_fd = xfopen_for_write("/tmp/fbsplash.log");
 
484
                }
 
485
#endif
 
486
        }
 
487
        config_close(parser);
 
488
}
 
489
 
 
490
 
 
491
int fbsplash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 
492
int fbsplash_main(int argc UNUSED_PARAM, char **argv)
 
493
{
 
494
        const char *fb_device, *cfg_filename, *fifo_filename;
 
495
        FILE *fp = fp; // for compiler
 
496
        char *num_buf;
 
497
        unsigned num;
 
498
        bool bCursorOff;
 
499
 
 
500
        INIT_G();
 
501
 
 
502
        // parse command line options
 
503
        fb_device = "/dev/fb0";
 
504
        cfg_filename = NULL;
 
505
        fifo_filename = NULL;
 
506
        bCursorOff = 1 & getopt32(argv, "cs:d:i:f:",
 
507
                        &G.image_filename, &fb_device, &cfg_filename, &fifo_filename);
 
508
 
 
509
        // parse configuration file
 
510
        if (cfg_filename)
 
511
                init(cfg_filename);
 
512
 
 
513
        // We must have -s IMG
 
514
        if (!G.image_filename)
 
515
                bb_show_usage();
 
516
 
 
517
        fb_open(fb_device);
 
518
 
 
519
        if (fifo_filename && bCursorOff) {
 
520
                // hide cursor (BEFORE any fb ops)
 
521
                full_write(STDOUT_FILENO, ESC"[?25l", 6);
 
522
        }
 
523
 
 
524
        fb_drawimage();
 
525
 
 
526
        if (!fifo_filename)
 
527
                return EXIT_SUCCESS;
 
528
 
 
529
        fp = xfopen_stdin(fifo_filename);
 
530
        if (fp != stdin) {
 
531
                // For named pipes, we want to support this:
 
532
                //  mkfifo cmd_pipe
 
533
                //  fbsplash -f cmd_pipe .... &
 
534
                //  ...
 
535
                //  echo 33 >cmd_pipe
 
536
                //  ...
 
537
                //  echo 66 >cmd_pipe
 
538
                // This means that we don't want fbsplash to get EOF
 
539
                // when last writer closes input end.
 
540
                // The simplest way is to open fifo for writing too
 
541
                // and become an additional writer :)
 
542
                open(fifo_filename, O_WRONLY); // errors are ignored
 
543
        }
 
544
 
 
545
        fb_drawprogressbar(0);
 
546
        // Block on read, waiting for some input.
 
547
        // Use of <stdio.h> style I/O allows to correctly
 
548
        // handle a case when we have many buffered lines
 
549
        // already in the pipe
 
550
        while ((num_buf = xmalloc_fgetline(fp)) != NULL) {
 
551
                if (is_prefixed_with(num_buf, "exit")) {
 
552
                        DEBUG_MESSAGE("exit");
 
553
                        break;
 
554
                }
 
555
                num = atoi(num_buf);
 
556
                if (isdigit(num_buf[0]) && (num <= 100)) {
 
557
#if DEBUG
 
558
                        DEBUG_MESSAGE(itoa(num));
 
559
#endif
 
560
                        fb_drawprogressbar(num);
 
561
                }
 
562
                free(num_buf);
 
563
        }
 
564
 
 
565
        if (bCursorOff) // restore cursor
 
566
                full_write(STDOUT_FILENO, ESC"[?25h", 6);
 
567
 
 
568
        return EXIT_SUCCESS;
 
569
}