/platform/osx/scaffold/console.d
D | 759 lines | 617 code | 73 blank | 69 comment | 211 complexity | 273b60f39cc4e4d1a5de6f7983274c4d MD5 | raw file
1/* 2 * console.d 3 * 4 * This file implements the Console interfaces for the OS X system. 5 * 6 * Author: Dave Wilkinson 7 * 8 */ 9 10module scaffold.console; 11 12import djehuty; 13 14import platform.unix.common; 15 16import platform.application; 17 18import synch.thread; 19 20import cui.application; 21import cui.window; 22 23private int _toNearestConsoleColor(Color clr) { 24 // 16 colors on console 25 // For each channel, it can be 00, 88, or ff 26 // That is, something mid range 27 28 int nearRed, nearGreen, nearBlue; 29 int ret; 30 31 nearRed = cast(int)((clr.red * 3.0) + 0.5); 32 nearGreen = cast(int)((clr.green * 3.0) + 0.5); 33 nearBlue = cast(int)((clr.blue * 3.0) + 0.5); 34 35 if ((nearRed == nearGreen) && (nearGreen == nearBlue)) { 36 // gray 37 if (clr.red < (Color.DarkGray.red / 2.0)) { 38 // Closer to black 39 ret = 0; 40 } 41 else if (clr.red < ((Color.Gray.red - Color.DarkGray.red) / 2.0) + Color.DarkGray.red) { 42 // Closer to dark gray 43 ret = 8; 44 } 45 else if (clr.red < ((Color.White.red - Color.Gray.red) / 2.0) + Color.Gray.red) { 46 // Closer to light gray 47 ret = 7; 48 } 49 else { 50 // Closer to white 51 ret = 15; 52 } 53 } 54 else { 55 // Nearest color match 56 static int[3][] translations = [ 57 [1,0,0], // 1, Dark Red 58 [0,1,0], // 2, Dark Green 59 [1,1,0], // 3, Dark Yellow 60 [0,0,1], // 4, Dark Blue 61 [1,0,1], // 5, Dark Magenta 62 [0,1,1], // 6, Dark Cyan 63 64 [2,0,0], // 9, Dark Red 65 [0,2,0], // 10, Dark Green 66 [2,2,0], // 11, Dark Yellow 67 [0,0,2], // 12, Dark Blue 68 [2,0,2], // 13, Dark Magenta 69 [0,2,2], // 14, Dark Cyan 70 ]; 71 72 float mindistance = 4*3; 73 74 foreach(size_t i, coord; translations) { 75 // Compare euclidian distance 76 float distance = 0.0; 77 float intermediate; 78 79 intermediate = coord[0] - nearRed; 80 intermediate *= intermediate; 81 82 distance += intermediate; 83 84 intermediate = coord[1] - nearGreen; 85 intermediate *= intermediate; 86 87 distance += intermediate; 88 89 intermediate = coord[2] - nearBlue; 90 intermediate *= intermediate; 91 92 distance += intermediate; 93 94 // Omitting square root, it is unnecessary for comparison 95 if (mindistance > distance) { 96 mindistance = distance; 97 ret = i; 98 ret++; 99 if (ret > 6) { 100 ret += 2; 101 } 102 } 103 } 104 } 105 106 return ret; 107} 108 109 110void ConsoleSetColors(Color fg, Color bg) { 111 int fgidx = _toNearestConsoleColor(fg); 112 int bgidx = _toNearestConsoleColor(bg); 113 114 int bright = 0; 115 if (fgidx > 7) { 116 fgidx %= 8; 117 bright = 1; 118 } 119 if (ApplicationController.instance.usingCurses) { 120 int idx = fgidx << 3; 121 idx |= bgidx; 122 123 if (bright) { 124 Curses.attron(Curses.A_BOLD); 125 } 126 else { 127 Curses.attroff(Curses.A_BOLD); 128 } 129 130 Curses.init_pair(idx, fgidx, bgidx); 131 Curses.attron(Curses.COLOR_PAIR(idx)); 132 } 133 else { 134 printf("\x1B[%d;%d;%dm", bright, 30 + fgidx, 40 + bgidx); 135 } 136} 137 138//will return the next character pressed 139Key consoleGetKey() { 140 Key ret; 141 142 ubyte[18] tmp; 143 uint count; 144 145 ret.code = Curses.getch(); 146// Curses.move(0,0); 147// Console.put(" "); 148// Curses.move(0,0); 149// Console.put(ret.code, " "); 150 151 if (ret.code != 0x1B) { 152 // Not an escape sequence 153 154 if (ret.code == Curses.KEY_MOUSE || ret.code == Curses.KEY_RESIZE || ret.code == Curses.KEY_EVENT || ret.code >= Curses.KEY_MAX) { 155 return ret; 156 } 157 158 if (ret.code == 127 || ret.code == 8 || ret.code == 9 || ret.code == 13) { 159 } 160 // For ctrl+char 161 else if (ret.code < 26) { 162 ret.ctrl = true; 163 ret.code = Key.A + ret.code - 1; 164 //Curses.move(1,0); 165 //Console.put(" "); 166 //Curses.move(1,0); 167 //Console.put("alt: ", ret.alt, " ctrl: ", ret.ctrl, " shift: ", ret.shift, " code: ", ret.code); 168 return ret; 169 } 170 // For F5-F16: 171 else if (ret.code >= 281 && ret.code <= 292) { 172 ret.code -= 12; 173 ret.shift = true; 174 } 175 else if (ret.code >= 293 && ret.code <= 304) { 176 ret.code -= 24; 177 ret.ctrl = true; 178 } 179 else if (ret.code >= 305 && ret.code <= 316) { 180 ret.code -= 36; 181 ret.ctrl = true; 182 ret.shift = true; 183 } 184 else if (ret.code >= 317 && ret.code <= 328) { 185 ret.code -= 48; 186 ret.alt = true; 187 } 188 189 consoleTranslateKey(ret); 190 //Curses.move(1,0); 191 //Console.put(" "); 192 //Curses.move(1,0); 193 //Console.put("alt: ", ret.alt, " ctrl: ", ret.ctrl, " shift: ", ret.shift, " code: ", ret.code); 194 195 return ret; 196 } 197 198 // Escape sequence... 199 ret.code = Curses.getch(); 200 //Console.put(ret.code, " "); 201 202 // Get extended commands 203 if (ret.code == 0x1B) { 204 // ESCAPE ESCAPE -> Escape 205 ret.code = Key.Escape; 206 } 207 else if (ret.code == '[') { 208 ret.code = Curses.getch(); 209 //Console.put(ret.code, " "); 210 if (ret.code == '1') { 211 ret.code = Curses.getch(); 212 //Console.put(ret.code, " "); 213 if (ret.code == '~') { 214 ret.code = Key.Home; 215 } 216 else if (ret.code == '#') { 217 ret.code = Curses.getch(); 218 //Console.put(ret.code, " "); 219 if (ret.code == '~') { 220 ret.code = Key.F5; 221 } 222 else if (ret.code == ';') { 223 ret.code = Curses.getch(); 224 //Console.put(ret.code, " "); 225 if (ret.code == '2') { 226 ret.shift = true; 227 ret.code = Key.F5; 228 } 229 } 230 } 231 else if (ret.code == '7') { 232 ret.code = Curses.getch(); 233 //Console.put(ret.code, " "); 234 if (ret.code == '~') { 235 ret.code = Key.F6; 236 } 237 else if (ret.code == ';') { 238 ret.code = Curses.getch(); 239 //Console.put(ret.code, " "); 240 if (ret.code == '2') { 241 ret.shift = true; 242 ret.code = Key.F6; 243 } 244 } 245 } 246 else if (ret.code == ';') { 247 // Arrow Keys 248 ret.code = Curses.getch(); 249 //Console.put(ret.code, " "); 250 getModifiers(ret); 251 //Console.put(ret.code, " "); 252 253 if (ret.code == 'A') { 254 ret.code = Key.Up; 255 } 256 else if (ret.code == 'B') { 257 ret.code = Key.Down; 258 } 259 else if (ret.code == 'C') { 260 ret.code = Key.Right; 261 } 262 else if (ret.code == 'D') { 263 ret.code = Key.Left; 264 } 265 } 266 else { 267 ret.code = Curses.getch(); 268 //Console.put(ret.code, " "); 269 } 270 } 271 else if (ret.code == '2') { 272 ret.code = Curses.getch(); 273 //Console.put(ret.code, " "); 274 if (ret.code == '~') { 275 } 276 else if (ret.code == ';') { 277 // Alt + Insert 278 ret.code = Curses.getch(); 279 //Console.put(ret.code, " "); 280 getModifiers(ret); 281 //Console.put(ret.code, " "); 282 if (ret.code == '~') { 283 ret.code = Key.Insert; 284 } 285 } 286 } 287 else if (ret.code == '3') { 288 ret.code = Curses.getch(); 289 //Console.put(ret.code, " "); 290 } 291 else if (ret.code == '4') { 292 ret.code = Curses.getch(); 293 //Console.put(ret.code, " "); 294 } 295 else if (ret.code == '5') { 296 ret.code = Curses.getch(); 297 //Console.put(ret.code, " "); 298 } 299 else if (ret.code == '6') { 300 ret.code = Curses.getch(); 301 //Console.put(ret.code, " "); 302 } 303 else { 304 } 305 } 306 else if (ret.code == 'O') { 307 ret.code = Curses.getch(); 308 309 // F1, F2, F3, F4 310 if (ret.code == '1') { 311 ret.code = Curses.getch(); 312 //Console.put(ret.code, " "); 313 if (ret.code == ';') { 314 ret.code = Curses.getch(); 315 //Console.put(ret.code, " "); 316 getModifiers(ret); 317 //Console.put(ret.code, " "); 318 319 if (ret.code == 'P') { 320 ret.code = Key.F1; 321 } 322 else if (ret.code == 'Q') { 323 ret.code = Key.F2; 324 } 325 else if (ret.code == 'R') { 326 ret.code = Key.F3; 327 } 328 else if (ret.code == 'S') { 329 ret.code = Key.F4; 330 } 331 } 332 } 333 else if (ret.code == 'H') { 334 ret.code = Key.Home; 335 } 336 else if (ret.code == 'F') { 337 ret.code = Key.End; 338 } 339 else if (ret.code == 0x80) { 340 ret.code = Key.F1; 341 } 342 else if (ret.code == 0x81) { 343 ret.code = Key.F2; 344 } 345 else if (ret.code == 0x82) { 346 ret.code = Key.F3; 347 } 348 else if (ret.code == 0x83) { 349 ret.code = Key.F4; 350 } 351 } 352 else { 353 // Alt + Char 354 ret.alt = true; 355 consoleTranslateKey(ret); 356 } 357 358 //Curses.move(1,0); 359 //Console.put(" "); 360 //Curses.move(1,0); 361 //Console.put("alt: ", ret.alt, " ctrl: ", ret.ctrl, " shift: ", ret.shift, " code: ", ret.code); 362 363 return ret; 364} 365 366void getModifiers(ref Key key) { 367 if (key.code == '2' || key.code == '4' || key.code == '6' || key.code == '8') { 368 key.shift = true; 369 } 370 371 if (key.code == '3' || key.code == '4' || key.code == '7' || key.code == '8') { 372 key.alt = true; 373 } 374 375 if (key.code == '5' || key.code == '6' || key.code == '7' || key.code == '8') { 376 key.ctrl = true; 377 } 378 379 if (key.shift || key.alt || key.ctrl) { 380 key.code = Curses.getch(); 381 } 382} 383 384void consoleTranslateKey(ref Key ky) 385{ 386 switch(ky.code) { 387 case '~': 388 case '!': 389 case '@': 390 case '#': 391 case '$': 392 case '%': 393 case '^': 394 case '&': 395 case '*': 396 case '(': 397 case ')': 398 case '_': 399 case '+': 400 case '{': 401 case '}': 402 case ':': 403 case '"': 404 case '<': 405 case '>': 406 case '|': 407 case '?': 408 ky.shift = true; 409 break; 410 default: 411 if (ky.code >= 'A' && ky.code <= 'Z') { 412 ky.shift = true; 413 } 414 else if (ky.code >= '0' && ky.code <= '9') { 415 ky.shift = false; 416 } 417 break; 418 } 419 ky.code = keyTranslation[ky.code]; 420} 421 422uint keyTranslation[Curses.KEY_MAX] = [ 423 ' ': Key.Space, 424 '\n': Key.Return, 425 '\r': Key.Return, 426 '\t': Key.Tab, 427 '\b': Key.Backspace, 428 127: Key.Backspace, 429 'a': Key.A, 430 'b': Key.B, 431 'c': Key.C, 432 'd': Key.D, 433 'e': Key.E, 434 'f': Key.F, 435 'g': Key.G, 436 'h': Key.H, 437 'i': Key.I, 438 'j': Key.J, 439 'k': Key.K, 440 'l': Key.L, 441 'm': Key.M, 442 'n': Key.N, 443 'o': Key.O, 444 'p': Key.P, 445 'q': Key.Q, 446 'r': Key.R, 447 's': Key.S, 448 't': Key.T, 449 'u': Key.U, 450 'v': Key.V, 451 'w': Key.W, 452 'x': Key.X, 453 'y': Key.Y, 454 'z': Key.Z, 455 'A': Key.A, 456 'B': Key.B, 457 'C': Key.C, 458 'D': Key.D, 459 'E': Key.E, 460 'F': Key.F, 461 'G': Key.G, 462 'H': Key.H, 463 'I': Key.I, 464 'J': Key.J, 465 'K': Key.K, 466 'L': Key.L, 467 'M': Key.M, 468 'N': Key.N, 469 'O': Key.O, 470 'P': Key.P, 471 'Q': Key.Q, 472 'R': Key.R, 473 'S': Key.S, 474 'T': Key.T, 475 'U': Key.U, 476 'V': Key.V, 477 'W': Key.W, 478 'X': Key.X, 479 'Y': Key.Y, 480 'Z': Key.Z, 481 '0': Key.Zero, 482 '1': Key.One, 483 '2': Key.Two, 484 '3': Key.Three, 485 '4': Key.Four, 486 '5': Key.Five, 487 '6': Key.Six, 488 '7': Key.Seven, 489 '8': Key.Eight, 490 '9': Key.Nine, 491 '`': Key.SingleQuote, 492 '~': Key.SingleQuote, 493 '!': Key.One, 494 '@': Key.Two, 495 '#': Key.Three, 496 '$': Key.Four, 497 '%': Key.Five, 498 '^': Key.Six, 499 '&': Key.Seven, 500 '*': Key.Eight, 501 '(': Key.Nine, 502 ')': Key.Zero, 503 '-': Key.Minus, 504 '_': Key.Minus, 505 '=': Key.Equals, 506 '+': Key.Equals, 507 '[': Key.LeftBracket, 508 '{': Key.LeftBracket, 509 ']': Key.RightBracket, 510 '}': Key.RightBracket, 511 ';': Key.Semicolon, 512 ':': Key.Semicolon, 513 '\'': Key.Quote, 514 '"': Key.Quote, 515 ',': Key.Comma, 516 '<': Key.Comma, 517 '>': Key.Period, 518 '.': Key.Period, 519 '/': Key.Foreslash, 520 '?': Key.Foreslash, 521 '\\': Key.Backslash, 522 '|': Key.Backslash, 523 Curses.KEY_DOWN: Key.Down, 524 Curses.KEY_UP: Key.Up, 525 Curses.KEY_LEFT: Key.Left, 526 Curses.KEY_RIGHT: Key.Right, 527 Curses.KEY_HOME: Key.Home, 528 Curses.KEY_BACKSPACE: Key.Backspace, 529 Curses.KEY_DC: Key.Delete, 530 Curses.KEY_F1: Key.F1, 531 Curses.KEY_F2: Key.F2, 532 Curses.KEY_F3: Key.F3, 533 Curses.KEY_F4: Key.F4, 534 Curses.KEY_F5: Key.F5, 535 Curses.KEY_F6: Key.F6, 536 Curses.KEY_F7: Key.F7, 537 Curses.KEY_F8: Key.F8, 538 Curses.KEY_F9: Key.F9, 539 Curses.KEY_F10: Key.F10, 540 Curses.KEY_F11: Key.F11, 541 Curses.KEY_F12: Key.F12, 542 Curses.KEY_F13: Key.F13, 543 Curses.KEY_F14: Key.F14, 544 Curses.KEY_F15: Key.F15, 545 Curses.KEY_F16: Key.F16, 546 Curses.KEY_NPAGE: Key.PageDown, 547 Curses.KEY_PPAGE: Key.PageUp, 548 Curses.KEY_ENTER: Key.Return, 549 Curses.KEY_END: Key.End 550]; 551 552 553struct winsize { 554 ushort ws_row; 555 ushort ws_col; 556 ushort ws_xpixel; 557 ushort ws_ypixel; 558} 559 560//window size 561 562int m_width; 563int m_height; 564 565bool m_winsize_state; 566 567 568 569 570const auto TIOCGWINSZ = 0x5413; 571const auto SIGWINCH = 28; 572 573//position tracking 574 575int m_x; 576int m_y; 577 578winsize m_winsize_saved; 579winsize m_winsize_working; 580 581termios m_term_info_saved; 582termios m_term_info_working; 583 584 585 586//signal handler for terminal Size 587 588extern(C) void close_sig_handler(int signal) { 589// Djehuty.end(0); 590// for(int i=0; i<256; i++) { 591// printf("\x1B[48;5;%dma", i); 592// } 593 ConsoleUninit(); 594 exit(0); 595} 596 597extern(C) void size_sig_handler(int signal) { 598 599 ioctl(STDIN, TIOCGWINSZ, &m_winsize_working); 600 601 if (m_width != m_winsize_working.ws_col || m_height != m_winsize_working.ws_row) { 602 m_width = m_winsize_working.ws_col; 603 m_height = m_winsize_working.ws_row; 604 605 while (m_x >= m_width) 606 { 607 m_y++; 608 m_x -= m_width; 609 } 610 611 if (m_y >= m_height) { m_y = m_height-1; } 612 if (m_x < 0) { m_x = 0; } 613 if (m_y < 0) { m_y = 0; } 614 615 //reset (this will be retained when program exits) 616 m_winsize_saved = m_winsize_working; 617 618 //the window resized through the users actions, not through the class' resize() 619 //therefore don't change it back to anything on exit 620 m_winsize_state = false; 621 } 622 623 //fire Size event 624 CuiApplication app = cast(CuiApplication)Djehuty.app; 625 app.window.onResize(); 626} 627 628void ConsoleInit() { 629 printf("\x1B7"); 630 setlocale(LC_ALL, ""); 631 setlocale(LC_CTYPE, ""); 632 633// setvbuf (stdout, null, _IONBF, 0); 634 635 setlocale(LC_ALL, ""); 636 setlocale(LC_CTYPE, ""); 637} 638 639void ConsoleUninit() { 640 printf("\x1B[0m"); 641} 642 643void ConsoleClear() { 644 if (ApplicationController.instance.usingCurses) { 645 Curses.clear(); 646 Curses.refresh(); 647 } 648 else { 649 printf("\x1B[2J\x1B[0;0H"); 650 } 651} 652 653void ConsoleSetRelative(int x, int y) { 654 int newx; 655 int newy; 656 657 Curses.getyx(Curses.stdscr, m_y, m_x); 658 659 newx = m_x + x; 660 newy = m_y + y; 661 662 Curses.move(newy, newx); 663} 664 665void ConsoleGetPosition(uint* x, uint* y) { 666 Curses.getyx(Curses.stdscr, m_y, m_x); 667 *x = m_x; 668 *y = m_y; 669} 670 671void ConsoleSetPosition(uint x, uint y) { 672 if (x >= m_width) { x = m_width-1; } 673 674 if (y >= m_height) { y = m_height-1; } 675 676 if (x < 0) { x = 0; } 677 678 if (y < 0) { y = 0; } 679 680 Curses.move(y,x); 681} 682 683void ConsoleHideCaret() { 684 printf("\x1B[?25l"); 685 Curses.curs_set(0); 686} 687 688void ConsoleShowCaret() { 689 printf("\x1B[?25h"); 690 Curses.curs_set(1); 691} 692 693void ConsoleSetHome() { 694 if (ApplicationController.instance.usingCurses) { 695 Curses.getyx(Curses.stdscr, m_y, m_x); 696 m_x = 0; 697 Curses.move(m_y, m_x); 698 } 699 else { 700 printf("\x1B[0G"); 701 } 702} 703 704void ConsolePutString(char[] chrs) { 705 chrs ~= '\0'; 706 Curses.getyx(Curses.stdscr, m_y, m_x); 707 char[] utf8 = chrs; 708 bool goBackOneLine = false; 709 if (ApplicationController.instance.usingCurses) { 710 for (uint i; i < utf8.length; i++) { 711 if (utf8[i] == '\r' || utf8[i] == '\n' || utf8[i] == '\0') { 712 if (i + m_x >= m_width) { 713 i = m_width - m_x; 714 goBackOneLine = true; 715 } 716 utf8[i] = '\0'; 717 Curses.wprintw(Curses.stdscr, "%s", &utf8[0]); 718 if (goBackOneLine) { 719 ConsoleSetPosition(m_width - 1, m_y); 720 } 721 Curses.refresh(); 722 return; 723 } 724 } 725 } 726 else { 727 printf("%s", utf8.ptr); 728 } 729} 730 731void ConsolePutChar(dchar chr) { 732 dchar[] chrarray = [ chr, '\0' ]; 733 char[] chrs = Unicode.toUtf8(chrarray); 734 735 if (ApplicationController.instance.usingCurses) { 736 if (chr == '\r' || chr == '\n') { 737 ConsoleSetRelative(0, 1); 738 ConsoleSetHome(); 739 } 740 else { 741 Curses.wprintw(Curses.stdscr, "%s", chrs.ptr); 742 Curses.refresh(); 743 } 744 } 745 else { 746 printf("%s", chrs.ptr); 747 } 748} 749 750void ConsoleGetSize(out uint width, out uint height) { 751 Curses.getmaxyx(Curses.stdscr, m_height, m_width); 752 753 width = m_width; 754 height = m_height; 755 756} 757 758void ConsoleGetChar(out dchar chr, out uint code) { 759}