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->Destroy();
}
// TODO re-configure emulator here
wxGetApp().StartEmulator();
}

View File

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

View File

@ -28,7 +28,7 @@
#include "KeyRepeatHandler.h"
#include "keyboard.h"
#include <SDL.h>
#include <wx/event.h>
class KeyEventHandler {
int keysDown;
@ -40,8 +40,8 @@ public:
KeyEventHandler(KeypressQueue &keypresses, KeyRepeatHandler &repeater);
virtual ~KeyEventHandler();
void dispatchKeyDown(const SDL_KeyboardEvent& keyEvent);
void dispatchKeyUp(const SDL_KeyboardEvent& keyEvent);
void dispatchKeyDown(const wxKeyEvent& keyEvent);
void dispatchKeyUp(const wxKeyEvent& keyEvent);
};
#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) {
CTRL(wxTreeCtrl, treItems);
const TreeItemData *data = (TreeItemData*)treItems->GetItemData(treItems->GetSelection());

View File

@ -38,6 +38,7 @@
Emulator::Emulator() :
screenImage(keyEventHandler),
display(screenImage),
videoStatic(display),
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
// from the user or other GUI events.
// 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->prev_ms = SDL_GetTicks();
}
@ -228,8 +202,8 @@ void Emulator::toggleBuffered() {
}
void Emulator::toggleFullScreen() {
this->screenImage.toggleFullScreen();
this->screenImage.drawPower(this->timable == &this->apple2);
// this->screenImage.toggleFullScreen();
// this->screenImage.drawPower(this->timable == &this->apple2);
}
void Emulator::screenshot() {

View File

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

View File

@ -64,62 +64,69 @@ static const int WIDTH = AppleNTSC::H - AppleNTSC::PIC_START - 2;
class ScreenException {
};
ScreenImage::ScreenImage() :
wxFrame(nullptr/*wxGetApp().GetFrame()*/, wxID_ANY, "Emulator", wxDefaultPosition, wxDefaultSize,
wxSYSTEM_MENU | wxCLOSE_BOX | wxCAPTION | wxCLIP_CHILDREN),
ScreenImage::ScreenImage(KeyEventHandler &k) :
wxFrame(nullptr, wxID_ANY, "Emulator"),
fullscreen(false),
buffer(true),
display(AnalogTV::TV_OLD_COLOR),
slotnames(8),
cassInName(32, ' '),
cassOutName(32, ' ') {
cassOutName(32, ' '),
keyEventHandler(k) {
createScreen();
Show();
Bind(wxEVT_IDLE, &ScreenImage::OnIdle, this);
}
ScreenImage::~ScreenImage() {
destroyScreen();
}
wxBEGIN_EVENT_TABLE(ScreenImage, wxFrame)
wxEND_EVENT_TABLE()
void ScreenImage::exitFullScreen() {
if (this->fullscreen) {
toggleFullScreen();
void ScreenImage::OnIdle(wxIdleEvent &evt) {
if (!this->FindFocus() || !this->sdl->HasFocus()) {
this->sdl->SetFocus();
}
}
void ScreenImage::exitFullScreen() {
// if (this->fullscreen) {
// toggleFullScreen();
// }
}
void ScreenImage::toggleFullScreen() {
this->fullscreen = !this->fullscreen;
const int flags = this->fullscreen ? SDL_WINDOW_FULLSCREEN : 0;
SDL_SetWindowFullscreen(this->window, flags);
// this->fullscreen = !this->fullscreen;
// const int flags = this->fullscreen ? SDL_WINDOW_FULLSCREEN : 0;
// SDL_SetWindowFullscreen(this->window, flags);
}
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);
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();
pszr->Add(this->sdl);
SetSizer(pszr);
pszr->SetSizeHints(this);
this->pixels = (unsigned int*) calloc(SCRW * SCRH, sizeof (unsigned int));
this->screen_pitch = SCRW;
drawLabels();
notifyObservers();
}
void ScreenImage::createSdlTexture(wxPanel *panelSdl) {
WXWidget nativeSdl = panelSdl->GetHandle();
void ScreenImage::createSdlTexture() {
WXWidget nativeSdl = this->sdl->GetHandle();
// TODO: do we need special gtk handling here, to get xid using:
// GtkWidget* widget = panel->GetHandle();
// gtk_widget_realize(widget);
// Window xid = GDK_WINDOW_XWINDOW(widget->window);
this->window = SDL_CreateWindowFrom(static_cast<void*>(nativeSdl));
if (this->window == NULL) {
printf("Unable to create window: %s\n", SDL_GetError());
@ -139,9 +146,6 @@ void ScreenImage::createSdlTexture(wxPanel *panelSdl) {
std::cerr << SDL_GetError() << std::endl;
throw ScreenException();
}
this->pixels = (unsigned int*) calloc(SCRW * SCRH, sizeof (unsigned int));
this->screen_pitch = SCRW;
}
void ScreenImage::destroyScreen() {
@ -320,25 +324,26 @@ void ScreenImage::drawPower(bool on) {
}
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) {
std::cerr << SDL_GetError() << std::endl;
}
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_RenderSetLogicalSize(this->renderer, SCRW, SCRH*ASPECT_RATIO);
}
void ScreenImage::setElem(unsigned int i, const unsigned int val) {
unsigned int* pn = this->pixels;
i += (i / WIDTH)*(SCRW - WIDTH);
i += (i/WIDTH)*(SCRW-WIDTH);
pn += i;
*pn = val;
}
void ScreenImage::blank() {
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_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
#define SCREENIMAGE_H
#include "keyboard.h"
#include "analogtv.h"
#include "KeyEventHandler.h"
#include <wx/frame.h>
#include <wx/panel.h>
#include <wx/event.h>
#include <filesystem>
#include <vector>
@ -34,7 +37,7 @@ struct SDL_Window;
class ScreenImage : public wxFrame {
private:
wxPanel *panelTop;
wxPanel *sdl;
SDL_Window* window;
SDL_Renderer* renderer;
SDL_Texture* texture;
@ -44,19 +47,23 @@ private:
bool buffer;
AnalogTV::DisplayType display;
void createScreen();
void createSdlTexture(wxPanel *panelSdl);
void createSdlTexture();
void destroyScreen();
std::vector<std::string> slotnames;
std::string cassInName;
std::string cassOutName;
KeyEventHandler &keyEventHandler;
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
public:
ScreenImage();
ScreenImage(KeyEventHandler &keyEventHandler);
~ScreenImage();
void exitFullScreen();