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