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