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

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

unix.superglobalmegacorp.com

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