|
|
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" ! 21: #include "bitrev.h" ! 22: #include "netdriver.h" ! 23: #include "scheduler.h" ! 24: #include "spc.h" ! 25: ! 26: WXStatusPanel *gStatusPanel; ! 27: ! 28: wxBEGIN_EVENT_TABLE(WXStatusPanel, inherited) ! 29: EVT_CREATE(WXStatusPanel::OnCreate) ! 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: // メインビューにイベントを接続したいがこの時点ではまだ gMainView が ! 57: // ないので OnCreate イベントで処理する。 ! 58: POST_CREATE(); ! 59: } ! 60: ! 61: // デストラクタ ! 62: WXStatusPanel::~WXStatusPanel() ! 63: { ! 64: } ! 65: ! 66: // ウィンドウ作成イベント ! 67: void ! 68: WXStatusPanel::OnCreate(wxCommandEvent& event) ! 69: { ! 70: // キー入力イベントをメインビューに転送。 ! 71: // このパネルへのキー入力は不要なのと、メインウィンドウ内のコントロール ! 72: // フォーカスがこのステータスパネルとメインビューのどっちにあるかは ! 73: // 分かりづらくて、こっちがフォーカス持ってしまうと (というか起動後は ! 74: // こっちにある) VM にキー入力が出来ずに焦ることになるので、ここへの ! 75: // キー入力イベントは全部メインビューに飛ばす。 ! 76: Connect(wxEVT_KEY_UP, wxKeyEventHandler(WXMainView::OnKeyUp), NULL, ! 77: gMainView); ! 78: Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(WXMainView::OnKeyDown), NULL, ! 79: gMainView); ! 80: } ! 81: ! 82: // クローズイベント ! 83: void ! 84: WXStatusPanel::OnClose(wxCloseEvent& event) ! 85: { ! 86: // クローズする前に更新を止める。 ! 87: timer.Stop(); ! 88: } ! 89: ! 90: // フォントサイズ変更 (外部インタフェース) ! 91: void ! 92: WXStatusPanel::SetFontSize(FontId new_fontid) ! 93: { ! 94: // 同じなら何もしない ! 95: if (fontid == new_fontid) { ! 96: return; ! 97: } ! 98: ! 99: // フォントを変更 ! 100: UpdateFont(new_fontid); ! 101: ! 102: // サイズ変更処理 ! 103: DoSize(); ! 104: } ! 105: ! 106: // 表示/非表示変更イベント ! 107: // new した時は (デフォルトで表示状態なのでか状態に変更がないという扱いの ! 108: // ようで) イベントは飛んでこない。同様に new 直後に Show() を明示発行しても ! 109: // 状態に変更がないためイベントは飛んでこない。 ! 110: // new 直後に Hide() すればイベントは飛んでくる。という動作をするようだ。 ! 111: // そのため WXMainFrame 側で生成後に Show/Hide を起こしている。 ! 112: void ! 113: WXStatusPanel::OnShow(wxShowEvent& event) ! 114: { ! 115: if (event.IsShown()) { ! 116: // 非表示→表示 ! 117: ! 118: // 表示開始前に一度情報を取得 ! 119: UpdateStat(); ! 120: ! 121: timer.Start(1000 / FPS); ! 122: } else { ! 123: // 表示→非表示 ! 124: ! 125: timer.Stop(); ! 126: } ! 127: } ! 128: ! 129: // サイズ変更処理 ! 130: // サイズを変更したい時に呼ぶ (サイズが変更されたからではない)。 ! 131: // ここではサイズは fontsize にのみ依存しているので、フォントサイズが ! 132: // 変化/確定した時に呼び出すこと。 ! 133: void ! 134: WXStatusPanel::DoSize() ! 135: { ! 136: assert(fontid != FontId::None); ! 137: ! 138: // ここで高速モードアイコンを作る、あるいは変更する。 ! 139: // どこでやるか悩ましいけど、ここがフォントサイズが変わった時と ! 140: // 起動時の他のイベントより先に呼ばれるので。 ! 141: UpdateFFMark(); ! 142: ! 143: // コントロールの大きさを変更 ! 144: wxSize size(wxDefaultCoord, font_height + 8); ! 145: SetClientSize(size); ! 146: SetMinClientSize(size); ! 147: } ! 148: ! 149: // サイズ変更イベント ! 150: void ! 151: WXStatusPanel::OnSize(wxSizeEvent& event) ! 152: { ! 153: // Mac ではウィンドウ作成時に 0 以下の値が来ることがある ! 154: // GTK では 1 で来ることがある ! 155: const wxSize& size = event.GetSize(); ! 156: if (size.x <= 1 || size.y <= 1) { ! 157: return; ! 158: } ! 159: ! 160: // ビットマップ作成 ! 161: bitmap.Create(size.x, size.y); ! 162: ! 163: LayoutIndicators(); ! 164: ! 165: // 再描画を指示 ! 166: Refresh(); ! 167: } ! 168: ! 169: // タイマーイベント ! 170: void ! 171: WXStatusPanel::OnTimer(wxTimerEvent& event) ! 172: { ! 173: if (UpdateStat()) { ! 174: Refresh(); ! 175: } ! 176: } ! 177: ! 178: // 状態をチェック。 ! 179: // 前回値からどれかでも変更があれば true を返す。 ! 180: bool ! 181: WXStatusPanel::UpdateStat() ! 182: { ! 183: bool updated = false; ! 184: ! 185: // 電源オンか ! 186: ispower = gScheduler->IsPower(); ! 187: if (last_ispower != ispower) { ! 188: last_ispower = ispower; ! 189: updated = true; ! 190: } ! 191: ! 192: // パフォーマンスカウンタ ! 193: perf_mode = gScheduler->GetRunMode(); ! 194: if (last_perf_mode != perf_mode) { ! 195: last_perf_mode = perf_mode; ! 196: updated = true; ! 197: } ! 198: perf_rate = gScheduler->GetPerfCounter(); ! 199: if (last_perf_rate != perf_rate) { ! 200: last_perf_rate = perf_rate; ! 201: updated = true; ! 202: } ! 203: ! 204: // FD ! 205: ! 206: // SCSI アクセス ! 207: scsi_bsy = scsibus->GetBSY(); ! 208: if (scsi_bsy != last_scsi_bsy) { ! 209: last_scsi_bsy = scsi_bsy; ! 210: updated = true; ! 211: } ! 212: ! 213: // ネットワーク ! 214: ! 215: // 電源 ! 216: ! 217: return updated; ! 218: } ! 219: ! 220: // 描画イベント ! 221: void ! 222: WXStatusPanel::OnPaint(wxPaintEvent& event) ! 223: { ! 224: // メモリ DC に描画 ! 225: wxMemoryDC memDC; ! 226: memDC.SelectObject(bitmap); ! 227: Draw(memDC); ! 228: ! 229: // 実画面 DC にコピー ! 230: wxPaintDC dstDC(this); ! 231: wxSize size = bitmap.GetSize(); ! 232: dstDC.Blit(0, 0, size.x, size.y, &memDC, 0, 0); ! 233: } ! 234: ! 235: // 描画本体 ! 236: void ! 237: WXStatusPanel::Draw(wxDC& dc) ! 238: { ! 239: std::string text; ! 240: ! 241: // 背景はいつものグレー。 ! 242: wxSize size = GetSize(); ! 243: dc.SetPen(wxPen(BGPANEL)); ! 244: dc.SetBrush(wxBrush(BGPANEL)); ! 245: dc.DrawRectangle(wxPoint(0, 0), size); ! 246: ! 247: // なんとなく見栄えのため上下に薄い線を引く。 ! 248: // 四方を線で囲むとオーバーレイウィンドウのように見えるので上下だけ。 ! 249: dc.SetPen(wxPen(UD_GREY)); ! 250: dc.DrawLine(0, 0, size.x - 1, 0); ! 251: dc.DrawLine(0, size.y - 1, size.x - 1, size.y - 1); ! 252: ! 253: // 各インジケータを描画 ! 254: for (auto& ind : indicators) { ! 255: (this->*(ind.draw))(dc, ind); ! 256: } ! 257: } ! 258: ! 259: // インジケータの初期化 ! 260: void ! 261: WXStatusPanel::InitIndicators() ! 262: { ! 263: // パフォーマンスカウンタ ! 264: Indicator perf(">>>9999%", &WXStatusPanel::DrawPerf); ! 265: perf.dclick = &WXStatusPanel::DClickPerf; ! 266: indicators.emplace_back(perf); ! 267: ! 268: // FD ! 269: ! 270: // SCSI ! 271: // SPC の接続済みデバイスリストから取得。 ! 272: // (ホストアダプタ2つ目のことは考えてない) ! 273: const std::vector<SCSIDevice *>& conn = gSPC->GetConnectedDevices(); ! 274: for (const auto& dev : conn) { ! 275: int id = dev->GetMyID(); ! 276: SCSI::DevType devtype = dev->GetDevType(); ! 277: ! 278: std::string disp; ! 279: switch (devtype) { ! 280: case SCSI::DevType::Initiator: ! 281: continue; ! 282: case SCSI::DevType::HD: ! 283: disp = "HD"; ! 284: break; ! 285: default: ! 286: disp = "??"; ! 287: break; ! 288: } ! 289: disp += string_format("%d", id); ! 290: ! 291: indicators.emplace_back(disp, &WXStatusPanel::DrawSCSI, id); ! 292: } ! 293: // BSY 取得用 ! 294: scsibus = gSPC->GetSCSIBus(); ! 295: ! 296: // ネットワーク ! 297: #if 0 // XXX まだアクセスランプどうするか要調査 ! 298: if (gNetDriver->IsOpen()) { ! 299: net_ok = true; ! 300: } else { ! 301: net_ok = false; ! 302: } ! 303: indicators.emplace_back("LAN", &WXStatusPanel::DrawNet); ! 304: #endif ! 305: ! 306: // 電源 LED。点灯ロジックが分からないのでハリボテ。 ! 307: // ロジックが分かった時のために一応 3 文字にしておく。 ! 308: indicators.emplace_back("PWR", &WXStatusPanel::DrawPower); ! 309: } ! 310: ! 311: // インジケータのレイアウト ! 312: void ! 313: WXStatusPanel::LayoutIndicators() ! 314: { ! 315: int x; ! 316: int y; ! 317: int w; ! 318: int h; ! 319: ! 320: // まずは前から順に列挙して必要な幅を積算していく ! 321: x = 0; ! 322: for (auto& ind : indicators) { ! 323: // 前の枠とは1つ開ける ! 324: if (x != 0) { ! 325: x += 1; ! 326: } ! 327: ! 328: // 上の枠はキャンバス上端から2px ! 329: y = 2; ! 330: // 4 は、左右の外枠(1*2) と 枠と文字の間(1*2) ! 331: w = ind.text.length() * font_width + 2 + 2; ! 332: // そこから上下の外枠と枠と文字の間 ! 333: h = font_height + 2 + 2; ! 334: ! 335: ind.rect = wxRect(x, y, w, h); ! 336: x += w; ! 337: } ! 338: ! 339: // 全員を一旦右寄せ ! 340: wxSize sz = GetClientSize(); ! 341: int offsetx = sz.x - x; ! 342: for (auto& ind : indicators) { ! 343: ind.rect.Offset(offsetx, 0); ! 344: } ! 345: ! 346: // そのうちパフォーマンスカウンタだけ中央寄せまで戻す ! 347: if (indicators.size() > 0) { ! 348: auto& p = indicators.front(); ! 349: x = (sz.x / 2) - (p.rect.GetWidth() / 2); ! 350: if (p.rect.GetX() > x) { ! 351: p.rect.SetX(x); ! 352: } ! 353: } ! 354: } ! 355: ! 356: // パフォーマンスカウンタ ! 357: void ! 358: WXStatusPanel::DrawPerf(wxDC& dc, const Indicator& ind) ! 359: { ! 360: const wxRect& rect = ind.rect; ! 361: ! 362: // 枠 ! 363: dc.SetPen(wxPen(UD_GREY)); ! 364: dc.SetBrush(wxBrush(BGPANEL)); ! 365: dc.DrawRectangle(rect); ! 366: ! 367: // 電源オフならここまで ! 368: if (ispower == false) { ! 369: return; ! 370: } ! 371: ! 372: // 高速モードアイコン ! 373: // UI による高速モード指示なら >> (2つ) ! 374: // そのうち実際に高速動作なら >>> (3つ) ! 375: if ((perf_mode & Scheduler::SCHED_SYNC) == 0) { ! 376: SetTextForeColor(UD_RED); ! 377: DrawBitmap(dc, rect.x + 2, rect.y + 2, *ffmark); ! 378: DrawBitmap(dc, rect.x + 2 + font_width, rect.y + 2, *ffmark); ! 379: if (perf_mode == 0) { ! 380: DrawBitmap(dc, rect.x + 2 + font_width * 2, rect.y + 2, *ffmark); ! 381: } ! 382: SetTextForeColor(*wxBLACK); ! 383: } ! 384: ! 385: // パフォーマンス表示 ! 386: std::string text = string_format("%4d%%", perf_rate); ! 387: DrawStringSJIS(dc, rect.x + 2 + font_width * 3, rect.y + 2, text.c_str()); ! 388: } ! 389: ! 390: // SCSI デバイス側のアクセスランプ ! 391: // 本体ではなく各 SCSI デバイス側 (外付け HDD の前面とか) にあるやつ。 ! 392: void ! 393: WXStatusPanel::DrawSCSI(wxDC& dc, const Indicator& ind) ! 394: { ! 395: wxColour bg; ! 396: ! 397: if ((scsi_bsy & (1U << ind.userdata)) != 0) { ! 398: bg = UD_RED; ! 399: } else { ! 400: bg = BGPANEL; ! 401: } ! 402: ! 403: DrawTextLED(dc, ind, *wxBLACK, bg); ! 404: } ! 405: ! 406: // ネットワーク(LAN) ! 407: void ! 408: WXStatusPanel::DrawNet(wxDC& dc, const Indicator& ind) ! 409: { ! 410: wxColour fg; ! 411: wxColour bg = BGPANEL; ! 412: ! 413: if (net_ok) { ! 414: fg = *wxBLACK; ! 415: } else { ! 416: fg = UD_GREY; ! 417: } ! 418: ! 419: DrawTextLED(dc, ind, fg, bg); ! 420: } ! 421: ! 422: // 電源LED? ! 423: void ! 424: WXStatusPanel::DrawPower(wxDC& dc, const Indicator& ind) ! 425: { ! 426: DrawTextLED(dc, ind, *wxBLACK, UD_YELLOW_GREEN); ! 427: } ! 428: ! 429: // 共通の描画処理。 ! 430: // ind.text を前景色 fg、背景色 bg で描画するだけの場合。 ! 431: void ! 432: WXStatusPanel::DrawTextLED(wxDC& dc, const Indicator& ind, ! 433: const wxColour& fg, const wxColour& bg) ! 434: { ! 435: const wxRect& rect = ind.rect; ! 436: ! 437: // 枠 ! 438: dc.SetPen(wxPen(UD_GREY)); ! 439: dc.SetBrush(wxBrush(bg)); ! 440: dc.DrawRectangle(rect); ! 441: ! 442: SetTextColor(fg, bg); ! 443: DrawStringSJIS(dc, rect.x + 2, rect.y + 2, ind.text.c_str()); ! 444: ResetTextColor(); ! 445: } ! 446: ! 447: // 左ダブルクリックイベント ! 448: void ! 449: WXStatusPanel::OnLeftDClick(wxMouseEvent& event) ! 450: { ! 451: const wxPoint& pt = event.GetPosition(); ! 452: ! 453: for (auto& ind : indicators) { ! 454: // dclick を処理できる枠内なら ! 455: if (ind.rect.Contains(pt) && ind.dclick != NULL) { ! 456: (this->*(ind.dclick))(ind); ! 457: break; ! 458: } ! 459: } ! 460: } ! 461: ! 462: // パフォーマンスカウンタ枠へのダブルクリック ! 463: void ! 464: WXStatusPanel::DClickPerf(const Indicator& ind) ! 465: { ! 466: // モードを切り替える ! 467: gScheduler->SetFullSpeed(!gScheduler->GetFullSpeed()); ! 468: } ! 469: ! 470: // 高速モードアイコンの三角一個分、半角文字と同じ大きさ。 ! 471: // XBM データは左右鏡像(反転前に左詰め)で保持する。 ! 472: static constexpr uint8 FFMark12[] = { ! 473: _rev8(0b000000'00), // 0 ! 474: _rev8(0b100000'00), // 1 ! 475: _rev8(0b110000'00), // 2 ! 476: _rev8(0b111000'00), // 3 ! 477: _rev8(0b111100'00), // 4 ! 478: _rev8(0b111110'00), // 5 ! 479: _rev8(0b111111'00), // 6 ! 480: _rev8(0b111110'00), // 7 ! 481: _rev8(0b111100'00), // 8 ! 482: _rev8(0b111000'00), // 9 ! 483: _rev8(0b110000'00), // a ! 484: _rev8(0b100000'00), // b ! 485: }; ! 486: static constexpr uint8 FFMark16[] = { ! 487: _rev8(0b00000000), // 0 ! 488: _rev8(0b10000000), // 1 ! 489: _rev8(0b11000000), // 2 ! 490: _rev8(0b11100000), // 3 ! 491: _rev8(0b11110000), // 4 ! 492: _rev8(0b11111000), // 5 ! 493: _rev8(0b11111100), // 6 ! 494: _rev8(0b11111110), // 7 ! 495: _rev8(0b11111111), // 8 ! 496: _rev8(0b11111110), // 9 ! 497: _rev8(0b11111100), // a ! 498: _rev8(0b11111000), // b ! 499: _rev8(0b11110000), // c ! 500: _rev8(0b11100000), // d ! 501: _rev8(0b11000000), // e ! 502: _rev8(0b10000000), // f ! 503: }; ! 504: ! 505: // 高速モードアイコン(の1文字分)を現在のフォントサイズに合わせて作り直す ! 506: void ! 507: WXStatusPanel::UpdateFFMark() ! 508: { ! 509: const uint8 *xbm; ! 510: ! 511: switch (fontid) { ! 512: case FontId::_6x12: ! 513: xbm = FFMark12; ! 514: break; ! 515: case FontId::_8x16: ! 516: xbm = FFMark16; ! 517: break; ! 518: default: ! 519: assert(false); ! 520: } ! 521: ffmark.reset(new wxBitmap((const char *)xbm, font_width, font_height, 1)); ! 522: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.