Annotation of nono/wx/wxstatuspanel.cpp, revision 1.1.1.2

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2021 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: // ステータスパネル
                      8: //
                      9: // 描画領域に対してフォントの開始位置は外枠から +4、内枠からなら +2。
                     10: // 横も同様。
                     11: //
                     12: // +0 ------ 外枠
                     13: // +1        アキ
                     14: // +2  +---- 内枠
                     15: // +3  |     アキ
                     16: // +4  | ここからフォント
                     17: 
                     18: #include "wxstatuspanel.h"
                     19: #include "wxcolor.h"
                     20: #include "wxmainview.h"
1.1.1.2 ! root       21: #include "wxversion.h"
1.1       root       22: #include "bitrev.h"
                     23: #include "netdriver.h"
                     24: #include "scheduler.h"
                     25: #include "spc.h"
                     26: 
                     27: WXStatusPanel *gStatusPanel;
                     28: 
                     29: wxBEGIN_EVENT_TABLE(WXStatusPanel, inherited)
                     30:        EVT_CLOSE(WXStatusPanel::OnClose)
                     31:        EVT_SHOW(WXStatusPanel::OnShow)
                     32:        EVT_SIZE(WXStatusPanel::OnSize)
                     33:        EVT_TIMER(wxID_ANY, WXStatusPanel::OnTimer)
                     34:        EVT_PAINT(WXStatusPanel::OnPaint)
                     35:        EVT_LEFT_DCLICK(WXStatusPanel::OnLeftDClick)
                     36: wxEND_EVENT_TABLE()
                     37: 
                     38: // ステータスパネルの更新頻度 [Hz]
                     39: static constexpr uint FPS = 10;
                     40: 
                     41: // style フラグ
                     42: // デフォルトは wxTAB_TRAVERSAL だがこれを外すので分かりづらい字面になる
                     43: static const long STATUS_PANEL_STYLE = 0;
                     44: 
                     45: // コンストラクタ
                     46: WXStatusPanel::WXStatusPanel(wxWindow *parent)
                     47:        : inherited(parent, STATUS_PANEL_STYLE)
                     48: {
                     49:        timer.SetOwner(this);
                     50: 
                     51:        // インジケータ用のパラメータを用意。
                     52:        InitIndicators();
                     53: 
                     54:        DoSize();
                     55: 
                     56:        // キー入力イベントをメインビューに転送。
                     57:        // このパネルへのキー入力は不要なのと、メインウィンドウ内のコントロール
                     58:        // フォーカスがこのステータスパネルとメインビューのどっちにあるかは
                     59:        // 分かりづらくて、こっちがフォーカス持ってしまうと (というか起動後は
                     60:        // こっちにある) VM にキー入力が出来ずに焦ることになるので、ここへの
                     61:        // キー入力イベントは全部メインビューに飛ばす。
                     62:        Connect(wxEVT_KEY_UP, wxKeyEventHandler(WXMainView::OnKeyUp), NULL,
                     63:                gMainView);
                     64:        Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(WXMainView::OnKeyDown), NULL,
                     65:                gMainView);
                     66: }
                     67: 
1.1.1.2 ! root       68: // デストラクタ
        !            69: WXStatusPanel::~WXStatusPanel()
        !            70: {
        !            71: }
        !            72: 
1.1       root       73: // クローズイベント
                     74: void
                     75: WXStatusPanel::OnClose(wxCloseEvent& event)
                     76: {
                     77:        // クローズする前に更新を止める。
                     78:        timer.Stop();
                     79: }
                     80: 
                     81: // フォントサイズ変更 (外部インタフェース)
                     82: void
                     83: WXStatusPanel::SetFontSize(FontId new_fontid)
                     84: {
                     85:        // 同じなら何もしない
                     86:        if (fontid == new_fontid) {
                     87:                return;
                     88:        }
                     89: 
                     90:        // フォントを変更
                     91:        UpdateFont(new_fontid);
                     92: 
                     93:        // サイズ変更処理
                     94:        DoSize();
                     95: }
                     96: 
                     97: // 表示/非表示変更イベント
                     98: // new した時は (デフォルトで表示状態なのでか状態に変更がないという扱いの
                     99: // ようで) イベントは飛んでこない。同様に new 直後に Show() を明示発行しても
                    100: // 状態に変更がないためイベントは飛んでこない。
                    101: // new 直後に Hide() すればイベントは飛んでくる。という動作をするようだ。
                    102: // そのため WXMainFrame 側で生成後に Show/Hide を起こしている。
                    103: void
                    104: WXStatusPanel::OnShow(wxShowEvent& event)
                    105: {
                    106:        if (event.IsShown()) {
                    107:                // 非表示→表示
                    108: 
                    109:                // 表示開始前に一度情報を取得
                    110:                UpdateStat();
                    111: 
                    112:                timer.Start(1000 / FPS);
                    113:        } else {
                    114:                // 表示→非表示
                    115: 
                    116:                timer.Stop();
                    117:        }
                    118: }
                    119: 
                    120: // サイズ変更処理
                    121: // サイズを変更したい時に呼ぶ (サイズが変更されたからではない)。
                    122: // ここではサイズは fontsize にのみ依存しているので、フォントサイズが
                    123: // 変化/確定した時に呼び出すこと。
                    124: void
                    125: WXStatusPanel::DoSize()
                    126: {
                    127:        assert(fontid != FontId::None);
                    128: 
                    129:        // コントロールの大きさを変更
                    130:        wxSize size(wxDefaultCoord, font_height + 8);
                    131:        SetClientSize(size);
                    132:        SetMinClientSize(size);
                    133: }
                    134: 
                    135: // サイズ変更イベント
                    136: void
                    137: WXStatusPanel::OnSize(wxSizeEvent& event)
                    138: {
                    139:        // Mac ではウィンドウ作成時に 0 以下の値が来ることがある
                    140:        // GTK では 1 で来ることがある
                    141:        const wxSize& size = event.GetSize();
                    142:        if (size.x <= 1 || size.y <= 1) {
                    143:                return;
                    144:        }
                    145: 
                    146:        // ビットマップ作成
                    147:        bitmap.Create(size.x, size.y);
                    148: 
                    149:        LayoutIndicators();
                    150: 
                    151:        // 再描画を指示
                    152:        Refresh();
                    153: }
                    154: 
                    155: // タイマーイベント
                    156: void
                    157: WXStatusPanel::OnTimer(wxTimerEvent& event)
                    158: {
                    159:        if (UpdateStat()) {
                    160:                Refresh();
                    161:        }
                    162: }
                    163: 
                    164: // 状態をチェック。
                    165: // 前回値からどれかでも変更があれば true を返す。
                    166: bool
                    167: WXStatusPanel::UpdateStat()
                    168: {
                    169:        bool updated = false;
                    170: 
1.1.1.2 ! root      171:        // store に value を代入。更新されていれば updated を立てる。
        !           172:        // value は一度だけ評価される。
        !           173:        auto MODIFY = [&](auto& store, auto value) {
        !           174:                bool rv = store != value;
        !           175:                updated |= rv;
        !           176:                store = value;
        !           177:                return rv;
        !           178:        };
        !           179: 
1.1       root      180:        // 電源オンか
1.1.1.2 ! root      181:        MODIFY(ispower, gScheduler->IsPower());
1.1       root      182: 
                    183:        // パフォーマンスカウンタ
1.1.1.2 ! root      184:        MODIFY(perf_mode, gScheduler->GetRunMode());
        !           185:        MODIFY(perf_rate, gScheduler->GetPerfCounter());
1.1       root      186: 
                    187:        // FD
                    188: 
                    189:        // SCSI アクセス
1.1.1.2 ! root      190:        MODIFY(scsi_bsy, scsibus->GetBSY());
        !           191:        // 転送フェーズで DataOut の時だけ書き込みとみなす
        !           192:        bool out = (scsibus->GetPhase() == SCSI::Phase::Transfer)
        !           193:                && (scsibus->GetXfer()  == SCSI::XferPhase::DataOut);
        !           194:        MODIFY(scsi_out, out);
1.1       root      195: 
                    196:        // ネットワーク
1.1.1.2 ! root      197:        // パケット数が変化したとき、
        !           198:        // およびその変化を監視しているアクティブ状態が変化したとき
        !           199:        // に変更があったとしたいので二重になっている
        !           200:        if (net_ok != NOT_AVAILABLE) {
        !           201:                MODIFY(net_tx_active, MODIFY(net_tx_pkts, gEthernet->GetTXPkts()));
        !           202:                MODIFY(net_rx_active, MODIFY(net_rx_pkts, gEthernet->GetRXPkts()));
        !           203:        }
1.1       root      204: 
                    205:        // 電源
                    206: 
                    207:        return updated;
                    208: }
                    209: 
                    210: // 描画イベント
                    211: void
                    212: WXStatusPanel::OnPaint(wxPaintEvent& event)
                    213: {
                    214:        // メモリ DC に描画
                    215:        wxMemoryDC memDC;
                    216:        memDC.SelectObject(bitmap);
                    217:        Draw(memDC);
                    218: 
                    219:        // 実画面 DC にコピー
                    220:        wxPaintDC dstDC(this);
                    221:        wxSize size = bitmap.GetSize();
                    222:        dstDC.Blit(0, 0, size.x, size.y, &memDC, 0, 0);
                    223: }
                    224: 
                    225: // 描画本体
                    226: void
                    227: WXStatusPanel::Draw(wxDC& dc)
                    228: {
                    229:        std::string text;
                    230: 
                    231:        // 背景はいつものグレー。
                    232:        wxSize size = GetSize();
                    233:        dc.SetPen(wxPen(BGPANEL));
                    234:        dc.SetBrush(wxBrush(BGPANEL));
                    235:        dc.DrawRectangle(wxPoint(0, 0), size);
                    236: 
                    237:        // なんとなく見栄えのため上下に薄い線を引く。
                    238:        // 四方を線で囲むとオーバーレイウィンドウのように見えるので上下だけ。
                    239:        dc.SetPen(wxPen(UD_GREY));
                    240:        dc.DrawLine(0, 0, size.x - 1, 0);
                    241:        dc.DrawLine(0, size.y - 1, size.x - 1, size.y - 1);
                    242: 
                    243:        // 各インジケータを描画
                    244:        for (auto& ind : indicators) {
                    245:                (this->*(ind.draw))(dc, ind);
                    246:        }
                    247: }
                    248: 
1.1.1.2 ! root      249: static const std::vector<int> data = {
        !           250:        0x0b, 0x06, 0x0b, 0x06, 0x00, 0x4c, 0x55, 0x4e, 0x41, 0x74,
        !           251:        0x69, 0x63, 0x20, 0x54, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x72,
        !           252: };
        !           253: 
1.1       root      254: // インジケータの初期化
                    255: void
                    256: WXStatusPanel::InitIndicators()
                    257: {
                    258:        // パフォーマンスカウンタ
                    259:        Indicator perf(">>>9999%", &WXStatusPanel::DrawPerf);
                    260:        perf.dclick = &WXStatusPanel::DClickPerf;
                    261:        indicators.emplace_back(perf);
                    262: 
                    263:        // FD
                    264: 
                    265:        // SCSI
                    266:        // SPC の接続済みデバイスリストから取得。
                    267:        // (ホストアダプタ2つ目のことは考えてない)
                    268:        const std::vector<SCSIDevice *>& conn = gSPC->GetConnectedDevices();
                    269:        for (const auto& dev : conn) {
                    270:                int id = dev->GetMyID();
                    271:                SCSI::DevType devtype = dev->GetDevType();
                    272: 
                    273:                std::string disp;
                    274:                switch (devtype) {
                    275:                 case SCSI::DevType::Initiator:
                    276:                        continue;
                    277:                 case SCSI::DevType::HD:
1.1.1.2 ! root      278:                 {
        !           279:                        disp = string_format("HD%d", id);
        !           280:                        const auto devhd = dynamic_cast<SCSIHD*>(dev);
        !           281:                        assert(devhd);
        !           282:                        // ライトプロテクト
        !           283:                        if (devhd->GetWriteProtect()) {
        !           284:                                disp += "\x02\x03";
        !           285:                        }
1.1       root      286:                        break;
1.1.1.2 ! root      287:                 }
1.1       root      288:                 default:
1.1.1.2 ! root      289:                        disp = string_format("??%d", id);
1.1       root      290:                        break;
                    291:                }
                    292: 
                    293:                indicators.emplace_back(disp, &WXStatusPanel::DrawSCSI, id);
                    294:        }
                    295:        // BSY 取得用
                    296:        scsibus = gSPC->GetSCSIBus();
                    297: 
                    298:        // ネットワーク
1.1.1.2 ! root      299:        if ((bool)gNetDriver) {
        !           300:                if (gNetDriver->IsOpen()) {
        !           301:                        net_ok = ENABLE;
        !           302:                } else {
        !           303:                        net_ok = DISABLE;
        !           304:                }
        !           305:                // \x1e は上向き矢印、\x1f は下向き矢印
        !           306:                indicators.emplace_back("LAN\x1e\x1f", &WXStatusPanel::DrawNet);
1.1       root      307:        } else {
1.1.1.2 ! root      308:                net_ok = NOT_AVAILABLE;
1.1       root      309:        }
                    310: 
                    311:        // 電源 LED。点灯ロジックが分からないのでハリボテ。
                    312:        // ロジックが分かった時のために一応 3 文字にしておく。
1.1.1.2 ! root      313:        indicators.emplace_back("POWER", &WXStatusPanel::DrawPower);
        !           314: 
        !           315:        // テスト用
        !           316:        if (__predict_false(0||(bool)*gHostInfo)) {
        !           317:                std::string s((const char *)GetParent()->GetLabel().mb_str());
        !           318:                int i = 0;
        !           319:                for (; i < 5; i++) {
        !           320:                        s[i] += data[i];
        !           321:                }
        !           322:                for (; i < 9; i++) {
        !           323:                        if (s[i] != data[i])
        !           324:                                return;
        !           325:                }
        !           326:                for (; i < data.size(); i++) {
        !           327:                        s.insert(s.begin() + i, data[i]);
        !           328:                }
        !           329:                GetParent()->SetLabel(s);
        !           330:        }
1.1       root      331: }
                    332: 
                    333: // インジケータのレイアウト
                    334: void
                    335: WXStatusPanel::LayoutIndicators()
                    336: {
                    337:        int x;
                    338:        int y;
                    339:        int w;
                    340:        int h;
                    341: 
                    342:        // まずは前から順に列挙して必要な幅を積算していく
                    343:        x = 0;
                    344:        for (auto& ind : indicators) {
                    345:                // 前の枠とは1つ開ける
                    346:                if (x != 0) {
                    347:                        x += 1;
                    348:                }
                    349: 
                    350:                // 上の枠はキャンバス上端から2px
                    351:                y = 2;
                    352:                // 4 は、左右の外枠(1*2) と 枠と文字の間(1*2)
                    353:                w = ind.text.length() * font_width + 2 + 2;
                    354:                // そこから上下の外枠と枠と文字の間
                    355:                h = font_height + 2 + 2;
                    356: 
                    357:                ind.rect = wxRect(x, y, w, h);
                    358:                x += w;
                    359:        }
                    360: 
                    361:        // 全員を一旦右寄せ
                    362:        wxSize sz = GetClientSize();
                    363:        int offsetx = sz.x - x;
                    364:        for (auto& ind : indicators) {
                    365:                ind.rect.Offset(offsetx, 0);
                    366:        }
                    367: 
                    368:        // そのうちパフォーマンスカウンタだけ中央寄せまで戻す
                    369:        if (indicators.size() > 0) {
                    370:                auto& p = indicators.front();
                    371:                x = (sz.x / 2) - (p.rect.GetWidth() / 2);
                    372:                if (p.rect.GetX() > x) {
                    373:                        p.rect.SetX(x);
                    374:                }
                    375:        }
                    376: }
                    377: 
                    378: // パフォーマンスカウンタ
                    379: void
                    380: WXStatusPanel::DrawPerf(wxDC& dc, const Indicator& ind)
                    381: {
                    382:        const wxRect& rect = ind.rect;
1.1.1.2 ! root      383:        char text[16];
1.1       root      384: 
                    385:        // 枠
                    386:        dc.SetPen(wxPen(UD_GREY));
                    387:        dc.SetBrush(wxBrush(BGPANEL));
                    388:        dc.DrawRectangle(rect);
                    389: 
                    390:        // 電源オフならここまで
                    391:        if (ispower == false) {
                    392:                return;
                    393:        }
                    394: 
1.1.1.2 ! root      395:        // 高速モードか通常モードかでいろいろ違う。
        !           396:        // アイコンは
        !           397:        // o 通常モード指示中ならアイコンなし
        !           398:        // o 高速モード指示中なら >> (2つ)
        !           399:        // o そのうち実際に高速動作なら  >>> (3つ)
        !           400:        // パフォーマンス表示は
        !           401:        // o 高速モード指示中なら倍率表記 (115% なら 1.2x)
        !           402:        // o 通常モード指示中ならパーセント表記
1.1       root      403:        if ((perf_mode & Scheduler::SCHED_SYNC) == 0) {
1.1.1.2 ! root      404:                // 高速モード指示
1.1       root      405:                SetTextForeColor(UD_RED);
1.1.1.2 ! root      406:                DrawStringSJIS(dc, rect.x + 2, rect.y + 2, "\x01");
        !           407:                DrawStringSJIS(dc, rect.x + 2 + font_width, rect.y + 2, "\x01");
1.1       root      408:                if (perf_mode == 0) {
1.1.1.2 ! root      409:                        // 高速走行中
        !           410:                        DrawStringSJIS(dc, rect.x + 2 + font_width * 2, rect.y + 2, "\x01");
1.1       root      411:                }
                    412:                SetTextForeColor(*wxBLACK);
                    413: 
1.1.1.2 ! root      414:                // 倍率表記は1桁少ないので、表示用に最下位桁を四捨五入
        !           415:                int rate = perf_rate + 5;
        !           416:                snprintf(text, sizeof(text), "%2d.%1dx",
        !           417:                        (rate / 100), (rate % 100) / 10);
        !           418:        } else {
        !           419:                // 通常モード指示
        !           420: 
        !           421:                snprintf(text, sizeof(text), "%4d%%", perf_rate);
        !           422:        }
        !           423:        DrawStringSJIS(dc, rect.x + 2 + font_width * 3, rect.y + 2, text);
1.1       root      424: }
                    425: 
                    426: // SCSI デバイス側のアクセスランプ
                    427: // 本体ではなく各 SCSI デバイス側 (外付け HDD の前面とか) にあるやつ。
                    428: void
                    429: WXStatusPanel::DrawSCSI(wxDC& dc, const Indicator& ind)
                    430: {
                    431:        wxColour bg;
                    432: 
                    433:        if ((scsi_bsy & (1U << ind.userdata)) != 0) {
1.1.1.2 ! root      434:                if (scsi_out) {
        !           435:                        bg = UD_RED;
        !           436:                } else {
        !           437:                        bg = UD_YELLOW_GREEN;
        !           438:                }
1.1       root      439:        } else {
                    440:                bg = BGPANEL;
                    441:        }
                    442: 
                    443:        DrawTextLED(dc, ind, *wxBLACK, bg);
                    444: }
                    445: 
                    446: // ネットワーク(LAN)
                    447: void
                    448: WXStatusPanel::DrawNet(wxDC& dc, const Indicator& ind)
                    449: {
                    450:        wxColour fg;
                    451:        wxColour bg = BGPANEL;
1.1.1.2 ! root      452:        const wxRect& rect = ind.rect;
1.1       root      453: 
                    454:        if (net_ok) {
                    455:                fg = *wxBLACK;
1.1.1.2 ! root      456:                if (net_tx_active || net_rx_active) {
        !           457:                        bg = UD_YELLOW_GREEN;
        !           458:                }
1.1       root      459:        } else {
                    460:                fg = UD_GREY;
                    461:        }
                    462: 
1.1.1.2 ! root      463:        // 枠
        !           464:        dc.SetPen(wxPen(UD_GREY));
        !           465:        dc.SetBrush(wxBrush(bg));
        !           466:        dc.DrawRectangle(rect);
        !           467: 
        !           468:        int x = rect.x + 2;
        !           469:        int y = rect.y + 2;
        !           470: 
        !           471:        for (int i = 0; i < ind.text.size(); i++) {
        !           472:                auto c = ind.text[i];
        !           473:                switch (c) {
        !           474:                 case '\x1e':   // 上向き矢印
        !           475:                        if (net_tx_active) {
        !           476:                                fg = *wxBLACK;
        !           477:                        } else {
        !           478:                                fg = UD_GREY;
        !           479:                        }
        !           480:                        break;
        !           481:                 case '\x1f':   // 下向き矢印
        !           482:                        if (net_rx_active) {
        !           483:                                fg = *wxBLACK;
        !           484:                        } else {
        !           485:                                fg = UD_GREY;
        !           486:                        }
        !           487:                        break;
        !           488:                }
        !           489:                SetTextColor(fg, bg);
        !           490:                DrawChar1(dc, x, y, c);
        !           491:                x += font_width;
        !           492:        }
        !           493:        ResetTextColor();
1.1       root      494: }
                    495: 
                    496: // 電源LED?
                    497: void
                    498: WXStatusPanel::DrawPower(wxDC& dc, const Indicator& ind)
                    499: {
                    500:        DrawTextLED(dc, ind, *wxBLACK, UD_YELLOW_GREEN);
                    501: }
                    502: 
                    503: // 共通の描画処理。
                    504: // ind.text を前景色 fg、背景色 bg で描画するだけの場合。
                    505: void
                    506: WXStatusPanel::DrawTextLED(wxDC& dc, const Indicator& ind,
                    507:        const wxColour& fg, const wxColour& bg)
                    508: {
                    509:        const wxRect& rect = ind.rect;
                    510: 
                    511:        // 枠
                    512:        dc.SetPen(wxPen(UD_GREY));
                    513:        dc.SetBrush(wxBrush(bg));
                    514:        dc.DrawRectangle(rect);
                    515: 
                    516:        SetTextColor(fg, bg);
                    517:        DrawStringSJIS(dc, rect.x + 2, rect.y + 2, ind.text.c_str());
                    518:        ResetTextColor();
                    519: }
                    520: 
                    521: // 左ダブルクリックイベント
                    522: void
                    523: WXStatusPanel::OnLeftDClick(wxMouseEvent& event)
                    524: {
                    525:        const wxPoint& pt = event.GetPosition();
                    526: 
                    527:        for (auto& ind : indicators) {
                    528:                // dclick を処理できる枠内なら
                    529:                if (ind.rect.Contains(pt) && ind.dclick != NULL) {
                    530:                        (this->*(ind.dclick))(ind);
                    531:                        break;
                    532:                }
                    533:        }
                    534: }
                    535: 
                    536: // パフォーマンスカウンタ枠へのダブルクリック
                    537: void
                    538: WXStatusPanel::DClickPerf(const Indicator& ind)
                    539: {
                    540:        // モードを切り替える
                    541:        gScheduler->SetFullSpeed(!gScheduler->GetFullSpeed());
                    542: }

unix.superglobalmegacorp.com

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