Annotation of nono/wx/wxapp.cpp, revision 1.1.1.21

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.