|
|
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.