|
|
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"
1.1.1.4 ! root 14: #include "event.h"
1.1 root 15: #include "comdriver_cons.h"
16: #include "mainapp.h"
17: #include "scheduler.h"
18: #include "wxcolor.h"
19: #include <algorithm>
20:
21: // エスケープシーケンス等。
22: #define ESC '\x1b'
23: #define CSI '['
24:
25: #define PRINTLOG(fmt...) do { \
26: if (log) { \
27: fprintf(log, fmt); \
28: fflush(log); \
29: } \
30: } while (0)
31:
32: // コンストラクタ
33: ConsoleDevice::ConsoleDevice()
34: : inherited(OBJ_CONSOLE)
35: {
36: screen.resize(width * height);
1.1.1.3 root 37: SetPalette(false);
1.1 root 38: }
39:
40: // デストラクタ
41: ConsoleDevice::~ConsoleDevice()
42: {
43: if (log) {
44: fclose(log);
45: log = NULL;
46: }
47: }
48:
49: // 初期化
50: bool
51: ConsoleDevice::Init()
52: {
53: renderer = GetRenderer();
54:
55: // ログファイル。
56: if (gMainApp.console_logfile) {
57: log = fopen(gMainApp.console_logfile, "w");
58: if (log == NULL) {
59: warn("console logfile '%s'", gMainApp.console_logfile);
60: return false;
61: }
62: }
63:
1.1.1.4 ! root 64: auto evman = GetEventManager();
! 65: vsync_event = evman->Regist(this,
! 66: ToEventCallback(&ConsoleDevice::VSyncCallback),
! 67: "Console V-Sync");
! 68: vsync_event->time = 16666.667_usec;
1.1 root 69:
70: return true;
71: }
72:
73: // COM ドライバを接続。
74: // (HostCOM の cons ドライバが呼ぶ)
75: void
76: ConsoleDevice::Attach(COMDriver *comdriver_)
77: {
78: comdriver = comdriver_;
1.1.1.3 root 79:
80: SetPalette((comdriver != NULL));
1.1 root 81: }
82:
83: void
84: ConsoleDevice::ResetHard(bool poweron)
85: {
86: if (poweron) {
87: // 最初に一回フチを含めて描画する。
88: init_screen = true;
89:
90: // シリアルコンソールなのでリセットには反応しない。
91: Clear();
92: Locate(0, 0);
93: }
94:
1.1.1.3 root 95: scheduler->RestartEvent(vsync_event);
1.1 root 96:
97: ResetTerminal();
98: }
99:
100: void
1.1.1.4 ! root 101: ConsoleDevice::VSyncCallback(Event *ev)
1.1 root 102: {
103: bool update_needed;
104:
105: // 更新 (dirty[]) があれば pending[] に重ねる。
106: {
1.1.1.4 ! root 107: std::lock_guard<std::mutex> lock(mtx);
! 108: pending |= dirty;
! 109: dirty.reset();
! 110: update_needed = pending.any();
1.1 root 111: }
112:
113: // 更新の必要があれば Render に作画指示。
114: if (update_needed) {
115: renderer->NotifyRender();
116: }
117:
118: scheduler->StartEvent(ev);
119: }
120:
1.1.1.3 root 121: // パレットの状態を変更する。
122: void
123: ConsoleDevice::SetPalette(bool console_is_active)
124: {
125: if (console_is_active) {
126: palette[0] = UD_LIGHT_GREY; // BG
127: palette[1] = UD_BLACK; // FG
128: } else {
129: palette[0] = UD_LIGHT_GREY; // BG
130: palette[1] = UD_GREY; // FG
131: }
132: // 反転色。
133: palette[2] = palette[0];
134:
135: // 画面に即反映させる。
136: {
1.1.1.4 ! root 137: std::lock_guard<std::mutex> lock(mtx);
! 138: pending.set();
1.1.1.3 root 139: }
140: }
141:
1.1 root 142: // 端末の状態をリセットする。
143: // (画面のクリアはここではしない?)
144: void
145: ConsoleDevice::ResetTerminal()
146: {
147: ResetState();
148: SetAttr(0);
149: scroll_top = 0;
150: scroll_btm = height - 1;
151: save_curx = 0;
152: save_cury = 0;
153: }
154:
155: // エスケープシーケンスの状態を初期化する。
156: void
157: ConsoleDevice::ResetState()
158: {
159: state = 0;
160: seq.clear();
161: arg.clear();
162: }
163:
164: // ch をログ出力する。
165: void
166: ConsoleDevice::PutcharDebug(uint32 ch)
167: {
168: if (ch == '\n') {
169: // 改行が来たらログも改行する。
170: fputs("\\n\n", log);
171: log_newline = true;
172: } else {
173: if (ch == ESC) {
174: // 読みやすさのため ESC の前で改行したい。
175: if (log_newline == false) {
176: fputc('\n', log);
177: }
178: fputs("ESC", log);
179: } else if (ch == '\r') {
180: fputs("\\r", log);
181: } else if (ch == '\t') {
182: fputs("\\t", log);
183: } else if (ch < 0x20 || ch >= 0x7f) {
184: fprintf(log, "\\x%02x", ch);
185: } else {
186: fputc(ch, log);
187: }
188: log_newline = false;
189: }
190: fflush(log);
191: }
192:
193: // シリアルコンソールから来る出力。
194: // ホストドライバスレッドから呼ばれる。
195: void
196: ConsoleDevice::Putchar(uint32 ch)
197: {
198: if (__predict_false(log)) {
199: PutcharDebug(ch);
200: }
201:
202: switch (state) {
203: case ESC:
204: if (PutcharESC(ch)) {
205: return;
206: }
207: break;
208:
209: case CSI:
210: if (PutcharCSI(ch)) {
211: return;
212: }
213: break;
214:
215: case '(': // G0
216: switch (ch) {
217: case '0': // DEC 特殊記号
218: SetAttr(cur_attr | ATTR_DECSG);
219: break;
220: case 'B': // ASCII
221: default:
222: SetAttr(cur_attr & ~ATTR_DECSG);
223: break;
224: }
225: ResetState();
226: return;
227:
228: case ')': // G1
229: case '$': // 94 multibyte
230: case '#':
231: // とりあえず何もしない。
232: ResetState();
233: return;
234:
235: case '?': // CSI '?'
236: if (PutcharCSIQ(ch)) {
237: return;
238: }
239: break;
240:
241: default:
242: PutcharPlain(ch);
243: return;
244: }
245:
246: // シーケンスを破棄。ここまでのを表示して平文に戻る。
247: putlog(0, "Unknown Sequence:%s", Dump(ch).c_str());
248: for (auto c : seq) {
249: Putc(c);
250: }
251: Putc(ch);
252: ResetState();
253: }
254:
255: // 何のシーケンスでもない1文字を表示する。
256: void
257: ConsoleDevice::PutcharPlain(uint32 ch)
258: {
259: switch (ch) {
260: case '\x00': // NUL
261: case '\x01': // SOH
262: case '\x02': // STX
263: case '\x03': // ETX
264: case '\x04': // EOT
265: case '\x05': // ENQ
266: case '\x06': // ACK
267: break;
268: case '\x07': // BEL
269: // とりあえず無視。
270: break;
271:
272: case '\x08': // BS
273: LocateX(cur_x - 1);
274: return;
275:
276: case '\t': // TAB
277: // XXX 右端どうなる?
278: LocateX(roundup(cur_x, 8));
279: return;
280:
281: case '\n': // LF
282: case '\x0b': // VT
283: case '\x0c': // FF
284: LF();
285: return;
286:
287: case '\r': // CR
288: CR();
289: return;
290:
291: case '\x0e': // SO
292: case '\x0f': // SI
293: // Change GL
294: break;
295:
296: case '\x10': // DLE
297: case '\x11': // DC1/XON
298: case '\x12': // DC2
299: case '\x13': // DC3/XOFF
300: case '\x14': // DC4
301: case '\x15': // NAK
302: case '\x16': // SYN
303: case '\x17': // ETB
304: break;
305:
306: case '\x18': // CAN
307: // Cancel
308: ResetState();
309: return;
310:
311: case '\x19': // EM
312: break;
313:
314: case '\x1a': // SUB
315: ResetState();
316: return;
317:
318: case ESC:
319: seq.push_back(ch);
320: state = ch;
321: return;
322:
323: case '\x1c': // FS
324: case '\x1d': // GS
325: case '\x1e': // RS
326: case '\x1f': // US
327: break;
328:
329: default:
330: if (ch < 0x80) {
331: // ここが通常の1文字出力。
332: Putc(ch);
333: return;
334: }
335: // 8ビットシーケンスは無視?
336: break;
337: }
338: putlog(2, "Ignore: \\x%02x", ch);
339: }
340:
341: // ESC の次の文字を処理する。
342: // 処理すれば true、知らないシーケンスなので中止する場合は false を返す。
343: bool
344: ConsoleDevice::PutcharESC(uint32 ch)
345: {
346: switch (ch) {
347: case '[': // CSI
348: case '(': // G0
349: case ')': // G1
350: case '$': // 94 multibyte
351: case '#': // Font width/height control
352: seq.push_back(ch);
353: state = ch;
354: return true;
355:
356: case '\\': // ST
357: break;
358:
359: case '7': // カーソル位置保存
360: save_curx = cur_x;
361: save_cury = cur_y;
362: break;
363:
364: case '8': // カーソル位置復帰
365: Locate(save_curx, save_cury);
366: break;
367:
368: case 'c': // Hard terminal reset
369: Clear();
370: ResetTerminal();
371: Locate(0, 0);
372: break;
373:
374: case 'M': // 1行上に移動 (画面を1行下にスクロール)
375: ScrollDown();
376: Locate(0, 0);
377: break;
378:
379: case '*': // G2
380: case '+': // G3
381: case 'B': // ASCII
382: case 'A': // ISO Latin 1
383: case '<': //
384: case '0': // DEC special graphics
385: case '-': // G1 (96)
386: case '.': // G2
387: case '/': // G3
388: case '4': // Dutch
389: case '5':
390: case '6': // Norwegian/Danish
391: case 'C': // Finnish
392: case 'R': // French
393: case 'Q': // French canadian
394: case 'K': // German
395: case 'Y': // Italian
396: // "%5" とかの2文字のやつは NetBSD/x68k ite も未対応。
397: case '`':
398: case 'n':
399: case '}':
400: case 'o':
401: case '|':
402: case '~':
403: case '=':
404: case '>':
405: putlog(2, "Ignore:%s", Dump(ch).c_str());
406: break;
407:
408: case 'Z': // 端末属性を応答する。
409: default:
410: return false;
411: }
412:
413: ResetState();
414: return true;
415: }
416:
417: // CSI の次の文字を処理する。
418: // 処理すれば true、知らないシーケンスなので中止する場合は false を返す。
419: bool
420: ConsoleDevice::PutcharCSI(uint32 ch)
421: {
422: switch (ch) {
423: case '0':
424: case '1':
425: case '2':
426: case '3':
427: case '4':
428: case '5':
429: case '6':
430: case '7':
431: case '8':
432: case '9':
433: case ';':
434: case '\"':
435: case '$':
436: case '>':
437: seq.push_back(ch);
438: arg.push_back(ch);
439: return true;
440:
441: case 'A':
442: {
443: int n = atoi(arg.c_str());
444: if (n == 0) {
445: n = 1;
446: }
447: LocateY(cur_y - n);
448: ResetState();
449: return true;
450: }
451:
452: case 'B':
453: {
454: int n = atoi(arg.c_str());
455: if (n == 0) {
456: n = 1;
457: }
458: LocateY(cur_y + n);
459: ResetState();
460: return true;
461: }
462:
463: case 'C':
464: {
465: int n = atoi(arg.c_str());
466: if (n == 0) {
467: n = 1;
468: }
469: LocateX(cur_x + n);
470: ResetState();
471: return true;
472: }
473:
474: case 'D':
475: {
476: int n = atoi(arg.c_str());
477: if (n == 0) {
478: n = 1;
479: }
480: LocateX(cur_x - n);
481: ResetState();
482: return true;
483: }
484:
485: case 'H':
486: {
487: // CSI <y> ; <x> H … (x-1, y-1) にカーソルを移動。
488: int y = atoi(arg.c_str());
489: int x = 0;
490: const char *p = strchr(arg.c_str(), ';');
491: if (p) {
492: x = atoi(p + 1);
493: }
494: if (x > 0) {
495: x--;
496: }
497: if (y > 0) {
498: y--;
499: }
500: Locate(x, y);
501: ResetState();
502: return true;
503: }
504:
505: case 'J':
506: {
507: // CSI [<n>] J
508: // <n> が 0 か省略なら、カーソル位置から右下までを消去。
509: // <n> が 1 なら、カーソル位置から左上までを消去。
510: // <n> が 2 なら、全画面消去。
511: // XXX SPA と ERM は未対応。
512: int n = atoi(arg.c_str());
513: switch (n) {
514: default: // 知らんけど
515: case 0:
516: for (int y = cur_y; y < height - 1; y++) {
517: int x = (y == cur_y) ? cur_x : 0;
518: for (; x < width; x++) {
519: Setc(x, y, ' ' | cur_attr);
520: }
521: }
522: break;
523: case 1:
524: for (int y = 0; y <= cur_y; y++) {
525: int xend = (y == cur_y) ? cur_x : (width - 1);
526: for (int x = 0; x <= xend; x++) {
527: Setc(x, y, ' ' | cur_attr);
528: }
529: }
530: break;
531: case 2:
532: // カーソル位置は移動しないまま?
533: Clear();
534: break;
535: }
536: ResetState();
537: return true;
538: }
539:
540: case 'K':
541: {
542: // CSI [<n>] K
543: // <n> が 0 か省略なら、カーソル位置の右側を消去。
544: // <n> が 1 なら、カーソル位置の左側を消去。
545: // <n> が 2 なら、カーソル行を消去。
546: int n = atoi(arg.c_str());
547: int x0;
548: int x1;
549: switch (n) {
550: default: // 知らんけど
551: case 0:
552: x0 = cur_x;
553: x1 = width - 1;
554: break;
555: case 1:
556: x0 = 0;
557: x1 = cur_x;
558: break;
559: case 2:
560: x0 = 0;
561: x1 = width - 1;
562: break;
563: }
564: for (int x = x0; x <= x1; x++) {
565: Setc(x, cur_y, ' ' | cur_attr);
566: }
567: ResetState();
568: return true;
569: }
570:
571: case 'm':
572: {
573: // CSI 7 ; 37 m みたいにセミコロンで区切ってパラメータを複数書ける。
574: // パラメータには 38:2:rr:gg:bb みたいにコロンで区切ったひとかたまり
575: // もある。
576: const char *ap = arg.c_str();
577: for (;;) {
578: int n = atoi(ap);
579: switch (n) {
580: case 0: // すべての属性を解除
581: SetAttr(0);
582: break;
583: case 1: // ボールドをセット
584: SetAttr(cur_attr | ATTR_BOLD);
585: break;
586: case 2: // 低輝度をセット (対応予定なし)
587: break;
588: case 3: // イタリックをセット
589: SetAttr(cur_attr | ATTR_ITALIC);
590: break;
591: case 4: // 下線をセット
592: SetAttr(cur_attr | ATTR_UNDERLINE);
593: break;
594: case 5: // 低速点滅をセット (対応予定なし)
595: break;
596: case 6: // 高速点滅をセット (対応予定なし)
597: break;
598: case 7: // 反転をセット
599: SetAttr(cur_attr | ATTR_REVERSE);
600: break;
601: case 8: // 文字色を背景色と同じにする
602: break;
603: case 9: // 打ち消し線をセット
604: SetAttr(cur_attr | ATTR_STRIKE);
605: break;
606: case 10 ... 20: // フォントセットの設定?
607: break;
608: case 21: // 二重下線をセット (対応予定なし)
609: break;
610: case 22: // ボールド(と低輝度)を解除
611: SetAttr(cur_attr & ~ATTR_BOLD);
612: break;
613: case 23: // イタリックを解除
614: SetAttr(cur_attr & ~ATTR_ITALIC);
615: break;
616: case 24: // 下線を解除
617: SetAttr(cur_attr & ~ATTR_UNDERLINE);
618: break;
619: case 25: // 点滅を解除
620: break;
621: case 26: // ?
622: break;
623: case 27: // 反転を解除
624: SetAttr(cur_attr & ~ATTR_REVERSE);
625: break;
626: case 28: // シークレットを解除
627: break;
628: case 29: // 打ち消し線を解除
629: SetAttr(cur_attr & ~ATTR_STRIKE);
630: break;
631: case 30 ... 37: // 文字色の設定
632: break;
633: case 38: // 文字色(拡張)を設定
634: break;
635: case 39: // 文字色をデフォルトに戻す
636: break;
637: case 40 ... 47: // 背景色を設定
638: break;
639: case 48: // 背景色(拡張)を設定
640: break;
641: case 49: // 背景色をデフォルトに戻す
642: break;
643: case 51: // □を重ねる (対応予定なし)
644: break;
645: case 52: // ○を重ねる (対応予定なし)
646: break;
647: case 53: // 上線をセット
648: SetAttr(cur_attr | ATTR_OVERLINE);
649: break;
650: case 54: // □○を取り消し
651: break;
652: case 55: // 上線を取り消し
653: SetAttr(cur_attr & ~ATTR_OVERLINE);
654: break;
655: case 60 ... 63: // 縦線 (対応予定なし)
656: break;
657: case 64: // 二重打ち消し線 (対応予定なし)
658: break;
659: case 65: // 60-64 を取り消し
660: break;
661: case 90 ... 97: // 文字色(高輝度)を設定
662: break;
663: case 100 ... 107: // 背景色(高輝度)を設定
664: break;
665: default:
666: // 無視
667: break;
668: }
669:
670: if (*ap == '\0') {
671: break;
672: }
673: ap = strchr(ap + 1, ';');
674: if (ap == NULL) {
675: break;
676: }
677: ap++;
678: }
679:
680: ResetState();
681: return true;
682: }
683:
684: case 'r':
685: {
686: // CSI <t> ; <b> r。<t> 省略時は 1。<b> 省略時は画面最下行。
687: int top = atoi(arg.c_str());
688: int btm = height;
689: const char *p = strchr(arg.c_str(), ';');
690: if (p) {
691: btm = atoi(p + 1);
692: }
693: if (top > 0) {
694: top--;
695: }
696: if (btm > 0) {
697: btm--;
698: }
699: scroll_top = top;
700: scroll_btm = btm;
701: ResetState();
702: return true;
703: }
704:
705: case '?':
706: seq.push_back(ch);
707: state = ch;
708: return true;
709:
710: default:
711: return false;
712: }
713: }
714:
715: // CSI '?' の次の文字を処理する。
716: // 処理すれば true、知らないシーケンスなので中止する場合は false を返す。
717: bool
718: ConsoleDevice::PutcharCSIQ(uint32 ch)
719: {
720: switch (ch) {
721: case '0':
722: case '1':
723: case '2':
724: case '3':
725: case '4':
726: case '5':
727: case '6':
728: case '7':
729: case '8':
730: case '9':
731: case ';':
732: case '\"':
733: case '$':
734: case '>':
735: seq.push_back(ch);
736: arg.push_back(ch);
737: return true;
738:
739: case 'h': // 拡張オプションを設定
740: case 'l': // 拡張オプションを解除
741: // CSI '?' <n> h
742: // CSI '?' <n> l
743: // 全部未対応。
744: putlog(2, "Ignored:%s", Dump(ch).c_str());
745: ResetState();
746: return true;
747:
748: default:
749: return false;
750: }
751: }
752:
753: // 現在の seq を表示用文字列にして返す。
754: std::string
755: ConsoleDevice::Dump() const
756: {
757: return Dump(seq);
758: }
759:
760: // 現在の seq と ch を表示用文字列にして返す。
761: // 大抵の場合最後の1文字はまだ seq に入っていないのでこの形式があると便利。
762: std::string
763: ConsoleDevice::Dump(uint32 ch) const
764: {
765: std::vector<uint8> tmp = seq;
766: tmp.push_back(ch);
767: return Dump(tmp);
768: }
769:
770: // エスケープシーケンスを表示用文字列にして返す。
771: // 戻り文字列には先頭に空白が入っている。
772: /*static*/ std::string
773: ConsoleDevice::Dump(const std::vector<uint8>& src)
774: {
775: std::string dst;
776:
777: for (auto ch : src) {
778: if (ch == ESC) {
779: dst += " ESC";
780: } else if (ch < ' ' || ch >= 0x7f) {
781: dst += string_format(" \\x%02x", ch);
782: } else if (ch == ' ') {
783: dst += " ' '";
784: } else {
785: dst += ' ';
786: dst += (char)ch;
787: }
788: }
789: return dst;
790: }
791:
792: // 画面を消去する。
793: // カーソル位置は変わらない。属性もクリアされない。
794: void
795: ConsoleDevice::Clear()
796: {
797: std::fill(screen.begin(), screen.end(), ' ' | cur_attr);
1.1.1.4 ! root 798: dirty.set();
1.1 root 799: }
800:
801: // カーソルの X 座標を移動。(画面をはみ出さないようにクリップされる)
802: void
803: ConsoleDevice::LocateX(int x)
804: {
805: if (__predict_false(x < 0)) {
806: x = 0;
807: }
808: if (__predict_false(x > width - 1)) {
809: x = width - 1;
810: }
811: cur_x = x;
1.1.1.4 ! root 812: dirty.set(cur_y);
1.1 root 813: }
814:
815: // カーソルの Y 座標を移動。(画面をはみ出さないようにクリップされる)
816: void
817: ConsoleDevice::LocateY(int y)
818: {
819: if (__predict_false(y < 0)) {
820: y = 0;
821: }
822: if (__predict_false(y > height - 1)) {
823: y = height - 1;
824: }
1.1.1.4 ! root 825: dirty.set(cur_y);
1.1 root 826: cur_y = y;
1.1.1.4 ! root 827: dirty.set(cur_y);
1.1 root 828: }
829:
830: // 文字出力後のカーソル移動。
831: void
832: ConsoleDevice::Ascend()
833: {
834: cur_x++;
1.1.1.4 ! root 835: dirty.set(cur_y);
1.1 root 836: if (cur_x >= width) {
837: CRLF();
838: }
839: }
840:
841: // 復帰。
842: void
843: ConsoleDevice::CR()
844: {
845: LocateX(0);
846: }
847:
848: // 改行。(DECSTBM の影響を受ける)
849: void
850: ConsoleDevice::LF()
851: {
1.1.1.4 ! root 852: dirty.set(cur_y);
1.1 root 853: cur_y++;
854: if (cur_y >= scroll_btm) {
855: ScrollUp();
856: cur_y = scroll_btm;
857: }
1.1.1.4 ! root 858: dirty.set(cur_y);
1.1 root 859: }
860:
861: // 全画面を1行上にスクロール。(DECSTBM の影響を受ける)
862: // カーソル位置は移動しない。
863: void
864: ConsoleDevice::ScrollUp()
865: {
866: int dst = scroll_top * width;
867: int lines = scroll_btm - scroll_top;
868: memmove(&screen[dst], &screen[dst + width],
869: lines * width * sizeof(screen[0]));
870: uint32 ch = ' ' | cur_attr;
871: for (int x = 0; x < width; x++) {
872: Setc(x, scroll_btm, ch);
873: }
1.1.1.4 ! root 874: dirty.set();
1.1 root 875: }
876:
877: // 全画面を1行下にスクロール。(DECSTBM の影響を受ける)
878: // カーソル位置は移動しない。
879: void
880: ConsoleDevice::ScrollDown()
881: {
882: int src = scroll_top * width;
883: int lines = scroll_btm - scroll_top;
884: memmove(&screen[src + width], &screen[src],
885: lines * width * sizeof(screen[0]));
886: uint32 ch = ' ' | cur_attr;
887: for (int x = 0; x < width; x++) {
888: Setc(x, scroll_top, ch);
889: }
1.1.1.4 ! root 890: dirty.set();
1.1 root 891: }
892:
893: // 現在位置に、現在の属性で文字 ch を出力する。
894: // 出力後カーソルは移動する。
895: void
896: ConsoleDevice::Putc(uint32 ch)
897: {
898: if (ch == '\n') {
899: CR();
900: LF();
901: return;
902: }
903:
904: Setc(cur_x, cur_y, ch | cur_attr);
905: Ascend();
906: }
907:
908: // (x, y) の位置に chattr を出力する。
909: // 出力後カーソルは移動しない。
910: // 現在の attr は参照しない (呼び出す側が指定すること)。
911: void
912: ConsoleDevice::Setc(int x, int y, uint32 chattr)
913: {
914: int pos = y * width + x;
915: assert(pos >= 0);
916: assert(pos < width * height);
917: screen[pos] = chattr;
918: }
919:
920: // 現在の属性を設定する。
921: // ほぼログ出力のため。
922: void
923: ConsoleDevice::SetAttr(uint32 new_attr)
924: {
925: if (cur_attr == new_attr) {
926: return;
927: }
928: cur_attr = new_attr;
929:
930: if (log) {
931: std::string buf;
932: if ((cur_attr & ATTR_BOLD)) {
933: buf += ",ATTR_BOLD";
934: }
935: if ((cur_attr & ATTR_ITALIC)) {
936: buf += ",ATTR_ITALIC";
937: }
938: if ((cur_attr & ATTR_UNDERLINE)) {
939: buf += ",ATTR_UNDERLINE";
940: }
941: if ((cur_attr & ATTR_STRIKE)) {
942: buf += ",ATTR_STRIKE";
943: }
944: if ((cur_attr & ATTR_OVERLINE)) {
945: buf += ",ATTR_OVERLINE";
946: }
947: if ((cur_attr & ATTR_DECSG)) {
948: buf += ",ATTR_DECSG";
949: }
950: if ((cur_attr & ATTR_REVERSE)) {
951: buf += ",ATTR_REVERSE";
952: }
953:
954: if (buf.empty()) {
955: buf = "<ATTR_NORMAL>";
956: } else {
957: buf[0] = '<';
958: buf += '>';
959: }
960: fputs(buf.c_str(), log);
961: fflush(log);
962: }
963: }
964:
965: // 画面合成。
966: // レンダリングスレッドから呼ばれる。
967: bool
968: ConsoleDevice::Render(BitmapRGBX& dst)
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: // 行ごとに更新があるか調べる。
1.1.1.4 ! root 980: bitsetH modified;
1.1 root 981: {
1.1.1.4 ! root 982: std::lock_guard<std::mutex> lock(mtx);
1.1 root 983: modified = pending;
1.1.1.4 ! root 984: pending.reset();
1.1 root 985: }
1.1.1.4 ! root 986: updated |= modified.any();
1.1 root 987:
988: const uint8 *cgrom8x16 = Builtin::CGROM8x16(0);
989: for (uint y = 0; y < height; y++) {
1.1.1.4 ! root 990: if (__predict_false(modified.test(y))) {
1.1 root 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: //
1307: // 入力担当デバイス
1308: //
1309:
1310: // コンストラクタ
1311: ConsoleKeyboard::ConsoleKeyboard()
1312: : inherited()
1313: {
1314: }
1315:
1316: // デストラクタ
1317: ConsoleKeyboard::~ConsoleKeyboard()
1318: {
1319: }
1320:
1321: // COM ドライバを接続。
1322: // (HostCOM の cons ドライバが呼ぶ)
1323: void
1.1.1.3 root 1324: ConsoleKeyboard::Attach(COMDriverConsole *comdriver_)
1.1 root 1325: {
1326: comdriver = comdriver_;
1327: }
1328:
1329: // 文字入力。
1330: // WXMainView から呼ばれる。
1331: void
1332: ConsoleKeyboard::CharInput(uint charcode)
1333: {
1334: if (comdriver == NULL) {
1335: return;
1336: }
1337:
1338: // charcode は 0x80 以上を独自に使っているので (keyboard.h 参照)、
1339: // それらをシリアルコンソールのエスケープに置き換えて送り込む。
1340: switch (charcode) {
1341: case CC_NUL:
1342: comdriver->EnqueueChar(0);
1343: break;
1344:
1345: case CC_F1 ... CC_F10:
1346: break;
1347:
1348: case CC_up:
1349: comdriver->EnqueueChar(ESC);
1350: comdriver->EnqueueChar('[');
1351: comdriver->EnqueueChar('A');
1352: break;
1353:
1354: case CC_down:
1355: comdriver->EnqueueChar(ESC);
1356: comdriver->EnqueueChar('[');
1357: comdriver->EnqueueChar('B');
1358: break;
1359:
1360: case CC_right:
1361: comdriver->EnqueueChar(ESC);
1362: comdriver->EnqueueChar('[');
1363: comdriver->EnqueueChar('C');
1364: break;
1365:
1366: case CC_left:
1367: comdriver->EnqueueChar(ESC);
1368: comdriver->EnqueueChar('[');
1369: comdriver->EnqueueChar('D');
1370: break;
1371:
1372: default:
1373: comdriver->EnqueueChar(charcode);
1374: break;
1375: }
1376: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.