--- nono/wx/wxmainframe.cpp 2026/04/29 17:04:56 1.1.1.10 +++ nono/wx/wxmainframe.cpp 2026/04/29 17:05:12 1.1.1.14 @@ -4,36 +4,171 @@ // Licensed under nono-license.txt // +// +// メインウィンドウ +// + #include "wxmainframe.h" +#include "fontmanager.h" #include "wxdumpmonitor.h" -#include "wxhistmonitor.h" #include "wxlcdwindow.h" #include "wxlogmonitor.h" +#include "wxlogsetting.h" #include "wxmainview.h" #include "wxmonitor.h" #include "wxmpumonitor.h" +#include "wxplanemonitor.h" +#include "wxromwindow.h" #include "wxsoftkey.h" #include "wxstatuspanel.h" -#include "wxtextpanel.h" +#include "wxuimessage.h" #include "wxversion.h" -#include "wxvideomonitor.h" +#include "config.h" #include "debugger.h" +#include "fdc.h" +#include "fdd.h" #include "mainapp.h" #include "mpu680x0.h" #include "mpu88xx0.h" #include "netdriver.h" -#include "scheduler.h" +#include "nmi.h" +#include "power.h" +#include "spc.h" +#include "sync.h" #include "vm.h" +// ID は 0 から (wxID_LOWEST = 5000 未満まで) 使えるっぽい。 +// メニュー ID のうちウィンドウのオープン状態を維持する必要があるものは +// (ID が 0 から使えるのなら) 0 から割り当てて配列で管理してしまうのが +// 手っ取り早い。 +// ウィンドウに割り当てるウィンドウ ID とメニューイベントで飛んでくる +// メニュー ID は互いに干渉しないはずなので、同じ値を使うことでメニューと +// ウィンドウの紐付けをする。 +// +// ウィンドウ(サブウィンドウ)は細かくは以下のように4つに分類できるが、 +// 処理上区別が必要なのは A. (デバッガの show コマンドとも共用できる +// サブウィンドウ) とそれ以外のサブウィンドウだけ。 +// +// -----------+------------+---- +// モニター | テキスト | A. 単純なテキストモニタウィンドウ +// ウィンドウ | モニター | (SPC, MFP などのデバイスのモニター) +// | ウィンドウ +---- +// | | B. それ以外のテキストモニターウィンドウ +// | | (ログ、メモリのように表示位置があるもの、 +// | | CMMU のように構造があるもの) +// +------------+---- +// | それ以外の | C. ビデオモニターウィンドウなど +// | モニター | (ビットマッププレーン、TVRAM など) +// | ウィンドウ | +// -----------+------------+---- +// それ以外のウィンドウ | D. ソフトウェアキーボードウィンドウなど +// ------------------------+---- +enum { + ID_SUBWIN_END_DUMMY = ID_SUBWIN_END, + + // ここから (ウィンドウでない)メニュー ID + ID_RESTART_VM, + ID_FULL_SPEED, + + ID_INPUT_START, + ID_INPUT_CHAR, + ID_INPUT_JP, + ID_INPUT_END, + + ID_SCALE_START, + ID_SCALE_1_00, + ID_SCALE_0_75, + ID_SCALE_0_50, + ID_SCALE_END, + + ID_FONTSIZE_START, // START 以降は fontsize_t と同じ順に並べること + ID_FONTSIZE_12, + ID_FONTSIZE_16, + ID_FONTSIZE_END, + + ID_MONRATE_START, + ID_MONRATE_20, + ID_MONRATE_5, + ID_MONRATE_1, + ID_MONRATE_END, + + ID_SHOW_STATPANEL, + + // FD + ID_FD_INSERT_0, + ID_FD_INSERT_1, + ID_FD_INSERT_2, + ID_FD_INSERT_3, + ID_FD_MISINSERT_0, + ID_FD_MISINSERT_1, + ID_FD_MISINSERT_2, + ID_FD_MISINSERT_3, + ID_FD_EJECT_0, + ID_FD_EJECT_1, + ID_FD_EJECT_2, + ID_FD_EJECT_3, + ID_FD_FORCE_0, + ID_FD_FORCE_1, + ID_FD_FORCE_2, + ID_FD_FORCE_3, + + // CD/MO (リムーバブルデバイス) + ID_CD_INSERT_0, + ID_CD_INSERT_1, + ID_CD_INSERT_2, + ID_CD_INSERT_3, + ID_CD_INSERT_4, + ID_CD_INSERT_5, + ID_CD_INSERT_6, + ID_CD_INSERT_7, + ID_CD_EJECT_0, + ID_CD_EJECT_1, + ID_CD_EJECT_2, + ID_CD_EJECT_3, + ID_CD_EJECT_4, + ID_CD_EJECT_5, + ID_CD_EJECT_6, + ID_CD_EJECT_7, + ID_CD_FORCE_0, + ID_CD_FORCE_1, + ID_CD_FORCE_2, + ID_CD_FORCE_3, + ID_CD_FORCE_4, + ID_CD_FORCE_5, + ID_CD_FORCE_6, + ID_CD_FORCE_7, + + ID_MOUSEMODE, + ID_POWER_BUTTON, + ID_RESET_BUTTON, + ID_NMI_BUTTON, + ID_KEYBOARD_CONNECT, + + ID_local_end, // 最後に置く (チェック用) +}; +static_assert(ID_local_end - 1 <= (int)IDGROUP_MAINFRAME_END, "ID exceeded"); + +#define ID_FD_INSERT(id) (ID_FD_INSERT_0 + (id)) +#define ID_FD_MISINSERT(id) (ID_FD_MISINSERT_0 + (id)) +#define ID_FD_EJECT(id) (ID_FD_EJECT_0 + (id)) +#define ID_FD_FORCE(id) (ID_FD_FORCE_0 + (id)) +#define ID_CD_INSERT(id) (ID_CD_INSERT_0 + (id)) +#define ID_CD_EJECT(id) (ID_CD_EJECT_0 + (id)) +#define ID_CD_FORCE(id) (ID_CD_FORCE_0 + (id)) + WXMainFrame *gMainFrame; wxBEGIN_EVENT_TABLE(WXMainFrame, inherited) EVT_CLOSE(WXMainFrame::OnClose) EVT_MENU(wxID_EXIT, WXMainFrame::OnExit) + EVT_MENU(ID_RESTART_VM, WXMainFrame::OnRestartVM) EVT_MENU(ID_FULL_SPEED, WXMainFrame::OnFullSpeed) EVT_UPDATE_UI(ID_FULL_SPEED, WXMainFrame::OnFullSpeedUI) - EVT_MENU(ID_RESTART_VM, WXMainFrame::OnRestartVM) + EVT_MENU_RANGE(ID_INPUT_START + 1, ID_INPUT_END - 1, + WXMainFrame::OnMenuInput) + EVT_UPDATE_UI_RANGE(ID_INPUT_START + 1, ID_INPUT_END -1, + WXMainFrame::OnMenuInputUI) EVT_MENU_RANGE(ID_SCALE_START + 1, ID_SCALE_END - 1, WXMainFrame::OnMenuScale) EVT_UPDATE_UI_RANGE(ID_SCALE_START + 1, ID_SCALE_END - 1, @@ -48,34 +183,68 @@ wxBEGIN_EVENT_TABLE(WXMainFrame, inherit WXMainFrame::OnMenuMonRateUI) EVT_MENU(ID_SHOW_STATPANEL, WXMainFrame::OnShowStatusPanel) EVT_UPDATE_UI(ID_SHOW_STATPANEL, WXMainFrame::OnShowStatusPanelUI) + EVT_MENU_RANGE(ID_FD_INSERT_0, ID_FD_INSERT_3, + WXMainFrame::OnFDInsert) + EVT_UPDATE_UI_RANGE(ID_FD_INSERT_0, ID_FD_INSERT_3, + WXMainFrame::OnFDInsertUI) + EVT_MENU_RANGE(ID_FD_EJECT_0, ID_FD_EJECT_3, + WXMainFrame::OnFDEject) + EVT_UPDATE_UI_RANGE(ID_FD_EJECT_0, ID_FD_EJECT_3, + WXMainFrame::OnFDEjectUI) + EVT_MENU_RANGE(ID_FD_FORCE_0, ID_FD_FORCE_3, + WXMainFrame::OnFDForce) + EVT_UPDATE_UI_RANGE(ID_FD_FORCE_0, ID_FD_FORCE_3, + WXMainFrame::OnFDForceUI) + EVT_MENU_RANGE(ID_CD_INSERT_0, ID_CD_INSERT_7, + WXMainFrame::OnCDInsert) + EVT_UPDATE_UI_RANGE(ID_CD_INSERT_0, ID_CD_INSERT_7, + WXMainFrame::OnCDInsertUI) + EVT_MENU_RANGE(ID_CD_EJECT_0, ID_CD_EJECT_7, + WXMainFrame::OnCDEject) + EVT_UPDATE_UI_RANGE(ID_CD_EJECT_0, ID_CD_EJECT_7, + WXMainFrame::OnCDEjectUI) + EVT_MENU_RANGE(ID_CD_FORCE_0, ID_CD_FORCE_7, + WXMainFrame::OnCDForce) + EVT_UPDATE_UI_RANGE(ID_CD_FORCE_0, ID_CD_FORCE_7, + WXMainFrame::OnCDForceUI) EVT_MENU(ID_MOUSEMODE, WXMainFrame::OnMouseMode) EVT_UPDATE_UI(ID_MOUSEMODE, WXMainFrame::OnMouseModeUI) + EVT_MENU(ID_POWER_BUTTON, WXMainFrame::OnPowerButton) + EVT_UPDATE_UI(ID_POWER_BUTTON, WXMainFrame::OnPowerButtonUI) EVT_MENU(ID_RESET_BUTTON, WXMainFrame::OnResetButton) + EVT_MENU(ID_NMI_BUTTON, WXMainFrame::OnNMIButton) + EVT_MENU(ID_KEYBOARD_CONNECT, WXMainFrame::OnKeyboardConnect) + EVT_UPDATE_UI(ID_KEYBOARD_CONNECT, WXMainFrame::OnKeyboardConnectUI) EVT_MENU_RANGE(0, ID_SUBWIN_END, WXMainFrame::OnWindow) EVT_UPDATE_UI_RANGE(0, ID_SUBWIN_END, WXMainFrame::OnWindowUI) EVT_MENU(wxID_ABOUT, WXMainFrame::OnAbout) - - EVT_COMMAND(wxID_ANY, WXW_EVT_HALT_NOTIFY, WXMainFrame::OnHaltNotify) wxEND_EVENT_TABLE() WXMainFrame::WXMainFrame(wxWindow *parent) : inherited(parent, wxID_ANY, "nono", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE & ~wxMAXIMIZE_BOX & ~wxRESIZE_BORDER) { - vmtype_t vmtype; + VMType vmtype; vmtype = gMainApp.GetVMType(); - // とりあえずメニュー - // XXX すぐにメニューファクトリーになる + // メニュー wxMenuBar *menubar = new wxMenuBar(); + // メニュー > ファイル wxMenu *menuFile = new wxMenu(); - // XXX とりあえず適当なところにおいとく - menuFile->Append(ID_FULL_SPEED, _("Full &Speed Mode"), "", wxITEM_CHECK); - menuFile->AppendSeparator(); menuFile->Append(ID_RESTART_VM, _("&Restart VM"), ""); menuFile->Append(wxID_EXIT, _("E&xit")); menubar->Append(menuFile, _("&File")); + // メニュー > 設定 + wxMenu *menuSet = new wxMenu(); + menuSet->Append(ID_FULL_SPEED, _("Full &Speed Mode"), "", wxITEM_CHECK); + wxMenu *menuSetInput = new wxMenu(); + menuSetInput->AppendRadioItem(ID_INPUT_CHAR, _("By &Character")); + menuSetInput->AppendRadioItem(ID_INPUT_JP, _("&JP Keyboard")); + menuSet->AppendSubMenu(menuSetInput, _("&Key Input")); + menubar->Append(menuSet, _("&Setting")); + + // メニュー > 表示 wxMenu *menuDisplay = new wxMenu(); wxMenu *menuViewScale = new wxMenu(); menuViewScale->Append(ID_SCALE_1_00, _("x1.&0"), "", wxITEM_CHECK); @@ -95,34 +264,53 @@ WXMainFrame::WXMainFrame(wxWindow *paren wxITEM_CHECK); menubar->Append(menuDisplay, _("&View")); + // メニュー > デバイス wxMenu *menuDevice = new wxMenu(); + CreateRemovableMenu(menuDevice); menuDevice->Append(ID_SUBWIN_SOFTKEY, _("Software &Keyboard"), "", wxITEM_CHECK); menuDevice->Append(ID_MOUSEMODE, _("&Mouse Mode\tCTRL+F12"), "", wxITEM_CHECK); - if (gMainApp.HasVMFeature(VMF_LUNA)) { + if (gMainApp.Has(VMCap::LUNA)) { menuDevice->Append(ID_SUBWIN_LCDPANEL, _("Front &LCD"), "", wxITEM_CHECK); } menuDevice->AppendSeparator(); wxMenu *menuOper = new wxMenu(); + wxItemKind power_button_kind; + if (gMainApp.Has(VMCap::LUNA)) { + power_button_kind = wxITEM_NORMAL; + } else { + power_button_kind = wxITEM_CHECK; + } + menuOper->Append(ID_POWER_BUTTON, _("Push &Power button"), + "", power_button_kind); menuOper->Append(ID_RESET_BUTTON, _("Push &Reset button")); + if (gMainApp.Has(VMCap::LUNA)) { + menuOper->Append(ID_NMI_BUTTON, _("Push &ABORT button")); + } else { + menuOper->Append(ID_NMI_BUTTON, _("Push &Interrupt button")); + } + menuOper->AppendSeparator(); + menuOper->Append(ID_KEYBOARD_CONNECT, _("Connect &Keyboard"), + "", wxITEM_CHECK); menuDevice->AppendSubMenu(menuOper, _("Operation")); menubar->Append(menuDevice, _("&Device")); // 機種によってモニタウィンドウを追加する #define AppendIf(menu, id, label) do { \ - vmf_t feature_ = gMonitorManager.GetFeature(id); \ - if (gMainApp.HasVMFeature(feature_)) { \ + VMCap vmcap_ = gMonitorManager->GetVMCap(id); \ + if (gMainApp.Has(vmcap_)) { \ (menu)->Append((id), (label), "", wxITEM_CHECK); \ } \ } while (0) -#define AppendIfSeparator(menu, feature_) do { \ - if (gMainApp.HasVMFeature(feature_)) { \ +#define AppendIfSeparator(menu, vmcap_) do { \ + if (gMainApp.Has(vmcap_)) { \ (menu)->AppendSeparator(); \ } \ } while (0) + // メニュー > モニター wxMenu *menuMonitor = new wxMenu(); AppendIf(menuMonitor, ID_MONITOR_SCHEDULER, _("&Scheduler")); AppendIf(menuMonitor, ID_SUBWIN_LOG, _("&Log")); @@ -131,16 +319,19 @@ WXMainFrame::WXMainFrame(wxWindow *paren AppendIf(menuDebug, ID_MONITOR_BREAKPOINT, _("&Breakpoint")); AppendIf(menuDebug, ID_SUBWIN_BRHIST, _("Branch &History")); AppendIf(menuDebug, ID_SUBWIN_EXHIST, _("&Exception History")); + AppendIf(menuDebug, ID_SUBWIN_VECTOR, _("&Vector Table")); + menuDebug->AppendSeparator(); + AppendIf(menuDebug, ID_SUBWIN_LOGSETTING, _("&Log Level Setting")); menuMonitor->AppendSubMenu(menuDebug, _("&Debug")); wxMenu *menuMPU = new wxMenu(); AppendIf(menuMPU, ID_MONITOR_MPUREG, _("&Register")); AppendIf(menuMPU, ID_MONITOR_MPUATC, "&ATC"); - AppendIfSeparator(menuMPU, VMF_M88K); - AppendIf(menuMPU, ID_MONITOR_CMMU7, "CMMU#7" + _("(Inst)")); + AppendIfSeparator(menuMPU, VMCap::M88K); + AppendIf(menuMPU, ID_MONITOR_CMMU7, "CMMU#7" + _("(Inst)")); AppendIf(menuMPU, ID_MONITOR_ATC7, "ATC#7"); AppendIf(menuMPU, ID_SUBWIN_CACHE7, _("Cache") + "#7"); - AppendIfSeparator(menuMPU, VMF_M88K); + AppendIfSeparator(menuMPU, VMCap::M88K); AppendIf(menuMPU, ID_MONITOR_CMMU6, "CMMU#6" + _("(Data)")); AppendIf(menuMPU, ID_MONITOR_ATC6, "ATC#6"); AppendIf(menuMPU, ID_SUBWIN_CACHE6, _("Cache") + "#6"); @@ -157,10 +348,12 @@ WXMainFrame::WXMainFrame(wxWindow *paren menuMonitor->AppendSubMenu(menuMemdump, _("&Memory Dump")); AppendIf(menuMonitor, ID_MONITOR_INTERRUPT, _("&Interrupt")); AppendIf(menuMonitor, ID_MONITOR_SPC, "S&PC"); + AppendIf(menuMonitor, ID_MONITOR_SCSIDEVS, _("SCSI Devices")); AppendIf(menuMonitor, ID_MONITOR_RTC, "&RTC"); AppendIf(menuMonitor, ID_MONITOR_MK48T02, "&RTC"); AppendIf(menuMonitor, ID_MONITOR_DMAC, "DM&AC"); AppendIf(menuMonitor, ID_MONITOR_MFP, "M&FP"); + AppendIf(menuMonitor, ID_MONITOR_FDC, "F&DC"); AppendIf(menuMonitor, ID_MONITOR_SCC, "SCC(&Z)"); AppendIf(menuMonitor, ID_MONITOR_SIO, "SIO(&U)"); AppendIf(menuMonitor, ID_MONITOR_PIO0, "PIO0"); @@ -168,16 +361,22 @@ WXMainFrame::WXMainFrame(wxWindow *paren AppendIf(menuMonitor, ID_MONITOR_LANCE, "Lance(&E)"); AppendIf(menuMonitor, ID_MONITOR_LCD, "LCD(&J)"); AppendIf(menuMonitor, ID_MONITOR_SYSCLK, _("S&ystem Clock")); - AppendIf(menuMonitor, ID_MONITOR_LUNAFB, _("&Bitmap Control")); - AppendIf(menuMonitor, ID_SUBWIN_LUNAFB0, _("Bitmap #&0")); + AppendIf(menuMonitor, ID_MONITOR_CRTC, "CRTC"); + AppendIf(menuMonitor, ID_MONITOR_LUNAVC, _("&Video Control")); + AppendIf(menuMonitor, ID_MONITOR_RENDERER, _("Renderer")); + AppendIf(menuMonitor, ID_SUBWIN_LUNAFB, _("&Bitmap Plane")); + AppendIf(menuMonitor, ID_SUBWIN_TVRAM, _("&Text VRAM")); + AppendIf(menuMonitor, ID_SUBWIN_ROM, "ROM"); wxMenu *menuHost = new wxMenu(); AppendIf(menuHost, ID_MONITOR_WXHOSTINFO, _("Host &Information")); AppendIf(menuHost, ID_MONITOR_HOSTNET, _("Host &Network")); + AppendIf(menuHost, ID_MONITOR_HOSTCOM, _("Host &Serial Port")); menuMonitor->AppendSubMenu(menuHost, _("&Host")); menubar->Append(menuMonitor, _("&Monitor")); + // メニュー > ヘルプ #if defined(__WXOSX__) // OSX では wxID_ABOUT はリンゴメニューに入れるしきたりになっている。 // Append() すると Quit より下に来てしまうので、Prepend() で上に追加。 @@ -194,13 +393,13 @@ WXMainFrame::WXMainFrame(wxWindow *paren wxString title("nono"); switch (vmtype) { - case VMTYPE_X68030: + case VMType::X68030: title += "(X68030)"; break; - case VMTYPE_LUNA1: + case VMType::LUNA1: title += "(LUNA-I)"; break; - case VMTYPE_LUNA88K: + case VMType::LUNA88K: title += "(LUNA88K)"; break; default: @@ -218,14 +417,10 @@ WXMainFrame::WXMainFrame(wxWindow *paren // 念のため事象が分かりやすいように 10秒くらいにしておく。 wxUpdateUIEvent::SetUpdateInterval(10000); - wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); - // WXStatusPanel コンストラクタが gMainView を参照するので // gMainView のほうを先に作成すること。 gMainView = new WXMainView(this); gStatusPanel = new WXStatusPanel(this); - vbox->Add(gStatusPanel, 0, wxEXPAND); - vbox->Add(gMainView, 1, wxEXPAND | wxFIXED_MINSIZE); // SHOW イベントを最初にも一度起こしたいため // ステータスパネルを一旦非表示にする (これで SHOW(false) イベントが飛ぶ) @@ -236,9 +431,8 @@ WXMainFrame::WXMainFrame(wxWindow *paren gStatusPanel->Show(); } - SetSizer(vbox); - - Fit(); + // Sizer 使わず自前でレイアウトする。 + SetAutoLayout(true); // モニタの更新頻度は初期値だけ設定から読んで、あとはここで管理する。 // 不正値ならデフォルトの 15Hz にしておくか @@ -255,32 +449,186 @@ WXMainFrame::WXMainFrame(wxWindow *paren Connect(wxEVT_ACTIVATE, wxActivateEventHandler(WXMainView::OnActivate), NULL, gMainView); - // ウィンドウ表示後にサブウィンドウを表示するための Paint イベント。 - Connect(wxEVT_PAINT, wxPaintEventHandler(WXMainFrame::OnPaint)); + // ウィンドウ表示後に続きの処理をするための単発アイドルイベント。 + Connect(wxEVT_IDLE, wxIdleEventHandler(WXMainFrame::OnIdle)); + + // VM からの通知を接続 + WXUIMessage::Connect(UIMessage::APPEXIT, this, + wxCommandEventHandler(WXMainFrame::OnExit)); + WXUIMessage::Connect(UIMessage::SCSI_MEDIA_FAILED, this, + wxCommandEventHandler(WXMainFrame::OnInsertFailed)); + WXUIMessage::Connect(UIMessage::FDD_MEDIA_FAILED, this, + wxCommandEventHandler(WXMainFrame::OnInsertFailed)); + if (gMPU680x0) { + WXUIMessage::Connect(UIMessage::HALT, this, + wxCommandEventHandler(WXMainFrame::OnHaltNotify)); + } } WXMainFrame::~WXMainFrame() { + WXUIMessage::Disconnect(UIMessage::APPEXIT, + wxCommandEventHandler(WXMainFrame::OnExit)); + WXUIMessage::Disconnect(UIMessage::SCSI_MEDIA_FAILED, + wxCommandEventHandler(WXMainFrame::OnInsertFailed)); + WXUIMessage::Disconnect(UIMessage::FDD_MEDIA_FAILED, + wxCommandEventHandler(WXMainFrame::OnInsertFailed)); + if (gMPU680x0) { + WXUIMessage::Disconnect(UIMessage::HALT, + wxCommandEventHandler(WXMainFrame::OnHaltNotify)); + } } -// ウィンドウ作成イベント (を実現するためここでは再描画イベントを使う) +// リムーバブルデバイス用のメニューを作成して menuDevice に追加する +void +WXMainFrame::CreateRemovableMenu(wxMenu *menuDevice) +{ + wxMenu *menu; + bool added = false; + + // FD + if (gFDC) { + const std::vector& fdd_list = gFDC->GetFDD(); + for (const auto fdd : fdd_list) { + int unit = fdd->GetUnitNo(); + fdd_array[unit] = fdd; + + menu = CreateFDMenu(unit); + std::string label = string_format("FD%d", unit); + menuDevice->AppendSubMenu(menu, label); + added = true; + } + } + + // CD, MO + for (int id = 0; id < 8; id++) { + scsidev[id] = gSPC->GetTarget(id); + if (scsidev[id]) { + menu = CreateSCSIRemovableMenu(id); + if (menu) { + std::string label = string_format("%s (ID%d)", + SCSI::GetDevTypeName(scsidev[id]->GetDevType()), id); + menuDevice->AppendSubMenu(menu, label); + added = true; + } + } + } + + // 1つでも追加したらセパレータ + if (added) { + menuDevice->AppendSeparator(); + } +} + +// FD 用のメニューを作成して返す。 +wxMenu * +WXMainFrame::CreateFDMenu(int unit) const +{ + wxMenu *menu = new wxMenu(); + // Insert の実際のラベルは UpdateUI でセットする。 + // ただし何か入れておかないといけないので適当にセット。 + menu->Append(ID_FD_INSERT(unit), "Insert", ""); + // XXX 未実装 + //menu->Append(ID_FD_MISINSERT(unit), _("Insert incorrectly"), ""); + menu->Append(ID_FD_EJECT(unit), _("&Eject"), ""); + menu->Append(ID_FD_FORCE(unit), _("&Force Eject"), ""); + return menu; +} + +// SCSI リムーバブルデバイス用のメニューを作成して返す。 +// id はデバイスが接続されていること (リムーバブルデバイスであるかは問わない)。 +// リムーバブルデバイスでなければ NULL を返す。 +// メニューバーとポップアップメニュー共通。 +wxMenu * +WXMainFrame::CreateSCSIRemovableMenu(uint id) const +{ + const auto *dev = scsidev[id]; + assert(dev); + + switch (dev->GetDevType()) { + case SCSI::DevType::CD: + case SCSI::DevType::MO: + { + wxMenu *menu = new wxMenu(); + // Insert の実際のラベルは UpdateUI でセットする。 + // ただし何か入れておかないといけないので適当にセット。 + menu->Append(ID_CD_INSERT(id), "Insert", ""); + menu->Append(ID_CD_EJECT(id), _("&Eject"), ""); + menu->Append(ID_CD_FORCE(id), _("&Force Eject"), ""); + return menu; + } + default: + return NULL; + } +} + +// (設定反映などの) 初期化。 +// 成功すれば true、失敗すれば false を返す。 +bool +WXMainFrame::Init() +{ + if (!gMainView->Init()) { + return false; + } + + return true; +} + +bool +WXMainFrame::Layout() +{ + if (inherited::Layout() == false) { + return false; + } + + wxSize msize = gStatusPanel->GetMinClientSize(); + wxSize ssize = gStatusPanel->GetClientSize(); + wxSize vsize = gMainView->GetClientSize(); + + if (gStatusPanel->IsShown()) { + ssize.y = msize.y; + } else { + ssize.y = 0; + } + + wxSize size; + size.x = std::max(msize.x, vsize.x); + size.y = ssize.y + vsize.y; + + if (ssize.y != 0) { + gStatusPanel->SetSize(0, 0, size.x, ssize.y); + } + gMainView->SetSize(0, ssize.y, size.x, vsize.y); + SetClientSize(size); + + return true; +} + +// ウィンドウ作成イベント (を実現するためここではアイドルイベントを使う) // -// 初期化(っぽい)もののうちコンストラクタでは早すぎる処理のため。 -// 似たようなイベントは WindowCreate イベントと自前の Create イベント -// (今はもうない)があり、Create -> WindowCreate -> Paint の順で発生する -// ようだ (Ubuntu 20.04 で確認、他でも同様だと思いたい)。 -// Create 時点では早すぎて、ここでサブウィンドウを作成するとサイズが 0 みたい -// なウィンドウが一瞬出来てしまい GTK のワーニングが出る (その後サイズを矯正 -// すれば表示自体は出来る)。 +// 初期化(っぽい)もののうちコンストラクタでは早すぎるものは、コンストラクタ +// 直後に起こすこのイベント内で処理する。使えそうなイベントはいくつかあり、 +// 自前の Create (今はもうない) -> WindowCreate -> Idle -> Paint の順で +// 起きるようだ。 +// 自前の Create 時点では早すぎて、ここでサブウィンドウを作成するとサイズが 0 +// みたいなウィンドウが一瞬出来てしまい GTK のワーニングが出る (その後サイズを +// 矯正すれば表示自体は出来る)。 // WindowCreate 時点ではその問題は起きないが、ここはまだメインウィンドウ表示 // 前なので、ここでサブウィンドウを表示した後メインウィンドウを表示すると // (ウィンドウマネージャにもよるかもだが)、メインウィンドウがサブウィンドウを // 隠してしまう。 -// Paint イベント時点まで来るとこの2つの問題が回避できるようだ。 -// なおこの Paint イベントは通常の再描画の用途では使わないため、一度処理した -// 後はこのイベントを外す。 -void WXMainFrame::OnPaint(wxPaintEvent& ev) -{ +// Idle イベント時点まで来ると上の2つの問題は回避できるようだ。 +// Paint イベントの時点でもタイミング的には問題ないが、環境によっては(?) +// このイベント自体が飛んで来ないことがあるようだ。WXMainFrame の再描画は +// 子コントロールが行っており、そのため WXMainFrame 自身が再描画しなければ +// ならない領域は存在しないからかも知れないが詳細未調査。 +void +WXMainFrame::OnIdle(wxIdleEvent& ev) +{ + // UIMessage の処理を開始する。 + // 少なくとも WXMainFrame のコンストラクタが完了した後のほうがいいはず。 + UIMessage::Attach(&WXUIMessage::Process); + // ウィンドウを表示 for (int i = 0; i < subwindow_start.size(); i++) { if (subwindow_start[i]) { @@ -289,10 +637,10 @@ void WXMainFrame::OnPaint(wxPaintEvent& } // 電源投入 - gVM->PowerButton(); + gPower->PowerButton(); // 一度処理したら、このイベントを外す - Disconnect(wxEVT_PAINT, wxPaintEventHandler(WXMainFrame::OnPaint)); + Disconnect(wxEVT_IDLE, wxIdleEventHandler(WXMainFrame::OnIdle)); } // ウィンドウクローズイベント @@ -301,6 +649,8 @@ WXMainFrame::OnClose(wxCloseEvent& event { // ステータスパネルの更新を停止 gStatusPanel->Close(); + // メインビューの描画を停止 + gMainView->Close(); // サブウィンドウを全部閉じる for (int i = 0; i < ID_SUBWIN_MAX; i++) { @@ -311,22 +661,36 @@ WXMainFrame::OnClose(wxCloseEvent& event // VM を解放 gVM->Dispose(); - gVM.reset(); // 後はデフォルト処理 event.Skip(); } +// メニューの「Restart VM」イベント +void +WXMainFrame::OnRestartVM(wxCommandEvent& event) +{ + gPower->MakeRestart(); +} + +// メニューの「終了」イベント +void +WXMainFrame::OnExit(wxCommandEvent& event) +{ + // ウィンドウを閉じる + Close(); +} + // メニューの「高速モード」イベント void WXMainFrame::OnFullSpeed(wxCommandEvent& event) { if (event.IsChecked()) { // チェックが入ったので、高速モードにする - gScheduler->SetFullSpeed(true); + gSync->RequestFullSpeed(true); } else { // チェックが消えたので、通常モードにする - gScheduler->SetFullSpeed(false); + gSync->RequestFullSpeed(false); } } @@ -334,23 +698,30 @@ WXMainFrame::OnFullSpeed(wxCommandEvent& void WXMainFrame::OnFullSpeedUI(wxUpdateUIEvent& event) { - bool is_fullspeed = gScheduler->GetFullSpeed(); + bool is_fullspeed = gSync->GetFullSpeed(); event.Check(is_fullspeed); } -// メニューの「Restart VM」イベント +// メニューの「設定 > 入力 > *」の共通イベント void -WXMainFrame::OnRestartVM(wxCommandEvent& event) +WXMainFrame::OnMenuInput(wxCommandEvent& event) { - gScheduler->RequestRestartVM(); + bool char_mode = (event.GetId() == ID_INPUT_CHAR); + + gMainView->SetCharInputMode(char_mode); } -// メニューの「終了」イベント +// メニューの「設定 > 入力 > *」の共通 UI void -WXMainFrame::OnExit(wxCommandEvent& event) +WXMainFrame::OnMenuInputUI(wxUpdateUIEvent& event) { - // ウィンドウを閉じる - Close(); + bool char_mode = gMainView->GetCharInputMode(); + + if (char_mode) { + event.Check(event.GetId() == ID_INPUT_CHAR); + } else { + event.Check(event.GetId() == ID_INPUT_JP); + } } // メニューの「表示 > スケール > *」の共通イベント @@ -374,7 +745,7 @@ WXMainFrame::OnMenuScale(wxCommandEvent& gMainView->DoResize(scale); // 再レイアウト - Fit(); + Layout(); } // メニューの「表示 > スケール > *」の共通 UI @@ -413,23 +784,22 @@ WXMainFrame::OnMenuFontSize(wxCommandEve break; } - if (fontid != WXTextPanel::global_fontid) { - WXTextPanel::global_fontid = fontid; + if (fontid != gFontManager->GetFontId()) { + gFontManager->SetFont(fontid); - // アクティブなサブウィンドウ全員に通知 + // アクティブなサブウィンドウ全員にフォント変更通知 for (int i = 0; i < ID_SUBWIN_MAX; i++) { if ((bool)windows[i]) { WXSubWindow *subwin = dynamic_cast(windows[i].get()); - subwin->SetFontSize(fontid); + subwin->FontChanged(); } } - - // ステータスパネルに通知 - gStatusPanel->SetFontSize(fontid); + // メインウィンドウはここで直接ステータスパネルに通知 + gStatusPanel->FontChanged(); // ステータスパネルの大きさが変わるので再レイアウト - Fit(); + Layout(); } } @@ -437,7 +807,7 @@ WXMainFrame::OnMenuFontSize(wxCommandEve void WXMainFrame::OnMenuFontSizeUI(wxUpdateUIEvent& event) { - FontId current_size = WXTextPanel::global_fontid; + FontId current_size = gFontManager->GetFontId(); FontId event_size = (FontId)(event.GetId() - (ID_FONTSIZE_START+1)); event.Check(current_size == event_size); @@ -464,7 +834,7 @@ WXMainFrame::OnMenuMonRate(wxCommandEven if ((bool)windows[i]) { auto *subwin = dynamic_cast(windows[i].get()); for (wxWindow *child : subwin->GetChildren()) { - auto *panel = dynamic_cast(child); + auto *panel = dynamic_cast(child); if (panel) { panel->SetRate(monitor_rate); } @@ -494,7 +864,7 @@ WXMainFrame::OnShowStatusPanel(wxCommand gStatusPanel->Show(enable); // 再レイアウト - Fit(); + Layout(); } // メニューの「表示 > ステータスパネルを表示」UI @@ -504,6 +874,188 @@ WXMainFrame::OnShowStatusPanelUI(wxUpdat event.Check(gStatusPanel->IsShown()); } +// メニューの「デバイス > FD > 挿入」 +void +WXMainFrame::OnFDInsert(wxCommandEvent& event) +{ + int unit = event.GetId() - ID_FD_INSERT_0; + const auto *fdd = fdd_array[unit]; + assert(fdd); + + std::string title(_("Open a FD image")); + std::string wildcard("FD images (*.xdf;*.img;*.fs)" + "|*.xdf;*.XDF;*.img;*.IMG;*.fs;*.FS" + "|All files (*.*)|*.*"); + + // ディレクトリは、まだ一度も使ってなければ VM ディレクトリにする + if (current_dir.empty()) { + current_dir = gMainApp.GetVMDir(); + } + + wxFileDialog dialog(this, title, current_dir, "", wildcard, + wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dialog.ShowModal() == wxID_CANCEL) { + return; + } + auto path = std::string(dialog.GetPath()); + InsertFD(unit, path); +} + +// メニューの「デバイス > FD > 挿入」UI +void +WXMainFrame::OnFDInsertUI(wxUpdateUIEvent& event) +{ + int unit = event.GetId() - ID_FD_INSERT_0; + const auto *fdd = fdd_array[unit]; + assert(fdd); + + if (fdd->IsMediumLoaded()) { + event.SetText(_("Ex&change")); + } else { + event.SetText(_("&Insert")); + } +} + +// メニューの「デバイス > FD > 取り出し」 +void +WXMainFrame::OnFDEject(wxCommandEvent& event) +{ + int unit = event.GetId() - ID_FD_EJECT_0; + + EjectFD(unit, false); +} + +// メニューの「デバイス > FD > 取り出し」UI +void +WXMainFrame::OnFDEjectUI(wxUpdateUIEvent& event) +{ + int unit = event.GetId() - ID_FD_EJECT_0; + FDDDevice *fdd = fdd_array[unit]; + assert(fdd); + + // 挿入されてて取り出し可能な時のみ有効 + event.Enable(fdd->IsMediumLoaded() && fdd->IsEjectEnable()); +} + +// メニューの「デバイス > FD > 強制取り出し」 +void +WXMainFrame::OnFDForce(wxCommandEvent& event) +{ + int unit = event.GetId() - ID_FD_FORCE_0; + + EjectFD(unit, true); +} + +// メニューの「デバイス > FD > 強制取り出し」UI +void +WXMainFrame::OnFDForceUI(wxUpdateUIEvent& event) +{ + int unit = event.GetId() - ID_FD_FORCE_0; + FDDDevice *fdd = fdd_array[unit]; + assert(fdd); + + // 挿入されてて取り出し禁止の時のみ有効 + event.Enable(fdd->IsMediumLoaded() && fdd->IsEjectEnable() == false); +} + +// メニューの「デバイス > CD/MO > 挿入」 +void +WXMainFrame::OnCDInsert(wxCommandEvent& event) +{ + std::string title; + std::string wildcard; + + int id = event.GetId() - ID_CD_INSERT_0; + const auto *dev = scsidev[id]; + assert(dev); + + switch (dev->GetDevType()) { + case SCSI::DevType::CD: + title = _("Open a CD image"); + wildcard = "ISO images (*.iso;*.img)|*.iso;*.img;*.ISO;*.IMG"; + break; + case SCSI::DevType::MO: + title = _("Open an MO image"); + wildcard = "MO images (*.img;*.mos)|*.img;*.mos;*.IMG;*.MOS"; + break; + default: + __unreachable(); + } + wildcard += "|All files (*.*)|*.*"; + + // ディレクトリは、まだ一度も使ってなければ VM ディレクトリにする + if (current_dir.empty()) { + current_dir = gMainApp.GetVMDir(); + } + + wxFileDialog dialog(this, title, current_dir, "", wildcard, + wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dialog.ShowModal() == wxID_CANCEL) { + return; + } + auto path = std::string(dialog.GetPath()); + InsertCD(id, path); +} + +// メニューの「デバイス > CD/MO > 挿入」UI +void +WXMainFrame::OnCDInsertUI(wxUpdateUIEvent& event) +{ + int id = event.GetId() - ID_CD_INSERT_0; + SCSIDisk *disk = dynamic_cast(scsidev[id]); + assert(disk); + + if (disk->IsMediumLoaded()) { + event.SetText(_("Ex&change")); + } else { + event.SetText(_("&Insert")); + } +} + +// メニューの「デバイス > CD/MO > 取り出し」 +void +WXMainFrame::OnCDEject(wxCommandEvent& event) +{ + int id = event.GetId() - ID_CD_EJECT_0; + + EjectCD(id, false); +} + +// メニューの「デバイス > CD/MO > 取り出し」UI +void +WXMainFrame::OnCDEjectUI(wxUpdateUIEvent& event) +{ + int id = event.GetId() - ID_CD_EJECT_0; + SCSIDisk *disk = dynamic_cast(scsidev[id]); + assert(disk); + + // 挿入されてて取り出し可能な時のみ有効 + event.Enable(disk->IsMediumLoaded() && + disk->IsMediumRemovalAllowed()); +} + +// メニューの「デバイス > CD/MO > 強制取り出し」 +void +WXMainFrame::OnCDForce(wxCommandEvent& event) +{ + int id = event.GetId() - ID_CD_FORCE_0; + + EjectCD(id, true); +} + +// メニューの「デバイス > CD/MO > 強制取り出し」UI +void +WXMainFrame::OnCDForceUI(wxUpdateUIEvent& event) +{ + int id = event.GetId() - ID_CD_FORCE_0; + SCSIDisk *disk = dynamic_cast(scsidev[id]); + assert(disk); + + // 挿入されてて取り出し禁止の時のみ有効 + event.Enable(disk->IsMediumLoaded() && + disk->IsMediumRemovalPrevented()); +} + // メニューの「デバイス > マウスモード」 void WXMainFrame::OnMouseMode(wxCommandEvent& event) @@ -528,12 +1080,62 @@ WXMainFrame::OnMouseModeUI(wxUpdateUIEve event.Check(gMainView->GetMouseMode()); } +// メニューの「デバイス > Push Power Button」イベント +void +WXMainFrame::OnPowerButton(wxCommandEvent& event) +{ + // LUNA はモーメンタリ動作、X68030 はオルタネート動作のスイッチ。 + // この違いは VM 側で吸収するのでここはボタンを押したという動作だけ + // 通知する。 + gPower->PowerButton(); +} + +// メニューの「デバイス > Push Power Button」UI +void +WXMainFrame::OnPowerButtonUI(wxUpdateUIEvent& event) +{ + if (gMainApp.Has(VMCap::LUNA)) { + // LUNA の電源ボタンはモーメンタリ動作で、電源オフ->オンの機能しかなく + // さらに今の所電源オフ状態はないので、実質無効。 + event.Enable(false); + } else { + // X68030 の電源ボタンはオルタネート動作なので、 + // チェックで状態を表す。 + event.Check(gPower->IsPowerPressed()); + } +} + // メニューの「デバイス > Push Reset Button」イベント // こっちは本体リセットボタンによるリセット void WXMainFrame::OnResetButton(wxCommandEvent& event) { - gScheduler->RequestResetHard(); + gPower->MakeResetHard(); +} + +// メニューの「デバイス > Push ABORT(Interrupt) Button」イベント +void +WXMainFrame::OnNMIButton(wxCommandEvent& event) +{ + gNMI->PressNMI(); +} + +// メニューの「デバイス > Operation > Keyboard Connect」イベント +void +WXMainFrame::OnKeyboardConnect(wxCommandEvent& event) +{ + // enable ならチェックを付けようとして呼ばれた + bool enable = event.IsChecked(); + + // キーボード接続を設定 + gKeyboard->Connect(enable); +} + +// メニューの「デバイス > Operation > Keyboard Connect」UI +void +WXMainFrame::OnKeyboardConnectUI(wxUpdateUIEvent& event) +{ + event.Check(gKeyboard->IsConnected()); } // メニューのウィンドウ ID 共通イベント @@ -556,7 +1158,7 @@ WXMainFrame::DoWindow(int id, bool enabl // (WXSubWindow::OnClose() 参照)。 #define SetMonitor(id, name) \ windows[id].reset(new WXMonitorWindow(this, (name), \ - gMonitorManager.Get(id))) + gMonitorManager->Get(id))) if (enable) { // チェックが入ったのでウィンドウを作成 @@ -605,10 +1207,26 @@ WXMainFrame::DoWindow(int id, bool enabl break; } + case ID_MONITOR_CRTC: + SetMonitor(id, "CRTC"); + break; + case ID_MONITOR_DMAC: SetMonitor(id, "DMAC"); break; + case ID_MONITOR_FDC: + SetMonitor(id, "FDC"); + break; + + case ID_MONITOR_HOSTCOM: + SetMonitor(id, _("Host Serial Port")); + break; + + case ID_MONITOR_HOSTNET: + SetMonitor(id, _("Host Network")); + break; + case ID_MONITOR_INTERRUPT: SetMonitor(id, _("Interrupt")); break; @@ -625,8 +1243,8 @@ WXMainFrame::DoWindow(int id, bool enabl windows[id].reset(new WXLCDMonitor(this)); break; - case ID_MONITOR_LUNAFB: - SetMonitor(id, _("Bitmap Control")); + case ID_MONITOR_LUNAVC: + SetMonitor(id, _("Video Control")); break; case ID_MONITOR_MEMDUMP0 ... @@ -653,14 +1271,14 @@ WXMainFrame::DoWindow(int id, bool enabl SetMonitor(id, _("MPU (Register)")); break; - case ID_MONITOR_HOSTNET: - SetMonitor(id, _("Host Network")); - break; - case ID_MONITOR_PIO0: SetMonitor(id, "PIO"); break; + case ID_MONITOR_RENDERER: + SetMonitor(id, _("Renderer")); + break; + case ID_MONITOR_RTC: SetMonitor(id, "RTC"); break; @@ -673,6 +1291,10 @@ WXMainFrame::DoWindow(int id, bool enabl SetMonitor(id, _("Scheduler")); break; + case ID_MONITOR_SCSIDEVS: + SetMonitor(id, _("SCSI Devices")); + break; + case ID_MONITOR_SIO: SetMonitor(id, "SIO"); break; @@ -690,13 +1312,13 @@ WXMainFrame::DoWindow(int id, bool enabl break; case ID_SUBWIN_BRHIST: - windows[id].reset(new WXBranchHistoryMonitor(this, - gMonitorManager.Get(id))); + windows[id].reset(new WXScrollMonitorWindow(this, + _("Branch History"), gMonitorManager->Get(id))); break; case ID_SUBWIN_EXHIST: - windows[id].reset(new WXExceptionHistoryMonitor(this, - gMonitorManager.Get(id))); + windows[id].reset(new WXScrollMonitorWindow(this, + _("Exception History"), gMonitorManager->Get(id))); break; case ID_SUBWIN_CACHE0: @@ -717,7 +1339,7 @@ WXMainFrame::DoWindow(int id, bool enabl name += _("(Inst)"); } windows[id].reset(new WXCacheWindow(this, name, - gMonitorManager.Get(id))); + gMonitorManager->Get(id))); break; } @@ -727,14 +1349,37 @@ WXMainFrame::DoWindow(int id, bool enabl case ID_SUBWIN_LOG: windows[id].reset(new WXLogMonitor(this)); break; - case ID_SUBWIN_LUNAFB0: - windows[id].reset(new WXLunaBitmapWindow(this, _("Bitmap #0"))); + case ID_SUBWIN_LOGSETTING: + windows[id].reset(new WXLogSettingWindow(this)); + break; + + case ID_SUBWIN_LUNAFB: + case ID_SUBWIN_TVRAM: + { + // 呼び方が違うので名前と ID が違う + wxString name; + if (id == ID_SUBWIN_LUNAFB) { + name = _("Bitmap Plane"); + } else { + name = _("Text VRAM"); + } + windows[id].reset(new WXPlaneVRAMWindow(this, name)); + break; + } + + case ID_SUBWIN_ROM: + windows[id].reset(new WXROMWindow(this)); break; case ID_SUBWIN_SOFTKEY: windows[id].reset(new WXSoftKeyWindow(this, gMainApp.GetVMType())); break; + case ID_SUBWIN_VECTOR: + windows[id].reset(new WXScrollMonitorWindow(this, + _("Vector Table"), gMonitorManager->Get(id))); + break; + default: assert(false); } @@ -771,54 +1416,80 @@ WXMainFrame::OnAbout(wxCommandEvent& eve dlg.ShowModal(); } -// ウィンドウをリストから削除する -// (ウィンドウの Close() から呼ばれる) +// FD を挿入する。 void -WXMainFrame::DeleteWindow(wxWindow *win) +WXMainFrame::InsertFD(int unit, const std::string& path) { - for (int id = 0; id < ID_SUBWIN_MAX; id++) { - if (windows[id].get() == win) { - windows[id].reset(); - break; - } - } + FDDDevice *fdd = fdd_array[unit]; + assert(fdd); + + // VM に依頼 + // (エラーが起きれば別途 UI メッセージが飛んでくる) + fdd->LoadDiskUI(path); + + // 次回以降のダイアログのカレントディレクトリをここに更新 + auto pos = path.rfind('/'); + current_dir = path.substr(0, pos); +} + +// FD をイジェクトする。 +void +WXMainFrame::EjectFD(int unit, bool force) +{ + FDDDevice *fdd = fdd_array[unit]; + assert(fdd); + + fdd->UnloadDiskUI(force); } -// VM 電源オフ時に呼ばれるコールバック。 -// (VM スレッドからアプリケーションを終了するため) +// CD/MO を挿入する。 void -wxmainframe_poweroff_callback() +WXMainFrame::InsertCD(int id, const std::string& path) { - gMainFrame->PowerOffCallback(); + SCSIDisk *scsidisk = dynamic_cast(scsidev[id]); + assert(scsidisk); + + // VM に依頼 + // (エラーが起きれば別途 UI メッセージが飛んでくる) + scsidisk->LoadDiskUI(path); + + // 次回以降のダイアログのカレントディレクトリをここに更新 + auto pos = path.rfind('/'); + current_dir = path.substr(0, pos); } -// VM からの電源オフ処理 +// CD/MO をイジェクトする。 void -WXMainFrame::PowerOffCallback() +WXMainFrame::EjectCD(int id, bool force) { - wxCommandEvent *ev = new wxCommandEvent(wxEVT_MENU, wxID_EXIT); - QueueEvent(ev); + SCSIDisk *scsidisk = dynamic_cast(scsidev[id]); + assert(scsidisk); + + scsidisk->UnloadDiskUI(force); } -// m680x0 ダブルバスフォールト時に呼ばれるコールバック。 -// step1. グローバル関数で受けて、クラスメンバ関数を呼ぶだけ。 +// CD/MO/FD の挿入失敗メッセージ。 +// CD/MO と FD でメッセージが同じなので共用する。 void -wxmainframe_halt_callback(void *arg) +WXMainFrame::OnInsertFailed(wxCommandEvent& event) { - ((WXMainFrame *)arg)->HaltCallback(); + ::wxMessageBox(_("Cannot load the image file")); } -// m680x0 ダブルバスフォールト処理。 -// step2. クラス内だが VM スレッドなので、ここではイベントをポストするだけ。 +// ウィンドウをリストから削除する +// (ウィンドウの Close() から呼ばれる) void -WXMainFrame::HaltCallback() +WXMainFrame::DeleteWindow(wxWindow *win) { - wxCommandEvent *ev = new wxCommandEvent(WXW_EVT_HALT_NOTIFY); - QueueEvent(ev); + for (int id = 0; id < ID_SUBWIN_MAX; id++) { + if (windows[id].get() == win) { + windows[id].reset(); + break; + } + } } // m680x0 ダブルバスフォールト処理 -// step3. ここが本題。でもダイアログを出すだけ。 void WXMainFrame::OnHaltNotify(wxCommandEvent& event) {