From 846d772b8f45f2eeb2036811c8d129b9d9d8db1e Mon Sep 17 00:00:00 2001 From: "Christopher A. Mosher" Date: Tue, 13 Dec 2022 20:32:50 -0500 Subject: [PATCH] fix crash if run without resources; fix some memory leaks; use Destroy to delete frame; add logging of shutdown process; remove some legacy code; restart emu after config prefs changes --- src/E2wxApp.cpp | 26 +++++++++++++++++++------ src/E2wxApp.h | 3 ++- src/E2wxFrame.cpp | 39 ++++++++++++++++++++++++-------------- src/E2wxFrame.h | 5 +---- src/PreferencesDialog.cpp | 10 ++++++++-- src/PreferencesDialog.h | 2 +- src/apple2.cpp | 9 ++++++--- src/emulator.cpp | 26 +++++++++++++------------ src/emulator.h | 8 +++++--- src/gui.cpp | 40 +++------------------------------------ src/gui.h | 3 --- src/languagecard.cpp | 3 +++ src/slots.cpp | 4 +++- 13 files changed, 91 insertions(+), 87 deletions(-) diff --git a/src/E2wxApp.cpp b/src/E2wxApp.cpp index b133f13..4717547 100644 --- a/src/E2wxApp.cpp +++ b/src/E2wxApp.cpp @@ -144,8 +144,8 @@ bool E2wxApp::OnInit() { InitBoostLog(); // TODO investigate redirecting wxLogs to boost -// wxLog* logger = new wxLogStream(&std::cerr); -// wxLog::SetActiveTarget(logger); + wxLog* logger = new wxLogStream(&std::cerr); + wxLog::SetActiveTarget(logger); // wxLogWarning("%s", "a warning has occurred"); // wxLogInfo("%s", "informational"); // wxLogVerbose("%s", "verbose"); @@ -186,12 +186,11 @@ bool E2wxApp::OnInit() { BOOST_LOG_TRIVIAL(info) << "Resource directory path: " << this->resdir; wxXmlResource::Get()->InitAllHandlers(); - if (!wxXmlResource::Get()->LoadAllFiles(this->resdir.c_str())) { - return false; - } + wxXmlResource::Get()->LoadAllFiles(this->resdir.c_str()); + // note: the frame is responsible for deleting itself (via Destroy()) frame = new E2wxFrame(); frame->DoInit(); frame->Show(); @@ -237,17 +236,28 @@ void E2wxApp::OnFnKeyPressed(const SDL_Keycode k) { bool E2wxApp::CloseMainFrame() { bool r = false; + BOOST_LOG_TRIVIAL(info) << "Received request to close main frame."; if (this->frame) { if (this->frame->Close(false)) { - delete this->frame; + BOOST_LOG_TRIVIAL(info) << "Main frame successfully deleted."; this->frame = nullptr; + wxApp::ExitMainLoop(); r = true; + } else { + BOOST_LOG_TRIVIAL(info) << "User cancelled application shutdown."; } + } else { + BOOST_LOG_TRIVIAL(warning) << "No main frame found."; } return r; } int E2wxApp::OnExit() { + BOOST_LOG_TRIVIAL(info) << "Begin application OnExit..."; + + delete wxXmlResource::Set(nullptr); + + BOOST_LOG_TRIVIAL(info) << "Deleting emulator instance..."; if (this->emu_timer) { delete this->emu_timer; this->emu_timer = nullptr; @@ -256,12 +266,16 @@ int E2wxApp::OnExit() { delete this->emu; this->emu = nullptr; } + + BOOST_LOG_TRIVIAL(info) << "Application OnExit complete."; return 0; } void E2wxApp::OnFatalException() { + BOOST_LOG_TRIVIAL(info) << "Application will handle fatal exception..."; + wxDebugReport report; report.AddAll(); diff --git a/src/E2wxApp.h b/src/E2wxApp.h index 2e5d63e..6409e15 100644 --- a/src/E2wxApp.h +++ b/src/E2wxApp.h @@ -70,7 +70,6 @@ class E2wxApp : public wxApp { const std::filesystem::path BuildLogFilePath() const; void InitBoostLog(); - void StartEmulator(); public: E2wxApp(); @@ -84,6 +83,8 @@ public: const std::filesystem::path GetConfigDir() const; const std::filesystem::path GetDocumentsDir() const; + void StartEmulator(); + void OnFnKeyPressed(const SDL_Keycode k); bool CloseMainFrame(); diff --git a/src/E2wxFrame.cpp b/src/E2wxFrame.cpp index b0a487d..bcea14d 100644 --- a/src/E2wxFrame.cpp +++ b/src/E2wxFrame.cpp @@ -22,12 +22,16 @@ #include "E2wxApp.h" #include "PreferencesDialog.h" #include "gui.h" + #include #include #include #include #include +#include + + enum E2MenuID { ID_MENUITEM_POWER = wxID_HIGHEST+1, @@ -41,7 +45,7 @@ enum E2MenuID { wxBEGIN_EVENT_TABLE(E2wxFrame, wxFrame) EVT_CLOSE(E2wxFrame::HandleUserQuitRequest) - EVT_MENU(wxID_EXIT, E2wxFrame::OnExit) + EVT_MENU(wxID_EXIT, E2wxFrame::OnFileExitCommand) EVT_MENU(wxID_PREFERENCES, E2wxFrame::OnPreferences) EVT_MENU(wxID_ABOUT, E2wxFrame::OnAbout) EVT_MENU(ID_MENUITEM_POWER, E2wxFrame::OnTogglePower) @@ -56,14 +60,10 @@ wxEND_EVENT_TABLE() -E2wxFrame::E2wxFrame() : wxFrame(nullptr, wxID_ANY, "epple2"), statusBar(nullptr) { +E2wxFrame::E2wxFrame() : wxFrame(nullptr, wxID_ANY, "epple2") { } E2wxFrame::~E2wxFrame() { - if (this->statusBar) { - delete this->statusBar; - this->statusBar = nullptr; - } } @@ -125,31 +125,42 @@ void E2wxFrame::InitMenuBar() { } void E2wxFrame::InitStatusBar() { - this->statusBar = CreateStatusBar(); + CreateStatusBar(); SetStatusText("Welcome to "+wxGetApp().GetID()); } void E2wxFrame::HandleUserQuitRequest(wxCloseEvent& event) { - // TODO how to handle event.CanVeto() ? I'd like to auto-save everything - if (wxGetApp().EnsureCanQuit()) { - event.Skip(); + BOOST_LOG_TRIVIAL(info) << "Main frame will handle request to close application..."; + if (!event.CanVeto()) { + BOOST_LOG_TRIVIAL(warning) << "Application is being forced to quit; any unsaved changes will be discarded."; + // TODO how to handle !event.CanVeto() ? + // I'd like to auto-save everything + Destroy(); + } else if (wxGetApp().EnsureCanQuit()) { + BOOST_LOG_TRIVIAL(info) << "Destroying main frame now..."; + Destroy(); } else { + BOOST_LOG_TRIVIAL(info) << "User cancelled closing main frame."; event.Veto(); } } -void E2wxFrame::OnExit(wxCommandEvent& event) { +void E2wxFrame::OnFileExitCommand(wxCommandEvent& event) { Close(false); } void E2wxFrame::OnPreferences(wxCommandEvent& event) { PreferencesDialog *dlg = new PreferencesDialog(this); - dlg->OnInit(); - dlg->ShowModal(); + if (dlg->OnInit()) { + dlg->ShowModal(); + dlg->Destroy(); + } + // TODO re-configure emulator here + wxGetApp().StartEmulator(); } void E2wxFrame::OnAbout(wxCommandEvent& event) { @@ -168,7 +179,7 @@ void E2wxFrame::OnAbout(wxCommandEvent& event) { void E2wxFrame::OnTogglePower(wxCommandEvent& event) { - GUI::queueTogglePower(); + wxGetApp().TogglePower(); } void E2wxFrame::OnCycleMonitor(wxCommandEvent& event) { diff --git a/src/E2wxFrame.h b/src/E2wxFrame.h index 3994004..54e42cf 100644 --- a/src/E2wxFrame.h +++ b/src/E2wxFrame.h @@ -23,7 +23,6 @@ #include #include -#include #include class E2wxFrame : public wxFrame { @@ -39,7 +38,7 @@ private: void HandleUserQuitRequest(wxCloseEvent& event); - void OnExit(wxCommandEvent& event); + void OnFileExitCommand(wxCommandEvent& event); void OnPreferences(wxCommandEvent& event); void OnAbout(wxCommandEvent& event); @@ -53,8 +52,6 @@ private: void OnToggleBuffered(wxCommandEvent& event); wxDECLARE_EVENT_TABLE(); - - wxStatusBar *statusBar; }; #endif /* E2WXFRAME_H */ diff --git a/src/PreferencesDialog.cpp b/src/PreferencesDialog.cpp index ed0db28..8c39312 100644 --- a/src/PreferencesDialog.cpp +++ b/src/PreferencesDialog.cpp @@ -132,10 +132,14 @@ void PreferencesDialog::BuildItemTree() { this->GetSizer()->Layout(); } -void PreferencesDialog::OnInit() { +bool PreferencesDialog::OnInit() { wxConfigBase::Get()->Read("/ActivePreferences/name", &this->active, ""); - wxXmlResource::Get()->LoadDialog(this, this->parent, "Preferences"); + const bool loaded = wxXmlResource::Get()->LoadDialog(this, this->parent, "Preferences"); + if (!loaded) { + BOOST_LOG_TRIVIAL(error) << "Could not load Preferences dialog resources."; + return false; + } SetSize(SIZ_DLG); @@ -144,6 +148,8 @@ void PreferencesDialog::OnInit() { CTRL(wxTreeCtrl, treItems); treItems->SetFocus(); treItems->SelectItem(treItems->GetRootItem()); + + return true; } void PreferencesDialog::Save(const std::filesystem::path& to) { diff --git a/src/PreferencesDialog.h b/src/PreferencesDialog.h index 3efa79b..4a91052 100644 --- a/src/PreferencesDialog.h +++ b/src/PreferencesDialog.h @@ -29,7 +29,7 @@ public: PreferencesDialog(wxWindow *parent); ~PreferencesDialog(); - void OnInit(); + bool OnInit(); }; #endif /* PREFERENCESDIALOG_H */ diff --git a/src/apple2.cpp b/src/apple2.cpp index 0a3d4ea..cd029c6 100644 --- a/src/apple2.cpp +++ b/src/apple2.cpp @@ -49,23 +49,26 @@ addressBus(gui, revision, ram, rom, kbd, videoMode, paddles, paddleButtonStates, picgen(tv, videoMode, revision), video(videoMode, addressBus, picgen, textRows), transistors("transistors"), // TODO load file from resources -cpu(NULL), +cpu(nullptr), powerUpReset(*this), revision(1) { } Apple2::~Apple2() { + if (this->cpu) { + delete this->cpu; + } } void Apple2::useEpple2Cpu() { - if (this->cpu == NULL) { + if (this->cpu == nullptr) { std::cout << "Using fast Epple2 CPU emulator" << std::endl; this->cpu = new CPU(this->addressBus); } } void Apple2::useVisual6502Cpu() { - if (this->cpu == NULL) { + if (this->cpu == nullptr) { std::cout << "Using http://www.visual6502.org/ CPU emulation (which will be slow)." << std::endl; this->cpu = new Emu6502(this->transistors, this->addressBus); } diff --git a/src/emulator.cpp b/src/emulator.cpp index 35b7395..7512025 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -25,6 +25,8 @@ #include +#include + #include @@ -222,6 +224,7 @@ static bool translateKeysToAppleModernized(SDL_Keycode keycode, SDL_Keymod modif // Take real-world keystrokes from SDL and filter them to emulate the Apple ][ keyboard void Emulator::dispatchKeyDown(const SDL_KeyboardEvent& keyEvent) { if (keyEvent.repeat) { + // To repeat on the real Apple ][, you need to use the REPT key (emulated below by F10) return; } @@ -235,22 +238,19 @@ void Emulator::dispatchKeyDown(const SDL_KeyboardEvent& keyEvent) { } if (sym == SDLK_F10) { - // ...start auto-repeat + // handle REPT key this->repeat = true; this->rept = CYCLES_PER_REPT; - return; } else if (SDLK_F1 <= sym && sym <= SDLK_F12) { wxGetApp().OnFnKeyPressed(sym); - return; - } - - unsigned char key; - const bool sendKey = translateKeysToAppleModernized(sym, mod, &key); - - if (sendKey) { - //printf(" sending to apple as ASCII ------------------------------> %02X (%02X) (%d)\n", key, key | 0x80, key | 0x80); - this->keypresses.push(key); - this->lastKeyDown = key; + } else { + unsigned char key; + const bool sendKey = translateKeysToAppleModernized(sym, mod, &key); + if (sendKey) { + //printf(" sending to apple as ASCII ------------------------------> %02X (%02X) (%d)\n", key, key | 0x80, key | 0x80); + this->keypresses.push(key); + this->lastKeyDown = key; + } } } @@ -296,6 +296,8 @@ static int askSave() { } bool Emulator::isSafeToQuit() { + BOOST_LOG_TRIVIAL(info) << "Checking for any unsaved changes..."; + this->screenImage.exitFullScreen(); if (!this->apple2.cassetteOut.eject()) { diff --git a/src/emulator.h b/src/emulator.h index 28a57a7..a2eafd3 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -51,14 +51,16 @@ class Emulator { unsigned char lastKeyDown; Uint32 prev_ms; - void dispatchKeyDown(const SDL_KeyboardEvent& keyEvent); - void dispatchKeyUp(const SDL_KeyboardEvent& keyEvent); void powerOnComputer(); void powerOffComputer(); - void handleRepeatKey(); void handleAnyPendingEvents(); + void dispatchKeyDown(const SDL_KeyboardEvent& keyEvent); + void dispatchKeyUp(const SDL_KeyboardEvent& keyEvent); + + void handleRepeatKey(); + public: Emulator(); virtual ~Emulator(); diff --git a/src/gui.cpp b/src/gui.cpp index 3b6046a..0caeab9 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -15,18 +15,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include + #include "gui.h" #include -/* - * Initialize SDL 2 - */ + GUI::GUI() { const int result = SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO); if (result != 0) { - std::cerr << "Failed to initialize SDL: " << SDL_GetError() << std::endl; throw GUI::NotInitException(); } } @@ -35,40 +32,9 @@ GUI::~GUI() { SDL_Quit(); } -static void pushSdlEvent(SDL_Event *e) { - const int r = SDL_PushEvent(e); - - if (r < 0) { - std::cerr << "SDL reported error: " << SDL_GetError() << std::endl; - } else if (r == 0) { - std::cerr << "SDL filtered event, sorry" << std::endl; - } else if (r == 1) { - std::cerr << "Pushed event to SDL" << std::endl; - } else { - std::cerr << "SDL reported unexpected error code: " << r << std::endl; - } -} - -void GUI::queueTogglePower() { - SDL_Event *e = new SDL_Event(); // note: creating struct on the stack doesn't seem to work - e->type = SDL_KEYDOWN; - e->key.keysym.sym = SDLK_F1; - pushSdlEvent(e); - delete e; -} - -void GUI::queueQuit() { - SDL_Event *e = new SDL_Event(); - e->type = SDL_KEYDOWN; - e->key.keysym.sym = SDLK_F9; - pushSdlEvent(e); - delete e; -} - -GUI::NotInitException::NotInitException() : runtime_error("Unable to initialize SDL") { - SDL_GetError(); +GUI::NotInitException::NotInitException() : runtime_error(SDL_GetError()) { } GUI::NotInitException::~NotInitException() { diff --git a/src/gui.h b/src/gui.h index 30e0c1c..1caa41f 100644 --- a/src/gui.h +++ b/src/gui.h @@ -25,9 +25,6 @@ public: GUI(); virtual ~GUI(); - static void queueQuit(); - static void queueTogglePower(); - class NotInitException : public std::runtime_error { public: NotInitException(); diff --git a/src/languagecard.cpp b/src/languagecard.cpp index d4d3cc4..b09393b 100644 --- a/src/languagecard.cpp +++ b/src/languagecard.cpp @@ -34,6 +34,9 @@ LanguageCard::LanguageCard(ScreenImage& gui, int slot): LanguageCard::~LanguageCard() { + for (Memory* m : this->ramBank) { + delete m; + } } diff --git a/src/slots.cpp b/src/slots.cpp index 118e213..0c79c87 100644 --- a/src/slots.cpp +++ b/src/slots.cpp @@ -30,7 +30,9 @@ Slots::Slots(ScreenImage& gui): Slots::~Slots() { for (Card *card : this->cards) { - delete card; + if (card != &this->empty) { + delete card; + } } }