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

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

unix.superglobalmegacorp.com

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