mirror of
https://github.com/cmosher01/Epple-II.git
synced 2025-01-27 19:36:15 +00:00
handle quit/close events across SDL and wx sides including asking to save changes; refactor to remove old unused event loop code
This commit is contained in:
parent
49d6e664f5
commit
6a52aadfe4
@ -70,8 +70,9 @@ EmuTimer::EmuTimer(Emulator *e) : wxTimer(), emu(e) {
|
|||||||
EmuTimer::~EmuTimer() {
|
EmuTimer::~EmuTimer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define EXPECTED_MS 50 // see CHECK_EVERY_CYCLE in Emulator.cpp
|
||||||
void EmuTimer::begin() {
|
void EmuTimer::begin() {
|
||||||
this->Start(50); // TODO: EXPECTED_MS from Emulator
|
this->Start(EXPECTED_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuTimer::Notify() {
|
void EmuTimer::Notify() {
|
||||||
@ -83,7 +84,10 @@ void EmuTimer::Notify() {
|
|||||||
|
|
||||||
E2wxApp::E2wxApp() :
|
E2wxApp::E2wxApp() :
|
||||||
id(wxSTRINGIZE(PROJECT_VENDOR) "." wxSTRINGIZE(PROJECT_NAME)),
|
id(wxSTRINGIZE(PROJECT_VENDOR) "." wxSTRINGIZE(PROJECT_NAME)),
|
||||||
version(wxSTRINGIZE(PROJECT_VERSION)) {
|
version(wxSTRINGIZE(PROJECT_VERSION)),
|
||||||
|
frame(nullptr),
|
||||||
|
emu(nullptr),
|
||||||
|
emu_timer(nullptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
E2wxApp::~E2wxApp() {
|
E2wxApp::~E2wxApp() {
|
||||||
@ -169,7 +173,7 @@ bool E2wxApp::OnInit() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
E2wxFrame *frame = new E2wxFrame();
|
frame = new E2wxFrame();
|
||||||
frame->DoInit();
|
frame->DoInit();
|
||||||
frame->Show();
|
frame->Show();
|
||||||
|
|
||||||
@ -184,12 +188,26 @@ bool E2wxApp::OnInit() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool E2wxApp::CloseMainFrame() {
|
||||||
|
bool r = false;
|
||||||
|
if (this->frame) {
|
||||||
|
if (this->frame->Close(false)) {
|
||||||
|
delete this->frame;
|
||||||
|
this->frame = nullptr;
|
||||||
|
r = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
int E2wxApp::OnExit() {
|
int E2wxApp::OnExit() {
|
||||||
if (this->emu_timer) {
|
if (this->emu_timer) {
|
||||||
delete this->emu_timer;
|
delete this->emu_timer;
|
||||||
|
this->emu_timer = nullptr;
|
||||||
}
|
}
|
||||||
if (this->emu) {
|
if (this->emu) {
|
||||||
delete this->emu;
|
delete this->emu;
|
||||||
|
this->emu = nullptr;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -308,10 +326,26 @@ void E2wxApp::InitBoostLog() {
|
|||||||
|
|
||||||
|
|
||||||
void E2wxApp::StartEmulator() {
|
void E2wxApp::StartEmulator() {
|
||||||
|
if (this->emu_timer) {
|
||||||
|
delete this->emu_timer;
|
||||||
|
}
|
||||||
|
if (this->emu) {
|
||||||
|
delete this->emu;
|
||||||
|
}
|
||||||
|
|
||||||
this->emu = new Emulator();
|
this->emu = new Emulator();
|
||||||
E2Config cfg(this->arg_configfile, this->opt_config_from_prefs_only);
|
E2Config cfg{this->arg_configfile, this->opt_config_from_prefs_only};
|
||||||
this->emu->config(cfg);
|
this->emu->config(cfg);
|
||||||
this->emu->init();
|
this->emu->init();
|
||||||
this->emu_timer = new EmuTimer(this->emu);
|
|
||||||
|
this->emu_timer = new EmuTimer{this->emu};
|
||||||
this->emu_timer->begin();
|
this->emu_timer->begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool E2wxApp::EnsureCanQuit() {
|
||||||
|
bool ok = true;
|
||||||
|
if (this->emu) {
|
||||||
|
ok = this->emu->isSafeToQuit();
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class E2wxFrame;
|
||||||
class Emulator;
|
class Emulator;
|
||||||
|
|
||||||
|
|
||||||
@ -60,6 +61,7 @@ class E2wxApp : public wxApp {
|
|||||||
std::filesystem::path docsdir;
|
std::filesystem::path docsdir;
|
||||||
std::filesystem::path arg_configfile;
|
std::filesystem::path arg_configfile;
|
||||||
bool opt_config_from_prefs_only;
|
bool opt_config_from_prefs_only;
|
||||||
|
E2wxFrame *frame;
|
||||||
EmuTimer *emu_timer;
|
EmuTimer *emu_timer;
|
||||||
Emulator *emu;
|
Emulator *emu;
|
||||||
|
|
||||||
@ -79,6 +81,9 @@ 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;
|
||||||
|
|
||||||
|
bool CloseMainFrame();
|
||||||
|
bool EnsureCanQuit();
|
||||||
|
|
||||||
virtual bool OnInit() override;
|
virtual bool OnInit() override;
|
||||||
virtual int OnExit() override;
|
virtual int OnExit() override;
|
||||||
virtual void OnFatalException() override;
|
virtual void OnFatalException() override;
|
||||||
|
@ -24,8 +24,11 @@
|
|||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
#include <wx/menu.h>
|
#include <wx/menu.h>
|
||||||
#include <wx/msgdlg.h>
|
#include <wx/msgdlg.h>
|
||||||
|
#include <wx/event.h>
|
||||||
#include <wx/persist/toplevel.h>
|
#include <wx/persist/toplevel.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enum E2MenuID {
|
enum E2MenuID {
|
||||||
ID_MENUITEM_POWER = wxID_HIGHEST+1
|
ID_MENUITEM_POWER = wxID_HIGHEST+1
|
||||||
};
|
};
|
||||||
@ -35,6 +38,7 @@ wxBEGIN_EVENT_TABLE(E2wxFrame, wxFrame)
|
|||||||
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)
|
||||||
|
EVT_CLOSE(E2wxFrame::HandleUserQuitRequest)
|
||||||
wxEND_EVENT_TABLE()
|
wxEND_EVENT_TABLE()
|
||||||
|
|
||||||
|
|
||||||
@ -84,7 +88,7 @@ void E2wxFrame::InitStatusBar() {
|
|||||||
|
|
||||||
|
|
||||||
void E2wxFrame::OnExit(wxCommandEvent& event) {
|
void E2wxFrame::OnExit(wxCommandEvent& event) {
|
||||||
Close(true);
|
Close(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void E2wxFrame::OnAbout(wxCommandEvent& event) {
|
void E2wxFrame::OnAbout(wxCommandEvent& event) {
|
||||||
@ -109,3 +113,12 @@ void E2wxFrame::OnPreferences(wxCommandEvent& event) {
|
|||||||
void E2wxFrame::OnTogglePower(wxCommandEvent& event) {
|
void E2wxFrame::OnTogglePower(wxCommandEvent& event) {
|
||||||
GUI::queueTogglePower();
|
GUI::queueTogglePower();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void E2wxFrame::HandleUserQuitRequest(wxCloseEvent& event) {
|
||||||
|
// TODO how to handle event.CanVeto() ? I'd like to auto-save everything
|
||||||
|
if (wxGetApp().EnsureCanQuit()) {
|
||||||
|
event.Skip();
|
||||||
|
} else {
|
||||||
|
event.Veto();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -37,6 +37,7 @@ private:
|
|||||||
void OnPreferences(wxCommandEvent& event);
|
void OnPreferences(wxCommandEvent& event);
|
||||||
void OnAbout(wxCommandEvent& event);
|
void OnAbout(wxCommandEvent& event);
|
||||||
void OnTogglePower(wxCommandEvent& event);
|
void OnTogglePower(wxCommandEvent& event);
|
||||||
|
void HandleUserQuitRequest(wxCloseEvent& event);
|
||||||
|
|
||||||
void InitMenuBar();
|
void InitMenuBar();
|
||||||
void InitStatusBar();
|
void InitStatusBar();
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
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 "emulator.h"
|
#include "emulator.h"
|
||||||
|
#include "E2wxApp.h"
|
||||||
#include "e2config.h"
|
#include "e2config.h"
|
||||||
#include "e2command.h"
|
#include "e2command.h"
|
||||||
#include "e2const.h"
|
#include "e2const.h"
|
||||||
@ -65,7 +66,6 @@ void Emulator::powerOnComputer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::powerOffComputer() {
|
void Emulator::powerOffComputer() {
|
||||||
// TODO Need to ask user if OK to lose any unsaved changes to disks
|
|
||||||
this->apple2.powerOff();
|
this->apple2.powerOff();
|
||||||
this->screenImage.drawPower(false);
|
this->screenImage.drawPower(false);
|
||||||
this->display.setNoise(true);
|
this->display.setNoise(true);
|
||||||
@ -85,26 +85,10 @@ void Emulator::init() {
|
|||||||
this->display.powerOn(true);
|
this->display.powerOn(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// How many emulation ticks between asking SDL if there is any new input
|
|
||||||
// from the user or other GUI events.
|
|
||||||
// This is also how often we shall update the estimate of the emulator's
|
|
||||||
// actual speed performance
|
|
||||||
// When the CPU is the object being ticked (each tick is a CPU cycle), then
|
|
||||||
// this is 20.04378892 Hz in emulated seconds time
|
|
||||||
#define CHECK_EVERY_CYCLE 51024
|
|
||||||
#define CHECK_CYCLES_K 51024000
|
|
||||||
#define EXPECTED_MS 50
|
|
||||||
|
|
||||||
// U.A.2 p. 7-13: REPT key repeats at 10Hz.
|
// U.A.2 p. 7-13: REPT key repeats at 10Hz.
|
||||||
static const int CYCLES_PER_REPT(E2Const::AVG_CPU_HZ / 10);
|
static const int CYCLES_PER_REPT(E2Const::AVG_CPU_HZ / 10);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// If the Apple ][ keyboard repeat is on (the REPT key is
|
// If the Apple ][ keyboard repeat is on (the REPT key is
|
||||||
// down)...
|
// down)...
|
||||||
void Emulator::handleRepeatKey() {
|
void Emulator::handleRepeatKey() {
|
||||||
@ -134,7 +118,9 @@ void Emulator::handleAnyPendingEvents() {
|
|||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
// If SDL is going away...
|
// If SDL is going away...
|
||||||
quitIfSafe();
|
// could be due to user closing the SDL window, pressing cmd-Q on Mac,
|
||||||
|
// ctrl-C from the command line on Linux, process being killed, etc.
|
||||||
|
handleUserQuitRequest();
|
||||||
break;
|
break;
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
// If we're collecting a command line for changing any
|
// If we're collecting a command line for changing any
|
||||||
@ -145,6 +131,7 @@ void Emulator::handleAnyPendingEvents() {
|
|||||||
// ...else we're collecting keypresses for the keyboard
|
// ...else we're collecting keypresses for the keyboard
|
||||||
// emulation (and thus the Apple ][ emulation itself)
|
// emulation (and thus the Apple ][ emulation itself)
|
||||||
dispatchKeypress(event.key);
|
dispatchKeypress(event.key);
|
||||||
|
// People who have too many press-releases should be referred to as "keyboards"
|
||||||
break;
|
break;
|
||||||
case SDL_KEYUP:
|
case SDL_KEYUP:
|
||||||
// If we're collecting a command line for changing any
|
// If we're collecting a command line for changing any
|
||||||
@ -166,54 +153,15 @@ void Emulator::handleAnyPendingEvents() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// How many emulation ticks between asking SDL if there is any new input
|
||||||
|
// from the user or other GUI events.
|
||||||
|
// This is also how often we shall update the estimate of the emulator's
|
||||||
|
// actual speed performance
|
||||||
|
// When the CPU is the object being ticked (each tick is a CPU cycle), then
|
||||||
|
// this is 20.04378892 Hz in emulated seconds time
|
||||||
|
#define CHECK_EVERY_CYCLE 51024
|
||||||
|
|
||||||
// The core of this Apple
|
// The core of this Apple
|
||||||
int Emulator::run() {
|
|
||||||
// While the user still wants to run this emulation...
|
|
||||||
while (!this->quit) {
|
|
||||||
tick();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Emulator::tick() {
|
|
||||||
if (this->timable) {
|
|
||||||
this->timable->tick(); // this runs the emulator!
|
|
||||||
handleRepeatKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
// People who have too many press releases should be referred to as
|
|
||||||
// keyboards
|
|
||||||
|
|
||||||
if (CHECK_EVERY_CYCLE <= ++this->skip) {
|
|
||||||
// ...then it's time to drain away any piled-up user interaction
|
|
||||||
// events that SDL has stored up for us
|
|
||||||
// Reload the skip quantity
|
|
||||||
this->skip = 0;
|
|
||||||
|
|
||||||
handleAnyPendingEvents();
|
|
||||||
|
|
||||||
// If we're trying to run as slow as a real Apple ][...
|
|
||||||
if (!this->fhyper.isHyper()) {
|
|
||||||
const int delta_ms = EXPECTED_MS - (SDL_GetTicks() - this->prev_ms);
|
|
||||||
if (0 < delta_ms && delta_ms <= EXPECTED_MS) {
|
|
||||||
SDL_Delay(delta_ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display the current estimate of the emulator's actual speed
|
|
||||||
// performance
|
|
||||||
this->screenImage.displayHz(CHECK_CYCLES_K / (SDL_GetTicks() - this->prev_ms));
|
|
||||||
|
|
||||||
this->prev_ms = SDL_GetTicks();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Emulator::tick50ms() {
|
void Emulator::tick50ms() {
|
||||||
if (this->timable) {
|
if (this->timable) {
|
||||||
for (int i = 0; i < CHECK_EVERY_CYCLE; ++i) {
|
for (int i = 0; i < CHECK_EVERY_CYCLE; ++i) {
|
||||||
@ -222,7 +170,7 @@ void Emulator::tick50ms() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleAnyPendingEvents();
|
handleAnyPendingEvents();
|
||||||
this->screenImage.displayHz(CHECK_CYCLES_K / (SDL_GetTicks() - this->prev_ms));
|
this->screenImage.displayHz((1000*CHECK_EVERY_CYCLE)/(SDL_GetTicks() - this->prev_ms));
|
||||||
this->prev_ms = SDL_GetTicks();
|
this->prev_ms = SDL_GetTicks();
|
||||||
// TODO: how to check this->quit ?
|
// TODO: how to check this->quit ?
|
||||||
}
|
}
|
||||||
@ -413,7 +361,7 @@ void Emulator::dispatchKeypress(const SDL_KeyboardEvent& keyEvent) {
|
|||||||
return;
|
return;
|
||||||
}// ...else exit the entire emulation
|
}// ...else exit the entire emulation
|
||||||
else if (sym == SDLK_F9) {
|
else if (sym == SDLK_F9) {
|
||||||
quitIfSafe();
|
handleUserQuitRequest();
|
||||||
return;
|
return;
|
||||||
}// ...else save a screen shot
|
}// ...else save a screen shot
|
||||||
else if (sym == SDLK_F8) {
|
else if (sym == SDLK_F8) {
|
||||||
@ -495,7 +443,10 @@ static int askSave() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Emulator::isSafeToQuit() {
|
bool Emulator::isSafeToQuit() {
|
||||||
|
this->screenImage.exitFullScreen();
|
||||||
|
|
||||||
if (!this->apple2.cassetteOut.eject()) {
|
if (!this->apple2.cassetteOut.eject()) {
|
||||||
|
// TODO does this handle the case where we fail to save?
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,14 +463,17 @@ bool Emulator::isSafeToQuit() {
|
|||||||
if (resp == wxID_YES) {
|
if (resp == wxID_YES) {
|
||||||
this->apple2.slts.save(0);
|
this->apple2.slts.save(0);
|
||||||
this->apple2.slts.save(1);
|
this->apple2.slts.save(1);
|
||||||
|
// TODO handle case where we fail to save,
|
||||||
|
// in which case we should alert the user,
|
||||||
|
// and return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::quitIfSafe() {
|
// we come here due to F9 or SQL_Quit event
|
||||||
this->screenImage.exitFullScreen();
|
void Emulator::handleUserQuitRequest() {
|
||||||
if (isSafeToQuit()) {
|
wxGetApp().CloseMainFrame();
|
||||||
this->quit = true;
|
// wxWidgets will then call us back with isSafeToQuit, and if so will exit the application
|
||||||
}
|
// (note wxWidgets will also need to call us back when it gets it's own quit requests)
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,7 @@
|
|||||||
class Timable;
|
class Timable;
|
||||||
class E2Config;
|
class E2Config;
|
||||||
|
|
||||||
class Emulator
|
class Emulator {
|
||||||
{
|
|
||||||
PaddleButtonStates paddleButtonStates;
|
PaddleButtonStates paddleButtonStates;
|
||||||
KeypressQueue keypresses;
|
KeypressQueue keypresses;
|
||||||
|
|
||||||
@ -58,16 +57,14 @@ class Emulator
|
|||||||
bool pendingCommandExit;
|
bool pendingCommandExit;
|
||||||
std::string cmdline;
|
std::string cmdline;
|
||||||
|
|
||||||
void tick();
|
|
||||||
|
|
||||||
void dispatchKeypress(const SDL_KeyboardEvent& keyEvent);
|
void dispatchKeypress(const SDL_KeyboardEvent& keyEvent);
|
||||||
void dispatchKeyUp(const SDL_KeyboardEvent& keyEvent);
|
void dispatchKeyUp(const SDL_KeyboardEvent& keyEvent);
|
||||||
void cmdKey(const SDL_KeyboardEvent& keyEvent);
|
void cmdKey(const SDL_KeyboardEvent& keyEvent);
|
||||||
void processCommand();
|
void processCommand();
|
||||||
bool isSafeToQuit();
|
|
||||||
|
|
||||||
void handleRepeatKey();
|
void handleRepeatKey();
|
||||||
void handleAnyPendingEvents();
|
void handleAnyPendingEvents();
|
||||||
|
void handleUserQuitRequest();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Emulator();
|
Emulator();
|
||||||
@ -75,15 +72,14 @@ public:
|
|||||||
|
|
||||||
void config(E2Config& cfg);
|
void config(E2Config& cfg);
|
||||||
|
|
||||||
virtual void init();
|
void init();
|
||||||
|
|
||||||
void powerOnComputer();
|
void powerOnComputer();
|
||||||
void powerOffComputer();
|
void powerOffComputer();
|
||||||
void toggleComputerPower();
|
void toggleComputerPower();
|
||||||
void cycleDisplayType();
|
void cycleDisplayType();
|
||||||
void quitIfSafe();
|
bool isSafeToQuit();
|
||||||
|
|
||||||
virtual int run();
|
|
||||||
void tick50ms();
|
void tick50ms();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user