remove SDL event loop and use wx for keypresses instead; fix screen resizing

This commit is contained in:
Chris Mosher 2022-12-23 09:31:34 +09:00
parent 6d5e2d589a
commit f7fc5e47e9
8 changed files with 115 additions and 123 deletions

View File

@ -169,7 +169,6 @@ void E2wxFrame::OnPreferences(wxCommandEvent& event) {
dlg->ShowModal(); dlg->ShowModal();
dlg->Destroy(); dlg->Destroy();
} }
// TODO re-configure emulator here
wxGetApp().StartEmulator(); wxGetApp().StartEmulator();
} }

View File

@ -43,21 +43,27 @@
90 GOTO 5 90 GOTO 5
*/ */
static bool isKeyDown(const SDL_Keycode sym, const SDL_Keymod mod) {
static bool is_key_down(const wxKeyEvent& keyEvent) {
const int sym = keyEvent.GetKeyCode();
return ( return (
(sym < 0x7F || sym == SDLK_LEFT || sym == SDLK_RIGHT) && (sym < 0x7F || sym == WXK_LEFT || sym == WXK_RIGHT) &&
!(sym == SDLK_TAB || sym == SDLK_BACKQUOTE || sym == '[' || sym == '\\' || sym == SDLK_DELETE) && !(sym == WXK_TAB || sym == '`' || sym == '[' || sym == '\\' || sym == WXK_DELETE) &&
!(sym == ']' && mod & KMOD_SHIFT) !(sym == ']' && keyEvent.ShiftDown())
); );
} }
static bool translateKeysToAppleModernized(SDL_Keycode keycode, SDL_Keymod modifiers, unsigned char* key) { // Take real-world keystrokes from SDL and filter them to emulate the Apple ][ keyboard
if (keycode == SDLK_LEFT) { static bool translate_key(const wxKeyEvent& keyEvent, unsigned char* key) {
const int keycode = keyEvent.GetKeyCode();
if (keycode == WXK_LEFT) {
*key = 8; *key = 8;
return true; return true;
} }
if (keycode == SDLK_RIGHT) { if (keycode == WXK_RIGHT) {
*key = 21; *key = 21;
return true; return true;
} }
@ -75,56 +81,56 @@ static bool translateKeysToAppleModernized(SDL_Keycode keycode, SDL_Keymod modif
// from SDL 1.2 to 2.0, we can't use UNICODE so we need to // from SDL 1.2 to 2.0, we can't use UNICODE so we need to
// apply shift and control modifiers ourselves // apply shift and control modifiers ourselves
if (modifiers & KMOD_SHIFT) { if (keyEvent.ShiftDown()) {
if (keycode == SDLK_BACKQUOTE) *key = '~'; if (keycode == '`') *key = '~';
else if (keycode == SDLK_1) *key = '!'; else if (keycode == '1') *key = '!';
else if (keycode == SDLK_2) *key = '@'; else if (keycode == '2') *key = '@';
else if (keycode == SDLK_3) *key = '#'; else if (keycode == '3') *key = '#';
else if (keycode == SDLK_4) *key = '$'; else if (keycode == '4') *key = '$';
else if (keycode == SDLK_5) *key = '%'; else if (keycode == '5') *key = '%';
else if (keycode == SDLK_6) *key = '^'; else if (keycode == '6') *key = '^';
else if (keycode == SDLK_7) *key = '&'; else if (keycode == '7') *key = '&';
else if (keycode == SDLK_8) *key = '*'; else if (keycode == '8') *key = '*';
else if (keycode == SDLK_9) *key = '('; else if (keycode == '9') *key = '(';
else if (keycode == SDLK_0) *key = ')'; else if (keycode == '0') *key = ')';
else if (keycode == SDLK_MINUS) *key = '_'; else if (keycode == '-') *key = '_';
else if (keycode == SDLK_EQUALS) *key = '+'; else if (keycode == '=') *key = '+';
else if (keycode == SDLK_SEMICOLON) *key = ':'; else if (keycode == ';') *key = ':';
else if (keycode == SDLK_QUOTE) *key = '\"'; else if (keycode == '\'') *key = '\"';
else if (keycode == SDLK_COMMA) *key = '<'; else if (keycode == ',') *key = '<';
else if (keycode == SDLK_PERIOD) *key = '>'; else if (keycode == '.') *key = '>';
else if (keycode == SDLK_SLASH) *key = '?'; else if (keycode == '/') *key = '?';
else if (keycode == SDLK_m) *key = ']'; else if (keycode == 'M') *key = ']';
else if (keycode == SDLK_n) *key = '^'; else if (keycode == 'N') *key = '^';
else if (keycode == SDLK_p) *key = '@'; else if (keycode == 'P') *key = '@';
} }
if (modifiers & KMOD_CTRL) { if (keyEvent.RawControlDown()) {
if (('A' <= *key && *key <= 'Z') || (*key == ']') || (*key == '^') || (*key == '@')) { if (('A' <= *key && *key <= 'Z') || (*key == ']') || (*key == '^') || (*key == '@')) {
*key -= 64; *key -= 64;
} }
} }
if ((modifiers & KMOD_SHIFT) && (modifiers & KMOD_CTRL) && keycode == ' ') { if (keyEvent.ShiftDown() && keyEvent.RawControlDown() && keycode == ' ') {
// Ctrl-Shift-Space is the same as Space // Ctrl-Shift-Space is the same as Space
*key = ' '; *key = ' ';
} else if ((modifiers & KMOD_CTRL) && !(modifiers & KMOD_SHIFT) && (('0' <= keycode && keycode <= '9') || keycode == '/' || keycode == ' ')) { } else if (keyEvent.RawControlDown() && !keyEvent.ShiftDown() && (('0' <= keycode && keycode <= '9') || keycode == '/' || keycode == ' ')) {
// Control-only upon 0-9, / and space leaves them unchanged, the same as unmodified // Control-only upon 0-9, / and space leaves them unchanged, the same as unmodified
*key = keycode; *key = keycode;
} else if (keycode == ']') { } else if (keycode == ']') {
if (modifiers & KMOD_SHIFT) { if (keyEvent.ShiftDown()) {
// ignore '}' (shift ']') // ignore '}' (shift ']')
return false; return false;
} }
if (modifiers & KMOD_CTRL) { if (keyEvent.RawControlDown()) {
// Ctrl-] == ASCII: $1D // Ctrl-] == ASCII: $1D
*key = 29; *key = 29;
} }
} // else if this is one of the *keys that can't be typed on an Apple ][ keyboard } // else if this is one of the *keys that can't be typed on an Apple ][ keyboard
else if (*key == 0 || keycode == SDLK_TAB || keycode == SDLK_BACKQUOTE || keycode == '[' || keycode == '\\' || keycode == SDLK_DELETE) { else if (*key == 0 || keycode == WXK_TAB || keycode == '`' || keycode == '[' || keycode == '\\' || keycode == WXK_DELETE) {
return false; return false;
} }
@ -140,29 +146,26 @@ KeyEventHandler::~KeyEventHandler() {
} }
// Take real-world keystrokes from SDL and filter them to emulate the Apple ][ keyboard
void KeyEventHandler::dispatchKeyDown(const SDL_KeyboardEvent& keyEvent) { void KeyEventHandler::dispatchKeyDown(const wxKeyEvent& keyEvent) {
if (keyEvent.repeat) { if (keyEvent.IsAutoRepeat()) {
// To repeat on the real Apple ][, you need to use the REPT key (emulated by F10) // To repeat on the real Apple ][, you need to use the REPT key (emulated by F10)
return; return;
} }
const SDL_Keycode sym = keyEvent.keysym.sym; const int sym = keyEvent.GetKeyCode();
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)); //printf("keydown: mod: %04X sym: %08X scan:%04X name:%s\n", mod, sym, scan, SDL_GetKeyName(sym));
if (isKeyDown(sym, mod)) { if (is_key_down(keyEvent)) {
++this->keysDown; ++this->keysDown;
} }
if (sym == SDLK_F10) { if (sym == WXK_F10) {
this->repeater.press(); this->repeater.press();
// } else if (SDLK_F1 <= sym && sym <= SDLK_F12) {
// wxGetApp().OnFnKeyPressed(sym);
} else { } else {
unsigned char key; unsigned char key;
const bool sendKey = translateKeysToAppleModernized(sym, mod, &key); const bool sendKey = translate_key(keyEvent, &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);
@ -171,18 +174,17 @@ void KeyEventHandler::dispatchKeyDown(const SDL_KeyboardEvent& keyEvent) {
} }
} }
void KeyEventHandler::dispatchKeyUp(const SDL_KeyboardEvent& keyEvent) { void KeyEventHandler::dispatchKeyUp(const wxKeyEvent& keyEvent) {
const SDL_Keycode sym = keyEvent.keysym.sym; const int sym = keyEvent.GetKeyCode();
const SDL_Keymod mod = (SDL_Keymod)keyEvent.keysym.mod;
if (isKeyDown(sym, mod)) { if (is_key_down(keyEvent)) {
--this->keysDown; --this->keysDown;
if (this->keysDown <= 0) { if (this->keysDown <= 0) {
this->repeater.clearKey(); this->repeater.clearKey();
} }
} }
if (sym == SDLK_F10) { if (sym == WXK_F10) {
this->repeater.release(); this->repeater.release();
} }
} }

View File

@ -28,7 +28,7 @@
#include "KeyRepeatHandler.h" #include "KeyRepeatHandler.h"
#include "keyboard.h" #include "keyboard.h"
#include <SDL.h> #include <wx/event.h>
class KeyEventHandler { class KeyEventHandler {
int keysDown; int keysDown;
@ -40,8 +40,8 @@ public:
KeyEventHandler(KeypressQueue &keypresses, KeyRepeatHandler &repeater); KeyEventHandler(KeypressQueue &keypresses, KeyRepeatHandler &repeater);
virtual ~KeyEventHandler(); virtual ~KeyEventHandler();
void dispatchKeyDown(const SDL_KeyboardEvent& keyEvent); void dispatchKeyDown(const wxKeyEvent& keyEvent);
void dispatchKeyUp(const SDL_KeyboardEvent& keyEvent); void dispatchKeyUp(const wxKeyEvent& keyEvent);
}; };
#endif /* KEYEVENTHANDLER_H */ #endif /* KEYEVENTHANDLER_H */

View File

@ -327,6 +327,7 @@ void PreferencesDialog::OnRename(wxCommandEvent& evt) {
} }
} }
// TODO make two buttons: "Close & Restart" and just "Close"
void PreferencesDialog::OnCloseButton(wxCommandEvent& evt) { void PreferencesDialog::OnCloseButton(wxCommandEvent& evt) {
CTRL(wxTreeCtrl, treItems); CTRL(wxTreeCtrl, treItems);
const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection()); const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());

View File

@ -38,6 +38,7 @@
Emulator::Emulator() : Emulator::Emulator() :
screenImage(keyEventHandler),
display(screenImage), display(screenImage),
videoStatic(display), videoStatic(display),
apple2(keypresses, paddleButtonStates, display, buffered, screenImage), apple2(keypresses, paddleButtonStates, display, buffered, screenImage),
@ -60,31 +61,6 @@ void Emulator::config(E2Config& cfg) {
void Emulator::handleAnyPendingEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
// 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.
wxGetApp().CloseMainFrame();
break;
case SDL_KEYDOWN:
// we're collecting keypresses for the keyboard
// emulation (and thus the Apple ][ emulation itself)
this->keyEventHandler.dispatchKeyDown(event.key);
// People who have too many press-releases should be referred to as "keyboards"
break;
case SDL_KEYUP:
this->keyEventHandler.dispatchKeyUp(event.key);
break;
}
}
}
// How many emulation ticks between asking SDL if there is any new input // How many emulation ticks between asking SDL if there is any new input
// from the user or other GUI events. // from the user or other GUI events.
// This is also how often we shall update the estimate of the emulator's // This is also how often we shall update the estimate of the emulator's
@ -101,8 +77,6 @@ void Emulator::tick50ms() {
} }
} }
handleAnyPendingEvents();
this->screenImage.displayHz((1000*CHECK_EVERY_CYCLE)/(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();
} }
@ -228,8 +202,8 @@ void Emulator::toggleBuffered() {
} }
void Emulator::toggleFullScreen() { void Emulator::toggleFullScreen() {
this->screenImage.toggleFullScreen(); // this->screenImage.toggleFullScreen();
this->screenImage.drawPower(this->timable == &this->apple2); // this->screenImage.drawPower(this->timable == &this->apple2);
} }
void Emulator::screenshot() { void Emulator::screenshot() {

View File

@ -52,10 +52,6 @@ class Emulator {
void powerOnComputer(); void powerOnComputer();
void powerOffComputer(); void powerOffComputer();
void handleAnyPendingEvents();
void handleRepeatKey();
public: public:
Emulator(); Emulator();
virtual ~Emulator(); virtual ~Emulator();

View File

@ -64,62 +64,69 @@ static const int WIDTH = AppleNTSC::H - AppleNTSC::PIC_START - 2;
class ScreenException { class ScreenException {
}; };
ScreenImage::ScreenImage() : ScreenImage::ScreenImage(KeyEventHandler &k) :
wxFrame(nullptr/*wxGetApp().GetFrame()*/, wxID_ANY, "Emulator", wxDefaultPosition, wxDefaultSize, wxFrame(nullptr, wxID_ANY, "Emulator"),
wxSYSTEM_MENU | wxCLOSE_BOX | wxCAPTION | wxCLIP_CHILDREN),
fullscreen(false), fullscreen(false),
buffer(true), buffer(true),
display(AnalogTV::TV_OLD_COLOR), display(AnalogTV::TV_OLD_COLOR),
slotnames(8), slotnames(8),
cassInName(32, ' '), cassInName(32, ' '),
cassOutName(32, ' ') { cassOutName(32, ' '),
keyEventHandler(k) {
createScreen(); createScreen();
Show(); Show();
Bind(wxEVT_IDLE, &ScreenImage::OnIdle, this);
} }
ScreenImage::~ScreenImage() { ScreenImage::~ScreenImage() {
destroyScreen(); destroyScreen();
} }
wxBEGIN_EVENT_TABLE(ScreenImage, wxFrame)
wxEND_EVENT_TABLE()
void ScreenImage::exitFullScreen() {
if (this->fullscreen) { void ScreenImage::OnIdle(wxIdleEvent &evt) {
toggleFullScreen(); if (!this->FindFocus() || !this->sdl->HasFocus()) {
this->sdl->SetFocus();
} }
} }
void ScreenImage::exitFullScreen() {
// if (this->fullscreen) {
// toggleFullScreen();
// }
}
void ScreenImage::toggleFullScreen() { void ScreenImage::toggleFullScreen() {
this->fullscreen = !this->fullscreen; // this->fullscreen = !this->fullscreen;
const int flags = this->fullscreen ? SDL_WINDOW_FULLSCREEN : 0; // const int flags = this->fullscreen ? SDL_WINDOW_FULLSCREEN : 0;
SDL_SetWindowFullscreen(this->window, flags); // SDL_SetWindowFullscreen(this->window, flags);
} }
void ScreenImage::createScreen() { void ScreenImage::createScreen() {
this->sdl = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(SCRW,SCRH*ASPECT_RATIO));
createSdlTexture();
this->sdl->Bind(wxEVT_KEY_DOWN, &ScreenImage::OnKeyDown, this);
this->sdl->Bind(wxEVT_KEY_UP, &ScreenImage::OnKeyUp, this);
wxSizer *pszr = new wxBoxSizer(wxVERTICAL); wxSizer *pszr = new wxBoxSizer(wxVERTICAL);
pszr->Add(this->sdl);
this->panelTop = new wxPanel(this);
// TODO why won't shaped work? (Disabled frame resize as a workaround, for now.)
pszr->Add(this->panelTop, wxSizerFlags(0).Expand().Shaped().Center());
wxPanel *panelSdl = new wxPanel(this->panelTop, wxID_ANY, wxDefaultPosition, wxSize(SCRW,SCRH*ASPECT_RATIO));
createSdlTexture(panelSdl);
drawLabels();
notifyObservers();
SetSizer(pszr); SetSizer(pszr);
pszr->SetSizeHints(this); pszr->SetSizeHints(this);
this->pixels = (unsigned int*) calloc(SCRW * SCRH, sizeof (unsigned int));
this->screen_pitch = SCRW;
drawLabels();
notifyObservers();
} }
void ScreenImage::createSdlTexture(wxPanel *panelSdl) { void ScreenImage::createSdlTexture() {
WXWidget nativeSdl = panelSdl->GetHandle(); WXWidget nativeSdl = this->sdl->GetHandle();
// TODO: do we need special gtk handling here, to get xid using: // TODO: do we need special gtk handling here, to get xid using:
// GtkWidget* widget = panel->GetHandle(); // GtkWidget* widget = panel->GetHandle();
// gtk_widget_realize(widget); // gtk_widget_realize(widget);
// Window xid = GDK_WINDOW_XWINDOW(widget->window); // Window xid = GDK_WINDOW_XWINDOW(widget->window);
this->window = SDL_CreateWindowFrom(static_cast<void*>(nativeSdl)); this->window = SDL_CreateWindowFrom(static_cast<void*>(nativeSdl));
if (this->window == NULL) { if (this->window == NULL) {
printf("Unable to create window: %s\n", SDL_GetError()); printf("Unable to create window: %s\n", SDL_GetError());
@ -139,9 +146,6 @@ void ScreenImage::createSdlTexture(wxPanel *panelSdl) {
std::cerr << SDL_GetError() << std::endl; std::cerr << SDL_GetError() << std::endl;
throw ScreenException(); throw ScreenException();
} }
this->pixels = (unsigned int*) calloc(SCRW * SCRH, sizeof (unsigned int));
this->screen_pitch = SCRW;
} }
void ScreenImage::destroyScreen() { void ScreenImage::destroyScreen() {
@ -320,25 +324,26 @@ void ScreenImage::drawPower(bool on) {
} }
void ScreenImage::notifyObservers() { void ScreenImage::notifyObservers() {
const int e = SDL_UpdateTexture(this->texture, NULL, this->pixels, SCRW * sizeof (unsigned int)); const int e = SDL_UpdateTexture(this->texture, NULL, this->pixels, SCRW*sizeof(unsigned int));
if (e) { if (e) {
std::cerr << SDL_GetError() << std::endl; std::cerr << SDL_GetError() << std::endl;
} }
SDL_RenderClear(this->renderer); SDL_RenderClear(this->renderer);
SDL_RenderCopy(this->renderer,this->texture,NULL,NULL); SDL_RenderCopy(this->renderer, this->texture, NULL, NULL);
SDL_RenderPresent(this->renderer); SDL_RenderPresent(this->renderer);
SDL_RenderSetLogicalSize(this->renderer, SCRW, SCRH*ASPECT_RATIO);
} }
void ScreenImage::setElem(unsigned int i, const unsigned int val) { void ScreenImage::setElem(unsigned int i, const unsigned int val) {
unsigned int* pn = this->pixels; unsigned int* pn = this->pixels;
i += (i / WIDTH)*(SCRW - WIDTH); i += (i/WIDTH)*(SCRW-WIDTH);
pn += i; pn += i;
*pn = val; *pn = val;
} }
void ScreenImage::blank() { void ScreenImage::blank() {
for (int r = 0; r < HEIGHT; ++r) { for (int r = 0; r < HEIGHT; ++r) {
memset((char*) (this->pixels) + r * SCRW * 4, 0, WIDTH * 4); memset((char*)(this->pixels)+r*SCRW*4, 0, WIDTH*4);
} }
} }
@ -539,3 +544,11 @@ void ScreenImage::saveBMP() {
SDL_SaveBMP(screenshot, time); SDL_SaveBMP(screenshot, time);
SDL_FreeSurface(screenshot); SDL_FreeSurface(screenshot);
} }
void ScreenImage::OnKeyDown(wxKeyEvent &evt) {
this->keyEventHandler.dispatchKeyDown(evt);
}
void ScreenImage::OnKeyUp(wxKeyEvent &evt) {
this->keyEventHandler.dispatchKeyUp(evt);
}

View File

@ -18,10 +18,13 @@
#ifndef SCREENIMAGE_H #ifndef SCREENIMAGE_H
#define SCREENIMAGE_H #define SCREENIMAGE_H
#include "keyboard.h"
#include "analogtv.h" #include "analogtv.h"
#include "KeyEventHandler.h"
#include <wx/frame.h> #include <wx/frame.h>
#include <wx/panel.h> #include <wx/panel.h>
#include <wx/event.h>
#include <filesystem> #include <filesystem>
#include <vector> #include <vector>
@ -34,7 +37,7 @@ struct SDL_Window;
class ScreenImage : public wxFrame { class ScreenImage : public wxFrame {
private: private:
wxPanel *panelTop; wxPanel *sdl;
SDL_Window* window; SDL_Window* window;
SDL_Renderer* renderer; SDL_Renderer* renderer;
SDL_Texture* texture; SDL_Texture* texture;
@ -44,19 +47,23 @@ private:
bool buffer; bool buffer;
AnalogTV::DisplayType display; AnalogTV::DisplayType display;
void createScreen(); void createScreen();
void createSdlTexture(wxPanel *panelSdl); void createSdlTexture();
void destroyScreen(); void destroyScreen();
std::vector<std::string> slotnames; std::vector<std::string> slotnames;
std::string cassInName; std::string cassInName;
std::string cassOutName; std::string cassOutName;
KeyEventHandler &keyEventHandler;
static std::string truncateFilePath(const std::filesystem::path& filepath); static std::string truncateFilePath(const std::filesystem::path& filepath);
wxDECLARE_EVENT_TABLE(); void OnIdle(wxIdleEvent &evt);
void OnKeyDown(wxKeyEvent &evt);
void OnKeyUp(wxKeyEvent &evt);
// TODO some of these methods should be private // TODO some of these methods should be private
public: public:
ScreenImage(); ScreenImage(KeyEventHandler &keyEventHandler);
~ScreenImage(); ~ScreenImage();
void exitFullScreen(); void exitFullScreen();