mvaganov

platform_conio.h

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