1: /* Simple termios based progress bar
2: *
3: * Copyright (c) 2012-2015 Joachim Nilsson <troglobit@gmail.com>
4: *
5: * Permission to use, copy, modify, and/or distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
18: #include <limits.h> /* INT_MAX */
19: #include <stdio.h>
20: #include <string.h>
21:
22: #include "conio.h"
23:
24: #define SPINNER_THROB ".oOo"
25: #define SPINNER_PULSAR ".oO°Oo."
26: #define SPINNER_ARROW "v<^>"
27: #define SPINNER_STAR ".oO@*"
28: #define SPINNER_DEFAULT "|/-\\"
29:
30: static char spinner(char *style)
31: {
32: size_t num;
33: static unsigned int i = 0;
34:
35: if (!style)
36: style = SPINNER_DEFAULT;
37: num = strlen(style);
38:
39: return style[i++ % num]; /* % Number of states in style */
40: }
41:
42: /**
43: * progress - Advanced ASCII progress bar with spinner
44: * @percent: Start first call with this set to 0, end with 100
45: * @max_width: Max width of progress bar, in total characters.
46: *
47: * This function draws an advanced ASCII progressbar at the current
48: * line. It always start from the first column.
49: *
50: * The progress bar will hide the cursor if started with @percent 0 and
51: * show it again at the end, when called with @percent 100.
52: *
53: * While being called with the same percentage the spinner will spin,
54: * to show the user the process hasn't frozen.
55: *
56: * If the output TTY cannot interpret control characters, like \r, it is
57: * advised to instead used the progress_simple() function.
58: */
59: void progress(int percent, int max_width)
60: {
61: int i, bar;
62:
63: /* Adjust for progress bar overhead */
64: max_width -= 10;
65:
66: if (0 == percent)
67: hidecursor();
68:
69: fprintf(stderr, "\r%3d%% %c [", percent, spinner(NULL));
70:
71: bar = percent * max_width / 100;
72: for (i = 0; i < max_width; i++) {
73: if (i > bar)
74: fputc(' ', stderr);
75: else if (i == bar)
76: fputc('>', stderr);
77: else
78: fputc('=', stderr);
79: }
80:
81: fprintf(stderr, "]");
82: if (100 == percent) {
83: showcursor();
84: fputc('\n', stderr);
85: }
86: }
87:
88: void progress_simple(int percent)
89: {
90: static int last = 1;
91: int ratio, numout;
92:
93: if (!percent && last) {
94: last = 0;
95: fputs("0% 25% 50% 75% 100%\n"
96: "|---------+---------+---------+---------|\n"
97: "|", stderr);
98: return;
99: }
100:
101: ratio = 40 * percent / 100;
102: numout = ratio - last;
103:
104: if (ratio <= last)
105: return;
106:
107: last = ratio;
108:
109: while (numout--) {
110: if (ratio != 40 || numout)
111: putc('=', stderr);
112: else
113: putc('|', stderr);
114: }
115: }
116:
117: #ifdef UNITTEST
118: #include <stdlib.h> /* atexit() */
119: #include <unistd.h> /* usleep() */
120:
121: #define MAX_WIDTH 80
122: #define msleep(msec) usleep(msec * 1000)
123:
124: static void bye(void)
125: {
126: showcursor();
127: }
128:
129: static void testit(int fn, int percent)
130: {
131: if (fn)
132: progress(percent, MAX_WIDTH);
133: else
134: progress_simple(percent);
135: }
136:
137: static void test(int fn)
138: {
139: int i, percent, block = 0, num = 85;
140:
141: while (block < num) {
142: percent = block * 100 / num;
143: for (i = 0; i < 10; i++) {
144: testit(fn, percent);
145: msleep(5);
146: }
147: block++;
148: msleep(10);
149: }
150: testit(fn, 100);
151: }
152:
153: int main(void)
154: {
155: atexit(bye);
156: hidecursor();
157:
158: printf("\nAdvanced:\n");
159: test(1);
160: printf("\nSimple:\n");
161: test(0);
162: printf("\n");
163:
164: return 0;
165: }
166: #endif /* UNITTEST */
167:
168: /**
169: * Local Variables:
170: * compile-command: "make V=1 -f progress.mk"
171: * version-control: t
172: * indent-tabs-mode: t
173: * c-file-style: "linux"
174: * End:
175: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>