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