Annotation of nono/vm/console.cpp, revision 1.1.1.6

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.