mirror of
https://github.com/cmosher01/Epple-II.git
synced 2025-01-13 21:33:02 +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() {
|
||||
}
|
||||
|
||||
#define EXPECTED_MS 50 // see CHECK_EVERY_CYCLE in Emulator.cpp
|
||||
void EmuTimer::begin() {
|
||||
this->Start(50); // TODO: EXPECTED_MS from Emulator
|
||||
this->Start(EXPECTED_MS);
|
||||
}
|
||||
|
||||
void EmuTimer::Notify() {
|
||||
@ -83,7 +84,10 @@ void EmuTimer::Notify() {
|
||||
|
||||
E2wxApp::E2wxApp() :
|
||||
id(wxSTRINGIZE(PROJECT_VENDOR) "." wxSTRINGIZE(PROJECT_NAME)),
|
||||
version(wxSTRINGIZE(PROJECT_VERSION)) {
|
||||
version(wxSTRINGIZE(PROJECT_VERSION)),
|
||||
frame(nullptr),
|
||||
emu(nullptr),
|
||||
emu_timer(nullptr) {
|
||||
}
|
||||
|
||||
E2wxApp::~E2wxApp() {
|
||||
@ -169,7 +173,7 @@ bool E2wxApp::OnInit() {
|
||||
|
||||
|
||||
|
||||
E2wxFrame *frame = new E2wxFrame();
|
||||
frame = new E2wxFrame();
|
||||
frame->DoInit();
|
||||
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() {
|
||||
if (this->emu_timer) {
|
||||
delete this->emu_timer;
|
||||
this->emu_timer = nullptr;
|
||||
}
|
||||
if (this->emu) {
|
||||
delete this->emu;
|
||||
this->emu = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -308,10 +326,26 @@ void E2wxApp::InitBoostLog() {
|
||||
|
||||
|
||||
void E2wxApp::StartEmulator() {
|
||||
if (this->emu_timer) {
|
||||
delete this->emu_timer;
|
||||
}
|
||||
if (this->emu) {
|
||||
delete this->emu;
|
||||
}
|
||||
|
||||
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->init();
|
||||
this->emu_timer = new EmuTimer(this->emu);
|
||||
|
||||
this->emu_timer = new EmuTimer{this->emu};
|
||||
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;
|
||||
|
||||
|
||||
@ -60,6 +61,7 @@ class E2wxApp : public wxApp {
|
||||
std::filesystem::path docsdir;
|
||||
std::filesystem::path arg_configfile;
|
||||
bool opt_config_from_prefs_only;
|
||||
E2wxFrame *frame;
|
||||
EmuTimer *emu_timer;
|
||||
Emulator *emu;
|
||||
|
||||
@ -79,6 +81,9 @@ public:
|
||||
const std::filesystem::path GetConfigDir() const;
|
||||
const std::filesystem::path GetDocumentsDir() const;
|
||||
|
||||
bool CloseMainFrame();
|
||||
bool EnsureCanQuit();
|
||||
|
||||
virtual bool OnInit() override;
|
||||
virtual int OnExit() override;
|
||||
virtual void OnFatalException() override;
|
||||
|
@ -24,8 +24,11 @@
|
||||
#include "gui.h"
|
||||
#include <wx/menu.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/persist/toplevel.h>
|
||||
|
||||
|
||||
|
||||
enum E2MenuID {
|
||||
ID_MENUITEM_POWER = wxID_HIGHEST+1
|
||||
};
|
||||
@ -35,6 +38,7 @@ wxBEGIN_EVENT_TABLE(E2wxFrame, wxFrame)
|
||||
EVT_MENU(wxID_PREFERENCES, E2wxFrame::OnPreferences)
|
||||
EVT_MENU(wxID_ABOUT, E2wxFrame::OnAbout)
|
||||
EVT_MENU(ID_MENUITEM_POWER, E2wxFrame::OnTogglePower)
|
||||
EVT_CLOSE(E2wxFrame::HandleUserQuitRequest)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
|
||||
@ -84,7 +88,7 @@ void E2wxFrame::InitStatusBar() {
|
||||
|
||||
|
||||
void E2wxFrame::OnExit(wxCommandEvent& event) {
|
||||
Close(true);
|
||||
Close(false);
|
||||
}
|
||||
|
||||
void E2wxFrame::OnAbout(wxCommandEvent& event) {
|
||||
@ -109,3 +113,12 @@ void E2wxFrame::OnPreferences(wxCommandEvent& event) {
|
||||
void E2wxFrame::OnTogglePower(wxCommandEvent& event) {
|
||||
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 OnAbout(wxCommandEvent& event);
|
||||
void OnTogglePower(wxCommandEvent& event);
|
||||
void HandleUserQuitRequest(wxCloseEvent& event);
|
||||
|
||||
void InitMenuBar();
|
||||
void InitStatusBar();
|
||||
|
@ -16,6 +16,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "emulator.h"
|
||||
#include "E2wxApp.h"
|
||||
#include "e2config.h"
|
||||
#include "e2command.h"
|
||||
#include "e2const.h"
|
||||
@ -65,7 +66,6 @@ void Emulator::powerOnComputer() {
|
||||
}
|
||||
|
||||
void Emulator::powerOffComputer() {
|
||||
// TODO Need to ask user if OK to lose any unsaved changes to disks
|
||||
this->apple2.powerOff();
|
||||
this->screenImage.drawPower(false);
|
||||
this->display.setNoise(true);
|
||||
@ -85,26 +85,10 @@ void Emulator::init() {
|
||||
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.
|
||||
static const int CYCLES_PER_REPT(E2Const::AVG_CPU_HZ / 10);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// If the Apple ][ keyboard repeat is on (the REPT key is
|
||||
// down)...
|
||||
void Emulator::handleRepeatKey() {
|
||||
@ -134,7 +118,9 @@ void Emulator::handleAnyPendingEvents() {
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
// 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;
|
||||
case SDL_KEYDOWN:
|
||||
// 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
|
||||
// emulation (and thus the Apple ][ emulation itself)
|
||||
dispatchKeypress(event.key);
|
||||
// People who have too many press-releases should be referred to as "keyboards"
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
// 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
|
||||
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() {
|
||||
if (this->timable) {
|
||||
for (int i = 0; i < CHECK_EVERY_CYCLE; ++i) {
|
||||
@ -222,7 +170,7 @@ void Emulator::tick50ms() {
|
||||
}
|
||||
}
|
||||
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();
|
||||
// TODO: how to check this->quit ?
|
||||
}
|
||||
@ -413,7 +361,7 @@ void Emulator::dispatchKeypress(const SDL_KeyboardEvent& keyEvent) {
|
||||
return;
|
||||
}// ...else exit the entire emulation
|
||||
else if (sym == SDLK_F9) {
|
||||
quitIfSafe();
|
||||
handleUserQuitRequest();
|
||||
return;
|
||||
}// ...else save a screen shot
|
||||
else if (sym == SDLK_F8) {
|
||||
@ -495,7 +443,10 @@ static int askSave() {
|
||||
}
|
||||
|
||||
bool Emulator::isSafeToQuit() {
|
||||
this->screenImage.exitFullScreen();
|
||||
|
||||
if (!this->apple2.cassetteOut.eject()) {
|
||||
// TODO does this handle the case where we fail to save?
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -512,14 +463,17 @@ bool Emulator::isSafeToQuit() {
|
||||
if (resp == wxID_YES) {
|
||||
this->apple2.slts.save(0);
|
||||
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;
|
||||
}
|
||||
|
||||
void Emulator::quitIfSafe() {
|
||||
this->screenImage.exitFullScreen();
|
||||
if (isSafeToQuit()) {
|
||||
this->quit = true;
|
||||
}
|
||||
// we come here due to F9 or SQL_Quit event
|
||||
void Emulator::handleUserQuitRequest() {
|
||||
wxGetApp().CloseMainFrame();
|
||||
// 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 E2Config;
|
||||
|
||||
class Emulator
|
||||
{
|
||||
class Emulator {
|
||||
PaddleButtonStates paddleButtonStates;
|
||||
KeypressQueue keypresses;
|
||||
|
||||
@ -58,16 +57,14 @@ class Emulator
|
||||
bool pendingCommandExit;
|
||||
std::string cmdline;
|
||||
|
||||
void tick();
|
||||
|
||||
void dispatchKeypress(const SDL_KeyboardEvent& keyEvent);
|
||||
void dispatchKeyUp(const SDL_KeyboardEvent& keyEvent);
|
||||
void cmdKey(const SDL_KeyboardEvent& keyEvent);
|
||||
void processCommand();
|
||||
bool isSafeToQuit();
|
||||
|
||||
void handleRepeatKey();
|
||||
void handleAnyPendingEvents();
|
||||
void handleUserQuitRequest();
|
||||
|
||||
public:
|
||||
Emulator();
|
||||
@ -75,15 +72,14 @@ public:
|
||||
|
||||
void config(E2Config& cfg);
|
||||
|
||||
virtual void init();
|
||||
void init();
|
||||
|
||||
void powerOnComputer();
|
||||
void powerOffComputer();
|
||||
void toggleComputerPower();
|
||||
void cycleDisplayType();
|
||||
void quitIfSafe();
|
||||
bool isSafeToQuit();
|
||||
|
||||
virtual int run();
|
||||
void tick50ms();
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user