|
|
1.1 root 1: //
2: // nono
1.1.1.3 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.11 root 7: //
8: // wx アプリケーションのエントリポイント
9: //
10:
11: #include "fontmanager.h"
12: #include "wxlogsetting.h"
1.1 root 13: #include "wxmainframe.h"
14: #include "wxmainview.h"
1.1.1.11 root 15: #include "wxuimessage.h"
1.1.1.4 root 16: #include "wxversion.h"
1.1.1.13 root 17: #include "config.h"
1.1 root 18: #include "mainapp.h"
1.1.1.13 root 19: #include "memdump.h"
1.1.1.8 root 20: #include <algorithm>
1.1.1.3 root 21: #include <wx/intl.h>
1.1 root 22:
1.1.1.8 root 23: class WXApp : public wxApp
1.1 root 24: {
1.1.1.6 root 25: using inherited = wxApp;
1.1 root 26: public:
1.1.1.11 root 27: ~WXApp() override;
1.1.1.13 root 28: bool OnInit() override;
1.1.1.6 root 29: int OnRun() override;
1.1 root 30: private:
31: bool ParseMonitors(const wxString& str);
1.1.1.15 root 32: bool ParseMemdumpValue(uint id, const std::string&, const std::string&);
1.1.1.8 root 33: void ShowMonitorsName();
1.1 root 34: int SearchMonitorName(const char *name);
1.1.1.15 root 35: int FindAvailable(const std::vector<uint>& candidates,
1.1.1.13 root 36: const std::string& name, int basenum, int count) const;
1.1.1.3 root 37:
38: wxLocale locale;
1.1.1.11 root 39:
40: std::unique_ptr<FontManager> pFontManager {};
41: std::unique_ptr<HostInfoMonitor> pHostInfo {};
1.1 root 42: };
43:
1.1.1.3 root 44: // IMPLEMENT_APP() は闇マクロすぎて clang のちからには耐えられない
1.1.1.11 root 45: PRAGMA_PUSH_WARNINGS
1.1 root 46: IMPLEMENT_APP(WXApp)
1.1.1.11 root 47: PRAGMA_POP_WARNINGS
48:
49: WXApp::~WXApp()
50: {
51: // 開発用
52: WXUIMessage::AssertAllDisconnected();
53: }
1.1 root 54:
55: bool
56: WXApp::OnInit()
57: {
1.1.1.6 root 58: return true;
59: }
60:
61: int
62: WXApp::OnRun()
63: {
1.1.1.5 root 64: int rv;
65:
1.1.1.7 root 66: rv = gMainApp.Init1(false, argc, argv);
1.1.1.5 root 67: if (rv != MainApp::PASS) {
1.1.1.6 root 68: return rv;
1.1 root 69: }
70:
1.1.1.3 root 71: // 環境変数 $NONO_BUILD_HOME が定義されていればカタログ検索パスに
72: // $NONO_BUILD_HOME/po を追加する (システムパスより前に挿入される)。
73: // カタログ開発時にワークツリーにあるカタログを見るため。
1.1.1.13 root 74: locale.Init(wxLANGUAGE_DEFAULT, wxLOCALE_DONT_LOAD_DEFAULT);
1.1.1.3 root 75: const char *build_home = getenv("NONO_BUILD_HOME");
76: if (build_home) {
77: wxString path(build_home);
78: path += "/po";
79: wxLocale::AddCatalogLookupPathPrefix(path);
80: }
81: locale.AddCatalog("nono");
82:
1.1.1.13 root 83: // テキスト系コントロールのフォントサイズ。
84: // 紛らわしいが設定の monitor-fontsize は 12, 16 とかいう値。
1.1.1.11 root 85: // FontManager::fontid は enum 値。
1.1.1.13 root 86: const ConfigItem& item_fontsize = gConfig->Find("monitor-fontsize");
87: int fontsize = item_fontsize.AsInt();
1.1.1.4 root 88: FontId fontid;
1.1.1.13 root 89: switch (fontsize) {
1.1.1.2 root 90: case 12:
1.1.1.4 root 91: fontid = FontId::_6x12;
1.1.1.2 root 92: break;
93: case 16:
1.1.1.4 root 94: fontid = FontId::_8x16;
1.1.1.2 root 95: break;
1.1.1.13 root 96: case 24:
97: fontid = FontId::_12x24;
98: break;
1.1.1.2 root 99: default:
1.1.1.13 root 100: item_fontsize.Err("Must be either 12, 16 or 24");
1.1.1.7 root 101: return EXIT_FAILURE;
1.1 root 102: }
1.1.1.18 root 103: try {
104: pFontManager.reset(new FontManager());
105: } catch (...) { }
106: if ((bool)pFontManager == false) {
107: warnx("Failed to initialize FontManager");
108: return EXIT_FAILURE;
109: }
1.1.1.11 root 110: gFontManager = pFontManager.get();
111: gFontManager->SetFont(fontid);
112:
113: // ホスト情報用のモニタ (どこでやるのがいいか)
1.1.1.18 root 114: try {
115: pHostInfo.reset(new HostInfoMonitor());
116: } catch (...) { }
117: if ((bool)pHostInfo == false) {
118: warnx("Failed to initialize HostInfoMonitor");
119: return EXIT_FAILURE;
120: }
1.1.1.11 root 121: gHostInfo = pHostInfo.get();
1.1 root 122:
1.1.1.17 root 123: // 手順 6. (vm/device.h)
1.1.1.8 root 124: // これ以降モニタの登録をしてはいけない。
1.1.1.11 root 125: gMonitorManager->Fix();
1.1.1.8 root 126:
1.1 root 127: // -M 起動時に表示するモニタ
1.1.1.2 root 128: if (!gMainApp.monitor_opt.empty()) {
129: if (!ParseMonitors(gMainApp.monitor_opt)) {
1.1.1.7 root 130: return EXIT_FAILURE;
1.1 root 131: }
132: }
133:
1.1.1.13 root 134: // メインスクリーン倍率
135: const ConfigItem& item_scale = gConfig->Find("mainview-scale");
136: double scale;
137: if (item_scale.TryDouble(&scale) == false) {
138: item_scale.Err();
139: return EXIT_FAILURE;
140: }
141: // 制限は適当
142: if (scale <= 0 || scale >= 10) {
143: item_scale.Err();
144: return EXIT_FAILURE;
145: }
146: WXMainView::screen_scale = scale;
147:
148: // モニター更新間隔
149: const ConfigItem& item_rate = gConfig->Find("monitor-rate");
150: int rate = item_rate.AsInt();
151: if (rate < 1 || rate > 60) {
152: item_rate.Err();
153: return EXIT_FAILURE;
154: }
155: WXMainFrame::SetMonitorRate(rate);
1.1 root 156:
1.1.1.7 root 157: // 引数処理が終わったので、スレッド作成を伴う初期化。
158: if (!gMainApp.Init2()) {
159: return EXIT_FAILURE;
160: }
161:
1.1.1.2 root 162: // メインウィンドウ
1.1.1.13 root 163: auto mainframe = new WXMainFrame(NULL);
164: mainframe->Show(true);
165: SetTopWindow(mainframe);
1.1.1.6 root 166:
1.1.1.10 root 167: // 設定反映などの初期化
1.1.1.13 root 168: if (!mainframe->Init()) {
1.1.1.10 root 169: return EXIT_FAILURE;
170: }
171:
1.1.1.14 root 172: // イベントループ。
173: rv = inherited::OnRun();
174:
175: // 戻ってきたので VM を解放。
176: gMainApp.Dispose();
177:
178: return rv;
1.1 root 179: }
180:
181: // -M オプションの解析。書式は
1.1.1.8 root 182: // -M <arg>[,<arg>[,...]]
183: // <arg> のどれかが "help" なら有効な名前を列挙して終了。
184: // <arg> は通常モニタ名(サブウィンドウ名)のみだが
185: // memdump* の場合のみ <name>=<hexaddr> 形式を受け付ける。
186: // 継続する場合は true、しない場合は false を返す。
1.1 root 187: bool
188: WXApp::ParseMonitors(const wxString& str)
189: {
1.1.1.8 root 190: std::vector<std::string> args = string_split(str, ',');
1.1 root 191:
1.1.1.8 root 192: // "help" があればヘルプを表示して終了
193: for (const auto& arg : args) {
194: if (arg == "help") {
195: ShowMonitorsName();
196: return false;
1.1 root 197: }
198: }
199:
1.1.1.8 root 200: // 一致するモニタ(ウィンドウ)の開始フラグを立てる
201: for (const auto& arg : args) {
202: std::string name;
203: std::string value;
204:
205: auto pos = arg.find('=');
206: if (pos != std::string::npos) {
207: name = arg.substr(0, pos);
208: value = arg.substr(++pos);
209: } else {
210: name = arg;
1.1.1.3 root 211: }
1.1.1.8 root 212:
213: // この名前のウィンドウを探して start フラグを立てる
214: int id = SearchMonitorName(name.c_str());
215: if (id < 0) {
1.1 root 216: return false;
217: }
1.1.1.8 root 218: WXMainFrame::subwindow_start[id] = true;
219:
220: // 値付き memdump の処理
1.1.1.13 root 221: if ((IS_MONITOR_MEMDUMP(id) || IS_MONITOR_XPMEMDUMP(id))
222: && value.empty() == false)
223: {
224: if (ParseMemdumpValue(id, name, value) == false) {
225: return false;
226: }
227: }
228: }
229: return true;
230: }
231:
232: // -Mmemdump= の続きの文字列 value を解析してセットする。
233: // 書式は <hex-address>[.<fmt>]
234: // fmt は B/W/L と M(MMU)、I(dIsasm)、Z(XPdisasm)
235: bool
1.1.1.15 root 236: WXApp::ParseMemdumpValue(uint id,
1.1.1.13 root 237: const std::string& name, const std::string& value)
238: {
239: auto mon = gMonitorManager->Get(id);
1.1.1.16 root 240: auto memdump = dynamic_cast<Memdump *>(mon->obj);
1.1.1.13 root 241: assert(memdump);
242:
243: errno = 0;
244: char *end;
245: unsigned long u = strtoul(value.c_str(), &end, 16);
246: // これが呼ばれた時点で value は空ではないはず
247: if (errno == ERANGE || u > 0xffffffff) {
248: errno = ERANGE;
249: warn("-M %s=%s", name.c_str(), value.c_str());
250: return false;
251: }
252: // アドレスを設定
253: memdump->SetAddr((uint32)u);
254:
255: // さらに後ろがあれば表示形式指定。
256: if (*end != '\0') {
257: if (*end++ != '.') {
258: goto usage;
259: }
260:
261: if (end[1] != '\0') {
262: goto usage;
263: }
264:
265: Memdump::Format fmt;
266: switch (std::toupper((int)*end)) {
267: case 'B': fmt = Memdump::Byte; break;
268: case '\0':
269: case 'W': fmt = Memdump::Word; break;
270: case 'L': fmt = Memdump::Long; break;
271: case 'M':
272: if (gMainApp.Has(VMCap::M88K)) {
273: fmt = Memdump::M88200Page;
274: } else {
1.1.1.16 root 275: // m68k はちょっと複雑すぎて放置。
276: goto usage;
1.1.1.8 root 277: }
1.1.1.13 root 278: break;
279: case 'I':
280: if (gMainApp.Has(VMCap::M88K)) {
281: fmt = Memdump::M88100Disasm;
282: } else {
1.1.1.16 root 283: fmt = Memdump::M680x0Disasm;
1.1.1.8 root 284: }
1.1.1.13 root 285: break;
286: case 'Z':
287: fmt = Memdump::HD64180Disasm;
288: break;
289: default:
290: usage:
1.1.1.14 root 291: warnx("-M %s=%s: syntax error; syntax is \"<hex-addr>[.<BWLMIZ>]\"",
1.1.1.13 root 292: name.c_str(), value.c_str());
293: return false;
1.1.1.8 root 294: }
1.1.1.13 root 295:
296: // 表示形式を設定
297: memdump->SetFormat(fmt);
1.1 root 298: }
1.1.1.13 root 299:
1.1 root 300: return true;
301: }
302:
1.1.1.8 root 303: // モニタ名の一覧を表示する
304: void
305: WXApp::ShowMonitorsName()
306: {
1.1.1.15 root 307: std::vector<uint> list;
1.1.1.8 root 308:
309: // モニターは登録されていれば列挙
310: // (登録されているものを全部列挙ではないことに注意)
1.1.1.15 root 311: for (uint id = ID_MONITOR_START; id <= ID_MONITOR_END; id++) {
1.1.1.11 root 312: if (gMonitorManager->Find(id)) {
1.1.1.8 root 313: list.push_back(id);
314: }
315: }
316: // サブウィンドウは機種情報から判断
1.1.1.15 root 317: for (uint id = ID_SUBWIN_START; id <= ID_SUBWIN_END; id++) {
1.1.1.12 root 318: VMCap vmcap = gMonitorManager->GetVMCap(id);
319: if (gMainApp.Has(vmcap)) {
1.1.1.8 root 320: list.push_back(id);
321: }
322: }
323:
324: // 一覧を表示
325: std::string msg = MonitorManager::MakeListString(list);
326: printf("%s", msg.c_str());
327: }
328:
329: // モニタ名 name を検索し、一つに確定すればその id を返す。
330: // 確定しない、あるいは一致しない場合はエラーメッセージを表示し -1 を返す。
331: //
332: // "memdump" (数字なし) は空いてる memdump<N> を順に使用する。
333: // ただし今の所この処理は1パスで前から順に処理しているだけなので、
334: // "memdump0,memdump" は指定可能だが(2つ目が memdump1 になる)、
335: // "memdump,memdump0" は memdump0 を2回指定したことになる。
1.1 root 336: int
337: WXApp::SearchMonitorName(const char *name)
338: {
1.1.1.15 root 339: std::vector<uint> candidates;
1.1.1.8 root 340: std::vector<std::string> cand_names;
341:
1.1.1.15 root 342: for (uint id = ID_MONITOR_START; id <= ID_SUBWIN_END; id++) {
1.1.1.8 root 343: if (id < ID_SUBWIN_START) {
1.1.1.19! root 344: // モニターは登録されていなければ除外する。
! 345: Monitor *mon = gMonitorManager->Find(id);
! 346: if (mon == NULL) {
! 347: continue;
! 348: }
! 349: // SLIRP モニタだけ、登録はされてて obj が NULL という状態がある。
! 350: if (mon->obj == NULL) {
1.1.1.8 root 351: continue;
352: }
353: } else {
354: // サブウィンドウは該当機種でなければ除外する
1.1.1.12 root 355: VMCap vmcap = gMonitorManager->GetVMCap(id);
356: if (gMainApp.Has(vmcap) == false) {
1.1.1.8 root 357: continue;
358: }
359: }
360:
1.1.1.11 root 361: const auto& aliases = gMonitorManager->GetAliases(id);
1.1.1.8 root 362:
363: bool matched = false;
364: for (const auto& alias : aliases) {
365: // 全文一致したら確定
366: // ("lunafb" と "lunafb0" みたいなのがあるため)
367: if (alias == name) {
368: return id;
369: }
370:
371: // 部分一致したら名前はすべて覚えておく
372: if (starts_with_ignorecase(alias, name)) {
373: cand_names.push_back(alias);
374: matched = true;
375: }
376: }
377: // 同じオブジェクトで別名が複数回部分一致しても、1回として数える
378: if (matched) {
379: candidates.push_back(id);
380: }
381: }
382:
383: if (candidates.empty()) {
384: // 一致しない
385: warnx("Unknown window name \"%s\"", name);
386: return -1;
387: } else if (candidates.size() == 1) {
388: // 部分一致したのが1つだけなら確定
389: return candidates[0];
390: } else {
391: // 候補が複数あった
1.1.1.15 root 392: uint id;
1.1.1.8 root 393:
1.1.1.13 root 394: // "memdump" を空いてる "memdump*" に割り当てる
395: id = FindAvailable(candidates, "memdump",
396: ID_MONITOR_MEMDUMP0, MAX_MEMDUMP_MONITOR);
397: if (id != 0) {
398: return id;
399: }
400:
401: // "xpmemdump" を空いてる "xpmemdump*" に割り当てる
402: id = FindAvailable(candidates, "xpmemdump",
403: ID_MONITOR_XPMEMDUMP0, MAX_XPMEMDUMP_MONITOR);
404: if (id != 0) {
405: return id;
1.1.1.8 root 406: }
407:
408: // そうでなければ、候補を表示
409: std::string candstr;
410: for (const auto& cname : cand_names) {
411: candstr += ' ';
412: candstr += cname;
1.1 root 413: }
1.1.1.8 root 414: warnx("Ambiguous window name \"%s\": candidates are%s",
415: name, candstr.c_str());
416: return -1;
1.1 root 417: }
418: }
1.1.1.13 root 419:
420: // candidates (候補の一覧) がすべて basenum .. count 内に入っていれば、
421: // その中の空きウィンドウ ID を返す。
422: // 空きがなければエラーメッセージを表示して - 1 を返す。
423: // candidates の中に basenum .. count 内に入っていないものがあれば 0 を返す。
424: // 今の所ここでターゲットにしている人たちに ID=0 はいないので。
425: // start は ID_MONITOR_*0、count は MAX_*_MONITOR のほうで、
426: // 最初と最後の ID ではないので注意。
427: int
1.1.1.15 root 428: WXApp::FindAvailable(const std::vector<uint>& candidates,
1.1.1.13 root 429: const std::string& name, int start, int count) const
430: {
431: // candidates がすべてこの start..end に入っているか
432: bool all = std::all_of(candidates.begin(), candidates.end(),
433: [&](int id) { return (start <= id && id < start + count); }
434: );
435:
436: if (all) {
437: // この時点で空いてるものを返す。
438: for (int i = 0; i < count; i++) {
439: int id = start + i;
440: if (WXMainFrame::subwindow_start[id] == false) {
441: return id;
442: }
443: }
444: // 空きがなければエラー
445: warnx("No more %s windows available", name.c_str());
446: return -1;
447: }
448:
449: // 候補が他にもあった
450: return 0;
451: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.