Annotation of nono/host/logger.cpp, revision 1.1.1.1

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2025 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
                      8: // ログ
                      9: //
                     10: 
                     11: #include "logger.h"
                     12: #include "monitor.h"
                     13: #include "mythread.h"
                     14: #include "sjis.h"
                     15: #include "textscreen.h"
                     16: 
                     17: // コンストラクタ
                     18: Logger::Logger()
                     19:        : inherited(OBJ_LOGGER)
                     20: {
                     21:        monitor = gMonitorManager->Regist(ID_MONITOR_LOG, this);
                     22:        monitor->func = ToMonitorCallback(&Logger::MonitorUpdate);
                     23:        monitor->SetSize(80, 40);       // 初期サイズは適当
                     24: }
                     25: 
                     26: // デストラクタ
                     27: Logger::~Logger()
                     28: {
                     29:        TerminateThread();
                     30: }
                     31: 
                     32: // UTF-8 -> Shift_JIS 変換関数を登録する。GUI が登録する。
                     33: void
                     34: Logger::SetConverter(std::string (*conv)(const std::string&))
                     35: {
                     36:        utf8_to_sjis_converter = conv;
                     37: }
                     38: 
                     39: // スレッドの開始。
                     40: bool
                     41: Logger::StartThread()
                     42: {
                     43:        auto func = [this]() {
                     44:                PTHREAD_SETNAME("Logger");
                     45:                std::lock_guard<std::mutex> lock_sub(this->thread_starter);
                     46:                this->ThreadRun();
                     47:        };
                     48: 
                     49:        if (ResizeCol(monitor->GetCol()) == false) {
                     50:                return false;
                     51:        }
                     52: 
                     53:        // スレッド起動
                     54:        std::lock_guard<std::mutex> lock(thread_starter);
                     55: 
                     56:        try {
                     57:                thread.reset(new std::thread(func));
                     58:        } catch (...) { }
                     59:        if ((bool)thread == false) {
                     60:                warnx("Failed to initialize thread at %s", __method__);
                     61:                return false;
                     62:        }
                     63:        return true;
                     64: }
                     65: 
                     66: // スレッドを終了させる
                     67: void
                     68: Logger::TerminateThread()
                     69: {
                     70:        if ((bool)thread) {
                     71:                Terminate();
                     72: 
                     73:                if (thread->joinable()) {
                     74:                        thread->join();
                     75:                }
                     76: 
                     77:                thread.reset();
                     78:        }
                     79: }
                     80: 
                     81: // スレッドの終了を指示
                     82: void
                     83: Logger::Terminate()
                     84: {
                     85:        std::lock_guard<std::mutex> lock(queue_mtx);
                     86:        request |= REQ_TERMINATE;
                     87:        cv.notify_one();
                     88: }
                     89: 
                     90: // ログ書き込み。
                     91: // 他スレッドから呼ばれる。
                     92: void
                     93: Logger::Write(const char *str)
                     94: {
                     95:        std::lock_guard<std::mutex> lock(queue_mtx);
                     96:        queue.emplace_back(str);
                     97:        request |= REQ_QUEUE;
                     98:        cv.notify_one();
                     99: }
                    100: 
                    101: void
                    102: Logger::ThreadRun()
                    103: {
                    104:        SetThreadAffinityHint(AffinityClass::Light);
                    105: 
                    106:        for (;;) {
                    107:                std::string utf8str;
                    108: 
                    109:                // 何か起きるまで待つ。
                    110:                {
                    111:                        std::unique_lock<std::mutex> lock(queue_mtx);
                    112:                        cv.wait(lock, [&]{ return (request != 0); });
                    113: 
                    114:                        if (__predict_false((request & REQ_TERMINATE))) {
                    115:                                // 終了するので、他の条件は無視
                    116:                                break;
                    117:                        }
                    118:                        // ここは (request == REQ_QUEUE) が成立しているはず。
                    119:                        if (__predict_false(queue.empty())) {
                    120:                                request = 0;
                    121:                                continue;
                    122:                        } else {
                    123:                                utf8str = queue.front();
                    124:                                queue.pop_front();
                    125:                                if (queue.empty()) {
                    126:                                        request = 0;
                    127:                                }
                    128:                        }
                    129:                }
                    130: 
                    131:                if (__predict_true(utf8str.empty() == false)) {
                    132:                        DoLog(utf8str);
                    133:                }
                    134:        }
                    135: }
                    136: 
                    137: // 着信処理。
                    138: void
                    139: Logger::DoLog(const std::string& utf8str)
                    140: {
                    141:        // 必要なら標準出力にも出力
                    142:        if (use_stdout) {
                    143:                puts(utf8str.c_str());
                    144:        }
                    145: 
                    146:        // コンバータがなければここで終了 (CLI)。
                    147:        // GUI でも万が一 dispbuf がなければ、これ以上出来ることはない。
                    148:        if (__predict_false(utf8_to_sjis_converter == NULL)) {
                    149:                return;
                    150:        }
                    151:        if (__predict_false(dispbuf.empty())) {
                    152:                return;
                    153:        }
                    154: 
                    155:        // Shift_JIS に変換。
                    156:        std::string sjis_str = utf8_to_sjis_converter(utf8str);
                    157: 
                    158:        // 入力の1文字列が改行を含んでいれば行を分解。
                    159:        std::vector<std::string> lines = string_split(sjis_str, '\n');
                    160: 
                    161:        {
                    162:                std::lock_guard<std::mutex> lock(backend_mtx);
                    163: 
                    164:                // 1行ずつバックログに追加。
                    165:                for (const auto& str : lines) {
                    166:                        AddLog(str);
                    167:                }
                    168:        }
                    169: }
                    170: 
                    171: // バックログに str を追加。
                    172: // str は Shift_JIS で改行を含まないこと。
                    173: void
                    174: Logger::AddLog(const std::string& str)
                    175: {
                    176:        // バックログに追加。埋まっていれば古いのを追い出す。
                    177:        if (__predict_true(logs.size() >= maxlines)) {
                    178:                logs.pop_front();
                    179:        }
                    180:        logs.emplace_back(str);
                    181: 
                    182:        // ディスプレイバッファにレンダリング。
                    183:        AddDisplay(str);
                    184: }
                    185: 
                    186: // ディスプレイバッファにレンダリングする。
                    187: // TAB 文字は展開されず 0x09 のグリフが表示される。
                    188: // backend_mtx 内で呼ぶこと。
                    189: void
                    190: Logger::AddDisplay(const std::string& str)
                    191: {
                    192:        assert(dispbuf.empty() == false);
                    193: 
                    194:        uint8 *d = &dispbuf[current * col];
                    195:        uint x = 0;
                    196:        for (const char *s = str.c_str(); ; ) {
                    197:                uint8 c = s[x];
                    198:                if (__predict_false(c == '\0')) {
                    199:                        // 改行へ。
                    200:                } else if (SJIS::IsHankaku(c) || s[x + 1] == '\0') {
                    201:                        // 半角。(半角でない判定でも次が終端文字なら半角扱い)
                    202:                        x++;
                    203:                        if (__predict_true(x < col)) {
                    204:                                continue;
                    205:                        }
                    206:                } else if (x < col - 1) {
                    207:                        x += 2;
                    208:                        if (__predict_true(x < col)) {
                    209:                                continue;
                    210:                        }
                    211:                } else {
                    212:                        // 2バイト文字がはみ出すなら改行。
                    213:                }
                    214: 
                    215:                // s から d に x 文字出力して、改行。
                    216:                memcpy(d, s, x);
                    217:                if (x < col) {
                    218:                        // 余った右側は空白で埋める。
                    219:                        memset(d + x, ' ', col - x);
                    220:                }
                    221:                s += x;
                    222:                x = 0;
                    223: 
                    224:                // 行送り。
                    225:                current = (current + 1) % maxlines;
                    226:                d = &dispbuf[current * col];
                    227:                if (__predict_false(displines < maxlines)) {
                    228:                        displines++;
                    229:                }
                    230: 
                    231:                // 行を出力したあとで、改めて s が終端なら終了。
                    232:                if (*s == '\0') {
                    233:                        break;
                    234:                }
                    235:        }
                    236: }
                    237: 
                    238: // ログの表示桁数を変更(再設定)する。
                    239: // 0 なら表示用バッファなし。
                    240: bool
                    241: Logger::ResizeCol(int newcol)
                    242: {
                    243:        std::lock_guard<std::mutex> lock(backend_mtx);
                    244: 
                    245:        size_t buflen = maxlines * newcol;
                    246:        try {
                    247:                dispbuf.resize(buflen);
                    248:        } catch (...) {
                    249:                // もう出来ることはない…。
                    250:                warnx("%s: Failed to allocate %zu bytes", __method__, buflen);
                    251:                dispbuf.resize(0);
                    252:                return false;
                    253:        }
                    254: 
                    255:        col = newcol;
                    256: 
                    257:        if (col != 0) {
                    258:                // 今あるバッファを全部書き直す。
                    259:                current = 0;
                    260:                displines = 0;
                    261:                for (const auto& str : logs) {
                    262:                        AddDisplay(str);
                    263:                }
                    264:        }
                    265: 
                    266:        return true;
                    267: }
                    268: 
                    269: // screen.userdata はログの最新から何行手前を screen の底の行にコピーするか
                    270: // を示す。
                    271: // userdata = 0 なら screen の底が最新行。
                    272: // userdata = 1 なら1行古いほうに遡った状態、となる。
                    273: // screen.userdata + screen の行数が maxlines を超えてはいけない。
                    274: void
                    275: Logger::MonitorUpdate(Monitor *, TextScreen& screen)
                    276: {
                    277:        assertmsg(screen.userdata + screen.GetRow() <= maxlines,
                    278:                "userdata=%" PRIu64 " row=%d maxlines=%d",
                    279:                screen.userdata, screen.GetRow(), maxlines);
                    280:        assertmsg(screen.GetCol() == col,
                    281:                "GetCol=%d col=%d", screen.GetCol(), col);
                    282: 
                    283:        // ここのモニタは常に Ring モードで書き込む。
                    284:        // 本当はこれを呼び出す側が一度初期化しておくだけでいいのだが
                    285:        // ここでやるほうが簡単なので。
                    286:        screen.Mode = TextScreen::Ring;
                    287: 
                    288:        std::lock_guard<std::mutex> lock(backend_mtx);
                    289: 
                    290:        // 万が一 dispbuf が空なら出来ることはない。
                    291:        if (__predict_false(dispbuf.empty())) {
                    292:                return;
                    293:        }
                    294: 
                    295:        // nlines はコピーする行数。
                    296:        int nlines = std::min(displines, screen.GetRow());
                    297:        if (nlines < screen.GetRow()) {
                    298:                // ログが画面高さ分埋まってなければ、画面をクリア。
                    299:                screen.Clear();
                    300:        } else {
                    301:                // 全文字上書きするのでクリア不要。
                    302:                screen.Locate(0, 0);
                    303:        }
                    304: 
                    305:        // dispy は dispbuf のコピー開始位置 (行単位)。
                    306:        int dispy = current - (screen.userdata + nlines);
                    307:        dispy = (dispy + maxlines) % maxlines;
                    308: 
                    309:        // 1行ずつコピー。
                    310:        for (uint y = 0; y < nlines; y++) {
                    311:                uint8 *dp = &dispbuf[dispy * col];
                    312:                for (uint x = 0; x < col; x++) {
                    313:                        screen.Putc(*dp++);
                    314:                }
                    315:                dispy++;
                    316:                if (__predict_false(dispy >= maxlines)) {
                    317:                        dispy = 0;
                    318:                }
                    319:        }
                    320: }

unix.superglobalmegacorp.com

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