Advertisement
mvaganov

platform_conio.h

Jan 1st, 2015 (edited)
710
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.87 KB | None | 0 0
  1. #pragma once
  2. #ifndef __PLATFORM_CONIO_H
  3. #define __PLATFORM_CONIO_H
  4. /*
  5. utility functions for the command-line console, usable in both Windows and Linux/Unix v20150415 http://pastebin.com/c0DQsNLQ
  6. license: MIT (http://opensource.org/licenses/MIT). TL;DR - this is free software, I won't fix it for you!
  7. */
  8.  
  9. /** move the cursor to the given location in the console */
  10. void platform_move(long row, long col);
  11. /** true (non-zero) if a key is pressed */
  12. long platform_kbhit();
  13. /** return what key is currently pressed as a character, or -1 if not pressed. PLATFORM_KEY_XXX special keys are different on windows/linux */
  14. long platform_getchar();
  15. /** set the color of the command line cursor. linux gets more colors than windows. ignore negative values (use current color) */
  16. void platform_setColor(long foreground, long background);
  17. /** converts the given 0-255 RGB value to an approximate in the command-line console */
  18. int platform_getApproxColor(unsigned char r, unsigned char g, unsigned char b);
  19. /** pause the thread for the given number of milliseconds */
  20. void platform_sleep(long ms);
  21. /** how many milliseconds since first use of this API */
  22. long long platform_upTimeMS();
  23. /** set the memory at the given variables to the size (in rows and columns) of the console */
  24. void platform_consoleSize(long * out_rows, long * out_columns);
  25. /** set the memory at the given variables to the positon (in rows and columns) of the console cursor */
  26. void platform_getCursorPosition(short * out_row, short * out_col);
  27. /** wait for any key to be pressed (thread blocking call) */
  28. static void platform_waitForAnyKey() { while (!platform_kbhit()) { platform_sleep(1); } }
  29. /** a blocking version of platform_getchar(), usable as replacement for _getch() in conio.h */
  30. static long platform_getch() { platform_waitForAnyKey(); return platform_getchar(); }
  31.  
  32. /// fills a rectangle in the console with the given character
  33. void platform_fillRect(int row, int col, int width, int height, char character) {
  34.   for(int y = row; y < row+height; y++) {
  35.     platform_move(y, col);
  36.     for(int x = col; x < col+width; x++) {
  37.       putchar(character);
  38.     }
  39.   }
  40. }
  41.  
  42. /*
  43. // example console application using this library:
  44. #include "platform_conio.h"
  45. #include <string.h>         // for strlen
  46.  
  47. int main(int argc, char * argv[])
  48. {
  49.   int userInput;
  50.   const int frameDelay = 100; // how many milliseconds for each frame of animation
  51.   long long now, whenToStopWaiting; // timing variables
  52.  
  53.   // character animation \ - / | \ - / | \ - / | \ - / |
  54.   const char * animation = "\\-/|";
  55.   int numframes = strlen(animation);
  56.   int frameIndex = 0;
  57.  
  58.   // color shifting for blinking messate
  59.   int colors[] = { 8, 7, 15, 7 };
  60.   int numColors = sizeof(colors) / sizeof(colors[0]);
  61.   int colorIndex = 0;
  62.   do {
  63.     // animation
  64.     platform_setColor(7, 0);
  65.     platform_move(3, 10);
  66.     putchar(animation[frameIndex++]);
  67.     frameIndex %= numframes;
  68.  
  69.     // blinking message
  70.     platform_move(3, 12);
  71.     platform_setColor(colors[colorIndex++], 0);
  72.     printf("Press \'a\' to continue ");
  73.     colorIndex %= numColors;
  74.  
  75.     // wait the specified time, or until there is a keyboard hit (interruptable throttle code)
  76.     now = platform_upTimeMS();
  77.     whenToStopWaiting = now + frameDelay;
  78.     while (platform_upTimeMS() < whenToStopWaiting && !platform_kbhit()) { platform_sleep(1); }
  79.  
  80.     userInput = platform_getchar(); // platform_getchar() returns -1 if no key is pressed
  81.   } while (userInput != 'a');
  82.   // prep to make the console look more usable after the program is done
  83.   platform_setColor(-1, -1);    // reset colors to original
  84.   platform_fillRect(0, 0, 80, 10, ' ');  // clear the top 10 lines (80 columns per line)
  85.   platform_move(0,0);           // move the cursor back to back to the start
  86.   return 0;
  87. }
  88. */
  89.  
  90. #include <stdio.h>      // printf and putchar
  91.  
  92. #ifdef _WIN32
  93. // how to do console utility stuff for Windows
  94.  
  95. // escape sequence for arrow keys
  96. #define PLATFORM_KEY_UP 18656 //'H\340'
  97. #define PLATFORM_KEY_LEFT 19424 //'K\340'
  98. #define PLATFORM_KEY_RIGHT 19936 //'M\340'
  99. #define PLATFORM_KEY_DOWN 20704 //'P\340'
  100. // used for the first 16 colors of platform_setColor
  101. #define PLATFORM_COLOR_INTENSITY (1 << 3)
  102. #define PLATFORM_COLOR_RED (1 << 2)
  103. #define PLATFORM_COLOR_GREEN (1 << 1)
  104. #define PLATFORM_COLOR_BLUE (1 << 0)
  105.  
  106. #ifndef NOMINMAX
  107. #define NOMINMAX // keeps Windows from defining "min" and "max"
  108. #endif
  109. #include <windows.h>    // move cursor, change color, sleep
  110. #include <conio.h>      // non-blocking input
  111. #include <time.h>       // clock()
  112.  
  113. static HANDLE * __stdOutputHandle() {
  114.     static HANDLE g_h = 0;
  115.     return &g_h;
  116. }
  117.  
  118. /** keep track of the old terminal settings */
  119. static WORD * __oldAttributes() {
  120.     static WORD oldAttributes;
  121.     return &oldAttributes;
  122. }
  123.  
  124. static void __platform_release() {
  125.     if (*__stdOutputHandle() != 0) {
  126.         platform_setColor(*__oldAttributes() & 0xf, *__oldAttributes() & 0xf0);
  127.         *__stdOutputHandle() = 0;
  128.     }
  129. }
  130.  
  131. static void __platform_init() {
  132.     if (*__stdOutputHandle() == 0) {
  133.         *__stdOutputHandle() = GetStdHandle(STD_OUTPUT_HANDLE);
  134.         CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo;
  135.         GetConsoleScreenBufferInfo(*__stdOutputHandle(), &lpConsoleScreenBufferInfo);
  136.         *__oldAttributes() = lpConsoleScreenBufferInfo.wAttributes;
  137.         atexit(__platform_release);
  138.     }
  139. }
  140.  
  141. inline void platform_move(long row, long col) {
  142.     if (col < 0) col = 0;
  143.     if (row < 0) row = 0;
  144.     COORD p = { (short)col, (short)row };
  145.     __platform_init();
  146.     SetConsoleCursorPosition(*__stdOutputHandle(), p);
  147. }
  148.  
  149. inline long platform_kbhit() {
  150.     __platform_init();
  151.     return _kbhit() != 0;
  152. }
  153.  
  154. inline long platform_getchar() {
  155.     long input;
  156.     if (!platform_kbhit()) return -1;
  157.     input = _getch();
  158.     switch ((char)input){
  159.     case '\0': case '\340':
  160.         if (_kbhit()) {
  161.             long nextByte = _getch();
  162.             input |= (nextByte & 0xff) << 8;
  163.         }
  164.     }
  165.     return input;
  166. }
  167.  
  168. inline void platform_setColor(long foreground, long background) {
  169.     __platform_init();
  170.     if (foreground < 0){ foreground = (*__oldAttributes()) & 0xf; }
  171.     if (background < 0){ background = (*__oldAttributes() & 0xf0) >> 4; }
  172.     SetConsoleTextAttribute(*__stdOutputHandle(), (foreground & 0xf) | ((background & 0xf) << 4));
  173. }
  174.  
  175. // TODO better algorithm needed here.
  176. inline int platform_getApproxColor(unsigned char r, unsigned char g, unsigned char b) {
  177.     int finalColor = 0;
  178.     for (int threshhold = 160; threshhold > 32; threshhold -= 32) {
  179.         if (finalColor == 0) {
  180.             if (r >= threshhold) finalColor |= FOREGROUND_RED;
  181.             if (g >= threshhold) finalColor |= FOREGROUND_GREEN;
  182.             if (b >= threshhold) finalColor |= FOREGROUND_BLUE;
  183.             if (finalColor != 0 && threshhold > 128) finalColor |= FOREGROUND_INTENSITY;
  184.         }
  185.         if (finalColor != 0) break;
  186.     }
  187.     return finalColor;
  188. }
  189.  
  190. inline void platform_sleep(long ms) {
  191.     Sleep(ms);
  192. }
  193.  
  194. inline long long platform_upTimeMS() {
  195.     return clock();
  196. }
  197.  
  198. inline void platform_consoleSize(long * out_rows, long * out_columns) {
  199.     CONSOLE_SCREEN_BUFFER_INFO csbi;
  200.     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
  201.     *out_columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
  202.     *out_rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
  203. }
  204.  
  205. inline void platform_getCursorPosition(short * out_row, short * out_col) {
  206.     CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
  207.     HANDLE hStd = GetStdHandle(STD_OUTPUT_HANDLE);
  208.     GetConsoleScreenBufferInfo(hStd, &screenBufferInfo);
  209.     *out_col = screenBufferInfo.dwCursorPosition.X;
  210.     *out_row = screenBufferInfo.dwCursorPosition.Y;
  211. }
  212.  
  213. // end of #ifdef _WIN32
  214. #else
  215. // how to do console utility stuff for *NIX
  216.  
  217. // escape sequence for arrow keys
  218. #define PLATFORM_KEY_UP 4283163 //'A[\033'
  219. #define PLATFORM_KEY_DOWN 4348699 //'B[\033'
  220. #define PLATFORM_KEY_RIGHT 4414235 //'C[\033'
  221. #define PLATFORM_KEY_LEFT 4479771 //'D[\033'
  222. // used for the first 16 colors of platform_setColor
  223. #define PLATFORM_COLOR_RED (1 << 0)
  224. #define PLATFORM_COLOR_GREEN (1 << 1)
  225. #define PLATFORM_COLOR_BLUE (1 << 2)
  226. #define PLATFORM_COLOR_INTENSITY (1 << 3)
  227.  
  228. #include <unistd.h>     // sleep
  229. #include <sys/select.h> // select, fd_set (for raw, low-level access to input)
  230. #include <sys/time.h>   // for wall-clock timer (as opposed to clock cycle timer)
  231. #include <sys/ioctl.h>  // for ioctl, which can get the console size
  232. #include <termios.h>    // standard terminal i/o stuff
  233. #include <stdlib.h>     // atexit (calls code to clean up when the program exits)
  234.  
  235. // using ANSI TTY console features to control color and cursor position by default.
  236. // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
  237.  
  238. /** keep track of the old terminal settings */
  239. inline struct termios * __oldTerminalIOSettings() {
  240.     static struct termios oldTerminalIOSettings;
  241.     return &oldTerminalIOSettings;
  242. }
  243.  
  244. inline long * __initialized() {
  245.     static long initted = 0;
  246.     return &initted;
  247. }
  248.  
  249. /** Linux keeps track of time this way. clock() returns CPU cycles, not time. */
  250. inline struct timeval * __g_startTime() {
  251.     static struct timeval g_startTime = { 0, 0 };
  252.     return &g_startTime;
  253. }
  254.  
  255. /** input check during kbhit */
  256. inline fd_set * __g_fds() {
  257.     static fd_set g_fds;
  258.     return &g_fds;
  259. }
  260.  
  261. inline void __platform_release() {
  262.     if (*__initialized() != 0) {
  263.         platform_setColor(-1, -1); // set to not-intense-white
  264.         *__initialized() = 0;
  265.     }
  266. }
  267.  
  268. inline void __platform__init() {
  269.     if (*__initialized() == 0) {
  270.         *__initialized() = 1;
  271.         FD_ZERO(__g_fds()); // initialize the struct that checks for input
  272.         gettimeofday(__g_startTime(), NULL);    // start the timer
  273.         atexit(__platform_release);
  274.     }
  275. }
  276.  
  277. inline void __platform_doConsoleInputMode() {
  278.     __platform__init();
  279.     // make getch read right at the key press, without echoing
  280.     tcgetattr(STDIN_FILENO, __oldTerminalIOSettings());
  281.     struct termios currentTerminalIOSettings = *__oldTerminalIOSettings();
  282.     currentTerminalIOSettings.c_lflag &= ~(ICANON | ECHO);  // don't wait for enter, don't print
  283.     tcsetattr(STDIN_FILENO, TCSANOW, &currentTerminalIOSettings);
  284. }
  285.  
  286. inline void __platform_undoConsoleInputMode() {
  287.     tcsetattr(STDIN_FILENO, TCSANOW, __oldTerminalIOSettings());
  288. }
  289.  
  290. /** checks if there is input in the keyboard buffer --requires __platform_doConsoleInputMode() */
  291. inline long __platform_kbhitCheck() {
  292.     static struct timeval g_tv_zero = { 0, 0 };
  293.     long result;
  294.     // check the hardware input stream if there is data waiting
  295.     FD_SET(STDIN_FILENO, __g_fds());
  296.     result = select(STDIN_FILENO + 1, __g_fds(), NULL, NULL, &g_tv_zero);
  297.     // specifically, check for data to be read
  298.     return result && (FD_ISSET(0, __g_fds()));
  299. }
  300.  
  301. inline long platform_kbhit() {
  302.     long result;
  303.     __platform_doConsoleInputMode();
  304.     result = __platform_kbhitCheck();
  305.     __platform_undoConsoleInputMode();
  306.     return result;
  307. }
  308.  
  309. inline long platform_getchar() {
  310.     long buffer = -1;
  311.     __platform_doConsoleInputMode(); // go into single-character-not-printed mode
  312.     if (__platform_kbhitCheck()) {
  313.         buffer = 0;
  314.         read(STDIN_FILENO, (char *)&buffer, 1); // read only one byte
  315.         switch (buffer) {
  316.         case '\033': // if it is an escape sequence, read some more...
  317.             read(STDIN_FILENO, ((char *)&buffer) + 1, 1);
  318.             switch (((char *)&buffer)[1]) {
  319.             case '[': // possibly arrow keys
  320.                 read(STDIN_FILENO, ((char *)&buffer) + 2, 1);
  321.                 break;
  322.             }
  323.             break;
  324.         }
  325.     }
  326.     __platform_undoConsoleInputMode(); // revert to regular input mode, so scanf/std::cin will work
  327.     return buffer;
  328. }
  329.  
  330. inline void platform_move(long row, long col) {
  331.     if (col < 0) col = 0;
  332.     if (row < 0) row = 0;
  333.     __platform__init();
  334.     fflush(stdout);
  335.     printf("\033[%d;%df", (int)row + 1, (int)col + 1);  // move cursor, using TTY codes (without ncurses)
  336.     fflush(stdout);
  337. }
  338.  
  339. inline void platform_setColor(long foreground, long background) {
  340.     static int cached_bg = -1, cached_fg = -1;
  341.     __platform__init();
  342.     // don't bother running the TTY commands if no actual change needs to happen.
  343.     if(foreground == cached_fg && background == cached_bg) { return; } cached_fg = foreground; cached_bg = background;
  344.     fflush(stdout);
  345.     // colorRGB and colorGRAY usable for TTY (unix/linux) expanded console color
  346.     if (foreground >= 0)
  347.         printf("\033[38;5;%dm", (int)foreground);
  348.     else
  349.         printf("\033[39m");// default foreground color
  350.     if (background >= 0)
  351.         printf("\033[48;5;%dm", (int)background);
  352.     else
  353.         printf("\033[49m");// default background color
  354.     fflush(stdout);
  355. }
  356.  
  357. inline void platform_sleep(long a_ms) {
  358.     //  long seconds = a_ms / 1000;
  359.     __platform__init();
  360.     //  a_ms -= seconds * 1000;
  361.     //  timespec time = { seconds, a_ms * 1000000 }; // 1 millisecond = 1,000,000 Nanoseconds
  362.     //  nanosleep(&time, NULL);
  363.     usleep((useconds_t)(a_ms * 1000));
  364. }
  365.  
  366. inline long long platform_upTimeMS() {
  367.     static struct timeval now;
  368.     static time_t seconds, useconds, ms;
  369.     __platform__init();
  370.     gettimeofday(&now, NULL);
  371.     seconds = now.tv_sec - __g_startTime()->tv_sec;
  372.     useconds = now.tv_usec - __g_startTime()->tv_usec;
  373.     ms = seconds * 1000 + useconds / 1000;
  374.     return ms;
  375. }
  376.  
  377. inline void platform_consoleSize(long * out_rows, long * out_columns) {
  378.     struct winsize w;
  379.     ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
  380.     *out_columns = w.ws_col;
  381.     *out_rows = w.ws_row;
  382. }
  383.  
  384. inline void platform_getCursorPosition(short * y, short * x) {
  385.     char buf[30]={0};
  386.     int ret, i, pow;
  387.     char ch;
  388.     *y = 0; *x = 0;
  389.     struct termios term, restore;
  390.     tcgetattr(0, &term);
  391.     tcgetattr(0, &restore);
  392.     term.c_lflag &= ~(ICANON|ECHO);
  393.     tcsetattr(0, TCSANOW, &term);
  394.     write(1, "\033[6n", 4); // TTY command to get the row/col position, printing it to stream '1'
  395.     for( i = 0, ch = 0; ch != 'R'; i++ ) {
  396.         ret = read(0, &ch, 1); // read output from stream '1'
  397.         if ( !ret ) {
  398.             tcsetattr(0, TCSANOW, &restore);
  399.             fprintf(stderr, "getpos: error reading response!\n");
  400.             return;
  401.         }
  402.         buf[i] = ch; // put output from stream 1 into the input buffer
  403.         //printf("buf[%d]: \t%c \t%d\n", i, ch, ch);
  404.     }
  405.     if (i < 2) {
  406.         tcsetattr(0, TCSANOW, &restore);
  407.         printf("i < 2\n");
  408.         return;
  409.     }
  410.     // parse the numbers from the input buffer into the output variables
  411.     for( i -= 2, pow = 1; buf[i] != ';'; i--, pow *= 10)
  412.         *x = *x + ( buf[i] - '0' ) * pow;
  413.     for( i-- , pow = 1; buf[i] != '['; i--, pow *= 10)
  414.         *y = *y + ( buf[i] - '0' ) * pow;
  415.     tcsetattr(0, TCSANOW, &restore);
  416. }
  417.  
  418. /**
  419. * pass to setColor function. Only works in linux/unix TTY
  420. * @param R, G, B values must be between 0 and 5 (inclusive)
  421. */
  422. inline int PLATFORM_COLOR_RGB8bit(int R, int G, int B){ return (16 + (B + (G * 6) + (R * 36))); }
  423.  
  424. inline int platform_getApproxColor(unsigned char r, unsigned char g, unsigned char b) {
  425.     int finalColor = 0;
  426.     if(r == g && g == b) {
  427.         int gray = (r/255.0)*23;
  428.         finalColor = (232+gray);
  429.     } else {
  430.         int R = (int)((r/255.0)*5);
  431.         int G = (int)((g/255.0)*5);
  432.         int B = (int)((b/255.0)*5);
  433.         finalColor = (16+(B+(G*6)+(R*36)));
  434.     }
  435.     return finalColor;
  436. }
  437.  
  438. /**
  439. * pass to setColor function. Only works in linux/unix
  440. * @param gray must be between 0 and 23 (inclusive)
  441. */
  442. inline int PLATFORM_COLOR_GRAYSCALE24(int gray){ return (232 + gray); }
  443. #endif // #ifdef _WIN32 #else
  444.  
  445. #endif // __PLATFORM_CONIO_H
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement