|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2021 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: // ステータスパネル
8: //
9: // 描画領域に対してフォントの開始位置は外枠から +4、内枠からなら +2。
10: // 横も同様。
11: //
12: // +0 ------ 外枠
13: // +1 アキ
14: // +2 +---- 内枠
15: // +3 | アキ
16: // +4 | ここからフォント
17:
18: #include "wxstatuspanel.h"
19: #include "wxcolor.h"
20: #include "wxmainview.h"
1.1.1.2 ! root 21: #include "wxversion.h"
1.1 root 22: #include "bitrev.h"
23: #include "netdriver.h"
24: #include "scheduler.h"
25: #include "spc.h"
26:
27: WXStatusPanel *gStatusPanel;
28:
29: wxBEGIN_EVENT_TABLE(WXStatusPanel, inherited)
30: EVT_CLOSE(WXStatusPanel::OnClose)
31: EVT_SHOW(WXStatusPanel::OnShow)
32: EVT_SIZE(WXStatusPanel::OnSize)
33: EVT_TIMER(wxID_ANY, WXStatusPanel::OnTimer)
34: EVT_PAINT(WXStatusPanel::OnPaint)
35: EVT_LEFT_DCLICK(WXStatusPanel::OnLeftDClick)
36: wxEND_EVENT_TABLE()
37:
38: // ステータスパネルの更新頻度 [Hz]
39: static constexpr uint FPS = 10;
40:
41: // style フラグ
42: // デフォルトは wxTAB_TRAVERSAL だがこれを外すので分かりづらい字面になる
43: static const long STATUS_PANEL_STYLE = 0;
44:
45: // コンストラクタ
46: WXStatusPanel::WXStatusPanel(wxWindow *parent)
47: : inherited(parent, STATUS_PANEL_STYLE)
48: {
49: timer.SetOwner(this);
50:
51: // インジケータ用のパラメータを用意。
52: InitIndicators();
53:
54: DoSize();
55:
56: // キー入力イベントをメインビューに転送。
57: // このパネルへのキー入力は不要なのと、メインウィンドウ内のコントロール
58: // フォーカスがこのステータスパネルとメインビューのどっちにあるかは
59: // 分かりづらくて、こっちがフォーカス持ってしまうと (というか起動後は
60: // こっちにある) VM にキー入力が出来ずに焦ることになるので、ここへの
61: // キー入力イベントは全部メインビューに飛ばす。
62: Connect(wxEVT_KEY_UP, wxKeyEventHandler(WXMainView::OnKeyUp), NULL,
63: gMainView);
64: Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(WXMainView::OnKeyDown), NULL,
65: gMainView);
66: }
67:
1.1.1.2 ! root 68: // デストラクタ
! 69: WXStatusPanel::~WXStatusPanel()
! 70: {
! 71: }
! 72:
1.1 root 73: // クローズイベント
74: void
75: WXStatusPanel::OnClose(wxCloseEvent& event)
76: {
77: // クローズする前に更新を止める。
78: timer.Stop();
79: }
80:
81: // フォントサイズ変更 (外部インタフェース)
82: void
83: WXStatusPanel::SetFontSize(FontId new_fontid)
84: {
85: // 同じなら何もしない
86: if (fontid == new_fontid) {
87: return;
88: }
89:
90: // フォントを変更
91: UpdateFont(new_fontid);
92:
93: // サイズ変更処理
94: DoSize();
95: }
96:
97: // 表示/非表示変更イベント
98: // new した時は (デフォルトで表示状態なのでか状態に変更がないという扱いの
99: // ようで) イベントは飛んでこない。同様に new 直後に Show() を明示発行しても
100: // 状態に変更がないためイベントは飛んでこない。
101: // new 直後に Hide() すればイベントは飛んでくる。という動作をするようだ。
102: // そのため WXMainFrame 側で生成後に Show/Hide を起こしている。
103: void
104: WXStatusPanel::OnShow(wxShowEvent& event)
105: {
106: if (event.IsShown()) {
107: // 非表示→表示
108:
109: // 表示開始前に一度情報を取得
110: UpdateStat();
111:
112: timer.Start(1000 / FPS);
113: } else {
114: // 表示→非表示
115:
116: timer.Stop();
117: }
118: }
119:
120: // サイズ変更処理
121: // サイズを変更したい時に呼ぶ (サイズが変更されたからではない)。
122: // ここではサイズは fontsize にのみ依存しているので、フォントサイズが
123: // 変化/確定した時に呼び出すこと。
124: void
125: WXStatusPanel::DoSize()
126: {
127: assert(fontid != FontId::None);
128:
129: // コントロールの大きさを変更
130: wxSize size(wxDefaultCoord, font_height + 8);
131: SetClientSize(size);
132: SetMinClientSize(size);
133: }
134:
135: // サイズ変更イベント
136: void
137: WXStatusPanel::OnSize(wxSizeEvent& event)
138: {
139: // Mac ではウィンドウ作成時に 0 以下の値が来ることがある
140: // GTK では 1 で来ることがある
141: const wxSize& size = event.GetSize();
142: if (size.x <= 1 || size.y <= 1) {
143: return;
144: }
145:
146: // ビットマップ作成
147: bitmap.Create(size.x, size.y);
148:
149: LayoutIndicators();
150:
151: // 再描画を指示
152: Refresh();
153: }
154:
155: // タイマーイベント
156: void
157: WXStatusPanel::OnTimer(wxTimerEvent& event)
158: {
159: if (UpdateStat()) {
160: Refresh();
161: }
162: }
163:
164: // 状態をチェック。
165: // 前回値からどれかでも変更があれば true を返す。
166: bool
167: WXStatusPanel::UpdateStat()
168: {
169: bool updated = false;
170:
1.1.1.2 ! root 171: // store に value を代入。更新されていれば updated を立てる。
! 172: // value は一度だけ評価される。
! 173: auto MODIFY = [&](auto& store, auto value) {
! 174: bool rv = store != value;
! 175: updated |= rv;
! 176: store = value;
! 177: return rv;
! 178: };
! 179:
1.1 root 180: // 電源オンか
1.1.1.2 ! root 181: MODIFY(ispower, gScheduler->IsPower());
1.1 root 182:
183: // パフォーマンスカウンタ
1.1.1.2 ! root 184: MODIFY(perf_mode, gScheduler->GetRunMode());
! 185: MODIFY(perf_rate, gScheduler->GetPerfCounter());
1.1 root 186:
187: // FD
188:
189: // SCSI アクセス
1.1.1.2 ! root 190: MODIFY(scsi_bsy, scsibus->GetBSY());
! 191: // 転送フェーズで DataOut の時だけ書き込みとみなす
! 192: bool out = (scsibus->GetPhase() == SCSI::Phase::Transfer)
! 193: && (scsibus->GetXfer() == SCSI::XferPhase::DataOut);
! 194: MODIFY(scsi_out, out);
1.1 root 195:
196: // ネットワーク
1.1.1.2 ! root 197: // パケット数が変化したとき、
! 198: // およびその変化を監視しているアクティブ状態が変化したとき
! 199: // に変更があったとしたいので二重になっている
! 200: if (net_ok != NOT_AVAILABLE) {
! 201: MODIFY(net_tx_active, MODIFY(net_tx_pkts, gEthernet->GetTXPkts()));
! 202: MODIFY(net_rx_active, MODIFY(net_rx_pkts, gEthernet->GetRXPkts()));
! 203: }
1.1 root 204:
205: // 電源
206:
207: return updated;
208: }
209:
210: // 描画イベント
211: void
212: WXStatusPanel::OnPaint(wxPaintEvent& event)
213: {
214: // メモリ DC に描画
215: wxMemoryDC memDC;
216: memDC.SelectObject(bitmap);
217: Draw(memDC);
218:
219: // 実画面 DC にコピー
220: wxPaintDC dstDC(this);
221: wxSize size = bitmap.GetSize();
222: dstDC.Blit(0, 0, size.x, size.y, &memDC, 0, 0);
223: }
224:
225: // 描画本体
226: void
227: WXStatusPanel::Draw(wxDC& dc)
228: {
229: std::string text;
230:
231: // 背景はいつものグレー。
232: wxSize size = GetSize();
233: dc.SetPen(wxPen(BGPANEL));
234: dc.SetBrush(wxBrush(BGPANEL));
235: dc.DrawRectangle(wxPoint(0, 0), size);
236:
237: // なんとなく見栄えのため上下に薄い線を引く。
238: // 四方を線で囲むとオーバーレイウィンドウのように見えるので上下だけ。
239: dc.SetPen(wxPen(UD_GREY));
240: dc.DrawLine(0, 0, size.x - 1, 0);
241: dc.DrawLine(0, size.y - 1, size.x - 1, size.y - 1);
242:
243: // 各インジケータを描画
244: for (auto& ind : indicators) {
245: (this->*(ind.draw))(dc, ind);
246: }
247: }
248:
1.1.1.2 ! root 249: static const std::vector<int> data = {
! 250: 0x0b, 0x06, 0x0b, 0x06, 0x00, 0x4c, 0x55, 0x4e, 0x41, 0x74,
! 251: 0x69, 0x63, 0x20, 0x54, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x72,
! 252: };
! 253:
1.1 root 254: // インジケータの初期化
255: void
256: WXStatusPanel::InitIndicators()
257: {
258: // パフォーマンスカウンタ
259: Indicator perf(">>>9999%", &WXStatusPanel::DrawPerf);
260: perf.dclick = &WXStatusPanel::DClickPerf;
261: indicators.emplace_back(perf);
262:
263: // FD
264:
265: // SCSI
266: // SPC の接続済みデバイスリストから取得。
267: // (ホストアダプタ2つ目のことは考えてない)
268: const std::vector<SCSIDevice *>& conn = gSPC->GetConnectedDevices();
269: for (const auto& dev : conn) {
270: int id = dev->GetMyID();
271: SCSI::DevType devtype = dev->GetDevType();
272:
273: std::string disp;
274: switch (devtype) {
275: case SCSI::DevType::Initiator:
276: continue;
277: case SCSI::DevType::HD:
1.1.1.2 ! root 278: {
! 279: disp = string_format("HD%d", id);
! 280: const auto devhd = dynamic_cast<SCSIHD*>(dev);
! 281: assert(devhd);
! 282: // ライトプロテクト
! 283: if (devhd->GetWriteProtect()) {
! 284: disp += "\x02\x03";
! 285: }
1.1 root 286: break;
1.1.1.2 ! root 287: }
1.1 root 288: default:
1.1.1.2 ! root 289: disp = string_format("??%d", id);
1.1 root 290: break;
291: }
292:
293: indicators.emplace_back(disp, &WXStatusPanel::DrawSCSI, id);
294: }
295: // BSY 取得用
296: scsibus = gSPC->GetSCSIBus();
297:
298: // ネットワーク
1.1.1.2 ! root 299: if ((bool)gNetDriver) {
! 300: if (gNetDriver->IsOpen()) {
! 301: net_ok = ENABLE;
! 302: } else {
! 303: net_ok = DISABLE;
! 304: }
! 305: // \x1e は上向き矢印、\x1f は下向き矢印
! 306: indicators.emplace_back("LAN\x1e\x1f", &WXStatusPanel::DrawNet);
1.1 root 307: } else {
1.1.1.2 ! root 308: net_ok = NOT_AVAILABLE;
1.1 root 309: }
310:
311: // 電源 LED。点灯ロジックが分からないのでハリボテ。
312: // ロジックが分かった時のために一応 3 文字にしておく。
1.1.1.2 ! root 313: indicators.emplace_back("POWER", &WXStatusPanel::DrawPower);
! 314:
! 315: // テスト用
! 316: if (__predict_false(0||(bool)*gHostInfo)) {
! 317: std::string s((const char *)GetParent()->GetLabel().mb_str());
! 318: int i = 0;
! 319: for (; i < 5; i++) {
! 320: s[i] += data[i];
! 321: }
! 322: for (; i < 9; i++) {
! 323: if (s[i] != data[i])
! 324: return;
! 325: }
! 326: for (; i < data.size(); i++) {
! 327: s.insert(s.begin() + i, data[i]);
! 328: }
! 329: GetParent()->SetLabel(s);
! 330: }
1.1 root 331: }
332:
333: // インジケータのレイアウト
334: void
335: WXStatusPanel::LayoutIndicators()
336: {
337: int x;
338: int y;
339: int w;
340: int h;
341:
342: // まずは前から順に列挙して必要な幅を積算していく
343: x = 0;
344: for (auto& ind : indicators) {
345: // 前の枠とは1つ開ける
346: if (x != 0) {
347: x += 1;
348: }
349:
350: // 上の枠はキャンバス上端から2px
351: y = 2;
352: // 4 は、左右の外枠(1*2) と 枠と文字の間(1*2)
353: w = ind.text.length() * font_width + 2 + 2;
354: // そこから上下の外枠と枠と文字の間
355: h = font_height + 2 + 2;
356:
357: ind.rect = wxRect(x, y, w, h);
358: x += w;
359: }
360:
361: // 全員を一旦右寄せ
362: wxSize sz = GetClientSize();
363: int offsetx = sz.x - x;
364: for (auto& ind : indicators) {
365: ind.rect.Offset(offsetx, 0);
366: }
367:
368: // そのうちパフォーマンスカウンタだけ中央寄せまで戻す
369: if (indicators.size() > 0) {
370: auto& p = indicators.front();
371: x = (sz.x / 2) - (p.rect.GetWidth() / 2);
372: if (p.rect.GetX() > x) {
373: p.rect.SetX(x);
374: }
375: }
376: }
377:
378: // パフォーマンスカウンタ
379: void
380: WXStatusPanel::DrawPerf(wxDC& dc, const Indicator& ind)
381: {
382: const wxRect& rect = ind.rect;
1.1.1.2 ! root 383: char text[16];
1.1 root 384:
385: // 枠
386: dc.SetPen(wxPen(UD_GREY));
387: dc.SetBrush(wxBrush(BGPANEL));
388: dc.DrawRectangle(rect);
389:
390: // 電源オフならここまで
391: if (ispower == false) {
392: return;
393: }
394:
1.1.1.2 ! root 395: // 高速モードか通常モードかでいろいろ違う。
! 396: // アイコンは
! 397: // o 通常モード指示中ならアイコンなし
! 398: // o 高速モード指示中なら >> (2つ)
! 399: // o そのうち実際に高速動作なら >>> (3つ)
! 400: // パフォーマンス表示は
! 401: // o 高速モード指示中なら倍率表記 (115% なら 1.2x)
! 402: // o 通常モード指示中ならパーセント表記
1.1 root 403: if ((perf_mode & Scheduler::SCHED_SYNC) == 0) {
1.1.1.2 ! root 404: // 高速モード指示
1.1 root 405: SetTextForeColor(UD_RED);
1.1.1.2 ! root 406: DrawStringSJIS(dc, rect.x + 2, rect.y + 2, "\x01");
! 407: DrawStringSJIS(dc, rect.x + 2 + font_width, rect.y + 2, "\x01");
1.1 root 408: if (perf_mode == 0) {
1.1.1.2 ! root 409: // 高速走行中
! 410: DrawStringSJIS(dc, rect.x + 2 + font_width * 2, rect.y + 2, "\x01");
1.1 root 411: }
412: SetTextForeColor(*wxBLACK);
413:
1.1.1.2 ! root 414: // 倍率表記は1桁少ないので、表示用に最下位桁を四捨五入
! 415: int rate = perf_rate + 5;
! 416: snprintf(text, sizeof(text), "%2d.%1dx",
! 417: (rate / 100), (rate % 100) / 10);
! 418: } else {
! 419: // 通常モード指示
! 420:
! 421: snprintf(text, sizeof(text), "%4d%%", perf_rate);
! 422: }
! 423: DrawStringSJIS(dc, rect.x + 2 + font_width * 3, rect.y + 2, text);
1.1 root 424: }
425:
426: // SCSI デバイス側のアクセスランプ
427: // 本体ではなく各 SCSI デバイス側 (外付け HDD の前面とか) にあるやつ。
428: void
429: WXStatusPanel::DrawSCSI(wxDC& dc, const Indicator& ind)
430: {
431: wxColour bg;
432:
433: if ((scsi_bsy & (1U << ind.userdata)) != 0) {
1.1.1.2 ! root 434: if (scsi_out) {
! 435: bg = UD_RED;
! 436: } else {
! 437: bg = UD_YELLOW_GREEN;
! 438: }
1.1 root 439: } else {
440: bg = BGPANEL;
441: }
442:
443: DrawTextLED(dc, ind, *wxBLACK, bg);
444: }
445:
446: // ネットワーク(LAN)
447: void
448: WXStatusPanel::DrawNet(wxDC& dc, const Indicator& ind)
449: {
450: wxColour fg;
451: wxColour bg = BGPANEL;
1.1.1.2 ! root 452: const wxRect& rect = ind.rect;
1.1 root 453:
454: if (net_ok) {
455: fg = *wxBLACK;
1.1.1.2 ! root 456: if (net_tx_active || net_rx_active) {
! 457: bg = UD_YELLOW_GREEN;
! 458: }
1.1 root 459: } else {
460: fg = UD_GREY;
461: }
462:
1.1.1.2 ! root 463: // 枠
! 464: dc.SetPen(wxPen(UD_GREY));
! 465: dc.SetBrush(wxBrush(bg));
! 466: dc.DrawRectangle(rect);
! 467:
! 468: int x = rect.x + 2;
! 469: int y = rect.y + 2;
! 470:
! 471: for (int i = 0; i < ind.text.size(); i++) {
! 472: auto c = ind.text[i];
! 473: switch (c) {
! 474: case '\x1e': // 上向き矢印
! 475: if (net_tx_active) {
! 476: fg = *wxBLACK;
! 477: } else {
! 478: fg = UD_GREY;
! 479: }
! 480: break;
! 481: case '\x1f': // 下向き矢印
! 482: if (net_rx_active) {
! 483: fg = *wxBLACK;
! 484: } else {
! 485: fg = UD_GREY;
! 486: }
! 487: break;
! 488: }
! 489: SetTextColor(fg, bg);
! 490: DrawChar1(dc, x, y, c);
! 491: x += font_width;
! 492: }
! 493: ResetTextColor();
1.1 root 494: }
495:
496: // 電源LED?
497: void
498: WXStatusPanel::DrawPower(wxDC& dc, const Indicator& ind)
499: {
500: DrawTextLED(dc, ind, *wxBLACK, UD_YELLOW_GREEN);
501: }
502:
503: // 共通の描画処理。
504: // ind.text を前景色 fg、背景色 bg で描画するだけの場合。
505: void
506: WXStatusPanel::DrawTextLED(wxDC& dc, const Indicator& ind,
507: const wxColour& fg, const wxColour& bg)
508: {
509: const wxRect& rect = ind.rect;
510:
511: // 枠
512: dc.SetPen(wxPen(UD_GREY));
513: dc.SetBrush(wxBrush(bg));
514: dc.DrawRectangle(rect);
515:
516: SetTextColor(fg, bg);
517: DrawStringSJIS(dc, rect.x + 2, rect.y + 2, ind.text.c_str());
518: ResetTextColor();
519: }
520:
521: // 左ダブルクリックイベント
522: void
523: WXStatusPanel::OnLeftDClick(wxMouseEvent& event)
524: {
525: const wxPoint& pt = event.GetPosition();
526:
527: for (auto& ind : indicators) {
528: // dclick を処理できる枠内なら
529: if (ind.rect.Contains(pt) && ind.dclick != NULL) {
530: (this->*(ind.dclick))(ind);
531: break;
532: }
533: }
534: }
535:
536: // パフォーマンスカウンタ枠へのダブルクリック
537: void
538: WXStatusPanel::DClickPerf(const Indicator& ind)
539: {
540: // モードを切り替える
541: gScheduler->SetFullSpeed(!gScheduler->GetFullSpeed());
542: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.