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