|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2024 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
1.1.1.6 ! root 8: // メイン画面を vt220 互換コンソールにする
1.1 root 9: //
10:
1.1.1.6 ! root 11: // https://vt100.net/docs/vt220-rm/contents.html
! 12:
! 13: // vttest ver2.7 (20210210) result
! 14: //
! 15: // OK 1. (1) 枠表示、DECALN、80桁
! 16: // OK 1. (2) 枠表示、DECALN、132桁
! 17: // OK 1. (3) Test of autowrap、80桁
! 18: // OK 1. (4) Test of autowrap、132桁
! 19: // OK 1. (5) Test of cursor-control
! 20: // OK 1. (6) Test of leading zeros in ESC
! 21: // FAIL 2. (1) Test of WRAP AROUND
! 22: // FAIL 2. (2) Test of TAB setting/resetting
! 23: // OK* 2. (3) 132 column, light background (*背景色の区別なし)
! 24: // OK* 2. (4) 80 column, light background (*背景色の区別なし)
! 25: // OK* 2. (5) 132 column, dark background (*背景色の区別なし)
! 26: // OK* 2. (6) 80 column, dark background (*背景色の区別なし)
! 27: // OK* 2. (7) Soft scroll down size 2 (*Soft/Jump の区別なし)
! 28: // OK* 2. (8) Soft scroll down size 24 (*Soft/Jump の区別なし)
! 29: // OK* 2. (9) Jump scroll down size 2 (*Soft/Jump の区別なし)
! 30: // OK* 2. (10) Jump scroll down size 24 (*Soft/Jump の区別なし)
! 31: // OK 2. (11) Origin mode test. Bottom
! 32: // OK 2. (12) Origin mode test. Top
! 33: // OK* 2. (13) Graphic rendition test. Dark (*背景色の区別なし)
! 34: // OK* 2. (14) Graphic rendition test. Light (*背景色の区別なし)
! 35: // OK 2. (15) Test of SAVE/RESTORE
! 36: // OK* 3. character sets (DEC 罫線以外対応予定なし)
! 37: // FAIL 4. double-sized characters は対応予定なし
! 38: // - 5.1 LED Lights
! 39: // FAIL 5.2 Auto Repeat
! 40: // - 5.3 KeyBoard Layout
! 41: // OK 5.4 Cursor Keys (VT52 モードのテストは動かない)
! 42: // - 5.5 Numeric Keypad
! 43: // - 5.6 Editing Keypad (?)
! 44: // - 5.7 Function Keys (?)
! 45: // - 5.8 AnswerBack (?)
! 46: // FAIL 5.9 Control Keys (CTRL-@ が通らない)
! 47: // - 6.1 <ENQ> (?)
! 48: // FAIL 6.2 Set/Reset Mode
! 49: // OK 6.3 Device Status Report (DSR)
! 50: // OK 6.4 Primary Device Attributes (DA)
! 51: // OK 6.5 Secondary Device Attributes (DA)
! 52: // - 6.6 Tertiary Device Attributes (DA) (*VT420以上)
! 53: // - 6.7 Request Terminal Parameters (DECREQTPARM) (*VT100)
! 54: // - 7. VT52 mode
! 55: // OK 8. (1) Screen accordion test
! 56: // OK 8. (2) Top A, Bottom X
! 57: // OK 8. (3) Test of Insert mode, A**B
! 58: // OK 8. (4) Test of Delete Char, AB
! 59: // OK 8. (5) The right column should be staggered
! 60: // FAIL 8. (6) 〃 (double-sized)
! 61: // OK 8. (7) ANSI Insert Char
! 62: // OK 8. (8) Screen Accordion test (以下132桁)
! 63: // OK 8. (9) Top A, Bottom X
! 64: // OK 8. (10) Test of Insert mode, A**B
! 65: // OK 8. (11) Test of Delete char, AB
! 66: // OK 8. (12) The right column should be staggered
! 67: // FAIL 8. (13) 〃 (double-sized)
! 68: // OK 8. (14) ANSI Insert Char
! 69: // - 9. Test of known bugs
! 70: // OK? 10.1 RIS
! 71: // FAIL 10.2 DECTST
! 72: // OK? 10.3 DECSTR
! 73: // OK 11.1.1.1.1 Keyboard Status
! 74: // OK 11.1.1.1.2 Operating Status
! 75: // OK 11.1.1.1.3 Printer Status
! 76: // OK 11.1.1.1.4 UDK Status
! 77: // ? 11.1.2.1 SRM
! 78: // OK 11.1.2.2 DECTCEM
! 79: // OK 11.1.2.3 ECH
! 80: // FAIL 11.1.2.4 (1) DECSCA, DECSED, DECSEL
! 81: // OK 11.1.2.4 (2) DECSCA, ignoring EL/ED
! 82: // OK 11.1.2.4 (3) DECSCA, ignoring ECH/ICH/DCH
! 83: // FAIL 11.1.3 S7C1T/S8C1T
! 84: // FAIL 11.1.4 Printer(MC)
! 85: // FAIL 11.1.5 DECDLD
! 86: // OK 11.1.6 DECSTR
! 87: // FAIL 11.1.7 DECUDK
! 88: // - 11.2 VT320 features
! 89: // - 11.3 VT420 features
! 90: // - 11.4 VT520 features
! 91: // - 11.5 ISO-6429 cursor-movement
! 92: // - 11.6 ISO-6429 colors
! 93: // - 11.7 Other ISO-6429 features
! 94: // - 11.8 XTERM special features
! 95:
1.1 root 96: #include "console.h"
97: #include "bitmap.h"
98: #include "builtinrom.h"
1.1.1.4 root 99: #include "event.h"
1.1 root 100: #include "comdriver_cons.h"
101: #include "mainapp.h"
102: #include "scheduler.h"
1.1.1.6 ! root 103: #include "syncer.h"
1.1 root 104: #include <algorithm>
105:
106: // エスケープシーケンス等。
107: #define ESC '\x1b'
108:
109: #define PRINTLOG(fmt...) do { \
110: if (log) { \
111: fprintf(log, fmt); \
112: fflush(log); \
113: } \
114: } while (0)
115:
116: // コンストラクタ
117: ConsoleDevice::ConsoleDevice()
118: : inherited(OBJ_CONSOLE)
119: {
1.1.1.6 ! root 120: screen.resize(fullwidth * height);
1.1.1.3 root 121: SetPalette(false);
1.1 root 122: }
123:
124: // デストラクタ
125: ConsoleDevice::~ConsoleDevice()
126: {
127: if (log) {
128: fclose(log);
129: log = NULL;
130: }
1.1.1.6 ! root 131:
! 132: if (comdriver) {
! 133: comdriver->DetachConsole();
! 134: comdriver = NULL;
! 135: }
1.1 root 136: }
137:
138: // 初期化
139: bool
140: ConsoleDevice::Init()
141: {
1.1.1.5 root 142: renderer = GetVideoRenderer();
1.1.1.6 ! root 143: keyboard = gMainApp.GetObject<ConsoleKeyboard>(OBJ_KEYBOARD);
! 144: syncer = GetSyncer();
1.1 root 145:
146: // ログファイル。
147: if (gMainApp.console_logfile) {
148: log = fopen(gMainApp.console_logfile, "w");
149: if (log == NULL) {
1.1.1.6 ! root 150: warn("console logfile \"%s\"", gMainApp.console_logfile);
1.1 root 151: return false;
152: }
153: }
154:
1.1.1.4 root 155: auto evman = GetEventManager();
156: vsync_event = evman->Regist(this,
157: ToEventCallback(&ConsoleDevice::VSyncCallback),
158: "Console V-Sync");
159: vsync_event->time = 16666.667_usec;
1.1 root 160:
161: return true;
162: }
163:
164: // COM ドライバを接続。
165: // (HostCOM の cons ドライバが呼ぶ)
166: void
1.1.1.6 ! root 167: ConsoleDevice::Attach(COMDriverConsole *comdriver_)
1.1 root 168: {
169: comdriver = comdriver_;
1.1.1.3 root 170:
171: SetPalette((comdriver != NULL));
1.1 root 172: }
173:
174: void
175: ConsoleDevice::ResetHard(bool poweron)
176: {
177: if (poweron) {
178: // 最初に一回フチを含めて描画する。
179: init_screen = true;
180:
181: // シリアルコンソールなのでリセットには反応しない。
182: Clear();
183: Locate(0, 0);
184: }
185:
1.1.1.3 root 186: scheduler->RestartEvent(vsync_event);
1.1 root 187:
1.1.1.6 ! root 188: ResetTerminalHard();
1.1 root 189: }
190:
191: void
1.1.1.4 root 192: ConsoleDevice::VSyncCallback(Event *ev)
1.1 root 193: {
194: bool update_needed;
195:
1.1.1.6 ! root 196: // 更新のあった行については、点滅があるか全て調べ直す。
! 197: for (int y = 0; y < dirty.size(); y++) {
! 198: if (__predict_false(dirty[y])) {
! 199: has_blink.set(y, std::any_of(
! 200: screen.begin() + y * fullwidth,
! 201: screen.begin() + y * fullwidth + fullwidth,
! 202: [](int v) { return (v & ATTR_BLINK); }));
! 203: }
! 204: }
! 205:
! 206: // 点滅を持つ行は周期ごとに dirty に追加。
! 207: if (__predict_false(has_blink.any())) {
! 208: auto rtime = syncer->GetRealTime();
! 209: // nsec を >>29 で 536_msec くらいになる
! 210: auto new_blinking = (bool)(((rtime/*_nsec*/) >> 29) & 1);
! 211: if (blinking != new_blinking) {
! 212: blinking = new_blinking;
! 213: dirty |= has_blink;
! 214: }
! 215: } else {
! 216: // 点滅が一つもなくなったら点滅状態を解除。
! 217: blinking = false;
! 218: }
! 219:
1.1 root 220: // 更新 (dirty[]) があれば pending[] に重ねる。
221: {
1.1.1.4 root 222: std::lock_guard<std::mutex> lock(mtx);
223: pending |= dirty;
224: update_needed = pending.any();
1.1 root 225: }
226:
1.1.1.6 ! root 227: dirty.reset();
! 228:
1.1 root 229: // 更新の必要があれば Render に作画指示。
230: if (update_needed) {
231: renderer->NotifyRender();
232: }
233:
234: scheduler->StartEvent(ev);
235: }
236:
1.1.1.3 root 237: // パレットの状態を変更する。
238: void
239: ConsoleDevice::SetPalette(bool console_is_active)
240: {
241: if (console_is_active) {
242: palette[0] = UD_LIGHT_GREY; // BG
243: palette[1] = UD_BLACK; // FG
244: } else {
245: palette[0] = UD_LIGHT_GREY; // BG
246: palette[1] = UD_GREY; // FG
247: }
248: // 反転色。
249: palette[2] = palette[0];
250:
251: // 画面に即反映させる。
252: {
1.1.1.4 root 253: std::lock_guard<std::mutex> lock(mtx);
254: pending.set();
1.1.1.3 root 255: }
256: }
257:
1.1.1.6 ! root 258: // ソフトリセット (DECSTR 用)。
! 259: void
! 260: ConsoleDevice::ResetTerminalSoft()
! 261: {
! 262: cursor_visible = true;
! 263: insert_mode = false;
! 264: decom = false;
! 265: keyboard->SetCKM(false);
! 266: ResetScroll();
! 267:
! 268: save_x = 0;
! 269: save_y = 0;
! 270: save_attr = 0;
! 271: save_wrap = false;
! 272: save_decom = false;
! 273: }
! 274:
! 275: // 端末の状態をハードリセットする。
1.1 root 276: // (画面のクリアはここではしない?)
277: void
1.1.1.6 ! root 278: ConsoleDevice::ResetTerminalHard()
1.1 root 279: {
1.1.1.6 ! root 280: ResetTerminalSoft();
! 281:
1.1 root 282: ResetState();
283: SetAttr(0);
1.1.1.6 ! root 284:
! 285: wrap_pending = false;
! 286: width = 80;
! 287: ResetMargin();
! 288:
! 289: cur_x = 0;
! 290: cur_y = 0;
! 291: cr_on_lf = false;
! 292: }
! 293:
! 294: // 左右マージンをリセットする。
! 295: void
! 296: ConsoleDevice::ResetMargin()
! 297: {
! 298: // margin は両閉区間で表す。
! 299: margin_left = 0;
! 300: margin_right = width - 1;
! 301: }
! 302:
! 303: // スクロール領域をリセットする。
! 304: void
! 305: ConsoleDevice::ResetScroll()
! 306: {
! 307: // scroll は両閉区間で表す。
1.1 root 308: scroll_top = 0;
309: scroll_btm = height - 1;
310: }
311:
312: // エスケープシーケンスの状態を初期化する。
313: void
314: ConsoleDevice::ResetState()
315: {
316: state = 0;
317: seq.clear();
318: arg.clear();
1.1.1.6 ! root 319: argv.clear();
! 320: argv_idx = -1;
1.1 root 321: }
322:
323: // ch をログ出力する。
324: void
325: ConsoleDevice::PutcharDebug(uint32 ch)
326: {
327: if (ch == '\n') {
328: // 改行が来たらログも改行する。
329: fputs("\\n\n", log);
330: log_newline = true;
331: } else {
332: if (ch == ESC) {
333: // 読みやすさのため ESC の前で改行したい。
334: if (log_newline == false) {
335: fputc('\n', log);
336: }
337: fputs("ESC", log);
338: } else if (ch == '\r') {
339: fputs("\\r", log);
340: } else if (ch == '\t') {
341: fputs("\\t", log);
342: } else if (ch < 0x20 || ch >= 0x7f) {
343: fprintf(log, "\\x%02x", ch);
344: } else {
345: fputc(ch, log);
346: }
347: log_newline = false;
348: }
349: fflush(log);
350: }
351:
352: // シリアルコンソールから来る出力。
353: // ホストドライバスレッドから呼ばれる。
354: void
355: ConsoleDevice::Putchar(uint32 ch)
356: {
357: if (__predict_false(log)) {
358: PutcharDebug(ch);
359: }
360:
1.1.1.6 ! root 361: // 処理したら return。処理できなかったら switch を抜ける。
1.1 root 362: switch (state) {
363: case ESC:
364: if (PutcharESC(ch)) {
365: return;
366: }
367: break;
368:
1.1.1.6 ! root 369: case '[': // CSI
1.1 root 370: if (PutcharCSI(ch)) {
371: return;
372: }
373: break;
374:
375: case '(': // G0
1.1.1.6 ! root 376: if (PutcharG0(ch)) {
! 377: return;
1.1 root 378: }
1.1.1.6 ! root 379: break;
1.1 root 380:
381: case '?': // CSI '?'
382: if (PutcharCSIQ(ch)) {
383: return;
384: }
385: break;
386:
1.1.1.6 ! root 387: case '#':
! 388: if (PutcharSharp(ch)) {
! 389: return;
! 390: }
! 391: break;
! 392:
! 393: case ')': // G1
! 394: case '*': // G2
! 395: case '+': // G3
! 396: // とりあえず何もしない。
! 397: ResetState();
! 398: return;
! 399:
1.1 root 400: default:
401: PutcharPlain(ch);
402: return;
403: }
404:
1.1.1.6 ! root 405: // シーケンスを破棄して平文に戻る。
1.1 root 406: putlog(0, "Unknown Sequence:%s", Dump(ch).c_str());
407: ResetState();
408: }
409:
410: // 何のシーケンスでもない1文字を表示する。
411: void
412: ConsoleDevice::PutcharPlain(uint32 ch)
413: {
414: switch (ch) {
415: case '\x00': // NUL
1.1.1.6 ! root 416: // 何もしない。パディングというか NOP 代わりに大量に送られてくる。
! 417: return;
! 418:
1.1 root 419: case '\x01': // SOH
420: case '\x02': // STX
421: case '\x03': // ETX
422: case '\x04': // EOT
423: case '\x05': // ENQ
424: case '\x06': // ACK
1.1.1.6 ! root 425: // 何もしない。
! 426: return;
! 427:
1.1 root 428: case '\x07': // BEL
429: // とりあえず無視。
1.1.1.6 ! root 430: putlog(2, "BEL (Ignored)");
! 431: return;
1.1 root 432:
433: case '\x08': // BS
1.1.1.6 ! root 434: // カーソルを 1 文字左に移動。文字は消さない。
! 435: // DECLRMM(左右マージン)の影響を受ける。
! 436: LocateXinMargin(cur_x - 1);
1.1 root 437: return;
438:
1.1.1.6 ! root 439: case '\t': // HT
! 440: HT();
1.1 root 441: return;
442:
443: case '\n': // LF
444: case '\x0b': // VT
445: case '\x0c': // FF
446: LF();
447: return;
448:
1.1.1.6 ! root 449: case '\r': // CR
1.1 root 450: CR();
451: return;
452:
453: case '\x0e': // SO
454: case '\x0f': // SI
455: case '\x10': // DLE
456: case '\x11': // DC1/XON
457: case '\x12': // DC2
458: case '\x13': // DC3/XOFF
459: case '\x14': // DC4
460: case '\x15': // NAK
461: case '\x16': // SYN
462: case '\x17': // ETB
1.1.1.6 ! root 463: // 何もしない。
! 464: return;
1.1 root 465:
466: case '\x18': // CAN
467: // Cancel
468: ResetState();
469: return;
470:
471: case '\x19': // EM
1.1.1.6 ! root 472: // 何もしない。
! 473: return;
1.1 root 474:
475: case '\x1a': // SUB
476: ResetState();
477: return;
478:
1.1.1.6 ! root 479: case ESC: // ESC
1.1 root 480: seq.push_back(ch);
481: state = ch;
482: return;
483:
1.1.1.6 ! root 484: case '\x1c': // FS (File Separator)
! 485: case '\x1d': // GS (Group Separator)
! 486: case '\x1e': // RS (Record Separator)
! 487: case '\x1f': // US (Unit Separator)
! 488: // 何もしない。
! 489: return;
! 490:
! 491: case '\x7f': // DEL
! 492: // 何もしない。
! 493: return;
1.1 root 494:
495: default:
1.1.1.6 ! root 496: // ここが通常の1文字出力。
! 497: // 8ビット文字はここでは素通しするが、レンダラで豆腐を出力する。
! 498: // NetBSD の sysinst が言語選択画面で1文字送ってくる。
! 499: // 8ビットのエスケープシーケンス (\x80〜\x9f) のことは無視する。
! 500: PutcharNormal(ch);
! 501: return;
1.1 root 502: }
503: }
504:
505: // ESC の次の文字を処理する。
506: // 処理すれば true、知らないシーケンスなので中止する場合は false を返す。
507: bool
508: ConsoleDevice::PutcharESC(uint32 ch)
509: {
510: switch (ch) {
511: case '[': // CSI
512: case '(': // G0
513: case ')': // G1
1.1.1.6 ! root 514: case '*': // G2
! 515: case '+': // G3
1.1 root 516: case '#': // Font width/height control
517: seq.push_back(ch);
518: state = ch;
519: return true;
520:
521: case '\\': // ST
1.1.1.6 ! root 522: putlog(2, "ST");
1.1 root 523: break;
524:
1.1.1.6 ! root 525: case ' ': // Select C1 Controls
! 526: // ESC ' ' F (S7C1T)
! 527: // ESC ' ' G (S8C1T)
! 528: return Unimpl(ch, "S*C1T");
! 529:
! 530: // DECKPAM/DECKPNM はテンキー入力をそのまま送る(Keypad Numeric Mode)か、
! 531: // Keypad Application Mode で送るかの切り替えだが、今の実装では
! 532: // 文字コードしかここに来ていないためテンキーかどうかはもう区別できない。
! 533: // ので、テンキーが存在していないか、あってもあえて押していないのと等価。
! 534: // vi(1) の開始/終了時に送られてくる。
! 535: case '=': // DECKPAM
! 536: putlog(3, "DECKPAM (Keypad App. Mode)");
! 537: break;
! 538: case '>': // DECKPNM
! 539: putlog(3, "DECKPNM (Keypad Num. Mode)");
! 540: break;
! 541:
! 542: case '7': // DECSC (カーソル位置保存)
! 543: putlog(2, "DECSC (Save)");
! 544: save_x = cur_x;
! 545: save_y = cur_y;
! 546: save_attr = cur_attr;
! 547: save_wrap = wrap_pending;
! 548: save_decom = decom;
! 549: break;
! 550:
! 551: case '8': // DECRC (カーソル位置復帰)
! 552: // 変数の復帰順序注意。
! 553: putlog(2, "DECRC (Restore)");
! 554: wrap_pending = save_wrap;
! 555: decom = save_decom;
! 556: cur_attr = save_attr;
! 557: Locate(save_x, save_y);
! 558: break;
! 559:
! 560: case 'c': // RIS (Hard terminal reset)
! 561: putlog(2, "RIS (Hard terminal reset)");
! 562: ResetTerminalHard();
! 563: Clear();
1.1 root 564: break;
565:
1.1.1.6 ! root 566: case 'D': // IND
! 567: {
! 568: // カーソルを1行下に移動する。
! 569: // ただしカーソルがスクロール下端行にいる場合はスクロール領域全体を
! 570: // 1行上に移して、カーソル行を空行にする。
! 571: // DECLRMM(左右マージン)の影響は受けない。
! 572: //
! 573: // スクロール領域外でも1行下に移動するだけなので、スクロール上端か
! 574: // どうかだけ。
! 575: wrap_pending = false;
! 576: if (cur_y != scroll_btm) {
! 577: putlog(3, "IND (Index) ");
! 578: LocateY(cur_y + 1);
! 579: } else {
! 580: // 下端から一つ下に移動しようとした。
! 581: putlog(3, "IND (Index) scrolled");
! 582: for (int y = scroll_top + 1; y <= scroll_btm; y++) {
! 583: CopyLineWhole(y - 1, y);
! 584: }
! 585: EraseLineWhole(scroll_btm);
! 586: }
1.1 root 587: break;
1.1.1.6 ! root 588: }
1.1 root 589:
1.1.1.6 ! root 590: case 'E': // NEL (Next Line)
! 591: // カーソルを次行の先頭に。スクロール領域下端に来たらスクロールアップ。
! 592: // なので CR+LF と等価。
! 593: putlog(3, "NEL (Next Line)");
! 594: CR();
! 595: LF();
1.1 root 596: break;
597:
1.1.1.6 ! root 598: case 'H': // HTS (Horizontal tab set)
! 599: return Unimpl(ch, "HTS");
! 600:
! 601: case 'M': // RI (Reverse Index)
! 602: {
! 603: // カーソルを1行上に移動する。
! 604: // ただしカーソルがスクロール上端行にいる場合はスクロール領域全体を
! 605: // 1行下に移して、カーソル行を空行にする。
! 606: // DECLRMM(左右マージン)の影響は受けない。
! 607: //
! 608: // スクロール領域外でも1行上に移動するだけなので、スクロール上端か
! 609: // どうかだけ。
! 610: wrap_pending = false;
! 611: if (cur_y != scroll_top) {
! 612: putlog(3, "RI (Rev. Index)");
! 613: LocateY(cur_y - 1);
! 614: } else {
! 615: // 上端から一つ上に移動しようとした。
! 616: putlog(3, "RI (Rev. Index) scrolled");
! 617: for (int y = scroll_btm - 1; y >= scroll_top; y--) {
! 618: CopyLineWhole(y + 1, y);
! 619: }
! 620: EraseLineWhole(scroll_top);
! 621: }
1.1 root 622: break;
1.1.1.6 ! root 623: }
1.1 root 624:
1.1.1.6 ! root 625: case 'N': // SS2
! 626: return Unimpl(ch, "SS2");
! 627: case 'O': // SS3
! 628: return Unimpl(ch, "SS3");
! 629:
! 630: case 'Z': // DECID (Identify Terminal)
! 631: // VT100 か問い合わせなので、常に VT100 相当を返答。
! 632: putlog(3, "DECID (Identify Terminal)");
! 633: SendString("\x1b/Z");
1.1 root 634: break;
635:
1.1.1.6 ! root 636: case '$': // 94 multibyte (VT300 シリーズ以降?)
1.1 root 637: default:
638: return false;
639: }
640:
641: ResetState();
642: return true;
643: }
644:
645: // CSI の次の文字を処理する。
646: // 処理すれば true、知らないシーケンスなので中止する場合は false を返す。
647: bool
648: ConsoleDevice::PutcharCSI(uint32 ch)
649: {
650: switch (ch) {
1.1.1.6 ! root 651: case '\x00'
! 652: ... '\x17':
! 653: case '\x19': // EM
! 654: case '\x1c': // FS (File Separator)
! 655: case '\x1d': // GS (Group Separator)
! 656: case '\x1e': // RS (Record Separator)
! 657: case '\x1f': // US (Unit Separator)
! 658: case '\x7f': // DEL
! 659: // 制御文字はその場で実行される。
! 660: PutcharPlain(ch);
! 661: // ステート完了ではないので break ではなく return。
! 662: return true;
! 663:
! 664: case '\x18': // CAN
! 665: // Cancel
! 666: putlog(3, "CAN");
! 667: break;
! 668: case '\x1a': // SUB
! 669: putlog(3, "SUB");
! 670: break;
! 671:
! 672: case ESC: // ESC
! 673: // ここまでのを捨てて新しく始める?らしい。
! 674: ResetState();
! 675: PutcharPlain(ch);
! 676: return true;
! 677:
1.1 root 678: case '0':
679: case '1':
680: case '2':
681: case '3':
682: case '4':
683: case '5':
684: case '6':
685: case '7':
686: case '8':
687: case '9':
688: case ';':
689: case '\"':
690: case '$':
691: case '>':
1.1.1.6 ! root 692: case '!':
1.1 root 693: seq.push_back(ch);
694: arg.push_back(ch);
695: return true;
696:
1.1.1.6 ! root 697: case '@': // ICH (Insert Character)
1.1 root 698: {
1.1.1.6 ! root 699: // カーソル位置に n 個空白を挿入。
! 700: // カーソル位置とそれ以降の文字は右にずれる。
! 701: // カーソル位置は変わらない。
! 702: // DECLRMM(左右マージン)の影響を受ける。
! 703: int n = GetArgDefault(1);
! 704: putlog(3, "ICH (Insert Char) %u", n);
! 705: int maxlen = margin_right - cur_x + 1;
! 706: n = std::min(n, maxlen);
! 707: memmove(&screen[cur_y * fullwidth + (cur_x + n)],
! 708: &screen[cur_y * fullwidth + cur_x],
! 709: (maxlen - n) * sizeof(screen[0]));
! 710: for (int i = 0; i < n; i++) {
! 711: Setchar(cur_x + i, cur_y, ' ' | cur_attr);
1.1 root 712: }
1.1.1.6 ! root 713: dirty.set(cur_y);
! 714: break;
1.1 root 715: }
716:
1.1.1.6 ! root 717: case 'A': // CUU
1.1 root 718: {
1.1.1.6 ! root 719: // カーソルを n 行上に移動。
! 720: // DECSTBM(スクロール領域)の影響を受ける。
! 721: int n = GetArgDefault(1);
! 722: putlog(3, "CUU (Cursor Up) %u", n);
! 723: LocateYinScroll(cur_y - n);
! 724: break;
1.1 root 725: }
726:
1.1.1.6 ! root 727: case 'B': // CUD
1.1 root 728: {
1.1.1.6 ! root 729: // カーソルを下に移動。
! 730: // DECSTBM(スクロール領域)の影響を受ける。
! 731: int n = GetArgDefault(1);
! 732: putlog(3, "CUD (Cursor Down) %u", n);
! 733: LocateYinScroll(cur_y + n);
! 734: break;
1.1 root 735: }
736:
1.1.1.6 ! root 737: case 'C': // CUF
1.1 root 738: {
1.1.1.6 ! root 739: // カーソルを右に移動。
! 740: // DECLRMM(左右マージン)の影響を受ける。
! 741: int n = GetArgDefault(1);
! 742: putlog(3, "CUF (Cursor Forward) %u", n);
! 743: LocateXinMargin(cur_x + n);
! 744: break;
1.1 root 745: }
746:
1.1.1.6 ! root 747: case 'D': // CUB
1.1 root 748: {
1.1.1.6 ! root 749: // カーソルを左に移動。
! 750: // DECLRMM(左右マージン)の影響を受ける。
! 751: int n = GetArgDefault(1);
! 752: putlog(3, "CUF (Cursor Backward) %u", n);
! 753: LocateXinMargin(cur_x - n);
! 754: break;
! 755: }
! 756:
! 757: case 'c': // DA
! 758: {
! 759: // デバイス属性を応答する。
! 760: // CSI [0] c なら Primary DA (DA1)。
! 761: // CSI > c なら Secondary DA (DA2)。
! 762: int argc = ParseArg();
! 763: if (argc > 0 && argv[0][0] == '>') {
! 764: // DA2はファームウェアバージョンとハードウェアオプション。
! 765: // どちらも関係ないので VT220('1'), ver 1.0 ('10'),
! 766: // no options ('0') とだけ返す。
! 767: putlog(3, "DA2 (Device Attributes)");
! 768: SendString("\x1b[>1;10;0c");
! 769: } else {
! 770: // とりあえず他は全部 DA1 ということにしておく。
! 771: // DA1 に対する応答は今のところ 62 (VT220) のみ。
! 772: putlog(3, "DA (Device Attribute)");
! 773: SendString("\x1b[?62c");
1.1 root 774: }
1.1.1.6 ! root 775: break;
! 776: }
! 777:
! 778: case 'f': // HVP
! 779: // HVP は VT52 互換性のため存在しており CUP と動作は同じらしい。
! 780: case 'H': // CUP
! 781: {
! 782: // CSI <y1> ; <x1> H … (x1, y1) にカーソルを移動。
! 783: // DECOM=0 なら画面左上が原点。
! 784: // DECOM=1 なら DECSTBM/DECLRMM の左上を原点とする。
! 785: int y1 = GetArgDefault(1);
! 786: int x1 = GetArgDefault(1);
! 787: putlog(3, "CUP (Cursor Position) row=%u col=%u", y1, x1);
! 788: int x = x1 - 1;
! 789: int y = y1 - 1;
! 790: if (decom) {
! 791: Locate(margin_left + x, scroll_top + y);
! 792: } else {
! 793: Locate(x, y);
1.1 root 794: }
1.1.1.6 ! root 795: break;
1.1 root 796: }
797:
1.1.1.6 ! root 798: case 'J': // ED (Erase Display)
1.1 root 799: {
800: // CSI [<n>] J
1.1.1.6 ! root 801: // <n> が 0 か省略なら、カーソル位置から画面右下までを消去。
1.1 root 802: // <n> が 1 なら、カーソル位置から左上までを消去。
803: // <n> が 2 なら、全画面消去。
1.1.1.6 ! root 804: // DECLRMM(左右マージン)/DECSTBM(スクロール領域)の影響は受けない。
! 805: // wrap は解除される。文字の属性も消去される。
1.1 root 806: // XXX SPA と ERM は未対応。
1.1.1.6 ! root 807: wrap_pending = false;
! 808: int pos = cur_y * fullwidth + cur_x;
! 809: int n = GetArg();
1.1 root 810: switch (n) {
1.1.1.6 ! root 811: default: // 未定義値は 0 とする実装が多いらしい。知らんけど
1.1 root 812: case 0:
1.1.1.6 ! root 813: putlog(3, "ED (Erase Display) Forward");
! 814: std::fill(screen.begin() + pos, screen.end(), ' ');
! 815: for (int y = cur_y; y < height; y++) {
! 816: dirty.set(y);
1.1 root 817: }
818: break;
819: case 1:
1.1.1.6 ! root 820: putlog(3, "ED (Erase Display) Backward");
! 821: std::fill(screen.begin(), screen.begin() + pos + 1, ' ');
1.1 root 822: for (int y = 0; y <= cur_y; y++) {
1.1.1.6 ! root 823: dirty.set(y);
1.1 root 824: }
825: break;
826: case 2:
1.1.1.6 ! root 827: putlog(3, "ED (Erase Display) All");
! 828: std::fill(screen.begin(), screen.end(), ' ');
! 829: dirty.set();
1.1 root 830: break;
831: }
1.1.1.6 ! root 832: break;
1.1 root 833: }
834:
1.1.1.6 ! root 835: case 'K': // EL (Erase Line)
1.1 root 836: {
837: // CSI [<n>] K
838: // <n> が 0 か省略なら、カーソル位置の右側を消去。
839: // <n> が 1 なら、カーソル位置の左側を消去。
840: // <n> が 2 なら、カーソル行を消去。
1.1.1.6 ! root 841: // DECLRMM (左右マージン) の影響は受けない。
! 842: int n = GetArg();
1.1 root 843: int x0;
844: int x1;
845: switch (n) {
1.1.1.6 ! root 846: default: // 未定義値は 0 とする実装が多いらしい。知らんけど
1.1 root 847: case 0:
1.1.1.6 ! root 848: putlog(3, "EL (Erase Line) Forward");
1.1 root 849: x0 = cur_x;
850: x1 = width - 1;
851: break;
852: case 1:
1.1.1.6 ! root 853: putlog(3, "EL (Erase Line) Backward");
1.1 root 854: x0 = 0;
855: x1 = cur_x;
856: break;
857: case 2:
1.1.1.6 ! root 858: putlog(3, "EL (Erase Line) All");
1.1 root 859: x0 = 0;
860: x1 = width - 1;
861: break;
862: }
863: for (int x = x0; x <= x1; x++) {
1.1.1.6 ! root 864: Setchar(x, cur_y, ' ' | cur_attr);
1.1 root 865: }
1.1.1.6 ! root 866: dirty.set(cur_y);
! 867: break;
! 868: }
! 869:
! 870: case 'L': // IL (Insert Line)
! 871: {
! 872: // CSI [<n>] L
! 873: // カーソル行とそれより下を1行ずつ下に移動し、
! 874: // カーソル行は空行にする。を n 回繰り返したもの。
! 875: // DECLRMM (左右マージン) の影響は受けない。
! 876: // DECSTBM (スクロール領域) の影響を受ける。
! 877: int n = GetArgDefault(1);
! 878: putlog(3, "IL (Insert Line) %u", n);
! 879:
! 880: // たぶん企図した時点で解除される?
! 881: wrap_pending = false;
! 882:
! 883: // カーソルがスクロール領域外にいたら何もしない。
! 884: if (cur_y < scroll_top || cur_y > scroll_btm) {
! 885: break;
! 886: }
! 887:
! 888: n = std::min(n, scroll_btm - cur_y + 1);
! 889: int y;
! 890: for (y = scroll_btm; y >= (cur_y + n); y--) {
! 891: CopyLineWhole(y, y - n);
! 892: }
! 893: for (; y >= cur_y; y--) {
! 894: EraseLineWhole(y);
! 895: }
! 896: // カーソルは先頭へ移動。
! 897: LocateX(0);
! 898: break;
1.1 root 899: }
900:
1.1.1.6 ! root 901: case 'M': // DL (Delete Line)
! 902: {
! 903: // CSI [<n>] M
! 904: // カーソル行を削除し、次行以降を1行ずつ上に移動して、
! 905: // 最下行を空行にする。を n 回繰り返したもの。
! 906: //
! 907: // DECLRMM (左右マージン) の影響は受けない。
! 908: // DECSTBM (スクロール領域) の影響を受ける。
! 909: int n = GetArgDefault(1);
! 910: putlog(3, "DL (Delete Line) %u", n);
! 911:
! 912: // たぶん企図した時点で解除される?
! 913: wrap_pending = false;
! 914:
! 915: // カーソルがスクロール領域外にいたら何もしない。
! 916: if (cur_y < scroll_top || cur_y > scroll_btm) {
! 917: break;
! 918: }
! 919:
! 920: n = std::min(n, scroll_btm + cur_y + 1);
! 921: int y;
! 922: for (y = cur_y; y < scroll_btm - n; y++) {
! 923: CopyLineWhole(y, y + n);
! 924: }
! 925: for (; y < scroll_btm; y++) {
! 926: EraseLineWhole(y);
! 927: }
! 928: // カーソルは先頭へ移動。
! 929: LocateX(0);
! 930: break;
! 931: }
! 932:
! 933: case 'g': // TBC (タブ設定をクリア)
! 934: // CSI [<n>] g
! 935: // <n> が 0 (か省略)なら、カーソル位置のタブをクリア。
! 936: // <n> が 3 ならすべてのタブをクリア。
! 937: return Unimpl(ch, "TBC");
! 938:
! 939: case 'i': // Printing
! 940: return Unimpl(ch, "Printing");
! 941:
! 942: case 'h': // SM (オプションを設定)
! 943: case 'l': // RM (オプションを解除)
! 944: {
! 945: // CSI <n> h
! 946: // CSI <n> l
! 947: bool val = (ch == 'h');
! 948: int argc = ParseArg();
! 949: for (int i = 0; i < argc; i++) {
! 950: int n = GetArg();
! 951: switch (n) {
! 952: case 2: // KAM (オンでキー入力を無視)
! 953: return Unimpl(ch, "KAM");
! 954: case 4: // IRM (Insert/Replace)
! 955: if (val) {
! 956: putlog(3, "Set IRM (to Insert)");
! 957: } else {
! 958: putlog(3, "Reset IRM (to Replace)");
! 959: }
! 960: insert_mode = val;
! 961: break;
! 962: case 12: // SRM (オンでローカルエコーを無効)
! 963: return Unimpl(ch, "SRM");
! 964: case 20: // LNM (オンで LF/VT/FF が行頭に移動)
! 965: putlog(3, "%s LNM(New Line Mode)", val ? "Set" : "Reset");
! 966: cr_on_lf = val;
! 967: break;
! 968: default:
! 969: putlog(2, "Ignore option %u %c", n, ch);
! 970: break;
! 971: }
! 972: }
! 973: (void)val;
! 974: break;
! 975: }
! 976:
! 977: case 'n': // DSR (Device Status Report)
! 978: {
! 979: int n = GetArg();
! 980: switch (n) {
! 981: case 5: // Request for terminal status
! 982: // 障害が起きているかどうかの問い合わせのようだ。応答は
! 983: // ESC [ 0 n … 障害なし
! 984: // ESC [ 3 n … 障害あり
! 985: putlog(3, "DSR 5(Request Terminal Status)");
! 986: SendString("\x1b[0n");
! 987: break;
! 988:
! 989: case 6: // Request for cursor position
! 990: {
! 991: // カーソル位置の問い合わせ。(ANSI 仕様?)
! 992: // DECOM (マージン原点) の影響を受ける。(vttest 6-3)
! 993: putlog(3, "DSR 6(Request Cursor Position)");
! 994: int x = cur_x;
! 995: int y = cur_y;
! 996: if (decom) {
! 997: x -= margin_left;
! 998: y -= scroll_top;
! 999: }
! 1000: SendString(string_format("\x1b[%u;%uR", y + 1, x + 1));
! 1001: break;
! 1002: }
! 1003:
! 1004: default:
! 1005: return false;
! 1006: }
! 1007: break;
! 1008: }
! 1009:
! 1010: case 'm': // SGR (Select Graphic Rendition)
1.1 root 1011: {
1012: // CSI 7 ; 37 m みたいにセミコロンで区切ってパラメータを複数書ける。
1013: // パラメータには 38:2:rr:gg:bb みたいにコロンで区切ったひとかたまり
1014: // もある。
1.1.1.6 ! root 1015: int argc = ParseArg();
! 1016: if (argc == 0) {
! 1017: argc++;
! 1018: }
! 1019: if (loglevel >= 3) {
! 1020: std::string msg;
! 1021: for (int j = 0; j < argc; j++) {
! 1022: int n = GetArg();
! 1023: msg += string_format("; %u", n);
! 1024: }
! 1025: putlogn("SGR %s", msg.c_str() + 2);
! 1026: // XXX Peek がないがインデックスを戻せばまた使える。
! 1027: argv_idx = 0;
! 1028: }
! 1029: for (int i = 0; i < argc; i++) {
! 1030: int n = GetArg();
1.1 root 1031: switch (n) {
1.1.1.6 ! root 1032: // VT220 で定義されているもの。
1.1 root 1033: case 0: // すべての属性を解除
1034: SetAttr(0);
1035: break;
1036: case 1: // ボールドをセット
1037: SetAttr(cur_attr | ATTR_BOLD);
1038: break;
1039: case 4: // 下線をセット
1040: SetAttr(cur_attr | ATTR_UNDERLINE);
1041: break;
1042: case 5: // 低速点滅をセット (対応予定なし)
1.1.1.6 ! root 1043: SetAttr(cur_attr | ATTR_BLINK);
1.1 root 1044: break;
1045: case 7: // 反転をセット
1046: SetAttr(cur_attr | ATTR_REVERSE);
1047: break;
1048: case 22: // ボールド(と低輝度)を解除
1049: SetAttr(cur_attr & ~ATTR_BOLD);
1050: break;
1051: case 24: // 下線を解除
1052: SetAttr(cur_attr & ~ATTR_UNDERLINE);
1053: break;
1054: case 25: // 点滅を解除
1.1.1.6 ! root 1055: SetAttr(cur_attr & ~ATTR_BLINK);
1.1 root 1056: break;
1057: case 27: // 反転を解除
1058: SetAttr(cur_attr & ~ATTR_REVERSE);
1059: break;
1.1.1.6 ! root 1060:
! 1061: // それ以降で拡張されたものに勝手に対応してみる。
! 1062:
! 1063: case 3: // イタリックをセット
! 1064: SetAttr(cur_attr | ATTR_ITALIC);
1.1 root 1065: break;
1.1.1.6 ! root 1066: case 9: // 打ち消し線をセット
! 1067: SetAttr(cur_attr | ATTR_STRIKE);
1.1 root 1068: break;
1.1.1.6 ! root 1069: case 23: // イタリックを解除
! 1070: SetAttr(cur_attr & ~ATTR_ITALIC);
1.1 root 1071: break;
1.1.1.6 ! root 1072: case 29: // 打ち消し線を解除
! 1073: SetAttr(cur_attr & ~ATTR_STRIKE);
1.1 root 1074: break;
1075: case 53: // 上線をセット
1076: SetAttr(cur_attr | ATTR_OVERLINE);
1077: break;
1078: case 55: // 上線を取り消し
1079: SetAttr(cur_attr & ~ATTR_OVERLINE);
1080: break;
1.1.1.6 ! root 1081:
! 1082: // 色も現状対応予定はないがメモも兼ねて並べておく。
! 1083: case 30 ... 37: // 文字色の設定
! 1084: case 38: // 文字色(拡張)を設定
! 1085: case 39: // 文字色をデフォルトに戻す
! 1086: case 40 ... 47: // 背景色を設定
! 1087: case 48: // 背景色(拡張)を設定
! 1088: case 49: // 背景色をデフォルトに戻す
1.1 root 1089: case 90 ... 97: // 文字色(高輝度)を設定
1090: case 100 ... 107: // 背景色(高輝度)を設定
1091: default:
1092: // 無視
1093: break;
1094: }
1.1.1.6 ! root 1095: }
! 1096: break;
! 1097: }
! 1098:
! 1099: case 'p': // DECSCL / DECSTR
! 1100: {
! 1101: int argc = ParseArg();
! 1102: if (argc == 1 && argv[0] == "!") {
! 1103: // DECSTR
! 1104: putlog(2, "DECSTR (Soft Terminal Reset)");
! 1105: ResetTerminalSoft();
! 1106: break;
! 1107: } else {
! 1108: // DECSCL
! 1109: return Unimpl(ch, "DECSCL");
! 1110: }
! 1111: }
! 1112:
! 1113: case 'P': // DCH (Delete Character)
! 1114: {
! 1115: // カーソル位置から n 文字削除。
! 1116: // 左へずらして、右に空白を挿入。
! 1117: // カーソル位置は変わらない。
! 1118: int n = GetArgDefault(1);
! 1119: putlog(3, "DCH (Delete Char) %u", n);
! 1120: int maxlen = margin_right - cur_x + 1;
! 1121: n = std::min(n, maxlen);
! 1122: memmove(&screen[cur_y * fullwidth + cur_x],
! 1123: &screen[cur_y * fullwidth + (cur_x + n)],
! 1124: (maxlen - n) * sizeof(screen[0]));
! 1125: for (int i = 0; i < n; i++) {
! 1126: Setchar(margin_right - i, cur_y, ' ' | cur_attr);
! 1127: }
! 1128: dirty.set(cur_y);
! 1129: break;
! 1130: }
! 1131:
! 1132: case 'r': // DECSTBM
! 1133: {
! 1134: // CSI <t1> ; <b1> r。
! 1135: // スクロール範囲を [t1, b1] に設定。
! 1136: int t1 = GetArg();
! 1137: int b1 = GetArg();
! 1138: if (t1 == 0 && b1 == 0) {
! 1139: // 省略 (0;0) なら解除。
! 1140: putlog(2, "DECSTBM (Set Top&Bottom Margin) Clear");
! 1141: ResetScroll();
! 1142: } else {
! 1143: if (t1 == 0) {
! 1144: t1 = 1;
! 1145: }
! 1146: if (b1 == 0) {
! 1147: b1 = 1;
! 1148: }
! 1149: putlog(2, "DECSTBM (Set Top&Bottom Margin) %u; %u", t1, b1);
! 1150: int top = t1 - 1;
! 1151: int btm = b1 - 1;
! 1152: // 無効値なら変更しない。
! 1153: if (top < 0 || top >= height) {
! 1154: break;
! 1155: }
! 1156: if (btm < 0 || btm >= height) {
1.1 root 1157: break;
1158: }
1.1.1.6 ! root 1159: if (top >= btm) {
1.1 root 1160: break;
1161: }
1.1.1.6 ! root 1162: scroll_top = top;
! 1163: scroll_btm = btm;
1.1 root 1164: }
1.1.1.6 ! root 1165: // そのホームポジションに移動。
! 1166: LocateXinMargin(0);
! 1167: LocateYinScroll(0);
! 1168: break;
1.1 root 1169: }
1170:
1.1.1.6 ! root 1171: case 'X': // ECH (Erase Character)
1.1 root 1172: {
1.1.1.6 ! root 1173: int n = GetArgDefault(1);
! 1174: putlog(3, "ECH (Erase Char) %u", n);
! 1175: int maxlen = margin_right - cur_x + 1;
! 1176: n = std::min(n, maxlen);
! 1177: for (int i = 0; i < n; i++) {
! 1178: Setchar(cur_x + i, cur_y, ' ' | cur_attr);
1.1 root 1179: }
1.1.1.6 ! root 1180: dirty.set(cur_y);
! 1181: break;
1.1 root 1182: }
1183:
1.1.1.6 ! root 1184: case 'y': // DECTST (Tests)
! 1185: return Unimpl(ch, "DECTST");
! 1186:
1.1 root 1187: case '?':
1188: seq.push_back(ch);
1189: state = ch;
1190: return true;
1191:
1192: default:
1193: return false;
1194: }
1.1.1.6 ! root 1195:
! 1196: ResetState();
! 1197: return true;
1.1 root 1198: }
1199:
1200: // CSI '?' の次の文字を処理する。
1201: // 処理すれば true、知らないシーケンスなので中止する場合は false を返す。
1202: bool
1203: ConsoleDevice::PutcharCSIQ(uint32 ch)
1204: {
1205: switch (ch) {
1.1.1.6 ! root 1206: case '\x00'
! 1207: ... '\x17':
! 1208: case '\x19': // EM
! 1209: case '\x1c': // FS (File Separator)
! 1210: case '\x1d': // GS (Group Separator)
! 1211: case '\x1e': // RS (Record Separator)
! 1212: case '\x1f': // US (Unit Separator)
! 1213: case '\x7f': // DEL
! 1214: // 制御文字はその場で実行される。
! 1215: PutcharPlain(ch);
! 1216: // ステート完了ではないので break ではなく return。
! 1217: return true;
! 1218:
! 1219: case '\x18': // CAN
! 1220: case '\x1a': // SUB
! 1221: // Cancel
! 1222: break;
! 1223:
! 1224: case ESC: // ESC
! 1225: // ここまでのを捨てて新しく始める?らしい。
! 1226: ResetState();
! 1227: PutcharPlain(ch);
! 1228: return true;
! 1229:
1.1 root 1230: case '0':
1231: case '1':
1232: case '2':
1233: case '3':
1234: case '4':
1235: case '5':
1236: case '6':
1237: case '7':
1238: case '8':
1239: case '9':
1240: case ';':
1241: case '\"':
1242: case '$':
1243: case '>':
1244: seq.push_back(ch);
1245: arg.push_back(ch);
1246: return true;
1247:
1.1.1.6 ! root 1248: case 'h': // DECSET (拡張オプションを設定)
! 1249: case 'l': // DECRST (拡張オプションを解除)
! 1250: {
1.1 root 1251: // CSI '?' <n> h
1252: // CSI '?' <n> l
1.1.1.6 ! root 1253: bool val = (ch == 'h');
! 1254: int argc = ParseArg();
! 1255: for (int i = 0; i < argc; i++) {
! 1256: int n = GetArg();
! 1257: switch (n) {
! 1258: case 1: // DECCKM
! 1259: // 上下左右キーの送信コード設定。
! 1260: if (val) {
! 1261: putlog(3, "Set DECCKM (Cursor App. Keycode)");
! 1262: } else {
! 1263: putlog(3, "Reset DECCKM (Cursor Keycode)");
! 1264: }
! 1265: keyboard->SetCKM(val);
! 1266: break;
! 1267:
! 1268: case 2: // DECANM (解除のみ)
! 1269: return Unimpl(ch, "DECANM");
! 1270:
! 1271: case 3: // DECCOLM (オンで 132 桁/オフで80桁)
! 1272: // 内部的に桁数を切り替える。(表示は80桁固定)
! 1273: if (val) {
! 1274: putlog(2, "Set DECCOLM (to 132 columns)");
! 1275: width = 132;
! 1276: } else {
! 1277: putlog(2, "Reset DECCOLM (to 80 columns)");
! 1278: width = 80;
! 1279: }
! 1280: ResetMargin();
! 1281: ResetScroll();
! 1282: Clear();
! 1283: Locate(0, 0);
! 1284: break;
! 1285:
! 1286: case 4: // DECSCLM (オンでスムーズスクロール)
! 1287: putlog(3, "%s DECSCLM (Smooth/Jump Scroll)",
! 1288: val ? "Set" : "Reset");
! 1289: // 対応不要?
! 1290: break;
! 1291:
! 1292: case 5: // DECSCNM (オンで明るい背景…)
! 1293: return Unimpl(ch, "DECSCNM");
! 1294:
! 1295: case 6: // DECOM
! 1296: putlog(3, "%s DECOM(Origin Mode)", val ? "Set" : "Reset");
! 1297: decom = val;
! 1298: break;
! 1299:
! 1300: case 7: // DECAWM (Auto Wrap)
! 1301: return Unimpl(ch, "DECAWM");
! 1302: case 8: // DECARM (Auto Repeat)
! 1303: return Unimpl(ch, "DECARM");
! 1304: case 18: // DECPFF (Print Form Feed)
! 1305: return Unimpl(ch, "DECPFF");
! 1306: case 19: // DECPEX (オンで MC/DECMC が DECSTBM の影響を受けない)
! 1307: return Unimpl(ch, "DECPEX");
! 1308:
! 1309: case 25: // DECTCEM
! 1310: putlog(3, "%s DECTCEM(Cursor Visible)", val ? "Set" : "Reset");
! 1311: cursor_visible = val;
! 1312: break;
! 1313:
! 1314: case 42: // DECNRCM (Character set National/Multinational)
! 1315: return Unimpl(ch, "DECRCM");
! 1316:
! 1317: case 68008:
! 1318: // パフォーマンス測定用のダミー。黙って無視する。
! 1319: break;
! 1320:
! 1321: default:
! 1322: // 知らないのは無視。
! 1323: putlog(2, "Ignore private option %u %c", n, ch);
! 1324: break;
! 1325: }
! 1326: }
! 1327: break;
! 1328: }
! 1329:
! 1330: case 'i': // Printing
! 1331: return Unimpl(ch, "Printing");
! 1332: case 'J': // DECSED (Selective Erase In Display)
! 1333: return Unimpl(ch, "DECSED");
! 1334: case 'K': // DECSEL (Selective Erase In Line)
! 1335: return Unimpl(ch, "DECSEL");
! 1336:
! 1337: case 'n': // DECDSR
! 1338: {
! 1339: int n = GetArg();
! 1340: switch (n) {
! 1341: case 15: // Request for printer status
! 1342: // プリンタの状態問い合わせ。
! 1343: // ここではプリンタなしを応答しておく。
! 1344: putlog(3, "DECDSR 15(Request Printer Status)");
! 1345: SendString("\x1b[?13n");
! 1346: break;
! 1347:
! 1348: case 25: // Request for UDK(User-Defined Key) status
! 1349: // UDK は実装していないので、常時初期値(Unlocked = 20) を返す。
! 1350: putlog(3, "DECDSR 25 (Request UDK Status)");
! 1351: SendString("\x1b[?20n");
! 1352: break;
! 1353:
! 1354: case 26: // Request for keyboard language
! 1355: // どの国(言語)のキーボードかを返す。
! 1356: // とりあえず North America。
! 1357: putlog(3, "DECDSR 26(Request Keyboard Language)");
! 1358: SendString("\x1b[?27;1n");
! 1359: break;
! 1360:
! 1361: default:
! 1362: return false;
! 1363: }
! 1364: break;
! 1365: }
! 1366:
! 1367: default:
! 1368: return false;
! 1369: }
! 1370:
! 1371: ResetState();
! 1372: return true;
! 1373: }
! 1374:
! 1375: // ESC '#' の次の文字を処理する。
! 1376: bool
! 1377: ConsoleDevice::PutcharSharp(uint32 ch)
! 1378: {
! 1379: switch (ch) {
! 1380: case '3': // DECDHL 上
! 1381: return Unimpl(ch, "DECDHL(hi)");
! 1382: case '4': // DECDHL 下
! 1383: return Unimpl(ch, "DECDHL(lo)");
! 1384: case '5': // DECSWL
! 1385: return Unimpl(ch, "DECSWL");
! 1386: case '6': // DECDWL
! 1387: return Unimpl(ch, "DECDWL");
! 1388:
! 1389: case '8': // DECALN (Screen Alignment Test)
! 1390: // 画面を全部 'E' で埋める。
! 1391: // メンテ用なのでたぶん属性無視だろうか。
! 1392: putlog(3, "DECALN (Screen Alignment Tests)");
! 1393: std::fill(screen.begin(), screen.end(), 'E');
! 1394: dirty.set();
! 1395: break;
! 1396:
! 1397: default:
! 1398: return false;
! 1399: }
! 1400:
! 1401: ResetState();
! 1402: return true;
! 1403: }
! 1404:
! 1405: // ESC '(' (G0) の次の文字を処理する。
! 1406: // 処理すれば true、知らないシーケンスなので中止する場合は false を返す。
! 1407: bool
! 1408: ConsoleDevice::PutcharG0(uint32 ch)
! 1409: {
! 1410: switch (ch) {
! 1411: case 'B': // ASCII
! 1412: putlog(3, "G0 ASCII");
! 1413: SetAttr(cur_attr & ~ATTR_DECSG);
! 1414: break;
! 1415:
! 1416: case '0': // DEC special graphics
! 1417: putlog(3, "G0 DEC Special Graphics)");
! 1418: SetAttr(cur_attr | ATTR_DECSG);
! 1419: break;
! 1420:
! 1421: // 各国語モード。同時にいずれか一つが有効らしい。いずれも対応しない。
! 1422: case 'A': // British
! 1423: case '4': // Dutch
! 1424: case 'C': // Finnish
! 1425: case '5': // Finnish
! 1426: case 'R': // French
! 1427: case 'Q': // French canadian
! 1428: case 'K': // German
! 1429: case 'Y': // Italian
! 1430: case 'E': // Norwegian/Danish
! 1431: case '6': // Norwegian/Danish
! 1432: case 'Z': // Spanish
! 1433: case 'H': // Swedish
! 1434: case '7': // Swedish
! 1435: case '=': // Swiss
! 1436: putlog(2, "Ignore national mode:%s", Dump(ch).c_str());
! 1437: break;
! 1438:
! 1439: case '<': // DEC Supplemental
! 1440: return Unimpl(ch, "DEC Supplemental");
1.1 root 1441:
1442: default:
1.1.1.6 ! root 1443: // VT220 にはない。
1.1 root 1444: return false;
1445: }
1.1.1.6 ! root 1446:
! 1447: ResetState();
! 1448: return true;
! 1449: }
! 1450:
! 1451: // 未実装シーケンスのログ表示をする。
! 1452: // 処理済みを表す true を返す。
! 1453: bool
! 1454: ConsoleDevice::Unimpl(uint32 ch, const char *name)
! 1455: {
! 1456: std::string msg = Dump(ch);
! 1457: if (name) {
! 1458: msg += " (";
! 1459: msg += name;
! 1460: msg += ')';
! 1461: }
! 1462: putlog(1, "%s (NOT IMPLEMENTED)", msg.c_str() + 1);
! 1463:
! 1464: ResetState();
! 1465: return true;
! 1466: }
! 1467:
! 1468: // argv を一つ取り出して整数にして返す。
! 1469: // 要素数が足りない場合、パラメータが空の場合、パラメータが数値に変換できない
! 1470: // 場合はいずれも defval を返す。
! 1471: // それをどう解釈するかは各シーケンスによる。
! 1472: int
! 1473: ConsoleDevice::GetArgDefault(int defval)
! 1474: {
! 1475: if (argv_idx < 0) {
! 1476: ParseArg();
! 1477: }
! 1478:
! 1479: // パラメータがない。
! 1480: if (argv_idx >= argv.size()) {
! 1481: return defval;
! 1482: }
! 1483: const std::string& a = argv[argv_idx++];
! 1484:
! 1485: // 空文字列。
! 1486: if (a.empty()) {
! 1487: return defval;
! 1488: }
! 1489:
! 1490: // '-' は arg に追加されないため負数にはならない。
! 1491: char *end;
! 1492: errno = 0;
! 1493: long lval = strtol(a.c_str(), &end, 10);
! 1494: if (errno == ERANGE || *end != '\0') {
! 1495: return defval;
! 1496: }
! 1497:
! 1498: if (lval == 0) {
! 1499: return defval;
! 1500: }
! 1501: return (int)lval;
! 1502: }
! 1503:
! 1504: // arg をセミコロンで分解して argv に代入(追加)する。
! 1505: // argv の要素数を返す。
! 1506: int
! 1507: ConsoleDevice::ParseArg()
! 1508: {
! 1509: const char *s = arg.c_str();
! 1510: while (*s) {
! 1511: auto pos = s - arg.c_str();
! 1512: const char *sep = strchr(s, ';');
! 1513: if (sep) {
! 1514: auto len = sep - s;
! 1515: argv.emplace_back(arg, pos, len);
! 1516: s = sep + 1;
! 1517: } else {
! 1518: argv.emplace_back(arg, pos);
! 1519: break;
! 1520: }
! 1521: }
! 1522:
! 1523: argv_idx = 0;
! 1524: return argv.size();
1.1 root 1525: }
1526:
1527: // 現在の seq を表示用文字列にして返す。
1528: std::string
1529: ConsoleDevice::Dump() const
1530: {
1531: return Dump(seq);
1532: }
1533:
1534: // 現在の seq と ch を表示用文字列にして返す。
1535: // 大抵の場合最後の1文字はまだ seq に入っていないのでこの形式があると便利。
1536: std::string
1537: ConsoleDevice::Dump(uint32 ch) const
1538: {
1539: std::vector<uint8> tmp = seq;
1540: tmp.push_back(ch);
1541: return Dump(tmp);
1542: }
1543:
1544: // エスケープシーケンスを表示用文字列にして返す。
1545: // 戻り文字列には先頭に空白が入っている。
1546: /*static*/ std::string
1547: ConsoleDevice::Dump(const std::vector<uint8>& src)
1548: {
1549: std::string dst;
1550:
1551: for (auto ch : src) {
1552: if (ch == ESC) {
1553: dst += " ESC";
1554: } else if (ch < ' ' || ch >= 0x7f) {
1555: dst += string_format(" \\x%02x", ch);
1556: } else if (ch == ' ') {
1557: dst += " ' '";
1558: } else {
1559: dst += ' ';
1560: dst += (char)ch;
1561: }
1562: }
1563: return dst;
1564: }
1565:
1566: // 画面を消去する。
1567: // カーソル位置は変わらない。属性もクリアされない。
1568: void
1569: ConsoleDevice::Clear()
1570: {
1571: std::fill(screen.begin(), screen.end(), ' ' | cur_attr);
1.1.1.4 root 1572: dirty.set();
1.1 root 1573: }
1574:
1.1.1.6 ! root 1575: // カーソルの X 座標を x に移動する。
! 1576: // x が物理画面外なら画面の左端か右端にクリップされる。
! 1577: // 左右マージンの影響は受けない。
1.1 root 1578: void
1579: ConsoleDevice::LocateX(int x)
1580: {
1.1.1.6 ! root 1581: wrap_pending = false;
! 1582:
! 1583: // 物理画面内 (左右マージンではなく) にクリップ。
1.1 root 1584: if (__predict_false(x < 0)) {
1585: x = 0;
1586: }
1587: if (__predict_false(x > width - 1)) {
1588: x = width - 1;
1589: }
1.1.1.6 ! root 1590:
1.1 root 1591: cur_x = x;
1.1.1.4 root 1592: dirty.set(cur_y);
1.1 root 1593: }
1594:
1.1.1.6 ! root 1595: // カーソルの Y 座標を y に移動する。
! 1596: // y が物理画面外なら画面の上端か下端にクリップされる。
! 1597: // スクロール領域の影響は受けない。
1.1 root 1598: void
1599: ConsoleDevice::LocateY(int y)
1600: {
1.1.1.6 ! root 1601: wrap_pending = false;
! 1602:
! 1603: // 物理画面内 (スクロール領域ではなく) にクリップ。
1.1 root 1604: if (__predict_false(y < 0)) {
1605: y = 0;
1606: }
1607: if (__predict_false(y > height - 1)) {
1608: y = height - 1;
1609: }
1.1.1.6 ! root 1610:
1.1.1.4 root 1611: dirty.set(cur_y);
1.1 root 1612: cur_y = y;
1.1.1.4 root 1613: dirty.set(cur_y);
1.1 root 1614: }
1615:
1.1.1.6 ! root 1616: // カーソルの X 座標を x に移動する。
! 1617: // x は常に画面左から数えるが、
! 1618: // 左右マージンを超える場合はマージンの左端か右端にクリップされる。
1.1 root 1619: void
1.1.1.6 ! root 1620: ConsoleDevice::LocateXinMargin(int x)
1.1 root 1621: {
1.1.1.6 ! root 1622: if (__predict_false(x < margin_left)) {
! 1623: x = margin_left;
1.1 root 1624: }
1.1.1.6 ! root 1625: if (__predict_false(x > margin_right)) {
! 1626: x = margin_right;
! 1627: }
! 1628: LocateX(x);
1.1 root 1629: }
1630:
1.1.1.6 ! root 1631: // カーソルの Y 座標を y に移動する。
! 1632: // y は常に画面全体で数えるが、
! 1633: // スクロール領域を超える場合はスクロール領域の上端か下端にクリップされる。
1.1 root 1634: void
1.1.1.6 ! root 1635: ConsoleDevice::LocateYinScroll(int y)
1.1 root 1636: {
1.1.1.6 ! root 1637: if (__predict_false(y < scroll_top)) {
! 1638: y = scroll_top;
! 1639: }
! 1640: if (__predict_false(y > scroll_btm)) {
! 1641: y = scroll_btm;
! 1642: }
! 1643: LocateY(y);
1.1 root 1644: }
1645:
1.1.1.6 ! root 1646: // src 行目を dst 行にコピーする。スクロールの下請け。
! 1647: // DECLRMM による左右マージンの影響は受けない。
! 1648: // DECSTBM によるスクロール領域は呼び出し側で管理すること
! 1649: // (基本どのスクロールも影響を受けるはず)。
1.1 root 1650: void
1.1.1.6 ! root 1651: ConsoleDevice::CopyLineWhole(int dst, int src)
1.1 root 1652: {
1.1.1.6 ! root 1653: assertmsg(0 <= src && src < height, "src=%d", src);
! 1654: assertmsg(0 <= dst && dst < height, "dst=%d", dst);
! 1655:
! 1656: memcpy(&screen[dst * fullwidth], &screen[src * fullwidth],
! 1657: fullwidth * sizeof(screen[0]));
! 1658: dirty.set(dst);
! 1659: }
! 1660:
! 1661: // src 行目を dst 行にコピーする。スクロールの下請け。
! 1662: // DECLRMM による左右マージン内のみコピーする。
! 1663: // DECSTBM によるスクロール領域は呼び出し側で管理すること
! 1664: // (基本どのスクロールも影響を受けるはず)。
! 1665: void
! 1666: ConsoleDevice::CopyLineMargin(int dst, int src)
! 1667: {
! 1668: assertmsg(0 <= src && src < height, "src=%d", src);
! 1669: assertmsg(0 <= dst && dst < height, "dst=%d", dst);
! 1670:
! 1671: int cols = margin_right - margin_left + 1;
! 1672: memcpy(&screen[dst * fullwidth + margin_left],
! 1673: &screen[src * fullwidth + margin_left],
! 1674: cols * sizeof(screen[0]));
! 1675: dirty.set(dst);
1.1 root 1676: }
1677:
1.1.1.6 ! root 1678: // y 行目の内容を消去する。
! 1679: // DECLRMM による左右マージンの影響は受けない。
! 1680: // DECSTBM は無関係。
1.1 root 1681: void
1.1.1.6 ! root 1682: ConsoleDevice::EraseLineWhole(int y)
1.1 root 1683: {
1.1.1.6 ! root 1684: assertmsg(0 <= y && y < height, "y=%d", y);
! 1685:
1.1 root 1686: for (int x = 0; x < width; x++) {
1.1.1.6 ! root 1687: Setchar(x, y, ' ' | cur_attr);
1.1 root 1688: }
1.1.1.6 ! root 1689: dirty.set(y);
1.1 root 1690: }
1691:
1.1.1.6 ! root 1692: // y 行目の内容を消去する。
! 1693: // DECLRMM による左右マージンの影響を受ける。
! 1694: // DECSTBM は無関係。
1.1 root 1695: void
1.1.1.6 ! root 1696: ConsoleDevice::EraseLineMargin(int y)
1.1 root 1697: {
1.1.1.6 ! root 1698: assertmsg(0 <= y && y < height, "y=%d", y);
! 1699:
! 1700: for (int x = margin_left; x <= margin_right; x++) {
! 1701: Setchar(x, y, ' ' | cur_attr);
1.1 root 1702: }
1.1.1.6 ! root 1703: dirty.set(y);
1.1 root 1704: }
1705:
1706: // 現在位置に、現在の属性で文字 ch を出力する。
1707: // 出力後カーソルは移動する。
1708: void
1.1.1.6 ! root 1709: ConsoleDevice::PutcharNormal(uint32 ch)
1.1 root 1710: {
1.1.1.6 ! root 1711: if (insert_mode) {
! 1712: // 折り返し待ちは解除される?
! 1713: wrap_pending = false;
! 1714:
! 1715: // カーソル以降を1つ右へ移動して、ここに1文字出力。
! 1716: memmove(&screen[cur_y * fullwidth + (cur_x + 1)],
! 1717: &screen[cur_y * fullwidth + cur_x],
! 1718: (margin_right - cur_x) * sizeof(screen[0]));
! 1719: Setchar(cur_x, cur_y, (ch | cur_attr));
! 1720: } else {
! 1721: // 折り返し待ちの時はこの文字を表示する前に改行。
! 1722: if (wrap_pending) {
! 1723: // wrap_pending は CR() で解除される。
! 1724: CR();
! 1725: LF();
! 1726: }
! 1727:
! 1728: // 1文字出力して、1つ右へ移動する。
! 1729: Setchar(cur_x, cur_y, (ch | cur_attr));
! 1730: cur_x++;
! 1731:
! 1732: // マージンの最終桁にいた場合は wrap pending を立てて、留まる。
! 1733: if (cur_x > margin_right) {
! 1734: wrap_pending = true;
! 1735: cur_x--;
! 1736: }
1.1 root 1737: }
1738:
1.1.1.6 ! root 1739: dirty.set(cur_y);
1.1 root 1740: }
1741:
1742: // (x, y) の位置に chattr を出力する。
1743: // 出力後カーソルは移動しない。
1744: // 現在の attr は参照しない (呼び出す側が指定すること)。
1745: void
1.1.1.6 ! root 1746: ConsoleDevice::Setchar(int x, int y, uint32 chattr)
1.1 root 1747: {
1.1.1.6 ! root 1748: int pos = y * fullwidth + x;
1.1 root 1749: assert(pos >= 0);
1.1.1.6 ! root 1750: assert(pos < screen.size());
1.1 root 1751: screen[pos] = chattr;
1752: }
1753:
1.1.1.6 ! root 1754: // TAB。
! 1755: // カーソルを次のタブ位置に移動。
! 1756: void
! 1757: ConsoleDevice::HT()
! 1758: {
! 1759: int x = cur_x;
! 1760: int tabstop = ((x / 8) + 1) * 8;
! 1761: if (tabstop > margin_right) {
! 1762: tabstop = margin_right;
! 1763: }
! 1764: for (; x < tabstop; x++) {
! 1765: PutcharNormal(' ');
! 1766: }
! 1767: }
! 1768:
! 1769: // 復帰。
! 1770: // DECLRMM による左右マージンの影響を受ける。
! 1771: void
! 1772: ConsoleDevice::CR()
! 1773: {
! 1774: LocateXinMargin(margin_left);
! 1775: }
! 1776:
! 1777: // 改行。
! 1778: // DECLRMM による左右マージンの影響を受ける。
! 1779: // DECSTBM によるスクロール領域の影響を受ける。
! 1780: void
! 1781: ConsoleDevice::LF()
! 1782: {
! 1783: wrap_pending = false;
! 1784:
! 1785: dirty.set(cur_y);
! 1786: cur_y++;
! 1787:
! 1788: // スクロール領域下端を出るなら画面をスクロールアップ。
! 1789: if (cur_y > scroll_btm) {
! 1790: for (int y = scroll_top + 1; y <= scroll_btm; y++) {
! 1791: CopyLineMargin(y - 1, y);
! 1792: }
! 1793: EraseLineMargin(scroll_btm);
! 1794: cur_y = scroll_btm;
! 1795: }
! 1796:
! 1797: // New Line Mode なら LF/VT/FF は行頭へ戻る。
! 1798: if (cr_on_lf) {
! 1799: CR();
! 1800: }
! 1801:
! 1802: dirty.set(cur_y);
! 1803: }
! 1804:
1.1 root 1805: // 現在の属性を設定する。
1806: // ほぼログ出力のため。
1807: void
1808: ConsoleDevice::SetAttr(uint32 new_attr)
1809: {
1810: if (cur_attr == new_attr) {
1811: return;
1812: }
1813: cur_attr = new_attr;
1814:
1815: if (log) {
1816: std::string buf;
1817: if ((cur_attr & ATTR_BOLD)) {
1818: buf += ",ATTR_BOLD";
1819: }
1820: if ((cur_attr & ATTR_ITALIC)) {
1821: buf += ",ATTR_ITALIC";
1822: }
1823: if ((cur_attr & ATTR_UNDERLINE)) {
1824: buf += ",ATTR_UNDERLINE";
1825: }
1826: if ((cur_attr & ATTR_STRIKE)) {
1827: buf += ",ATTR_STRIKE";
1828: }
1829: if ((cur_attr & ATTR_OVERLINE)) {
1830: buf += ",ATTR_OVERLINE";
1831: }
1832: if ((cur_attr & ATTR_DECSG)) {
1833: buf += ",ATTR_DECSG";
1834: }
1835: if ((cur_attr & ATTR_REVERSE)) {
1836: buf += ",ATTR_REVERSE";
1837: }
1838:
1839: if (buf.empty()) {
1840: buf = "<ATTR_NORMAL>";
1841: } else {
1842: buf[0] = '<';
1843: buf += '>';
1844: }
1845: fputs(buf.c_str(), log);
1846: fflush(log);
1847: }
1848: }
1849:
1.1.1.6 ! root 1850: // 文字列を送信。終端の '\0' は送らない。
! 1851: void
! 1852: ConsoleDevice::SendString(const std::string& str)
! 1853: {
! 1854: if (__predict_false(loglevel >= 2)) {
! 1855: // Dump() は vector を受け取るので…。
! 1856: std::vector<uint8> vec(str.length());
! 1857: for (int i = 0, end = str.length(); i < end; i++) {
! 1858: vec[i] = str[i];
! 1859: }
! 1860: putlogn("SendString%s", Dump(vec).c_str());
! 1861: }
! 1862:
! 1863: if (comdriver) {
! 1864: for (auto ch : str) {
! 1865: comdriver->EnqueueChar(ch);
! 1866: }
! 1867: }
! 1868: }
! 1869:
! 1870: // 現在の画面を文字列にして返す。
! 1871: std::string
! 1872: ConsoleDevice::ToString() const
! 1873: {
! 1874: std::string dump;
! 1875:
! 1876: for (int y = 0; y < height; y++) {
! 1877: const uint32 *s = &screen[y * fullwidth];
! 1878: const uint32 *e = s + width - 1;
! 1879: // 行末方向から空白でない文字を探す。
! 1880: for (; e >= s; e--) {
! 1881: uint8 ch = *e & 0xff;
! 1882: if (ch != ' ') {
! 1883: break;
! 1884: }
! 1885: }
! 1886: // 行頭からそこまでをコピー。
! 1887: for (; s <= e; s++) {
! 1888: uint8 ch = *s & 0xff;
! 1889: dump += (char)ch;
! 1890: }
! 1891: // 論理的に折り返してるかどうかの区別はつかない。
! 1892: dump += '\n';
! 1893: }
! 1894:
! 1895: return dump;
! 1896: }
! 1897:
1.1 root 1898: // 画面合成。
1899: // レンダリングスレッドから呼ばれる。
1900: bool
1901: ConsoleDevice::Render(BitmapRGBX& dst)
1902: {
1.1.1.6 ! root 1903: BitmapRGBX row(width * font_width, font_height);
1.1 root 1904: bool updated = false;
1905:
1906: // フチも含めて再描画。
1907: if (__predict_false(init_screen)) {
1908: init_screen = false;
1909: dst.Fill(palette[0]);
1910: updated = true;
1911: }
1912:
1913: // 行ごとに更新があるか調べる。
1.1.1.4 root 1914: bitsetH modified;
1.1 root 1915: {
1.1.1.4 root 1916: std::lock_guard<std::mutex> lock(mtx);
1.1 root 1917: modified = pending;
1.1.1.4 root 1918: pending.reset();
1.1 root 1919: }
1.1.1.4 root 1920: updated |= modified.any();
1.1 root 1921:
1922: const uint8 *cgrom8x16 = Builtin::CGROM8x16(0);
1923: for (uint y = 0; y < height; y++) {
1.1.1.4 root 1924: if (__predict_false(modified.test(y))) {
1.1.1.6 ! root 1925: row.Fill(palette[0]);
! 1926: const uint32 *s = &screen[y * fullwidth];
1.1 root 1927: for (uint x = 0; x < width; x++) {
1928: uint32 attr = *s++;
1.1.1.6 ! root 1929: uint32 ch = attr & 0xff;
1.1 root 1930: // 属性による反転。
1931: int paloff = 0;
1932: if ((attr & ATTR_REVERSE) != 0) {
1933: paloff ^= 1;
1934: }
1935: // カーソル位置は更に反転。
1.1.1.6 ! root 1936: if (cursor_visible && x == cur_x && y == cur_y) {
1.1 root 1937: paloff ^= 1;
1938: }
1939:
1.1.1.6 ! root 1940: // 点滅の滅の時はグリフを空白に差し替えて描画。
! 1941: if (__predict_false(blinking) && (attr & ATTR_BLINK)) {
! 1942: ch = ' ';
! 1943: attr &= ~ATTR_LINEMASK;
! 1944: }
! 1945:
1.1 root 1946: const uint8 *font;
1.1.1.6 ! root 1947: if (__predict_false(ch >= 0x80)) {
! 1948: font = &tofu8x16[0];
! 1949: } else if ((attr & ATTR_DECSG) && ('j' <= ch && ch <= 'x')) {
1.1 root 1950: font = &decsg8x16[(ch - 'j') * font_height];
1951: } else {
1.1.1.6 ! root 1952: // 0x5c は CGROM では円記号なのでバックスラッシュ(0x80)
! 1953: // にする。同様に 0x7e の ' ̄'(オーバーライン)をチルダ
! 1954: // (0x81) にする。
! 1955: if (ch == '\\') {
! 1956: ch = 0x80;
! 1957: } else if (ch == '~') {
! 1958: ch = 0x81;
! 1959: }
1.1 root 1960: font = &cgrom8x16[ch * font_height];
1961: }
1962: BitmapI1 bc1(font, font_width, font_height);
1963: if (__predict_false((attr & ATTR_ITALIC))) {
1964: bc1 = bc1.ConvertToItalic();
1965: }
1966: if (__predict_false((attr & ATTR_BOLD))) {
1967: bc1 = bc1.ConvertToBold();
1968: }
1969:
1.1.1.6 ! root 1970: uint px = x * font_width;
! 1971: row.DrawBitmapI1(px, 0, bc1, &palette[paloff]);
1.1 root 1972:
1973: // 下線、上線、取り消し線
1974: if (__predict_false((attr & ATTR_LINEMASK))) {
1975: Color fg = palette[paloff ^ 1];
1976: if ((attr & ATTR_OVERLINE)) {
1.1.1.6 ! root 1977: row.DrawLineH(fg, px, 0, px + font_width);
1.1 root 1978: }
1979: if ((attr & ATTR_STRIKE)) {
1.1.1.6 ! root 1980: row.DrawLineH(fg, px, 9, px + font_width);
1.1 root 1981: }
1982: if ((attr & ATTR_UNDERLINE)) {
1.1.1.6 ! root 1983: row.DrawLineH(fg, px, 14, px + font_width);
1.1 root 1984: }
1985: }
1986: }
1.1.1.6 ! root 1987:
! 1988: Rect rect(Padding, Padding + y * font_height,
! 1989: dst.GetWidth() - Padding * 2, font_height);
! 1990: dst.DrawBitmapStretch(rect, row);
1.1 root 1991: }
1992: }
1993:
1994: return updated;
1995: }
1996:
1997: #define B(x) (0b##x)
1998: #define B2(x) (B(x) >> 8), (B(x) & 0xff)
1999:
2000: // DEC Special Graphics Character Set フォント。(罫線のみ)
2001: /*static*/ const uint8 ConsoleDevice::decsg8x16[15 * font_height] = {
2002: B(00010000), // [0] j:┘
2003: B(00010000),
2004: B(00010000),
2005: B(00010000),
2006: B(00010000),
2007: B(00010000),
2008: B(00010000),
2009: B(11110000),
2010: B(00000000),
2011: B(00000000),
2012: B(00000000),
2013: B(00000000),
2014: B(00000000),
2015: B(00000000),
2016: B(00000000),
2017: B(00000000),
2018:
2019: B(00000000), // [1] k:┐
2020: B(00000000),
2021: B(00000000),
2022: B(00000000),
2023: B(00000000),
2024: B(00000000),
2025: B(00000000),
2026: B(11110000),
2027: B(00010000),
2028: B(00010000),
2029: B(00010000),
2030: B(00010000),
2031: B(00010000),
2032: B(00010000),
2033: B(00010000),
2034: B(00010000),
2035:
2036: B(00000000), // [2] l:┌
2037: B(00000000),
2038: B(00000000),
2039: B(00000000),
2040: B(00000000),
2041: B(00000000),
2042: B(00000000),
2043: B(00011111),
2044: B(00010000),
2045: B(00010000),
2046: B(00010000),
2047: B(00010000),
2048: B(00010000),
2049: B(00010000),
2050: B(00010000),
2051: B(00010000),
2052:
2053: B(00010000), // [3] m:└
2054: B(00010000),
2055: B(00010000),
2056: B(00010000),
2057: B(00010000),
2058: B(00010000),
2059: B(00010000),
2060: B(00011111),
2061: B(00000000),
2062: B(00000000),
2063: B(00000000),
2064: B(00000000),
2065: B(00000000),
2066: B(00000000),
2067: B(00000000),
2068: B(00000000),
2069:
2070: B(00010000), // [4] n:┼
2071: B(00010000),
2072: B(00010000),
2073: B(00010000),
2074: B(00010000),
2075: B(00010000),
2076: B(00010000),
2077: B(11111111),
2078: B(00010000),
2079: B(00010000),
2080: B(00010000),
2081: B(00010000),
2082: B(00010000),
2083: B(00010000),
2084: B(00010000),
2085: B(00010000),
2086:
2087: B(00000000), // [5] o:─(Scan1)
2088: B(11111111),
2089: B(00000000),
2090: B(00000000),
2091: B(00000000),
2092: B(00000000),
2093: B(00000000),
2094: B(00000000),
2095: B(00000000),
2096: B(00000000),
2097: B(00000000),
2098: B(00000000),
2099: B(00000000),
2100: B(00000000),
2101: B(00000000),
2102: B(00000000),
2103:
2104: B(00000000), // [6] p:─(Scan3)
2105: B(00000000),
2106: B(00000000),
2107: B(00000000),
2108: B(11111111),
2109: B(00000000),
2110: B(00000000),
2111: B(00000000),
2112: B(00000000),
2113: B(00000000),
2114: B(00000000),
2115: B(00000000),
2116: B(00000000),
2117: B(00000000),
2118: B(00000000),
2119: B(00000000),
2120:
2121: B(00000000), // [7] q:─(Scan5)
2122: B(00000000),
2123: B(00000000),
2124: B(00000000),
2125: B(00000000),
2126: B(00000000),
2127: B(00000000),
2128: B(11111111),
2129: B(00000000),
2130: B(00000000),
2131: B(00000000),
2132: B(00000000),
2133: B(00000000),
2134: B(00000000),
2135: B(00000000),
2136: B(00000000),
2137:
2138: B(00000000), // [8] r:─(Scan7)
2139: B(00000000),
2140: B(00000000),
2141: B(00000000),
2142: B(00000000),
2143: B(00000000),
2144: B(00000000),
2145: B(00000000),
2146: B(00000000),
2147: B(00000000),
2148: B(11111111),
2149: B(00000000),
2150: B(00000000),
2151: B(00000000),
2152: B(00000000),
2153: B(00000000),
2154:
2155: B(00000000), // [9] s:─(Scan9)
2156: B(00000000),
2157: B(00000000),
2158: B(00000000),
2159: B(00000000),
2160: B(00000000),
2161: B(00000000),
2162: B(00000000),
2163: B(00000000),
2164: B(00000000),
2165: B(00000000),
2166: B(00000000),
2167: B(00000000),
2168: B(11111111),
2169: B(00000000),
2170: B(00000000),
2171:
2172: B(00010000), // [10] t:├
2173: B(00010000),
2174: B(00010000),
2175: B(00010000),
2176: B(00010000),
2177: B(00010000),
2178: B(00010000),
2179: B(00011111),
2180: B(00010000),
2181: B(00010000),
2182: B(00010000),
2183: B(00010000),
2184: B(00010000),
2185: B(00010000),
2186: B(00010000),
2187: B(00010000),
2188:
2189: B(00010000), // [11] u:┤
2190: B(00010000),
2191: B(00010000),
2192: B(00010000),
2193: B(00010000),
2194: B(00010000),
2195: B(00010000),
2196: B(11110000),
2197: B(00010000),
2198: B(00010000),
2199: B(00010000),
2200: B(00010000),
2201: B(00010000),
2202: B(00010000),
2203: B(00010000),
2204: B(00010000),
2205:
2206: B(00010000), // [12] v:┴
2207: B(00010000),
2208: B(00010000),
2209: B(00010000),
2210: B(00010000),
2211: B(00010000),
2212: B(00010000),
2213: B(11111111),
2214: B(00000000),
2215: B(00000000),
2216: B(00000000),
2217: B(00000000),
2218: B(00000000),
2219: B(00000000),
2220: B(00000000),
2221: B(00000000),
2222:
2223: B(00000000), // [13] w:┬
2224: B(00000000),
2225: B(00000000),
2226: B(00000000),
2227: B(00000000),
2228: B(00000000),
2229: B(00000000),
2230: B(11111111),
2231: B(00010000),
2232: B(00010000),
2233: B(00010000),
2234: B(00010000),
2235: B(00010000),
2236: B(00010000),
2237: B(00010000),
2238: B(00010000),
2239:
2240: B(00010000), // [14] x:│
2241: B(00010000),
2242: B(00010000),
2243: B(00010000),
2244: B(00010000),
2245: B(00010000),
2246: B(00010000),
2247: B(00010000),
2248: B(00010000),
2249: B(00010000),
2250: B(00010000),
2251: B(00010000),
2252: B(00010000),
2253: B(00010000),
2254: B(00010000),
2255: B(00010000),
2256: };
2257:
1.1.1.6 ! root 2258: // 豆腐
! 2259: /*static*/ const uint8 ConsoleDevice::tofu8x16[font_height] = {
! 2260: B(00000000),
! 2261: B(11111110),
! 2262: B(10000010),
! 2263: B(10000110),
! 2264: B(10000110),
! 2265: B(10001010),
! 2266: B(10001010),
! 2267: B(10010010),
! 2268: B(10010010),
! 2269: B(10100010),
! 2270: B(10100010),
! 2271: B(11000010),
! 2272: B(11000010),
! 2273: B(10000010),
! 2274: B(11111110),
! 2275: B(00000000),
! 2276: };
! 2277:
1.1 root 2278:
2279: //
2280: // 入力担当デバイス
2281: //
2282:
2283: // コンストラクタ
2284: ConsoleKeyboard::ConsoleKeyboard()
2285: : inherited()
2286: {
1.1.1.6 ! root 2287: // Console 側から初期化されるはずだが、念のため。
! 2288: SetCKM(false);
1.1 root 2289: }
2290:
2291: // デストラクタ
2292: ConsoleKeyboard::~ConsoleKeyboard()
2293: {
2294: }
2295:
2296: // COM ドライバを接続。
2297: // (HostCOM の cons ドライバが呼ぶ)
2298: void
1.1.1.3 root 2299: ConsoleKeyboard::Attach(COMDriverConsole *comdriver_)
1.1 root 2300: {
2301: comdriver = comdriver_;
2302: }
2303:
2304: // 文字入力。
2305: // WXMainView から呼ばれる。
2306: void
2307: ConsoleKeyboard::CharInput(uint charcode)
2308: {
2309: if (comdriver == NULL) {
2310: return;
2311: }
2312:
2313: // charcode は 0x80 以上を独自に使っているので (keyboard.h 参照)、
2314: // それらをシリアルコンソールのエスケープに置き換えて送り込む。
2315: switch (charcode) {
2316: case CC_NUL:
2317: comdriver->EnqueueChar(0);
2318: break;
2319:
2320: case CC_F1 ... CC_F10:
2321: break;
2322:
1.1.1.6 ! root 2323: // 上下左右は Normal Cursor Mode なら ESC [ A/B/C/D、
! 2324: // Application Cursor Mode なら ESC O A/B/C/D を送る。
! 2325: // 拡張オプション 1 (DECCKM) で切り替える。
1.1 root 2326: case CC_up:
2327: comdriver->EnqueueChar(ESC);
1.1.1.6 ! root 2328: comdriver->EnqueueChar(ckm_char);
1.1 root 2329: comdriver->EnqueueChar('A');
2330: break;
2331:
2332: case CC_down:
2333: comdriver->EnqueueChar(ESC);
1.1.1.6 ! root 2334: comdriver->EnqueueChar(ckm_char);
1.1 root 2335: comdriver->EnqueueChar('B');
2336: break;
2337:
2338: case CC_right:
2339: comdriver->EnqueueChar(ESC);
1.1.1.6 ! root 2340: comdriver->EnqueueChar(ckm_char);
1.1 root 2341: comdriver->EnqueueChar('C');
2342: break;
2343:
2344: case CC_left:
2345: comdriver->EnqueueChar(ESC);
1.1.1.6 ! root 2346: comdriver->EnqueueChar(ckm_char);
1.1 root 2347: comdriver->EnqueueChar('D');
2348: break;
2349:
2350: default:
2351: comdriver->EnqueueChar(charcode);
2352: break;
2353: }
2354: }
1.1.1.6 ! root 2355:
! 2356: // DECCKM を設定/解除する。
! 2357: void
! 2358: ConsoleKeyboard::SetCKM(bool val)
! 2359: {
! 2360: if (val) {
! 2361: ckm_char = 'O';
! 2362: } else {
! 2363: ckm_char = '[';
! 2364: }
! 2365: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.