1 module dstatus.terminal; 2 3 import std.conv : text, to; 4 5 short getTerminalWidth() { 6 version (Windows) { 7 import core.sys.windows.winbase : GetStdHandle, STD_OUTPUT_HANDLE; 8 import core.sys.windows.wincon : CONSOLE_SCREEN_BUFFER_INFO, GetConsoleScreenBufferInfo; 9 import core.sys.windows.windef; 10 11 CONSOLE_SCREEN_BUFFER_INFO info; 12 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info) == TRUE) { 13 return info.dwSize.X; 14 } 15 } 16 else version (Posix) { 17 version (FreeBSD) { 18 import core.sys.posix.sys.ioctl : ioctl, winsize; 19 enum TIOCGWINSZ = 0x40087468; 20 } 21 else { 22 import core.sys.posix.sys.ioctl : ioctl, TIOCGWINSZ, winsize; 23 } 24 25 import core.sys.posix.unistd : STDOUT_FILENO; 26 27 winsize size; 28 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0) { 29 return size.ws_col; 30 } 31 } 32 33 // Terminal width could not be gotten - return -1 34 return -1; 35 } 36 37 version (Posix) { 38 import core.sys.posix.termios : ECHO, ICANON, tcflush, tcgetattr, TCIFLUSH, tcsetattr, TCSANOW, termios; 39 import unistd = core.sys.posix.unistd; 40 import std.stdio : File; 41 42 class TerminalPosition { 43 private { 44 string setCursorPosition; 45 int _tty; 46 } 47 48 this(File output) { 49 _tty = output.fileno(); 50 51 if (!unistd.isatty(_tty)) { 52 return; 53 } 54 55 // Retrieve current terminal settings 56 termios oldtermios; 57 tcgetattr(_tty, &oldtermios); 58 59 // Disable echoing and waiting for ENTER 60 auto newtermios = oldtermios; 61 newtermios.c_lflag &= ~(ECHO | ICANON); 62 tcsetattr(_tty, TCSANOW, &newtermios); 63 64 /* Ensure that original terminal settings are restored upon leaving the scope, 65 lest we mess up the user's terminal. */ 66 scope(exit) tcsetattr(_tty, TCSANOW, &oldtermios); 67 68 // Flush input to prevent user interfering with cursor position acquisition 69 tcflush(_tty, TCIFLUSH); 70 71 // Send ANSI sequence for getting cursor position to STDOUT 72 auto getPosition = "\033[6n"; 73 unistd.write(_tty, cast(void*)getPosition, getPosition.length); 74 75 // Read the response from STDIN 76 char[16] response; 77 auto readlen = unistd.read(_tty, cast(void*)response, 16); 78 79 /* Store ANSI sequence for settings cursor position from response 80 by taking the response, chopping off the R and appending an H instead. */ 81 setCursorPosition = text(response[0..readlen-1], "H"); 82 } 83 84 void restore() { 85 // Send ANSI sequence for setting cursor position 86 unistd.write(_tty, cast(void*)setCursorPosition, setCursorPosition.length); 87 } 88 } 89 } 90 else version (Windows) { 91 import core.sys.windows.wincon : CONSOLE_CURSOR_INFO, CONSOLE_SCREEN_BUFFER_INFO, COORD, GetConsoleCursorInfo, GetConsoleScreenBufferInfo, SetConsoleCursorPosition; 92 import core.sys.windows.windef; 93 import std.stdio : File; 94 95 class TerminalPosition { 96 private { 97 COORD _cursorPosition; 98 File _output; 99 } 100 101 this(File output) { 102 _output = output; 103 104 CONSOLE_SCREEN_BUFFER_INFO info; 105 if (GetConsoleScreenBufferInfo(_output.windowsHandle, &info) != TRUE) { 106 throw new Exception("Error getting cursor position."); 107 } 108 109 _cursorPosition = info.dwCursorPosition; 110 } 111 112 void restore() { 113 SetConsoleCursorPosition(_output.windowsHandle, _cursorPosition); 114 } 115 } 116 }