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