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