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