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