daily pastebin goal
69%
SHARE
TWEET

platform_conio.h

mvaganov Jan 1st, 2015 (edited) 351 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 rows/columns variable to the size (in rows and columns) of the console */
  25. void platform_consoleSize(long & out_rows, long & out_columns);
  26. /** wait for any key to be pressed (thread blocking call) */
  27. inline void platform_waitForAnyKey() { while (!platform_kbhit()) { platform_sleep(1); } }
  28. /** a blocking version of platform_getchar(), usable as replacement for _getch() in conio.h */
  29. inline long platform_getch() { platform_waitForAnyKey(); return platform_getchar(); }
  30.  
  31. /*
  32. // example console application using this library:
  33. #include "platform_conio.h"
  34. #include <string.h>         // for strlen
  35.  
  36. /// fills a rectangle in the console with the given character
  37. void fillRect(int row, int col, int width, int height, char character)
  38. {
  39.   for(int y = row; y < row+height; y++) {
  40.     platform_move(y, col);
  41.     for(int x = col; x < col+width; x++) {
  42.       putchar(character);
  43.     }
  44.   }
  45. }
  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.   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. #define NOMINMAX // keeps Windows from defining "min" and "max"
  107. #include <windows.h>    // move cursor, change color, sleep
  108. #include <conio.h>      // non-blocking input
  109. #include <time.h>       // clock()
  110.  
  111. inline HANDLE * __stdOutputHandle() {
  112.     static HANDLE g_h = 0;
  113.     return &g_h;
  114. }
  115.  
  116. /** keep track of the old terminal settings */
  117. inline WORD * __oldAttributes() {
  118.     static WORD oldAttributes;
  119.     return &oldAttributes;
  120. }
  121.  
  122. inline void __platform_release() {
  123.     if (*__stdOutputHandle() != 0) {
  124.         platform_setColor(*__oldAttributes() & 0xf, *__oldAttributes() & 0xf0);
  125.         *__stdOutputHandle() = 0;
  126.     }
  127. }
  128.  
  129. inline void __platform_init() {
  130.     if (*__stdOutputHandle() == 0) {
  131.         *__stdOutputHandle() = GetStdHandle(STD_OUTPUT_HANDLE);
  132.         CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo;
  133.         GetConsoleScreenBufferInfo(*__stdOutputHandle(), &lpConsoleScreenBufferInfo);
  134.         *__oldAttributes() = lpConsoleScreenBufferInfo.wAttributes;
  135.         atexit(__platform_release);
  136.     }
  137. }
  138.  
  139. inline void platform_move(long row, long col) {
  140.     if (col < 0) col = 0;
  141.     if (row < 0) row = 0;
  142.     COORD p = { (short)col, (short)row };
  143.     __platform_init();
  144.     SetConsoleCursorPosition(*__stdOutputHandle(), p);
  145. }
  146.  
  147. inline long platform_kbhit() {
  148.     __platform_init();
  149.     return _kbhit() != 0;
  150. }
  151.  
  152. inline long platform_getchar() {
  153.     long input;
  154.     if (!platform_kbhit()) return -1;
  155.     input = _getch();
  156.     switch ((char)input){
  157.     case '\0': case '\340':
  158.         if (_kbhit()) {
  159.             long nextByte = _getch();
  160.             input |= (nextByte & 0xff) << 8;
  161.         }
  162.     }
  163.     return input;
  164. }
  165.  
  166. inline void platform_setColor(long foreground, long background) {
  167.     __platform_init();
  168.     if (foreground < 0){ foreground = (*__oldAttributes()) & 0xf; }
  169.     if (background < 0){ background = (*__oldAttributes() & 0xf0) >> 4; }
  170.     SetConsoleTextAttribute(*__stdOutputHandle(), (foreground & 0xf) | ((background & 0xf) << 4));
  171. }
  172.  
  173. // TODO better algorithm needed here.
  174. inline int platform_getApproxColor(unsigned char r, unsigned char g, unsigned char b) {
  175.     int finalColor = 0;
  176.     for (int threshhold = 160; threshhold > 32; threshhold -= 32) {
  177.         if (finalColor == 0) {
  178.             if (r >= threshhold) finalColor |= FOREGROUND_RED;
  179.             if (g >= threshhold) finalColor |= FOREGROUND_GREEN;
  180.             if (b >= threshhold) finalColor |= FOREGROUND_BLUE;
  181.             if (finalColor != 0 && threshhold > 128) finalColor |= FOREGROUND_INTENSITY;
  182.         }
  183.         if (finalColor != 0) break;
  184.     }
  185.     return finalColor;
  186. }
  187.  
  188. inline void platform_sleep(long ms) {
  189.     Sleep(ms);
  190. }
  191.  
  192. inline long long platform_upTimeMS() {
  193.     return clock();
  194. }
  195.  
  196. inline void platform_consoleSize(long & out_rows, long & out_columns) {
  197.     CONSOLE_SCREEN_BUFFER_INFO csbi;
  198.     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
  199.     out_columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
  200.     out_rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
  201. }
  202.  
  203. #else // #ifdef _WIN32
  204. // how to do console utility stuff for *NIX
  205.  
  206. // escape sequence for arrow keys
  207. #define PLATFORM_KEY_UP 4283163 //'A[\033'
  208. #define PLATFORM_KEY_DOWN 4348699 //'B[\033'
  209. #define PLATFORM_KEY_RIGHT 4414235 //'C[\033'
  210. #define PLATFORM_KEY_LEFT 4479771 //'D[\033'
  211. // used for the first 16 colors of platform_setColor
  212. #define PLATFORM_COLOR_RED (1 << 0)
  213. #define PLATFORM_COLOR_GREEN (1 << 1)
  214. #define PLATFORM_COLOR_BLUE (1 << 2)
  215. #define PLATFORM_COLOR_INTENSITY (1 << 3)
  216.  
  217. #include <unistd.h>     // sleep
  218. #include <sys/select.h> // select, fd_set (for raw, low-level access to input)
  219. #include <sys/time.h>   // for wall-clock timer (as opposed to clock cycle timer)
  220. #include <sys/ioctl.h>  // for ioctl, which can get the console size
  221. #include <termios.h>    // standard terminal i/o stuff
  222. #include <stdlib.h>     // atexit (calls code to clean up when the program exits)
  223.  
  224. // using ANSI TTY console features to control color and cursor position by default.
  225. // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
  226.  
  227. /** keep track of the old terminal settings */
  228. inline termios * __oldTerminalIOSettings() {
  229.     static termios oldTerminalIOSettings;
  230.     return &oldTerminalIOSettings;
  231. }
  232.  
  233. inline long * __initialized() {
  234.     static long initted = 0;
  235.     return &initted;
  236. }
  237.  
  238. /** Linux keeps track of time this way. clock() returns CPU cycles, not time. */
  239. inline timeval * __g_startTime() {
  240.     static timeval g_startTime = { 0, 0 };
  241.     return &g_startTime;
  242. }
  243.  
  244. /** input check during kbhit */
  245. inline fd_set * __g_fds() {
  246.     static fd_set g_fds;
  247.     return &g_fds;
  248. }
  249.  
  250. inline void __platform_release() {
  251.     if (*__initialized() != 0) {
  252.         platform_setColor(-1, -1); // set to not-intense-white
  253.         *__initialized() = 0;
  254.     }
  255. }
  256.  
  257. inline void __platform__init() {
  258.     if (*__initialized() == 0) {
  259.         *__initialized() = 1;
  260.         FD_ZERO(__g_fds()); // initialize the struct that checks for input
  261.         gettimeofday(__g_startTime(), NULL);    // start the timer
  262.         atexit(__platform_release);
  263.     }
  264. }
  265.  
  266. inline void __platform_doConsoleInputMode() {
  267.     __platform__init();
  268.     // make getch read right at the key press, without echoing
  269.     tcgetattr(STDIN_FILENO, __oldTerminalIOSettings());
  270.     termios currentTerminalIOSettings = *__oldTerminalIOSettings();
  271.     currentTerminalIOSettings.c_lflag &= ~(ICANON | ECHO);  // don't wait for enter, don't print
  272.     tcsetattr(STDIN_FILENO, TCSANOW, &currentTerminalIOSettings);
  273. }
  274.  
  275. inline void __platform_undoConsoleInputMode() {
  276.     tcsetattr(STDIN_FILENO, TCSANOW, __oldTerminalIOSettings());
  277. }
  278.  
  279. /** checks if there is input in the keyboard buffer --requires __platform_doConsoleInputMode() */
  280. inline long __platform_kbhitCheck() {
  281.     static timeval g_tv_zero = { 0, 0 };
  282.     long result;
  283.     // check the hardware input stream if there is data waiting
  284.     FD_SET(STDIN_FILENO, __g_fds());
  285.     result = select(STDIN_FILENO + 1, __g_fds(), NULL, NULL, &g_tv_zero);
  286.     // specifically, check for data to be read
  287.     return result && (FD_ISSET(0, __g_fds()));
  288. }
  289.  
  290. inline long platform_kbhit() {
  291.     long result;
  292.     __platform_doConsoleInputMode();
  293.     result = __platform_kbhitCheck();
  294.     __platform_undoConsoleInputMode();
  295.     return result;
  296. }
  297.  
  298. inline long platform_getchar() {
  299.     long buffer = -1;
  300.     __platform_doConsoleInputMode(); // go into single-character-not-printed mode
  301.     if (__platform_kbhitCheck()) {
  302.         buffer = 0;
  303.         read(STDIN_FILENO, (char *)&buffer, 1); // read only one byte
  304.         switch (buffer) {
  305.         case '\033': // if it is an escape sequence, read some more...
  306.             read(STDIN_FILENO, ((char *)&buffer) + 1, 1);
  307.             switch (((char *)&buffer)[1]) {
  308.             case '[': // possibly arrow keys
  309.                 read(STDIN_FILENO, ((char *)&buffer) + 2, 1);
  310.                 break;
  311.             }
  312.             break;
  313.         }
  314.     }
  315.     __platform_undoConsoleInputMode(); // revert to regular input mode, so scanf/std::cin will work
  316.     return buffer;
  317. }
  318.  
  319. inline void platform_move(long row, long col) {
  320.     if (col < 0) col = 0;
  321.     if (row < 0) row = 0;
  322.     __platform__init();
  323.     fflush(stdout);
  324.     printf("\033[%d;%df", (int)row + 1, (int)col + 1);  // move cursor, using TTY codes (without ncurses)
  325.     fflush(stdout);
  326. }
  327.  
  328. inline void platform_setColor(long foreground, long background) {
  329.     static int cached_bg = -1, cached_fg = -1;
  330.     __platform__init();
  331.     // don't bother running the TTY commands if no actual change needs to happen.
  332.     if(foreground == cached_fg && background == cached_bg) { return; } cached_fg = foreground; cached_bg = background;
  333.     fflush(stdout);
  334.     // colorRGB and colorGRAY usable for TTY (unix/linux) expanded console color
  335.     if (foreground >= 0)
  336.         printf("\033[38;5;%dm", (int)foreground);
  337.     else
  338.         printf("\033[39m");// default foreground color
  339.     if (background >= 0)
  340.         printf("\033[48;5;%dm", (int)background);
  341.     else
  342.         printf("\033[49m");// default background color
  343.     fflush(stdout);
  344. }
  345.  
  346. inline void platform_sleep(long a_ms) {
  347.     //  long seconds = a_ms / 1000;
  348.     __platform__init();
  349.     //  a_ms -= seconds * 1000;
  350.     //  timespec time = { seconds, a_ms * 1000000 }; // 1 millisecond = 1,000,000 Nanoseconds
  351.     //  nanosleep(&time, NULL);
  352.     usleep((useconds_t)(a_ms * 1000));
  353. }
  354.  
  355. inline long long platform_upTimeMS() {
  356.     static timeval now;
  357.     static time_t seconds, useconds, ms;
  358.     __platform__init();
  359.     gettimeofday(&now, NULL);
  360.     seconds = now.tv_sec - __g_startTime()->tv_sec;
  361.     useconds = now.tv_usec - __g_startTime()->tv_usec;
  362.     ms = seconds * 1000 + useconds / 1000;
  363.     return ms;
  364. }
  365.  
  366. inline void platform_consoleSize(long & out_rows, long & out_columns) {
  367.     struct winsize w;
  368.     ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
  369.     out_columns = w.ws_col;
  370.     out_rows = w.ws_row;
  371. }
  372.  
  373. /**
  374. * pass to setColor function. Only works in linux/unix TTY
  375. * @param R, G, B values must be between 0 and 5 (inclusive)
  376. */
  377. inline int PLATFORM_COLOR_RGB8bit(int R, int G, int B){ return (16 + (B + (G * 6) + (R * 36))); }
  378.  
  379. inline int platform_getApproxColor(unsigned char r, unsigned char g, unsigned char b) {
  380.     int finalColor = 0;
  381.     if(r == g && g == b) {
  382.         int gray = (r/255.0)*23;
  383.         finalColor = (232+gray);
  384.     } else {
  385.         int R = (int)((r/255.0)*5);
  386.         int G = (int)((g/255.0)*5);
  387.         int B = (int)((b/255.0)*5);
  388.         finalColor = (16+(B+(G*6)+(R*36)));
  389.     }
  390.     return finalColor;
  391. }
  392.  
  393. /**
  394. * pass to setColor function. Only works in linux/unix
  395. * @param gray must be between 0 and 23 (inclusive)
  396. */
  397. inline int PLATFORM_COLOR_GRAYSCALE24(int gray){ return (232 + gray); }
  398. #endif // #ifdef _WIN32 #else
  399.  
  400. #endif // __PLATFORM_CONIO_H
RAW Paste Data
Top