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