mvaganov

platform_conio.h

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