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