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

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2021 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
1.1.1.4   root        7: //
1.1       root        8: // ステータスパネル
                      9: //
1.1.1.4   root       10: 
1.1       root       11: // 描画領域に対してフォントの開始位置は外枠から +4、内枠からなら +2。
                     12: // 横も同様。
                     13: //
                     14: // +0 ------ 外枠
                     15: // +1        アキ
                     16: // +2  +---- 内枠
                     17: // +3  |     アキ
                     18: // +4  | ここからフォント
                     19: 
                     20: #include "wxstatuspanel.h"
1.1.1.4   root       21: #include "fontmanager.h"
1.1       root       22: #include "wxcolor.h"
1.1.1.3   root       23: #include "wxmainframe.h"
1.1       root       24: #include "wxmainview.h"
1.1.1.3   root       25: #include "wxuimessage.h"
1.1.1.5   root       26: #include "fdc.h"
1.1.1.4   root       27: #include "hostnet.h"
1.1.1.14! root       28: #include "mpu.h"
1.1.1.6   root       29: #include "newsctlr.h"
1.1.1.4   root       30: #include "power.h"
1.1.1.9   root       31: #include "scsi.h"
1.1.1.12  root       32: #include "scsidomain.h"
1.1.1.6   root       33: #include "syncer.h"
1.1.1.8   root       34: #include "virtio_block.h"
1.1.1.12  root       35: #include "virtio_scsi.h"
1.1       root       36: 
1.1.1.9   root       37: //
                     38: // インジケータ情報 (ほぼ構造体)
                     39: //
                     40: class Indicator
                     41: {
                     42:  public:
                     43:        // インジケータの識別子
                     44:        enum {
                     45:                NONE = 0,
                     46:                PERF,
                     47:                POWER,
                     48:                SCSI0,
                     49:                SCSI1,
                     50:                SCSI2,
                     51:                SCSI3,
                     52:                SCSI4,
                     53:                SCSI5,
                     54:                SCSI6,
                     55:                SCSI7,
                     56:                VBLK0,
                     57:                VBLK1,
                     58:                VBLK2,
                     59:                VBLK3,
                     60:                VBLK4,
                     61:                VBLK5,
                     62:                VBLK6,
                     63:                VBLK7,
                     64:                LAN0,
                     65:                LAN1,
                     66:                FDD0,
                     67:                FDD1,
                     68:                FDD2,
                     69:                FDD3,
                     70:                NEWSLED1,
                     71:                NEWSLED2,
                     72:        };
                     73:  protected:
                     74:        using dispfunc_t = void (WXStatusPanel::*)(const Indicator *);
                     75:        using eventfunc_t = void (WXStatusPanel::*)(const Indicator *);
                     76:  public:
                     77:        Indicator() { }
                     78:        // 文字列の長さだけ指定する場合
                     79:        Indicator(int id_, dispfunc_t draw_, int minlen_, Status *stat_) {
                     80:                id = id_;
                     81:                draw = draw_;
                     82:                stat = stat_;
                     83:                minlen = minlen_;
                     84:        }
                     85:        // 文字列で指定する場合
                     86:        Indicator(int id_, dispfunc_t draw_, const std::string& text_,
                     87:                Status *stat_)
                     88:                : Indicator(id_, draw_, text_.length(), stat_)
                     89:        {
                     90:                text = text_;
                     91:        }
                     92:        virtual ~Indicator();
                     93: 
                     94:        // インジケータ識別子
                     95:        int id {};
                     96: 
                     97:        // 対応する状態
                     98:        Status *stat {};
                     99: 
                    100:        // 枠内の最小文字列長。
                    101:        // 枠の大きさはこの文字列長と text.length() の長いほうで描画される。
                    102:        int minlen {};
                    103: 
                    104:        // 表示する文字列。
                    105:        std::string text {};
                    106: 
                    107:        // 描画関数 (必須)
                    108:        dispfunc_t draw {};
                    109: 
                    110:        // ダブルクリックの処理関数 (必要なら)
                    111:        eventfunc_t dclick {};
                    112: 
                    113:        // コンテキストメニューの処理関数 (必要なら)
                    114:        eventfunc_t contextmenu {};
                    115: 
                    116:        // 対応するデバイス (必要なら)
                    117:        Device *dev {};
                    118: 
                    119:        // ToolTip を表示するためのダミーパネル
                    120:        wxPanel *panel {};
                    121: 
                    122:        // 枠の位置と大きさは panel.GetRect() で取得できる (実行時に計算する)
                    123:        Rect rect {};
                    124: 
                    125:  public:
                    126:        // SCSI なら SCSI ID を返す。そうでなければ -1 を返す。
                    127:        int GetSCSIID() const;
                    128: 
                    129:        // FDD ならユニット番号を返す。そうでなければ -1 を返す。
                    130:        int GetFDUnit() const;
                    131: };
                    132: 
                    133: // デストラクタ
                    134: Indicator::~Indicator()
                    135: {
                    136: }
                    137: 
                    138: // SCSI なら SCSI ID を返す。そうでなければ -1 を返す。
                    139: int
                    140: Indicator::GetSCSIID() const
                    141: {
                    142:        int scsiid = id - SCSI0;
                    143:        if (0 <= scsiid && scsiid <= 7) {
                    144:                return scsiid;
                    145:        } else {
                    146:                return -1;
                    147:        }
                    148: }
                    149: 
                    150: // FDD ならユニット番号を返す。そうでなければ -1 を返す。
                    151: int
                    152: Indicator::GetFDUnit() const
                    153: {
                    154:        int unit = id - FDD0;
                    155:        if (0 <= unit && unit <= 3) {
                    156:                return unit;
                    157:        } else {
                    158:                return -1;
                    159:        }
                    160: }
                    161: 
                    162: 
                    163: //
                    164: // ステータスパネル
                    165: //
                    166: 
1.1       root      167: wxBEGIN_EVENT_TABLE(WXStatusPanel, inherited)
                    168:        EVT_CLOSE(WXStatusPanel::OnClose)
                    169:        EVT_SHOW(WXStatusPanel::OnShow)
                    170:        EVT_TIMER(wxID_ANY, WXStatusPanel::OnTimer)
                    171:        EVT_LEFT_DCLICK(WXStatusPanel::OnLeftDClick)
1.1.1.3   root      172:        EVT_CONTEXT_MENU(WXStatusPanel::OnContextMenu)
1.1       root      173: wxEND_EVENT_TABLE()
                    174: 
                    175: // ステータスパネルの更新頻度 [Hz]
                    176: static constexpr uint FPS = 10;
                    177: 
                    178: // style フラグ
                    179: // デフォルトは wxTAB_TRAVERSAL だがこれを外すので分かりづらい字面になる
                    180: static const long STATUS_PANEL_STYLE = 0;
                    181: 
                    182: // コンストラクタ
                    183: WXStatusPanel::WXStatusPanel(wxWindow *parent)
1.1.1.6   root      184:        : inherited(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
                    185:                STATUS_PANEL_STYLE)
1.1       root      186: {
1.1.1.10  root      187:        SetName("WXStatusPanel");
                    188: 
1.1       root      189:        timer.SetOwner(this);
                    190: 
1.1.1.7   root      191:        power = GetPowerDevice();
                    192:        syncer = GetSyncer();
                    193: 
1.1       root      194:        // インジケータ用のパラメータを用意。
                    195:        InitIndicators();
                    196: 
1.1.1.13  root      197:        SetAutoLayout(true);
1.1.1.4   root      198:        FontChanged();
1.1       root      199: 
                    200:        // キー入力イベントをメインビューに転送。
                    201:        // このパネルへのキー入力は不要なのと、メインウィンドウ内のコントロール
                    202:        // フォーカスがこのステータスパネルとメインビューのどっちにあるかは
                    203:        // 分かりづらくて、こっちがフォーカス持ってしまうと (というか起動後は
                    204:        // こっちにある) VM にキー入力が出来ずに焦ることになるので、ここへの
                    205:        // キー入力イベントは全部メインビューに飛ばす。
1.1.1.6   root      206:        auto mainframe = dynamic_cast<WXMainFrame*>(GetParent());
                    207:        auto mainview = mainframe->GetMainView();
1.1       root      208:        Connect(wxEVT_KEY_UP, wxKeyEventHandler(WXMainView::OnKeyUp), NULL,
1.1.1.6   root      209:                mainview);
1.1       root      210:        Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(WXMainView::OnKeyDown), NULL,
1.1.1.6   root      211:                mainview);
1.1.1.10  root      212:        Connect(wxEVT_CHAR, wxKeyEventHandler(WXMainView::OnChar), NULL,
                    213:                mainview);
1.1.1.3   root      214: 
1.1.1.5   root      215:        // VM からの通知を受け取る
1.1.1.3   root      216:        WXUIMessage::Connect(UIMessage::SCSI_MEDIA_CHANGE, this,
                    217:                wxCommandEventHandler(WXStatusPanel::OnSCSIMediaChanged));
1.1.1.5   root      218:        WXUIMessage::Connect(UIMessage::FDD_MEDIA_CHANGE, this,
                    219:                wxCommandEventHandler(WXStatusPanel::OnFDDMediaChanged));
                    220:        WXUIMessage::Connect(UIMessage::LED, this,
                    221:                wxCommandEventHandler(WXStatusPanel::OnLEDChanged));
1.1       root      222: }
                    223: 
1.1.1.2   root      224: // デストラクタ
                    225: WXStatusPanel::~WXStatusPanel()
                    226: {
1.1.1.4   root      227:        while (indicators.empty() == false) {
                    228:                Indicator *ind = indicators.back();
                    229:                indicators.pop_back();
                    230:                delete ind;
                    231:        }
1.1.1.5   root      232: 
1.1.1.6   root      233:        WXUIMessage::Disconnect(UIMessage::SCSI_MEDIA_CHANGE, this,
1.1.1.4   root      234:                wxCommandEventHandler(WXStatusPanel::OnSCSIMediaChanged));
1.1.1.6   root      235:        WXUIMessage::Disconnect(UIMessage::FDD_MEDIA_CHANGE, this,
1.1.1.5   root      236:                wxCommandEventHandler(WXStatusPanel::OnFDDMediaChanged));
1.1.1.6   root      237:        WXUIMessage::Disconnect(UIMessage::LED, this,
1.1.1.5   root      238:                wxCommandEventHandler(WXStatusPanel::OnLEDChanged));
1.1.1.2   root      239: }
                    240: 
1.1.1.14! root      241: // 初期化
        !           242: void
        !           243: WXStatusPanel::Init()
        !           244: {
        !           245:        // ここで最初に一回、全メディアの状態更新を能動的に行う。
        !           246:        for (const auto ind : indicators) {
        !           247:                int id;
        !           248:                id = ind->GetSCSIID();
        !           249:                if (id >= 0) {
        !           250:                        wxCommandEvent event(NONO_EVT_UIMESSAGE);
        !           251:                        event.SetInt(id);
        !           252:                        OnSCSIMediaChanged(event);
        !           253:                }
        !           254:                id = ind->GetFDUnit();
        !           255:                if (id >= 0) {
        !           256:                        wxCommandEvent event(NONO_EVT_UIMESSAGE);
        !           257:                        event.SetInt(id);
        !           258:                        OnFDDMediaChanged(event);
        !           259:                }
        !           260:        }
        !           261: }
        !           262: 
1.1       root      263: // クローズイベント
                    264: void
                    265: WXStatusPanel::OnClose(wxCloseEvent& event)
                    266: {
                    267:        // クローズする前に更新を止める。
                    268:        timer.Stop();
                    269: }
                    270: 
                    271: // 表示/非表示変更イベント
                    272: // new した時は (デフォルトで表示状態なのでか状態に変更がないという扱いの
                    273: // ようで) イベントは飛んでこない。同様に new 直後に Show() を明示発行しても
                    274: // 状態に変更がないためイベントは飛んでこない。
                    275: // new 直後に Hide() すればイベントは飛んでくる。という動作をするようだ。
                    276: // そのため WXMainFrame 側で生成後に Show/Hide を起こしている。
                    277: void
                    278: WXStatusPanel::OnShow(wxShowEvent& event)
                    279: {
                    280:        if (event.IsShown()) {
                    281:                // 非表示→表示
                    282: 
                    283:                // 表示開始前に一度情報を取得
                    284:                UpdateStat();
                    285: 
                    286:                timer.Start(1000 / FPS);
                    287:        } else {
                    288:                // 表示→非表示
                    289: 
                    290:                timer.Stop();
                    291:        }
                    292: }
                    293: 
1.1.1.4   root      294: // フォントサイズ変更
1.1       root      295: void
1.1.1.4   root      296: WXStatusPanel::FontChanged()
1.1       root      297: {
1.1.1.4   root      298:        inherited::FontChanged();
                    299: 
1.1.1.6   root      300:        // アクセスマークをサイズに応じて作り直す
                    301:        const uint8 *src;
                    302:        switch (font_height) {
                    303:         case 12:
                    304:                src = AccessMark12;
                    305:                break;
                    306:         case 16:
                    307:                src = AccessMark16;
                    308:                break;
                    309:         case 24:
                    310:                src = AccessMark24;
                    311:                break;
                    312:         default:
                    313:                PANIC("Unexpected font_height=%d", font_height);
                    314:        }
                    315:        accmark.reset(new BitmapI8(src, font_height, font_height));
                    316: 
1.1.1.13  root      317:        Fit();
1.1       root      318: }
                    319: 
                    320: void
1.1.1.13  root      321: WXStatusPanel::Fit()
1.1       root      322: {
1.1.1.13  root      323:        wxSize size = LayoutIndicators();
1.1       root      324: 
1.1.1.13  root      325:        // バックバッファのサイズを固定。
                    326:        SetMinBitmapSize(size);
1.1       root      327: 
1.1.1.13  root      328:        SetMinSize(size);
                    329:        SetSize(size);
1.1       root      330: }
                    331: 
                    332: // タイマーイベント
                    333: void
                    334: WXStatusPanel::OnTimer(wxTimerEvent& event)
                    335: {
                    336:        if (UpdateStat()) {
                    337:                Refresh();
                    338:        }
                    339: }
                    340: 
1.1.1.12  root      341: // store に value を代入。更新されていれば true を返す。
1.1.1.7   root      342: // value は引数に渡す時に一度だけ評価される。
                    343: #define MODIFY(store, value_)  ({      \
                    344:        bool mod_;      \
                    345:        decltype(value_) value = value_;        \
                    346:        if (store != value)     { \
                    347:                store = value;  \
                    348:                mod_ = true;    \
                    349:        } else {        \
                    350:                mod_ = false;   \
                    351:        }       \
                    352:        mod_;   /* return value */      \
                    353: })
                    354: 
                    355: //
                    356: // インジケータ用の情報
                    357: //
                    358: class Status
                    359: {
                    360:  public:
1.1.1.9   root      361:        virtual ~Status();
1.1.1.7   root      362: 
                    363:        // 情報取得メソッド
                    364:        virtual bool Poll() = 0;
                    365: };
                    366: 
1.1.1.9   root      367: // デストラクタ
                    368: Status::~Status()
                    369: {
                    370: }
                    371: 
1.1.1.12  root      372: // Read/Write が別の3値タイプのアクセスインジケータ用
                    373: enum class RWStatus : uint8 {
                    374:        None = 0,
                    375:        Read = 1,
                    376:        Write = 2,
                    377: };
                    378: 
1.1.1.7   root      379: //
1.1.1.12  root      380: // SCSI 情報 (Bus/VirtIO 共通)
1.1.1.7   root      381: //
                    382: class StatusSCSI : public Status
                    383: {
                    384:  public:
1.1.1.12  root      385:        std::array<RWStatus, 8> active {};
                    386: };
                    387: 
                    388: //
                    389: // SCSIBus 情報
                    390: //
                    391: class StatusSCSIBus : public StatusSCSI
                    392: {
                    393:  public:
                    394:        StatusSCSIBus(SCSIBus *scsibus_) {
1.1.1.7   root      395:                scsibus = scsibus_;
                    396:        }
1.1.1.12  root      397:        ~StatusSCSIBus() override;
1.1.1.7   root      398: 
                    399:        bool Poll() override {
1.1.1.12  root      400:                uint8 bsy = scsibus->GetBSY();
1.1.1.7   root      401:                // 転送フェーズで DataOut の時だけ書き込みとみなす
                    402:                bool out = (scsibus->GetPhase() == SCSI::Phase::Transfer)
                    403:                        && (scsibus->GetXfer()  == SCSI::XferPhase::DataOut);
1.1.1.12  root      404: 
                    405:                bool mod = false;
                    406:                for (uint id = 0; id < 8; id++) {
                    407:                        RWStatus newactive;
                    408:                        if (__predict_false((bsy & (1U << id)) != 0)) {
                    409:                                if (out) {
                    410:                                        newactive = RWStatus::Write;
                    411:                                } else {
                    412:                                        newactive = RWStatus::Read;
                    413:                                }
                    414:                        } else {
                    415:                                newactive = RWStatus::None;
                    416:                        }
                    417:                        mod |= MODIFY(active[id], newactive);
                    418:                }
1.1.1.7   root      419:                return mod;
                    420:        }
                    421: 
                    422:  private:
                    423:        SCSIBus *scsibus {};
                    424: };
                    425: 
1.1.1.9   root      426: // デストラクタ
1.1.1.12  root      427: StatusSCSIBus::~StatusSCSIBus()
1.1.1.9   root      428: {
                    429: }
                    430: 
1.1.1.7   root      431: //
1.1.1.12  root      432: // VirtIOSCSI 情報
                    433: //
                    434: class StatusVSCSI : public StatusSCSI
                    435: {
                    436:  public:
                    437:        StatusVSCSI(VirtIOSCSIDevice *dev_) {
                    438:                dev = dev_;
                    439:        }
                    440:        ~StatusVSCSI() override = default;
                    441: 
                    442:        bool Poll() override {
                    443:                std::array<uint32, 16> newacc;
                    444:                dev->GetAccess(&newacc);
                    445:                bool mod = false;
                    446:                for (uint id = 0; id < 8; id++) {
                    447:                        bool rmod = MODIFY(access[id * 2 + 0], newacc[id * 2 + 0]);
                    448:                        bool wmod = MODIFY(access[id * 2 + 1], newacc[id * 2 + 1]);
                    449: 
                    450:                        RWStatus newactive;
                    451:                        if (wmod) {
                    452:                                newactive = RWStatus::Write;
                    453:                        } else if (rmod) {
                    454:                                newactive = RWStatus::Read;
                    455:                        } else {
                    456:                                newactive = RWStatus::None;
                    457:                        }
                    458:                        mod |= MODIFY(active[id], newactive);
                    459:                }
                    460:                return mod;
                    461:        }
                    462: 
                    463:        std::array<uint32, 8 * 2> access {};
                    464: 
                    465:  private:
                    466:        VirtIOSCSIDevice *dev {};
                    467: };
                    468: 
                    469: //
1.1.1.8   root      470: // VirtIOBlock 情報
                    471: //
                    472: class StatusVBlk : public Status
                    473: {
                    474:  public:
                    475:        StatusVBlk(VirtIOBlockDevice *dev_) {
                    476:                dev = dev_;
                    477:        }
1.1.1.9   root      478:        ~StatusVBlk() override;
1.1.1.8   root      479: 
                    480:        bool Poll() override {
                    481:                bool rmod = MODIFY(access_read,  dev->GetAccessRead());
                    482:                bool wmod = MODIFY(access_write, dev->GetAccessWrite());
1.1.1.12  root      483:                RWStatus newactive;
                    484:                if (wmod) {
                    485:                        newactive = RWStatus::Write;
                    486:                } else if (rmod) {
                    487:                        newactive = RWStatus::Read;
                    488:                } else {
                    489:                        newactive = RWStatus::None;
                    490:                }
                    491:                return MODIFY(active, newactive);
1.1.1.8   root      492:        }
                    493: 
                    494:        // アクセス状況
                    495:        uint32 access_read {};
                    496:        uint32 access_write {};
1.1.1.12  root      497:        RWStatus active {};
1.1.1.8   root      498: 
                    499:  private:
                    500:        VirtIOBlockDevice *dev {};
                    501: };
                    502: 
1.1.1.9   root      503: // デストラクタ
                    504: StatusVBlk::~StatusVBlk()
                    505: {
                    506: }
                    507: 
1.1.1.8   root      508: //
1.1.1.7   root      509: // ネットワーク情報
                    510: //
                    511: class StatusNet : public Status
                    512: {
                    513:  public:
                    514:        StatusNet(HostNetDevice *hostnet_) {
                    515:                hostnet = hostnet_;
                    516:        }
1.1.1.9   root      517:        ~StatusNet() override;
1.1.1.7   root      518: 
                    519:        bool Poll() override {
                    520:                // パケット数が変化したとき、および
                    521:                // その変化を監視しているアクティブ状態が変化したときに
                    522:                // 変更があったとしたいので二重になっている。
                    523:                bool tx_mod = MODIFY(tx_pkts, hostnet->GetTXPkts());
                    524:                bool rx_mod = MODIFY(rx_pkts, hostnet->GetRXPkts());
                    525: 
                    526:                bool mod = false;
                    527:                mod |= MODIFY(tx_active, tx_mod);
                    528:                mod |= MODIFY(rx_active, rx_mod);
                    529:                return mod;
                    530:        }
                    531: 
                    532:        bool enable {};
                    533:        uint64 tx_pkts {};
                    534:        uint64 rx_pkts {};
                    535:        bool tx_active {};
                    536:        bool rx_active {};
                    537: 
                    538:  private:
                    539:        HostNetDevice *hostnet {};
                    540: };
                    541: 
1.1.1.9   root      542: // デストラクタ
                    543: StatusNet::~StatusNet()
                    544: {
                    545: }
                    546: 
1.1.1.7   root      547: //
                    548: // FDD 情報
                    549: //
                    550: class StatusFDD : public Status
                    551: {
                    552:  public:
                    553:        StatusFDD(FDDDevice *fdd_) {
                    554:                fdd = fdd_;
                    555:        }
1.1.1.9   root      556:        ~StatusFDD() override;
1.1.1.7   root      557: 
                    558:        bool Poll() override {
                    559:                bool mod = false;
                    560:                mod |= MODIFY(access_led, fdd->GetAccessLED());
                    561:                mod |= MODIFY(eject_led,  fdd->GetEjectLED());
                    562:                return mod;
                    563:        }
                    564: 
                    565:        FDDDevice::LED access_led {};   // アクセス LED の状態
                    566:        bool eject_led {};                              // イジェクト LED 点灯なら true
                    567:  private:
                    568:        FDDDevice *fdd {};
                    569: };
                    570: 
1.1.1.9   root      571: // デストラクタ
                    572: StatusFDD::~StatusFDD()
                    573: {
                    574: }
                    575: 
1.1.1.7   root      576: //
                    577: // NEWS の LED 情報
                    578: //
                    579: class StatusNEWS : public Status
                    580: {
                    581:  public:
                    582:        StatusNEWS(NewsCtlrDevice *newsctlr_) {
                    583:                newsctlr = newsctlr_;
                    584:        }
1.1.1.9   root      585:        ~StatusNEWS() override;
1.1.1.7   root      586: 
                    587:        bool Poll() override {
                    588:                return MODIFY(led, newsctlr->GetLED());
                    589:        }
                    590: 
                    591:        uint led {};
                    592:  private:
                    593:        NewsCtlrDevice *newsctlr {};
                    594: };
                    595: 
1.1.1.9   root      596: // デストラクタ
                    597: StatusNEWS::~StatusNEWS()
                    598: {
                    599: }
                    600: 
1.1.1.7   root      601: 
1.1       root      602: // インジケータの初期化
                    603: void
                    604: WXStatusPanel::InitIndicators()
                    605: {
1.1.1.7   root      606:        Status *stat;
1.1.1.6   root      607:        Indicator *ind;
                    608: 
1.1       root      609:        // パフォーマンスカウンタ
1.1.1.14! root      610:        // 0123456789012345
        !           611:        // >>> 99.9x 100MHz
        !           612:        auto mpu = gMainApp.FindObject<MainMPUDevice>(OBJ_MPU);
        !           613:        assert(mpu);
        !           614:        clock_khz = mpu->GetClockSpeed();
        !           615:        ind = new Indicator(Indicator::PERF, &WXStatusPanel::DrawPerf, 15, NULL);
1.1.1.6   root      616:        ind->dclick = &WXStatusPanel::DClickPerf;
                    617:        indicators.emplace_back(ind);
1.1       root      618: 
                    619:        // SCSI
1.1.1.9   root      620:        // 接続済みデバイスリストから取得。
1.1.1.12  root      621:        auto scsibus = gMainApp.FindObject<SCSIBus>(OBJ_SCSIBUS);
                    622:        if (scsibus) {
                    623:                stat = new StatusSCSIBus(scsibus);
1.1.1.7   root      624:                stats.emplace_back(stat);
                    625: 
1.1.1.12  root      626:                const auto& conn = scsibus->GetDomain()->GetConnectedDevices();
                    627:                for (const auto& dev : conn) {
1.1.1.6   root      628:                        SCSI::DevType devtype = dev->GetDevType();
                    629:                        if (devtype == SCSI::DevType::Initiator) {
                    630:                                continue;
                    631:                        }
                    632:                        int scsiid = dev->GetMyID();
                    633: 
1.1.1.13  root      634:                        std::string label = string_format("%s%d",
                    635:                                SCSI::GetDevTypeName(devtype), scsiid);
                    636:                        const auto disk = dynamic_cast<SCSIDisk *>(dev);
                    637:                        if (disk && disk->GetWriteMode() == SCSIDisk::RW::WriteIgnore) {
                    638:                                label += "\x02\x03";
                    639:                        }
1.1.1.7   root      640:                        ind = new Indicator(Indicator::SCSI0 + scsiid,
1.1.1.13  root      641:                                &WXStatusPanel::DrawSCSI, label, stat);
1.1.1.6   root      642:                        ind->contextmenu = &WXStatusPanel::ContextMenuCD;
                    643:                        ind->dev = dev;
                    644:                        ind->panel = new wxPanel(this);
1.1.1.10  root      645:                        ind->panel->SetName("WXStatusPanel.SCSI");
1.1.1.6   root      646:                        // このパネルがフォーカスを受け取らないようにする。
                    647:                        // StatusPanel がフォーカスを持っていてもキー入力を MainFrame に渡す
                    648:                        // ようにしているため、StatusPanel のフォーカスを奪ってはいけない。
                    649:                        ind->panel->SetCanFocus(false);
1.1.1.3   root      650: 
1.1.1.6   root      651:                        indicators.emplace_back(ind);
                    652:                }
1.1       root      653:        }
                    654: 
1.1.1.8   root      655:        // VirtIOBlock
                    656:        for (int i = 0; i < 8; i++) {
                    657:                auto dev = gMainApp.FindObject<VirtIOBlockDevice>(OBJ_VIRTIO_BLOCK(i));
                    658:                if (dev) {
                    659:                        stat = new StatusVBlk(dev);
                    660:                        stats.emplace_back(stat);
                    661: 
                    662:                        std::string label = string_format("VB%u", i);
                    663:                        if (dev->GetWriteMode() == SCSIDisk::RW::WriteIgnore) {
                    664:                                label += "\x02\x03";
                    665:                        }
                    666:                        ind = new Indicator(Indicator::VBLK0 + i, &WXStatusPanel::DrawVBlk,
                    667:                                label, stat);
                    668:                        ind->dev = dev;
                    669:                        indicators.emplace_back(ind);
                    670:                }
                    671:        }
                    672: 
1.1.1.12  root      673:        // VirtIOSCSI
                    674:        auto vscsi = gMainApp.FindObject<VirtIOSCSIDevice>(OBJ_VIRTIO_SCSI);
                    675:        if (vscsi) {
                    676:                stat = new StatusVSCSI(vscsi);
                    677:                stats.emplace_back(stat);
                    678: 
                    679:                const auto& conn = vscsi->GetDomain()->GetConnectedDevices();
                    680:                for (const auto& dev : conn) {
1.1.1.13  root      681:                        SCSI::DevType devtype = dev->GetDevType();
1.1.1.12  root      682:                        int scsiid = dev->GetMyID();
                    683: 
1.1.1.13  root      684:                        std::string label = string_format("%s%d",
                    685:                                SCSI::GetDevTypeName(devtype), scsiid);
                    686:                        const auto disk = dynamic_cast<SCSIDisk *>(dev);
                    687:                        if (disk && disk->GetWriteMode() == SCSIDisk::RW::WriteIgnore) {
                    688:                                label += "\x02\x03";
                    689:                        }
1.1.1.12  root      690:                        ind = new Indicator(Indicator::SCSI0 + scsiid,
1.1.1.13  root      691:                                &WXStatusPanel::DrawSCSI, label, stat);
1.1.1.12  root      692:                        ind->contextmenu = &WXStatusPanel::ContextMenuCD;
                    693:                        ind->dev = dev;
                    694:                        ind->panel = new wxPanel(this);
                    695:                        ind->panel->SetName("WXStatusPanel.VSCSI");
                    696:                        // このパネルがフォーカスを受け取らないようにする。
                    697:                        // StatusPanel がフォーカスを持っていてもキー入力を MainFrame に渡す
                    698:                        // ようにしているため、StatusPanel のフォーカスを奪ってはいけない。
                    699:                        ind->panel->SetCanFocus(false);
                    700: 
                    701:                        indicators.emplace_back(ind);
                    702:                }
                    703:        }
                    704: 
1.1       root      705:        // ネットワーク
1.1.1.7   root      706:        for (int i = 0; i < 2; i++) {
                    707:                auto hostnet = gMainApp.FindObject<HostNetDevice>(OBJ_HOSTNET(i));
                    708:                if (hostnet) {
                    709:                        auto statnet = new StatusNet(hostnet);
                    710:                        stat = statnet;
                    711:                        stats.emplace_back(stat);
                    712: 
                    713:                        if (hostnet->GetDriverName() == "none") {
                    714:                                statnet->enable = false;
                    715:                        } else {
                    716:                                statnet->enable = true;
                    717:                        }
                    718:                        // \x1e は上向き矢印、\x1f は下向き矢印。
                    719:                        // ラベルは結局機種で変わる…
                    720:                        std::string label;
                    721:                        if (gMainApp.IsX68030()) {
                    722:                                label = string_format("LAN%d\x1e\x1f", i);
                    723:                        } else {
                    724:                                label = "LAN\x1e\x1f";
                    725:                        }
                    726:                        ind = new Indicator(Indicator::LAN0 + i, &WXStatusPanel::DrawNet,
                    727:                                label, stat);
                    728:                        indicators.emplace_back(ind);
1.1.1.2   root      729:                }
1.1       root      730:        }
                    731: 
1.1.1.5   root      732:        // FD
1.1.1.6   root      733:        for (int unit = 0; unit < FDCDevice::MAX_DRIVE; unit++) {
                    734:                auto fdd = gMainApp.FindObject<FDDDevice>(OBJ_FDD(unit));
                    735:                if (fdd) {
1.1.1.7   root      736:                        stat = new StatusFDD(fdd);
                    737:                        stats.emplace_back(stat);
1.1.1.6   root      738: 
1.1.1.13  root      739:                        // 後ろの4文字分のスペースは LED アイコン用の場所取り。
                    740:                        std::string label = string_format("FD%d    ", fdd->GetUnitNo());
                    741:                        if (fdd->GetWriteMode() == FDDDevice::RW::WriteIgnore) {
                    742:                                label += "\x02\x03";
                    743:                        }
                    744: 
1.1.1.6   root      745:                        // 文字列は OnFDDMediaChanged() でセットする。
1.1.1.7   root      746:                        ind = new Indicator(Indicator::FDD0 + unit,
1.1.1.13  root      747:                                &WXStatusPanel::DrawFD, label, stat);
1.1.1.6   root      748:                        ind->contextmenu = &WXStatusPanel::ContextMenuFD;
                    749:                        ind->dev = fdd;
                    750:                        ind->panel = new wxPanel(this);
1.1.1.10  root      751:                        ind->panel->SetName("WXStatusPanel.FD");
1.1.1.6   root      752:                        // フォーカスを受け取らない。少し上の CD のところ参照。
                    753:                        ind->panel->SetCanFocus(false);
                    754: 
                    755:                        indicators.emplace_back(ind);
1.1.1.5   root      756:                }
                    757:        }
                    758: 
1.1.1.6   root      759:        // NEWS の LED (2->1 の順で並べる)
1.1.1.7   root      760:        auto newsctlr = gMainApp.FindObject<NewsCtlrDevice>(OBJ_NEWSCTLR);
1.1.1.6   root      761:        if (newsctlr) {
1.1.1.7   root      762:                stat = new StatusNEWS(newsctlr);
                    763:                stats.emplace_back(stat);
                    764: 
                    765:                ind = new Indicator(Indicator::NEWSLED2, &WXStatusPanel::DrawNewsLED2,
                    766:                        "LED2", stat);
1.1.1.6   root      767:                indicators.emplace_back(ind);
                    768: 
1.1.1.7   root      769:                ind = new Indicator(Indicator::NEWSLED1, &WXStatusPanel::DrawNewsLED1,
                    770:                        "LED1", stat);
1.1.1.6   root      771:                indicators.emplace_back(ind);
                    772:        }
                    773: 
1.1.1.5   root      774:        // 電源 LED
1.1.1.7   root      775:        ind = new Indicator(Indicator::POWER, &WXStatusPanel::DrawPower,
                    776:                "POWER", NULL);
1.1.1.6   root      777:        indicators.emplace_back(ind);
1.1       root      778: }
                    779: 
1.1.1.13  root      780: // インジケータの配置位置を求める。
                    781: // ind->rect を更新するがここで描画とかはしない。
                    782: // 戻り値はインジケータの配置に必要な最小サイズ。
                    783: wxSize
1.1       root      784: WXStatusPanel::LayoutIndicators()
                    785: {
                    786:        int x;
                    787:        int y;
                    788:        int w;
                    789:        int h;
                    790: 
1.1.1.4   root      791:        // 上の枠はキャンバス上端から2px
                    792:        y = 2;
                    793:        // そこから上下の外枠と枠と文字の間
                    794:        h = font_height + 2 + 2;
                    795: 
1.1       root      796:        // まずは前から順に列挙して必要な幅を積算していく
                    797:        x = 0;
1.1.1.3   root      798:        for (auto ind : indicators) {
1.1.1.4   root      799:                // 前の枠とは1つ空ける
                    800:                x += 1;
1.1       root      801: 
                    802:                // 4 は、左右の外枠(1*2) と 枠と文字の間(1*2)
1.1.1.5   root      803:                int len = std::max(ind->minlen, (int)ind->text.length());
                    804:                w = len * font_width + 2 + 2;
1.1       root      805: 
1.1.1.4   root      806:                ind->rect = Rect(x, y, w, h);
1.1       root      807:                x += w;
                    808:        }
                    809: 
1.1.1.4   root      810:        // 右端も1つ空ける
                    811:        x += 1;
                    812: 
                    813:        // この x が最小サイズなのでこれをコントロールに設定。
1.1.1.13  root      814:        wxSize size(x, h + 2 + 2);
                    815:        return size;
                    816: }
                    817: 
                    818: // レイアウト
                    819: bool
                    820: WXStatusPanel::Layout()
                    821: {
                    822:        if (indicators.empty()) {
                    823:                return false;
                    824:        }
                    825: 
                    826:        // レイアウトの計算はビットマップサイズに対して行う。
                    827:        // 起動直後に何かの拍子で随分小さいサイズにリサイズされることがあったり
                    828:        // また実画面が小さくてそもそも入り切らないような場合でも、
                    829:        // ビットマップは常に最低限描画できるサイズを保証してあるので。
                    830:        // それを画面に転送しても見た目は事故るがとりあえず死にはしないので。
                    831:        wxSize size(bitmap.GetWidth(), bitmap.GetHeight());
1.1.1.4   root      832: 
1.1.1.13  root      833:        // 配置情報をリセット。(左寄せ相当になる)
                    834:        wxSize minsize = LayoutIndicators();
                    835: 
                    836:        // 全員を一旦右寄せ。
                    837:        int offsetx = size.x - minsize.x;
1.1.1.3   root      838:        for (auto ind : indicators) {
                    839:                ind->rect.Offset(offsetx, 0);
1.1       root      840:        }
                    841: 
                    842:        // そのうちパフォーマンスカウンタだけ中央寄せまで戻す
1.1.1.13  root      843:        auto perf = indicators.front();
                    844:        int x = (size.x / 2) - (perf->rect.w / 2);
                    845:        if (perf->rect.x > x) {
                    846:                perf->rect.x = x;
1.1.1.3   root      847:        }
                    848: 
                    849:        // パネルをもっていれば、パネルをその位置に(再)設定。
                    850:        for (auto ind : indicators) {
1.1.1.5   root      851:                if (ind->panel) {
1.1.1.3   root      852:                        const auto& rect = ind->rect;
1.1.1.5   root      853:                        ind->panel->SetSize(rect.x, rect.y, rect.w, rect.h);
1.1       root      854:                }
                    855:        }
1.1.1.5   root      856: 
                    857:        refresh_background = true;
1.1.1.13  root      858:        return true;
1.1       root      859: }
                    860: 
1.1.1.3   root      861: // 状態をチェック。
                    862: // 前回値からどれかでも変更があれば true を返す。
                    863: bool
                    864: WXStatusPanel::UpdateStat()
                    865: {
1.1.1.7   root      866:        bool mod = false;
1.1.1.3   root      867: 
1.1.1.7   root      868:        // 電源オン
                    869:        mod |= MODIFY(ispower, power->IsPower());
                    870:        mod |= MODIFY(powerled, power->GetPowerLED());
1.1.1.3   root      871: 
                    872:        // パフォーマンスカウンタ
1.1.1.7   root      873:        mod |= MODIFY(perf_mode, syncer->GetRunMode());
                    874:        mod |= MODIFY(perf_rate, syncer->GetPerfCounter());
1.1.1.3   root      875: 
1.1.1.7   root      876:        // それ以外
                    877:        for (const auto stat : stats) {
                    878:                mod |= stat->Poll();
1.1.1.3   root      879:        }
                    880: 
1.1.1.7   root      881:        return mod;
1.1.1.3   root      882: }
                    883: 
                    884: // 描画本体
                    885: void
1.1.1.4   root      886: WXStatusPanel::Draw()
1.1.1.3   root      887: {
                    888:        std::string text;
                    889: 
1.1.1.5   root      890:        // 指定された時だけ背景を再描画
                    891:        if (refresh_background) {
                    892:                refresh_background = false;
                    893:                bitmap.Fill(BGPANEL);
                    894: 
1.1.1.13  root      895:                // なんとなく見栄えのため上下に薄い線を引く。
                    896:                // 四方を線で囲むとオーバーレイウィンドウのように見えるので上下だけ。
                    897:                wxSize size = GetSize();
                    898:                bitmap.DrawLineH(UD_GREY, 0, 0, size.x);
                    899:                bitmap.DrawLineH(UD_GREY, 0, size.y - 1, size.x);
                    900:        }
1.1.1.3   root      901: 
                    902:        // 各インジケータを描画
                    903:        for (auto ind : indicators) {
1.1.1.4   root      904:                (this->*(ind->draw))(ind);
1.1.1.3   root      905:        }
                    906: }
                    907: 
1.1       root      908: // パフォーマンスカウンタ
                    909: void
1.1.1.4   root      910: WXStatusPanel::DrawPerf(const Indicator *ind)
1.1       root      911: {
1.1.1.4   root      912:        const Rect& rect = ind->rect;
1.1.1.2   root      913:        char text[16];
1.1.1.4   root      914:        const char *ffmark;
1.1       root      915: 
                    916:        // 枠
1.1.1.4   root      917:        bitmap.DrawRect(UD_GREY, rect);
1.1       root      918: 
                    919:        // 電源オフならここまで
                    920:        if (ispower == false) {
1.1.1.4   root      921:                bitmap.FillRect(BGPANEL,
                    922:                        rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2);
1.1       root      923:                return;
                    924:        }
                    925: 
1.1.1.2   root      926:        // 高速モードか通常モードかでいろいろ違う。
                    927:        // アイコンは
                    928:        // o 通常モード指示中ならアイコンなし
                    929:        // o 高速モード指示中なら >> (2つ)
                    930:        // o そのうち実際に高速動作なら  >>> (3つ)
                    931:        // パフォーマンス表示は
                    932:        // o 高速モード指示中なら倍率表記 (115% なら 1.2x)
                    933:        // o 通常モード指示中ならパーセント表記
1.1.1.11  root      934:        if ((perf_mode & Syncer::SYNCMODE_SYNC) == 0) {
1.1.1.2   root      935:                // 高速モード指示
1.1       root      936:                if (perf_mode == 0) {
1.1.1.2   root      937:                        // 高速走行中
1.1.1.4   root      938:                        ffmark = "\x01\x01\x01";
                    939:                } else {
                    940:                        ffmark = "\x01\x01 ";
1.1       root      941:                }
                    942: 
1.1.1.2   root      943:                // 倍率表記は1桁少ないので、表示用に最下位桁を四捨五入
1.1.1.9   root      944:                uint rate = perf_rate + 5;
                    945:                if (rate < 100 * 100) {
                    946:                        // 2桁倍までなら "99.9x" のように小数以下1桁まで。
                    947:                        snprintf(text, sizeof(text), "%2u.%1ux",
                    948:                                (rate / 100), (rate % 100) / 10);
                    949:                } else if (rate < 10000 * 100) {
                    950:                        // 3桁倍、4桁倍だと整数部のみ。"9999x"
                    951:                        snprintf(text, sizeof(text), "%4ux", rate / 100);
                    952:                } else {
                    953:                        // どうする?
                    954:                        strlcpy(text, "9999x", sizeof(text));
                    955:                }
1.1.1.2   root      956:        } else {
                    957:                // 通常モード指示
1.1.1.4   root      958:                ffmark = "   ";
1.1.1.9   root      959:                snprintf(text, sizeof(text), "%4u%%", perf_rate);
1.1.1.2   root      960:        }
1.1.1.4   root      961: 
1.1.1.14! root      962:        // クロックスピード(MHz)も四捨五入。
        !           963:        uint eff_khz = (clock_khz * perf_rate + 50) / 100;
        !           964:        snprintf(text + 5, sizeof(text) - 5, "%4uMHz", (eff_khz + 500) / 1000);
        !           965: 
1.1.1.4   root      966:        DrawStringSJIS(UD_RED, rect.x + 2, rect.y + 2, ffmark);
                    967:        DrawStringSJIS(rect.x + 2 + font_width * 3, rect.y + 2, text);
1.1       root      968: }
                    969: 
                    970: // SCSI デバイス側のアクセスランプ
                    971: // 本体ではなく各 SCSI デバイス側 (外付け HDD の前面とか) にあるやつ。
                    972: void
1.1.1.4   root      973: WXStatusPanel::DrawSCSI(const Indicator *ind)
1.1       root      974: {
1.1.1.7   root      975:        const StatusSCSI *stat = dynamic_cast<const StatusSCSI*>(ind->stat);
1.1.1.4   root      976:        Color fg;
                    977:        Color bg;
1.1.1.5   root      978:        int scsiid;
1.1.1.3   root      979: 
1.1.1.5   root      980:        scsiid = ind->GetSCSIID();
                    981:        assertmsg(scsiid >= 0, "id=%d", ind->id);
1.1       root      982: 
1.1.1.7   root      983:        if (scsi_loaded[scsiid]) {
1.1.1.4   root      984:                fg = UD_BLACK;
1.1.1.3   root      985:        } else {
                    986:                fg = UD_GREY;
                    987:        }
                    988: 
1.1.1.12  root      989:        if (stat->active[scsiid] == RWStatus::Write) {
                    990:                bg = UD_RED;
                    991:        } else if (stat->active[scsiid] == RWStatus::Read) {
                    992:                bg = UD_YELLOW_GREEN;
1.1       root      993:        } else {
                    994:                bg = BGPANEL;
                    995:        }
                    996: 
1.1.1.4   root      997:        DrawTextLED(ind, fg, bg);
1.1       root      998: }
                    999: 
1.1.1.8   root     1000: // VirtIOBlock デバイスのアクセスランプ。
                   1001: void
                   1002: WXStatusPanel::DrawVBlk(const Indicator *ind)
                   1003: {
                   1004:        const StatusVBlk *stat = dynamic_cast<const StatusVBlk*>(ind->stat);
                   1005:        Color fg;
                   1006:        Color bg;
                   1007: 
                   1008:        fg = UD_BLACK;
1.1.1.12  root     1009:        if (stat->active == RWStatus::Write) {
1.1.1.8   root     1010:                bg = UD_RED;
1.1.1.12  root     1011:        } else if (stat->active == RWStatus::Read) {
1.1.1.8   root     1012:                bg = UD_YELLOW_GREEN;
                   1013:        } else {
                   1014:                bg = BGPANEL;
                   1015:        }
                   1016: 
                   1017:        DrawTextLED(ind, fg, bg);
                   1018: }
                   1019: 
1.1       root     1020: // ネットワーク(LAN)
                   1021: void
1.1.1.4   root     1022: WXStatusPanel::DrawNet(const Indicator *ind)
1.1       root     1023: {
1.1.1.7   root     1024:        const StatusNet *stat = dynamic_cast<const StatusNet*>(ind->stat);
                   1025:        const Rect& rect = ind->rect;
1.1.1.4   root     1026:        Color fg;
                   1027:        Color bg = BGPANEL;
1.1       root     1028: 
1.1.1.7   root     1029:        if (stat->enable) {
1.1.1.4   root     1030:                fg = UD_BLACK;
1.1.1.7   root     1031:                if (stat->tx_active || stat->rx_active) {
1.1.1.2   root     1032:                        bg = UD_YELLOW_GREEN;
                   1033:                }
1.1       root     1034:        } else {
                   1035:                fg = UD_GREY;
                   1036:        }
                   1037: 
1.1.1.2   root     1038:        // 枠
1.1.1.4   root     1039:        bitmap.FillRect(bg, rect);
                   1040:        bitmap.DrawRect(UD_GREY, rect);
1.1.1.2   root     1041: 
                   1042:        int x = rect.x + 2;
                   1043:        int y = rect.y + 2;
                   1044: 
1.1.1.3   root     1045:        for (int i = 0; i < ind->text.size(); i++) {
                   1046:                auto c = ind->text[i];
1.1.1.2   root     1047:                switch (c) {
                   1048:                 case '\x1e':   // 上向き矢印
1.1.1.7   root     1049:                        if (stat->tx_active) {
1.1.1.4   root     1050:                                fg = UD_BLACK;
1.1.1.2   root     1051:                        } else {
                   1052:                                fg = UD_GREY;
                   1053:                        }
                   1054:                        break;
                   1055:                 case '\x1f':   // 下向き矢印
1.1.1.7   root     1056:                        if (stat->rx_active) {
1.1.1.4   root     1057:                                fg = UD_BLACK;
1.1.1.2   root     1058:                        } else {
                   1059:                                fg = UD_GREY;
                   1060:                        }
                   1061:                        break;
                   1062:                }
                   1063:                SetTextColor(fg, bg);
1.1.1.4   root     1064:                DrawChar1(x, y, c);
1.1.1.2   root     1065:                x += font_width;
                   1066:        }
                   1067:        ResetTextColor();
1.1       root     1068: }
                   1069: 
1.1.1.5   root     1070: // FD
                   1071: void
                   1072: WXStatusPanel::DrawFD(const Indicator *ind)
                   1073: {
1.1.1.7   root     1074:        const StatusFDD *stat = dynamic_cast<const StatusFDD*>(ind->stat);
                   1075:        const Rect& rect = ind->rect;
1.1.1.5   root     1076:        Rect iconrect;
                   1077:        Color pal[3];
1.1.1.7   root     1078:        int unit;
1.1.1.5   root     1079:        enum {
                   1080:                BG = 0,
                   1081:                FG,
                   1082:                IN,
                   1083:        };
                   1084: 
                   1085:        unit = ind->GetFDUnit();
                   1086: 
                   1087:        pal[BG] = BGPANEL;
1.1.1.7   root     1088:        if (fd_loaded[unit]) {
1.1.1.5   root     1089:                pal[FG] = UD_BLACK;
                   1090:        } else {
                   1091:                pal[FG] = UD_GREY;
                   1092:        }
                   1093: 
                   1094:        // まず枠とテキストを通常描画
                   1095:        DrawTextLED(ind, pal[FG], pal[BG]);
                   1096: 
                   1097:        // 続いて 5,6文字目の空き地に四角いインジケータを描画。
                   1098:        // 同じ色で済むので先に描画する。
                   1099:        iconrect.x = rect.x + 2 + font_width * 5 + 1;
                   1100:        iconrect.y = rect.y + 2 + 1;
                   1101:        iconrect.w = font_width * 2 - 2;
                   1102:        iconrect.h = font_height - 2;
                   1103:        bitmap.DrawRect(pal[FG], iconrect);
                   1104:        // ディスク挿入(取り出し可)なら黄緑点灯
1.1.1.7   root     1105:        if (stat->eject_led) {
1.1.1.5   root     1106:                iconrect.x += 1;
                   1107:                iconrect.y += 1;
                   1108:                iconrect.w -= 2;
                   1109:                iconrect.h -= 2;
                   1110:                bitmap.FillRect(UD_YELLOW_GREEN, iconrect);
                   1111:        }
                   1112: 
                   1113:        // その後で 3,4文字目の空き地に丸いインジケータを描画。
                   1114:        int dx = rect.x + 2 + font_width * 3;
                   1115:        int dy = rect.y + 2;
                   1116: 
                   1117:        // アクセス LED はディスク非挿入時に点滅するケースがあって、
                   1118:        // その場合、フチがグレーのまま中を黄緑で塗りつぶすのは見えづらいので
                   1119:        // 中を塗りつぶす時はフチを濃くしたほうがいい。
1.1.1.7   root     1120:        switch (stat->access_led) {
1.1.1.5   root     1121:         case FDDDevice::LED::GREEN:
                   1122:                pal[FG] = UD_BLACK;
                   1123:                pal[IN] = UD_YELLOW_GREEN;
                   1124:                break;
                   1125:         case FDDDevice::LED::RED:
                   1126:                pal[FG] = UD_BLACK;
                   1127:                pal[IN] = UD_RED;
                   1128:                break;
                   1129:         default:
                   1130:                pal[IN] = pal[BG];
                   1131:                break;
                   1132:        }
1.1.1.9   root     1133:        bitmap.DrawBitmapI8(dx, dy, *accmark, pal);
1.1.1.5   root     1134: }
                   1135: 
                   1136: /*static*/ const uint8
                   1137: WXStatusPanel::AccessMark12[] = {
                   1138:        0,0,0,0,1,1,1,0,0,0,0,0,
                   1139:        0,0,1,1,2,2,2,1,1,0,0,0,
                   1140:        0,1,2,2,2,2,2,2,2,1,0,0,
                   1141:        0,1,2,2,2,2,2,2,2,1,0,0,
                   1142:        1,2,2,2,2,2,2,2,2,2,1,0,
                   1143:        1,2,2,2,2,2,2,2,2,2,1,0,
                   1144:        1,2,2,2,2,2,2,2,2,2,1,0,
                   1145:        0,1,2,2,2,2,2,2,2,1,0,0,
                   1146:        0,1,2,2,2,2,2,2,2,1,0,0,
                   1147:        0,0,1,1,2,2,2,1,1,0,0,0,
                   1148:        0,0,0,0,1,1,1,0,0,0,0,0,
                   1149:        0,0,0,0,0,0,0,0,0,0,0,0,
                   1150: };
                   1151: /*static*/ const uint8
                   1152: WXStatusPanel::AccessMark16[] = {
                   1153:        0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,
                   1154:        0,0,0,1,1,2,2,2,2,2,1,1,0,0,0,0,
                   1155:        0,0,1,2,2,2,2,2,2,2,2,2,1,0,0,0,
                   1156:        0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,
                   1157:        0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,
                   1158:        1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,
                   1159:        1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,
                   1160:        1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,
                   1161:        1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,
                   1162:        1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,
                   1163:        0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,
                   1164:        0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,
                   1165:        0,0,1,2,2,2,2,2,2,2,2,2,1,0,0,0,
                   1166:        0,0,0,1,1,2,2,2,2,2,1,1,0,0,0,0,
                   1167:        0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,
                   1168:        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                   1169: };
1.1.1.6   root     1170: /*static*/ const uint8
                   1171: WXStatusPanel::AccessMark24[] = {
                   1172:        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
                   1173:        0,0,0,0,0,0,0,0, 0,1,1,1,1,1,1,0, 0,0,0,0,0,0,0,0,
                   1174:        0,0,0,0,0,0,0,1, 1,2,2,2,2,2,2,1, 1,0,0,0,0,0,0,0,
                   1175:        0,0,0,0,0,1,1,2, 2,2,2,2,2,2,2,2, 2,1,1,0,0,0,0,0,
                   1176:        0,0,0,0,1,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,1,0,0,0,0,
                   1177:        0,0,0,1,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,1,0,0,0,
                   1178:        0,0,0,1,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,1,0,0,0,
                   1179:        0,0,1,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,1,0,0,
                   1180:        0,0,1,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,1,0,0,
                   1181:        0,1,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,1,0,
                   1182:        0,1,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,1,0,
                   1183:        0,1,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,1,0,
                   1184:        0,1,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,1,0,
                   1185:        0,1,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,1,0,
                   1186:        0,1,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,1,0,
                   1187:        0,0,1,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,1,0,0,
                   1188:        0,0,1,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,1,0,0,
                   1189:        0,0,0,1,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,1,0,0,0,
                   1190:        0,0,0,1,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,1,0,0,0,
                   1191:        0,0,0,0,1,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,1,0,0,0,0,
                   1192:        0,0,0,0,0,1,1,2, 2,2,2,2,2,2,2,2, 2,1,1,0,0,0,0,0,
                   1193:        0,0,0,0,0,0,0,1, 1,2,2,2,2,2,2,1, 1,0,0,0,0,0,0,0,
                   1194:        0,0,0,0,0,0,0,0, 0,1,1,1,1,1,1,0, 0,0,0,0,0,0,0,0,
                   1195:        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
                   1196: };
                   1197: 
                   1198: // NEWS LED1
                   1199: void
                   1200: WXStatusPanel::DrawNewsLED1(const Indicator *ind)
                   1201: {
1.1.1.7   root     1202:        const StatusNEWS *stat = dynamic_cast<const StatusNEWS*>(ind->stat);
1.1.1.6   root     1203:        Color bg;
                   1204: 
1.1.1.7   root     1205:        if ((stat->led & NewsCtlrDevice::LED_ORANGE) != 0) {
1.1.1.6   root     1206:                bg = UD_ORANGE;
                   1207:        } else {
                   1208:                bg = BGPANEL;
                   1209:        }
                   1210:        DrawTextLED(ind, UD_BLACK, bg);
                   1211: }
                   1212: 
                   1213: // NEWS LED2
                   1214: void
                   1215: WXStatusPanel::DrawNewsLED2(const Indicator *ind)
                   1216: {
1.1.1.7   root     1217:        const StatusNEWS *stat = dynamic_cast<const StatusNEWS*>(ind->stat);
1.1.1.6   root     1218:        Color bg;
                   1219: 
1.1.1.7   root     1220:        if ((stat->led & NewsCtlrDevice::LED_GREEN) != 0) {
1.1.1.6   root     1221:                bg = UD_GREEN;
                   1222:        } else {
                   1223:                bg = BGPANEL;
                   1224:        }
                   1225:        DrawTextLED(ind, UD_BLACK, bg);
                   1226: }
1.1.1.5   root     1227: 
                   1228: // 電源 LED
1.1       root     1229: void
1.1.1.4   root     1230: WXStatusPanel::DrawPower(const Indicator *ind)
1.1       root     1231: {
1.1.1.5   root     1232:        Color bg;
                   1233: 
                   1234:        if (ispower) {
                   1235:                if (powerled) {
                   1236:                        bg = UD_YELLOW_GREEN;
                   1237:                } else {
                   1238:                        bg = UD_GREY;
                   1239:                }
                   1240:        } else {
                   1241:                // XXX 電源オフが出来たら X68030 なら赤
                   1242:                bg = BGPANEL;
                   1243:        }
                   1244: 
                   1245:        DrawTextLED(ind, UD_BLACK, bg);
1.1       root     1246: }
                   1247: 
                   1248: // 共通の描画処理。
1.1.1.3   root     1249: // ind->text を前景色 fg、背景色 bg で描画するだけの場合。
1.1       root     1250: void
1.1.1.4   root     1251: WXStatusPanel::DrawTextLED(const Indicator *ind, Color fg, Color bg)
1.1       root     1252: {
1.1.1.4   root     1253:        const Rect& rect = ind->rect;
1.1       root     1254: 
                   1255:        // 枠
1.1.1.4   root     1256:        bitmap.FillRect(bg, rect);
                   1257:        bitmap.DrawRect(UD_GREY, rect);
1.1       root     1258: 
                   1259:        SetTextColor(fg, bg);
1.1.1.4   root     1260:        DrawStringSJIS(rect.x + 2, rect.y + 2, ind->text.c_str());
1.1       root     1261:        ResetTextColor();
                   1262: }
                   1263: 
                   1264: // 左ダブルクリックイベント
                   1265: void
                   1266: WXStatusPanel::OnLeftDClick(wxMouseEvent& event)
                   1267: {
                   1268:        const wxPoint& pt = event.GetPosition();
                   1269: 
1.1.1.3   root     1270:        for (auto ind : indicators) {
1.1       root     1271:                // dclick を処理できる枠内なら
1.1.1.4   root     1272:                if (ind->rect.Contains(pt.x, pt.y) && ind->dclick != NULL) {
1.1.1.3   root     1273:                        (this->*(ind->dclick))(ind);
1.1       root     1274:                        break;
                   1275:                }
                   1276:        }
                   1277: }
                   1278: 
                   1279: // パフォーマンスカウンタ枠へのダブルクリック
                   1280: void
1.1.1.3   root     1281: WXStatusPanel::DClickPerf(const Indicator *ind)
1.1       root     1282: {
                   1283:        // モードを切り替える
1.1.1.6   root     1284:        syncer->RequestFullSpeed(!syncer->GetFullSpeed());
1.1       root     1285: }
1.1.1.3   root     1286: 
                   1287: // コンテキストメニューイベント
                   1288: void
                   1289: WXStatusPanel::OnContextMenu(wxContextMenuEvent& event)
                   1290: {
                   1291:        wxPoint pos = ScreenToClient(event.GetPosition());
                   1292: 
                   1293:        for (auto ind : indicators) {
                   1294:                // コンテキストメニューを処理できる枠内なら
1.1.1.5   root     1295:                if (ind->contextmenu && ind->rect.Contains(pos.x, pos.y)) {
                   1296:                        (this->*(ind->contextmenu))(ind);
1.1.1.3   root     1297:                        break;
                   1298:                }
                   1299:        }
                   1300: }
                   1301: 
                   1302: // CD 枠でのコンテキストメニュー
                   1303: void
                   1304: WXStatusPanel::ContextMenuCD(const Indicator *ind)
                   1305: {
1.1.1.5   root     1306:        int scsiid = ind->GetSCSIID();
                   1307:        assertmsg(scsiid >= 0, "id=%d", ind->id);
                   1308: 
1.1.1.6   root     1309:        auto mainframe = dynamic_cast<WXMainFrame*>(GetParent());
                   1310:        auto menu = mainframe->CreateSCSIRemovableMenu(scsiid);
1.1.1.5   root     1311:        if (menu) {
                   1312:                PopupMenu(menu);
                   1313:        }
                   1314: }
                   1315: 
                   1316: // FD 枠でのコンテキストメニュー
                   1317: void
                   1318: WXStatusPanel::ContextMenuFD(const Indicator *ind)
                   1319: {
                   1320:        int unit = ind->GetFDUnit();
                   1321:        assertmsg(unit >= 0, "id=%d", ind->id);
1.1.1.3   root     1322: 
1.1.1.6   root     1323:        auto mainframe = dynamic_cast<WXMainFrame*>(GetParent());
                   1324:        auto menu = mainframe->CreateFDMenu(unit);
1.1.1.3   root     1325:        if (menu) {
                   1326:                PopupMenu(menu);
                   1327:        }
                   1328: }
                   1329: 
                   1330: // SCSI メディア変更イベント (UIMessage イベント)
                   1331: void
                   1332: WXStatusPanel::OnSCSIMediaChanged(wxCommandEvent& event)
                   1333: {
1.1.1.5   root     1334:        Indicator *ind = NULL;
1.1.1.7   root     1335:        bool new_loaded;
1.1.1.3   root     1336: 
                   1337:        // 該当の Indicator を探す (見付かるはず)
1.1.1.5   root     1338:        int scsiid = event.GetInt();
                   1339:        int id = Indicator::SCSI0 + scsiid;
                   1340:        for (auto i : indicators) {
                   1341:                if (i->id == id) {
                   1342:                        ind = i;
1.1.1.3   root     1343:                        break;
                   1344:                }
                   1345:        }
1.1.1.5   root     1346:        assert(ind);
                   1347:        const auto disk = dynamic_cast<const SCSIDisk *>(ind->dev);
1.1.1.3   root     1348: 
                   1349:        // メディアの状態を一旦クリア
1.1.1.7   root     1350:        new_loaded = false;
1.1.1.3   root     1351: 
                   1352:        // ツールチップを設定
                   1353:        SCSI::DevType devtype = disk->GetDevType();
                   1354:        std::string tip = string_format("%s%d: ",
1.1.1.5   root     1355:                SCSI::GetDevTypeName(devtype), scsiid);
1.1.1.3   root     1356:        const std::string& path = disk->GetPathName();
                   1357:        // 同時にメディア状態もセット
                   1358:        if (path.empty()) {
                   1359:                tip += "-";
                   1360:        } else {
                   1361:                tip += path;
1.1.1.7   root     1362:                new_loaded = true;
1.1.1.3   root     1363:        }
                   1364:        switch (disk->GetWriteMode()) {
                   1365:         case SCSIDisk::RW::ReadOnly:
                   1366:                tip += "\n(Read Only)";
                   1367:                break;
                   1368:         case SCSIDisk::RW::WriteProtect:
                   1369:                tip += "\n(Write Protected)";
                   1370:                break;
                   1371:         case SCSIDisk::RW::WriteIgnore:
                   1372:                tip += "\n(Write Ignored)";
                   1373:                break;
                   1374:         default:
                   1375:                break;
                   1376:        }
1.1.1.5   root     1377:        ind->panel->SetToolTip(tip);
                   1378: 
1.1.1.3   root     1379:        // メディアのロード状態が変われば (再配置して) 再描画。
                   1380:        // 通常周期のタイマーによる更新チェックではメディア状態はチェックして
                   1381:        // いないので、ここで自発的に行う。
1.1.1.7   root     1382:        if (scsi_loaded[scsiid] != new_loaded) {
                   1383:                scsi_loaded[scsiid] = new_loaded;
1.1.1.3   root     1384:                Refresh();
                   1385:        }
                   1386: }
1.1.1.5   root     1387: 
                   1388: // FDD メディア変更イベント (UIMessage イベント)
                   1389: void
                   1390: WXStatusPanel::OnFDDMediaChanged(wxCommandEvent& event)
                   1391: {
                   1392:        Indicator *ind = NULL;
                   1393:        bool new_loaded;
                   1394: 
                   1395:        // 該当の Indicator を探す (見付かるはず)
                   1396:        int unit = event.GetInt();
                   1397:        int id = Indicator::FDD0 + unit;
                   1398:        for (auto i : indicators) {
                   1399:                if (i->id == id) {
                   1400:                        ind = i;
                   1401:                        break;
                   1402:                }
                   1403:        }
                   1404:        assert(ind);
                   1405:        const auto fdd = dynamic_cast<const FDDDevice *>(ind->dev);
                   1406: 
                   1407:        // メディアの状態を一旦クリア
                   1408:        new_loaded = false;
                   1409: 
                   1410:        // ツールチップを設定
                   1411:        std::string tip = string_format("FD%d: ", unit);
                   1412:        const std::string& path = fdd->GetPathName();
                   1413:        // 同時にメディア状態もセット
                   1414:        if (path.empty()) {
                   1415:                tip += "-";
                   1416:        } else {
                   1417:                tip += path;
                   1418:                new_loaded = true;
                   1419:        }
                   1420:        switch (fdd->GetWriteMode()) {
                   1421:         case FDDDevice::RW::WriteProtect:
                   1422:                tip += "\n(Write Protected)";
                   1423:                break;
                   1424:         case FDDDevice::RW::WriteIgnore:
                   1425:                tip += "\n(Write Ignored)";
                   1426:                break;
                   1427:         default:
                   1428:                break;
                   1429:        }
                   1430:        ind->panel->SetToolTip(tip);
                   1431: 
                   1432:        // メディアのロード状態が変われば (再配置して) 再描画。
                   1433:        // 通常周期のタイマーによる更新チェックではメディア状態はチェックして
                   1434:        // いないので、ここで自発的に行う。
1.1.1.7   root     1435:        if (fd_loaded[unit] != new_loaded) {
                   1436:                fd_loaded[unit] = new_loaded;
1.1.1.5   root     1437:                Refresh();
                   1438:        }
                   1439: }
                   1440: 
                   1441: // LED 状態変更イベント (UIMessage イベント)。
                   1442: //
                   1443: // 状態変更があったことをプッシュ通知する。主に、通常の更新頻度では
                   1444: // 間に合わない X68030 の電源 LED の 16Hz 点滅用。
                   1445: void
                   1446: WXStatusPanel::OnLEDChanged(wxCommandEvent& event)
                   1447: {
                   1448:        if (UpdateStat()) {
                   1449:                Refresh();
                   1450:        }
                   1451: }

unix.superglobalmegacorp.com

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