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

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

unix.superglobalmegacorp.com

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