--- nono/wx/wxstatuspanel.cpp 2026/04/29 17:05:35 1.1.1.11 +++ nono/wx/wxstatuspanel.cpp 2026/04/29 17:05:44 1.1.1.13 @@ -28,8 +28,10 @@ #include "newsctlr.h" #include "power.h" #include "scsi.h" +#include "scsidomain.h" #include "syncer.h" #include "virtio_block.h" +#include "virtio_scsi.h" // // インジケータ情報 (ほぼ構造体) @@ -164,7 +166,6 @@ Indicator::GetFDUnit() const wxBEGIN_EVENT_TABLE(WXStatusPanel, inherited) EVT_CLOSE(WXStatusPanel::OnClose) EVT_SHOW(WXStatusPanel::OnShow) - EVT_SIZE(WXStatusPanel::OnSize) EVT_TIMER(wxID_ANY, WXStatusPanel::OnTimer) EVT_LEFT_DCLICK(WXStatusPanel::OnLeftDClick) EVT_CONTEXT_MENU(WXStatusPanel::OnContextMenu) @@ -192,6 +193,7 @@ WXStatusPanel::WXStatusPanel(wxWindow *p // インジケータ用のパラメータを用意。 InitIndicators(); + SetAutoLayout(true); FontChanged(); // キー入力イベントをメインビューに転送。 @@ -289,33 +291,19 @@ WXStatusPanel::FontChanged() } accmark.reset(new BitmapI8(src, font_height, font_height)); - // インジケータを再レイアウト - LayoutIndicators(); - - // コントロールの大きさを変更 - wxSize size = GetClientSize(); - size.y = font_height + 8; - SetClientSize(size); + Fit(); } -// サイズ変更イベント void -WXStatusPanel::OnSize(wxSizeEvent& event) +WXStatusPanel::Fit() { - // Mac ではウィンドウ作成時に 0 以下の値が来ることがある。 - // GTK では 1 で来ることがある。 - const wxSize& size = event.GetSize(); - if (size.x <= 1 || size.y <= 1) { - return; - } + wxSize size = LayoutIndicators(); - // 親クラス - inherited::OnSize(event); + // バックバッファのサイズを固定。 + SetMinBitmapSize(size); - LayoutIndicators(); - - // 再描画を指示 - Refresh(); + SetMinSize(size); + SetSize(size); } // タイマーイベント @@ -327,7 +315,7 @@ WXStatusPanel::OnTimer(wxTimerEvent& eve } } -// store に value を代入。更新されていれば updated を立てる。 +// store に value を代入。更新されていれば true を返す。 // value は引数に渡す時に一度だけ評価される。 #define MODIFY(store, value_) ({ \ bool mod_; \ @@ -358,40 +346,104 @@ Status::~Status() { } +// Read/Write が別の3値タイプのアクセスインジケータ用 +enum class RWStatus : uint8 { + None = 0, + Read = 1, + Write = 2, +}; + // -// SCSI 情報 +// SCSI 情報 (Bus/VirtIO 共通) // class StatusSCSI : public Status { public: - StatusSCSI(SCSIBus *scsibus_) { + std::array active {}; +}; + +// +// SCSIBus 情報 +// +class StatusSCSIBus : public StatusSCSI +{ + public: + StatusSCSIBus(SCSIBus *scsibus_) { scsibus = scsibus_; } - ~StatusSCSI() override; + ~StatusSCSIBus() override; bool Poll() override { - bool mod = false; - mod |= MODIFY(bsy, scsibus->GetBSY()); + uint8 bsy = scsibus->GetBSY(); // 転送フェーズで DataOut の時だけ書き込みとみなす bool out = (scsibus->GetPhase() == SCSI::Phase::Transfer) && (scsibus->GetXfer() == SCSI::XferPhase::DataOut); - mod |= MODIFY(dataout, out); + + bool mod = false; + for (uint id = 0; id < 8; id++) { + RWStatus newactive; + if (__predict_false((bsy & (1U << id)) != 0)) { + if (out) { + newactive = RWStatus::Write; + } else { + newactive = RWStatus::Read; + } + } else { + newactive = RWStatus::None; + } + mod |= MODIFY(active[id], newactive); + } return mod; } - uint8 bsy {}; // BSY (ID0-7) - bool dataout {}; // DataOut フェーズなら true - private: SCSIBus *scsibus {}; }; // デストラクタ -StatusSCSI::~StatusSCSI() +StatusSCSIBus::~StatusSCSIBus() { } // +// VirtIOSCSI 情報 +// +class StatusVSCSI : public StatusSCSI +{ + public: + StatusVSCSI(VirtIOSCSIDevice *dev_) { + dev = dev_; + } + ~StatusVSCSI() override = default; + + bool Poll() override { + std::array newacc; + dev->GetAccess(&newacc); + bool mod = false; + for (uint id = 0; id < 8; id++) { + bool rmod = MODIFY(access[id * 2 + 0], newacc[id * 2 + 0]); + bool wmod = MODIFY(access[id * 2 + 1], newacc[id * 2 + 1]); + + RWStatus newactive; + if (wmod) { + newactive = RWStatus::Write; + } else if (rmod) { + newactive = RWStatus::Read; + } else { + newactive = RWStatus::None; + } + mod |= MODIFY(active[id], newactive); + } + return mod; + } + + std::array access {}; + + private: + VirtIOSCSIDevice *dev {}; +}; + +// // VirtIOBlock 情報 // class StatusVBlk : public Status @@ -405,18 +457,21 @@ class StatusVBlk : public Status bool Poll() override { bool rmod = MODIFY(access_read, dev->GetAccessRead()); bool wmod = MODIFY(access_write, dev->GetAccessWrite()); - - bool mod = false; - mod |= MODIFY(read_active, rmod); - mod |= MODIFY(write_active, wmod); - return mod; + RWStatus newactive; + if (wmod) { + newactive = RWStatus::Write; + } else if (rmod) { + newactive = RWStatus::Read; + } else { + newactive = RWStatus::None; + } + return MODIFY(active, newactive); } // アクセス状況 uint32 access_read {}; uint32 access_write {}; - bool read_active {}; - bool write_active {}; + RWStatus active {}; private: VirtIOBlockDevice *dev {}; @@ -536,23 +591,27 @@ WXStatusPanel::InitIndicators() // SCSI // 接続済みデバイスリストから取得。 - auto scsi = gMainApp.FindObject(OBJ_SCSI); - if (scsi) { - auto scsibus = scsi->GetOwnedBus(); - stat = new StatusSCSI(scsibus); + auto scsibus = gMainApp.FindObject(OBJ_SCSIBUS); + if (scsibus) { + stat = new StatusSCSIBus(scsibus); stats.emplace_back(stat); - const auto& conn = scsibus->GetConnectedDevices(); - for (auto& dev : conn) { + const auto& conn = scsibus->GetDomain()->GetConnectedDevices(); + for (const auto& dev : conn) { SCSI::DevType devtype = dev->GetDevType(); if (devtype == SCSI::DevType::Initiator) { continue; } int scsiid = dev->GetMyID(); - // 文字列は OnSCSIMediaChanged() でセットする。 + std::string label = string_format("%s%d", + SCSI::GetDevTypeName(devtype), scsiid); + const auto disk = dynamic_cast(dev); + if (disk && disk->GetWriteMode() == SCSIDisk::RW::WriteIgnore) { + label += "\x02\x03"; + } ind = new Indicator(Indicator::SCSI0 + scsiid, - &WXStatusPanel::DrawSCSI, 3, stat); + &WXStatusPanel::DrawSCSI, label, stat); ind->contextmenu = &WXStatusPanel::ContextMenuCD; ind->dev = dev; ind->panel = new wxPanel(this); @@ -584,6 +643,38 @@ WXStatusPanel::InitIndicators() } } + // VirtIOSCSI + auto vscsi = gMainApp.FindObject(OBJ_VIRTIO_SCSI); + if (vscsi) { + stat = new StatusVSCSI(vscsi); + stats.emplace_back(stat); + + const auto& conn = vscsi->GetDomain()->GetConnectedDevices(); + for (const auto& dev : conn) { + SCSI::DevType devtype = dev->GetDevType(); + int scsiid = dev->GetMyID(); + + std::string label = string_format("%s%d", + SCSI::GetDevTypeName(devtype), scsiid); + const auto disk = dynamic_cast(dev); + if (disk && disk->GetWriteMode() == SCSIDisk::RW::WriteIgnore) { + label += "\x02\x03"; + } + ind = new Indicator(Indicator::SCSI0 + scsiid, + &WXStatusPanel::DrawSCSI, label, stat); + ind->contextmenu = &WXStatusPanel::ContextMenuCD; + ind->dev = dev; + ind->panel = new wxPanel(this); + ind->panel->SetName("WXStatusPanel.VSCSI"); + // このパネルがフォーカスを受け取らないようにする。 + // StatusPanel がフォーカスを持っていてもキー入力を MainFrame に渡す + // ようにしているため、StatusPanel のフォーカスを奪ってはいけない。 + ind->panel->SetCanFocus(false); + + indicators.emplace_back(ind); + } + } + // ネットワーク for (int i = 0; i < 2; i++) { auto hostnet = gMainApp.FindObject(OBJ_HOSTNET(i)); @@ -618,10 +709,15 @@ WXStatusPanel::InitIndicators() stat = new StatusFDD(fdd); stats.emplace_back(stat); + // 後ろの4文字分のスペースは LED アイコン用の場所取り。 + std::string label = string_format("FD%d ", fdd->GetUnitNo()); + if (fdd->GetWriteMode() == FDDDevice::RW::WriteIgnore) { + label += "\x02\x03"; + } + // 文字列は OnFDDMediaChanged() でセットする。 ind = new Indicator(Indicator::FDD0 + unit, - &WXStatusPanel::DrawFD, 7, stat); - + &WXStatusPanel::DrawFD, label, stat); ind->contextmenu = &WXStatusPanel::ContextMenuFD; ind->dev = fdd; ind->panel = new wxPanel(this); @@ -654,8 +750,10 @@ WXStatusPanel::InitIndicators() indicators.emplace_back(ind); } -// インジケータのレイアウト -void +// インジケータの配置位置を求める。 +// ind->rect を更新するがここで描画とかはしない。 +// 戻り値はインジケータの配置に必要な最小サイズ。 +wxSize WXStatusPanel::LayoutIndicators() { int x; @@ -686,23 +784,39 @@ WXStatusPanel::LayoutIndicators() x += 1; // この x が最小サイズなのでこれをコントロールに設定。 - // (コントロールのサイズは WXMainFrame::Layout() が設定する) - SetMinClientSize(wxSize(x, font_height + 8)); + wxSize size(x, h + 2 + 2); + return size; +} + +// レイアウト +bool +WXStatusPanel::Layout() +{ + if (indicators.empty()) { + return false; + } + + // レイアウトの計算はビットマップサイズに対して行う。 + // 起動直後に何かの拍子で随分小さいサイズにリサイズされることがあったり + // また実画面が小さくてそもそも入り切らないような場合でも、 + // ビットマップは常に最低限描画できるサイズを保証してあるので。 + // それを画面に転送しても見た目は事故るがとりあえず死にはしないので。 + wxSize size(bitmap.GetWidth(), bitmap.GetHeight()); - // 全員を一旦右寄せ - wxSize sz = GetClientSize(); - int offsetx = sz.x - x; + // 配置情報をリセット。(左寄せ相当になる) + wxSize minsize = LayoutIndicators(); + + // 全員を一旦右寄せ。 + int offsetx = size.x - minsize.x; for (auto ind : indicators) { ind->rect.Offset(offsetx, 0); } // そのうちパフォーマンスカウンタだけ中央寄せまで戻す - if (indicators.size() > 0) { - auto p = indicators.front(); - x = (sz.x / 2) - (p->rect.w / 2); - if (p->rect.x > x) { - p->rect.x = x; - } + auto perf = indicators.front(); + int x = (size.x / 2) - (perf->rect.w / 2); + if (perf->rect.x > x) { + perf->rect.x = x; } // パネルをもっていれば、パネルをその位置に(再)設定。 @@ -714,6 +828,7 @@ WXStatusPanel::LayoutIndicators() } refresh_background = true; + return true; } // 状態をチェック。 @@ -749,13 +864,13 @@ WXStatusPanel::Draw() if (refresh_background) { refresh_background = false; bitmap.Fill(BGPANEL); - } - // なんとなく見栄えのため上下に薄い線を引く。 - // 四方を線で囲むとオーバーレイウィンドウのように見えるので上下だけ。 - wxSize size = GetSize(); - bitmap.DrawLineH(UD_GREY, 0, 0, size.x); - bitmap.DrawLineH(UD_GREY, 0, size.y - 1, size.x); + // なんとなく見栄えのため上下に薄い線を引く。 + // 四方を線で囲むとオーバーレイウィンドウのように見えるので上下だけ。 + wxSize size = GetSize(); + bitmap.DrawLineH(UD_GREY, 0, 0, size.x); + bitmap.DrawLineH(UD_GREY, 0, size.y - 1, size.x); + } // 各インジケータを描画 for (auto ind : indicators) { @@ -830,11 +945,9 @@ WXStatusPanel::DrawSCSI(const Indicator Color fg; Color bg; int scsiid; - uint idmask; scsiid = ind->GetSCSIID(); assertmsg(scsiid >= 0, "id=%d", ind->id); - idmask = 1U << scsiid; if (scsi_loaded[scsiid]) { fg = UD_BLACK; @@ -842,12 +955,10 @@ WXStatusPanel::DrawSCSI(const Indicator fg = UD_GREY; } - if ((stat->bsy & idmask) != 0) { - if (stat->dataout) { - bg = UD_RED; - } else { - bg = UD_YELLOW_GREEN; - } + if (stat->active[scsiid] == RWStatus::Write) { + bg = UD_RED; + } else if (stat->active[scsiid] == RWStatus::Read) { + bg = UD_YELLOW_GREEN; } else { bg = BGPANEL; } @@ -864,9 +975,9 @@ WXStatusPanel::DrawVBlk(const Indicator Color bg; fg = UD_BLACK; - if (stat->write_active) { + if (stat->active == RWStatus::Write) { bg = UD_RED; - } else if (stat->read_active) { + } else if (stat->active == RWStatus::Read) { bg = UD_YELLOW_GREEN; } else { bg = BGPANEL; @@ -1234,24 +1345,11 @@ WXStatusPanel::OnSCSIMediaChanged(wxComm } ind->panel->SetToolTip(tip); - // 書き込み無視だけ、これによって shutdown が必要かどうかという - // オペレーションに違いが出るため表示する。 - // 無駄に幅を取らないためメディア挿入時のみ表示。 - // MO のライトプロテクト表示は必要になったら考える…。 - ind->text = string_format("%s%d", SCSI::GetDevTypeName(devtype), scsiid); - if (new_loaded) { - if (disk->GetWriteMode() == SCSIDisk::RW::WriteIgnore) { - ind->text += "\x02\x03"; - } - } - // メディアのロード状態が変われば (再配置して) 再描画。 // 通常周期のタイマーによる更新チェックではメディア状態はチェックして // いないので、ここで自発的に行う。 if (scsi_loaded[scsiid] != new_loaded) { scsi_loaded[scsiid] = new_loaded; - - LayoutIndicators(); Refresh(); } } @@ -1300,22 +1398,11 @@ WXStatusPanel::OnFDDMediaChanged(wxComma } ind->panel->SetToolTip(tip); - ind->text = string_format("FD%d", fdd->GetUnitNo()); - // 書き込み無視だけ表示する。無駄に幅を取らないためメディア挿入時のみ表示。 - // 4文字分のマークの後ろに "WI" を追加。 - if (new_loaded) { - if (fdd->GetWriteMode() == FDDDevice::RW::WriteIgnore) { - ind->text += " \x02\x03"; - } - } - // メディアのロード状態が変われば (再配置して) 再描画。 // 通常周期のタイマーによる更新チェックではメディア状態はチェックして // いないので、ここで自発的に行う。 if (fd_loaded[unit] != new_loaded) { fd_loaded[unit] = new_loaded; - - LayoutIndicators(); Refresh(); } }