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