1 #pragma once
2 /**
3 * File: rlutil.h
4 *
5 * About: Description
6 * This file provides some useful utilities for console mode
7 * roguelike game development with C and C++. It is aimed to
8 * be cross-platform (at least Windows and Linux).
9 *
10 * About: Copyright
11 * (C) 2010 Tapio Vierros
12 *
13 * About: Licensing
14 * See <License>
15 */
16
17
18 /// Define: RLUTIL_USE_ANSI
19 /// Define this to use ANSI escape sequences also on Windows
20 /// (defaults to using WinAPI instead).
21 #if 0
22 #define RLUTIL_USE_ANSI
23 #endif
24
25 /// Define: RLUTIL_STRING_T
26 /// Define/typedef this to your preference to override rlutil's string type.
27 ///
28 /// Defaults to std::string with C++ and char* with C.
29 #if 0
30 #define RLUTIL_STRING_T char*
31 #endif
32
33 #ifndef RLUTIL_INLINE
34 #ifdef _MSC_VER
35 #define RLUTIL_INLINE __inline
36 #else
37 #define RLUTIL_INLINE static __inline__
38 #endif
39 #endif
40
41 #ifdef __cplusplus
42 /// Common C++ headers
43 #include <iostream>
44 #include <string>
45 #include <cstdio> // for getch()
46 /// Namespace forward declarations
47 namespace rlutil {
48 RLUTIL_INLINE void locate(int x, int y);
49 }
50 #else
51 #include <stdio.h> // for getch() / printf()
52 #include <string.h> // for strlen()
53 RLUTIL_INLINE void locate(int x, int y); // Forward declare for C to avoid warnings
54 #endif // __cplusplus
55
56 #ifdef _WIN32
57 #include <windows.h> // for WinAPI and Sleep()
58 #define _NO_OLDNAMES // for MinGW compatibility
59 #include <conio.h> // for getch() and kbhit()
60 #define getch _getch
61 #define kbhit _kbhit
62 #else
63 #include <termios.h> // for getch() and kbhit()
64 #include <unistd.h> // for getch(), kbhit() and (u)sleep()
65 #include <sys/ioctl.h> // for getkey()
66 #include <sys/types.h> // for kbhit()
67 #include <sys/time.h> // for kbhit()
68
69 /// Function: getch
70 /// Get character without waiting for Return to be pressed.
71 /// Windows has this in conio.h
getch(void)72 RLUTIL_INLINE int getch(void) {
73 // Here be magic.
74 struct termios oldt, newt;
75 int ch;
76 tcgetattr(STDIN_FILENO, &oldt);
77 newt = oldt;
78 newt.c_lflag &= ~(ICANON | ECHO);
79 tcsetattr(STDIN_FILENO, TCSANOW, &newt);
80 ch = getchar();
81 tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
82 return ch;
83 }
84
85 /// Function: kbhit
86 /// Determines if keyboard has been hit.
87 /// Windows has this in conio.h
kbhit(void)88 RLUTIL_INLINE int kbhit(void) {
89 // Here be dragons.
90 static struct termios oldt, newt;
91 int cnt = 0;
92 tcgetattr(STDIN_FILENO, &oldt);
93 newt = oldt;
94 newt.c_lflag &= ~(ICANON | ECHO);
95 newt.c_iflag = 0; // input mode
96 newt.c_oflag = 0; // output mode
97 newt.c_cc[VMIN] = 1; // minimum time to wait
98 newt.c_cc[VTIME] = 1; // minimum characters to wait for
99 tcsetattr(STDIN_FILENO, TCSANOW, &newt);
100 ioctl(0, FIONREAD, &cnt); // Read count
101 struct timeval tv;
102 tv.tv_sec = 0;
103 tv.tv_usec = 100;
104 select(STDIN_FILENO+1, NULL, NULL, NULL, &tv); // A small time delay
105 tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
106 return cnt; // Return number of characters
107 }
108 #endif // _WIN32
109
110 #ifndef gotoxy
111 /// Function: gotoxy
112 /// Same as <rlutil.locate>.
gotoxy(int x,int y)113 RLUTIL_INLINE void gotoxy(int x, int y) {
114 #ifdef __cplusplus
115 rlutil::
116 #endif
117 locate(x,y);
118 }
119 #endif // gotoxy
120
121 #ifdef __cplusplus
122 /// Namespace: rlutil
123 /// In C++ all functions except <getch>, <kbhit> and <gotoxy> are arranged
124 /// under namespace rlutil. That is because some platforms have them defined
125 /// outside of rlutil.
126 namespace rlutil {
127 #endif
128
129 /**
130 * Defs: Internal typedefs and macros
131 * RLUTIL_STRING_T - String type depending on which one of C or C++ is used
132 * RLUTIL_PRINT(str) - Printing macro independent of C/C++
133 */
134
135 #ifdef __cplusplus
136 #ifndef RLUTIL_STRING_T
137 typedef std::string RLUTIL_STRING_T;
138 #endif // RLUTIL_STRING_T
139
140 #define RLUTIL_PRINT(st) do { std::cout << st; } while(false)
141 #else // __cplusplus
142 #ifndef RLUTIL_STRING_T
143 typedef const char* RLUTIL_STRING_T;
144 #endif // RLUTIL_STRING_T
145
146 #define RLUTIL_PRINT(st) printf("%s", st)
147 #endif // __cplusplus
148
149 /**
150 * Enums: Color codes
151 *
152 * BLACK - Black
153 * BLUE - Blue
154 * GREEN - Green
155 * CYAN - Cyan
156 * RED - Red
157 * MAGENTA - Magenta / purple
158 * BROWN - Brown / dark yellow
159 * GREY - Grey / dark white
160 * DARKGREY - Dark grey / light black
161 * LIGHTBLUE - Light blue
162 * LIGHTGREEN - Light green
163 * LIGHTCYAN - Light cyan
164 * LIGHTRED - Light red
165 * LIGHTMAGENTA - Light magenta / light purple
166 * YELLOW - Yellow (bright)
167 * WHITE - White (bright)
168 */
169 enum {
170 BLACK,
171 BLUE,
172 GREEN,
173 CYAN,
174 RED,
175 MAGENTA,
176 BROWN,
177 GREY,
178 DARKGREY,
179 LIGHTBLUE,
180 LIGHTGREEN,
181 LIGHTCYAN,
182 LIGHTRED,
183 LIGHTMAGENTA,
184 YELLOW,
185 WHITE
186 };
187
188 /**
189 * Consts: ANSI escape strings
190 *
191 * ANSI_CLS - Clears screen
192 * ANSI_ATTRIBUTE_RESET - Resets all attributes
193 * ANSI_CURSOR_HIDE - Hides the cursor
194 * ANSI_CURSOR_SHOW - Shows the cursor
195 * ANSI_CURSOR_HOME - Moves the cursor home (0,0)
196 * ANSI_BLACK - Black
197 * ANSI_RED - Red
198 * ANSI_GREEN - Green
199 * ANSI_BROWN - Brown / dark yellow
200 * ANSI_BLUE - Blue
201 * ANSI_MAGENTA - Magenta / purple
202 * ANSI_CYAN - Cyan
203 * ANSI_GREY - Grey / dark white
204 * ANSI_DARKGREY - Dark grey / light black
205 * ANSI_LIGHTRED - Light red
206 * ANSI_LIGHTGREEN - Light green
207 * ANSI_YELLOW - Yellow (bright)
208 * ANSI_LIGHTBLUE - Light blue
209 * ANSI_LIGHTMAGENTA - Light magenta / light purple
210 * ANSI_LIGHTCYAN - Light cyan
211 * ANSI_WHITE - White (bright)
212 * ANSI_BACKGROUND_BLACK - Black background
213 * ANSI_BACKGROUND_RED - Red background
214 * ANSI_BACKGROUND_GREEN - Green background
215 * ANSI_BACKGROUND_YELLOW - Yellow background
216 * ANSI_BACKGROUND_BLUE - Blue background
217 * ANSI_BACKGROUND_MAGENTA - Magenta / purple background
218 * ANSI_BACKGROUND_CYAN - Cyan background
219 * ANSI_BACKGROUND_WHITE - White background
220 */
221 const RLUTIL_STRING_T ANSI_CLS = "\033[2J\033[3J";
222 const RLUTIL_STRING_T ANSI_ATTRIBUTE_RESET = "\033[0m";
223 const RLUTIL_STRING_T ANSI_CURSOR_HIDE = "\033[?25l";
224 const RLUTIL_STRING_T ANSI_CURSOR_SHOW = "\033[?25h";
225 const RLUTIL_STRING_T ANSI_CURSOR_HOME = "\033[H";
226 const RLUTIL_STRING_T ANSI_BLACK = "\033[22;30m";
227 const RLUTIL_STRING_T ANSI_RED = "\033[22;31m";
228 const RLUTIL_STRING_T ANSI_GREEN = "\033[22;32m";
229 const RLUTIL_STRING_T ANSI_BROWN = "\033[22;33m";
230 const RLUTIL_STRING_T ANSI_BLUE = "\033[22;34m";
231 const RLUTIL_STRING_T ANSI_MAGENTA = "\033[22;35m";
232 const RLUTIL_STRING_T ANSI_CYAN = "\033[22;36m";
233 const RLUTIL_STRING_T ANSI_GREY = "\033[22;37m";
234 const RLUTIL_STRING_T ANSI_DARKGREY = "\033[01;30m";
235 const RLUTIL_STRING_T ANSI_LIGHTRED = "\033[01;31m";
236 const RLUTIL_STRING_T ANSI_LIGHTGREEN = "\033[01;32m";
237 const RLUTIL_STRING_T ANSI_YELLOW = "\033[01;33m";
238 const RLUTIL_STRING_T ANSI_LIGHTBLUE = "\033[01;34m";
239 const RLUTIL_STRING_T ANSI_LIGHTMAGENTA = "\033[01;35m";
240 const RLUTIL_STRING_T ANSI_LIGHTCYAN = "\033[01;36m";
241 const RLUTIL_STRING_T ANSI_WHITE = "\033[01;37m";
242 const RLUTIL_STRING_T ANSI_BACKGROUND_BLACK = "\033[40m";
243 const RLUTIL_STRING_T ANSI_BACKGROUND_RED = "\033[41m";
244 const RLUTIL_STRING_T ANSI_BACKGROUND_GREEN = "\033[42m";
245 const RLUTIL_STRING_T ANSI_BACKGROUND_YELLOW = "\033[43m";
246 const RLUTIL_STRING_T ANSI_BACKGROUND_BLUE = "\033[44m";
247 const RLUTIL_STRING_T ANSI_BACKGROUND_MAGENTA = "\033[45m";
248 const RLUTIL_STRING_T ANSI_BACKGROUND_CYAN = "\033[46m";
249 const RLUTIL_STRING_T ANSI_BACKGROUND_WHITE = "\033[47m";
250 // Remaining colors not supported as background colors
251
252 /**
253 * Enums: Key codes for keyhit()
254 *
255 * KEY_ESCAPE - Escape
256 * KEY_ENTER - Enter
257 * KEY_SPACE - Space
258 * KEY_INSERT - Insert
259 * KEY_HOME - Home
260 * KEY_END - End
261 * KEY_DELETE - Delete
262 * KEY_PGUP - PageUp
263 * KEY_PGDOWN - PageDown
264 * KEY_UP - Up arrow
265 * KEY_DOWN - Down arrow
266 * KEY_LEFT - Left arrow
267 * KEY_RIGHT - Right arrow
268 * KEY_F1 - F1
269 * KEY_F2 - F2
270 * KEY_F3 - F3
271 * KEY_F4 - F4
272 * KEY_F5 - F5
273 * KEY_F6 - F6
274 * KEY_F7 - F7
275 * KEY_F8 - F8
276 * KEY_F9 - F9
277 * KEY_F10 - F10
278 * KEY_F11 - F11
279 * KEY_F12 - F12
280 * KEY_NUMDEL - Numpad del
281 * KEY_NUMPAD0 - Numpad 0
282 * KEY_NUMPAD1 - Numpad 1
283 * KEY_NUMPAD2 - Numpad 2
284 * KEY_NUMPAD3 - Numpad 3
285 * KEY_NUMPAD4 - Numpad 4
286 * KEY_NUMPAD5 - Numpad 5
287 * KEY_NUMPAD6 - Numpad 6
288 * KEY_NUMPAD7 - Numpad 7
289 * KEY_NUMPAD8 - Numpad 8
290 * KEY_NUMPAD9 - Numpad 9
291 */
292 enum {
293 KEY_ESCAPE = 0,
294 KEY_ENTER = 1,
295 KEY_SPACE = 32,
296
297 KEY_INSERT = 2,
298 KEY_HOME = 3,
299 KEY_PGUP = 4,
300 KEY_DELETE = 5,
301 KEY_END = 6,
302 KEY_PGDOWN = 7,
303
304 KEY_UP = 14,
305 KEY_DOWN = 15,
306 KEY_LEFT = 16,
307 KEY_RIGHT = 17,
308
309 KEY_F1 = 18,
310 KEY_F2 = 19,
311 KEY_F3 = 20,
312 KEY_F4 = 21,
313 KEY_F5 = 22,
314 KEY_F6 = 23,
315 KEY_F7 = 24,
316 KEY_F8 = 25,
317 KEY_F9 = 26,
318 KEY_F10 = 27,
319 KEY_F11 = 28,
320 KEY_F12 = 29,
321
322 KEY_NUMDEL = 30,
323 KEY_NUMPAD0 = 31,
324 KEY_NUMPAD1 = 127,
325 KEY_NUMPAD2 = 128,
326 KEY_NUMPAD3 = 129,
327 KEY_NUMPAD4 = 130,
328 KEY_NUMPAD5 = 131,
329 KEY_NUMPAD6 = 132,
330 KEY_NUMPAD7 = 133,
331 KEY_NUMPAD8 = 134,
332 KEY_NUMPAD9 = 135
333 };
334
335 /// Function: getkey
336 /// Reads a key press (blocking) and returns a key code.
337 ///
338 /// See <Key codes for keyhit()>
339 ///
340 /// Note:
341 /// Only Arrows, Esc, Enter and Space are currently working properly.
getkey(void)342 RLUTIL_INLINE int getkey(void) {
343 #ifndef _WIN32
344 int cnt = kbhit(); // for ANSI escapes processing
345 #endif
346 int k = getch();
347 switch(k) {
348 case 0: {
349 int kk;
350 switch (kk = getch()) {
351 case 71: return KEY_NUMPAD7;
352 case 72: return KEY_NUMPAD8;
353 case 73: return KEY_NUMPAD9;
354 case 75: return KEY_NUMPAD4;
355 case 77: return KEY_NUMPAD6;
356 case 79: return KEY_NUMPAD1;
357 case 80: return KEY_NUMPAD4;
358 case 81: return KEY_NUMPAD3;
359 case 82: return KEY_NUMPAD0;
360 case 83: return KEY_NUMDEL;
361 default: return kk-59+KEY_F1; // Function keys
362 }}
363 case 224: {
364 int kk;
365 switch (kk = getch()) {
366 case 71: return KEY_HOME;
367 case 72: return KEY_UP;
368 case 73: return KEY_PGUP;
369 case 75: return KEY_LEFT;
370 case 77: return KEY_RIGHT;
371 case 79: return KEY_END;
372 case 80: return KEY_DOWN;
373 case 81: return KEY_PGDOWN;
374 case 82: return KEY_INSERT;
375 case 83: return KEY_DELETE;
376 default: return kk-123+KEY_F1; // Function keys
377 }}
378 case 13: return KEY_ENTER;
379 #ifdef _WIN32
380 case 27: return KEY_ESCAPE;
381 #else // _WIN32
382 case 155: // single-character CSI
383 case 27: {
384 // Process ANSI escape sequences
385 if (cnt >= 3 && getch() == '[') {
386 switch (k = getch()) {
387 case 'A': return KEY_UP;
388 case 'B': return KEY_DOWN;
389 case 'C': return KEY_RIGHT;
390 case 'D': return KEY_LEFT;
391 }
392 } else return KEY_ESCAPE;
393 }
394 #endif // _WIN32
395 default: return k;
396 }
397 }
398
399 /// Function: nb_getch
400 /// Non-blocking getch(). Returns 0 if no key was pressed.
nb_getch(void)401 RLUTIL_INLINE int nb_getch(void) {
402 if (kbhit()) return getch();
403 else return 0;
404 }
405
406 /// Function: getANSIColor
407 /// Return ANSI color escape sequence for specified number 0-15.
408 ///
409 /// See <Color Codes>
getANSIColor(const int c)410 RLUTIL_INLINE RLUTIL_STRING_T getANSIColor(const int c) {
411 switch (c) {
412 case BLACK : return ANSI_BLACK;
413 case BLUE : return ANSI_BLUE; // non-ANSI
414 case GREEN : return ANSI_GREEN;
415 case CYAN : return ANSI_CYAN; // non-ANSI
416 case RED : return ANSI_RED; // non-ANSI
417 case MAGENTA : return ANSI_MAGENTA;
418 case BROWN : return ANSI_BROWN;
419 case GREY : return ANSI_GREY;
420 case DARKGREY : return ANSI_DARKGREY;
421 case LIGHTBLUE : return ANSI_LIGHTBLUE; // non-ANSI
422 case LIGHTGREEN : return ANSI_LIGHTGREEN;
423 case LIGHTCYAN : return ANSI_LIGHTCYAN; // non-ANSI;
424 case LIGHTRED : return ANSI_LIGHTRED; // non-ANSI;
425 case LIGHTMAGENTA: return ANSI_LIGHTMAGENTA;
426 case YELLOW : return ANSI_YELLOW; // non-ANSI
427 case WHITE : return ANSI_WHITE;
428 default: return "";
429 }
430 }
431
432 /// Function: getANSIBackgroundColor
433 /// Return ANSI background color escape sequence for specified number 0-15.
434 ///
435 /// See <Color Codes>
getANSIBackgroundColor(const int c)436 RLUTIL_INLINE RLUTIL_STRING_T getANSIBackgroundColor(const int c) {
437 switch (c) {
438 case BLACK : return ANSI_BACKGROUND_BLACK;
439 case BLUE : return ANSI_BACKGROUND_BLUE;
440 case GREEN : return ANSI_BACKGROUND_GREEN;
441 case CYAN : return ANSI_BACKGROUND_CYAN;
442 case RED : return ANSI_BACKGROUND_RED;
443 case MAGENTA: return ANSI_BACKGROUND_MAGENTA;
444 case BROWN : return ANSI_BACKGROUND_YELLOW;
445 case GREY : return ANSI_BACKGROUND_WHITE;
446 default: return "";
447 }
448 }
449
450 /// Function: setColor
451 /// Change color specified by number (Windows / QBasic colors).
452 /// Don't change the background color
453 ///
454 /// See <Color Codes>
setColor(int c)455 RLUTIL_INLINE void setColor(int c) {
456 #if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
457 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
458 CONSOLE_SCREEN_BUFFER_INFO csbi;
459
460 GetConsoleScreenBufferInfo(hConsole, &csbi);
461
462 SetConsoleTextAttribute(hConsole, (csbi.wAttributes & 0xFFF0) | (WORD)c); // Foreground colors take up the least significant byte
463 #else
464 RLUTIL_PRINT(getANSIColor(c));
465 #endif
466 }
467
468 /// Function: setBackgroundColor
469 /// Change background color specified by number (Windows / QBasic colors).
470 /// Don't change the foreground color
471 ///
472 /// See <Color Codes>
setBackgroundColor(int c)473 RLUTIL_INLINE void setBackgroundColor(int c) {
474 #if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
475 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
476 CONSOLE_SCREEN_BUFFER_INFO csbi;
477
478 GetConsoleScreenBufferInfo(hConsole, &csbi);
479
480 SetConsoleTextAttribute(hConsole, (csbi.wAttributes & 0xFF0F) | (((WORD)c) << 4)); // Background colors take up the second-least significant byte
481 #else
482 RLUTIL_PRINT(getANSIBackgroundColor(c));
483 #endif
484 }
485
486 /// Function: saveDefaultColor
487 /// Call once to preserve colors for use in resetColor()
488 /// on Windows without ANSI, no-op otherwise
489 ///
490 /// See <Color Codes>
491 /// See <resetColor>
saveDefaultColor()492 RLUTIL_INLINE int saveDefaultColor() {
493 #if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
494 static char initialized = 0; // bool
495 static WORD attributes;
496
497 if (!initialized) {
498 CONSOLE_SCREEN_BUFFER_INFO csbi;
499 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
500 attributes = csbi.wAttributes;
501 initialized = 1;
502 }
503 return (int)attributes;
504 #else
505 return -1;
506 #endif
507 }
508
509 /// Function: resetColor
510 /// Reset color to default
511 /// Requires a call to saveDefaultColor() to set the defaults
512 ///
513 /// See <Color Codes>
514 /// See <setColor>
515 /// See <saveDefaultColor>
resetColor()516 RLUTIL_INLINE void resetColor() {
517 #if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
518 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), (WORD)saveDefaultColor());
519 #else
520 RLUTIL_PRINT(ANSI_ATTRIBUTE_RESET);
521 #endif
522 }
523
524 /// Function: cls
525 /// Clears screen, resets all attributes and moves cursor home.
cls(void)526 RLUTIL_INLINE void cls(void) {
527 #if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
528 // Based on https://msdn.microsoft.com/en-us/library/windows/desktop/ms682022%28v=vs.85%29.aspx
529 const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
530 const COORD coordScreen = {0, 0};
531 DWORD cCharsWritten;
532 CONSOLE_SCREEN_BUFFER_INFO csbi;
533
534 GetConsoleScreenBufferInfo(hConsole, &csbi);
535 const DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
536 FillConsoleOutputCharacter(hConsole, (TCHAR)' ', dwConSize, coordScreen, &cCharsWritten);
537
538 GetConsoleScreenBufferInfo(hConsole, &csbi);
539 FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
540
541 SetConsoleCursorPosition(hConsole, coordScreen);
542 #else
543 RLUTIL_PRINT(ANSI_CLS);
544 RLUTIL_PRINT(ANSI_CURSOR_HOME);
545 #endif
546 }
547
548 /// Function: locate
549 /// Sets the cursor position to 1-based x,y.
locate(int x,int y)550 RLUTIL_INLINE void locate(int x, int y) {
551 #if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
552 COORD coord;
553 // TODO: clamping/assert for x/y <= 0?
554 coord.X = (SHORT)(x - 1);
555 coord.Y = (SHORT)(y - 1); // Windows uses 0-based coordinates
556 SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
557 #else // _WIN32 || USE_ANSI
558 #ifdef __cplusplus
559 RLUTIL_PRINT("\033[" << y << ";" << x << "H");
560 #else // __cplusplus
561 char buf[32];
562 sprintf(buf, "\033[%d;%df", y, x);
563 RLUTIL_PRINT(buf);
564 #endif // __cplusplus
565 #endif // _WIN32 || USE_ANSI
566 }
567
568 /// Function: setString
569 /// Prints the supplied string without advancing the cursor
570 #ifdef __cplusplus
setString(const RLUTIL_STRING_T & str_)571 RLUTIL_INLINE void setString(const RLUTIL_STRING_T & str_) {
572 const char * const str = str_.data();
573 unsigned int len = str_.size();
574 #else // __cplusplus
575 RLUTIL_INLINE void setString(RLUTIL_STRING_T str) {
576 unsigned int len = strlen(str);
577 #endif // __cplusplus
578 #if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
579 HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
580 DWORD numberOfCharsWritten;
581 CONSOLE_SCREEN_BUFFER_INFO csbi;
582
583 GetConsoleScreenBufferInfo(hConsoleOutput, &csbi);
584 WriteConsoleOutputCharacter(hConsoleOutput, str, len, csbi.dwCursorPosition, &numberOfCharsWritten);
585 #else // _WIN32 || USE_ANSI
586 RLUTIL_PRINT(str);
587 #ifdef __cplusplus
588 RLUTIL_PRINT("\033[" << len << 'D');
589 #else // __cplusplus
590 char buf[3 + 20 + 1]; // 20 = max length of 64-bit unsigned int when printed as dec
591 sprintf(buf, "\033[%uD", len);
592 RLUTIL_PRINT(buf);
593 #endif // __cplusplus
594 #endif // _WIN32 || USE_ANSI
595 }
596
597 /// Function: setChar
598 /// Sets the character at the cursor without advancing the cursor
599 RLUTIL_INLINE void setChar(char ch) {
600 const char buf[] = {ch, 0};
601 setString(buf);
602 }
603
604 /// Function: setCursorVisibility
605 /// Shows/hides the cursor.
606 RLUTIL_INLINE void setCursorVisibility(char visible) {
607 #if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
608 HANDLE hConsoleOutput = GetStdHandle( STD_OUTPUT_HANDLE );
609 CONSOLE_CURSOR_INFO structCursorInfo;
610 GetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); // Get current cursor size
611 structCursorInfo.bVisible = (visible ? TRUE : FALSE);
612 SetConsoleCursorInfo( hConsoleOutput, &structCursorInfo );
613 #else // _WIN32 || USE_ANSI
614 RLUTIL_PRINT((visible ? ANSI_CURSOR_SHOW : ANSI_CURSOR_HIDE));
615 #endif // _WIN32 || USE_ANSI
616 }
617
618 /// Function: hidecursor
619 /// Hides the cursor.
620 RLUTIL_INLINE void hidecursor(void) {
621 setCursorVisibility(0);
622 }
623
624 /// Function: showcursor
625 /// Shows the cursor.
626 RLUTIL_INLINE void showcursor(void) {
627 setCursorVisibility(1);
628 }
629
630 /// Function: msleep
631 /// Waits given number of milliseconds before continuing.
632 RLUTIL_INLINE void msleep(unsigned int ms) {
633 #ifdef _WIN32
634 Sleep(ms);
635 #else
636 // usleep argument must be under 1 000 000
637 if (ms > 1000) sleep(ms/1000000);
638 usleep((ms % 1000000) * 1000);
639 #endif
640 }
641
642 /// Function: trows
643 /// Get the number of rows in the terminal window or -1 on error.
644 RLUTIL_INLINE int trows(void) {
645 #ifdef _WIN32
646 CONSOLE_SCREEN_BUFFER_INFO csbi;
647 if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
648 return -1;
649 else
650 return csbi.srWindow.Bottom - csbi.srWindow.Top + 1; // Window height
651 // return csbi.dwSize.Y; // Buffer height
652 #else
653 #ifdef TIOCGSIZE
654 struct ttysize ts;
655 ioctl(STDIN_FILENO, TIOCGSIZE, &ts);
656 return ts.ts_lines;
657 #elif defined(TIOCGWINSZ)
658 struct winsize ts;
659 ioctl(STDIN_FILENO, TIOCGWINSZ, &ts);
660 return ts.ws_row;
661 #else // TIOCGSIZE
662 return -1;
663 #endif // TIOCGSIZE
664 #endif // _WIN32
665 }
666
667 /// Function: tcols
668 /// Get the number of columns in the terminal window or -1 on error.
669 RLUTIL_INLINE int tcols(void) {
670 #ifdef _WIN32
671 CONSOLE_SCREEN_BUFFER_INFO csbi;
672 if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
673 return -1;
674 else
675 return csbi.srWindow.Right - csbi.srWindow.Left + 1; // Window width
676 // return csbi.dwSize.X; // Buffer width
677 #else
678 #ifdef TIOCGSIZE
679 struct ttysize ts;
680 ioctl(STDIN_FILENO, TIOCGSIZE, &ts);
681 return ts.ts_cols;
682 #elif defined(TIOCGWINSZ)
683 struct winsize ts;
684 ioctl(STDIN_FILENO, TIOCGWINSZ, &ts);
685 return ts.ws_col;
686 #else // TIOCGSIZE
687 return -1;
688 #endif // TIOCGSIZE
689 #endif // _WIN32
690 }
691
692 /// Function: anykey
693 /// Waits until a key is pressed.
694 /// In C++, it either takes no arguments
695 /// or a template-type-argument-deduced
696 /// argument.
697 /// In C, it takes a const char* representing
698 /// the message to be displayed, or NULL
699 /// for no message.
700 #ifdef __cplusplus
701 RLUTIL_INLINE void anykey() {
702 getch();
703 }
704
705 template <class T> void anykey(const T& msg) {
706 RLUTIL_PRINT(msg);
707 #else
708 RLUTIL_INLINE void anykey(RLUTIL_STRING_T msg) {
709 if (msg)
710 RLUTIL_PRINT(msg);
711 #endif // __cplusplus
712 getch();
713 }
714
715 // Classes are here at the end so that documentation is pretty.
716
717 #ifdef __cplusplus
718 /// Class: CursorHider
719 /// RAII OOP wrapper for <rlutil.hidecursor>.
720 /// Hides the cursor and shows it again
721 /// when the object goes out of scope.
722 struct CursorHider {
723 CursorHider() { hidecursor(); }
724 ~CursorHider() { showcursor(); }
725 };
726
727 } // namespace rlutil
728 #endif
729