1 #ifdef _WIN32
2
3 #include <windows.h>
4 #include <conio.h>
5
6 #else
7
8 #include <unistd.h> // for close()
9 #include <fcntl.h> // for open()
10 #include <sys/ioctl.h>
11
12 #endif
13
14 #include "libtorrent/config.hpp"
15
16 #include "print.hpp"
17
18 #include <cstdlib> // for atoi
19 #include <cstring> // for strlen
20 #include <cmath>
21 #include <algorithm> // for std::min
22 #include <iterator> // for back_inserter
23
esc(char const * code)24 char const* esc(char const* code)
25 {
26 // this is a silly optimization
27 // to avoid copying of strings
28 enum { num_strings = 200 };
29 static char buf[num_strings][20];
30 static int round_robin = 0;
31 char* ret = buf[round_robin];
32 ++round_robin;
33 if (round_robin >= num_strings) round_robin = 0;
34 ret[0] = '\033';
35 ret[1] = '[';
36 int i = 2;
37 int j = 0;
38 while (code[j]) ret[i++] = code[j++];
39 ret[i++] = 'm';
40 ret[i++] = 0;
41 return ret;
42 }
43
to_string(int v,int width)44 std::string to_string(int v, int width)
45 {
46 char buf[100];
47 std::snprintf(buf, sizeof(buf), "%*d", width, v);
48 return buf;
49 }
50
add_suffix_float(double val,char const * suffix)51 std::string add_suffix_float(double val, char const* suffix)
52 {
53 if (val < 0.001)
54 {
55 std::string ret;
56 ret.resize(4 + 2, ' ');
57 if (suffix) ret.resize(4 + 2 + strlen(suffix), ' ');
58 return ret;
59 }
60
61 const char* prefix[] = {"kB", "MB", "GB", "TB", "PB"};
62 const int num_prefix = sizeof(prefix) / sizeof(const char*);
63 int i = 0;
64 for (; i < num_prefix - 1; ++i)
65 {
66 val /= 1000.;
67 if (std::fabs(val) < 1000.) break;
68 }
69 char ret[100];
70 std::snprintf(ret, sizeof(ret), "%4.*f%s%s", val < 99 ? 1 : 0, val, prefix[i], suffix ? suffix : "");
71 return ret;
72 }
73
color(std::string const & s,color_code c)74 std::string color(std::string const& s, color_code c)
75 {
76 if (c == col_none) return s;
77 if (std::count(s.begin(), s.end(), ' ') == int(s.size())) return s;
78
79 char buf[1024];
80 std::snprintf(buf, sizeof(buf), "\x1b[3%dm%s\x1b[39m", c, s.c_str());
81 return buf;
82 }
83
progress_bar(int progress,int width,color_code c,char fill,char bg,std::string caption,int flags)84 std::string const& progress_bar(int progress, int width, color_code c
85 , char fill, char bg, std::string caption, int flags)
86 {
87 static std::string bar;
88 bar.clear();
89 bar.reserve(size_t(width + 10));
90
91 auto const progress_chars = static_cast<std::size_t>((progress * width + 500) / 1000);
92
93 if (caption.empty())
94 {
95 char code[10];
96 std::snprintf(code, sizeof(code), "\x1b[3%dm", c);
97 bar = code;
98 std::fill_n(std::back_inserter(bar), progress_chars, fill);
99 std::fill_n(std::back_inserter(bar), width - progress_chars, bg);
100 bar += esc("39");
101 }
102 else
103 {
104 // foreground color (depends a bit on background color)
105 color_code tc = col_black;
106 if (c == col_black || c == col_blue)
107 tc = col_white;
108
109 caption.resize(size_t(width), ' ');
110
111 #ifdef _WIN32
112 char const* background = "40";
113 #else
114 char const* background = "48;5;238";
115 #endif
116
117 char str[256];
118 if (flags & progress_invert)
119 std::snprintf(str, sizeof(str), "\x1b[%sm\x1b[37m%s\x1b[4%d;3%dm%s\x1b[49;39m"
120 , background, caption.substr(0, progress_chars).c_str(), c, tc
121 , caption.substr(progress_chars).c_str());
122 else
123 std::snprintf(str, sizeof(str), "\x1b[4%d;3%dm%s\x1b[%sm\x1b[37m%s\x1b[49;39m"
124 , c, tc, caption.substr(0, progress_chars).c_str(), background
125 , caption.substr(progress_chars).c_str());
126 bar = str;
127 }
128 return bar;
129 }
130
get_piece(lt::bitfield const & p,int index)131 int get_piece(lt::bitfield const& p, int index)
132 {
133 if (index < 0 || index >= p.size()) return 0;
134 return p.get_bit(index) ? 1 : 0;
135 }
136
piece_bar(lt::bitfield const & p,int width)137 std::string const& piece_bar(lt::bitfield const& p, int width)
138 {
139 #ifdef _WIN32
140 int const table_size = 5;
141 #else
142 int const table_size = 18;
143 width *= 2; // we only print one character for every two "slots"
144 #endif
145
146 double const piece_per_char = p.size() / double(width);
147 static std::string bar;
148 bar.clear();
149 bar.reserve(width * 6);
150 bar += "[";
151 if (p.size() == 0)
152 {
153 for (int i = 0; i < width; ++i) bar += ' ';
154 bar += "]";
155 return bar;
156 }
157
158 // the [piece, piece + pieces_per_char) range is the pieces that are represented by each character
159 double piece = 0;
160
161 // we print two blocks at a time, so calculate the color in pair
162 #ifndef _WIN32
163 int color[2];
164 int last_color[2] = { -1, -1};
165 #endif
166
167 for (int i = 0; i < width; ++i, piece += piece_per_char)
168 {
169 int num_pieces = 0;
170 int num_have = 0;
171 int end = (std::max)(int(piece + piece_per_char), int(piece) + 1);
172 for (int k = int(piece); k < end; ++k, ++num_pieces)
173 if (p[k]) ++num_have;
174 int const c = int(std::ceil(num_have / float((std::max)(num_pieces, 1)) * (table_size - 1)));
175
176 #ifndef _WIN32
177 color[i & 1] = c;
178
179 if ((i & 1) == 1)
180 {
181 // now, print color[0] and [1]
182 // bg determines whether we're settings foreground or background color
183 static int const bg[] = { 38, 48};
184 for (int k = 0; k < 2; ++k)
185 {
186 if (color[k] != last_color[k])
187 {
188 char buf[40];
189 std::snprintf(buf, sizeof(buf), "\x1b[%d;5;%dm", bg[k & 1], 232 + color[k]);
190 last_color[k] = color[k];
191 bar += buf;
192 }
193 }
194 bar += "\u258C";
195 }
196 #else
197 static char const table[] = {' ', '\xb0', '\xb1', '\xb2', '\xdb'};
198 bar += table[c];
199 #endif
200 }
201 bar += esc("0");
202 bar += "]";
203 return bar;
204 }
205
206 #ifndef _WIN32
207 // this function uses the block characters that splits up the glyph in 4
208 // segments and provide all combinations of a segment lit or not. This allows us
209 // to print 4 pieces per character.
piece_matrix(lt::bitfield const & p,int width,int * height)210 std::string piece_matrix(lt::bitfield const& p, int width, int* height)
211 {
212 // print two rows of pieces at a time
213 int piece = 0;
214 ++*height;
215 std::string ret;
216 ret.reserve((p.size() + width * 2 - 1) / width / 2 * 4);
217 while (piece < p.size())
218 {
219 for (int i = 0; i < width; ++i)
220 {
221 // each character has 4 pieces. store them in a byte to use for lookups
222 int const c = get_piece(p, piece)
223 | (get_piece(p, piece+1) << 1)
224 | (get_piece(p, width*2+piece) << 2)
225 | (get_piece(p, width*2+piece+1) << 3);
226
227 // we have 4 bits, 16 different combinations
228 static char const* const chars[] =
229 {
230 " ", // no bit is set 0000
231 "\u2598", // upper left 0001
232 "\u259d", // upper right 0010
233 "\u2580", // both top bits 0011
234 "\u2596", // lower left 0100
235 "\u258c", // both left bits 0101
236 "\u259e", // upper right, lower left 0110
237 "\u259b", // left and upper sides 0111
238 "\u2597", // lower right 1000
239 "\u259a", // lower right, upper left 1001
240 "\u2590", // right side 1010
241 "\u259c", // lower right, top side 1011
242 "\u2584", // both lower bits 1100
243 "\u2599", // both lower, top left 1101
244 "\u259f", // both lower, top right 1110
245 "\x1b[7m \x1b[27m" // all bits are set (full block)
246 };
247
248 ret += chars[c];
249 piece += 2;
250 }
251 ret += "\x1b[K\n";
252 ++*height;
253 piece += width * 2; // skip another row, as we've already printed it
254 }
255 return ret;
256 }
257 #else
258 // on MS-DOS terminals, we only have block characters for upper half and lower
259 // half. This lets us print two pieces per character.
piece_matrix(lt::bitfield const & p,int width,int * height)260 std::string piece_matrix(lt::bitfield const& p, int width, int* height)
261 {
262 // print two rows of pieces at a time
263 int piece = 0;
264 ++*height;
265 std::string ret;
266 ret.reserve((p.size() + width * 2 - 1) / width);
267 while (piece < p.size())
268 {
269 for (int i = 0; i < width; ++i)
270 {
271 // each character has 8 pieces. store them in a byte to use for lookups
272 // the ordering of these bits
273 int const c = get_piece(p, piece)
274 | (get_piece(p, width*2+piece) << 1);
275
276 static char const* const chars[] =
277 {
278 " ", // no piece 00
279 "\xdf", // top piece 01
280 "\xdc", // bottom piece 10
281 "\xdb" // both pieces 11
282 };
283
284 ret += chars[c];
285 ++piece;
286 }
287 ret += '\n';
288 ++*height;
289 piece += width * 2; // skip another row, as we've already printed it
290 }
291 return ret;
292 }
293 #endif
294
set_cursor_pos(int x,int y)295 void set_cursor_pos(int x, int y)
296 {
297 #ifdef _WIN32
298 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
299 COORD c = {SHORT(x), SHORT(y)};
300 SetConsoleCursorPosition(out, c);
301 #else
302 std::printf("\033[%d;%dH", y + 1, x + 1);
303 #endif
304 }
305
clear_screen()306 void clear_screen()
307 {
308 #ifdef _WIN32
309 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
310
311 COORD c = {0, 0};
312 CONSOLE_SCREEN_BUFFER_INFO si;
313 GetConsoleScreenBufferInfo(out, &si);
314 DWORD n;
315 FillConsoleOutputCharacter(out, ' ', si.dwSize.X * si.dwSize.Y, c, &n);
316 FillConsoleOutputAttribute(out, 0x7, si.dwSize.X * si.dwSize.Y, c, &n);
317 #else
318 std::printf("\033[2J");
319 #endif
320 }
321
clear_rows(int y1,int y2)322 void clear_rows(int y1, int y2)
323 {
324 if (y1 > y2) return;
325
326 #ifdef _WIN32
327 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
328
329 COORD c = {0, SHORT(y1)};
330 SetConsoleCursorPosition(out, c);
331 CONSOLE_SCREEN_BUFFER_INFO si;
332 GetConsoleScreenBufferInfo(out, &si);
333 DWORD n;
334 int num_chars = si.dwSize.X * (std::min)(si.dwSize.Y - y1, y2 - y1);
335 FillConsoleOutputCharacter(out, ' ', num_chars, c, &n);
336 FillConsoleOutputAttribute(out, 0x7, num_chars, c, &n);
337 #else
338 for (int i = y1; i < y2; ++i)
339 std::printf("\033[%d;1H\033[2K", i + 1);
340 #endif
341 }
342
terminal_size()343 std::pair<int, int> terminal_size()
344 {
345 int width = 80;
346 int height = 50;
347 #ifdef _WIN32
348 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
349 CONSOLE_SCREEN_BUFFER_INFO coninfo;
350 if (GetConsoleScreenBufferInfo(out, &coninfo))
351 {
352 width = coninfo.dwSize.X;
353 height = coninfo.srWindow.Bottom - coninfo.srWindow.Top;
354 #else
355 int tty = open("/dev/tty", O_RDONLY);
356 if (tty < 0)
357 {
358 width = 190;
359 height = 100;
360 return {width, height};
361 }
362 winsize size;
363 int ret = ioctl(tty, TIOCGWINSZ, reinterpret_cast<char*>(&size));
364 close(tty);
365 if (ret == 0)
366 {
367 width = size.ws_col;
368 height = size.ws_row;
369 #endif
370
371 if (width < 64)
372 width = 64;
373 if (height < 25)
374 height = 25;
375 }
376 else
377 {
378 width = 190;
379 height = 100;
380 }
381 return {width, height};
382 }
383
384 #ifdef _WIN32
385 void apply_ansi_code(WORD* attributes, bool* reverse, bool* support_chaining, int code)
386 {
387 static const WORD color_table[8] =
388 {
389 0, // black
390 FOREGROUND_RED, // red
391 FOREGROUND_GREEN, // green
392 FOREGROUND_RED | FOREGROUND_GREEN, // yellow
393 FOREGROUND_BLUE, // blue
394 FOREGROUND_RED | FOREGROUND_BLUE, // magenta
395 FOREGROUND_BLUE | FOREGROUND_GREEN, // cyan
396 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE // white
397 };
398
399 enum
400 {
401 foreground_mask = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
402 background_mask = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY
403 };
404
405 static const int fg_mask[2] = {foreground_mask, background_mask};
406 static const int bg_mask[2] = {background_mask, foreground_mask};
407 static const int fg_shift[2] = { 0, 4};
408 static const int bg_shift[2] = { 4, 0};
409
410 // default foreground
411 if (code == 39) code = 37;
412
413 // default background
414 if (code == 49) code = 40;
415
416 if (code == 0)
417 {
418 // reset
419 *attributes = color_table[7];
420 *reverse = false;
421 *support_chaining = true;
422 }
423 else if (code == 1)
424 {
425 // intensity
426 *attributes |= *reverse ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;
427 *support_chaining = true;
428 }
429 else if (code == 7)
430 {
431 // reverse video
432 *support_chaining = true;
433 if (*reverse) return;
434 *reverse = true;
435 int fg_col = *attributes & foreground_mask;
436 int bg_col = (*attributes & background_mask) >> 4;
437 *attributes &= ~(foreground_mask + background_mask);
438 *attributes |= fg_col << 4;
439 *attributes |= bg_col;
440 }
441 else if (code >= 30 && code <= 37)
442 {
443 // foreground color
444 *attributes &= ~fg_mask[*reverse];
445 *attributes |= color_table[code - 30] << fg_shift[*reverse];
446 *support_chaining = true;
447 }
448 else if (code >= 40 && code <= 47)
449 {
450 // background color
451 *attributes &= ~bg_mask[*reverse];
452 *attributes |= color_table[code - 40] << bg_shift[*reverse];
453 *support_chaining = true;
454 }
455 }
456 #endif
457 void print(char const* buf)
458 {
459 #ifdef _WIN32
460 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
461
462 WORD current_attributes = 7;
463 bool reverse = false;
464 SetConsoleTextAttribute(out, current_attributes);
465
466 char const* start = buf;
467 DWORD written;
468 while (*buf != 0)
469 {
470 if (*buf == '\033' && buf[1] == '[')
471 {
472 WriteFile(out, start, DWORD(buf - start), &written, nullptr);
473 buf += 2; // skip escape and '['
474 start = buf;
475 if (*buf == 0) break;
476 if (*start == 'K')
477 {
478 // this means clear the rest of the line.
479 CONSOLE_SCREEN_BUFFER_INFO sbi;
480 if (GetConsoleScreenBufferInfo(out, &sbi))
481 {
482 COORD const pos = sbi.dwCursorPosition;
483 int const width = sbi.dwSize.X;
484 int const run = width - pos.X;
485 DWORD n;
486 FillConsoleOutputAttribute(out, 0x7, run, pos, &n);
487 FillConsoleOutputCharacter(out, ' ', run, pos, &n);
488 }
489 ++buf;
490 start = buf;
491 continue;
492 }
493 else if (*start == 'J')
494 {
495 // clear rest of screen
496 CONSOLE_SCREEN_BUFFER_INFO sbi;
497 if (GetConsoleScreenBufferInfo(out, &sbi))
498 {
499 COORD pos = sbi.dwCursorPosition;
500 int width = sbi.dwSize.X;
501 int run = (width - pos.X) + width * (sbi.dwSize.Y - pos.Y - 1);
502 DWORD n;
503 FillConsoleOutputAttribute(out, 0x7, run, pos, &n);
504 FillConsoleOutputCharacter(out, ' ', run, pos, &n);
505 }
506 ++buf;
507 start = buf;
508 continue;
509 }
510 one_more:
511 while (*buf != 'm' && *buf != ';' && *buf != 0) ++buf;
512
513 // this is where we handle reset, color and reverse codes
514 if (*buf == 0) break;
515 int code = atoi(start);
516 bool support_chaining = false;
517 apply_ansi_code(¤t_attributes, &reverse, &support_chaining, code);
518 if (support_chaining)
519 {
520 if (*buf == ';')
521 {
522 ++buf;
523 start = buf;
524 goto one_more;
525 }
526 }
527 else
528 {
529 // ignore codes with multiple fields for now
530 while (*buf != 'm' && *buf != 0) ++buf;
531 }
532 SetConsoleTextAttribute(out, current_attributes);
533 ++buf; // skip 'm'
534 start = buf;
535 }
536 else
537 {
538 ++buf;
539 }
540 }
541 WriteFile(out, start, DWORD(buf - start), &written, nullptr);
542
543 #else
544 fputs(buf, stdout);
545 #endif
546 }
547