|
|
1.1 ! root 1: // ! 2: // nono ! 3: // Copyright (C) 2024 nono project ! 4: // Licensed under nono-license.txt ! 5: // ! 6: ! 7: // ! 8: // メイン画面をコンソールにする ! 9: // ! 10: ! 11: #include "console.h" ! 12: #include "bitmap.h" ! 13: #include "builtinrom.h" ! 14: #include "comdriver_cons.h" ! 15: #include "mainapp.h" ! 16: #include "scheduler.h" ! 17: #include "wxcolor.h" ! 18: #include <algorithm> ! 19: ! 20: // エスケープシーケンス等。 ! 21: #define ESC '\x1b' ! 22: #define CSI '[' ! 23: ! 24: #define PRINTLOG(fmt...) do { \ ! 25: if (log) { \ ! 26: fprintf(log, fmt); \ ! 27: fflush(log); \ ! 28: } \ ! 29: } while (0) ! 30: ! 31: // v[] がいずれかでも true なら true を返す。 ! 32: static bool ! 33: AnyOf(const std::vector<bool>& v) ! 34: { ! 35: return std::any_of(v.begin(), v.end(), [](bool x) { return x; }); ! 36: } ! 37: ! 38: // コンストラクタ ! 39: ConsoleDevice::ConsoleDevice() ! 40: : inherited(OBJ_CONSOLE) ! 41: { ! 42: screen.resize(width * height); ! 43: dirty.resize(height); ! 44: pending.resize(height); ! 45: } ! 46: ! 47: // デストラクタ ! 48: ConsoleDevice::~ConsoleDevice() ! 49: { ! 50: if (log) { ! 51: fclose(log); ! 52: log = NULL; ! 53: } ! 54: } ! 55: ! 56: // 初期化 ! 57: bool ! 58: ConsoleDevice::Init() ! 59: { ! 60: if (inherited::Init() == false) { ! 61: return false; ! 62: } ! 63: ! 64: renderer = GetRenderer(); ! 65: ! 66: // ログファイル。 ! 67: if (gMainApp.console_logfile) { ! 68: log = fopen(gMainApp.console_logfile, "w"); ! 69: if (log == NULL) { ! 70: warn("console logfile '%s'", gMainApp.console_logfile); ! 71: return false; ! 72: } ! 73: } ! 74: ! 75: vsync_event.func = ToEventCallback(&ConsoleDevice::VSyncCallback); ! 76: vsync_event.time = 16666.667_usec; ! 77: vsync_event.SetName("Console V-Sync"); ! 78: scheduler->RegistEvent(vsync_event); ! 79: ! 80: return true; ! 81: } ! 82: ! 83: // COM ドライバを接続。 ! 84: // NULL を指定すると解除。 ! 85: // (HostCOM の cons ドライバが呼ぶ) ! 86: void ! 87: ConsoleDevice::Attach(COMDriver *comdriver_) ! 88: { ! 89: comdriver = comdriver_; ! 90: } ! 91: ! 92: void ! 93: ConsoleDevice::ResetHard(bool poweron) ! 94: { ! 95: if (poweron) { ! 96: // 最初に一回フチを含めて描画する。 ! 97: init_screen = true; ! 98: ! 99: // シリアルコンソールなのでリセットには反応しない。 ! 100: Clear(); ! 101: Locate(0, 0); ! 102: } ! 103: ! 104: if (comdriver) { ! 105: scheduler->RestartEvent(vsync_event); ! 106: } ! 107: ! 108: ResetTerminal(); ! 109: } ! 110: ! 111: void ! 112: ConsoleDevice::VSyncCallback(Event& ev) ! 113: { ! 114: bool update_needed; ! 115: ! 116: // 更新 (dirty[]) があれば pending[] に重ねる。 ! 117: { ! 118: std::unique_lock<std::mutex> lock(mtx); ! 119: for (int y = 0; y < height; y++) { ! 120: // std::vector<bool> には operator|=() がない。 ! 121: if (__predict_false(dirty[y])) { ! 122: pending[y] = true; ! 123: dirty[y] = false; ! 124: } ! 125: } ! 126: ! 127: update_needed = AnyOf(pending); ! 128: } ! 129: ! 130: // 更新の必要があれば Render に作画指示。 ! 131: if (update_needed) { ! 132: renderer->NotifyRender(); ! 133: } ! 134: ! 135: scheduler->StartEvent(ev); ! 136: } ! 137: ! 138: // 端末の状態をリセットする。 ! 139: // (画面のクリアはここではしない?) ! 140: void ! 141: ConsoleDevice::ResetTerminal() ! 142: { ! 143: ResetState(); ! 144: SetAttr(0); ! 145: scroll_top = 0; ! 146: scroll_btm = height - 1; ! 147: save_curx = 0; ! 148: save_cury = 0; ! 149: } ! 150: ! 151: // エスケープシーケンスの状態を初期化する。 ! 152: void ! 153: ConsoleDevice::ResetState() ! 154: { ! 155: state = 0; ! 156: seq.clear(); ! 157: arg.clear(); ! 158: } ! 159: ! 160: // ch をログ出力する。 ! 161: void ! 162: ConsoleDevice::PutcharDebug(uint32 ch) ! 163: { ! 164: if (ch == '\n') { ! 165: // 改行が来たらログも改行する。 ! 166: fputs("\\n\n", log); ! 167: log_newline = true; ! 168: } else { ! 169: if (ch == ESC) { ! 170: // 読みやすさのため ESC の前で改行したい。 ! 171: if (log_newline == false) { ! 172: fputc('\n', log); ! 173: } ! 174: fputs("ESC", log); ! 175: } else if (ch == '\r') { ! 176: fputs("\\r", log); ! 177: } else if (ch == '\t') { ! 178: fputs("\\t", log); ! 179: } else if (ch < 0x20 || ch >= 0x7f) { ! 180: fprintf(log, "\\x%02x", ch); ! 181: } else { ! 182: fputc(ch, log); ! 183: } ! 184: log_newline = false; ! 185: } ! 186: fflush(log); ! 187: } ! 188: ! 189: // シリアルコンソールから来る出力。 ! 190: // ホストドライバスレッドから呼ばれる。 ! 191: void ! 192: ConsoleDevice::Putchar(uint32 ch) ! 193: { ! 194: if (__predict_false(log)) { ! 195: PutcharDebug(ch); ! 196: } ! 197: ! 198: switch (state) { ! 199: case ESC: ! 200: if (PutcharESC(ch)) { ! 201: return; ! 202: } ! 203: break; ! 204: ! 205: case CSI: ! 206: if (PutcharCSI(ch)) { ! 207: return; ! 208: } ! 209: break; ! 210: ! 211: case '(': // G0 ! 212: switch (ch) { ! 213: case '0': // DEC 特殊記号 ! 214: SetAttr(cur_attr | ATTR_DECSG); ! 215: break; ! 216: case 'B': // ASCII ! 217: default: ! 218: SetAttr(cur_attr & ~ATTR_DECSG); ! 219: break; ! 220: } ! 221: ResetState(); ! 222: return; ! 223: ! 224: case ')': // G1 ! 225: case '$': // 94 multibyte ! 226: case '#': ! 227: // とりあえず何もしない。 ! 228: ResetState(); ! 229: return; ! 230: ! 231: case '?': // CSI '?' ! 232: if (PutcharCSIQ(ch)) { ! 233: return; ! 234: } ! 235: break; ! 236: ! 237: default: ! 238: PutcharPlain(ch); ! 239: return; ! 240: } ! 241: ! 242: // シーケンスを破棄。ここまでのを表示して平文に戻る。 ! 243: putlog(0, "Unknown Sequence:%s", Dump(ch).c_str()); ! 244: for (auto c : seq) { ! 245: Putc(c); ! 246: } ! 247: Putc(ch); ! 248: ResetState(); ! 249: } ! 250: ! 251: // 何のシーケンスでもない1文字を表示する。 ! 252: void ! 253: ConsoleDevice::PutcharPlain(uint32 ch) ! 254: { ! 255: switch (ch) { ! 256: case '\x00': // NUL ! 257: case '\x01': // SOH ! 258: case '\x02': // STX ! 259: case '\x03': // ETX ! 260: case '\x04': // EOT ! 261: case '\x05': // ENQ ! 262: case '\x06': // ACK ! 263: break; ! 264: case '\x07': // BEL ! 265: // とりあえず無視。 ! 266: break; ! 267: ! 268: case '\x08': // BS ! 269: LocateX(cur_x - 1); ! 270: return; ! 271: ! 272: case '\t': // TAB ! 273: // XXX 右端どうなる? ! 274: LocateX(roundup(cur_x, 8)); ! 275: return; ! 276: ! 277: case '\n': // LF ! 278: case '\x0b': // VT ! 279: case '\x0c': // FF ! 280: LF(); ! 281: return; ! 282: ! 283: case '\r': // CR ! 284: CR(); ! 285: return; ! 286: ! 287: case '\x0e': // SO ! 288: case '\x0f': // SI ! 289: // Change GL ! 290: break; ! 291: ! 292: case '\x10': // DLE ! 293: case '\x11': // DC1/XON ! 294: case '\x12': // DC2 ! 295: case '\x13': // DC3/XOFF ! 296: case '\x14': // DC4 ! 297: case '\x15': // NAK ! 298: case '\x16': // SYN ! 299: case '\x17': // ETB ! 300: break; ! 301: ! 302: case '\x18': // CAN ! 303: // Cancel ! 304: ResetState(); ! 305: return; ! 306: ! 307: case '\x19': // EM ! 308: break; ! 309: ! 310: case '\x1a': // SUB ! 311: ResetState(); ! 312: return; ! 313: ! 314: case ESC: ! 315: seq.push_back(ch); ! 316: state = ch; ! 317: return; ! 318: ! 319: case '\x1c': // FS ! 320: case '\x1d': // GS ! 321: case '\x1e': // RS ! 322: case '\x1f': // US ! 323: break; ! 324: ! 325: default: ! 326: if (ch < 0x80) { ! 327: // ここが通常の1文字出力。 ! 328: Putc(ch); ! 329: return; ! 330: } ! 331: // 8ビットシーケンスは無視? ! 332: break; ! 333: } ! 334: putlog(2, "Ignore: \\x%02x", ch); ! 335: } ! 336: ! 337: // ESC の次の文字を処理する。 ! 338: // 処理すれば true、知らないシーケンスなので中止する場合は false を返す。 ! 339: bool ! 340: ConsoleDevice::PutcharESC(uint32 ch) ! 341: { ! 342: switch (ch) { ! 343: case '[': // CSI ! 344: case '(': // G0 ! 345: case ')': // G1 ! 346: case '$': // 94 multibyte ! 347: case '#': // Font width/height control ! 348: seq.push_back(ch); ! 349: state = ch; ! 350: return true; ! 351: ! 352: case '\\': // ST ! 353: break; ! 354: ! 355: case '7': // カーソル位置保存 ! 356: save_curx = cur_x; ! 357: save_cury = cur_y; ! 358: break; ! 359: ! 360: case '8': // カーソル位置復帰 ! 361: Locate(save_curx, save_cury); ! 362: break; ! 363: ! 364: case 'c': // Hard terminal reset ! 365: Clear(); ! 366: ResetTerminal(); ! 367: Locate(0, 0); ! 368: break; ! 369: ! 370: case 'M': // 1行上に移動 (画面を1行下にスクロール) ! 371: ScrollDown(); ! 372: Locate(0, 0); ! 373: break; ! 374: ! 375: case '*': // G2 ! 376: case '+': // G3 ! 377: case 'B': // ASCII ! 378: case 'A': // ISO Latin 1 ! 379: case '<': // ! 380: case '0': // DEC special graphics ! 381: case '-': // G1 (96) ! 382: case '.': // G2 ! 383: case '/': // G3 ! 384: case '4': // Dutch ! 385: case '5': ! 386: case '6': // Norwegian/Danish ! 387: case 'C': // Finnish ! 388: case 'R': // French ! 389: case 'Q': // French canadian ! 390: case 'K': // German ! 391: case 'Y': // Italian ! 392: // "%5" とかの2文字のやつは NetBSD/x68k ite も未対応。 ! 393: case '`': ! 394: case 'n': ! 395: case '}': ! 396: case 'o': ! 397: case '|': ! 398: case '~': ! 399: case '=': ! 400: case '>': ! 401: putlog(2, "Ignore:%s", Dump(ch).c_str()); ! 402: break; ! 403: ! 404: case 'Z': // 端末属性を応答する。 ! 405: default: ! 406: return false; ! 407: } ! 408: ! 409: ResetState(); ! 410: return true; ! 411: } ! 412: ! 413: // CSI の次の文字を処理する。 ! 414: // 処理すれば true、知らないシーケンスなので中止する場合は false を返す。 ! 415: bool ! 416: ConsoleDevice::PutcharCSI(uint32 ch) ! 417: { ! 418: switch (ch) { ! 419: case '0': ! 420: case '1': ! 421: case '2': ! 422: case '3': ! 423: case '4': ! 424: case '5': ! 425: case '6': ! 426: case '7': ! 427: case '8': ! 428: case '9': ! 429: case ';': ! 430: case '\"': ! 431: case '$': ! 432: case '>': ! 433: seq.push_back(ch); ! 434: arg.push_back(ch); ! 435: return true; ! 436: ! 437: case 'A': ! 438: { ! 439: int n = atoi(arg.c_str()); ! 440: if (n == 0) { ! 441: n = 1; ! 442: } ! 443: LocateY(cur_y - n); ! 444: ResetState(); ! 445: return true; ! 446: } ! 447: ! 448: case 'B': ! 449: { ! 450: int n = atoi(arg.c_str()); ! 451: if (n == 0) { ! 452: n = 1; ! 453: } ! 454: LocateY(cur_y + n); ! 455: ResetState(); ! 456: return true; ! 457: } ! 458: ! 459: case 'C': ! 460: { ! 461: int n = atoi(arg.c_str()); ! 462: if (n == 0) { ! 463: n = 1; ! 464: } ! 465: LocateX(cur_x + n); ! 466: ResetState(); ! 467: return true; ! 468: } ! 469: ! 470: case 'D': ! 471: { ! 472: int n = atoi(arg.c_str()); ! 473: if (n == 0) { ! 474: n = 1; ! 475: } ! 476: LocateX(cur_x - n); ! 477: ResetState(); ! 478: return true; ! 479: } ! 480: ! 481: case 'H': ! 482: { ! 483: // CSI <y> ; <x> H … (x-1, y-1) にカーソルを移動。 ! 484: int y = atoi(arg.c_str()); ! 485: int x = 0; ! 486: const char *p = strchr(arg.c_str(), ';'); ! 487: if (p) { ! 488: x = atoi(p + 1); ! 489: } ! 490: if (x > 0) { ! 491: x--; ! 492: } ! 493: if (y > 0) { ! 494: y--; ! 495: } ! 496: Locate(x, y); ! 497: ResetState(); ! 498: return true; ! 499: } ! 500: ! 501: case 'J': ! 502: { ! 503: // CSI [<n>] J ! 504: // <n> が 0 か省略なら、カーソル位置から右下までを消去。 ! 505: // <n> が 1 なら、カーソル位置から左上までを消去。 ! 506: // <n> が 2 なら、全画面消去。 ! 507: // XXX SPA と ERM は未対応。 ! 508: int n = atoi(arg.c_str()); ! 509: switch (n) { ! 510: default: // 知らんけど ! 511: case 0: ! 512: for (int y = cur_y; y < height - 1; y++) { ! 513: int x = (y == cur_y) ? cur_x : 0; ! 514: for (; x < width; x++) { ! 515: Setc(x, y, ' ' | cur_attr); ! 516: } ! 517: } ! 518: break; ! 519: case 1: ! 520: for (int y = 0; y <= cur_y; y++) { ! 521: int xend = (y == cur_y) ? cur_x : (width - 1); ! 522: for (int x = 0; x <= xend; x++) { ! 523: Setc(x, y, ' ' | cur_attr); ! 524: } ! 525: } ! 526: break; ! 527: case 2: ! 528: // カーソル位置は移動しないまま? ! 529: Clear(); ! 530: break; ! 531: } ! 532: ResetState(); ! 533: return true; ! 534: } ! 535: ! 536: case 'K': ! 537: { ! 538: // CSI [<n>] K ! 539: // <n> が 0 か省略なら、カーソル位置の右側を消去。 ! 540: // <n> が 1 なら、カーソル位置の左側を消去。 ! 541: // <n> が 2 なら、カーソル行を消去。 ! 542: int n = atoi(arg.c_str()); ! 543: int x0; ! 544: int x1; ! 545: switch (n) { ! 546: default: // 知らんけど ! 547: case 0: ! 548: x0 = cur_x; ! 549: x1 = width - 1; ! 550: break; ! 551: case 1: ! 552: x0 = 0; ! 553: x1 = cur_x; ! 554: break; ! 555: case 2: ! 556: x0 = 0; ! 557: x1 = width - 1; ! 558: break; ! 559: } ! 560: for (int x = x0; x <= x1; x++) { ! 561: Setc(x, cur_y, ' ' | cur_attr); ! 562: } ! 563: ResetState(); ! 564: return true; ! 565: } ! 566: ! 567: case 'm': ! 568: { ! 569: // CSI 7 ; 37 m みたいにセミコロンで区切ってパラメータを複数書ける。 ! 570: // パラメータには 38:2:rr:gg:bb みたいにコロンで区切ったひとかたまり ! 571: // もある。 ! 572: const char *ap = arg.c_str(); ! 573: for (;;) { ! 574: int n = atoi(ap); ! 575: switch (n) { ! 576: case 0: // すべての属性を解除 ! 577: SetAttr(0); ! 578: break; ! 579: case 1: // ボールドをセット ! 580: SetAttr(cur_attr | ATTR_BOLD); ! 581: break; ! 582: case 2: // 低輝度をセット (対応予定なし) ! 583: break; ! 584: case 3: // イタリックをセット ! 585: SetAttr(cur_attr | ATTR_ITALIC); ! 586: break; ! 587: case 4: // 下線をセット ! 588: SetAttr(cur_attr | ATTR_UNDERLINE); ! 589: break; ! 590: case 5: // 低速点滅をセット (対応予定なし) ! 591: break; ! 592: case 6: // 高速点滅をセット (対応予定なし) ! 593: break; ! 594: case 7: // 反転をセット ! 595: SetAttr(cur_attr | ATTR_REVERSE); ! 596: break; ! 597: case 8: // 文字色を背景色と同じにする ! 598: break; ! 599: case 9: // 打ち消し線をセット ! 600: SetAttr(cur_attr | ATTR_STRIKE); ! 601: break; ! 602: case 10 ... 20: // フォントセットの設定? ! 603: break; ! 604: case 21: // 二重下線をセット (対応予定なし) ! 605: break; ! 606: case 22: // ボールド(と低輝度)を解除 ! 607: SetAttr(cur_attr & ~ATTR_BOLD); ! 608: break; ! 609: case 23: // イタリックを解除 ! 610: SetAttr(cur_attr & ~ATTR_ITALIC); ! 611: break; ! 612: case 24: // 下線を解除 ! 613: SetAttr(cur_attr & ~ATTR_UNDERLINE); ! 614: break; ! 615: case 25: // 点滅を解除 ! 616: break; ! 617: case 26: // ? ! 618: break; ! 619: case 27: // 反転を解除 ! 620: SetAttr(cur_attr & ~ATTR_REVERSE); ! 621: break; ! 622: case 28: // シークレットを解除 ! 623: break; ! 624: case 29: // 打ち消し線を解除 ! 625: SetAttr(cur_attr & ~ATTR_STRIKE); ! 626: break; ! 627: case 30 ... 37: // 文字色の設定 ! 628: break; ! 629: case 38: // 文字色(拡張)を設定 ! 630: break; ! 631: case 39: // 文字色をデフォルトに戻す ! 632: break; ! 633: case 40 ... 47: // 背景色を設定 ! 634: break; ! 635: case 48: // 背景色(拡張)を設定 ! 636: break; ! 637: case 49: // 背景色をデフォルトに戻す ! 638: break; ! 639: case 51: // □を重ねる (対応予定なし) ! 640: break; ! 641: case 52: // ○を重ねる (対応予定なし) ! 642: break; ! 643: case 53: // 上線をセット ! 644: SetAttr(cur_attr | ATTR_OVERLINE); ! 645: break; ! 646: case 54: // □○を取り消し ! 647: break; ! 648: case 55: // 上線を取り消し ! 649: SetAttr(cur_attr & ~ATTR_OVERLINE); ! 650: break; ! 651: case 60 ... 63: // 縦線 (対応予定なし) ! 652: break; ! 653: case 64: // 二重打ち消し線 (対応予定なし) ! 654: break; ! 655: case 65: // 60-64 を取り消し ! 656: break; ! 657: case 90 ... 97: // 文字色(高輝度)を設定 ! 658: break; ! 659: case 100 ... 107: // 背景色(高輝度)を設定 ! 660: break; ! 661: default: ! 662: // 無視 ! 663: break; ! 664: } ! 665: ! 666: if (*ap == '\0') { ! 667: break; ! 668: } ! 669: ap = strchr(ap + 1, ';'); ! 670: if (ap == NULL) { ! 671: break; ! 672: } ! 673: ap++; ! 674: } ! 675: ! 676: ResetState(); ! 677: return true; ! 678: } ! 679: ! 680: case 'r': ! 681: { ! 682: // CSI <t> ; <b> r。<t> 省略時は 1。<b> 省略時は画面最下行。 ! 683: int top = atoi(arg.c_str()); ! 684: int btm = height; ! 685: const char *p = strchr(arg.c_str(), ';'); ! 686: if (p) { ! 687: btm = atoi(p + 1); ! 688: } ! 689: if (top > 0) { ! 690: top--; ! 691: } ! 692: if (btm > 0) { ! 693: btm--; ! 694: } ! 695: scroll_top = top; ! 696: scroll_btm = btm; ! 697: ResetState(); ! 698: return true; ! 699: } ! 700: ! 701: case '?': ! 702: seq.push_back(ch); ! 703: state = ch; ! 704: return true; ! 705: ! 706: default: ! 707: return false; ! 708: } ! 709: } ! 710: ! 711: // CSI '?' の次の文字を処理する。 ! 712: // 処理すれば true、知らないシーケンスなので中止する場合は false を返す。 ! 713: bool ! 714: ConsoleDevice::PutcharCSIQ(uint32 ch) ! 715: { ! 716: switch (ch) { ! 717: case '0': ! 718: case '1': ! 719: case '2': ! 720: case '3': ! 721: case '4': ! 722: case '5': ! 723: case '6': ! 724: case '7': ! 725: case '8': ! 726: case '9': ! 727: case ';': ! 728: case '\"': ! 729: case '$': ! 730: case '>': ! 731: seq.push_back(ch); ! 732: arg.push_back(ch); ! 733: return true; ! 734: ! 735: case 'h': // 拡張オプションを設定 ! 736: case 'l': // 拡張オプションを解除 ! 737: // CSI '?' <n> h ! 738: // CSI '?' <n> l ! 739: // 全部未対応。 ! 740: putlog(2, "Ignored:%s", Dump(ch).c_str()); ! 741: ResetState(); ! 742: return true; ! 743: ! 744: default: ! 745: return false; ! 746: } ! 747: } ! 748: ! 749: // 現在の seq を表示用文字列にして返す。 ! 750: std::string ! 751: ConsoleDevice::Dump() const ! 752: { ! 753: return Dump(seq); ! 754: } ! 755: ! 756: // 現在の seq と ch を表示用文字列にして返す。 ! 757: // 大抵の場合最後の1文字はまだ seq に入っていないのでこの形式があると便利。 ! 758: std::string ! 759: ConsoleDevice::Dump(uint32 ch) const ! 760: { ! 761: std::vector<uint8> tmp = seq; ! 762: tmp.push_back(ch); ! 763: return Dump(tmp); ! 764: } ! 765: ! 766: // エスケープシーケンスを表示用文字列にして返す。 ! 767: // 戻り文字列には先頭に空白が入っている。 ! 768: /*static*/ std::string ! 769: ConsoleDevice::Dump(const std::vector<uint8>& src) ! 770: { ! 771: std::string dst; ! 772: ! 773: for (auto ch : src) { ! 774: if (ch == ESC) { ! 775: dst += " ESC"; ! 776: } else if (ch < ' ' || ch >= 0x7f) { ! 777: dst += string_format(" \\x%02x", ch); ! 778: } else if (ch == ' ') { ! 779: dst += " ' '"; ! 780: } else { ! 781: dst += ' '; ! 782: dst += (char)ch; ! 783: } ! 784: } ! 785: return dst; ! 786: } ! 787: ! 788: // 画面を消去する。 ! 789: // カーソル位置は変わらない。属性もクリアされない。 ! 790: void ! 791: ConsoleDevice::Clear() ! 792: { ! 793: std::fill(screen.begin(), screen.end(), ' ' | cur_attr); ! 794: std::fill(dirty.begin(), dirty.end(), true); ! 795: } ! 796: ! 797: // カーソルの X 座標を移動。(画面をはみ出さないようにクリップされる) ! 798: void ! 799: ConsoleDevice::LocateX(int x) ! 800: { ! 801: if (__predict_false(x < 0)) { ! 802: x = 0; ! 803: } ! 804: if (__predict_false(x > width - 1)) { ! 805: x = width - 1; ! 806: } ! 807: cur_x = x; ! 808: dirty[cur_y] = true; ! 809: } ! 810: ! 811: // カーソルの Y 座標を移動。(画面をはみ出さないようにクリップされる) ! 812: void ! 813: ConsoleDevice::LocateY(int y) ! 814: { ! 815: if (__predict_false(y < 0)) { ! 816: y = 0; ! 817: } ! 818: if (__predict_false(y > height - 1)) { ! 819: y = height - 1; ! 820: } ! 821: dirty[cur_y] = true; ! 822: cur_y = y; ! 823: dirty[cur_y] = true; ! 824: } ! 825: ! 826: // 文字出力後のカーソル移動。 ! 827: void ! 828: ConsoleDevice::Ascend() ! 829: { ! 830: cur_x++; ! 831: dirty[cur_y] = true; ! 832: if (cur_x >= width) { ! 833: CRLF(); ! 834: } ! 835: } ! 836: ! 837: // 復帰。 ! 838: void ! 839: ConsoleDevice::CR() ! 840: { ! 841: LocateX(0); ! 842: } ! 843: ! 844: // 改行。(DECSTBM の影響を受ける) ! 845: void ! 846: ConsoleDevice::LF() ! 847: { ! 848: dirty[cur_y] = true; ! 849: cur_y++; ! 850: if (cur_y >= scroll_btm) { ! 851: ScrollUp(); ! 852: cur_y = scroll_btm; ! 853: } ! 854: dirty[cur_y] = true; ! 855: } ! 856: ! 857: // 全画面を1行上にスクロール。(DECSTBM の影響を受ける) ! 858: // カーソル位置は移動しない。 ! 859: void ! 860: ConsoleDevice::ScrollUp() ! 861: { ! 862: int dst = scroll_top * width; ! 863: int lines = scroll_btm - scroll_top; ! 864: memmove(&screen[dst], &screen[dst + width], ! 865: lines * width * sizeof(screen[0])); ! 866: uint32 ch = ' ' | cur_attr; ! 867: for (int x = 0; x < width; x++) { ! 868: Setc(x, scroll_btm, ch); ! 869: } ! 870: std::fill(dirty.begin(), dirty.end(), true); ! 871: } ! 872: ! 873: // 全画面を1行下にスクロール。(DECSTBM の影響を受ける) ! 874: // カーソル位置は移動しない。 ! 875: void ! 876: ConsoleDevice::ScrollDown() ! 877: { ! 878: int src = scroll_top * width; ! 879: int lines = scroll_btm - scroll_top; ! 880: memmove(&screen[src + width], &screen[src], ! 881: lines * width * sizeof(screen[0])); ! 882: uint32 ch = ' ' | cur_attr; ! 883: for (int x = 0; x < width; x++) { ! 884: Setc(x, scroll_top, ch); ! 885: } ! 886: std::fill(dirty.begin(), dirty.end(), true); ! 887: } ! 888: ! 889: // 現在位置に、現在の属性で文字 ch を出力する。 ! 890: // 出力後カーソルは移動する。 ! 891: void ! 892: ConsoleDevice::Putc(uint32 ch) ! 893: { ! 894: if (ch == '\n') { ! 895: CR(); ! 896: LF(); ! 897: return; ! 898: } ! 899: ! 900: Setc(cur_x, cur_y, ch | cur_attr); ! 901: Ascend(); ! 902: } ! 903: ! 904: // (x, y) の位置に chattr を出力する。 ! 905: // 出力後カーソルは移動しない。 ! 906: // 現在の attr は参照しない (呼び出す側が指定すること)。 ! 907: void ! 908: ConsoleDevice::Setc(int x, int y, uint32 chattr) ! 909: { ! 910: int pos = y * width + x; ! 911: assert(pos >= 0); ! 912: assert(pos < width * height); ! 913: screen[pos] = chattr; ! 914: } ! 915: ! 916: // 現在の属性を設定する。 ! 917: // ほぼログ出力のため。 ! 918: void ! 919: ConsoleDevice::SetAttr(uint32 new_attr) ! 920: { ! 921: if (cur_attr == new_attr) { ! 922: return; ! 923: } ! 924: cur_attr = new_attr; ! 925: ! 926: if (log) { ! 927: std::string buf; ! 928: if ((cur_attr & ATTR_BOLD)) { ! 929: buf += ",ATTR_BOLD"; ! 930: } ! 931: if ((cur_attr & ATTR_ITALIC)) { ! 932: buf += ",ATTR_ITALIC"; ! 933: } ! 934: if ((cur_attr & ATTR_UNDERLINE)) { ! 935: buf += ",ATTR_UNDERLINE"; ! 936: } ! 937: if ((cur_attr & ATTR_STRIKE)) { ! 938: buf += ",ATTR_STRIKE"; ! 939: } ! 940: if ((cur_attr & ATTR_OVERLINE)) { ! 941: buf += ",ATTR_OVERLINE"; ! 942: } ! 943: if ((cur_attr & ATTR_DECSG)) { ! 944: buf += ",ATTR_DECSG"; ! 945: } ! 946: if ((cur_attr & ATTR_REVERSE)) { ! 947: buf += ",ATTR_REVERSE"; ! 948: } ! 949: ! 950: if (buf.empty()) { ! 951: buf = "<ATTR_NORMAL>"; ! 952: } else { ! 953: buf[0] = '<'; ! 954: buf += '>'; ! 955: } ! 956: fputs(buf.c_str(), log); ! 957: fflush(log); ! 958: } ! 959: } ! 960: ! 961: // 画面合成。 ! 962: // レンダリングスレッドから呼ばれる。 ! 963: bool ! 964: ConsoleDevice::Render(BitmapRGBX& dst) ! 965: { ! 966: if (comdriver == NULL) { ! 967: return false; ! 968: } ! 969: ! 970: bool updated = false; ! 971: ! 972: // フチも含めて再描画。 ! 973: if (__predict_false(init_screen)) { ! 974: init_screen = false; ! 975: dst.Fill(palette[0]); ! 976: updated = true; ! 977: } ! 978: ! 979: // 行ごとに更新があるか調べる。 ! 980: std::vector<bool> modified(height); ! 981: { ! 982: std::unique_lock<std::mutex> lock(mtx); ! 983: modified = pending; ! 984: std::fill(pending.begin(), pending.end(), false); ! 985: } ! 986: updated |= AnyOf(modified); ! 987: ! 988: const uint8 *cgrom8x16 = Builtin::CGROM8x16(0); ! 989: for (uint y = 0; y < height; y++) { ! 990: if (__predict_false(modified[y])) { ! 991: uint py = Padding + y * font_height; ! 992: const uint32 *s = &screen[y * width]; ! 993: for (uint x = 0; x < width; x++) { ! 994: uint px = Padding + x * font_width; ! 995: uint32 attr = *s++; ! 996: // とりあえず上半分(G1)も無視。 ! 997: uint32 ch = attr & 0x7f; ! 998: // 属性による反転。 ! 999: int paloff = 0; ! 1000: if ((attr & ATTR_REVERSE) != 0) { ! 1001: paloff ^= 1; ! 1002: } ! 1003: // カーソル位置は更に反転。 ! 1004: if (x == cur_x && y == cur_y) { ! 1005: paloff ^= 1; ! 1006: } ! 1007: ! 1008: const uint8 *font; ! 1009: if ((attr & ATTR_DECSG) && ('j' <= ch && ch <= 'x')) { ! 1010: font = &decsg8x16[(ch - 'j') * font_height]; ! 1011: } else { ! 1012: font = &cgrom8x16[ch * font_height]; ! 1013: } ! 1014: BitmapI1 bc1(font, font_width, font_height); ! 1015: if (__predict_false((attr & ATTR_ITALIC))) { ! 1016: bc1 = bc1.ConvertToItalic(); ! 1017: } ! 1018: if (__predict_false((attr & ATTR_BOLD))) { ! 1019: bc1 = bc1.ConvertToBold(); ! 1020: } ! 1021: ! 1022: dst.DrawBitmapI1(px, py, bc1, &palette[paloff]); ! 1023: ! 1024: // 下線、上線、取り消し線 ! 1025: if (__predict_false((attr & ATTR_LINEMASK))) { ! 1026: Color fg = palette[paloff ^ 1]; ! 1027: if ((attr & ATTR_OVERLINE)) { ! 1028: dst.DrawLineH(fg, px, py + 0, px + font_width); ! 1029: } ! 1030: if ((attr & ATTR_STRIKE)) { ! 1031: dst.DrawLineH(fg, px, py + 9, px + font_width); ! 1032: } ! 1033: if ((attr & ATTR_UNDERLINE)) { ! 1034: dst.DrawLineH(fg, px, py + 14, px + font_width); ! 1035: } ! 1036: } ! 1037: } ! 1038: } ! 1039: } ! 1040: ! 1041: return updated; ! 1042: } ! 1043: ! 1044: #define B(x) (0b##x) ! 1045: #define B2(x) (B(x) >> 8), (B(x) & 0xff) ! 1046: ! 1047: // DEC Special Graphics Character Set フォント。(罫線のみ) ! 1048: /*static*/ const uint8 ConsoleDevice::decsg8x16[15 * font_height] = { ! 1049: B(00010000), // [0] j:┘ ! 1050: B(00010000), ! 1051: B(00010000), ! 1052: B(00010000), ! 1053: B(00010000), ! 1054: B(00010000), ! 1055: B(00010000), ! 1056: B(11110000), ! 1057: B(00000000), ! 1058: B(00000000), ! 1059: B(00000000), ! 1060: B(00000000), ! 1061: B(00000000), ! 1062: B(00000000), ! 1063: B(00000000), ! 1064: B(00000000), ! 1065: ! 1066: B(00000000), // [1] k:┐ ! 1067: B(00000000), ! 1068: B(00000000), ! 1069: B(00000000), ! 1070: B(00000000), ! 1071: B(00000000), ! 1072: B(00000000), ! 1073: B(11110000), ! 1074: B(00010000), ! 1075: B(00010000), ! 1076: B(00010000), ! 1077: B(00010000), ! 1078: B(00010000), ! 1079: B(00010000), ! 1080: B(00010000), ! 1081: B(00010000), ! 1082: ! 1083: B(00000000), // [2] l:┌ ! 1084: B(00000000), ! 1085: B(00000000), ! 1086: B(00000000), ! 1087: B(00000000), ! 1088: B(00000000), ! 1089: B(00000000), ! 1090: B(00011111), ! 1091: B(00010000), ! 1092: B(00010000), ! 1093: B(00010000), ! 1094: B(00010000), ! 1095: B(00010000), ! 1096: B(00010000), ! 1097: B(00010000), ! 1098: B(00010000), ! 1099: ! 1100: B(00010000), // [3] m:└ ! 1101: B(00010000), ! 1102: B(00010000), ! 1103: B(00010000), ! 1104: B(00010000), ! 1105: B(00010000), ! 1106: B(00010000), ! 1107: B(00011111), ! 1108: B(00000000), ! 1109: B(00000000), ! 1110: B(00000000), ! 1111: B(00000000), ! 1112: B(00000000), ! 1113: B(00000000), ! 1114: B(00000000), ! 1115: B(00000000), ! 1116: ! 1117: B(00010000), // [4] n:┼ ! 1118: B(00010000), ! 1119: B(00010000), ! 1120: B(00010000), ! 1121: B(00010000), ! 1122: B(00010000), ! 1123: B(00010000), ! 1124: B(11111111), ! 1125: B(00010000), ! 1126: B(00010000), ! 1127: B(00010000), ! 1128: B(00010000), ! 1129: B(00010000), ! 1130: B(00010000), ! 1131: B(00010000), ! 1132: B(00010000), ! 1133: ! 1134: B(00000000), // [5] o:─(Scan1) ! 1135: B(11111111), ! 1136: B(00000000), ! 1137: B(00000000), ! 1138: B(00000000), ! 1139: B(00000000), ! 1140: B(00000000), ! 1141: B(00000000), ! 1142: B(00000000), ! 1143: B(00000000), ! 1144: B(00000000), ! 1145: B(00000000), ! 1146: B(00000000), ! 1147: B(00000000), ! 1148: B(00000000), ! 1149: B(00000000), ! 1150: ! 1151: B(00000000), // [6] p:─(Scan3) ! 1152: B(00000000), ! 1153: B(00000000), ! 1154: B(00000000), ! 1155: B(11111111), ! 1156: B(00000000), ! 1157: B(00000000), ! 1158: B(00000000), ! 1159: B(00000000), ! 1160: B(00000000), ! 1161: B(00000000), ! 1162: B(00000000), ! 1163: B(00000000), ! 1164: B(00000000), ! 1165: B(00000000), ! 1166: B(00000000), ! 1167: ! 1168: B(00000000), // [7] q:─(Scan5) ! 1169: B(00000000), ! 1170: B(00000000), ! 1171: B(00000000), ! 1172: B(00000000), ! 1173: B(00000000), ! 1174: B(00000000), ! 1175: B(11111111), ! 1176: B(00000000), ! 1177: B(00000000), ! 1178: B(00000000), ! 1179: B(00000000), ! 1180: B(00000000), ! 1181: B(00000000), ! 1182: B(00000000), ! 1183: B(00000000), ! 1184: ! 1185: B(00000000), // [8] r:─(Scan7) ! 1186: B(00000000), ! 1187: B(00000000), ! 1188: B(00000000), ! 1189: B(00000000), ! 1190: B(00000000), ! 1191: B(00000000), ! 1192: B(00000000), ! 1193: B(00000000), ! 1194: B(00000000), ! 1195: B(11111111), ! 1196: B(00000000), ! 1197: B(00000000), ! 1198: B(00000000), ! 1199: B(00000000), ! 1200: B(00000000), ! 1201: ! 1202: B(00000000), // [9] s:─(Scan9) ! 1203: B(00000000), ! 1204: B(00000000), ! 1205: B(00000000), ! 1206: B(00000000), ! 1207: B(00000000), ! 1208: B(00000000), ! 1209: B(00000000), ! 1210: B(00000000), ! 1211: B(00000000), ! 1212: B(00000000), ! 1213: B(00000000), ! 1214: B(00000000), ! 1215: B(11111111), ! 1216: B(00000000), ! 1217: B(00000000), ! 1218: ! 1219: B(00010000), // [10] t:├ ! 1220: B(00010000), ! 1221: B(00010000), ! 1222: B(00010000), ! 1223: B(00010000), ! 1224: B(00010000), ! 1225: B(00010000), ! 1226: B(00011111), ! 1227: B(00010000), ! 1228: B(00010000), ! 1229: B(00010000), ! 1230: B(00010000), ! 1231: B(00010000), ! 1232: B(00010000), ! 1233: B(00010000), ! 1234: B(00010000), ! 1235: ! 1236: B(00010000), // [11] u:┤ ! 1237: B(00010000), ! 1238: B(00010000), ! 1239: B(00010000), ! 1240: B(00010000), ! 1241: B(00010000), ! 1242: B(00010000), ! 1243: B(11110000), ! 1244: B(00010000), ! 1245: B(00010000), ! 1246: B(00010000), ! 1247: B(00010000), ! 1248: B(00010000), ! 1249: B(00010000), ! 1250: B(00010000), ! 1251: B(00010000), ! 1252: ! 1253: B(00010000), // [12] v:┴ ! 1254: B(00010000), ! 1255: B(00010000), ! 1256: B(00010000), ! 1257: B(00010000), ! 1258: B(00010000), ! 1259: B(00010000), ! 1260: B(11111111), ! 1261: B(00000000), ! 1262: B(00000000), ! 1263: B(00000000), ! 1264: B(00000000), ! 1265: B(00000000), ! 1266: B(00000000), ! 1267: B(00000000), ! 1268: B(00000000), ! 1269: ! 1270: B(00000000), // [13] w:┬ ! 1271: B(00000000), ! 1272: B(00000000), ! 1273: B(00000000), ! 1274: B(00000000), ! 1275: B(00000000), ! 1276: B(00000000), ! 1277: B(11111111), ! 1278: B(00010000), ! 1279: B(00010000), ! 1280: B(00010000), ! 1281: B(00010000), ! 1282: B(00010000), ! 1283: B(00010000), ! 1284: B(00010000), ! 1285: B(00010000), ! 1286: ! 1287: B(00010000), // [14] x:│ ! 1288: B(00010000), ! 1289: B(00010000), ! 1290: B(00010000), ! 1291: B(00010000), ! 1292: B(00010000), ! 1293: B(00010000), ! 1294: B(00010000), ! 1295: B(00010000), ! 1296: B(00010000), ! 1297: B(00010000), ! 1298: B(00010000), ! 1299: B(00010000), ! 1300: B(00010000), ! 1301: B(00010000), ! 1302: B(00010000), ! 1303: }; ! 1304: ! 1305: // パレット。 ! 1306: // [0] からだと通常色、[1] からだと反転色になる。 ! 1307: /*static*/ const Color ConsoleDevice::palette[3] = { ! 1308: UD_LIGHT_GREY, // BG ! 1309: UD_BLACK, // FG ! 1310: UD_LIGHT_GREY, // BG ! 1311: }; ! 1312: ! 1313: ! 1314: // ! 1315: // 入力担当デバイス ! 1316: // ! 1317: ! 1318: // コンストラクタ ! 1319: ConsoleKeyboard::ConsoleKeyboard() ! 1320: : inherited() ! 1321: { ! 1322: } ! 1323: ! 1324: // デストラクタ ! 1325: ConsoleKeyboard::~ConsoleKeyboard() ! 1326: { ! 1327: } ! 1328: ! 1329: // COM ドライバを接続。 ! 1330: // (HostCOM の cons ドライバが呼ぶ) ! 1331: void ! 1332: ConsoleKeyboard::Attach(COMDriverCons *comdriver_) ! 1333: { ! 1334: comdriver = comdriver_; ! 1335: } ! 1336: ! 1337: // 文字入力。 ! 1338: // WXMainView から呼ばれる。 ! 1339: void ! 1340: ConsoleKeyboard::CharInput(uint charcode) ! 1341: { ! 1342: if (comdriver == NULL) { ! 1343: return; ! 1344: } ! 1345: ! 1346: // charcode は 0x80 以上を独自に使っているので (keyboard.h 参照)、 ! 1347: // それらをシリアルコンソールのエスケープに置き換えて送り込む。 ! 1348: switch (charcode) { ! 1349: case CC_NUL: ! 1350: comdriver->EnqueueChar(0); ! 1351: break; ! 1352: ! 1353: case CC_F1 ... CC_F10: ! 1354: break; ! 1355: ! 1356: case CC_up: ! 1357: comdriver->EnqueueChar(ESC); ! 1358: comdriver->EnqueueChar('['); ! 1359: comdriver->EnqueueChar('A'); ! 1360: break; ! 1361: ! 1362: case CC_down: ! 1363: comdriver->EnqueueChar(ESC); ! 1364: comdriver->EnqueueChar('['); ! 1365: comdriver->EnqueueChar('B'); ! 1366: break; ! 1367: ! 1368: case CC_right: ! 1369: comdriver->EnqueueChar(ESC); ! 1370: comdriver->EnqueueChar('['); ! 1371: comdriver->EnqueueChar('C'); ! 1372: break; ! 1373: ! 1374: case CC_left: ! 1375: comdriver->EnqueueChar(ESC); ! 1376: comdriver->EnqueueChar('['); ! 1377: comdriver->EnqueueChar('D'); ! 1378: break; ! 1379: ! 1380: default: ! 1381: comdriver->EnqueueChar(charcode); ! 1382: break; ! 1383: } ! 1384: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.