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