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