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

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

unix.superglobalmegacorp.com

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