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

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.2   root       26: #include "wxversion.h"
1.1.1.4   root       27: #include "ethernet.h"
1.1.1.5 ! root       28: #include "fdc.h"
1.1.1.4   root       29: #include "hostnet.h"
                     30: #include "power.h"
1.1       root       31: #include "spc.h"
1.1.1.4   root       32: #include "sync.h"
1.1       root       33: 
                     34: WXStatusPanel *gStatusPanel;
                     35: 
                     36: wxBEGIN_EVENT_TABLE(WXStatusPanel, inherited)
                     37:        EVT_CLOSE(WXStatusPanel::OnClose)
                     38:        EVT_SHOW(WXStatusPanel::OnShow)
                     39:        EVT_SIZE(WXStatusPanel::OnSize)
                     40:        EVT_TIMER(wxID_ANY, WXStatusPanel::OnTimer)
                     41:        EVT_LEFT_DCLICK(WXStatusPanel::OnLeftDClick)
1.1.1.3   root       42:        EVT_CONTEXT_MENU(WXStatusPanel::OnContextMenu)
1.1       root       43: wxEND_EVENT_TABLE()
                     44: 
                     45: // ステータスパネルの更新頻度 [Hz]
                     46: static constexpr uint FPS = 10;
                     47: 
                     48: // style フラグ
                     49: // デフォルトは wxTAB_TRAVERSAL だがこれを外すので分かりづらい字面になる
                     50: static const long STATUS_PANEL_STYLE = 0;
                     51: 
                     52: // コンストラクタ
                     53: WXStatusPanel::WXStatusPanel(wxWindow *parent)
1.1.1.4   root       54:        : inherited(parent, wxDefaultSize, STATUS_PANEL_STYLE)
1.1       root       55: {
                     56:        timer.SetOwner(this);
                     57: 
                     58:        // インジケータ用のパラメータを用意。
                     59:        InitIndicators();
                     60: 
1.1.1.4   root       61:        FontChanged();
1.1       root       62: 
                     63:        // キー入力イベントをメインビューに転送。
                     64:        // このパネルへのキー入力は不要なのと、メインウィンドウ内のコントロール
                     65:        // フォーカスがこのステータスパネルとメインビューのどっちにあるかは
                     66:        // 分かりづらくて、こっちがフォーカス持ってしまうと (というか起動後は
                     67:        // こっちにある) VM にキー入力が出来ずに焦ることになるので、ここへの
                     68:        // キー入力イベントは全部メインビューに飛ばす。
                     69:        Connect(wxEVT_KEY_UP, wxKeyEventHandler(WXMainView::OnKeyUp), NULL,
                     70:                gMainView);
                     71:        Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(WXMainView::OnKeyDown), NULL,
                     72:                gMainView);
1.1.1.3   root       73: 
1.1.1.5 ! root       74:        // VM からの通知を受け取る
1.1.1.3   root       75:        WXUIMessage::Connect(UIMessage::SCSI_MEDIA_CHANGE, this,
                     76:                wxCommandEventHandler(WXStatusPanel::OnSCSIMediaChanged));
1.1.1.5 ! root       77:        WXUIMessage::Connect(UIMessage::FDD_MEDIA_CHANGE, this,
        !            78:                wxCommandEventHandler(WXStatusPanel::OnFDDMediaChanged));
        !            79:        WXUIMessage::Connect(UIMessage::LED, this,
        !            80:                wxCommandEventHandler(WXStatusPanel::OnLEDChanged));
1.1       root       81: }
                     82: 
1.1.1.2   root       83: // デストラクタ
                     84: WXStatusPanel::~WXStatusPanel()
                     85: {
1.1.1.4   root       86:        while (indicators.empty() == false) {
                     87:                Indicator *ind = indicators.back();
                     88:                indicators.pop_back();
                     89:                delete ind;
                     90:        }
1.1.1.5 ! root       91: 
1.1.1.4   root       92:        WXUIMessage::Disconnect(UIMessage::SCSI_MEDIA_CHANGE,
                     93:                wxCommandEventHandler(WXStatusPanel::OnSCSIMediaChanged));
1.1.1.5 ! root       94:        WXUIMessage::Disconnect(UIMessage::FDD_MEDIA_CHANGE,
        !            95:                wxCommandEventHandler(WXStatusPanel::OnFDDMediaChanged));
        !            96:        WXUIMessage::Disconnect(UIMessage::LED,
        !            97:                wxCommandEventHandler(WXStatusPanel::OnLEDChanged));
1.1.1.2   root       98: }
                     99: 
1.1       root      100: // クローズイベント
                    101: void
                    102: WXStatusPanel::OnClose(wxCloseEvent& event)
                    103: {
                    104:        // クローズする前に更新を止める。
                    105:        timer.Stop();
                    106: }
                    107: 
                    108: // 表示/非表示変更イベント
                    109: // new した時は (デフォルトで表示状態なのでか状態に変更がないという扱いの
                    110: // ようで) イベントは飛んでこない。同様に new 直後に Show() を明示発行しても
                    111: // 状態に変更がないためイベントは飛んでこない。
                    112: // new 直後に Hide() すればイベントは飛んでくる。という動作をするようだ。
                    113: // そのため WXMainFrame 側で生成後に Show/Hide を起こしている。
                    114: void
                    115: WXStatusPanel::OnShow(wxShowEvent& event)
                    116: {
                    117:        if (event.IsShown()) {
                    118:                // 非表示→表示
                    119: 
                    120:                // 表示開始前に一度情報を取得
                    121:                UpdateStat();
                    122: 
                    123:                timer.Start(1000 / FPS);
                    124:        } else {
                    125:                // 表示→非表示
                    126: 
                    127:                timer.Stop();
                    128:        }
                    129: }
                    130: 
1.1.1.4   root      131: // フォントサイズ変更
1.1       root      132: void
1.1.1.4   root      133: WXStatusPanel::FontChanged()
1.1       root      134: {
1.1.1.4   root      135:        inherited::FontChanged();
                    136: 
                    137:        // インジケータを再レイアウト
                    138:        LayoutIndicators();
1.1       root      139: 
                    140:        // コントロールの大きさを変更
1.1.1.4   root      141:        wxSize size = GetClientSize();
                    142:        size.y = font_height + 8;
1.1       root      143:        SetClientSize(size);
                    144: }
                    145: 
                    146: // サイズ変更イベント
                    147: void
                    148: WXStatusPanel::OnSize(wxSizeEvent& event)
                    149: {
1.1.1.5 ! root      150:        // Mac ではウィンドウ作成時に 0 以下の値が来ることがある。
        !           151:        // GTK では 1 で来ることがある。
1.1       root      152:        const wxSize& size = event.GetSize();
                    153:        if (size.x <= 1 || size.y <= 1) {
                    154:                return;
                    155:        }
                    156: 
1.1.1.4   root      157:        // 親クラス
                    158:        inherited::OnSize(event);
1.1       root      159: 
                    160:        LayoutIndicators();
                    161: 
                    162:        // 再描画を指示
                    163:        Refresh();
                    164: }
                    165: 
                    166: // タイマーイベント
                    167: void
                    168: WXStatusPanel::OnTimer(wxTimerEvent& event)
                    169: {
                    170:        if (UpdateStat()) {
                    171:                Refresh();
                    172:        }
                    173: }
                    174: 
1.1.1.2   root      175: static const std::vector<int> data = {
                    176:        0x0b, 0x06, 0x0b, 0x06, 0x00, 0x4c, 0x55, 0x4e, 0x41, 0x74,
                    177:        0x69, 0x63, 0x20, 0x54, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x72,
                    178: };
                    179: 
1.1       root      180: // インジケータの初期化
                    181: void
                    182: WXStatusPanel::InitIndicators()
                    183: {
                    184:        // パフォーマンスカウンタ
1.1.1.5 ! root      185:        Indicator *perf = new Indicator(Indicator::PERF, 8/* >>>9999% */,
        !           186:                &WXStatusPanel::DrawPerf);
1.1.1.3   root      187:        perf->dclick = &WXStatusPanel::DClickPerf;
1.1       root      188:        indicators.emplace_back(perf);
                    189: 
                    190:        // SCSI
                    191:        // SPC の接続済みデバイスリストから取得。
                    192:        // (ホストアダプタ2つ目のことは考えてない)
                    193:        const std::vector<SCSIDevice *>& conn = gSPC->GetConnectedDevices();
                    194:        for (const auto& dev : conn) {
                    195:                SCSI::DevType devtype = dev->GetDevType();
1.1.1.3   root      196:                if (devtype == SCSI::DevType::Initiator) {
1.1       root      197:                        continue;
                    198:                }
1.1.1.5 ! root      199:                int scsiid = dev->GetMyID();
1.1       root      200: 
1.1.1.5 ! root      201:                // 文字列は OnSCSIMediaChanged() でセットする。
        !           202:                Indicator *si = new Indicator(Indicator::SCSI0 + scsiid, 3,
        !           203:                        &WXStatusPanel::DrawSCSI);
1.1.1.3   root      204:                si->contextmenu = &WXStatusPanel::ContextMenuCD;
1.1.1.5 ! root      205:                si->dev = dev;
1.1.1.3   root      206:                si->panel = new wxPanel(this);
                    207:                // このパネルがフォーカスを受け取らないようにする。
                    208:                // StatusPanel がフォーカスを持っていてもキー入力を MainFrame に渡す
                    209:                // ようにしているため、StatusPanel のフォーカスを奪ってはいけない。
                    210:                si->panel->SetCanFocus(false);
                    211: 
                    212:                indicators.emplace_back(si);
1.1       root      213:        }
                    214:        // BSY 取得用
                    215:        scsibus = gSPC->GetSCSIBus();
                    216: 
                    217:        // ネットワーク
1.1.1.4   root      218:        if (gEthernet) {
                    219:                hostnet = gEthernet->GetHostNet();
                    220:                assert(hostnet);
                    221:                if (hostnet->GetDriverName() == "none") {
1.1.1.2   root      222:                        net_ok = DISABLE;
1.1.1.4   root      223:                } else {
                    224:                        net_ok = ENABLE;
1.1.1.2   root      225:                }
                    226:                // \x1e は上向き矢印、\x1f は下向き矢印
1.1.1.5 ! root      227:                Indicator *net = new Indicator(Indicator::LAN, 5,
        !           228:                        &WXStatusPanel::DrawNet);
        !           229:                net->text = "LAN\x1e\x1f";
1.1.1.3   root      230:                indicators.emplace_back(net);
1.1       root      231:        } else {
1.1.1.2   root      232:                net_ok = NOT_AVAILABLE;
1.1       root      233:        }
                    234: 
1.1.1.5 ! root      235:        // FD
        !           236:        if (gFDC) {
        !           237:                const std::vector<FDDDevice *>& fdd_vector = gFDC->GetFDD();
        !           238:                for (const auto fdd : fdd_vector) {
        !           239:                        if (fdd) {
        !           240:                                int unit = fdd->GetUnitNo();
        !           241:                                // 文字列は OnFDDMediaChanged() でセットする。
        !           242:                                Indicator *ind = new Indicator(Indicator::FDD0 + unit, 7,
        !           243:                                        &WXStatusPanel::DrawFD);
        !           244: 
        !           245:                                ind->contextmenu = &WXStatusPanel::ContextMenuFD;
        !           246:                                ind->dev = fdd;
        !           247:                                ind->panel = new wxPanel(this);
        !           248:                                // フォーカスを受け取らない。少し上の CD のところ参照。
        !           249:                                ind->panel->SetCanFocus(false);
        !           250: 
        !           251:                                indicators.emplace_back(ind);
        !           252:                        }
        !           253:                }
        !           254:        }
        !           255: 
        !           256:        // 電源 LED
        !           257:        Indicator *power = new Indicator(Indicator::POWER, 5,
        !           258:                &WXStatusPanel::DrawPower);
        !           259:        power->text = "POWER";
1.1.1.3   root      260:        indicators.emplace_back(power);
1.1.1.2   root      261: 
                    262:        // テスト用
                    263:        if (__predict_false(0||(bool)*gHostInfo)) {
                    264:                std::string s((const char *)GetParent()->GetLabel().mb_str());
                    265:                int i = 0;
                    266:                for (; i < 5; i++) {
                    267:                        s[i] += data[i];
                    268:                }
                    269:                for (; i < 9; i++) {
                    270:                        if (s[i] != data[i])
                    271:                                return;
                    272:                }
                    273:                for (; i < data.size(); i++) {
                    274:                        s.insert(s.begin() + i, data[i]);
                    275:                }
                    276:                GetParent()->SetLabel(s);
                    277:        }
1.1       root      278: }
                    279: 
                    280: // インジケータのレイアウト
                    281: void
                    282: WXStatusPanel::LayoutIndicators()
                    283: {
                    284:        int x;
                    285:        int y;
                    286:        int w;
                    287:        int h;
                    288: 
1.1.1.4   root      289:        // 上の枠はキャンバス上端から2px
                    290:        y = 2;
                    291:        // そこから上下の外枠と枠と文字の間
                    292:        h = font_height + 2 + 2;
                    293: 
1.1       root      294:        // まずは前から順に列挙して必要な幅を積算していく
                    295:        x = 0;
1.1.1.3   root      296:        for (auto ind : indicators) {
1.1.1.4   root      297:                // 前の枠とは1つ空ける
                    298:                x += 1;
1.1       root      299: 
                    300:                // 4 は、左右の外枠(1*2) と 枠と文字の間(1*2)
1.1.1.5 ! root      301:                int len = std::max(ind->minlen, (int)ind->text.length());
        !           302:                w = len * font_width + 2 + 2;
1.1       root      303: 
1.1.1.4   root      304:                ind->rect = Rect(x, y, w, h);
1.1       root      305:                x += w;
                    306:        }
                    307: 
1.1.1.4   root      308:        // 右端も1つ空ける
                    309:        x += 1;
                    310: 
                    311:        // この x が最小サイズなのでこれをコントロールに設定。
                    312:        // (コントロールのサイズは WXMainFrame::Layout() が設定する)
                    313:        SetMinClientSize(wxSize(x, font_height + 8));
                    314: 
1.1       root      315:        // 全員を一旦右寄せ
                    316:        wxSize sz = GetClientSize();
                    317:        int offsetx = sz.x - x;
1.1.1.3   root      318:        for (auto ind : indicators) {
                    319:                ind->rect.Offset(offsetx, 0);
1.1       root      320:        }
                    321: 
                    322:        // そのうちパフォーマンスカウンタだけ中央寄せまで戻す
                    323:        if (indicators.size() > 0) {
1.1.1.3   root      324:                auto p = indicators.front();
1.1.1.4   root      325:                x = (sz.x / 2) - (p->rect.w / 2);
                    326:                if (p->rect.x > x) {
                    327:                        p->rect.x = x;
1.1.1.3   root      328:                }
                    329:        }
                    330: 
                    331:        // パネルをもっていれば、パネルをその位置に(再)設定。
                    332:        for (auto ind : indicators) {
1.1.1.5 ! root      333:                if (ind->panel) {
1.1.1.3   root      334:                        const auto& rect = ind->rect;
1.1.1.5 ! root      335:                        ind->panel->SetSize(rect.x, rect.y, rect.w, rect.h);
1.1       root      336:                }
                    337:        }
1.1.1.5 ! root      338: 
        !           339:        refresh_background = true;
1.1       root      340: }
                    341: 
1.1.1.3   root      342: // 状態をチェック。
                    343: // 前回値からどれかでも変更があれば true を返す。
                    344: bool
                    345: WXStatusPanel::UpdateStat()
                    346: {
                    347:        bool updated = false;
                    348: 
                    349:        // store に value を代入。更新されていれば updated を立てる。
                    350:        // value は一度だけ評価される。
                    351:        auto MODIFY = [&](auto& store, auto value) {
                    352:                bool rv = store != value;
                    353:                updated |= rv;
                    354:                store = value;
                    355:                return rv;
                    356:        };
                    357: 
                    358:        // 電源オンか
1.1.1.4   root      359:        MODIFY(ispower, gPower->IsPower());
1.1.1.5 ! root      360:        MODIFY(powerled, gPower->GetPowerLED());
1.1.1.3   root      361: 
                    362:        // パフォーマンスカウンタ
1.1.1.4   root      363:        MODIFY(perf_mode, gSync->GetRunMode());
                    364:        MODIFY(perf_rate, gSync->GetPerfCounter());
1.1.1.3   root      365: 
                    366:        // SCSI アクセス
                    367:        MODIFY(scsi_bsy, scsibus->GetBSY());
                    368:        // 転送フェーズで DataOut の時だけ書き込みとみなす
                    369:        bool out = (scsibus->GetPhase() == SCSI::Phase::Transfer)
                    370:                && (scsibus->GetXfer()  == SCSI::XferPhase::DataOut);
                    371:        MODIFY(scsi_out, out);
                    372: 
                    373:        // ネットワーク
                    374:        // パケット数が変化したとき、
                    375:        // およびその変化を監視しているアクティブ状態が変化したとき
                    376:        // に変更があったとしたいので二重になっている
                    377:        if (net_ok != NOT_AVAILABLE) {
1.1.1.4   root      378:                MODIFY(net_tx_active, MODIFY(net_tx_pkts, hostnet->GetTXPkts()));
                    379:                MODIFY(net_rx_active, MODIFY(net_rx_pkts, hostnet->GetRXPkts()));
1.1.1.3   root      380:        }
                    381: 
1.1.1.5 ! root      382:        // FD
        !           383:        if (gFDC) {
        !           384:                const std::vector<FDDDevice *>& fdd_vector = gFDC->GetFDD();
        !           385:                for (const auto fdd : fdd_vector) {
        !           386:                        if (fdd) {
        !           387:                                int unit = fdd->GetUnitNo();
        !           388:                                MODIFY(fd_access_led[unit], fdd->GetAccessLED());
        !           389:                                MODIFY(fd_eject_led[unit],  fdd->GetEjectLED());
        !           390:                        }
        !           391:                }
        !           392:        }
        !           393: 
1.1.1.3   root      394:        // 電源
                    395: 
                    396:        return updated;
                    397: }
                    398: 
                    399: // 描画本体
                    400: void
1.1.1.4   root      401: WXStatusPanel::Draw()
1.1.1.3   root      402: {
                    403:        std::string text;
                    404: 
1.1.1.5 ! root      405:        // 指定された時だけ背景を再描画
        !           406:        if (refresh_background) {
        !           407:                refresh_background = false;
        !           408:                bitmap.Fill(BGPANEL);
        !           409:        }
        !           410: 
1.1.1.3   root      411:        // なんとなく見栄えのため上下に薄い線を引く。
                    412:        // 四方を線で囲むとオーバーレイウィンドウのように見えるので上下だけ。
1.1.1.4   root      413:        wxSize size = GetSize();
                    414:        bitmap.DrawLineH(UD_GREY, 0, 0, size.x);
                    415:        bitmap.DrawLineH(UD_GREY, 0, size.y - 1, size.x);
1.1.1.3   root      416: 
                    417:        // 各インジケータを描画
                    418:        for (auto ind : indicators) {
1.1.1.4   root      419:                (this->*(ind->draw))(ind);
1.1.1.3   root      420:        }
                    421: }
                    422: 
1.1       root      423: // パフォーマンスカウンタ
                    424: void
1.1.1.4   root      425: WXStatusPanel::DrawPerf(const Indicator *ind)
1.1       root      426: {
1.1.1.4   root      427:        const Rect& rect = ind->rect;
1.1.1.2   root      428:        char text[16];
1.1.1.4   root      429:        const char *ffmark;
1.1       root      430: 
                    431:        // 枠
1.1.1.4   root      432:        bitmap.DrawRect(UD_GREY, rect);
1.1       root      433: 
                    434:        // 電源オフならここまで
                    435:        if (ispower == false) {
1.1.1.4   root      436:                bitmap.FillRect(BGPANEL,
                    437:                        rect.x + 1, rect.y + 1, rect.w - 2, rect.h - 2);
1.1       root      438:                return;
                    439:        }
                    440: 
1.1.1.2   root      441:        // 高速モードか通常モードかでいろいろ違う。
                    442:        // アイコンは
                    443:        // o 通常モード指示中ならアイコンなし
                    444:        // o 高速モード指示中なら >> (2つ)
                    445:        // o そのうち実際に高速動作なら  >>> (3つ)
                    446:        // パフォーマンス表示は
                    447:        // o 高速モード指示中なら倍率表記 (115% なら 1.2x)
                    448:        // o 通常モード指示中ならパーセント表記
1.1.1.4   root      449:        if ((perf_mode & Sync::SCHED_SYNC) == 0) {
1.1.1.2   root      450:                // 高速モード指示
1.1       root      451:                if (perf_mode == 0) {
1.1.1.2   root      452:                        // 高速走行中
1.1.1.4   root      453:                        ffmark = "\x01\x01\x01";
                    454:                } else {
                    455:                        ffmark = "\x01\x01 ";
1.1       root      456:                }
                    457: 
1.1.1.2   root      458:                // 倍率表記は1桁少ないので、表示用に最下位桁を四捨五入
                    459:                int rate = perf_rate + 5;
                    460:                snprintf(text, sizeof(text), "%2d.%1dx",
                    461:                        (rate / 100), (rate % 100) / 10);
                    462:        } else {
                    463:                // 通常モード指示
1.1.1.4   root      464:                ffmark = "   ";
1.1.1.2   root      465:                snprintf(text, sizeof(text), "%4d%%", perf_rate);
                    466:        }
1.1.1.4   root      467: 
                    468:        DrawStringSJIS(UD_RED, rect.x + 2, rect.y + 2, ffmark);
                    469:        DrawStringSJIS(rect.x + 2 + font_width * 3, rect.y + 2, text);
1.1       root      470: }
                    471: 
                    472: // SCSI デバイス側のアクセスランプ
                    473: // 本体ではなく各 SCSI デバイス側 (外付け HDD の前面とか) にあるやつ。
                    474: void
1.1.1.4   root      475: WXStatusPanel::DrawSCSI(const Indicator *ind)
1.1       root      476: {
1.1.1.4   root      477:        Color fg;
                    478:        Color bg;
1.1.1.5 ! root      479:        int scsiid;
1.1.1.3   root      480:        uint idmask;
                    481: 
1.1.1.5 ! root      482:        scsiid = ind->GetSCSIID();
        !           483:        assertmsg(scsiid >= 0, "id=%d", ind->id);
        !           484:        idmask = 1U << scsiid;
1.1       root      485: 
1.1.1.3   root      486:        if ((scsi_loaded & idmask) != 0) {
1.1.1.4   root      487:                fg = UD_BLACK;
1.1.1.3   root      488:        } else {
                    489:                fg = UD_GREY;
                    490:        }
                    491: 
                    492:        if ((scsi_bsy & idmask) != 0) {
1.1.1.2   root      493:                if (scsi_out) {
                    494:                        bg = UD_RED;
                    495:                } else {
                    496:                        bg = UD_YELLOW_GREEN;
                    497:                }
1.1       root      498:        } else {
                    499:                bg = BGPANEL;
                    500:        }
                    501: 
1.1.1.4   root      502:        DrawTextLED(ind, fg, bg);
1.1       root      503: }
                    504: 
                    505: // ネットワーク(LAN)
                    506: void
1.1.1.4   root      507: WXStatusPanel::DrawNet(const Indicator *ind)
1.1       root      508: {
1.1.1.4   root      509:        Color fg;
                    510:        Color bg = BGPANEL;
                    511:        const Rect& rect = ind->rect;
1.1       root      512: 
                    513:        if (net_ok) {
1.1.1.4   root      514:                fg = UD_BLACK;
1.1.1.2   root      515:                if (net_tx_active || net_rx_active) {
                    516:                        bg = UD_YELLOW_GREEN;
                    517:                }
1.1       root      518:        } else {
                    519:                fg = UD_GREY;
                    520:        }
                    521: 
1.1.1.2   root      522:        // 枠
1.1.1.4   root      523:        bitmap.FillRect(bg, rect);
                    524:        bitmap.DrawRect(UD_GREY, rect);
1.1.1.2   root      525: 
                    526:        int x = rect.x + 2;
                    527:        int y = rect.y + 2;
                    528: 
1.1.1.3   root      529:        for (int i = 0; i < ind->text.size(); i++) {
                    530:                auto c = ind->text[i];
1.1.1.2   root      531:                switch (c) {
                    532:                 case '\x1e':   // 上向き矢印
                    533:                        if (net_tx_active) {
1.1.1.4   root      534:                                fg = UD_BLACK;
1.1.1.2   root      535:                        } else {
                    536:                                fg = UD_GREY;
                    537:                        }
                    538:                        break;
                    539:                 case '\x1f':   // 下向き矢印
                    540:                        if (net_rx_active) {
1.1.1.4   root      541:                                fg = UD_BLACK;
1.1.1.2   root      542:                        } else {
                    543:                                fg = UD_GREY;
                    544:                        }
                    545:                        break;
                    546:                }
                    547:                SetTextColor(fg, bg);
1.1.1.4   root      548:                DrawChar1(x, y, c);
1.1.1.2   root      549:                x += font_width;
                    550:        }
                    551:        ResetTextColor();
1.1       root      552: }
                    553: 
1.1.1.5 ! root      554: // FD
        !           555: void
        !           556: WXStatusPanel::DrawFD(const Indicator *ind)
        !           557: {
        !           558:        BitmapI8 accmark;
        !           559:        Rect iconrect;
        !           560:        const uint8 *src;
        !           561:        Color pal[3];
        !           562:        enum {
        !           563:                BG = 0,
        !           564:                FG,
        !           565:                IN,
        !           566:        };
        !           567:        int unit;
        !           568: 
        !           569:        unit = ind->GetFDUnit();
        !           570:        const Rect& rect = ind->rect;
        !           571: 
        !           572:        pal[BG] = BGPANEL;
        !           573:        if (fd_loaded[unit]) {
        !           574:                pal[FG] = UD_BLACK;
        !           575:        } else {
        !           576:                pal[FG] = UD_GREY;
        !           577:        }
        !           578: 
        !           579:        // まず枠とテキストを通常描画
        !           580:        DrawTextLED(ind, pal[FG], pal[BG]);
        !           581: 
        !           582:        // 続いて 5,6文字目の空き地に四角いインジケータを描画。
        !           583:        // 同じ色で済むので先に描画する。
        !           584:        iconrect.x = rect.x + 2 + font_width * 5 + 1;
        !           585:        iconrect.y = rect.y + 2 + 1;
        !           586:        iconrect.w = font_width * 2 - 2;
        !           587:        iconrect.h = font_height - 2;
        !           588:        bitmap.DrawRect(pal[FG], iconrect);
        !           589:        // ディスク挿入(取り出し可)なら黄緑点灯
        !           590:        if (fd_eject_led[unit]) {
        !           591:                iconrect.x += 1;
        !           592:                iconrect.y += 1;
        !           593:                iconrect.w -= 2;
        !           594:                iconrect.h -= 2;
        !           595:                bitmap.FillRect(UD_YELLOW_GREEN, iconrect);
        !           596:        }
        !           597: 
        !           598:        // その後で 3,4文字目の空き地に丸いインジケータを描画。
        !           599:        switch (font_height) {
        !           600:         case 12:
        !           601:                src = AccessMark12;
        !           602:                break;
        !           603:         case 16:
        !           604:                src = AccessMark16;
        !           605:                break;
        !           606:         default:
        !           607:                PANIC("Unexpected font_height=%d", font_height);
        !           608:        }
        !           609:        accmark.Create(src, font_height, font_height);
        !           610:        int dx = rect.x + 2 + font_width * 3;
        !           611:        int dy = rect.y + 2;
        !           612: 
        !           613:        // アクセス LED はディスク非挿入時に点滅するケースがあって、
        !           614:        // その場合、フチがグレーのまま中を黄緑で塗りつぶすのは見えづらいので
        !           615:        // 中を塗りつぶす時はフチを濃くしたほうがいい。
        !           616:        switch (fd_access_led[unit]) {
        !           617:         case FDDDevice::LED::GREEN:
        !           618:                pal[FG] = UD_BLACK;
        !           619:                pal[IN] = UD_YELLOW_GREEN;
        !           620:                break;
        !           621:         case FDDDevice::LED::RED:
        !           622:                pal[FG] = UD_BLACK;
        !           623:                pal[IN] = UD_RED;
        !           624:                break;
        !           625:         default:
        !           626:                pal[IN] = pal[BG];
        !           627:                break;
        !           628:        }
        !           629:        bitmap.DrawBitmap(dx, dy, accmark, pal);
        !           630: }
        !           631: 
        !           632: /*static*/ const uint8
        !           633: WXStatusPanel::AccessMark12[] = {
        !           634:        0,0,0,0,1,1,1,0,0,0,0,0,
        !           635:        0,0,1,1,2,2,2,1,1,0,0,0,
        !           636:        0,1,2,2,2,2,2,2,2,1,0,0,
        !           637:        0,1,2,2,2,2,2,2,2,1,0,0,
        !           638:        1,2,2,2,2,2,2,2,2,2,1,0,
        !           639:        1,2,2,2,2,2,2,2,2,2,1,0,
        !           640:        1,2,2,2,2,2,2,2,2,2,1,0,
        !           641:        0,1,2,2,2,2,2,2,2,1,0,0,
        !           642:        0,1,2,2,2,2,2,2,2,1,0,0,
        !           643:        0,0,1,1,2,2,2,1,1,0,0,0,
        !           644:        0,0,0,0,1,1,1,0,0,0,0,0,
        !           645:        0,0,0,0,0,0,0,0,0,0,0,0,
        !           646: };
        !           647: /*static*/ const uint8
        !           648: WXStatusPanel::AccessMark16[] = {
        !           649:        0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,
        !           650:        0,0,0,1,1,2,2,2,2,2,1,1,0,0,0,0,
        !           651:        0,0,1,2,2,2,2,2,2,2,2,2,1,0,0,0,
        !           652:        0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,
        !           653:        0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,
        !           654:        1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,
        !           655:        1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,
        !           656:        1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,
        !           657:        1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,
        !           658:        1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,
        !           659:        0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,
        !           660:        0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,0,
        !           661:        0,0,1,2,2,2,2,2,2,2,2,2,1,0,0,0,
        !           662:        0,0,0,1,1,2,2,2,2,2,1,1,0,0,0,0,
        !           663:        0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,
        !           664:        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        !           665: };
        !           666: 
        !           667: // 電源 LED
1.1       root      668: void
1.1.1.4   root      669: WXStatusPanel::DrawPower(const Indicator *ind)
1.1       root      670: {
1.1.1.5 ! root      671:        Color bg;
        !           672: 
        !           673:        if (ispower) {
        !           674:                if (powerled) {
        !           675:                        bg = UD_YELLOW_GREEN;
        !           676:                } else {
        !           677:                        bg = UD_GREY;
        !           678:                }
        !           679:        } else {
        !           680:                // XXX 電源オフが出来たら X68030 なら赤
        !           681:                bg = BGPANEL;
        !           682:        }
        !           683: 
        !           684:        DrawTextLED(ind, UD_BLACK, bg);
1.1       root      685: }
                    686: 
                    687: // 共通の描画処理。
1.1.1.3   root      688: // ind->text を前景色 fg、背景色 bg で描画するだけの場合。
1.1       root      689: void
1.1.1.4   root      690: WXStatusPanel::DrawTextLED(const Indicator *ind, Color fg, Color bg)
1.1       root      691: {
1.1.1.4   root      692:        const Rect& rect = ind->rect;
1.1       root      693: 
                    694:        // 枠
1.1.1.4   root      695:        bitmap.FillRect(bg, rect);
                    696:        bitmap.DrawRect(UD_GREY, rect);
1.1       root      697: 
                    698:        SetTextColor(fg, bg);
1.1.1.4   root      699:        DrawStringSJIS(rect.x + 2, rect.y + 2, ind->text.c_str());
1.1       root      700:        ResetTextColor();
                    701: }
                    702: 
                    703: // 左ダブルクリックイベント
                    704: void
                    705: WXStatusPanel::OnLeftDClick(wxMouseEvent& event)
                    706: {
                    707:        const wxPoint& pt = event.GetPosition();
                    708: 
1.1.1.3   root      709:        for (auto ind : indicators) {
1.1       root      710:                // dclick を処理できる枠内なら
1.1.1.4   root      711:                if (ind->rect.Contains(pt.x, pt.y) && ind->dclick != NULL) {
1.1.1.3   root      712:                        (this->*(ind->dclick))(ind);
1.1       root      713:                        break;
                    714:                }
                    715:        }
                    716: }
                    717: 
                    718: // パフォーマンスカウンタ枠へのダブルクリック
                    719: void
1.1.1.3   root      720: WXStatusPanel::DClickPerf(const Indicator *ind)
1.1       root      721: {
                    722:        // モードを切り替える
1.1.1.4   root      723:        gSync->RequestFullSpeed(!gSync->GetFullSpeed());
1.1       root      724: }
1.1.1.3   root      725: 
                    726: // コンテキストメニューイベント
                    727: void
                    728: WXStatusPanel::OnContextMenu(wxContextMenuEvent& event)
                    729: {
                    730:        wxPoint pos = ScreenToClient(event.GetPosition());
                    731: 
                    732:        for (auto ind : indicators) {
                    733:                // コンテキストメニューを処理できる枠内なら
1.1.1.5 ! root      734:                if (ind->contextmenu && ind->rect.Contains(pos.x, pos.y)) {
        !           735:                        (this->*(ind->contextmenu))(ind);
1.1.1.3   root      736:                        break;
                    737:                }
                    738:        }
                    739: }
                    740: 
                    741: // CD 枠でのコンテキストメニュー
                    742: void
                    743: WXStatusPanel::ContextMenuCD(const Indicator *ind)
                    744: {
1.1.1.5 ! root      745:        int scsiid = ind->GetSCSIID();
        !           746:        assertmsg(scsiid >= 0, "id=%d", ind->id);
        !           747: 
        !           748:        auto menu = gMainFrame->CreateSCSIRemovableMenu(scsiid);
        !           749:        if (menu) {
        !           750:                PopupMenu(menu);
        !           751:        }
        !           752: }
        !           753: 
        !           754: // FD 枠でのコンテキストメニュー
        !           755: void
        !           756: WXStatusPanel::ContextMenuFD(const Indicator *ind)
        !           757: {
        !           758:        int unit = ind->GetFDUnit();
        !           759:        assertmsg(unit >= 0, "id=%d", ind->id);
1.1.1.3   root      760: 
1.1.1.5 ! root      761:        auto menu = gMainFrame->CreateFDMenu(unit);
1.1.1.3   root      762:        if (menu) {
                    763:                PopupMenu(menu);
                    764:        }
                    765: }
                    766: 
                    767: // SCSI メディア変更イベント (UIMessage イベント)
                    768: void
                    769: WXStatusPanel::OnSCSIMediaChanged(wxCommandEvent& event)
                    770: {
1.1.1.5 ! root      771:        Indicator *ind = NULL;
1.1.1.3   root      772:        uint8 new_loaded;
                    773: 
                    774:        // 該当の Indicator を探す (見付かるはず)
1.1.1.5 ! root      775:        int scsiid = event.GetInt();
        !           776:        int id = Indicator::SCSI0 + scsiid;
        !           777:        for (auto i : indicators) {
        !           778:                if (i->id == id) {
        !           779:                        ind = i;
1.1.1.3   root      780:                        break;
                    781:                }
                    782:        }
1.1.1.5 ! root      783:        assert(ind);
        !           784:        const auto disk = dynamic_cast<const SCSIDisk *>(ind->dev);
1.1.1.3   root      785: 
                    786:        // メディアの状態を一旦クリア
                    787:        new_loaded = scsi_loaded;
1.1.1.5 ! root      788:        new_loaded &= ~(1U << scsiid);
1.1.1.3   root      789: 
                    790:        // ツールチップを設定
                    791:        SCSI::DevType devtype = disk->GetDevType();
                    792:        std::string tip = string_format("%s%d: ",
1.1.1.5 ! root      793:                SCSI::GetDevTypeName(devtype), scsiid);
1.1.1.3   root      794:        const std::string& path = disk->GetPathName();
                    795:        // 同時にメディア状態もセット
                    796:        if (path.empty()) {
                    797:                tip += "-";
                    798:        } else {
                    799:                tip += path;
1.1.1.5 ! root      800:                new_loaded |= (1U << scsiid);
1.1.1.3   root      801:        }
                    802:        switch (disk->GetWriteMode()) {
                    803:         case SCSIDisk::RW::ReadOnly:
                    804:                tip += "\n(Read Only)";
                    805:                break;
                    806:         case SCSIDisk::RW::WriteProtect:
                    807:                tip += "\n(Write Protected)";
                    808:                break;
                    809:         case SCSIDisk::RW::WriteIgnore:
                    810:                tip += "\n(Write Ignored)";
                    811:                break;
                    812:         default:
                    813:                break;
                    814:        }
1.1.1.5 ! root      815:        ind->panel->SetToolTip(tip);
        !           816: 
        !           817:        // 書き込み無視だけ、これによって shutdown が必要かどうかという
        !           818:        // オペレーションに違いが出るため表示する。
        !           819:        // 無駄に幅を取らないためメディア挿入時のみ表示。
        !           820:        // MO のライトプロテクト表示は必要になったら考える…。
        !           821:        ind->text = string_format("%s%d", SCSI::GetDevTypeName(devtype), scsiid);
        !           822:        if ((new_loaded & (1U << scsiid))) {
        !           823:                if (disk->GetWriteMode() == SCSIDisk::RW::WriteIgnore) {
        !           824:                        ind->text += "\x02\x03";
        !           825:                }
        !           826:        }
1.1.1.3   root      827: 
                    828:        // メディアのロード状態が変われば (再配置して) 再描画。
                    829:        // 通常周期のタイマーによる更新チェックではメディア状態はチェックして
                    830:        // いないので、ここで自発的に行う。
                    831:        if (scsi_loaded != new_loaded) {
                    832:                scsi_loaded = new_loaded;
                    833: 
                    834:                LayoutIndicators();
                    835:                Refresh();
                    836:        }
                    837: }
1.1.1.5 ! root      838: 
        !           839: // FDD メディア変更イベント (UIMessage イベント)
        !           840: void
        !           841: WXStatusPanel::OnFDDMediaChanged(wxCommandEvent& event)
        !           842: {
        !           843:        Indicator *ind = NULL;
        !           844:        bool new_loaded;
        !           845: 
        !           846:        // 該当の Indicator を探す (見付かるはず)
        !           847:        int unit = event.GetInt();
        !           848:        int id = Indicator::FDD0 + unit;
        !           849:        for (auto i : indicators) {
        !           850:                if (i->id == id) {
        !           851:                        ind = i;
        !           852:                        break;
        !           853:                }
        !           854:        }
        !           855:        assert(ind);
        !           856:        const auto fdd = dynamic_cast<const FDDDevice *>(ind->dev);
        !           857: 
        !           858:        // メディアの状態を一旦クリア
        !           859:        new_loaded = false;
        !           860: 
        !           861:        // ツールチップを設定
        !           862:        std::string tip = string_format("FD%d: ", unit);
        !           863:        const std::string& path = fdd->GetPathName();
        !           864:        // 同時にメディア状態もセット
        !           865:        if (path.empty()) {
        !           866:                tip += "-";
        !           867:        } else {
        !           868:                tip += path;
        !           869:                new_loaded = true;
        !           870:        }
        !           871:        switch (fdd->GetWriteMode()) {
        !           872:         case FDDDevice::RW::WriteProtect:
        !           873:                tip += "\n(Write Protected)";
        !           874:                break;
        !           875:         case FDDDevice::RW::WriteIgnore:
        !           876:                tip += "\n(Write Ignored)";
        !           877:                break;
        !           878:         default:
        !           879:                break;
        !           880:        }
        !           881:        ind->panel->SetToolTip(tip);
        !           882: 
        !           883:        ind->text = string_format("FD%d", fdd->GetUnitNo());
        !           884:        // 書き込み無視だけ表示する。無駄に幅を取らないためメディア挿入時のみ表示。
        !           885:        // 4文字分のマークの後ろに "WI" を追加。
        !           886:        if (new_loaded) {
        !           887:                if (fdd->GetWriteMode() == FDDDevice::RW::WriteIgnore) {
        !           888:                        ind->text += "    \x02\x03";
        !           889:                }
        !           890:        }
        !           891: 
        !           892:        // メディアのロード状態が変われば (再配置して) 再描画。
        !           893:        // 通常周期のタイマーによる更新チェックではメディア状態はチェックして
        !           894:        // いないので、ここで自発的に行う。
        !           895:        if (fd_loaded[unit] != new_loaded) {
        !           896:                fd_loaded[unit] = new_loaded;
        !           897: 
        !           898:                LayoutIndicators();
        !           899:                Refresh();
        !           900:        }
        !           901: }
        !           902: 
        !           903: // LED 状態変更イベント (UIMessage イベント)。
        !           904: //
        !           905: // 状態変更があったことをプッシュ通知する。主に、通常の更新頻度では
        !           906: // 間に合わない X68030 の電源 LED の 16Hz 点滅用。
        !           907: void
        !           908: WXStatusPanel::OnLEDChanged(wxCommandEvent& event)
        !           909: {
        !           910:        if (UpdateStat()) {
        !           911:                Refresh();
        !           912:        }
        !           913: }
        !           914: 
        !           915: 
        !           916: //
        !           917: // インジケータ情報
        !           918: //
        !           919: 
        !           920: // SCSI なら SCSI ID を返す。そうでなければ -1 を返す。
        !           921: int
        !           922: Indicator::GetSCSIID() const
        !           923: {
        !           924:        int scsiid = id - SCSI0;
        !           925:        if (0 <= scsiid && scsiid <= 7) {
        !           926:                return scsiid;
        !           927:        } else {
        !           928:                return -1;
        !           929:        }
        !           930: }
        !           931: 
        !           932: // FDD ならユニット番号を返す。そうでなければ -1 を返す。
        !           933: int
        !           934: Indicator::GetFDUnit() const
        !           935: {
        !           936:        int unit = id - FDD0;
        !           937:        if (0 <= unit && unit <= 3) {
        !           938:                return unit;
        !           939:        } else {
        !           940:                return -1;
        !           941:        }
        !           942: }

unix.superglobalmegacorp.com

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