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