remove SDL event loop and use wx for keypresses instead; fix screen resizing
This commit is contained in:
parent
6d5e2d589a
commit
f7fc5e47e9
|
@ -169,7 +169,6 @@ void E2wxFrame::OnPreferences(wxCommandEvent& event) {
|
|||
dlg->ShowModal();
|
||||
dlg->Destroy();
|
||||
}
|
||||
// TODO re-configure emulator here
|
||||
wxGetApp().StartEmulator();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -52,10 +52,6 @@ class Emulator {
|
|||
void powerOnComputer();
|
||||
void powerOffComputer();
|
||||
|
||||
void handleAnyPendingEvents();
|
||||
|
||||
void handleRepeatKey();
|
||||
|
||||
public:
|
||||
Emulator();
|
||||
virtual ~Emulator();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue