From 498edf22e1f7fd7500d230be5a1f68b1e320fc03 Mon Sep 17 00:00:00 2001 From: "Christopher A. Mosher" Date: Mon, 12 Dec 2022 21:47:10 -0500 Subject: [PATCH] Move FN key handling from SDL to main App, and add menu items for them --- src/E2wxApp.cpp | 72 ++++++++++++- src/E2wxApp.h | 11 ++ src/E2wxFrame.cpp | 93 +++++++++++++---- src/E2wxFrame.h | 21 ++-- src/emulator.cpp | 252 ++++++++++++++++++++++------------------------ src/emulator.h | 15 +-- 6 files changed, 298 insertions(+), 166 deletions(-) diff --git a/src/E2wxApp.cpp b/src/E2wxApp.cpp index a6473a9..eeedc66 100644 --- a/src/E2wxApp.cpp +++ b/src/E2wxApp.cpp @@ -28,6 +28,8 @@ #include "e2const.h" #include +#include +#include #include #include #include @@ -201,6 +203,34 @@ bool E2wxApp::OnInit() { +void E2wxApp::OnFnKeyPressed(const SDL_Keycode k) { + if (k == SDLK_F1) { + this->TogglePower(); + } else if (k == SDLK_F2) { + this->CycleMonitor(); + } else if (k == SDLK_F3) { + this->ToggleFullScreen(); + } else if (k == SDLK_F4) { + // + } else if (k == SDLK_F5) { + // + } else if (k == SDLK_F6) { + this->Reset(); + } else if (k == SDLK_F7) { + this->Paste(); + } else if (k == SDLK_F8) { + this->ScreenShot(); + } else if (k == SDLK_F9) { + this->CloseMainFrame(); + } else if (k == SDLK_F10) { + // + } else if (k == SDLK_F11) { + // + } else if (k == SDLK_F12) { + this->ToggleBuffered(); + } +} + bool E2wxApp::CloseMainFrame() { bool r = false; if (this->frame) { @@ -365,6 +395,8 @@ void E2wxApp::StartEmulator() { this->emu_timer->begin(); } + + bool E2wxApp::EnsureCanQuit() { bool ok = true; if (this->emu) { @@ -375,6 +407,42 @@ bool E2wxApp::EnsureCanQuit() { void E2wxApp::Paste() { if (this->emu) { - this->emu->handlePaste(); + this->emu->paste(); } -} \ No newline at end of file +} + +void E2wxApp::CycleMonitor() { + if (this->emu) { + this->emu->monitorCycle(); + } +} + +void E2wxApp::ScreenShot() { + if (this->emu) { + this->emu->screenshot(); + } +} + +void E2wxApp::Reset() { + if (this->emu) { + this->emu->reset(); + } +} + +void E2wxApp::TogglePower() { + if (this->emu) { + this->emu->toggleComputerPower(); + } +} + +void E2wxApp::ToggleBuffered() { + if (this->emu) { + this->emu->toggleBuffered(); + } +} + +void E2wxApp::ToggleFullScreen() { + if (this->emu) { + this->emu->toggleFullScreen(); + } +} diff --git a/src/E2wxApp.h b/src/E2wxApp.h index 53361cc..c73405d 100644 --- a/src/E2wxApp.h +++ b/src/E2wxApp.h @@ -23,10 +23,13 @@ +#include + #include #include #include #include + #include #include @@ -81,9 +84,17 @@ public: const std::filesystem::path GetConfigDir() const; const std::filesystem::path GetDocumentsDir() const; + void OnFnKeyPressed(const SDL_Keycode k); + bool CloseMainFrame(); bool EnsureCanQuit(); void Paste(); + void CycleMonitor(); + void ScreenShot(); + void Reset(); + void TogglePower(); + void ToggleBuffered(); + void ToggleFullScreen(); virtual bool OnInit() override; virtual int OnExit() override; diff --git a/src/E2wxFrame.cpp b/src/E2wxFrame.cpp index 71a3030..1384773 100644 --- a/src/E2wxFrame.cpp +++ b/src/E2wxFrame.cpp @@ -30,16 +30,26 @@ enum E2MenuID { - ID_MENUITEM_POWER = wxID_HIGHEST+1 + ID_MENUITEM_POWER = wxID_HIGHEST+1, + ID_MENUITEM_CYCLE_MONITOR, + ID_MENUITEM_TOGGLE_FULL_SCREEN, + ID_MENUITEM_RESET, + ID_MENUITEM_SCREEN_SHOT, + ID_MENUITEM_TOGGLE_BUFFERED, }; wxBEGIN_EVENT_TABLE(E2wxFrame, wxFrame) + EVT_CLOSE(E2wxFrame::HandleUserQuitRequest) EVT_MENU(wxID_EXIT, E2wxFrame::OnExit) - EVT_MENU(wxID_PASTE, E2wxFrame::OnPaste) EVT_MENU(wxID_PREFERENCES, E2wxFrame::OnPreferences) EVT_MENU(wxID_ABOUT, E2wxFrame::OnAbout) EVT_MENU(ID_MENUITEM_POWER, E2wxFrame::OnTogglePower) - EVT_CLOSE(E2wxFrame::HandleUserQuitRequest) + EVT_MENU(ID_MENUITEM_CYCLE_MONITOR, E2wxFrame::OnCycleMonitor) + EVT_MENU(ID_MENUITEM_TOGGLE_FULL_SCREEN, E2wxFrame::OnToggleFullScreen) + EVT_MENU(ID_MENUITEM_RESET, E2wxFrame::OnReset) + EVT_MENU(wxID_PASTE, E2wxFrame::OnPaste) + EVT_MENU(ID_MENUITEM_SCREEN_SHOT, E2wxFrame::OnScreenShot) + EVT_MENU(ID_MENUITEM_TOGGLE_BUFFERED, E2wxFrame::OnToggleBuffered) wxEND_EVENT_TABLE() @@ -50,6 +60,8 @@ E2wxFrame::E2wxFrame() : wxFrame(nullptr, wxID_ANY, "epple2") { E2wxFrame::~E2wxFrame() { } + + void E2wxFrame::DoInit() { InitMenuBar(); InitStatusBar(); @@ -74,12 +86,29 @@ void E2wxFrame::InitMenuBar() { wxMenuItem *miPaste = menuEdit->Append(wxID_PASTE); miPaste->AddExtraAccel(wxAcceleratorEntry(wxACCEL_NORMAL, WXK_F7)); menuEdit->AppendSeparator(); - menuEdit->Append(wxID_PREFERENCES); + wxMenuItem *miPrefs = menuEdit->Append(wxID_PREFERENCES); + miPrefs->SetAccel(new wxAcceleratorEntry(wxACCEL_CTRL, ',')); + wxMenu *menuMachine = new wxMenu(); menuBar->Append(menuMachine, "&Machine"); wxMenuItem *miPower = menuMachine->Append(ID_MENUITEM_POWER, "Toggle Power"); miPower->SetAccel(new wxAcceleratorEntry(wxACCEL_NORMAL, WXK_F1)); + wxMenuItem *miReset = menuMachine->Append(ID_MENUITEM_RESET, "Reset"); + miReset->SetAccel(new wxAcceleratorEntry(wxACCEL_NORMAL, WXK_F6)); + menuMachine->AppendSeparator(); + wxMenuItem *miBuffered = menuMachine->Append(ID_MENUITEM_TOGGLE_BUFFERED, "Toggle Keyboard Buffer"); + miBuffered->SetAccel(new wxAcceleratorEntry(wxACCEL_NORMAL, WXK_F12)); + + wxMenu *menuMonitor = new wxMenu(); + menuBar->Append(menuMonitor, "Mo&nitor"); + wxMenuItem *miCycleMonitor = menuMonitor->Append(ID_MENUITEM_CYCLE_MONITOR, "Cycle Type"); + miCycleMonitor->SetAccel(new wxAcceleratorEntry(wxACCEL_NORMAL, WXK_F2)); + wxMenuItem *miToggleFullScreen = menuMonitor->Append(ID_MENUITEM_TOGGLE_FULL_SCREEN, "Toggle Full Screen"); + miToggleFullScreen->SetAccel(new wxAcceleratorEntry(wxACCEL_NORMAL, WXK_F3)); + menuMonitor->AppendSeparator(); + wxMenuItem *miScreenShot = menuMonitor->Append(ID_MENUITEM_SCREEN_SHOT, "Screen Shot!"); + miScreenShot->SetAccel(new wxAcceleratorEntry(wxACCEL_NORMAL, WXK_F8)); wxMenu *menuHelp = new wxMenu(); menuBar->Append(menuHelp, "&Help"); @@ -93,10 +122,27 @@ void E2wxFrame::InitStatusBar() { +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(); + } +} + + + void E2wxFrame::OnExit(wxCommandEvent& event) { Close(false); } +void E2wxFrame::OnPreferences(wxCommandEvent& event) { + PreferencesDialog *dlg = new PreferencesDialog(this); + dlg->OnInit(); + dlg->ShowModal(); +} + void E2wxFrame::OnAbout(wxCommandEvent& event) { wxString msg = ""; @@ -110,25 +156,34 @@ void E2wxFrame::OnAbout(wxCommandEvent& event) { wxMessageBox(msg, "About "+wxGetApp().GetID(), wxOK | wxICON_INFORMATION); } -void E2wxFrame::OnPaste(wxCommandEvent& event) { - wxGetApp().Paste(); -} -void E2wxFrame::OnPreferences(wxCommandEvent& event) { - PreferencesDialog *dlg = new PreferencesDialog(this); - dlg->OnInit(); - dlg->ShowModal(); -} 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(); - } +void E2wxFrame::OnCycleMonitor(wxCommandEvent& event) { + wxGetApp().CycleMonitor(); } + +void E2wxFrame::OnToggleFullScreen(wxCommandEvent& event) { + wxGetApp().ToggleFullScreen(); +} + +void E2wxFrame::OnReset(wxCommandEvent& event) { + wxGetApp().Reset(); +} + +void E2wxFrame::OnPaste(wxCommandEvent& event) { + wxGetApp().Paste(); +} + +void E2wxFrame::OnScreenShot(wxCommandEvent& event) { + wxGetApp().ScreenShot(); +} + +void E2wxFrame::OnToggleBuffered(wxCommandEvent& event) { + wxGetApp().ToggleBuffered(); +} + + diff --git a/src/E2wxFrame.h b/src/E2wxFrame.h index f3ca51b..57fd78a 100644 --- a/src/E2wxFrame.h +++ b/src/E2wxFrame.h @@ -33,16 +33,23 @@ public: void DoInit(); private: - void OnExit(wxCommandEvent& event); - void OnPaste(wxCommandEvent& event); - void OnPreferences(wxCommandEvent& event); - void OnAbout(wxCommandEvent& event); - void OnTogglePower(wxCommandEvent& event); - void HandleUserQuitRequest(wxCloseEvent& event); - void InitMenuBar(); void InitStatusBar(); + void HandleUserQuitRequest(wxCloseEvent& event); + + void OnExit(wxCommandEvent& event); + void OnPreferences(wxCommandEvent& event); + void OnAbout(wxCommandEvent& event); + + void OnTogglePower(wxCommandEvent& event); + void OnCycleMonitor(wxCommandEvent& event); + void OnToggleFullScreen(wxCommandEvent& event); + void OnReset(wxCommandEvent& event); + void OnPaste(wxCommandEvent& event); + void OnScreenShot(wxCommandEvent& event); + void OnToggleBuffered(wxCommandEvent& event); + wxDECLARE_EVENT_TABLE(); }; diff --git a/src/emulator.cpp b/src/emulator.cpp index 5776787..a760407 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -27,48 +27,39 @@ #include + + + + + +static bool isKeyDown(const SDL_Keycode sym, const SDL_Keymod mod) { + return ( + (sym < 0x7F || sym == SDLK_LEFT || sym == SDLK_RIGHT) && + !(sym == SDLK_TAB || sym == SDLK_BACKQUOTE || sym == '[' || sym == '\\' || sym == SDLK_DELETE) && + !(sym == ']' && mod & KMOD_SHIFT) + ); +} + + + + + Emulator::Emulator() : -display(screenImage), -videoStatic(display), -apple2(keypresses, paddleButtonStates, display, fhyper, buffered, screenImage), -timable(0), // No ticked object (NULL pointer) -quit(false), -repeat(false), -keysDown(0), -skip(0), -prev_ms(SDL_GetTicks()), -command(false), -pendingCommandExit(false) { + display(screenImage), + videoStatic(display), + apple2(keypresses, paddleButtonStates, display, fhyper, buffered, screenImage), + timable(nullptr), // No ticked object (NULL pointer) + repeat(false), + keysDown(0), + prev_ms(SDL_GetTicks()), + command(false), + pendingCommandExit(false) { } Emulator::~Emulator() { } -void Emulator::toggleComputerPower() { - if (this->timable == &this->videoStatic) - powerOnComputer(); - else - powerOffComputer(); -} -void Emulator::powerOnComputer() { - this->apple2.powerOn(); - this->screenImage.drawPower(true); - this->display.setNoise(false); - - // The apple2 becomes the ticked object - this->timable = &this->apple2; -} - -void Emulator::powerOffComputer() { - this->apple2.powerOff(); - this->screenImage.drawPower(false); - this->display.setNoise(true); - this->videoStatic.powerOn(); - - // The video static becomes the ticked object - this->timable = &this->videoStatic; -} void Emulator::config(E2Config& cfg) { cfg.parse(this->apple2.ram, this->apple2.rom, this->apple2.slts, this->apple2.revision, this->screenImage, this->apple2.cassetteIn, this->apple2.cassetteOut, &this->apple2); @@ -112,7 +103,7 @@ void Emulator::handleAnyPendingEvents() { // If SDL is going away... // 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(); + wxGetApp().CloseMainFrame(); break; case SDL_KEYDOWN: // If we're collecting a command line for changing any @@ -122,7 +113,7 @@ void Emulator::handleAnyPendingEvents() { else // ...else we're collecting keypresses for the keyboard // emulation (and thus the Apple ][ emulation itself) - dispatchKeypress(event.key); + dispatchKeyDown(event.key); // People who have too many press-releases should be referred to as "keyboards" break; case SDL_KEYUP: @@ -164,27 +155,10 @@ void Emulator::tick50ms() { handleAnyPendingEvents(); this->screenImage.displayHz((1000*CHECK_EVERY_CYCLE)/(SDL_GetTicks() - this->prev_ms)); this->prev_ms = SDL_GetTicks(); - // TODO: how to check this->quit ? } -void Emulator::dispatchKeyUp(const SDL_KeyboardEvent& keyEvent) { - SDL_Keycode sym = keyEvent.keysym.sym; - SDL_Keymod mod = (SDL_Keymod) keyEvent.keysym.mod; - - if ((sym < 0x7F || sym == SDLK_LEFT || sym == SDLK_RIGHT) && - !(sym == SDLK_TAB || sym == SDLK_BACKQUOTE || sym == '[' || sym == '\\' || sym == SDLK_DELETE) && - !(sym == ']' && mod & KMOD_SHIFT)) { - --this->keysDown; - }// ...else if this is the emulated REPT key on the Apple keyboard... - else if (sym == SDLK_F10) { - // ...stop repeating. The key has been released - this->repeat = false; - this->rept = 0; - } -} - static bool translateKeysToAppleModernized(SDL_Keycode keycode, SDL_Keymod modifiers, unsigned char* key) { if (keycode == SDLK_LEFT) { *key = 8; @@ -264,101 +238,38 @@ static bool translateKeysToAppleModernized(SDL_Keycode keycode, SDL_Keymod modif return true; } - - -// Take real-world keystrokes from SDL and filter them to emulate the -// Apple ][ or Apple ][ plus keyboard - -void Emulator::dispatchKeypress(const SDL_KeyboardEvent& keyEvent) { +// Take real-world keystrokes from SDL and filter them to emulate the Apple ][ keyboard +void Emulator::dispatchKeyDown(const SDL_KeyboardEvent& keyEvent) { if (keyEvent.repeat) { return; } - SDL_Keycode sym = keyEvent.keysym.sym; - SDL_Keymod mod = (SDL_Keymod) keyEvent.keysym.mod; + const SDL_Keycode sym = keyEvent.keysym.sym; + const SDL_Keymod mod = (SDL_Keymod) keyEvent.keysym.mod; //printf("keydown: mod: %04X sym: %08X scan:%04X name:%s\n", mod, sym, scan, SDL_GetKeyName(sym)); - if ((sym < 0x7F || sym == SDLK_LEFT || sym == SDLK_RIGHT) && - !(sym == SDLK_TAB || sym == SDLK_BACKQUOTE || sym == '[' || sym == '\\' || sym == SDLK_DELETE) && - !(sym == ']' && mod & KMOD_SHIFT)) { + if (isKeyDown(sym, mod)) { ++this->keysDown; } - - - /* - * First handle the key presses that are for commands, as opposed to - * simple key-presses that we send to the apple. - */ - // TODO remove all function key handling from here, and - // instead redirect them to the main frame, which will call back - // our appropriate handler functions - // Note: REPT key is special (need to track key-up event) - if (sym == SDLK_F6) { - this->apple2.reset(); - return; - } else if (sym == SDLK_F7) { - handlePaste(); - return; - }// ...else if this is the emulated REPT key on the Apple keyboard... - else if (sym == SDLK_F10) { + if (sym == SDLK_F10) { // ...start auto-repeat this->repeat = true; this->rept = CYCLES_PER_REPT; return; - }// ...else if the user wants to run at full speed instead of emulating - // the Apple's speed... - else if (sym == SDLK_F11) { // TODO remove hyper - this->fhyper.toggleHyper(); - this->screenImage.toggleHyperLabel(); - return; - } else if (sym == SDLK_F12) { - this->buffered.toggleBuffered(); - this->screenImage.toggleKdbBufferLabel(); - return; - }// ...else if the user has hit the rocker switch on the back of the Apple... - else if (sym == SDLK_F1) { - toggleComputerPower(); - return; - }// ...else if the user wants to look at a different video display medium... - else if (sym == SDLK_F2) { - this->display.cycleType(); - this->screenImage.cycleDisplayLabel(); - return; - }// ...else if the user wants to switch to/from full screen and an - // individual application window... - else if (sym == SDLK_F3) { - this->screenImage.toggleFullScreen(); - this->screenImage.drawPower(this->timable == &this->apple2); - return; - }// ...else if the user wants to switch between the interlaced extension - // of the display and the non-interlaced historically correct display... - else if (sym == SDLK_F4) { // TODO remove bleed-down - this->display.toggleBleedDown(); - this->screenImage.toggleFillLinesLabel(); - return; - }// ...else initiate command line entry at the bottom of the emulator window - else if (sym == SDLK_F5) { // TODO re-do user-entered command line + } else if (sym == SDLK_F5) { // TODO re-do user-entered command line this->command = true; this->screenImage.enterCommandMode(); return; - }// ...else exit the entire emulation - else if (sym == SDLK_F9) { - handleUserQuitRequest(); - return; - }// ...else save a screen shot - else if (sym == SDLK_F8) { - this->screenImage.saveBMP(); + } 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); @@ -366,10 +277,27 @@ void Emulator::dispatchKeypress(const SDL_KeyboardEvent& keyEvent) { } } +void Emulator::dispatchKeyUp(const SDL_KeyboardEvent& keyEvent) { + const SDL_Keycode sym = keyEvent.keysym.sym; + const SDL_Keymod mod = (SDL_Keymod) keyEvent.keysym.mod; -// Collect and edit a command line typed at the bottom of the emulator -// window + if (isKeyDown(sym, mod)) { + --this->keysDown; + } else if (sym == SDLK_F10) { + // ...else if this is the emulated REPT key on the Apple keyboard... + // ...stop repeating. The key has been released + this->repeat = false; + this->rept = 0; + } +} + + + + + +// TODO redo command line handling +// Collect and edit a command line typed at the bottom of the emulator window void Emulator::cmdKey(const SDL_KeyboardEvent& keyEvent) { SDL_Keycode sym = keyEvent.keysym.sym; unsigned char key = (unsigned char) (sym & 0x7F); @@ -416,6 +344,14 @@ void Emulator::processCommand() { cmdline.erase(cmdline.begin(), cmdline.end()); } + + + + + + + + static int askSave() { wxMessageDialog *dlg = new wxMessageDialog{ nullptr, @@ -455,11 +391,42 @@ bool Emulator::isSafeToQuit() { return true; } -void Emulator::handleUserQuitRequest() { - wxGetApp().CloseMainFrame(); + + + + + + + + + +void Emulator::toggleComputerPower() { + if (this->timable == &this->videoStatic) + powerOnComputer(); + else + powerOffComputer(); } -void Emulator::handlePaste() { +void Emulator::powerOnComputer() { + this->apple2.powerOn(); + this->screenImage.drawPower(true); + this->display.setNoise(false); + + // The apple2 becomes the ticked object + this->timable = &this->apple2; +} + +void Emulator::powerOffComputer() { + this->apple2.powerOff(); + this->screenImage.drawPower(false); + this->display.setNoise(true); + this->videoStatic.powerOn(); + + // The video static becomes the ticked object + this->timable = &this->videoStatic; +} + +void Emulator::paste() { // Feed input from the clipboard to the Apple keyboard std::string s = this->clip.getText(); for (unsigned int i = 0; i < s.length(); ++i) { @@ -473,3 +440,26 @@ void Emulator::handlePaste() { this->keypresses.push(key); } } + +void Emulator::monitorCycle() { + this->display.cycleType(); + this->screenImage.cycleDisplayLabel(); +} + +void Emulator::reset() { + this->apple2.reset(); +} + +void Emulator::toggleBuffered() { + this->buffered.toggleBuffered(); + this->screenImage.toggleKdbBufferLabel(); +} + +void Emulator::toggleFullScreen() { + this->screenImage.toggleFullScreen(); + this->screenImage.drawPower(this->timable == &this->apple2); +} + +void Emulator::screenshot() { + this->screenImage.saveBMP(); +} diff --git a/src/emulator.h b/src/emulator.h index fd29c0e..5ce0fbf 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -46,18 +46,16 @@ class Emulator { Timable* timable; - bool quit; bool repeat; int keysDown; int rept; unsigned char lastKeyDown; - int skip; Uint32 prev_ms; bool command; bool pendingCommandExit; std::string cmdline; - void dispatchKeypress(const SDL_KeyboardEvent& keyEvent); + void dispatchKeyDown(const SDL_KeyboardEvent& keyEvent); void dispatchKeyUp(const SDL_KeyboardEvent& keyEvent); void cmdKey(const SDL_KeyboardEvent& keyEvent); void processCommand(); @@ -66,19 +64,22 @@ class Emulator { void handleRepeatKey(); void handleAnyPendingEvents(); - void handleUserQuitRequest(); public: Emulator(); virtual ~Emulator(); void config(E2Config& cfg); - void tick50ms(); + bool isSafeToQuit(); void toggleComputerPower(); - void handlePaste(); - bool isSafeToQuit(); + void monitorCycle(); + void paste(); + void reset(); + void toggleBuffered(); + void toggleFullScreen(); + void screenshot(); }; #endif