--- nono/wx/wxapp.cpp 2026/04/29 17:04:33 1.1.1.2 +++ nono/wx/wxapp.cpp 2026/04/29 17:04:56 1.1.1.8 @@ -1,122 +1,297 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "vm.h" -#include "wxheader.h" +#include "wxdumpmonitor.h" #include "wxmainframe.h" #include "wxmainview.h" #include "wxtextpanel.h" +#include "wxversion.h" #include "mainapp.h" +#include "mpu680x0.h" +#include "mpu88xx0.h" +#include "scheduler.h" +#include +#include // イベントタイプの定義 -DEFINE_EVENT_TYPE(WXW_EVT_CREATE); -DEFINE_EVENT_TYPE(WXW_EVT_MONITOR_UPDATE); +wxDEFINE_EVENT(WXW_EVT_HALT_NOTIFY, wxCommandEvent); -class WXApp - : public wxApp +class WXApp : public wxApp { + using inherited = wxApp; public: bool OnInit(); + int OnRun() override; private: bool ParseMonitors(const wxString& str); + void ShowMonitorsName(); int SearchMonitorName(const char *name); + + wxLocale locale; }; +// IMPLEMENT_APP() は闇マクロすぎて clang のちからには耐えられない +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Weverything" +#endif IMPLEMENT_APP(WXApp) +#if defined(__clang__) +#pragma clang diagnostic pop +#endif bool WXApp::OnInit() { - if (!gMainApp.Init(false, argc, argv)) { - return false; + return true; +} + +int +WXApp::OnRun() +{ + int rv; + + rv = gMainApp.Init1(false, argc, argv); + if (rv != MainApp::PASS) { + return rv; + } + + // 環境変数 $NONO_BUILD_HOME が定義されていればカタログ検索パスに + // $NONO_BUILD_HOME/po を追加する (システムパスより前に挿入される)。 + // カタログ開発時にワークツリーにあるカタログを見るため。 + locale.Init(wxLANGUAGE_DEFAULT); + const char *build_home = getenv("NONO_BUILD_HOME"); + if (build_home) { + wxString path(build_home); + path += "/po"; + wxLocale::AddCatalogLookupPathPrefix(path); } + locale.AddCatalog("nono"); // --fontsize テキスト系コントロールのフォントサイズ // 紛らわしいが ::global_fontsize は 12, 16 とかいう値。 - // WXTextPanel::global_fontsize (fontsize_t) は enum 値。 - fontsize_t fontsize; + // WXTextPanel::global_fontsize (FontId) は enum 値。 + FontId fontid; switch (gMainApp.fontsize) { case 12: - fontsize = FONT_6x12; + fontid = FontId::_6x12; break; case 16: - fontsize = FONT_8x16; - break; - case 24: - fontsize = FONT_12x24; + fontid = FontId::_8x16; break; default: warnx("invalid fontsize: %d", gMainApp.fontsize); - return false; + return EXIT_FAILURE; } - WXTextPanel::global_fontsize = fontsize; + WXTextPanel::global_fontid = fontid; + + // グローバルオブジェクト (どこでやるのがいいか) + gHostInfo.reset(new HostInfoMonitor()); + + // これ以降モニタの登録をしてはいけない。 + gMonitorManager.Fix(); // -M 起動時に表示するモニタ if (!gMainApp.monitor_opt.empty()) { if (!ParseMonitors(gMainApp.monitor_opt)) { - return false; + return EXIT_FAILURE; } } // -s screen scale WXMainView::screen_scale = gMainApp.screen_scale; - // 引数処理が終わったので VM 開始。 - if (!gMainApp.Start()) { - return false; + // 引数処理が終わったので、スレッド作成を伴う初期化。 + if (!gMainApp.Init2()) { + return EXIT_FAILURE; + } + + // VM にコールバックを登録 + gScheduler->SetPowerOffCallback(wxmainframe_poweroff_callback); + if (gMPU680x0) { + gMPU680x0->SetHaltCallback(wxmainframe_halt_callback, this); } // メインウィンドウ gMainFrame = new WXMainFrame(NULL); gMainFrame->Show(true); SetTopWindow(gMainFrame); - return true; + + return inherited::OnRun(); } // -M オプションの解析。書式は -// -M [,[,...]] -// -M help -// 継続不能なエラーなら false を返す。 +// -M [,[,...]] +// のどれかが "help" なら有効な名前を列挙して終了。 +// は通常モニタ名(サブウィンドウ名)のみだが +// memdump* の場合のみ = 形式を受け付ける。 +// 継続する場合は true、しない場合は false を返す。 bool WXApp::ParseMonitors(const wxString& str) { - const char *sep = ","; - char *word; - char *last; - char buf[str.Length() + 1]; - - if (str == wxT("help")) { - for (int i = 0; i < WinID_MAX; i++) { - fprintf(stderr, " %s\n", WXMainFrame::monitor_names[i]); - } - return false; - } - - // 由緒正しい C でパースしてしまう - strcpy(buf, (const char *)str.mb_str()); - for (word = strtok_r(buf, sep, &last); - word; - word = strtok_r(NULL, sep, &last)) - { - int i = SearchMonitorName(word); - if (i < 0) { - warnx("Unknown window name \"%s\"", word); + std::vector args = string_split(str, ','); + + // "help" があればヘルプを表示して終了 + for (const auto& arg : args) { + if (arg == "help") { + ShowMonitorsName(); + return false; + } + } + + // 一致するモニタ(ウィンドウ)の開始フラグを立てる + for (const auto& arg : args) { + std::string name; + std::string value; + + auto pos = arg.find('='); + if (pos != std::string::npos) { + name = arg.substr(0, pos); + value = arg.substr(++pos); + } else { + name = arg; + } + + // この名前のウィンドウを探して start フラグを立てる + int id = SearchMonitorName(name.c_str()); + if (id < 0) { return false; } - WXMainFrame::monitor_start[i] = true; + WXMainFrame::subwindow_start[id] = true; + + // 値付き memdump の処理 + if (IS_MONITOR_MEMDUMP(id) && value.empty() == false) { + char *end; + errno = 0; + unsigned long u = strtoul(value.c_str(), &end, 16); + if (end == optarg || end[0] != '\0') { + warnx("%s: syntax error on monitor address", value.c_str()); + return -1; + } + if (errno == ERANGE || u > 0xffffffff) { + errno = ERANGE; + warn("%s", value.c_str()); + return -1; + } + WXMemdumpWindow::SetStickyAddr(id, (uint32)u); + } } return true; } +// モニタ名の一覧を表示する +void +WXApp::ShowMonitorsName() +{ + std::vector list; + + // モニターは登録されていれば列挙 + // (登録されているものを全部列挙ではないことに注意) + for (int id = ID_MONITOR_START; id <= ID_MONITOR_END; id++) { + if (gMonitorManager.Find(id)) { + list.push_back(id); + } + } + // サブウィンドウは機種情報から判断 + for (int id = ID_SUBWIN_START; id <= ID_SUBWIN_END; id++) { + vmf_t feature = gMonitorManager.GetFeature(id); + if (gMainApp.HasVMFeature(feature)) { + list.push_back(id); + } + } + + // 一覧を表示 + std::string msg = MonitorManager::MakeListString(list); + printf("%s", msg.c_str()); +} + +// モニタ名 name を検索し、一つに確定すればその id を返す。 +// 確定しない、あるいは一致しない場合はエラーメッセージを表示し -1 を返す。 +// +// "memdump" (数字なし) は空いてる memdump を順に使用する。 +// ただし今の所この処理は1パスで前から順に処理しているだけなので、 +// "memdump0,memdump" は指定可能だが(2つ目が memdump1 になる)、 +// "memdump,memdump0" は memdump0 を2回指定したことになる。 int WXApp::SearchMonitorName(const char *name) { - for (int i = 0; i < WinID_MAX; i++) { - if (strcmp(name, WXMainFrame::monitor_names[i]) == 0) { - return i; + std::vector candidates; + std::vector cand_names; + + for (int id = ID_MONITOR_START; id <= ID_SUBWIN_END; id++) { + if (id < ID_SUBWIN_START) { + // モニターは登録されていなければ除外する + if (gMonitorManager.Find(id) == NULL) { + continue; + } + } else { + // サブウィンドウは該当機種でなければ除外する + vmf_t feature = gMonitorManager.GetFeature(id); + if (gMainApp.HasVMFeature(feature) == false) { + continue; + } + } + + const auto& aliases = gMonitorManager.GetAliases(id); + + bool matched = false; + for (const auto& alias : aliases) { + // 全文一致したら確定 + // ("lunafb" と "lunafb0" みたいなのがあるため) + if (alias == name) { + return id; + } + + // 部分一致したら名前はすべて覚えておく + if (starts_with_ignorecase(alias, name)) { + cand_names.push_back(alias); + matched = true; + } + } + // 同じオブジェクトで別名が複数回部分一致しても、1回として数える + if (matched) { + candidates.push_back(id); + } + } + + if (candidates.empty()) { + // 一致しない + warnx("Unknown window name \"%s\"", name); + return -1; + } else if (candidates.size() == 1) { + // 部分一致したのが1つだけなら確定 + return candidates[0]; + } else { + // 候補が複数あった + + // 候補が memdumpN のみなら無記名 memdump とする。 + bool is_memdump = std::all_of(candidates.begin(), candidates.end(), + [](int x) { return IS_MONITOR_MEMDUMP(x); } + ); + if (is_memdump) { + // この時点で空いてるものを返す + for (int i = 0; i < MAX_MEMDUMP_MONITOR; i++) { + int id = ID_MONITOR_MEMDUMP0 + i; + if (WXMainFrame::subwindow_start[id] == false) { + return id; + } + } + // 空きがなければエラー + warnx("No more memdump windows available"); + return -1; + } + + // そうでなければ、候補を表示 + std::string candstr; + for (const auto& cname : cand_names) { + candstr += ' '; + candstr += cname; } + warnx("Ambiguous window name \"%s\": candidates are%s", + name, candstr.c_str()); + return -1; } - return -1; }