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

This commit is contained in:
Christopher A. Mosher 2022-12-13 20:32:50 -05:00
parent 01bc7fa02f
commit 846d772b8f
13 changed files with 91 additions and 87 deletions

View File

@ -144,8 +144,8 @@ bool E2wxApp::OnInit() {
InitBoostLog(); InitBoostLog();
// TODO investigate redirecting wxLogs to boost // TODO investigate redirecting wxLogs to boost
// wxLog* logger = new wxLogStream(&std::cerr); wxLog* logger = new wxLogStream(&std::cerr);
// wxLog::SetActiveTarget(logger); wxLog::SetActiveTarget(logger);
// wxLogWarning("%s", "a warning has occurred"); // wxLogWarning("%s", "a warning has occurred");
// wxLogInfo("%s", "informational"); // wxLogInfo("%s", "informational");
// wxLogVerbose("%s", "verbose"); // wxLogVerbose("%s", "verbose");
@ -186,12 +186,11 @@ bool E2wxApp::OnInit() {
BOOST_LOG_TRIVIAL(info) << "Resource directory path: " << this->resdir; BOOST_LOG_TRIVIAL(info) << "Resource directory path: " << this->resdir;
wxXmlResource::Get()->InitAllHandlers(); wxXmlResource::Get()->InitAllHandlers();
if (!wxXmlResource::Get()->LoadAllFiles(this->resdir.c_str())) { wxXmlResource::Get()->LoadAllFiles(this->resdir.c_str());
return false;
}
// note: the frame is responsible for deleting itself (via Destroy())
frame = new E2wxFrame(); frame = new E2wxFrame();
frame->DoInit(); frame->DoInit();
frame->Show(); frame->Show();
@ -237,17 +236,28 @@ void E2wxApp::OnFnKeyPressed(const SDL_Keycode k) {
bool E2wxApp::CloseMainFrame() { bool E2wxApp::CloseMainFrame() {
bool r = false; bool r = false;
BOOST_LOG_TRIVIAL(info) << "Received request to close main frame.";
if (this->frame) { if (this->frame) {
if (this->frame->Close(false)) { if (this->frame->Close(false)) {
delete this->frame; BOOST_LOG_TRIVIAL(info) << "Main frame successfully deleted.";
this->frame = nullptr; this->frame = nullptr;
wxApp::ExitMainLoop();
r = true; r = true;
} else {
BOOST_LOG_TRIVIAL(info) << "User cancelled application shutdown.";
} }
} else {
BOOST_LOG_TRIVIAL(warning) << "No main frame found.";
} }
return r; return r;
} }
int E2wxApp::OnExit() { 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) { if (this->emu_timer) {
delete this->emu_timer; delete this->emu_timer;
this->emu_timer = nullptr; this->emu_timer = nullptr;
@ -256,12 +266,16 @@ int E2wxApp::OnExit() {
delete this->emu; delete this->emu;
this->emu = nullptr; this->emu = nullptr;
} }
BOOST_LOG_TRIVIAL(info) << "Application OnExit complete.";
return 0; return 0;
} }
void E2wxApp::OnFatalException() { void E2wxApp::OnFatalException() {
BOOST_LOG_TRIVIAL(info) << "Application will handle fatal exception...";
wxDebugReport report; wxDebugReport report;
report.AddAll(); report.AddAll();

View File

@ -70,7 +70,6 @@ class E2wxApp : public wxApp {
const std::filesystem::path BuildLogFilePath() const; const std::filesystem::path BuildLogFilePath() const;
void InitBoostLog(); void InitBoostLog();
void StartEmulator();
public: public:
E2wxApp(); E2wxApp();
@ -84,6 +83,8 @@ public:
const std::filesystem::path GetConfigDir() const; const std::filesystem::path GetConfigDir() const;
const std::filesystem::path GetDocumentsDir() const; const std::filesystem::path GetDocumentsDir() const;
void StartEmulator();
void OnFnKeyPressed(const SDL_Keycode k); void OnFnKeyPressed(const SDL_Keycode k);
bool CloseMainFrame(); bool CloseMainFrame();

View File

@ -22,12 +22,16 @@
#include "E2wxApp.h" #include "E2wxApp.h"
#include "PreferencesDialog.h" #include "PreferencesDialog.h"
#include "gui.h" #include "gui.h"
#include <wx/menu.h> #include <wx/menu.h>
#include <wx/accel.h> #include <wx/accel.h>
#include <wx/msgdlg.h> #include <wx/msgdlg.h>
#include <wx/event.h> #include <wx/event.h>
#include <wx/persist/toplevel.h> #include <wx/persist/toplevel.h>
#include <boost/log/trivial.hpp>
enum E2MenuID { enum E2MenuID {
ID_MENUITEM_POWER = wxID_HIGHEST+1, ID_MENUITEM_POWER = wxID_HIGHEST+1,
@ -41,7 +45,7 @@ enum E2MenuID {
wxBEGIN_EVENT_TABLE(E2wxFrame, wxFrame) wxBEGIN_EVENT_TABLE(E2wxFrame, wxFrame)
EVT_CLOSE(E2wxFrame::HandleUserQuitRequest) 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_PREFERENCES, E2wxFrame::OnPreferences)
EVT_MENU(wxID_ABOUT, E2wxFrame::OnAbout) EVT_MENU(wxID_ABOUT, E2wxFrame::OnAbout)
EVT_MENU(ID_MENUITEM_POWER, E2wxFrame::OnTogglePower) 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() { E2wxFrame::~E2wxFrame() {
if (this->statusBar) {
delete this->statusBar;
this->statusBar = nullptr;
}
} }
@ -125,31 +125,42 @@ void E2wxFrame::InitMenuBar() {
} }
void E2wxFrame::InitStatusBar() { void E2wxFrame::InitStatusBar() {
this->statusBar = CreateStatusBar(); CreateStatusBar();
SetStatusText("Welcome to "+wxGetApp().GetID()); SetStatusText("Welcome to "+wxGetApp().GetID());
} }
void E2wxFrame::HandleUserQuitRequest(wxCloseEvent& event) { void E2wxFrame::HandleUserQuitRequest(wxCloseEvent& event) {
// TODO how to handle event.CanVeto() ? I'd like to auto-save everything BOOST_LOG_TRIVIAL(info) << "Main frame will handle request to close application...";
if (wxGetApp().EnsureCanQuit()) { if (!event.CanVeto()) {
event.Skip(); 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 { } else {
BOOST_LOG_TRIVIAL(info) << "User cancelled closing main frame.";
event.Veto(); event.Veto();
} }
} }
void E2wxFrame::OnExit(wxCommandEvent& event) { void E2wxFrame::OnFileExitCommand(wxCommandEvent& event) {
Close(false); Close(false);
} }
void E2wxFrame::OnPreferences(wxCommandEvent& event) { void E2wxFrame::OnPreferences(wxCommandEvent& event) {
PreferencesDialog *dlg = new PreferencesDialog(this); PreferencesDialog *dlg = new PreferencesDialog(this);
dlg->OnInit(); if (dlg->OnInit()) {
dlg->ShowModal(); dlg->ShowModal();
dlg->Destroy();
}
// TODO re-configure emulator here
wxGetApp().StartEmulator();
} }
void E2wxFrame::OnAbout(wxCommandEvent& event) { void E2wxFrame::OnAbout(wxCommandEvent& event) {
@ -168,7 +179,7 @@ void E2wxFrame::OnAbout(wxCommandEvent& event) {
void E2wxFrame::OnTogglePower(wxCommandEvent& event) { void E2wxFrame::OnTogglePower(wxCommandEvent& event) {
GUI::queueTogglePower(); wxGetApp().TogglePower();
} }
void E2wxFrame::OnCycleMonitor(wxCommandEvent& event) { void E2wxFrame::OnCycleMonitor(wxCommandEvent& event) {

View File

@ -23,7 +23,6 @@
#include <wx/frame.h> #include <wx/frame.h>
#include <wx/event.h> #include <wx/event.h>
#include <wx/statusbr.h>
#include <string> #include <string>
class E2wxFrame : public wxFrame { class E2wxFrame : public wxFrame {
@ -39,7 +38,7 @@ private:
void HandleUserQuitRequest(wxCloseEvent& event); void HandleUserQuitRequest(wxCloseEvent& event);
void OnExit(wxCommandEvent& event); void OnFileExitCommand(wxCommandEvent& event);
void OnPreferences(wxCommandEvent& event); void OnPreferences(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event); void OnAbout(wxCommandEvent& event);
@ -53,8 +52,6 @@ private:
void OnToggleBuffered(wxCommandEvent& event); void OnToggleBuffered(wxCommandEvent& event);
wxDECLARE_EVENT_TABLE(); wxDECLARE_EVENT_TABLE();
wxStatusBar *statusBar;
}; };
#endif /* E2WXFRAME_H */ #endif /* E2WXFRAME_H */

View File

@ -132,10 +132,14 @@ void PreferencesDialog::BuildItemTree() {
this->GetSizer()->Layout(); this->GetSizer()->Layout();
} }
void PreferencesDialog::OnInit() { bool PreferencesDialog::OnInit() {
wxConfigBase::Get()->Read("/ActivePreferences/name", &this->active, ""); 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); SetSize(SIZ_DLG);
@ -144,6 +148,8 @@ void PreferencesDialog::OnInit() {
CTRL(wxTreeCtrl, treItems); CTRL(wxTreeCtrl, treItems);
treItems->SetFocus(); treItems->SetFocus();
treItems->SelectItem(treItems->GetRootItem()); treItems->SelectItem(treItems->GetRootItem());
return true;
} }
void PreferencesDialog::Save(const std::filesystem::path& to) { void PreferencesDialog::Save(const std::filesystem::path& to) {

View File

@ -29,7 +29,7 @@ public:
PreferencesDialog(wxWindow *parent); PreferencesDialog(wxWindow *parent);
~PreferencesDialog(); ~PreferencesDialog();
void OnInit(); bool OnInit();
}; };
#endif /* PREFERENCESDIALOG_H */ #endif /* PREFERENCESDIALOG_H */

View File

@ -49,23 +49,26 @@ addressBus(gui, revision, ram, rom, kbd, videoMode, paddles, paddleButtonStates,
picgen(tv, videoMode, revision), picgen(tv, videoMode, revision),
video(videoMode, addressBus, picgen, textRows), video(videoMode, addressBus, picgen, textRows),
transistors("transistors"), // TODO load file from resources transistors("transistors"), // TODO load file from resources
cpu(NULL), cpu(nullptr),
powerUpReset(*this), powerUpReset(*this),
revision(1) { revision(1) {
} }
Apple2::~Apple2() { Apple2::~Apple2() {
if (this->cpu) {
delete this->cpu;
}
} }
void Apple2::useEpple2Cpu() { void Apple2::useEpple2Cpu() {
if (this->cpu == NULL) { if (this->cpu == nullptr) {
std::cout << "Using fast Epple2 CPU emulator" << std::endl; std::cout << "Using fast Epple2 CPU emulator" << std::endl;
this->cpu = new CPU(this->addressBus); this->cpu = new CPU(this->addressBus);
} }
} }
void Apple2::useVisual6502Cpu() { 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; std::cout << "Using http://www.visual6502.org/ CPU emulation (which will be slow)." << std::endl;
this->cpu = new Emu6502(this->transistors, this->addressBus); this->cpu = new Emu6502(this->transistors, this->addressBus);
} }

View File

@ -25,6 +25,8 @@
#include <SDL.h> #include <SDL.h>
#include <boost/log/trivial.hpp>
#include <ctime> #include <ctime>
@ -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 // Take real-world keystrokes from SDL and filter them to emulate the Apple ][ keyboard
void Emulator::dispatchKeyDown(const SDL_KeyboardEvent& keyEvent) { void Emulator::dispatchKeyDown(const SDL_KeyboardEvent& keyEvent) {
if (keyEvent.repeat) { if (keyEvent.repeat) {
// To repeat on the real Apple ][, you need to use the REPT key (emulated below by F10)
return; return;
} }
@ -235,24 +238,21 @@ void Emulator::dispatchKeyDown(const SDL_KeyboardEvent& keyEvent) {
} }
if (sym == SDLK_F10) { if (sym == SDLK_F10) {
// ...start auto-repeat // handle REPT key
this->repeat = true; this->repeat = true;
this->rept = CYCLES_PER_REPT; this->rept = CYCLES_PER_REPT;
return;
} else if (SDLK_F1 <= sym && sym <= SDLK_F12) { } else if (SDLK_F1 <= sym && sym <= SDLK_F12) {
wxGetApp().OnFnKeyPressed(sym); wxGetApp().OnFnKeyPressed(sym);
return; } else {
}
unsigned char key; unsigned char key;
const bool sendKey = translateKeysToAppleModernized(sym, mod, &key); const bool sendKey = translateKeysToAppleModernized(sym, mod, &key);
if (sendKey) { if (sendKey) {
//printf(" sending to apple as ASCII ------------------------------> %02X (%02X) (%d)\n", key, key | 0x80, key | 0x80); //printf(" sending to apple as ASCII ------------------------------> %02X (%02X) (%d)\n", key, key | 0x80, key | 0x80);
this->keypresses.push(key); this->keypresses.push(key);
this->lastKeyDown = key; this->lastKeyDown = key;
} }
} }
}
void Emulator::dispatchKeyUp(const SDL_KeyboardEvent& keyEvent) { void Emulator::dispatchKeyUp(const SDL_KeyboardEvent& keyEvent) {
const SDL_Keycode sym = keyEvent.keysym.sym; const SDL_Keycode sym = keyEvent.keysym.sym;
@ -296,6 +296,8 @@ static int askSave() {
} }
bool Emulator::isSafeToQuit() { bool Emulator::isSafeToQuit() {
BOOST_LOG_TRIVIAL(info) << "Checking for any unsaved changes...";
this->screenImage.exitFullScreen(); this->screenImage.exitFullScreen();
if (!this->apple2.cassetteOut.eject()) { if (!this->apple2.cassetteOut.eject()) {

View File

@ -51,14 +51,16 @@ class Emulator {
unsigned char lastKeyDown; unsigned char lastKeyDown;
Uint32 prev_ms; Uint32 prev_ms;
void dispatchKeyDown(const SDL_KeyboardEvent& keyEvent);
void dispatchKeyUp(const SDL_KeyboardEvent& keyEvent);
void powerOnComputer(); void powerOnComputer();
void powerOffComputer(); void powerOffComputer();
void handleRepeatKey();
void handleAnyPendingEvents(); void handleAnyPendingEvents();
void dispatchKeyDown(const SDL_KeyboardEvent& keyEvent);
void dispatchKeyUp(const SDL_KeyboardEvent& keyEvent);
void handleRepeatKey();
public: public:
Emulator(); Emulator();
virtual ~Emulator(); virtual ~Emulator();

View File

@ -15,18 +15,15 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <iostream>
#include "gui.h" #include "gui.h"
#include <SDL.h> #include <SDL.h>
/*
* Initialize SDL 2
*/
GUI::GUI() { GUI::GUI() {
const int result = SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO); const int result = SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO);
if (result != 0) { if (result != 0) {
std::cerr << "Failed to initialize SDL: " << SDL_GetError() << std::endl;
throw GUI::NotInitException(); throw GUI::NotInitException();
} }
} }
@ -35,40 +32,9 @@ GUI::~GUI() {
SDL_Quit(); 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(SDL_GetError()) {
GUI::NotInitException::NotInitException() : runtime_error("Unable to initialize SDL") {
SDL_GetError();
} }
GUI::NotInitException::~NotInitException() { GUI::NotInitException::~NotInitException() {

View File

@ -25,9 +25,6 @@ public:
GUI(); GUI();
virtual ~GUI(); virtual ~GUI();
static void queueQuit();
static void queueTogglePower();
class NotInitException : public std::runtime_error { class NotInitException : public std::runtime_error {
public: public:
NotInitException(); NotInitException();

View File

@ -34,6 +34,9 @@ LanguageCard::LanguageCard(ScreenImage& gui, int slot):
LanguageCard::~LanguageCard() LanguageCard::~LanguageCard()
{ {
for (Memory* m : this->ramBank) {
delete m;
}
} }

View File

@ -30,9 +30,11 @@ Slots::Slots(ScreenImage& gui):
Slots::~Slots() Slots::~Slots()
{ {
for (Card *card : this->cards) { for (Card *card : this->cards) {
if (card != &this->empty) {
delete card; delete card;
} }
} }
}
unsigned char Slots::io(const int islot, const int iswch, const unsigned char b, const bool writing) unsigned char Slots::io(const int islot, const int iswch, const unsigned char b, const bool writing)
{ {