1 /** 2 * D port of rlutil.h 3 * https://github.com/tapio/rlutil 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 * License: 11 * <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License</a> 12 * Authors: 13 * <a href="http://github.com/danyalzia">Danyal Zia</a> 14 */ 15 16 module rlutil.rlutil; 17 18 import std.stdio : printf; 19 import std.process : executeShell; 20 import std..string : toStringz; 21 import std.c.stdio; 22 import std.c.stdlib; 23 24 /// Define: RLUTIL_USE_ANSI 25 /// Define this to use ANSI escape sequences also on Windows 26 /// (defaults to using WinAPI instead). 27 enum RLUTIL_USE_ANSI = true; 28 29 version (Windows) 30 enum Windows = true; 31 else 32 enum Windows = false; 33 34 version (Posix) 35 enum Linux = true; 36 else 37 enum Linux = false; 38 39 version (Windows) { 40 import std.c.windows.windows; 41 42 // Unfortunately, D2 doesn't provide these functions 43 extern (C) int getch(); 44 extern (C) int kbhit(); 45 } 46 47 version (Posix) { 48 import core.sys.posix.unistd, 49 core.sys.posix.sys.ioctl, 50 core.sys.linux.termios, 51 core.sys.posix.termios, 52 core.sys.posix.fcntl, 53 core.sys.posix.sys.time; 54 } 55 56 alias RLUTIL_STRING_T = string; 57 58 /// Function: getch 59 /// Get character without waiting for Return to be pressed. 60 /// Windows has this in conio.h 61 version (Posix) { 62 int getch() { 63 // Here be magic. 64 termios oldt, newt; 65 int ch; 66 tcgetattr(STDIN_FILENO, &oldt); 67 newt = oldt; 68 newt.c_lflag &= ~(ICANON | ECHO); 69 tcsetattr(STDIN_FILENO, TCSANOW, &newt); 70 ch = getchar(); 71 tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 72 return ch; 73 } 74 75 /// Function: kbhit 76 /// Determines if keyboard has been hit. 77 /// Windows has this in conio.h 78 int kbhit() { 79 // Here be dragons. 80 static termios oldt, newt; 81 int cnt = 0; 82 tcgetattr(STDIN_FILENO, &oldt); 83 newt = oldt; 84 newt.c_lflag &= ~(ICANON | ECHO); 85 newt.c_iflag = 0; // input mode 86 newt.c_oflag = 0; // output mode 87 newt.c_cc[VMIN] = 1; // minimum time to wait 88 newt.c_cc[VTIME] = 1; // minimum characters to wait for 89 tcsetattr(STDIN_FILENO, TCSANOW, &newt); 90 ioctl(0, FIONREAD, &cnt); // Read count 91 timeval tv; 92 tv.tv_sec = 0; 93 tv.tv_usec = 100; 94 select(STDIN_FILENO+1, null, null, null, &tv); // A small time delay 95 tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 96 return cnt; // Return number of characters 97 } 98 } 99 100 /// Function: gotoxy 101 /// Same as <rlutil.locate>. 102 void gotoxy(int x, int y) { 103 locate(x,y); 104 } 105 106 /** 107 * Enums: Color codes 108 * 109 * BLACK - Black 110 * BLUE - Blue 111 * GREEN - Green 112 * CYAN - Cyan 113 * RED - Red 114 * MAGENTA - Magenta / purple 115 * BROWN - Brown / dark yellow 116 * GREY - Grey / dark white 117 * DARKGREY - Dark grey / light black 118 * LIGHTBLUE - Light blue 119 * LIGHTGREEN - Light green 120 * LIGHTCYAN - Light cyan 121 * LIGHTRED - Light red 122 * LIGHTMAGENTA - Light magenta / light purple 123 * YELLOW - Yellow (bright) 124 * WHITE - White (bright) 125 */ 126 enum { 127 BLACK, 128 BLUE, 129 GREEN, 130 CYAN, 131 RED, 132 MAGENTA, 133 BROWN, 134 GREY, 135 DARKGREY, 136 LIGHTBLUE, 137 LIGHTGREEN, 138 LIGHTCYAN, 139 LIGHTRED, 140 LIGHTMAGENTA, 141 YELLOW, 142 WHITE 143 }; 144 145 /** 146 * Consts: ANSI color strings 147 * 148 * ANSI_CLS - Clears screen 149 * ANSI_BLACK - Black 150 * ANSI_RED - Red 151 * ANSI_GREEN - Green 152 * ANSI_BROWN - Brown / dark yellow 153 * ANSI_BLUE - Blue 154 * ANSI_MAGENTA - Magenta / purple 155 * ANSI_CYAN - Cyan 156 * ANSI_GREY - Grey / dark white 157 * ANSI_DARKGREY - Dark grey / light black 158 * ANSI_LIGHTRED - Light red 159 * ANSI_LIGHTGREEN - Light green 160 * ANSI_YELLOW - Yellow (bright) 161 * ANSI_LIGHTBLUE - Light blue 162 * ANSI_LIGHTMAGENTA - Light magenta / light purple 163 * ANSI_LIGHTCYAN - Light cyan 164 * ANSI_WHITE - White (bright) 165 */ 166 RLUTIL_STRING_T ANSI_CLS = "\033[2J"; 167 RLUTIL_STRING_T ANSI_BLACK = "\033[22;30m"; 168 RLUTIL_STRING_T ANSI_RED = "\033[22;31m"; 169 RLUTIL_STRING_T ANSI_GREEN = "\033[22;32m"; 170 RLUTIL_STRING_T ANSI_BROWN = "\033[22;33m"; 171 RLUTIL_STRING_T ANSI_BLUE = "\033[22;34m"; 172 RLUTIL_STRING_T ANSI_MAGENTA = "\033[22;35m"; 173 RLUTIL_STRING_T ANSI_CYAN = "\033[22;36m"; 174 RLUTIL_STRING_T ANSI_GREY = "\033[22;37m"; 175 RLUTIL_STRING_T ANSI_DARKGREY = "\033[01;30m"; 176 RLUTIL_STRING_T ANSI_LIGHTRED = "\033[01;31m"; 177 RLUTIL_STRING_T ANSI_LIGHTGREEN = "\033[01;32m"; 178 RLUTIL_STRING_T ANSI_YELLOW = "\033[01;33m"; 179 RLUTIL_STRING_T ANSI_LIGHTBLUE = "\033[01;34m"; 180 RLUTIL_STRING_T ANSI_LIGHTMAGENTA = "\033[01;35m"; 181 RLUTIL_STRING_T ANSI_LIGHTCYAN = "\033[01;36m"; 182 RLUTIL_STRING_T ANSI_WHITE = "\033[01;37m"; 183 184 /** 185 * Consts: Key codes for keyhit() 186 * 187 * KEY_ESCAPE - Escape 188 * KEY_ENTER - Enter 189 * KEY_SPACE - Space 190 * KEY_INSERT - Insert 191 * KEY_HOME - Home 192 * KEY_END - End 193 * KEY_DELETE - Delete 194 * KEY_PGUP - PageUp 195 * KEY_PGDOWN - PageDown 196 * KEY_UP - Up arrow 197 * KEY_DOWN - Down arrow 198 * KEY_LEFT - Left arrow 199 * KEY_RIGHT - Right arrow 200 * KEY_F1 - F1 201 * KEY_F2 - F2 202 * KEY_F3 - F3 203 * KEY_F4 - F4 204 * KEY_F5 - F5 205 * KEY_F6 - F6 206 * KEY_F7 - F7 207 * KEY_F8 - F8 208 * KEY_F9 - F9 209 * KEY_F10 - F10 210 * KEY_F11 - F11 211 * KEY_F12 - F12 212 * KEY_NUMDEL - Numpad del 213 * KEY_NUMPAD0 - Numpad 0 214 * KEY_NUMPAD1 - Numpad 1 215 * KEY_NUMPAD2 - Numpad 2 216 * KEY_NUMPAD3 - Numpad 3 217 * KEY_NUMPAD4 - Numpad 4 218 * KEY_NUMPAD5 - Numpad 5 219 * KEY_NUMPAD6 - Numpad 6 220 * KEY_NUMPAD7 - Numpad 7 221 * KEY_NUMPAD8 - Numpad 8 222 * KEY_NUMPAD9 - Numpad 9 223 */ 224 const int KEY_ESCAPE = 0; 225 const int KEY_ENTER = 1; 226 const int KEY_SPACE = 32; 227 228 const int KEY_INSERT = 2; 229 const int KEY_HOME = 3; 230 const int KEY_PGUP = 4; 231 const int KEY_DELETE = 5; 232 const int KEY_END = 6; 233 const int KEY_PGDOWN = 7; 234 235 const int KEY_UP = 14; 236 const int KEY_DOWN = 15; 237 const int KEY_LEFT = 16; 238 const int KEY_RIGHT = 17; 239 240 const int KEY_F1 = 18; 241 const int KEY_F2 = 19; 242 const int KEY_F3 = 20; 243 const int KEY_F4 = 21; 244 const int KEY_F5 = 22; 245 const int KEY_F6 = 23; 246 const int KEY_F7 = 24; 247 const int KEY_F8 = 25; 248 const int KEY_F9 = 26; 249 const int KEY_F10 = 27; 250 const int KEY_F11 = 28; 251 const int KEY_F12 = 29; 252 253 const int KEY_NUMDEL = 30; 254 const int KEY_NUMPAD0 = 31; 255 const int KEY_NUMPAD1 = 127; 256 const int KEY_NUMPAD2 = 128; 257 const int KEY_NUMPAD3 = 129; 258 const int KEY_NUMPAD4 = 130; 259 const int KEY_NUMPAD5 = 131; 260 const int KEY_NUMPAD6 = 132; 261 const int KEY_NUMPAD7 = 133; 262 const int KEY_NUMPAD8 = 134; 263 const int KEY_NUMPAD9 = 135; 264 265 /// Function: getkey 266 /// Reads a key press (blocking) and returns a key code. 267 /// 268 /// See <Key codes for keyhit()> 269 /// 270 /// Note: 271 /// Only Arrows, Esc, Enter and Space are currently working properly. 272 int getkey() { 273 version(Posix) 274 int cnt = kbhit(); // for ANSI escapes processing 275 276 int k = getch(); 277 switch(k) { 278 case 0: { 279 int kk; 280 switch (kk = getch()) { 281 case 71: return KEY_NUMPAD7; 282 case 72: return KEY_NUMPAD8; 283 case 73: return KEY_NUMPAD9; 284 case 75: return KEY_NUMPAD4; 285 case 77: return KEY_NUMPAD6; 286 case 79: return KEY_NUMPAD1; 287 case 80: return KEY_NUMPAD4; 288 case 81: return KEY_NUMPAD3; 289 case 82: return KEY_NUMPAD0; 290 case 83: return KEY_NUMDEL; 291 default: return kk-59+KEY_F1; // Function keys 292 }} 293 case 224: { 294 int kk; 295 switch (kk = getch()) { 296 case 71: return KEY_HOME; 297 case 72: return KEY_UP; 298 case 73: return KEY_PGUP; 299 case 75: return KEY_LEFT; 300 case 77: return KEY_RIGHT; 301 case 79: return KEY_END; 302 case 80: return KEY_DOWN; 303 case 81: return KEY_PGDOWN; 304 case 82: return KEY_INSERT; 305 case 83: return KEY_DELETE; 306 default: return kk-123+KEY_F1; // Function keys 307 }} 308 case 13: return KEY_ENTER; 309 version (Windows) 310 case 27: return KEY_ESCAPE; 311 version (Posix) { 312 case 155: // single-character CSI 313 case 27: { 314 // Process ANSI escape sequences 315 if (cnt >= 3 && getch() == '[') { 316 switch (k = getch()) { 317 case 'A': return KEY_UP; 318 case 'B': return KEY_DOWN; 319 case 'C': return KEY_RIGHT; 320 case 'D': return KEY_LEFT; 321 } 322 } else return KEY_ESCAPE; 323 } 324 } 325 default: return k; 326 } 327 } 328 329 /// Function: nb_getch 330 /// Non-blocking getch(). Returns 0 if no key was pressed. 331 int nb_getch() { 332 if (kbhit()) return getch(); 333 else return 0; 334 } 335 336 /// Function: getANSIColor 337 /// Return ANSI color escape sequence for specified number 0-15. 338 /// 339 /// See <Color Codes> 340 RLUTIL_STRING_T getANSIColor(const(int) c) { 341 switch (c) { 342 case 0 : return ANSI_BLACK; 343 case 1 : return ANSI_BLUE; // non-ANSI 344 case 2 : return ANSI_GREEN; 345 case 3 : return ANSI_CYAN; // non-ANSI 346 case 4 : return ANSI_RED; // non-ANSI 347 case 5 : return ANSI_MAGENTA; 348 case 6 : return ANSI_BROWN; 349 case 7 : return ANSI_GREY; 350 case 8 : return ANSI_DARKGREY; 351 case 9 : return ANSI_LIGHTBLUE; // non-ANSI 352 case 10: return ANSI_LIGHTGREEN; 353 case 11: return ANSI_LIGHTCYAN; // non-ANSI; 354 case 12: return ANSI_LIGHTRED; // non-ANSI; 355 case 13: return ANSI_LIGHTMAGENTA; 356 case 14: return ANSI_YELLOW; // non-ANSI 357 case 15: return ANSI_WHITE; 358 default: return ""; 359 } 360 } 361 362 /// Function: setColor 363 /// Change color specified by number (Windows / QBasic colors). 364 /// 365 /// See <Color Codes> 366 void setColor(int c) { 367 version (Windows) { 368 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 369 SetConsoleTextAttribute(hConsole, cast(WORD)c); 370 } 371 372 373 version (Posix) { 374 printf("%s", getANSIColor(c).toStringz); 375 } 376 } 377 378 void cls() { 379 if (Windows == true) { 380 // TODO: This is cheating... 381 executeShell("cls"); 382 } 383 384 else { 385 printf("%s", "\033[2J\033[H".toStringz); 386 } 387 } 388 389 /// Function: locate 390 /// Sets the cursor position to 1-based x,y. 391 //void locate(int x, int y) { 392 void locate(int x, int y) { 393 version (Windows) { 394 import std.conv; 395 COORD coord; 396 coord.X = to!(short)(x-1); 397 coord.Y = to!(short)(y-1); // Windows uses 0-based coordinates 398 SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); 399 } 400 401 version (Posix) { 402 char* buf; 403 const(char*) format = "\033[%d;%df"; 404 sprintf(buf, format, y, x); 405 printf("%s", buf); 406 } 407 } 408 409 /// Function: hidecursor 410 /// Hides the cursor. 411 void hidecursor() { 412 version (Windows) { 413 HANDLE hConsoleOutput; 414 CONSOLE_CURSOR_INFO structCursorInfo; 415 hConsoleOutput = GetStdHandle( STD_OUTPUT_HANDLE ); 416 GetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); // Get current cursor size 417 structCursorInfo.bVisible = FALSE; 418 SetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); 419 } 420 421 version (Posix) { 422 printf("%s", "\033[?25l".toStringz); 423 } 424 } 425 426 /// Function: showcursor 427 /// Shows the cursor. 428 void showcursor() { 429 version (Windows) { 430 HANDLE hConsoleOutput; 431 CONSOLE_CURSOR_INFO structCursorInfo; 432 hConsoleOutput = GetStdHandle( STD_OUTPUT_HANDLE ); 433 GetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); // Get current cursor size 434 structCursorInfo.bVisible = TRUE; 435 SetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); 436 } 437 438 version (Posix) { 439 printf("%s", "\033[?25h".toStringz); 440 } 441 } 442 443 /// Function: msleep 444 /// Waits given number of milliseconds before continuing. 445 void msleep(uint ms) { 446 version (Windows) { 447 Sleep(ms); 448 } 449 450 version (Posix) { 451 // usleep argument must be under 1 000 000 452 if (ms > 1000) sleep(ms/1000000); 453 usleep((ms % 1000000) * 1000); 454 } 455 } 456 457 ///// Function: trows 458 ///// Get the number of rows in the terminal window or -1 on error. 459 //int trows() { 460 //version (Windows) { 461 //CONSOLE_SCREEN_BUFFER_INFO csbi; 462 //if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) 463 //return -1; 464 //else 465 //return csbi.srWindow.Bottom - csbi.srWindow.Top + 1; // Window height 466 //// return csbi.dwSize.Y; // Buffer height 467 //} 468 469 //ttysize ts; 470 //ioctl(STDIN_FILENO, TIOCGSIZE, &ts); 471 //return ts.ts_lines; 472 //} 473 474 ///// Function: tcols 475 ///// Get the number of columns in the terminal window or -1 on error. 476 //int tcols() { 477 //version (Windows) { 478 //CONSOLE_SCREEN_BUFFER_INFO csbi; 479 //if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) 480 //return -1; 481 //else 482 //return csbi.srWindow.Right - csbi.srWindow.Left + 1; // Window width 483 //// return csbi.dwSize.X; // Buffer width 484 485 //winsize ts; 486 //ioctl(STDIN_FILENO, TIOCGWINSZ, &ts); 487 //return ts.ws_col; 488 //} 489 //} 490 491 /// Function: anykey 492 /// Waits until a key is pressed. 493 void anykey() { 494 getch(); 495 }