1 /** 2 * Using arsd.terminal.d is recommended as it is more mature and stable. 3 * 4 * Provides simple API for coloring and formatting text in arsd.terminal. 5 * On Windows OS it uses WinAPI functions, on POSIX systems it uses mainly ANSI codes. 6 * 7 * 8 * $(B Important notes): 9 * $(UL 10 * $(LI Font styles have no effect on windows platform.) 11 * $(LI Light background colors are not supported. Non-light equivalents are used on Posix platforms.) 12 * ) 13 * 14 * License: 15 * <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License</a> 16 * Authors: 17 * <a href="http://github.com/robik">Robert 'Robik' Pasiński</a> 18 */ 19 module consoled; 20 21 import std.typecons, std.algorithm; 22 23 24 /// Console output stream 25 enum ConsoleOutputStream 26 { 27 /// Standard output 28 stdout, 29 30 /// Standard error output 31 stderr 32 } 33 34 35 /** 36 * Console font output style 37 * 38 * Does nothing on windows. 39 */ 40 enum FontStyle 41 { 42 none = 0, /// Default 43 underline = 1, /// Underline 44 strikethrough = 2, /// Characters legible, but marked for deletion. Not widely supported. 45 bold = 4 /// Bold 46 } 47 48 alias void delegate(CloseEvent) @system CloseHandler; 49 50 /** 51 * Represents close event. 52 */ 53 struct CloseEvent 54 { 55 /// Close type 56 CloseType type; 57 58 /// Is close event blockable? 59 bool isBlockable; 60 } 61 62 /** 63 * Close type. 64 */ 65 enum CloseType 66 { 67 Interrupt, // User pressed Ctrl+C key combination. 68 Stop, // User pressed Ctrl+Break key combination. On posix it is Ctrl+Z. 69 Quit, // Posix only. User pressed Ctrl+\ key combination. 70 Other // Other close reasons. Probably unblockable. 71 } 72 73 /** 74 * Console input mode 75 */ 76 struct ConsoleInputMode 77 { 78 /// Echo printed characters? 79 bool echo = true; 80 81 /// Enable line buffering? 82 bool line = true; 83 84 /** 85 * Creates new ConsoleInputMode instance 86 * 87 * Params: 88 * echo = Echo printed characters? 89 * line = Use Line buffering? 90 */ 91 this(bool echo, bool line) 92 { 93 this.echo = echo; 94 this.line = line; 95 } 96 97 /** 98 * Console input mode with no feature enabled 99 */ 100 static ConsoleInputMode None = ConsoleInputMode(false, false); 101 } 102 103 /** 104 * Represents point in console. 105 */ 106 alias Tuple!(int, "x", int, "y") ConsolePoint; 107 108 /// Special keys 109 enum SpecialKey 110 { 111 home = 512, /// Home key 112 pageUp, /// Page Up key 113 pageDown, /// Page Down key 114 end, /// End key 115 delete_, /// Delete key 116 insert, /// Insert key 117 up, /// Arrow up key 118 down, /// Arrow down key 119 left, /// Arrow left key 120 right, /// Arrow right key 121 122 escape = 27,/// Escape key 123 tab = 9, /// Tab key 124 } 125 126 //////////////////////////////////////////////////////////////////////// 127 version(Windows) 128 { 129 private enum BG_MASK = 0xf0; 130 private enum FG_MASK = 0x0f; 131 132 import core.sys.windows.windows, std.stdio, std..string; 133 134 /// 135 enum Color : ushort 136 { 137 black = 0, /// The black color. 138 blue = 1, /// The blue color. 139 green = 2, /// The green color. 140 cyan = 3, /// The cyan color. (blue-green) 141 red = 4, /// The red color. 142 magenta = 5, /// The magenta color. (dark pink like) 143 yellow = 6, /// The yellow color. 144 lightGray = 7, /// The light gray color. (silver) 145 146 gray = 8, /// The gray color. 147 lightBlue = 9, /// The light blue color. 148 lightGreen = 10, /// The light green color. 149 lightCyan = 11, /// The light cyan color. (light blue-green) 150 lightRed = 12, /// The light red color. 151 lightMagenta = 13, /// The light magenta color. (pink) 152 lightYellow = 14, /// The light yellow color. 153 white = 15, /// The white color. 154 155 bright = 8, /// Bright flag. Use with dark colors to make them light equivalents. 156 initial = 256 /// Default color. 157 } 158 159 160 private __gshared 161 { 162 CONSOLE_SCREEN_BUFFER_INFO info; 163 HANDLE hOutput = null, hInput = null; 164 165 Color fg, bg, defFg, defBg; 166 CloseHandler[] closeHandlers; 167 } 168 169 170 shared static this() 171 { 172 loadDefaultColors(ConsoleOutputStream.stdout); 173 SetConsoleCtrlHandler(cast(PHANDLER_ROUTINE)&defaultCloseHandler, true); 174 } 175 176 private void loadDefaultColors(ConsoleOutputStream cos) 177 { 178 uint handle; 179 180 if(cos == ConsoleOutputStream.stdout) { 181 handle = STD_OUTPUT_HANDLE; 182 } else if(cos == ConsoleOutputStream.stderr) { 183 handle = STD_ERROR_HANDLE; 184 } else { 185 assert(0, "Invalid console output stream specified"); 186 } 187 188 189 hOutput = GetStdHandle(handle); 190 hInput = GetStdHandle(STD_INPUT_HANDLE); 191 192 // Get current colors 193 GetConsoleScreenBufferInfo( hOutput, &info ); 194 195 // Background are first 4 bits 196 defBg = cast(Color)((info.wAttributes & (BG_MASK)) >> 4); 197 198 // Rest are foreground 199 defFg = cast(Color) (info.wAttributes & (FG_MASK)); 200 201 fg = Color.initial; 202 bg = Color.initial; 203 } 204 205 private ushort buildColor(Color fg, Color bg) 206 { 207 if(fg == Color.initial) { 208 fg = defFg; 209 } 210 211 if(bg == Color.initial) { 212 bg = defBg; 213 } 214 215 return cast(ushort)(fg | bg << 4); 216 } 217 218 private void updateColor() 219 { 220 stdout.flush(); 221 SetConsoleTextAttribute(hOutput, buildColor(fg, bg)); 222 } 223 224 225 /** 226 * Current console font color 227 * 228 * Returns: 229 * Current foreground color set 230 */ 231 Color foreground() @property 232 { 233 return fg; 234 } 235 236 /** 237 * Current console background color 238 * 239 * Returns: 240 * Current background color set 241 */ 242 Color background() @property 243 { 244 return bg; 245 } 246 247 /** 248 * Sets console foreground color 249 * 250 * Flushes stdout. 251 * 252 * Params: 253 * color = Foreground color to set 254 */ 255 void foreground(Color color) @property 256 { 257 fg = color; 258 updateColor(); 259 } 260 261 262 /** 263 * Sets console background color 264 * 265 * Flushes stdout. 266 * 267 * Params: 268 * color = Background color to set 269 */ 270 void background(Color color) @property 271 { 272 bg = color; 273 updateColor(); 274 } 275 276 /** 277 * Sets new console output stream 278 * 279 * This function sets default colors 280 * that are used when function is called. 281 * 282 * Params: 283 * cos = New console output stream 284 */ 285 void outputStream(ConsoleOutputStream cos) @property 286 { 287 loadDefaultColors(cos); 288 } 289 290 /** 291 * Sets console font style 292 * 293 * Does nothing on windows. 294 * 295 * Params: 296 * fs = Font style to set 297 */ 298 void fontStyle(FontStyle fs) @property {} 299 300 /** 301 * Returns console font style 302 * 303 * Returns: 304 * Font style, always none on windows. 305 */ 306 FontStyle fontStyle() @property 307 { 308 return FontStyle.none; 309 } 310 311 312 /** 313 * Console size 314 * 315 * Returns: 316 * Tuple containing console rows and cols. 317 */ 318 ConsolePoint size() @property 319 { 320 GetConsoleScreenBufferInfo( hOutput, &info ); 321 322 int cols, rows; 323 324 cols = (info.srWindow.Right - info.srWindow.Left + 1); 325 rows = (info.srWindow.Bottom - info.srWindow.Top + 1); 326 327 return ConsolePoint(cols, rows); 328 } 329 330 /** 331 * Sets console position 332 * 333 * Params: 334 * x = X coordinate of cursor postion 335 * y = Y coordinate of cursor position 336 */ 337 void setCursorPos(int x, int y) 338 { 339 COORD coord = { 340 cast(short)min(width, max(0, x)), 341 cast(short)max(0, y) 342 }; 343 stdout.flush(); 344 SetConsoleCursorPosition(hOutput, coord); 345 } 346 347 /** 348 * Gets cursor position 349 * 350 * Returns: 351 * Cursor position 352 */ 353 ConsolePoint cursorPos() @property 354 { 355 GetConsoleScreenBufferInfo( hOutput, &info ); 356 return ConsolePoint( 357 info.dwCursorPosition.X, 358 min(info.dwCursorPosition.Y, height) // To keep same behaviour with posix 359 ); 360 } 361 362 363 364 /** 365 * Sets console title 366 * 367 * Params: 368 * title = Title to set 369 */ 370 void title(string title) @property 371 { 372 SetConsoleTitleA(toStringz(title)); 373 } 374 375 376 /** 377 * Adds handler for console close event. 378 * 379 * Params: 380 * closeHandler = New close handler 381 */ 382 void addCloseHandler(CloseHandler closeHandler) 383 { 384 closeHandlers ~= closeHandler; 385 } 386 387 /** 388 * Moves cursor by specified offset 389 * 390 * Params: 391 * x = X offset 392 * y = Y offset 393 */ 394 private void moveCursor(int x, int y) 395 { 396 stdout.flush(); 397 auto pos = cursorPos(); 398 setCursorPos(max(pos.x + x, 0), max(0, pos.y + y)); 399 } 400 401 /** 402 * Moves cursor up by n rows 403 * 404 * Params: 405 * n = Number of rows to move 406 */ 407 void moveCursorUp(int n = 1) 408 { 409 moveCursor(0, -n); 410 } 411 412 /** 413 * Moves cursor down by n rows 414 * 415 * Params: 416 * n = Number of rows to move 417 */ 418 void moveCursorDown(int n = 1) 419 { 420 moveCursor(0, n); 421 } 422 423 /** 424 * Moves cursor left by n columns 425 * 426 * Params: 427 * n = Number of columns to move 428 */ 429 void moveCursorLeft(int n = 1) 430 { 431 moveCursor(-n, 0); 432 } 433 434 /** 435 * Moves cursor right by n columns 436 * 437 * Params: 438 * n = Number of columns to move 439 */ 440 void moveCursorRight(int n = 1) 441 { 442 moveCursor(n, 0); 443 } 444 445 /** 446 * Gets console mode 447 * 448 * Returns: 449 * Current console mode 450 */ 451 ConsoleInputMode mode() @property 452 { 453 ConsoleInputMode cim; 454 DWORD m; 455 GetConsoleMode(hInput, &m); 456 457 cim.echo = !!(m & ENABLE_ECHO_INPUT); 458 cim.line = !!(m & ENABLE_LINE_INPUT); 459 460 return cim; 461 } 462 463 /** 464 * Sets console mode 465 * 466 * Params: 467 * New console mode 468 */ 469 void mode(ConsoleInputMode cim) @property 470 { 471 DWORD m; 472 473 (cim.echo) ? (m |= ENABLE_ECHO_INPUT) : (m &= ~ENABLE_ECHO_INPUT); 474 (cim.line) ? (m |= ENABLE_LINE_INPUT) : (m &= ~ENABLE_LINE_INPUT); 475 476 SetConsoleMode(hInput, m); 477 } 478 479 /** 480 * Reads character without line buffering 481 * 482 * Params: 483 * echo = Print typed characters 484 */ 485 int getch(bool echo = false) 486 { 487 INPUT_RECORD ir; 488 DWORD count; 489 auto m = mode; 490 491 mode = ConsoleInputMode.None; 492 493 do { 494 ReadConsoleInputA(hInput, &ir, 1, &count); 495 } while((ir.EventType != KEY_EVENT || !ir.KeyEvent.bKeyDown) && kbhit()); 496 // the extra kbhit is to filter out events AFTER the keydown 497 // to ensure next time we call this, we're back on a fresh keydown 498 // event. Without that, the key up event will trigger kbhit, then 499 // you call getch(), and it blocks because it read keyup then looped 500 // and is waiting for another keydown. 501 502 mode = m; 503 504 return ir.KeyEvent.wVirtualKeyCode; 505 } 506 507 /** 508 * Checks if any key is pressed. 509 * 510 * Shift, Ctrl and Alt keys are not detected. 511 * 512 * Returns: 513 * True if any key is pressed, false otherwise. 514 */ 515 bool kbhit() 516 { 517 return WaitForSingleObject(hInput, 0) == WAIT_OBJECT_0; 518 } 519 520 /** 521 * Sets cursor visibility 522 * 523 * Params: 524 * visible = Cursor visibility 525 */ 526 void cursorVisible(bool visible) @property 527 { 528 CONSOLE_CURSOR_INFO cci; 529 GetConsoleCursorInfo(hOutput, &cci); 530 cci.bVisible = visible; 531 SetConsoleCursorInfo(hOutput, &cci); 532 } 533 534 private CloseEvent idToCloseEvent(ulong i) 535 { 536 CloseEvent ce; 537 538 switch(i) 539 { 540 case 0: 541 ce.type = CloseType.Interrupt; 542 break; 543 544 case 1: 545 ce.type = CloseType.Stop; 546 break; 547 548 default: 549 ce.type = CloseType.Other; 550 } 551 552 ce.isBlockable = (ce.type != CloseType.Other); 553 554 return ce; 555 } 556 557 private bool defaultCloseHandler(ulong reason) 558 { 559 foreach(closeHandler; closeHandlers) 560 { 561 closeHandler(idToCloseEvent(reason)); 562 } 563 564 return true; 565 } 566 } 567 //////////////////////////////////////////////////////////////////////// 568 else version(Posix) 569 { 570 static import arsd.terminal; 571 import std.stdio, 572 std.conv, 573 std..string, 574 core.sys.posix.unistd, 575 core.sys.posix.sys.ioctl, 576 core.sys.posix.termios, 577 core.sys.posix.fcntl, 578 core.sys.posix.sys.time; 579 enum SIGINT = 2; 580 enum SIGTSTP = 20; 581 enum SIGQUIT = 3; 582 extern(C) void signal(int, void function(int) @system); 583 584 enum 585 { 586 UNDERLINE_ENABLE = 4, 587 UNDERLINE_DISABLE = 24, 588 589 STRIKE_ENABLE = 9, 590 STRIKE_DISABLE = 29, 591 592 BOLD_ENABLE = 1, 593 BOLD_DISABLE = 21 594 } 595 596 /// 597 enum Color : ushort 598 { 599 black = 30, /// The black color. 600 red = 31, /// The red color. 601 green = 32, /// The green color. 602 yellow = 33, /// The yellow color. 603 blue = 34, /// The blue color. 604 magenta = 35, /// The magenta color. (dark pink like) 605 cyan = 36, /// The cyan color. (blue-green) 606 lightGray = 37, /// The light gray color. (silver) 607 608 gray = 94, /// The gray color. 609 lightRed = 95, /// The light red color. 610 lightGreen = 96, /// The light green color. 611 lightYellow = 97, /// The light yellow color. 612 lightBlue = 98, /// The light red color. 613 lightMagenta = 99, /// The light magenta color. (pink) 614 lightCyan = 100, /// The light cyan color.(light blue-green) 615 white = 101, /// The white color. 616 617 bright = 64, /// Bright flag. Use with dark colors to make them light equivalents. 618 initial = 256 /// Default color 619 } 620 621 622 private __gshared 623 { 624 Color fg = Color.initial; 625 Color bg = Color.initial; 626 File stream; 627 int stdinFd; 628 FontStyle currentFontStyle; 629 630 CloseHandler[] closeHandlers; 631 SpecialKey[string] specialKeys; 632 } 633 634 shared static this() 635 { 636 stream = stdout; 637 signal(SIGINT, &defaultCloseHandler); 638 signal(SIGTSTP, &defaultCloseHandler); 639 signal(SIGQUIT, &defaultCloseHandler); 640 stdinFd = fileno(stdin.getFP); 641 642 specialKeys = [ 643 "[A" : SpecialKey.up, 644 "[B" : SpecialKey.down, 645 "[C" : SpecialKey.right, 646 "[D" : SpecialKey.left, 647 648 "OH" : SpecialKey.home, 649 "[5~": SpecialKey.pageUp, 650 "[6~": SpecialKey.pageDown, 651 "OF" : SpecialKey.end, 652 "[3~": SpecialKey.delete_, 653 "[2~": SpecialKey.insert, 654 655 "\033":SpecialKey.escape 656 ]; 657 } 658 659 660 private bool isRedirected() 661 { 662 return isatty( fileno(stream.getFP) ) != 1; 663 } 664 665 private void printAnsi() 666 { 667 stream.writef("\033[%d;%d;%d;%d;%d;%dm", 668 fg & Color.bright ? 1 : 0, 669 fg & ~Color.bright, 670 (bg & ~Color.bright) + 10, // Background colors are normal + 10 671 672 currentFontStyle & FontStyle.underline ? UNDERLINE_ENABLE : UNDERLINE_DISABLE, 673 currentFontStyle & FontStyle.strikethrough ? STRIKE_ENABLE : STRIKE_DISABLE, 674 currentFontStyle & FontStyle.bold ? BOLD_ENABLE : BOLD_DISABLE 675 ); 676 } 677 678 /** 679 * Sets console foreground color 680 * 681 * Params: 682 * color = Foreground color to set 683 */ 684 void foreground(Color color) @property 685 { 686 if(isRedirected()) { 687 return; 688 } 689 690 fg = color; 691 printAnsi(); 692 } 693 694 /** 695 * Sets console background color 696 * 697 * Params: 698 * color = Background color to set 699 */ 700 void background(Color color) @property 701 { 702 if(isRedirected()) { 703 return; 704 } 705 706 bg = color; 707 printAnsi(); 708 } 709 710 /** 711 * Current console background color 712 * 713 * Returns: 714 * Current foreground color set 715 */ 716 Color foreground() @property 717 { 718 return fg; 719 } 720 721 /** 722 * Current console font color 723 * 724 * Returns: 725 * Current background color set 726 */ 727 Color background() @property 728 { 729 return bg; 730 } 731 732 /** 733 * Sets new console output stream 734 * 735 * Params: 736 * cos = New console output stream 737 */ 738 void outputStream(ConsoleOutputStream cos) @property 739 { 740 if(cos == ConsoleOutputStream.stdout) { 741 stream = stdout; 742 } else if(cos == ConsoleOutputStream.stderr) { 743 stream = stderr; 744 } else { 745 assert(0, "Invalid consone output stream specified"); 746 } 747 } 748 749 750 /** 751 * Sets console font style 752 * 753 * Params: 754 * fs = Font style to set 755 */ 756 void fontStyle(FontStyle fs) @property 757 { 758 currentFontStyle = fs; 759 printAnsi(); 760 } 761 762 /** 763 * Console size 764 * 765 * Returns: 766 * Tuple containing console rows and cols. 767 */ 768 ConsolePoint size() @property 769 { 770 winsize w; 771 terminal.ioctl(STDOUT_FILENO, terminal.TIOCGWINSZ, &w); 772 773 return ConsolePoint(cast(int)w.ws_col, cast(int)w.ws_row); 774 } 775 776 /** 777 * Sets console position 778 * 779 * Params: 780 * x = X coordinate of cursor postion 781 * y = Y coordinate of cursor position 782 */ 783 void setCursorPos(int x, int y) 784 { 785 stdout.flush(); 786 writef("\033[%d;%df", y + 1, x + 1); 787 } 788 789 /** 790 * Gets cursor position 791 * 792 * Returns: 793 * Cursor position 794 */ 795 ConsolePoint cursorPos() @property 796 { 797 termios told, tnew; 798 char[] buf; 799 800 tcgetattr(0, &told); 801 tnew = told; 802 tnew.c_lflag &= ~ECHO & ~ICANON; 803 tcsetattr(0, TCSANOW, &tnew); 804 805 write("\033[6n"); 806 stdout.flush(); 807 foreach(i; 0..8) 808 { 809 char c; 810 c = cast(char)getch(); 811 buf ~= c; 812 if(c == 'R') 813 break; 814 } 815 tcsetattr(0, TCSANOW, &told); 816 817 buf = buf[2..$-1]; 818 auto tmp = buf.split(";"); 819 820 return ConsolePoint(to!int(tmp[1]) - 1, to!int(tmp[0]) - 1); 821 } 822 823 /** 824 * Sets console title 825 * 826 * Params: 827 * title = Title to set 828 */ 829 void title(string title) @property 830 { 831 stdout.flush(); 832 writef("\033]0;%s\007", title); // TODO: Check if supported 833 } 834 835 /** 836 * Adds handler for console close event. 837 * 838 * Params: 839 * closeHandler = New close handler 840 */ 841 void addCloseHandler(CloseHandler closeHandler) 842 { 843 closeHandlers ~= closeHandler; 844 } 845 846 /** 847 * Moves cursor up by n rows 848 * 849 * Params: 850 * n = Number of rows to move 851 */ 852 void moveCursorUp(int n = 1) 853 { 854 writef("\033[%dA", n); 855 } 856 857 /** 858 * Moves cursor down by n rows 859 * 860 * Params: 861 * n = Number of rows to move 862 */ 863 void moveCursorDown(int n = 1) 864 { 865 writef("\033[%dB", n); 866 } 867 868 /** 869 * Moves cursor left by n columns 870 * 871 * Params: 872 * n = Number of columns to move 873 */ 874 void moveCursorLeft(int n = 1) 875 { 876 writef("\033[%dD", n); 877 } 878 879 /** 880 * Moves cursor right by n columns 881 * 882 * Params: 883 * n = Number of columns to move 884 */ 885 void moveCursorRight(int n = 1) 886 { 887 writef("\033[%dC", n); 888 } 889 890 /** 891 * Gets console mode 892 * 893 * Returns: 894 * Current console mode 895 */ 896 ConsoleInputMode mode() @property 897 { 898 ConsoleInputMode cim; 899 termios tio; 900 ubyte[100] hack; 901 902 tcgetattr(stdinFd, &tio); 903 cim.echo = !!(tio.c_lflag & ECHO); 904 cim.line = !!(tio.c_lflag & ICANON); 905 906 return cim; 907 } 908 909 /** 910 * Sets console mode 911 * 912 * Params: 913 * New console mode 914 */ 915 void mode(ConsoleInputMode cim) @property 916 { 917 termios tio; 918 ubyte[100] hack; 919 920 tcgetattr(stdinFd, &tio); 921 922 (cim.echo) ? (tio.c_lflag |= ECHO) : (tio.c_lflag &= ~ECHO); 923 (cim.line) ? (tio.c_lflag |= ICANON) : (tio.c_lflag &= ~ICANON); 924 tcsetattr(stdinFd, TCSANOW, &tio); 925 } 926 927 /** 928 * Reads character without line buffering 929 * 930 * Params: 931 * echo = Print typed characters 932 */ 933 int getch(bool echo = false) 934 { 935 import std.ascii : toUpper; 936 937 int c; 938 string buf; 939 ConsoleInputMode m; 940 941 m = mode; 942 mode = ConsoleInputMode(echo, false); 943 c = getchar(); 944 945 if(c == SpecialKey.escape) 946 { 947 while(kbhit()) 948 { 949 buf ~= getchar(); 950 } 951 writeln(buf); 952 if(buf in specialKeys) { 953 c = specialKeys[buf]; 954 } else { 955 c = -1; 956 } 957 } 958 959 mode = m; 960 961 return c.toUpper(); 962 } 963 964 /** 965 * Checks if anykey is pressed. 966 * 967 * Shift, Ctrl and Alt keys are not detected. 968 * 969 * Returns: 970 * True if anykey is pressed, false otherwise. 971 */ 972 bool kbhit() 973 { 974 ConsoleInputMode m; 975 int c; 976 int old; 977 978 m = mode; 979 mode = ConsoleInputMode.None; 980 981 old = fcntl(STDIN_FILENO, F_GETFL, 0); 982 fcntl(STDIN_FILENO, F_SETFL, old | O_NONBLOCK); 983 984 c = getchar(); 985 986 fcntl(STDIN_FILENO, F_SETFL, old); 987 mode = m; 988 989 if(c != EOF) 990 { 991 ungetc(c, stdin.getFP); 992 return true; 993 } 994 995 return false; 996 } 997 998 /** 999 * Sets cursor visibility 1000 * 1001 * Params: 1002 * visible = Cursor visibility 1003 */ 1004 void cursorVisible(bool visible) @property 1005 { 1006 char c; 1007 if(visible) 1008 c = 'h'; 1009 else 1010 c = 'l'; 1011 1012 writef("\033[?25%c", c); 1013 } 1014 1015 private CloseEvent idToCloseEvent(ulong i) 1016 { 1017 CloseEvent ce; 1018 1019 switch(i) 1020 { 1021 case SIGINT: 1022 ce.type = CloseType.Interrupt; 1023 break; 1024 1025 case SIGQUIT: 1026 ce.type = CloseType.Quit; 1027 break; 1028 1029 case SIGTSTP: 1030 ce.type = CloseType.Stop; 1031 break; 1032 1033 default: 1034 ce.type = CloseType.Other; 1035 } 1036 1037 ce.isBlockable = (ce.type != CloseType.Other); 1038 1039 return ce; 1040 } 1041 1042 private extern(C) void defaultCloseHandler(int reason) @system 1043 { 1044 foreach(closeHandler; closeHandlers) 1045 { 1046 closeHandler(idToCloseEvent(reason)); 1047 } 1048 } 1049 } 1050 1051 /** 1052 * Console width 1053 * 1054 * Returns: 1055 * Console width as number of columns 1056 */ 1057 @property int width() 1058 { 1059 return size.x; 1060 } 1061 1062 /** 1063 * Console height 1064 * 1065 * Returns: 1066 * Console height as number of rows 1067 */ 1068 @property int height() 1069 { 1070 return size.y; 1071 } 1072 1073 1074 /** 1075 * Reads password from user 1076 * 1077 * Params: 1078 * mask = Typed character mask 1079 * 1080 * Returns: 1081 * Password 1082 */ 1083 string readPassword(char mask = '*') 1084 { 1085 // import terminal; 1086 static import arsd.terminal; 1087 auto term = Terminal(ConsoleOutputType.linear); 1088 auto input = RealTimeConsoleInput(&term, ConsoleInputFlags.raw); 1089 string pass; 1090 dchar ch; 1091 ch = input.getch(); 1092 while(ch != '\r' && ch != '\n') 1093 { 1094 import std.range; 1095 if(ch == '\b' && !pass.empty) 1096 { 1097 pass = pass[0..$-1]; 1098 write('\b'); 1099 stdout.flush; 1100 } 1101 else 1102 { 1103 pass ~= ch; 1104 write(mask); 1105 stdout.flush(); 1106 } 1107 ch = input.getch(); 1108 } 1109 return pass; 1110 } 1111 1112 1113 /** 1114 * Fills area with specified character 1115 * 1116 * Params: 1117 * p1 = Top-Left corner coordinates of area 1118 * p2 = Bottom-Right corner coordinates of area 1119 * fill = Character to fill area 1120 */ 1121 void fillArea(ConsolePoint p1, ConsolePoint p2, char fill) 1122 { 1123 foreach(i; p1.y .. p2.y + 1) 1124 { 1125 setCursorPos(p1.x, i); 1126 write( replicate((&fill)[0..1], p2.x - p1.x)); 1127 // ^ Converting char to char[] 1128 stdout.flush(); 1129 } 1130 } 1131 1132 /** 1133 * Draws box with specified border character 1134 * 1135 * Params: 1136 * p1 = Top-Left corner coordinates of box 1137 * p2 = Bottom-Right corner coordinates of box 1138 * fill = Border character 1139 */ 1140 void drawBox(ConsolePoint p1, ConsolePoint p2, char border) 1141 { 1142 drawHorizontalLine(p1, p2.x - p1.x, border); 1143 foreach(i; p1.y + 1 .. p2.y) 1144 { 1145 setCursorPos(p1.x, i); 1146 write(border); 1147 setCursorPos(p2.x - 1, i); 1148 write(border); 1149 } 1150 drawHorizontalLine(ConsolePoint(p1.x, p2.y), p2.x - p1.x, border); 1151 } 1152 1153 /** 1154 * Draws horizontal line with specified fill character 1155 * 1156 * Params: 1157 * pos = Start coordinates 1158 * length = Line width 1159 * border = Border character 1160 */ 1161 void drawHorizontalLine(ConsolePoint pos, int length, char border) 1162 { 1163 setCursorPos(pos.x, pos.y); 1164 write(replicate((&border)[0..1], length)); 1165 } 1166 1167 /** 1168 * Draws horizontal line with specified fill character 1169 * 1170 * Params: 1171 * pos = Start coordinates 1172 * length = Line height 1173 * border = Border character 1174 */ 1175 void drawVerticalLine(ConsolePoint pos, int length, char border) 1176 { 1177 foreach(i; pos.y .. length) 1178 { 1179 setCursorPos(pos.x, i); 1180 write(border); 1181 } 1182 } 1183 1184 /** 1185 * Writes at specified position 1186 * 1187 * Params: 1188 * point = Where to write 1189 * data = Data to write 1190 */ 1191 void writeAt(T)(ConsolePoint point, T data) 1192 { 1193 setCursorPos(point.x, point.y); 1194 write(data); 1195 stdout.flush(); 1196 } 1197 1198 /** 1199 * Clears console screen 1200 */ 1201 void clearScreen() 1202 { 1203 auto size = size; 1204 short length = cast(short)(size.x * size.y); // Number of all characters to write 1205 setCursorPos(0, 0); 1206 import std.array : replicate; 1207 write( replicate(" ", length)); 1208 stdout.flush(); 1209 } 1210 1211 /** 1212 * Brings default colors back 1213 */ 1214 void resetColors() 1215 { 1216 foreground = Color.initial; 1217 background = Color.initial; 1218 } 1219 1220 1221 /** 1222 * Brings font formatting to default 1223 */ 1224 void resetFontStyle() 1225 { 1226 fontStyle = FontStyle.none; 1227 } 1228 1229 1230 struct EnumTypedef(T, string _name) if(is(T == enum)) 1231 { 1232 public T val = T.init; 1233 1234 this(T v) { val = v; } 1235 1236 static EnumTypedef!(T, _name) opDispatch(string n)() 1237 { 1238 return EnumTypedef!(T, _name)(__traits(getMember, val, n)); 1239 } 1240 } 1241 1242 /// Alias for color enum 1243 alias EnumTypedef!(Color, "fg") Fg; 1244 1245 /// ditto 1246 alias EnumTypedef!(Color, "bg") Bg; 1247 1248 1249 /** 1250 * Represents color theme. 1251 * 1252 * Examples: 1253 * ---- 1254 * alias ThError = ColorTheme(Color.red, Color.black); 1255 * writeln(ThError("string to write using Error theme(red foreground on black background)")); 1256 * ---- 1257 */ 1258 struct ColorTheme(Color fg, Color bg) 1259 { 1260 string s; 1261 this(string s) 1262 { 1263 this.s = s; 1264 } 1265 1266 void toString(scope void delegate(const(char)[]) sink) const 1267 { 1268 auto _fg = foreground; 1269 auto _bg = background; 1270 foreground = fg; 1271 background = bg; 1272 sink(s.dup); 1273 foreground = _fg; 1274 background = _bg; 1275 } 1276 } 1277 1278 1279 /** 1280 * Writes text to console and colorizes text 1281 * 1282 * Params: 1283 * params = Text to write 1284 */ 1285 void writec(T...)(T params) 1286 { 1287 foreach(param; params) 1288 { 1289 static if(is(typeof(param) == Fg)) { 1290 foreground = param.val; 1291 } else static if(is(typeof(param) == Bg)) { 1292 background = param.val; 1293 } else static if(is(typeof(param) == FontStyle)) { 1294 fontStyle = param; 1295 } else { 1296 write(param); 1297 } 1298 } 1299 } 1300 1301 /** 1302 * Writes line to console and goes to newline 1303 * 1304 * Params: 1305 * params = Text to write 1306 */ 1307 void writecln(T...)(T params) 1308 { 1309 writec(params); 1310 writeln(); 1311 }