--- nono/wx/wxapp.cpp 2026/04/29 17:04:28 1.1 +++ nono/wx/wxapp.cpp 2026/04/29 17:05:23 1.1.1.14 @@ -1,199 +1,434 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "vm.h" -#include "wxheader.h" +// +// wx アプリケーションのエントリポイント +// + +#include "fontmanager.h" +#include "wxlogsetting.h" #include "wxmainframe.h" #include "wxmainview.h" -#include "wxtextpanel.h" +#include "wxuimessage.h" +#include "wxversion.h" +#include "config.h" #include "mainapp.h" -#include - -// イベントタイプの定義 -DEFINE_EVENT_TYPE(WXW_EVT_CREATE); -DEFINE_EVENT_TYPE(WXW_EVT_MONITOR_UPDATE); - -// コマンドラインオプションとか -bool console_log; - -// コマンドラインオプション -static const wxCmdLineEntryDesc cmddesc[] = -{ - { wxCMD_LINE_OPTION, "b", "b", "breakpoint", wxCMD_LINE_VAL_STRING }, - { wxCMD_LINE_OPTION, "c", "c", "vm directory", wxCMD_LINE_VAL_STRING }, - { wxCMD_LINE_SWITCH, "d", "d", "debugger prompt on startup", }, - { wxCMD_LINE_SWITCH, "C", "C", "console log" }, - { wxCMD_LINE_SWITCH, "f", "f", "fast mode" }, - { wxCMD_LINE_OPTION, "L", "L", "loglevel (-L help displays names)", - wxCMD_LINE_VAL_STRING }, - { wxCMD_LINE_OPTION, "M", "M", "monitors to display at startup", - wxCMD_LINE_VAL_STRING }, - { wxCMD_LINE_OPTION, "s", "s", "screen scale", wxCMD_LINE_VAL_STRING }, - { wxCMD_LINE_OPTION, "", "fontsize", "font size {12, 16, 24} (default:12)", - wxCMD_LINE_VAL_STRING }, - { wxCMD_LINE_SWITCH, "v", "v", "version" }, - - { wxCMD_LINE_NONE }, -}; +#include "memdump.h" +#include +#include -class WXApp - : public wxApp +class WXApp : public wxApp { + using inherited = wxApp; public: - bool OnInit(); + ~WXApp() override; + bool OnInit() override; + int OnRun() override; private: - bool Parse(); bool ParseMonitors(const wxString& str); + bool ParseMemdumpValue(int id, const std::string&, const std::string&); + void ShowMonitorsName(); int SearchMonitorName(const char *name); + int FindAvailable(const std::vector& candidates, + const std::string& name, int basenum, int count) const; + + wxLocale locale; + + std::unique_ptr pFontManager {}; + std::unique_ptr pHostInfo {}; }; +// IMPLEMENT_APP() は闇マクロすぎて clang のちからには耐えられない +PRAGMA_PUSH_WARNINGS IMPLEMENT_APP(WXApp) +PRAGMA_POP_WARNINGS + +WXApp::~WXApp() +{ + // 開発用 + WXUIMessage::AssertAllDisconnected(); +} bool WXApp::OnInit() { - debug_breakaddr = 0xffffffff; - WXMainView::screen_scale = 1.0; - WXTextPanel::global_fontsize = FONT_6x12; - vmdir = "."; + return true; +} - if (!Parse()) { - return false; +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, wxLOCALE_DONT_LOAD_DEFAULT); + const char *build_home = getenv("NONO_BUILD_HOME"); + if (build_home) { + wxString path(build_home); + path += "/po"; + wxLocale::AddCatalogLookupPathPrefix(path); + } + locale.AddCatalog("nono"); + + // テキスト系コントロールのフォントサイズ。 + // 紛らわしいが設定の monitor-fontsize は 12, 16 とかいう値。 + // FontManager::fontid は enum 値。 + const ConfigItem& item_fontsize = gConfig->Find("monitor-fontsize"); + int fontsize = item_fontsize.AsInt(); + FontId fontid; + switch (fontsize) { + case 12: + fontid = FontId::_6x12; + break; + case 16: + fontid = FontId::_8x16; + break; + case 24: + fontid = FontId::_12x24; + break; + default: + item_fontsize.Err("Must be either 12, 16 or 24"); + return EXIT_FAILURE; + } + pFontManager.reset(new FontManager()); + gFontManager = pFontManager.get(); + gFontManager->SetFont(fontid); + + // ホスト情報用のモニタ (どこでやるのがいいか) + pHostInfo.reset(new HostInfoMonitor()); + gHostInfo = pHostInfo.get(); + + // 手順 4. (vm/device.h) + // これ以降モニタの登録をしてはいけない。 + gMonitorManager->Fix(); + + // -M 起動時に表示するモニタ + if (!gMainApp.monitor_opt.empty()) { + if (!ParseMonitors(gMainApp.monitor_opt)) { + return EXIT_FAILURE; + } } - // -C オプションならコンソールにもログを出力 - if (!main_init(console_log)) { - return false; + // メインスクリーン倍率 + const ConfigItem& item_scale = gConfig->Find("mainview-scale"); + double scale; + if (item_scale.TryDouble(&scale) == false) { + item_scale.Err(); + return EXIT_FAILURE; + } + // 制限は適当 + if (scale <= 0 || scale >= 10) { + item_scale.Err(); + return EXIT_FAILURE; + } + WXMainView::screen_scale = scale; + + // モニター更新間隔 + const ConfigItem& item_rate = gConfig->Find("monitor-rate"); + int rate = item_rate.AsInt(); + if (rate < 1 || rate > 60) { + item_rate.Err(); + return EXIT_FAILURE; + } + WXMainFrame::SetMonitorRate(rate); + + // 引数処理が終わったので、スレッド作成を伴う初期化。 + if (!gMainApp.Init2()) { + return EXIT_FAILURE; } // メインウィンドウ - gMainFrame = new WXMainFrame(NULL); - gMainFrame->Show(true); - SetTopWindow(gMainFrame); - return true; + auto mainframe = new WXMainFrame(NULL); + mainframe->Show(true); + SetTopWindow(mainframe); + + // 設定反映などの初期化 + if (!mainframe->Init()) { + return EXIT_FAILURE; + } + + // イベントループ。 + rv = inherited::OnRun(); + + // 戻ってきたので VM を解放。 + gMainApp.Dispose(); + + return rv; } +// -M オプションの解析。書式は +// -M [,[,...]] +// のどれかが "help" なら有効な名前を列挙して終了。 +// は通常モニタ名(サブウィンドウ名)のみだが +// memdump* の場合のみ = 形式を受け付ける。 +// 継続する場合は true、しない場合は false を返す。 bool -WXApp::Parse() +WXApp::ParseMonitors(const wxString& str) { - u_long ulval; + std::vector args = string_split(str, ','); - wxCmdLineParser cmd(argc, argv); - cmd.SetDesc(cmddesc); - if (cmd.Parse() != 0) { - return false; + // "help" があればヘルプを表示して終了 + for (const auto& arg : args) { + if (arg == "help") { + ShowMonitorsName(); + return false; + } } - wxString arg; - - // -v - if (cmd.Found("v")) { - fprintf(stderr, "nono version %d.%d.%d\n", - NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER); - 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; + } - // -b ブレイクポイント - if (cmd.Found("b", &arg)) { - arg.ToULong(&ulval, 16); - debug_breakaddr = ulval; - } + // この名前のウィンドウを探して start フラグを立てる + int id = SearchMonitorName(name.c_str()); + if (id < 0) { + return false; + } + WXMainFrame::subwindow_start[id] = true; - // -c 設定ファイル - if (cmd.Found("c", &arg)) { - vmdir = std::string((const char *)arg.mb_str()); + // 値付き memdump の処理 + if ((IS_MONITOR_MEMDUMP(id) || IS_MONITOR_XPMEMDUMP(id)) + && value.empty() == false) + { + if (ParseMemdumpValue(id, name, value) == false) { + return false; + } + } } + return true; +} - // -L ログレベル - if (cmd.Found("L", &arg)) { - strlcpy(logopt, (const char *)arg.mb_str(), sizeof(logopt)); +// -Mmemdump= の続きの文字列 value を解析してセットする。 +// 書式は [.] +// fmt は B/W/L と M(MMU)、I(dIsasm)、Z(XPdisasm) +bool +WXApp::ParseMemdumpValue(int id, + const std::string& name, const std::string& value) +{ + auto mon = gMonitorManager->Get(id); + auto memdump = dynamic_cast(mon.obj); + assert(memdump); + + errno = 0; + char *end; + unsigned long u = strtoul(value.c_str(), &end, 16); + // これが呼ばれた時点で value は空ではないはず + if (errno == ERANGE || u > 0xffffffff) { + errno = ERANGE; + warn("-M %s=%s", name.c_str(), value.c_str()); + return false; } + // アドレスを設定 + memdump->SetAddr((uint32)u); - // -M 起動時に表示するモニタ - if (cmd.Found("M", &arg)) { - if (!ParseMonitors(arg)) { - return false; + // さらに後ろがあれば表示形式指定。 + if (*end != '\0') { + if (*end++ != '.') { + goto usage; } - } - // screen scale - if (cmd.Found("s", &arg)) { - if (!arg.ToDouble(&WXMainView::screen_scale)) { - return false; + if (end[1] != '\0') { + goto usage; } - } - // テキスト系コントロールのフォントサイズ - if (cmd.Found("fontsize", &arg)) { - fontsize_t fontsize; - arg.ToULong(&ulval, 10); - switch (ulval) { - case 12: - fontsize = FONT_6x12; + Memdump::Format fmt; + switch (std::toupper((int)*end)) { + case 'B': fmt = Memdump::Byte; break; + case '\0': + case 'W': fmt = Memdump::Word; break; + case 'L': fmt = Memdump::Long; break; + case 'M': + if (gMainApp.Has(VMCap::M88K)) { + fmt = Memdump::M88200Page; + } else { + // ロングは指定できないけど使われてないので放置 + fmt = Memdump::M68030PageShort; + } break; - case 16: - fontsize = FONT_8x16; + case 'I': + if (gMainApp.Has(VMCap::M88K)) { + fmt = Memdump::M88100Disasm; + } else { + fmt = Memdump::M68030Disasm; + } break; - case 24: - fontsize = FONT_12x24; + case 'Z': + fmt = Memdump::HD64180Disasm; break; default: + usage: + warnx("-M %s=%s: syntax error; syntax is \"[.]\"", + name.c_str(), value.c_str()); return false; } - WXTextPanel::global_fontsize = fontsize; - } - console_log = cmd.Found("C"); - debug_on_start = cmd.Found("d"); - fast_mode = cmd.Found("f"); + // 表示形式を設定 + memdump->SetFormat(fmt); + } return true; } -// -M オプションの解析。書式は -// -M [,[,...]] -// -M help -// 継続不能なエラーなら false を返す。 -bool -WXApp::ParseMonitors(const wxString& str) +// モニタ名の一覧を表示する +void +WXApp::ShowMonitorsName() { - 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]); + std::vector list; + + // モニターは登録されていれば列挙 + // (登録されているものを全部列挙ではないことに注意) + for (int id = ID_MONITOR_START; id <= ID_MONITOR_END; id++) { + if (gMonitorManager->Find(id)) { + list.push_back(id); } - 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); - return false; + // サブウィンドウは機種情報から判断 + for (int id = ID_SUBWIN_START; id <= ID_SUBWIN_END; id++) { + VMCap vmcap = gMonitorManager->GetVMCap(id); + if (gMainApp.Has(vmcap)) { + list.push_back(id); } - WXMainFrame::monitor_start[i] = true; } - return true; + + // 一覧を表示 + 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 { + // サブウィンドウは該当機種でなければ除外する + VMCap vmcap = gMonitorManager->GetVMCap(id); + if (gMainApp.Has(vmcap) == 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 { + // 候補が複数あった + int id; + + // "memdump" を空いてる "memdump*" に割り当てる + id = FindAvailable(candidates, "memdump", + ID_MONITOR_MEMDUMP0, MAX_MEMDUMP_MONITOR); + if (id != 0) { + return id; + } + + // "xpmemdump" を空いてる "xpmemdump*" に割り当てる + id = FindAvailable(candidates, "xpmemdump", + ID_MONITOR_XPMEMDUMP0, MAX_XPMEMDUMP_MONITOR); + if (id != 0) { + return id; + } + + // そうでなければ、候補を表示 + 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; + } +} + +// candidates (候補の一覧) がすべて basenum .. count 内に入っていれば、 +// その中の空きウィンドウ ID を返す。 +// 空きがなければエラーメッセージを表示して - 1 を返す。 +// candidates の中に basenum .. count 内に入っていないものがあれば 0 を返す。 +// 今の所ここでターゲットにしている人たちに ID=0 はいないので。 +// start は ID_MONITOR_*0、count は MAX_*_MONITOR のほうで、 +// 最初と最後の ID ではないので注意。 +int +WXApp::FindAvailable(const std::vector& candidates, + const std::string& name, int start, int count) const +{ + // candidates がすべてこの start..end に入っているか + bool all = std::all_of(candidates.begin(), candidates.end(), + [&](int id) { return (start <= id && id < start + count); } + ); + + if (all) { + // この時点で空いてるものを返す。 + for (int i = 0; i < count; i++) { + int id = start + i; + if (WXMainFrame::subwindow_start[id] == false) { + return id; + } } + // 空きがなければエラー + warnx("No more %s windows available", name.c_str()); + return -1; } - return -1; + + // 候補が他にもあった + return 0; }