Annotation of nono/wx/wxlogmonitor.cpp, revision 1.1.1.5

1.1       root        1: //
                      2: // nono
1.1.1.3   root        3: // Copyright (C) 2020 nono project
                      4: // Licensed under nono-license.txt
1.1       root        5: //
                      6: 
                      7: #include "wxlogmonitor.h"
                      8: #include "logger.h"
                      9: 
                     10: //
                     11: // ログモニター
                     12: //
                     13: 
                     14: // イベントテーブル
                     15: wxBEGIN_EVENT_TABLE(WXLogMonitor, inherited)
1.1.1.4   root       16:        EVT_SIZE(WXLogMonitor::OnSize)
                     17:        EVT_SCROLL(WXLogMonitor::OnScroll)
1.1       root       18: wxEND_EVENT_TABLE()
                     19: 
                     20: // コンストラクタ
                     21: WXLogMonitor::WXLogMonitor(wxWindow *parent, wxWindowID id)
1.1.1.3   root       22:        : inherited(parent, id, _("Log"))
1.1       root       23: {
                     24:        col = 80;
                     25:        row = 40;
                     26: 
1.1.1.4   root       27:        // バックログは今の所固定長
                     28:        logbuf.resize(col * backlog, ' ');
                     29: 
                     30:        // →
                     31:        // +--+-----------------------+--+--+
                     32:        // |  |↓+------------------+ |  |  |
                     33:        // |  |  | 上余白(padding1) | |  |  |
                     34:        // |左|  +------------------+ |右|  |
                     35:        // |余|  | TextScreen       | |余| <- VScroll
                     36:        // |白|  +------------------+ |白|  |
                     37:        // |  |  | 下余白(padding2) | |  |  |
                     38:        // |  |  +------------------+ |  |  |
                     39:        // +--+-----------------------+--+--+
                     40: 
                     41:        topsizer = new wxBoxSizer(wxHORIZONTAL);
                     42:        // 左余白
                     43:        topsizer->AddSpacer(DefaultPadding);
                     44:        // 中列の縦 sizer
                     45:        auto *vbox = new wxBoxSizer(wxVERTICAL);
                     46:        topsizer->Add(vbox, 0, wxEXPAND);
                     47: 
                     48:        // パディングパネル(上下)
                     49:        // ウィンドウを1行ずつ伸縮させるためには、クライアント領域がフォント高さの
                     50:        // 整数倍になってないといけない。TextScreen はそれ自身が行の整数倍の大きさ
                     51:        // だが、上下の余白はその時点のフォントサイズによって変更する必要がある。
                     52:        // 先にサイズ不定のパネルだけ置いといて、フォントサイズ確定後に大きさを
                     53:        // 確定させる。DoSize() 参照。
                     54:        padding1 = new wxPanel(this, wxID_ANY);
                     55:        padding2 = new wxPanel(this, wxID_ANY);
                     56:        vbox->Add(padding1);
                     57:        screen = new WXTextScreen(this, *this);
                     58:        vbox->Add(screen, 1, wxEXPAND);
                     59:        vbox->Add(padding2);
                     60: 
                     61:        // 右余白
                     62:        topsizer->AddSpacer(DefaultPadding);
                     63:        // スクロールバー
                     64:        vscroll = new wxScrollBar(this, wxID_ANY, wxDefaultPosition,
                     65:                wxDefaultSize, wxSB_VERTICAL);
                     66:        topsizer->Add(vscroll, 0, wxEXPAND);
1.1.1.3   root       67: 
                     68:        SetSizer(topsizer);
1.1       root       69: 
1.1.1.4   root       70:        // ウィンドウサイズを確定させる
                     71:        DoSize();
                     72: 
                     73:        // スクロールのために (パネル領域での) MouseWheel イベントもここで
                     74:        // 受け取りたい。スクロールバー上のマウスホイール操作は最初から Scroll
                     75:        // イベントとして処理されているので問題ないが、パネル領域でのマウス
                     76:        // ホイール操作はここ(wxFrame)ではなくパネルに対して飛んでくる。
                     77:        // ここで一緒に処理したほうが楽なので、こちらに回す。
                     78:        screen->Connect(wxEVT_MOUSEWHEEL,
                     79:                wxMouseEventHandler(WXLogMonitor::OnMouseWheel), NULL, this);
1.1       root       80: }
                     81: 
                     82: // デストラクタ
                     83: WXLogMonitor::~WXLogMonitor()
                     84: {
                     85: }
                     86: 
1.1.1.4   root       87: // フォントサイズが確定したら、それによってウィンドウサイズを再計算する。
                     88: void
                     89: WXLogMonitor::DoSize()
                     90: {
                     91:        CalcVSize(0);
                     92: }
                     93: 
                     94: // サイズ変更イベント
                     95: void
                     96: WXLogMonitor::OnSize(wxSizeEvent& event)
                     97: {
                     98:        inherited::OnSize(event);
                     99: 
                    100:        // スクロールバーを再設定
                    101:        SetScroll();
                    102: }
                    103: 
                    104: // スクロールバーを再設定
                    105: void
                    106: WXLogMonitor::SetScroll()
                    107: {
                    108:        int pos;
                    109:        int thumbsize;
                    110:        int range;
                    111:        int pagesize;
                    112: 
                    113:        const wxSize& size = screen->GetSize();
                    114:        row = size.GetHeight();
                    115: 
                    116:        if (lines <= row) {
                    117:                pos = 0;
                    118:                range = row;
                    119:        } else {
                    120:                pos = (lines - row) - vpos;
                    121:                range = lines;
                    122:        }
                    123:        thumbsize = row;
                    124:        pagesize = thumbsize - 1;
                    125:        vscroll->SetScrollbar(pos, thumbsize, range, pagesize);
                    126: }
                    127: 
                    128: // マウスホイールイベント (WXTextScreen 上で起きたやつをこっちに回してある)
1.1       root      129: void
1.1.1.4   root      130: WXLogMonitor::OnMouseWheel(wxMouseEvent& event)
                    131: {
                    132:        // GetWheelRotation() は上(奥)向きに回すと +120、下(手前)に回すと -120。
                    133:        // どこの決まりか知らんけど、スクロールバーのほうはホイール1段階で3行
                    134:        // スクロールするのでそれに合わせる。
                    135:        int pos = vscroll->GetThumbPosition();
                    136:        pos = pos - event.GetWheelRotation() / 40;
                    137: 
                    138:        int maxpos = vscroll->GetRange() - vscroll->GetThumbSize();
                    139:        if (pos < 0)
                    140:                pos = 0;
                    141:        if (pos > maxpos)
                    142:                pos = maxpos;
                    143: 
                    144:        DoScroll(pos);
                    145: }
                    146: 
                    147: // スクロールイベント (全部入り)
                    148: void
                    149: WXLogMonitor::OnScroll(wxScrollEvent& event)
                    150: {
                    151:        // Thumb の上端の位置 (0 なら Thumb が上端に接している)
                    152:        DoScroll(event.GetPosition());
                    153: }
                    154: 
                    155: // スクロール処理 (OnScroll() と OnMouseWheel() から呼ばれる)
                    156: void
                    157: WXLogMonitor::DoScroll(int pos)
                    158: {
                    159:        // pos 即ちスクロールバーの Thumb の位置は上端の位置。
                    160:        // 対してログウィンドウでは下端を基準にしたほうが処理しやすい。
                    161:        // vpos = 0 だと Thumb が下に接していてパネルの最下行が最新行、
                    162:        // vpos = 1 だと Thumb が下から1段階分上にありパネルの最下行は最新行の
                    163:        // 一つ前の行、となる。
                    164:        //
                    165:        // Range=6              Range=6
                    166:        // Pos=4                Pos=0
                    167:        // ThumbSz=2    ThumbSz=2
                    168:        // vpos=0               vpos=4
                    169:        // △                  △
                    170:        // □                  ■
                    171:        // □                  ■
                    172:        // □                  □
                    173:        // □                  □
                    174:        // ■                  □
                    175:        // ■                  □
                    176:        // ▽                  ▽
                    177: 
                    178:        // パネル最下行に表示する行の最新行からのオフセット
                    179:        // (0 なら Thumb が下端に接している)
                    180:        vpos = vscroll->GetRange() - vscroll->GetThumbSize() - pos;
                    181: 
                    182:        // Thumb の位置を更新
                    183:        vscroll->SetThumbPosition(pos);
                    184: }
                    185: 
                    186: // モニターサイズ取得
                    187: nnSize
                    188: WXLogMonitor::GetMonitorSize()
                    189: {
                    190:        return nnSize(col, row);
                    191: }
                    192: 
                    193: // バックログから TextScreen へのコピー
                    194: #define CopyText(text, row)    do {                    \
                    195:        for (int i = 0; i < (row); i++) {               \
                    196:                text.Locate(0, y++);                            \
                    197:                for (int x = 0; x < col; x++) {         \
                    198:                        text.Putc(*s++);                                \
                    199:                }                                                                       \
                    200:        }                                                                               \
                    201: } while (0)
                    202: 
                    203: // モニターテキストを更新
                    204: void
                    205: WXLogMonitor::MonitorUpdate(TextScreen& text)
1.1       root      206: {
                    207:        char buf[1024];
                    208: 
1.1.1.4   root      209:        // どこでどうするかはあるけど、
                    210:        // とりあえず row は常に現在の TextScreen の高さということにしておく
                    211:        row = text.GetRow();
                    212: 
                    213:        // キューから読めるだけバックログに書き込む。
                    214:        // 読めるだけと言っても while ループで読めるだけ読んでしまうと、
                    215:        // 流速が早すぎた場合にこのループから抜けるタイミングがなくなる可能性が
                    216:        // (可能性としては) あるので、適当に1画面分くらいで打ち切る。
                    217:        // 取り込み残した分は次回取り込まれるし、その流速も上回るようだと
                    218:        // どのみち gLogger の固定長キューで落ちる。
                    219:        for (int i = 0; i < row; i++) {
                    220:                if (gLogger.Read(buf, sizeof(buf)) == false)
                    221:                        break;
1.1       root      222:                Append(buf);
                    223:        }
                    224: 
1.1.1.4   root      225:        text.Clear();
                    226: 
                    227:        // スクロールバー(vpos)による表示位置計算
                    228:        // vpos は行数、v は logbuf[] 先頭からのバイトオフセット
                    229:        int v = cursor - col * vpos;
                    230:        if (v < 0) {
                    231:                v += col * backlog;
1.1       root      232:        }
                    233: 
1.1.1.4   root      234:        // 表示位置(v)の Row 行分手前から順に表示
                    235:        int size = col * row;
                    236:        int y = 0;
                    237:        uint8 *s;
                    238:        if (v >= size) {
                    239:                // 折り返しなし
                    240:                s = &logbuf[v - size];
                    241:                CopyText(text, row);
                    242:        } else {
                    243:                // 折り返しあり
                    244:                int row1 = (size - v) / col;
                    245:                int row2 = row - row1;
                    246:                s = &logbuf[col * backlog - (size - v)];
                    247:                CopyText(text, row1);
                    248:                s = &logbuf[0];
                    249:                CopyText(text, row2);
                    250:        }
                    251: }
1.1       root      252: 
1.1.1.4   root      253: // 1行追加された時の共通処理
                    254: void
                    255: WXLogMonitor::IncLines()
                    256: {
                    257:        // カーソルを次行先頭に移動
                    258:        cursor += col;
                    259:        // バッファ末尾まで来たら折り返し
                    260:        if (cursor >= col * backlog) {
                    261:                cursor = 0;
1.1       root      262:        }
1.1.1.4   root      263: 
                    264:        // スクロールバーが下端にない時(バックログを見ている時)は
                    265:        // ログが追加されても現在位置をキープする。
                    266:        if (vpos > 0) {
                    267:                // 上端に達したら増加させない
                    268:                if (vpos < backlog - vscroll->GetThumbSize()) {
                    269:                        vpos++;
1.1       root      270:                }
                    271:        }
                    272: 
1.1.1.4   root      273:        // 有効行数を増やす
                    274:        if (lines < backlog) {
                    275:                lines++;
1.1       root      276: 
1.1.1.4   root      277:                // 行数が変わったのでスクロールバーを更新
                    278:                SetScroll();
                    279:        }
1.1       root      280: }
                    281: 
1.1.1.4   root      282: // バックログに行を追加。
1.1       root      283: //
1.1.1.4   root      284: // 引数の utfbuf は UTF-8 で改行コードのない1行ずつ。
                    285: // これをバックログの最新位置に書き込む。古い行は順次上書きされる。
1.1       root      286: //
1.1.1.4   root      287: // バックログは col * backlog バイトの Shift_JIS バッファ。
                    288: // 属性は持たず文字コードのみ。漢字は2バイト幅をとる。
                    289: // 横幅 col が固定なので書き込む時点で行数は表示用のそれと一致する。
1.1       root      290: //
1.1.1.4   root      291: // cursor はこれから書き込む位置の行頭。cursor は必ず行頭だけをポイント
                    292: // しながら移動する。
                    293: //
                    294: // logbuf    +------------------------
                    295: //           | :
                    296: //           | 前回書き込んだ行
                    297: // cursor -> | 今から書きこむ行 (保存されていた一番古い行)
                    298: //           :
                    299: //           |
                    300: //           +------------------------
1.1       root      301: void
                    302: WXLogMonitor::Append(const char *utfbuf)
                    303: {
                    304:        const char *sjis;
1.1.1.4   root      305:        const char *s;
                    306:        int x;
                    307:        bool iskanji;
1.1       root      308: 
                    309:        // 文字コードの変換
                    310:        wxString utfstr(utfbuf, wxConvUTF8);
                    311:        sjis = (const char *)utfstr.mb_str(conv);
                    312: 
1.1.1.4   root      313:        iskanji = false;
                    314:        s = sjis;
                    315:        x = 0;
                    316:        for (; *s; ) {
                    317:                // Shift_JIS の1文字目は上位ニブルが 8,9,E。
                    318:                // 最終桁で2バイト文字の1バイト目が来たら
                    319:                // 代わりに1文字空白を入れる (それによってこの後改行が起きる)
                    320:                if (iskanji == false &&
                    321:                    (int16)(0b00000000'11000010 << (*s >> 4)) < 0 &&
                    322:                        x >= col - 1)
                    323:                {
1.1.1.5 ! root      324:                        logbuf[cursor + x] = ' ';
1.1.1.4   root      325:                } else {
                    326:                        // そうでなければそのまま出力
                    327:                        logbuf[cursor + x] = *s++;
                    328:                        iskanji = false;
1.1       root      329:                }
1.1.1.4   root      330:                x++;
1.1       root      331: 
1.1.1.4   root      332:                // 行端まで来たら改行
                    333:                if (x >= col) {
                    334:                        IncLines();
                    335:                        // 行頭
                    336:                        x = 0;
1.1       root      337:                }
1.1.1.4   root      338:        }
1.1       root      339: 
1.1.1.4   root      340:        // 1行追加し終わって行頭でなければ、改行
                    341:        if (x > 0) {
                    342:                IncLines();
1.1       root      343:        }
                    344: }

unix.superglobalmegacorp.com

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