From f0c8c246cc318f5a3f42bff2c8a7f04b9a03d9e9 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 7 Jul 2015 01:03:36 -0700 Subject: [PATCH 01/52] VFS: Fix Windows build --- src/util/vfs/vfs-fd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/vfs/vfs-fd.c b/src/util/vfs/vfs-fd.c index 3dd5fa8b6..ce57c38f9 100644 --- a/src/util/vfs/vfs-fd.c +++ b/src/util/vfs/vfs-fd.c @@ -173,5 +173,9 @@ static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size) { UNUSED(buffer); UNUSED(size); struct VFileFD* vfd = (struct VFileFD*) vf; +#ifndef _WIN32 return fsync(vfd->fd) == 0; +#else + return FlushFileBuffers((HANDLE) _get_osfhandle(vfd->fd)); +#endif } From 9911484aee8778d4c1f1eab597d5cd8625ba9848 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 7 Jul 2015 22:51:13 -0700 Subject: [PATCH 02/52] Qt: Add confirmation for making portable --- src/platform/qt/Window.cpp | 11 ++++++++++- src/platform/qt/Window.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index e94aec4d1..933a73869 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -561,6 +561,15 @@ void Window::unimplementedBiosCall(int call) { fail->show(); } +void Window::tryMakePortable() { + QMessageBox* confirm = new QMessageBox(QMessageBox::Question, tr("Really make portable?"), + tr("This will make the emulator load its configuration from the same directory as the executable. Do you want to continue?"), + QMessageBox::Yes | QMessageBox::Cancel, this, Qt::Sheet); + confirm->setAttribute(Qt::WA_DeleteOnClose); + connect(confirm->button(QMessageBox::Yes), SIGNAL(clicked()), m_config, SLOT(makePortable())); + confirm->show(); +} + void Window::recordFrame() { m_frameList.append(QDateTime::currentDateTime()); while (m_frameList.count() > FRAME_LIST_SIZE) { @@ -642,7 +651,7 @@ void Window::setupMenu(QMenuBar* menubar) { fileMenu->addSeparator(); - addControlledAction(fileMenu, fileMenu->addAction(tr("Make portable"), m_config, SLOT(makePortable())), "makePortable"); + addControlledAction(fileMenu, fileMenu->addAction(tr("Make portable"), this, SLOT(tryMakePortable())), "makePortable"); fileMenu->addSeparator(); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index f0377c185..d97d2b537 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -115,6 +115,8 @@ private slots: void gameFailed(); void unimplementedBiosCall(int); + void tryMakePortable(); + void recordFrame(); void showFPS(); From f6581773d8e33d1e600a5d2fb615f36023711126 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 8 Jul 2015 20:43:26 -0700 Subject: [PATCH 03/52] GBA: Remove GBA_LOG_INFO from default log levels --- src/gba/gba.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gba/gba.c b/src/gba/gba.c index 500068e4b..4e126e543 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -79,7 +79,7 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) { gba->biosVf = 0; gba->logHandler = 0; - gba->logLevel = GBA_LOG_INFO | GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL; + gba->logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL; gba->stream = 0; gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS); From 1ee5ccd1ff4b08ed4aedb108938198e8bff26d59 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 9 Jul 2015 21:55:13 -0700 Subject: [PATCH 04/52] GBA: Clean up non-standard logging a bit --- src/gba/gba.h | 6 ------ src/gba/renderers/software-mode0.c | 2 +- src/gba/renderers/video-software.c | 8 +++----- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/gba/gba.h b/src/gba/gba.h index aff2120d7..92847c84d 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -49,12 +49,6 @@ enum GBALogLevel { GBA_LOG_SIO = 0x800, GBA_LOG_ALL = 0xF3F, - -#ifdef NDEBUG - GBA_LOG_DANGER = GBA_LOG_ERROR -#else - GBA_LOG_DANGER = GBA_LOG_FATAL -#endif }; enum GBAKey { diff --git a/src/gba/renderers/software-mode0.c b/src/gba/renderers/software-mode0.c index 886306072..6ff17e55b 100644 --- a/src/gba/renderers/software-mode0.c +++ b/src/gba/renderers/software-mode0.c @@ -406,7 +406,7 @@ return; \ } \ if (UNLIKELY(end < outX)) { \ - GBALog(0, GBA_LOG_DANGER, "Out of bounds background draw!"); \ + GBALog(0, GBA_LOG_FATAL, "Out of bounds background draw!"); \ return; \ } \ DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \ diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 035ad0c11..8078293c5 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -403,12 +403,10 @@ static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, if (win->h.end >= oldWindow.endX) { // Trim off extra windows we've overwritten for (++activeWindow; softwareRenderer->nWindows > activeWindow + 1 && win->h.end >= softwareRenderer->windows[activeWindow].endX; ++activeWindow) { -#ifdef DEBUG - if (activeWindow >= MAX_WINDOW) { - GBALog(0, GBA_LOG_DANGER, "Out of bounds window write will occur"); + if (VIDEO_CHECKS && activeWindow >= MAX_WINDOW) { + GBALog(0, GBA_LOG_FATAL, "Out of bounds window write will occur"); return; } -#endif softwareRenderer->windows[activeWindow] = softwareRenderer->windows[activeWindow + 1]; --softwareRenderer->nWindows; } @@ -428,7 +426,7 @@ static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, } #ifdef DEBUG if (softwareRenderer->nWindows > MAX_WINDOW) { - GBALog(0, GBA_LOG_ABORT, "Out of bounds window write occurred!"); + GBALog(0, GBA_LOG_FATAL, "Out of bounds window write occurred!"); } #endif } From 5749649b9c5d90c4090ee4e771f1d4f1ffef85c4 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 11 Jul 2015 02:57:08 -0700 Subject: [PATCH 05/52] All: Update port progress a bit --- PORTING.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/PORTING.md b/PORTING.md index 6e4fdae16..baa6f3712 100644 --- a/PORTING.md +++ b/PORTING.md @@ -12,7 +12,7 @@ Port-specific TODO The ports are vaguely usable, but by no means should be considered stable. -### 3DS +### 3DS (port/3ds) * Add menu * Add audio * Thread support testing @@ -20,7 +20,7 @@ The ports are vaguely usable, but by no means should be considered stable. * ARMv6 dynarec * Hardware acceleration -### PSP +### PSP (port/psp) * Add menu * Add audio * Thread support @@ -28,7 +28,14 @@ The ports are vaguely usable, but by no means should be considered stable. * MIPS dynarec * Hardware acceleration -### Wii +### PS Vita (port/psp2) +* Add menu +* Add audio +* Make it faster + * Threaded renderer shim + * Hardware acceleration + +### Wii (port/wii) * Add menu * Add audio * Thread support From 0496691c9b0a767cf92dd50dbfec38f38e67439b Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 11 Jul 2015 14:51:09 -0700 Subject: [PATCH 06/52] Qt: Prompt for restart when changing display driver --- src/platform/qt/Window.cpp | 9 +++++++++ src/platform/qt/Window.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 933a73869..5da8d9714 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -296,6 +296,7 @@ void Window::openSettingsWindow() { SettingsView* settingsWindow = new SettingsView(m_config); connect(settingsWindow, SIGNAL(biosLoaded(const QString&)), m_controller, SLOT(loadBIOS(const QString&))); connect(settingsWindow, SIGNAL(audioDriverChanged()), m_controller, SLOT(reloadAudioDriver())); + connect(settingsWindow, SIGNAL(displayDriverChanged()), this, SLOT(mustRestart())); openView(settingsWindow); } @@ -570,6 +571,14 @@ void Window::tryMakePortable() { confirm->show(); } +void Window::mustRestart() { + QMessageBox* dialog = new QMessageBox(QMessageBox::Warning, tr("Restart needed"), + tr("Some changes will not take effect until the emulator is restarted."), + QMessageBox::Ok, this, Qt::Sheet); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); +} + void Window::recordFrame() { m_frameList.append(QDateTime::currentDateTime()); while (m_frameList.count() > FRAME_LIST_SIZE) { diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index d97d2b537..7db12500c 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -116,6 +116,7 @@ private slots: void unimplementedBiosCall(int); void tryMakePortable(); + void mustRestart(); void recordFrame(); void showFPS(); From 9d7b79db2798ae6d29aa6eca5fb651ea686ca471 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 11 Jul 2015 16:32:11 -0700 Subject: [PATCH 07/52] GBA: Cannot clean a savefile if there is no vf --- src/gba/savedata.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gba/savedata.c b/src/gba/savedata.c index f283a8e75..daa8aca68 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -407,6 +407,9 @@ uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) { } void GBASavedataClean(struct GBASavedata* savedata, uint32_t frameCount) { + if (!savedata->vf) { + return; + } if (savedata->dirty & SAVEDATA_DIRT_NEW) { savedata->dirty &= ~SAVEDATA_DIRT_NEW; if (!(savedata->dirty & SAVEDATA_DIRT_SEEN)) { From 712b0ccb84a9e532b8725276365de7780a875d0f Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 12 Jul 2015 00:19:17 -0700 Subject: [PATCH 08/52] Qt: Ensure proper audio sample rate is used when resampling audio --- src/platform/qt/AudioProcessor.h | 2 ++ src/platform/qt/AudioProcessorQt.cpp | 7 +++++++ src/platform/qt/AudioProcessorQt.h | 2 ++ src/platform/qt/AudioProcessorSDL.cpp | 4 ++++ src/platform/qt/AudioProcessorSDL.h | 2 ++ src/platform/qt/GameController.cpp | 2 +- 6 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/AudioProcessor.h b/src/platform/qt/AudioProcessor.h index a29763c23..abbca9395 100644 --- a/src/platform/qt/AudioProcessor.h +++ b/src/platform/qt/AudioProcessor.h @@ -39,6 +39,8 @@ public slots: virtual void setBufferSamples(int samples) = 0; virtual void inputParametersChanged() = 0; + virtual unsigned sampleRate() const = 0; + protected: GBAThread* input() { return m_context; } diff --git a/src/platform/qt/AudioProcessorQt.cpp b/src/platform/qt/AudioProcessorQt.cpp index 23a3f8e7f..281ca6bba 100644 --- a/src/platform/qt/AudioProcessorQt.cpp +++ b/src/platform/qt/AudioProcessorQt.cpp @@ -83,3 +83,10 @@ void AudioProcessorQt::inputParametersChanged() { m_device->setFormat(m_audioOutput->format()); } } + +unsigned AudioProcessorQt::sampleRate() const { + if (!m_audioOutput) { + return 0; + } + return m_audioOutput->format().sampleRate(); +} diff --git a/src/platform/qt/AudioProcessorQt.h b/src/platform/qt/AudioProcessorQt.h index 38edf28d7..ee9dde24d 100644 --- a/src/platform/qt/AudioProcessorQt.h +++ b/src/platform/qt/AudioProcessorQt.h @@ -28,6 +28,8 @@ public slots: virtual void setBufferSamples(int samples); virtual void inputParametersChanged(); + virtual unsigned sampleRate() const override; + private: QAudioOutput* m_audioOutput; AudioDevice* m_device; diff --git a/src/platform/qt/AudioProcessorSDL.cpp b/src/platform/qt/AudioProcessorSDL.cpp index ce6e66bd4..0caa9765c 100644 --- a/src/platform/qt/AudioProcessorSDL.cpp +++ b/src/platform/qt/AudioProcessorSDL.cpp @@ -54,3 +54,7 @@ void AudioProcessorSDL::setBufferSamples(int samples) { void AudioProcessorSDL::inputParametersChanged() { } + +unsigned AudioProcessorSDL::sampleRate() const { + return m_audio.obtainedSpec.freq; +} diff --git a/src/platform/qt/AudioProcessorSDL.h b/src/platform/qt/AudioProcessorSDL.h index 9a918f6b6..002dcd839 100644 --- a/src/platform/qt/AudioProcessorSDL.h +++ b/src/platform/qt/AudioProcessorSDL.h @@ -29,6 +29,8 @@ public slots: virtual void setBufferSamples(int samples); virtual void inputParametersChanged(); + virtual unsigned sampleRate() const override; + private: GBASDLAudio m_audio; }; diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 8440cd284..24cd47ab2 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -746,7 +746,7 @@ void GameController::redoSamples(int samples) { if (m_threadContext.gba) { sampleRate = m_threadContext.gba->audio.sampleRate; } - ratio = GBAAudioCalculateRatio(sampleRate, m_threadContext.fpsTarget, 44100); + ratio = GBAAudioCalculateRatio(sampleRate, m_threadContext.fpsTarget, m_audioProcess->sampleRate()); m_threadContext.audioBuffers = ceil(samples / ratio); #else m_threadContext.audioBuffers = samples; From 1b8fe1aa09563444903cd5609dde104222a80ec6 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 12 Jul 2015 12:27:15 -0700 Subject: [PATCH 09/52] Qt: Add fast forward cap to settings --- src/platform/qt/SettingsView.cpp | 19 ++++++++++ src/platform/qt/SettingsView.ui | 60 ++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 5f288da37..cc343cd29 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -36,6 +36,19 @@ SettingsView::SettingsView(ConfigController* controller, QWidget* parent) loadSetting("allowOpposingDirections", m_ui.allowOpposingDirections); loadSetting("suspendScreensaver", m_ui.suspendScreensaver); + double fastForwardRatio = loadSetting("fastForwardRatio").toDouble(); + if (fastForwardRatio <= 0) { + m_ui.fastForwardUnbounded->setChecked(true); + m_ui.fastForwardRatio->setEnabled(false); + } else { + m_ui.fastForwardUnbounded->setChecked(false); + m_ui.fastForwardRatio->setEnabled(true); + m_ui.fastForwardRatio->setValue(fastForwardRatio); + } + connect(m_ui.fastForwardUnbounded, &QAbstractButton::toggled, [this](bool checked) { + m_ui.fastForwardRatio->setEnabled(!checked); + }); + QString idleOptimization = loadSetting("idleOptimization"); if (idleOptimization == "ignore") { m_ui.idleOptimization->setCurrentIndex(0); @@ -103,6 +116,12 @@ void SettingsView::updateConfig() { saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections); saveSetting("suspendScreensaver", m_ui.suspendScreensaver); + if (m_ui.fastForwardUnbounded->isChecked()) { + saveSetting("fastForwardRatio", "-1"); + } else { + saveSetting("fastForwardRatio", m_ui.fastForwardRatio); + } + switch (m_ui.idleOptimization->currentIndex() + IDLE_LOOP_IGNORE) { case IDLE_LOOP_IGNORE: saveSetting("idleOptimization", "ignore"); diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index 9de1f76e4..bfd7af451 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -6,8 +6,8 @@ 0 0 - 698 - 366 + 707 + 420 @@ -380,21 +380,21 @@ - + Qt::Horizontal - + Allow opposing input directions - + Suspend screensaver @@ -404,14 +404,14 @@ - + Idle loops - + @@ -430,6 +430,52 @@ + + + + false + + + × + + + 0.010000000000000 + + + 20.000000000000000 + + + 0.500000000000000 + + + 5.000000000000000 + + + + + + + Fast forward speed + + + + + + + Unbounded + + + true + + + + + + + Qt::Horizontal + + + From 386da2accd6a168f4d59b3a011c401f14a3913ff Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 12 Jul 2015 15:49:04 -0700 Subject: [PATCH 10/52] VFS: Add VFile.sync for memory vfs --- src/util/vfs/vfs-mem.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/util/vfs/vfs-mem.c b/src/util/vfs/vfs-mem.c index 8d52eb0b1..10b03ef19 100644 --- a/src/util/vfs/vfs-mem.c +++ b/src/util/vfs/vfs-mem.c @@ -20,6 +20,7 @@ static void* _vfmMap(struct VFile* vf, size_t size, int flags); static void _vfmUnmap(struct VFile* vf, void* memory, size_t size); static void _vfmTruncate(struct VFile* vf, size_t size); static ssize_t _vfmSize(struct VFile* vf); +static bool _vfmSync(struct VFile* vf, const void* buffer, size_t size); struct VFile* VFileFromMemory(void* mem, size_t size) { if (!mem || !size) { @@ -43,6 +44,7 @@ struct VFile* VFileFromMemory(void* mem, size_t size) { vfm->d.unmap = _vfmUnmap; vfm->d.truncate = _vfmTruncate; vfm->d.size = _vfmSize; + vfm->d.sync = _vfmSync; return &vfm->d; } @@ -137,3 +139,10 @@ ssize_t _vfmSize(struct VFile* vf) { struct VFileMem* vfm = (struct VFileMem*) vf; return vfm->size; } + +bool _vfmSync(struct VFile* vf, const void* buffer, size_t size) { + UNUSED(vf); + UNUSED(buffer); + UNUSED(size); + return true; +} From 18ec3de2e2d12a1f81a5afa688bc14f28f6c2125 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 12 Jul 2015 15:49:27 -0700 Subject: [PATCH 11/52] Libretro: Add rumble support --- src/platform/libretro/libretro.c | 42 +++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index 967627e43..3ac3a8777 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -12,9 +12,11 @@ #include "gba/serialize.h" #include "gba/supervisor/overrides.h" #include "gba/video.h" +#include "util/circle-buffer.h" #include "util/vfs.h" #define SAMPLES 1024 +#define RUMBLE_PWM 35 static retro_environment_t environCallback; static retro_video_refresh_t videoCallback; @@ -22,11 +24,13 @@ static retro_audio_sample_batch_t audioCallback; static retro_input_poll_t inputPollCallback; static retro_input_state_t inputCallback; static retro_log_printf_t logCallback; +static retro_set_rumble_state_t rumbleCallback; static void GBARetroLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args); static void _postAudioBuffer(struct GBAAVStream*, struct GBAAudio* audio); static void _postVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer); +static void _setRumble(struct GBARumble* rumble, int enable); static struct GBA gba; static struct ARMCore cpu; @@ -36,6 +40,9 @@ static void* data; static struct VFile* save; static void* savedata; static struct GBAAVStream stream; +static int rumbleLevel; +static struct CircleBuffer rumbleHistory; +static struct GBARumble rumble; unsigned retro_api_version(void) { return RETRO_API_VERSION; @@ -112,7 +119,15 @@ void retro_init(void) { environCallback(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &inputDescriptors); // TODO: RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME when BIOS booting is supported - // TODO: RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE + + struct retro_rumble_interface rumbleInterface; + if (environCallback(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumbleInterface)) { + rumbleCallback = rumbleInterface.set_rumble_state; + CircleBufferInit(&rumbleHistory, RUMBLE_PWM); + rumble.setRumble = _setRumble; + } else { + rumbleCallback = 0; + } struct retro_log_callback log; if (environCallback(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log)) { @@ -132,6 +147,9 @@ void retro_init(void) { gba.logHandler = GBARetroLog; gba.stream = &stream; gba.idleOptimization = IDLE_LOOP_REMOVE; // TODO: Settings + if (rumbleCallback) { + gba.rumble = &rumble; + } rom = 0; GBAVideoSoftwareRendererCreate(&renderer); @@ -176,6 +194,10 @@ void retro_run(void) { void retro_reset(void) { ARMReset(&cpu); + + if (rumbleCallback) { + CircleBufferClear(&rumbleHistory); + } } bool retro_load_game(const struct retro_game_info* game) { @@ -219,6 +241,7 @@ void retro_unload_game(void) { save = 0; free(savedata); savedata = 0; + CircleBufferDeinit(&rumbleHistory); } size_t retro_serialize_size(void) { @@ -317,10 +340,12 @@ void GBARetroLog(struct GBAThread* thread, enum GBALogLevel level, const char* f case GBA_LOG_INFO: case GBA_LOG_GAME_ERROR: case GBA_LOG_SWI: + case GBA_LOG_STATUS: retroLevel = RETRO_LOG_INFO; break; case GBA_LOG_DEBUG: case GBA_LOG_STUB: + case GBA_LOG_SIO: retroLevel = RETRO_LOG_DEBUG; break; } @@ -352,3 +377,18 @@ static void _postVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer->getPixels(renderer, &stride, &pixels); videoCallback(pixels, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, BYTES_PER_PIXEL * stride); } + +static void _setRumble(struct GBARumble* rumble, int enable) { + UNUSED(rumble); + if (!rumbleCallback) { + return; + } + rumbleLevel += enable; + if (CircleBufferSize(&rumbleHistory) == RUMBLE_PWM) { + int8_t oldLevel; + CircleBufferRead8(&rumbleHistory, &oldLevel); + rumbleLevel -= oldLevel; + } + CircleBufferWrite8(&rumbleHistory, enable); + rumbleCallback(0, RETRO_RUMBLE_STRONG, rumbleLevel * 0xFFFF / RUMBLE_PWM); +} From 817f1573d3b9fcdc8dc95acdd9ee2eaf86d70af0 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 12 Jul 2015 16:02:16 -0700 Subject: [PATCH 12/52] All: Move version generation to external CMake script --- CMakeLists.txt | 40 ++-------------------------------------- version.cmake | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 438b9b555..0c6cf442a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,47 +76,11 @@ function(find_feature FEATURE_NAME FEATURE_REQUIRES) endfunction() # Version information -set(LIB_VERSION_MAJOR 0) -set(LIB_VERSION_MINOR 3) -set(LIB_VERSION_PATCH 0) -set(LIB_VERSION_ABI 0.3) -set(LIB_VERSION_STRING ${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}) - -execute_process(COMMAND git describe --always --abbrev=40 --dirty OUTPUT_VARIABLE GIT_COMMIT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND git describe --always --dirty OUTPUT_VARIABLE GIT_COMMIT_SHORT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND git symbolic-ref --short HEAD OUTPUT_VARIABLE GIT_BRANCH ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND git rev-list HEAD --count OUTPUT_VARIABLE GIT_REV ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND git describe --tag --exact-match OUTPUT_VARIABLE GIT_TAG ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - -if(GIT_REV STREQUAL "") - set(GIT_REV -1) -endif() -if(NOT GIT_TAG STREQUAL "") - set(VERSION_STRING ${GIT_TAG}) -elseif(GIT_BRANCH STREQUAL "") - set(VERSION_STRING ${LIB_VERSION_STRING}) -else() - if(GIT_BRANCH STREQUAL "master") - set(VERSION_STRING ${GIT_REV}-${GIT_COMMIT_SHORT}) - else() - set(VERSION_STRING ${GIT_BRANCH}-${GIT_REV}-${GIT_COMMIT_SHORT}) - endif() - - if(NOT LIB_VERSION_ABI STREQUAL GIT_BRANCH) - set(VERSION_STRING ${LIB_VERSION_ABI}-${VERSION_STRING}) - endif() -endif() - add_custom_target(version-info ALL ${CMAKE_COMMAND} -E touch ${CMAKE_SOURCE_DIR}/src/util/version.c.in COMMAND ${CMAKE_COMMAND} - -DGIT_COMMIT=${GIT_COMMIT} - -DGIT_COMMIT_SHORT=${GIT_COMMIT_SHORT} - -DGIT_BRANCH=${GIT_BRANCH} - -DGIT_REV=${GIT_REV} -DBINARY_NAME=${BINARY_NAME} - -DPROJECT_NAME=${PROJECT_NAME} - -DVERSION_STRING=${VERSION_STRING} - -D${BINARY_NAME}_SOURCE_DIR=${CMAKE_SOURCE_DIR} + -DCONFIG_FILE=${CMAKE_SOURCE_DIR}/src/util/version.c.in + -DOUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/version.c -P ${CMAKE_SOURCE_DIR}/version.cmake WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/version.cmake b/version.cmake index 32bf65821..718ecdb5f 100644 --- a/version.cmake +++ b/version.cmake @@ -1,5 +1,34 @@ -if(NOT ${BINARY_NAME}_SOURCE_DIR) - set(${BINARY_NAME}_SOURCE_DIR ${CMAKE_SOURCE_DIR}) +set(LIB_VERSION_MAJOR 0) +set(LIB_VERSION_MINOR 3) +set(LIB_VERSION_PATCH 0) +set(LIB_VERSION_ABI 0.3) +set(LIB_VERSION_STRING ${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}) + +execute_process(COMMAND git describe --always --abbrev=40 --dirty OUTPUT_VARIABLE GIT_COMMIT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND git describe --always --dirty OUTPUT_VARIABLE GIT_COMMIT_SHORT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND git symbolic-ref --short HEAD OUTPUT_VARIABLE GIT_BRANCH ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND git rev-list HEAD --count OUTPUT_VARIABLE GIT_REV ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND git describe --tag --exact-match OUTPUT_VARIABLE GIT_TAG ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + +if(GIT_REV STREQUAL "") + set(GIT_REV -1) +endif() +if(NOT GIT_TAG STREQUAL "") + set(VERSION_STRING ${GIT_TAG}) +elseif(GIT_BRANCH STREQUAL "") + set(VERSION_STRING ${LIB_VERSION_STRING}) +else() + if(GIT_BRANCH STREQUAL "master") + set(VERSION_STRING ${GIT_REV}-${GIT_COMMIT_SHORT}) + else() + set(VERSION_STRING ${GIT_BRANCH}-${GIT_REV}-${GIT_COMMIT_SHORT}) + endif() + + if(NOT LIB_VERSION_ABI STREQUAL GIT_BRANCH) + set(VERSION_STRING ${LIB_VERSION_ABI}-${VERSION_STRING}) + endif() endif() -configure_file("${${BINARY_NAME}_SOURCE_DIR}/src/util/version.c.in" "${CMAKE_CURRENT_BINARY_DIR}/version.c") +if(CONFIG_FILE AND OUT_FILE) + configure_file("${CONFIG_FILE}" "${OUT_FILE}") +endif() From 8a66ee0d5673ff5f701fd3b3bf73876da2aeefcd Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 12 Jul 2015 16:10:48 -0700 Subject: [PATCH 13/52] All: Fix fresh build --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c6cf442a..b52159bff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,7 @@ add_custom_target(version-info ALL ${CMAKE_COMMAND} -E touch ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) include(${CMAKE_SOURCE_DIR}/version.cmake) +configure_file(${CMAKE_SOURCE_DIR}/src/util/version.c.in ${CMAKE_CURRENT_BINARY_DIR}/version.c) list(APPEND UTIL_SRC ${CMAKE_BINARY_DIR}/version.c) source_group("Generated sources" FILES ${CMAKE_BINARY_DIR}/version.c) From 4d5c1f98497b90e5abb79bdaf7af0c0ad5b606bf Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 12 Jul 2015 17:42:30 -0700 Subject: [PATCH 14/52] Qt: Hide cursor opportunistically --- src/platform/qt/Display.cpp | 10 ++++++++++ src/platform/qt/Display.h | 8 ++++++++ src/platform/qt/DisplayGL.cpp | 2 ++ src/platform/qt/DisplayGL.h | 2 ++ src/platform/qt/Window.cpp | 6 ++++++ 5 files changed, 28 insertions(+) diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index 7f0a37f50..6a11a5c64 100644 --- a/src/platform/qt/Display.cpp +++ b/src/platform/qt/Display.cpp @@ -51,6 +51,10 @@ Display::Display(QWidget* parent) { setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); setMinimumSize(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); + connect(&m_mouseTimer, SIGNAL(timeout()), this, SIGNAL(hideCursor())); + m_mouseTimer.setSingleShot(true); + m_mouseTimer.setInterval(MOUSE_DISAPPEAR_TIMER); + setMouseTracking(true); } void Display::resizeEvent(QResizeEvent*) { @@ -69,3 +73,9 @@ void Display::filter(bool filter) { void Display::showMessage(const QString& message) { m_messagePainter.showMessage(message); } + +void Display::mouseMoveEvent(QMouseEvent*) { + emit showCursor(); + m_mouseTimer.stop(); + m_mouseTimer.start(); +} diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index 6ae6a35c2..551d8247c 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -33,6 +33,10 @@ public: bool isAspectRatioLocked() const { return m_lockAspectRatio; } bool isFiltered() const { return m_filter; } +signals: + void showCursor(); + void hideCursor(); + public slots: virtual void startDrawing(GBAThread* context) = 0; virtual void stopDrawing() = 0; @@ -47,15 +51,19 @@ public slots: protected: void resizeEvent(QResizeEvent*); + virtual void mouseMoveEvent(QMouseEvent*) override; MessagePainter* messagePainter() { return &m_messagePainter; } + private: static Driver s_driver; + static const int MOUSE_DISAPPEAR_TIMER = 2000; MessagePainter m_messagePainter; bool m_lockAspectRatio; bool m_filter; + QTimer m_mouseTimer; }; } diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 456840751..ca41f0f46 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -21,6 +21,8 @@ DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent) , m_drawThread(nullptr) , m_context(nullptr) { + m_gl->setMouseTracking(true); + m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work? } DisplayGL::~DisplayGL() { diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index c49bab16c..fc4cb500f 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -9,6 +9,7 @@ #include "Display.h" #include +#include #include #include @@ -27,6 +28,7 @@ public: protected: void paintEvent(QPaintEvent*) override {} void resizeEvent(QResizeEvent*) override {} + void mouseMoveEvent(QMouseEvent* event) override { event->ignore(); } }; class PainterGL; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 5da8d9714..71f5e02d4 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -128,6 +128,12 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) connect(this, SIGNAL(audioBufferSamplesChanged(int)), m_controller, SLOT(setAudioBufferSamples(int))); connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float))); connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS())); + connect(m_display, &Display::hideCursor, [this]() { + setCursor(Qt::BlankCursor); + }); + connect(m_display, &Display::showCursor, [this]() { + unsetCursor(); + }); m_log.setLevels(GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL | GBA_LOG_STATUS); m_fpsTimer.setInterval(FPS_TIMER_INTERVAL); From 56208521d6280222ca42ecfa16a9e11c9d0c5592 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 13 Jul 2015 01:15:59 -0700 Subject: [PATCH 15/52] ARM7: Fix decoding LDR3 --- src/arm/decoder-thumb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arm/decoder-thumb.c b/src/arm/decoder-thumb.c index d1eac9c7d..626004f2c 100644 --- a/src/arm/decoder-thumb.c +++ b/src/arm/decoder-thumb.c @@ -153,7 +153,7 @@ DEFINE_DECODER_WITH_HIGH_THUMB(MOV3, MOV, ARM_OPERAND_AFFECTED_1, 0) #define DEFINE_IMMEDIATE_WITH_REGISTER_MEM_THUMB(NAME, MNEMONIC, REG, CYCLES) \ DEFINE_THUMB_DECODER(NAME, MNEMONIC, \ - info->op1.reg = (opcode >> 6) & 0x0007; \ + info->op1.reg = (opcode >> 8) & 0x0007; \ info->memory.baseReg = REG; \ info->memory.offset.immediate = (opcode & 0x00FF) << 2; \ info->memory.width = ARM_ACCESS_WORD; \ From 43d9c8b754b4ccc2376ac319d56da25a7ced8eda Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 13 Jul 2015 20:46:41 -0700 Subject: [PATCH 16/52] GBA: Gigantic refactor and add preliminary Game Boy Player support --- src/gba/gba.c | 3 ++ src/gba/gba.h | 42 +-------------- src/gba/hardware.c | 123 ++++++++++++++++++++++++++++++++++++++++++++ src/gba/hardware.h | 40 +++++++------- src/gba/interface.h | 101 ++++++++++++++++++++++++++++++++++++ src/gba/io.c | 10 ++-- src/gba/sio.h | 24 +-------- 7 files changed, 257 insertions(+), 86 deletions(-) create mode 100644 src/gba/interface.h diff --git a/src/gba/gba.c b/src/gba/gba.c index 4e126e543..9cf50a3ef 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -81,6 +81,7 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) { gba->logHandler = 0; gba->logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL; gba->stream = 0; + gba->keyCallback = 0; gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS); @@ -777,6 +778,8 @@ void GBAFrameEnded(struct GBA* gba) { gba->stream->postVideoFrame(gba->stream, gba->video.renderer); } + GBAHardwarePlayerUpdate(gba); + struct GBAThread* thread = GBAThreadGetContext(); if (!thread) { return; diff --git a/src/gba/gba.h b/src/gba/gba.h index 92847c84d..0e5d92a42 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -11,6 +11,7 @@ #include "arm.h" #include "debugger/debugger.h" +#include "gba/interface.h" #include "gba/memory.h" #include "gba/video.h" #include "gba/audio.h" @@ -35,37 +36,6 @@ enum GBAIRQ { IRQ_GAMEPAK = 0xD }; -enum GBALogLevel { - GBA_LOG_FATAL = 0x01, - GBA_LOG_ERROR = 0x02, - GBA_LOG_WARN = 0x04, - GBA_LOG_INFO = 0x08, - GBA_LOG_DEBUG = 0x10, - GBA_LOG_STUB = 0x20, - - GBA_LOG_GAME_ERROR = 0x100, - GBA_LOG_SWI = 0x200, - GBA_LOG_STATUS = 0x400, - GBA_LOG_SIO = 0x800, - - GBA_LOG_ALL = 0xF3F, -}; - -enum GBAKey { - GBA_KEY_A = 0, - GBA_KEY_B = 1, - GBA_KEY_SELECT = 2, - GBA_KEY_START = 3, - GBA_KEY_RIGHT = 4, - GBA_KEY_LEFT = 5, - GBA_KEY_UP = 6, - GBA_KEY_DOWN = 7, - GBA_KEY_R = 8, - GBA_KEY_L = 9, - GBA_KEY_MAX, - GBA_KEY_NONE = -1 -}; - enum GBAComponent { GBA_COMPONENT_DEBUGGER, GBA_COMPONENT_CHEAT_DEVICE, @@ -85,19 +55,10 @@ enum { }; struct GBA; -struct GBARotationSource; struct GBAThread; struct Patch; struct VFile; -typedef void (*GBALogHandler)(struct GBAThread*, enum GBALogLevel, const char* format, va_list args); - -struct GBAAVStream { - void (*postVideoFrame)(struct GBAAVStream*, struct GBAVideoRenderer* renderer); - void (*postAudioFrame)(struct GBAAVStream*, int16_t left, int16_t right); - void (*postAudioBuffer)(struct GBAAVStream*, struct GBAAudio*); -}; - struct GBATimer { uint16_t reload; uint16_t oldReload; @@ -150,6 +111,7 @@ struct GBA { GBALogHandler logHandler; enum GBALogLevel logLevel; struct GBAAVStream* stream; + struct GBAKeyCallback* keyCallback; enum GBAIdleLoopOptimization idleOptimization; uint32_t idleLoop; diff --git a/src/gba/hardware.c b/src/gba/hardware.c index e0c341e26..a2e61c927 100644 --- a/src/gba/hardware.c +++ b/src/gba/hardware.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "hardware.h" +#include "gba/io.h" #include "gba/serialize.h" #include "util/hash.h" @@ -25,6 +26,11 @@ static void _rumbleReadPins(struct GBACartridgeHardware* hw); static void _lightReadPins(struct GBACartridgeHardware* hw); +static uint16_t _gbpRead(struct GBAKeyCallback*); +static bool _gbpSioLoad(struct GBASIODriver* driver); +static uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); +static int32_t _gbpSioProcessEvents(struct GBASIODriver* driver, int32_t cycles); + static const int RTC_BYTES[8] = { 0, // Force reset 0, // Empty @@ -46,6 +52,7 @@ void GBAHardwareClear(struct GBACartridgeHardware* hw) { hw->direction = GPIO_WRITE_ONLY; hw->pinState = 0; hw->direction = 0; + hw->gbpRunning = false; } void GBAHardwareGPIOWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value) { @@ -461,6 +468,30 @@ static const uint16_t _logoPalette[] = { static const uint32_t _logoHash = 0xEEDA6963; +static const uint32_t _gbpTxData[] = { + 0x0000494E, 0x0000494E, + 0xB6B1494E, 0xB6B1544E, + 0xABB1544E, 0xABB14E45, + 0xB1BA4E45, 0xB1BA4F44, + 0xB0BB4F44, 0xB0BB8002, + 0x10000010, 0x20000013, + 0x30000003, 0x30000003, + 0x30000003, 0x30000003, + 0x30000003, 0x00000000, +}; + +static const uint32_t _gbpRxData[] = { + 0x00000000, 0x494EB6B1, + 0x494EB6B1, 0x544EB6B1, + 0x544EABB1, 0x4E45ABB1, + 0x4E45B1BA, 0x4F44B1BA, + 0x4F44B0BB, 0x8000B0BB, + 0x10000010, 0x20000013, + 0x40000004, 0x40000004, + 0x40000004, 0x40000004, + 0x40000004, 0x40000004 +}; + bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video) { if (memcmp(video->palette, _logoPalette, sizeof(_logoPalette)) != 0) { return false; @@ -469,6 +500,98 @@ bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video) { return hash == _logoHash; } +void GBAHardwarePlayerUpdate(struct GBA* gba) { + if (gba->memory.hw.gbpRunning) { + if (GBAHardwarePlayerCheckScreen(&gba->video)) { + ++gba->memory.hw.gbpInputsPosted; + gba->memory.hw.gbpInputsPosted %= 3; + gba->keyCallback = &gba->memory.hw.gbpCallback.d; + } else { + // TODO: Save and restore + gba->keyCallback = 0; + } + gba->memory.hw.gbpTxPosition = 0; + return; + } + if (gba->keyCallback || gba->sio.drivers.normal) { + return; + } + if (GBAHardwarePlayerCheckScreen(&gba->video)) { + gba->memory.hw.gbpRunning = true; + gba->memory.hw.gbpCallback.d.readKeys = _gbpRead; + gba->memory.hw.gbpCallback.p = &gba->memory.hw; + gba->memory.hw.gbpDriver.d.init = 0; + gba->memory.hw.gbpDriver.d.deinit = 0; + gba->memory.hw.gbpDriver.d.load = _gbpSioLoad; + gba->memory.hw.gbpDriver.d.unload = 0; + gba->memory.hw.gbpDriver.d.writeRegister = _gbpSioWriteRegister; + gba->memory.hw.gbpDriver.d.processEvents = _gbpSioProcessEvents; + gba->memory.hw.gbpDriver.p = &gba->memory.hw; + gba->memory.hw.gbpInputsPosted = 0; + gba->keyCallback = &gba->memory.hw.gbpCallback.d; + GBASIOSetDriver(&gba->sio, &gba->memory.hw.gbpDriver.d, SIO_NORMAL_32); + } +} + +uint16_t _gbpRead(struct GBAKeyCallback* callback) { + struct GBAGBPKeyCallback* gbpCallback = (struct GBAGBPKeyCallback*) callback; + if (gbpCallback->p->gbpInputsPosted == 2) { + return 0x30F; + } + return 0x3FF; +} + +bool _gbpSioLoad(struct GBASIODriver* driver) { + struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver; + gbp->p->gbpTxPosition = 0; + gbp->p->gbpNextEvent = INT_MAX; + return true; +} + +uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { + struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver; + if (address == REG_SIOCNT) { + if (value & 0x0080) { + if (gbp->p->gbpTxPosition <= 16 && gbp->p->gbpTxPosition > 0) { + uint32_t rx = gbp->p->p->memory.io[REG_SIODATA32_LO >> 1] | (gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] << 16); + uint32_t expected = _gbpRxData[gbp->p->gbpTxPosition]; + // TODO: Check expected + uint32_t mask = 0; + if (gbp->p->gbpTxPosition == 15) { + mask = 0x22; + if (gbp->p->p->rumble) { + gbp->p->p->rumble->setRumble(gbp->p->p->rumble, (rx & mask) == mask); + } + } + } + gbp->p->gbpNextEvent = 2048; + } + value &= 0x78FB; + } + return value; +} + +int32_t _gbpSioProcessEvents(struct GBASIODriver* driver, int32_t cycles) { + struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver; + gbp->p->gbpNextEvent -= cycles; + if (gbp->p->gbpNextEvent <= 0) { + uint32_t tx = 0; + if (gbp->p->gbpTxPosition <= 16) { + tx = _gbpTxData[gbp->p->gbpTxPosition]; + ++gbp->p->gbpTxPosition; + } + gbp->p->p->memory.io[REG_SIODATA32_LO >> 1] = tx; + gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] = tx >> 16; + if (gbp->d.p->normalControl.irq) { + GBARaiseIRQ(gbp->p->p, IRQ_SIO); + } + gbp->d.p->normalControl.start = 0; + gbp->p->p->memory.io[REG_SIOCNT >> 1] = gbp->d.p->siocnt; + gbp->p->gbpNextEvent = INT_MAX; + } + return gbp->p->gbpNextEvent; +} + // == Serialization void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASerializedState* state) { diff --git a/src/gba/hardware.h b/src/gba/hardware.h index 5ba17c221..8a1930685 100644 --- a/src/gba/hardware.h +++ b/src/gba/hardware.h @@ -7,6 +7,7 @@ #define GBA_HARDWARE_H #include "util/common.h" +#include "gba/interface.h" #include "macros.h" @@ -14,27 +15,6 @@ #define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL) -struct GBARotationSource { - void (*sample)(struct GBARotationSource*); - - int32_t (*readTiltX)(struct GBARotationSource*); - int32_t (*readTiltY)(struct GBARotationSource*); - - int32_t (*readGyroZ)(struct GBARotationSource*); -}; - -struct GBALuminanceSource { - void (*sample)(struct GBALuminanceSource*); - - uint8_t (*readLuminance)(struct GBALuminanceSource*); -}; - -struct GBARTCSource { - void (*sample)(struct GBARTCSource*); - - time_t (*unixTime)(struct GBARTCSource*); -}; - struct GBARTCGenericSource { struct GBARTCSource d; struct GBA* p; @@ -102,6 +82,16 @@ struct GBARumble { void (*setRumble)(struct GBARumble*, int enable); }; +struct GBAGBPKeyCallback { + struct GBAKeyCallback d; + struct GBACartridgeHardware* p; +}; + +struct GBAGBPSIODriver { + struct GBASIODriver d; + struct GBACartridgeHardware* p; +}; + DECL_BITFIELD(GPIOPin, uint16_t); struct GBACartridgeHardware { @@ -125,6 +115,13 @@ struct GBACartridgeHardware { uint16_t tiltX; uint16_t tiltY; int tiltState; + + bool gbpRunning; + int gbpInputsPosted; + int gbpTxPosition; + struct GBAGBPKeyCallback gbpCallback; + struct GBAGBPSIODriver gbpDriver; + int32_t gbpNextEvent; }; void GBAHardwareInit(struct GBACartridgeHardware* gpio, uint16_t* gpioBase); @@ -141,6 +138,7 @@ void GBAHardwareTiltWrite(struct GBACartridgeHardware* gpio, uint32_t address, u uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* gpio, uint32_t address); struct GBAVideo; +void GBAHardwarePlayerUpdate(struct GBA* gba); bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video); void GBARTCGenericSourceInit(struct GBARTCGenericSource* rtc, struct GBA* gba); diff --git a/src/gba/interface.h b/src/gba/interface.h new file mode 100644 index 000000000..7b5e1c9f5 --- /dev/null +++ b/src/gba/interface.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef INTERFACE_H +#define INTERFACE_H + +#include "util/common.h" + +enum GBALogLevel { + GBA_LOG_FATAL = 0x01, + GBA_LOG_ERROR = 0x02, + GBA_LOG_WARN = 0x04, + GBA_LOG_INFO = 0x08, + GBA_LOG_DEBUG = 0x10, + GBA_LOG_STUB = 0x20, + + GBA_LOG_GAME_ERROR = 0x100, + GBA_LOG_SWI = 0x200, + GBA_LOG_STATUS = 0x400, + GBA_LOG_SIO = 0x800, + + GBA_LOG_ALL = 0xF3F, +}; + +enum GBAKey { + GBA_KEY_A = 0, + GBA_KEY_B = 1, + GBA_KEY_SELECT = 2, + GBA_KEY_START = 3, + GBA_KEY_RIGHT = 4, + GBA_KEY_LEFT = 5, + GBA_KEY_UP = 6, + GBA_KEY_DOWN = 7, + GBA_KEY_R = 8, + GBA_KEY_L = 9, + GBA_KEY_MAX, + GBA_KEY_NONE = -1 +}; + +enum GBASIOMode { + SIO_NORMAL_8 = 0, + SIO_NORMAL_32 = 1, + SIO_MULTI = 2, + SIO_UART = 3, + SIO_GPIO = 8, + SIO_JOYBUS = 12 +}; + +struct GBA; +struct GBAAudio; +struct GBASIO; +struct GBAThread; +struct GBAVideoRenderer; + +typedef void (*GBALogHandler)(struct GBAThread*, enum GBALogLevel, const char* format, va_list args); + +struct GBAAVStream { + void (*postVideoFrame)(struct GBAAVStream*, struct GBAVideoRenderer* renderer); + void (*postAudioFrame)(struct GBAAVStream*, int16_t left, int16_t right); + void (*postAudioBuffer)(struct GBAAVStream*, struct GBAAudio*); +}; + +struct GBAKeyCallback { + uint16_t (*readKeys)(struct GBAKeyCallback*); +}; + +struct GBARotationSource { + void (*sample)(struct GBARotationSource*); + + int32_t (*readTiltX)(struct GBARotationSource*); + int32_t (*readTiltY)(struct GBARotationSource*); + + int32_t (*readGyroZ)(struct GBARotationSource*); +}; + +struct GBALuminanceSource { + void (*sample)(struct GBALuminanceSource*); + + uint8_t (*readLuminance)(struct GBALuminanceSource*); +}; + +struct GBARTCSource { + void (*sample)(struct GBARTCSource*); + + time_t (*unixTime)(struct GBARTCSource*); +}; + +struct GBASIODriver { + struct GBASIO* p; + + bool (*init)(struct GBASIODriver* driver); + void (*deinit)(struct GBASIODriver* driver); + bool (*load)(struct GBASIODriver* driver); + bool (*unload)(struct GBASIODriver* driver); + uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value); + int32_t (*processEvents)(struct GBASIODriver* driver, int32_t cycles); +}; + +#endif diff --git a/src/gba/io.c b/src/gba/io.c index a93227669..f5e383713 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -584,14 +584,18 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { case REG_KEYINPUT: if (gba->rr && gba->rr->isPlaying(gba->rr)) { return 0x3FF ^ gba->rr->queryInput(gba->rr); - } else if (gba->keySource) { - uint16_t input = *gba->keySource; + } else { + uint16_t input = 0x3FF; + if (gba->keyCallback) { + input = gba->keyCallback->readKeys(gba->keyCallback); + } else if (gba->keySource) { + input = *gba->keySource; + } if (gba->rr && gba->rr->isRecording(gba->rr)) { gba->rr->logInput(gba->rr, input); } return 0x3FF ^ input; } - break; case REG_SIOCNT: return gba->sio.siocnt; diff --git a/src/gba/sio.h b/src/gba/sio.h index 61e979385..5963698d9 100644 --- a/src/gba/sio.h +++ b/src/gba/sio.h @@ -8,36 +8,16 @@ #include "util/common.h" +#include "gba/interface.h" + #define MAX_GBAS 4 extern const int GBASIOCyclesPerTransfer[4][MAX_GBAS]; -enum GBASIOMode { - SIO_NORMAL_8 = 0, - SIO_NORMAL_32 = 1, - SIO_MULTI = 2, - SIO_UART = 3, - SIO_GPIO = 8, - SIO_JOYBUS = 12 -}; - enum { RCNT_INITIAL = 0x8000 }; -struct GBASIO; - -struct GBASIODriver { - struct GBASIO* p; - - bool (*init)(struct GBASIODriver* driver); - void (*deinit)(struct GBASIODriver* driver); - bool (*load)(struct GBASIODriver* driver); - bool (*unload)(struct GBASIODriver* driver); - uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value); - int32_t (*processEvents)(struct GBASIODriver* driver, int32_t cycles); -}; - struct GBASIODriverSet { struct GBASIODriver* normal; struct GBASIODriver* multiplayer; From 5ed05dc66f3cff5e065a70ea79e857ddcec0570e Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 13 Jul 2015 21:19:01 -0700 Subject: [PATCH 17/52] GBA: Get GB Player working with savestates --- src/gba/hardware.c | 22 ++++++++++------------ src/gba/hardware.h | 8 ++++---- src/gba/serialize.h | 17 ++++++++++++----- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/gba/hardware.c b/src/gba/hardware.c index a2e61c927..7d5a16d7a 100644 --- a/src/gba/hardware.c +++ b/src/gba/hardware.c @@ -27,7 +27,6 @@ static void _rumbleReadPins(struct GBACartridgeHardware* hw); static void _lightReadPins(struct GBACartridgeHardware* hw); static uint16_t _gbpRead(struct GBAKeyCallback*); -static bool _gbpSioLoad(struct GBASIODriver* driver); static uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); static int32_t _gbpSioProcessEvents(struct GBASIODriver* driver, int32_t cycles); @@ -52,7 +51,6 @@ void GBAHardwareClear(struct GBACartridgeHardware* hw) { hw->direction = GPIO_WRITE_ONLY; hw->pinState = 0; hw->direction = 0; - hw->gbpRunning = false; } void GBAHardwareGPIOWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value) { @@ -501,7 +499,7 @@ bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video) { } void GBAHardwarePlayerUpdate(struct GBA* gba) { - if (gba->memory.hw.gbpRunning) { + if (gba->memory.hw.devices & HW_GB_PLAYER) { if (GBAHardwarePlayerCheckScreen(&gba->video)) { ++gba->memory.hw.gbpInputsPosted; gba->memory.hw.gbpInputsPosted %= 3; @@ -517,17 +515,18 @@ void GBAHardwarePlayerUpdate(struct GBA* gba) { return; } if (GBAHardwarePlayerCheckScreen(&gba->video)) { - gba->memory.hw.gbpRunning = true; + gba->memory.hw.devices |= HW_GB_PLAYER; gba->memory.hw.gbpCallback.d.readKeys = _gbpRead; gba->memory.hw.gbpCallback.p = &gba->memory.hw; gba->memory.hw.gbpDriver.d.init = 0; gba->memory.hw.gbpDriver.d.deinit = 0; - gba->memory.hw.gbpDriver.d.load = _gbpSioLoad; + gba->memory.hw.gbpDriver.d.load = 0; gba->memory.hw.gbpDriver.d.unload = 0; gba->memory.hw.gbpDriver.d.writeRegister = _gbpSioWriteRegister; gba->memory.hw.gbpDriver.d.processEvents = _gbpSioProcessEvents; gba->memory.hw.gbpDriver.p = &gba->memory.hw; gba->memory.hw.gbpInputsPosted = 0; + gba->memory.hw.gbpNextEvent = INT_MAX; gba->keyCallback = &gba->memory.hw.gbpCallback.d; GBASIOSetDriver(&gba->sio, &gba->memory.hw.gbpDriver.d, SIO_NORMAL_32); } @@ -541,13 +540,6 @@ uint16_t _gbpRead(struct GBAKeyCallback* callback) { return 0x3FF; } -bool _gbpSioLoad(struct GBASIODriver* driver) { - struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver; - gbp->p->gbpTxPosition = 0; - gbp->p->gbpNextEvent = INT_MAX; - return true; -} - uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver; if (address == REG_SIOCNT) { @@ -608,6 +600,9 @@ void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASeria state->hw.lightCounter = hw->lightCounter; state->hw.lightSample = hw->lightSample; state->hw.lightEdge = hw->lightEdge; + state->hw.gbpInputsPosted = hw->gbpInputsPosted; + state->hw.gbpTxPosition = hw->gbpTxPosition; + state->hw.gbpNextEvent = hw->gbpNextEvent; } void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASerializedState* state) { @@ -623,4 +618,7 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer hw->lightCounter = state->hw.lightCounter; hw->lightSample = state->hw.lightSample; hw->lightEdge = state->hw.lightEdge; + hw->gbpInputsPosted = state->hw.gbpInputsPosted; + hw->gbpTxPosition = state->hw.gbpTxPosition; + hw->gbpNextEvent = state->hw.gbpNextEvent; } diff --git a/src/gba/hardware.h b/src/gba/hardware.h index 8a1930685..53a4899a7 100644 --- a/src/gba/hardware.h +++ b/src/gba/hardware.h @@ -33,7 +33,8 @@ enum GBAHardwareDevice { HW_RUMBLE = 2, HW_LIGHT_SENSOR = 4, HW_GYRO = 8, - HW_TILT = 16 + HW_TILT = 16, + HW_GB_PLAYER = 32 }; enum GPIORegister { @@ -116,12 +117,11 @@ struct GBACartridgeHardware { uint16_t tiltY; int tiltState; - bool gbpRunning; - int gbpInputsPosted; + unsigned gbpInputsPosted; int gbpTxPosition; + int32_t gbpNextEvent; struct GBAGBPKeyCallback gbpCallback; struct GBAGBPSIODriver gbpDriver; - int32_t gbpNextEvent; }; void GBAHardwareInit(struct GBACartridgeHardware* gpio, uint16_t* gpioBase); diff --git a/src/gba/serialize.h b/src/gba/serialize.h index 1ccfd123c..4c23f4bb7 100644 --- a/src/gba/serialize.h +++ b/src/gba/serialize.h @@ -136,7 +136,8 @@ extern const uint32_t GBA_SAVESTATE_MAGIC; * | bit 2: Has light sensor value * | bit 3: Has gyroscope value * | bit 4: Has tilt values - * | bits 5 - 7: Reserved + * | bit 5: Has Game Boy Player attached + * | bits 6 - 7: Reserved * | 0x002B8 - 0x002B9: Gyroscope sample * | 0x002BA - 0x002BB: Tilt x sample * | 0x002BC - 0x002BD: Tilt y sample @@ -149,8 +150,11 @@ extern const uint32_t GBA_SAVESTATE_MAGIC; * | 0x002C0 - 0x002C0: Light sample * | 0x002C1 - 0x002C3: Flags * | bits 0 - 1: Tilt state machine - * | bits 2 - 31: Reserved - * 0x002C4 - 0x002DF: Reserved (leave zero) + * | bits 2 - 3: GB Player inputs posted + * | bits 4 - 8: GB Player transmit position + * | bits 9 - 23: Reserved + * 0x002C4 - 0x002C7: Game Boy Player next event + * 0x002C8 - 0x002DF: Reserved (leave zero) * 0x002E0 - 0x002EF: Savedata state * | 0x002E0 - 0x002E0: Savedata type * | 0x002E1 - 0x002E1: Savedata command (see savedata.h) @@ -282,10 +286,13 @@ struct GBASerializedState { unsigned lightCounter : 12; unsigned lightSample : 8; unsigned tiltState : 2; - unsigned : 22; + unsigned gbpInputsPosted : 2; + unsigned gbpTxPosition : 5; + unsigned : 15; + uint32_t gbpNextEvent : 32; } hw; - uint32_t reservedHardware[7]; + uint32_t reservedHardware[6]; struct { unsigned type : 8; From f50f98416bc9e5455b71732a6727eda9bf8d6064 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 13 Jul 2015 21:56:05 -0700 Subject: [PATCH 18/52] Qt: Add savestate load undo --- CHANGES | 1 + src/platform/qt/GameController.cpp | 24 ++++++++++++++++++++++++ src/platform/qt/GameController.h | 3 +++ src/platform/qt/Window.cpp | 10 ++++++++-- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 8c8f164c1..6c4170f45 100644 --- a/CHANGES +++ b/CHANGES @@ -24,6 +24,7 @@ Features: - Preliminary support for yanking out the game pak while a game is running - Thumb-drive mode by putting a file called portable.ini in the same folder - Configurable display driver, between software and OpenGL + - Undo-able savestate loading Bugfixes: - ARM7: Fix SWI and IRQ timings - GBA Audio: Force audio FIFOs to 32-bit diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 24cd47ab2..d7dc24c4f 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -51,6 +51,8 @@ GameController::GameController(QObject* parent) , m_inputController(nullptr) , m_multiplayer(nullptr) , m_stateSlot(1) + , m_backupLoadState(nullptr) + , m_backupSaveState(nullptr) { m_renderer = new GBAVideoSoftwareRenderer; GBAVideoSoftwareRendererCreate(m_renderer); @@ -162,6 +164,7 @@ GameController::~GameController() { GBACheatDeviceDestroy(&m_cheatDevice); delete m_renderer; delete[] m_drawContext; + delete m_backupLoadState; } void GameController::setMultiplayerController(MultiplayerController* controller) { @@ -561,6 +564,10 @@ void GameController::loadState(int slot) { } GBARunOnThread(&m_threadContext, [](GBAThread* context) { GameController* controller = static_cast(context->userData); + if (!controller->m_backupLoadState) { + controller->m_backupLoadState = new GBASerializedState; + } + GBASerialize(context->gba, controller->m_backupLoadState); if (GBALoadState(context, context->stateDir, controller->m_stateSlot)) { controller->frameAvailable(controller->m_drawContext); controller->stateLoaded(context); @@ -578,6 +585,23 @@ void GameController::saveState(int slot) { }); } +void GameController::loadBackupState() { + if (!m_backupLoadState) { + return; + } + + GBARunOnThread(&m_threadContext, [](GBAThread* context) { + GameController* controller = static_cast(context->userData); + if (GBADeserialize(context->gba, controller->m_backupLoadState)) { + GBALog(context->gba, GBA_LOG_STATUS, "Undid state load"); + controller->frameAvailable(controller->m_drawContext); + controller->stateLoaded(context); + } + delete controller->m_backupLoadState; + controller->m_backupLoadState = nullptr; + }); +} + void GameController::setVideoSync(bool set) { m_videoSync = set; if (!m_turbo) { diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index 349a39ce6..6160a6818 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -120,6 +120,7 @@ public slots: void setFPSTarget(float fps); void loadState(int slot = 0); void saveState(int slot = 0); + void loadBackupState(); void setVideoSync(bool); void setAudioSync(bool); void setFrameskip(int); @@ -192,6 +193,8 @@ private: bool m_wasPaused; int m_stateSlot; + GBASerializedState* m_backupLoadState; + GBASerializedState* m_backupSaveState; // TODO: Use this InputController* m_inputController; MultiplayerController* m_multiplayer; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 71f5e02d4..d49eaeace 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -697,6 +697,14 @@ void Window::setupMenu(QMenuBar* menubar) { m_gameActions.append(quickSave); addControlledAction(quickSaveMenu, quickSave, "quickSave"); + quickLoadMenu->addSeparator(); + + QAction* undoLoadState = new QAction(tr("Undo load state"), quickLoadMenu); + undoLoadState->setShortcut(tr("F11")); + connect(undoLoadState, SIGNAL(triggered()), m_controller, SLOT(loadBackupState())); + m_gameActions.append(undoLoadState); + addControlledAction(quickLoadMenu, undoLoadState, "undoLoadState"); + quickLoadMenu->addSeparator(); quickSaveMenu->addSeparator(); @@ -961,14 +969,12 @@ void Window::setupMenu(QMenuBar* menubar) { #ifdef USE_FFMPEG QAction* recordOutput = new QAction(tr("Record output..."), avMenu); - recordOutput->setShortcut(tr("F11")); connect(recordOutput, SIGNAL(triggered()), this, SLOT(openVideoWindow())); addControlledAction(avMenu, recordOutput, "recordOutput"); #endif #ifdef USE_MAGICK QAction* recordGIF = new QAction(tr("Record GIF..."), avMenu); - recordGIF->setShortcut(tr("Shift+F11")); connect(recordGIF, SIGNAL(triggered()), this, SLOT(openGIFWindow())); addControlledAction(avMenu, recordGIF, "recordGIF"); #endif From b3cf9ec162885dbdcf5aa6e4e5abbcd112d9ed5b Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 13 Jul 2015 22:09:20 -0700 Subject: [PATCH 19/52] Qt: Only hide cursor if the display is the top widget --- src/platform/qt/Window.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index d49eaeace..472a39163 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -129,7 +129,9 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float))); connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS())); connect(m_display, &Display::hideCursor, [this]() { - setCursor(Qt::BlankCursor); + if (static_cast(m_screenWidget->layout())->currentWidget() == m_display) { + setCursor(Qt::BlankCursor); + } }); connect(m_display, &Display::showCursor, [this]() { unsetCursor(); @@ -1122,6 +1124,7 @@ void Window::setupMenu(QMenuBar* menubar) { void Window::attachWidget(QWidget* widget) { m_screenWidget->layout()->addWidget(widget); + unsetCursor(); static_cast(m_screenWidget->layout())->setCurrentWidget(widget); } From 73518ceda5abab61009b1c5e49d32eb534b5c9ae Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 14 Jul 2015 20:45:29 -0700 Subject: [PATCH 20/52] Qt: Add savestate save undo --- CHANGES | 2 +- src/platform/qt/GameController.cpp | 23 +++++++++++++++++++++++ src/platform/qt/GameController.h | 3 ++- src/platform/qt/Window.cpp | 6 ++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 6c4170f45..b7562b903 100644 --- a/CHANGES +++ b/CHANGES @@ -24,7 +24,7 @@ Features: - Preliminary support for yanking out the game pak while a game is running - Thumb-drive mode by putting a file called portable.ini in the same folder - Configurable display driver, between software and OpenGL - - Undo-able savestate loading + - Undo-able savestate loading and saving Bugfixes: - ARM7: Fix SWI and IRQ timings - GBA Audio: Force audio FIFOs to 32-bit diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index d7dc24c4f..893a5bc02 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -581,6 +581,12 @@ void GameController::saveState(int slot) { } GBARunOnThread(&m_threadContext, [](GBAThread* context) { GameController* controller = static_cast(context->userData); + VFile* vf = GBAGetState(context->gba, context->stateDir, controller->m_stateSlot, false); + if (vf) { + controller->m_backupSaveState.resize(vf->size(vf)); + vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size()); + vf->close(vf); + } GBASaveState(context, context->stateDir, controller->m_stateSlot, true); }); } @@ -602,6 +608,23 @@ void GameController::loadBackupState() { }); } +void GameController::saveBackupState() { + if (m_backupSaveState.isEmpty()) { + return; + } + + GBARunOnThread(&m_threadContext, [](GBAThread* context) { + GameController* controller = static_cast(context->userData); + VFile* vf = GBAGetState(context->gba, context->stateDir, controller->m_stateSlot, true); + if (vf) { + vf->write(vf, controller->m_backupSaveState.constData(), controller->m_backupSaveState.size()); + vf->close(vf); + GBALog(context->gba, GBA_LOG_STATUS, "Undid state save"); + } + controller->m_backupSaveState.clear(); + }); +} + void GameController::setVideoSync(bool set) { m_videoSync = set; if (!m_turbo) { diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index 6160a6818..0fb82ba14 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -121,6 +121,7 @@ public slots: void loadState(int slot = 0); void saveState(int slot = 0); void loadBackupState(); + void saveBackupState(); void setVideoSync(bool); void setAudioSync(bool); void setFrameskip(int); @@ -194,7 +195,7 @@ private: int m_stateSlot; GBASerializedState* m_backupLoadState; - GBASerializedState* m_backupSaveState; // TODO: Use this + QByteArray m_backupSaveState; InputController* m_inputController; MultiplayerController* m_multiplayer; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 472a39163..3c217c861 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -707,6 +707,12 @@ void Window::setupMenu(QMenuBar* menubar) { m_gameActions.append(undoLoadState); addControlledAction(quickLoadMenu, undoLoadState, "undoLoadState"); + QAction* undoSaveState = new QAction(tr("Undo save state"), quickSaveMenu); + undoSaveState->setShortcut(tr("Shift+F11")); + connect(undoSaveState, SIGNAL(triggered()), m_controller, SLOT(saveBackupState())); + m_gameActions.append(undoSaveState); + addControlledAction(quickLoadMenu, undoSaveState, "undoSaveState"); + quickLoadMenu->addSeparator(); quickSaveMenu->addSeparator(); From 8ef50827fdc358fbdd7af8ac8e3ca548e305294b Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 14 Jul 2015 22:51:27 -0700 Subject: [PATCH 21/52] Qt: Controller profiles now store shortcut settings --- CHANGES | 1 + src/platform/qt/InputController.cpp | 1 + src/platform/qt/InputController.h | 3 ++ src/platform/qt/ShortcutController.cpp | 55 ++++++++++++++++++++------ src/platform/qt/ShortcutController.h | 9 +++++ src/platform/qt/Window.cpp | 1 + 6 files changed, 59 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index b7562b903..92a6b6d7d 100644 --- a/CHANGES +++ b/CHANGES @@ -25,6 +25,7 @@ Features: - Thumb-drive mode by putting a file called portable.ini in the same folder - Configurable display driver, between software and OpenGL - Undo-able savestate loading and saving + - Controller profiles now store shortcut settings Bugfixes: - ARM7: Fix SWI and IRQ timings - GBA Audio: Force audio FIFOs to 32-bit diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index ec5ef6b37..6b0773673 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -108,6 +108,7 @@ void InputController::loadConfiguration(uint32_t type) { void InputController::loadProfile(uint32_t type, const QString& profile) { GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData()); recalibrateAxes(); + emit profileLoaded(profile); } void InputController::saveConfiguration() { diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index 05493db8e..65bdec0f0 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -77,6 +77,9 @@ public: GBARumble* rumble(); GBARotationSource* rotationSource(); +signals: + void profileLoaded(const QString& profile); + public slots: void testGamepad(int type); diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index 34993a386..68e27d374 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -239,6 +239,9 @@ void ShortcutController::updateButton(const QModelIndex& index, int button) { } if (m_config) { m_config->setQtOption(item->name(), button, BUTTON_SECTION); + if (!m_profile.isNull()) { + m_config->setQtOption(item->name(), button, BUTTON_PROFILE_SECTION + m_profile); + } } emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), createIndex(index.row(), 2, index.internalPointer())); @@ -272,6 +275,9 @@ void ShortcutController::updateAxis(const QModelIndex& index, int axis, GamepadA d = '-'; } m_config->setQtOption(item->name(), QString("%1%2").arg(d).arg(axis), AXIS_SECTION); + if (!m_profile.isNull()) { + m_config->setQtOption(item->name(), QString("%1%2").arg(d).arg(axis), AXIS_PROFILE_SECTION + m_profile); + } } emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), createIndex(index.row(), 2, index.internalPointer())); @@ -365,6 +371,9 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) { } void ShortcutController::loadShortcuts(ShortcutItem* item) { + if (item->name().isNull()) { + return; + } QVariant shortcut = m_config->getQtOption(item->name(), KEY_SECTION); if (!shortcut.isNull()) { QKeySequence keySequence(shortcut.toString()); @@ -377,19 +386,32 @@ void ShortcutController::loadShortcuts(ShortcutItem* item) { } item->setShortcut(keySequence); } - QVariant button = m_config->getQtOption(item->name(), BUTTON_SECTION); + loadGamepadShortcuts(item); +} + +void ShortcutController::loadGamepadShortcuts(ShortcutItem* item) { + if (item->name().isNull()) { + return; + } + QVariant button = m_config->getQtOption(item->name(), !m_profile.isNull() ? BUTTON_PROFILE_SECTION + m_profile : BUTTON_SECTION); + int oldButton = item->button(); + if (oldButton >= 0) { + m_buttons.take(oldButton); + item->setButton(-1); + } if (!button.isNull()) { - int oldButton = item->button(); item->setButton(button.toInt()); - if (oldButton >= 0) { - m_buttons.take(oldButton); - } m_buttons[button.toInt()] = item; } - QVariant axis = m_config->getQtOption(item->name(), AXIS_SECTION); + + QVariant axis = m_config->getQtOption(item->name(), !m_profile.isNull() ? AXIS_PROFILE_SECTION + m_profile : AXIS_SECTION); + int oldAxis = item->axis(); + GamepadAxisEvent::Direction oldDirection = item->direction(); + if (oldAxis >= 0) { + m_axes.take(qMakePair(oldAxis, oldDirection)); + item->setAxis(-1, GamepadAxisEvent::NEUTRAL); + } if (!axis.isNull()) { - int oldAxis = item->axis(); - GamepadAxisEvent::Direction oldDirection = item->direction(); QString axisDesc = axis.toString(); if (axisDesc.size() >= 2) { GamepadAxisEvent::Direction direction = GamepadAxisEvent::NEUTRAL; @@ -403,9 +425,6 @@ void ShortcutController::loadShortcuts(ShortcutItem* item) { int axis = axisDesc.mid(1).toInt(&ok); if (ok) { item->setAxis(axis, direction); - if (oldAxis >= 0) { - m_axes.take(qMakePair(oldAxis, oldDirection)); - } m_axes[qMakePair(axis, direction)] = item; } } @@ -432,6 +451,20 @@ QKeySequence ShortcutController::keyEventToSequence(const QKeyEvent* event) { return QKeySequence(modifier + key); } +void ShortcutController::loadProfile(const QString& profile) { + m_profile = profile; + onSubitems(&m_rootMenu, [this](ShortcutItem* item) { + loadGamepadShortcuts(item); + }); +} + +void ShortcutController::onSubitems(ShortcutItem* item, std::function func) { + for (ShortcutItem& subitem : item->items()) { + func(&subitem); + onSubitems(&subitem, func); + } +} + ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent) : m_action(action) , m_shortcut(action->shortcut()) diff --git a/src/platform/qt/ShortcutController.h b/src/platform/qt/ShortcutController.h index f8be878bf..5869c25f1 100644 --- a/src/platform/qt/ShortcutController.h +++ b/src/platform/qt/ShortcutController.h @@ -29,6 +29,8 @@ private: constexpr static const char* const KEY_SECTION = "shortcutKey"; constexpr static const char* const BUTTON_SECTION = "shortcutButton"; constexpr static const char* const AXIS_SECTION = "shortcutAxis"; + constexpr static const char* const BUTTON_PROFILE_SECTION = "shortcutProfileButton."; + constexpr static const char* const AXIS_PROFILE_SECTION = "shortcutProfileAxis."; class ShortcutItem { public: @@ -84,6 +86,7 @@ public: ShortcutController(QObject* parent = nullptr); void setConfigController(ConfigController* controller); + void setProfile(const QString& profile); virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; @@ -111,6 +114,9 @@ public: static QKeySequence keyEventToSequence(const QKeyEvent*); +public slots: + void loadProfile(const QString& profile); + protected: bool eventFilter(QObject*, QEvent*) override; @@ -118,6 +124,8 @@ private: ShortcutItem* itemAt(const QModelIndex& index); const ShortcutItem* itemAt(const QModelIndex& index) const; void loadShortcuts(ShortcutItem*); + void loadGamepadShortcuts(ShortcutItem*); + void onSubitems(ShortcutItem*, std::function func); ShortcutItem m_rootMenu; QMap m_menuMap; @@ -125,6 +133,7 @@ private: QMap, ShortcutItem*> m_axes; QMap m_heldKeys; ConfigController* m_config; + QString m_profile; }; } diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 3c217c861..2efab9e8e 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -136,6 +136,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) connect(m_display, &Display::showCursor, [this]() { unsetCursor(); }); + connect(&m_inputController, SIGNAL(profileLoaded(const QString&)), m_shortcutController, SLOT(loadProfile(const QString&))); m_log.setLevels(GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL | GBA_LOG_STATUS); m_fpsTimer.setInterval(FPS_TIMER_INTERVAL); From 97a5a240007a7f9986c0a810699a80e4638ec422 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 14 Jul 2015 23:13:07 -0700 Subject: [PATCH 22/52] All: Fix project name not getting set when rebuilding --- version.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/version.cmake b/version.cmake index 718ecdb5f..5ec40e6bf 100644 --- a/version.cmake +++ b/version.cmake @@ -1,3 +1,6 @@ +if(NOT PROJECT_NAME) + set(PROJECT_NAME "mGBA") +endif() set(LIB_VERSION_MAJOR 0) set(LIB_VERSION_MINOR 3) set(LIB_VERSION_PATCH 0) From b9c276ee30da6edd59468f5f775abd08d1ceec3a Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 15 Jul 2015 00:28:53 -0700 Subject: [PATCH 23/52] Qt: Put undo savestate in the correct menu --- src/platform/qt/Window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 2efab9e8e..8ce43001e 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -712,7 +712,7 @@ void Window::setupMenu(QMenuBar* menubar) { undoSaveState->setShortcut(tr("Shift+F11")); connect(undoSaveState, SIGNAL(triggered()), m_controller, SLOT(saveBackupState())); m_gameActions.append(undoSaveState); - addControlledAction(quickLoadMenu, undoSaveState, "undoSaveState"); + addControlledAction(quickSaveMenu, undoSaveState, "undoSaveState"); quickLoadMenu->addSeparator(); quickSaveMenu->addSeparator(); From 963758c348585650fd22c9e1647c2ccec4aea568 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 16 Jul 2015 23:45:55 -0700 Subject: [PATCH 24/52] Qt: Default controller profiles, with a few included already --- CHANGES | 1 + src/gba/input.c | 10 +- src/gba/input.h | 2 +- src/platform/qt/CMakeLists.txt | 1 + src/platform/qt/InputController.cpp | 9 +- src/platform/qt/InputProfile.cpp | 170 +++++++++++++++++++++++++ src/platform/qt/InputProfile.h | 78 ++++++++++++ src/platform/qt/ShortcutController.cpp | 30 ++++- src/platform/qt/ShortcutController.h | 4 +- src/util/configuration.c | 4 + src/util/configuration.h | 1 + 11 files changed, 297 insertions(+), 13 deletions(-) create mode 100644 src/platform/qt/InputProfile.cpp create mode 100644 src/platform/qt/InputProfile.h diff --git a/CHANGES b/CHANGES index 92a6b6d7d..af39fd1f2 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,7 @@ Features: - Configurable display driver, between software and OpenGL - Undo-able savestate loading and saving - Controller profiles now store shortcut settings + - Default controller profiles for several common controllers Bugfixes: - ARM7: Fix SWI and IRQ timings - GBA Audio: Force audio FIFOs to 32-bit diff --git a/src/gba/input.c b/src/gba/input.c index 65cf7a650..734d7f4e4 100644 --- a/src/gba/input.c +++ b/src/gba/input.c @@ -257,7 +257,10 @@ void _unbindAxis(uint32_t axis, void* dp, void* user) { } } -static void _loadAll(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) { +static bool _loadAll(struct GBAInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) { + if (!ConfigurationHasSection(config, sectionName)) { + return false; + } _loadKey(map, type, sectionName, config, GBA_KEY_A, "A"); _loadKey(map, type, sectionName, config, GBA_KEY_B, "B"); _loadKey(map, type, sectionName, config, GBA_KEY_L, "L"); @@ -279,6 +282,7 @@ static void _loadAll(struct GBAInputMap* map, uint32_t type, const char* section _loadAxis(map, type, sectionName, config, GBA_KEY_DOWN, "Down"); _loadAxis(map, type, sectionName, config, GBA_KEY_LEFT, "Left"); _loadAxis(map, type, sectionName, config, GBA_KEY_RIGHT, "Right"); + return true; } static void _saveAll(const struct GBAInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config) { @@ -469,11 +473,11 @@ void GBAInputMapSave(const struct GBAInputMap* map, uint32_t type, struct Config _saveAll(map, type, sectionName, config); } -void GBAInputProfileLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) { +bool GBAInputProfileLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) { char sectionName[SECTION_NAME_MAX]; snprintf(sectionName, SECTION_NAME_MAX, "input-profile.%s", profile); sectionName[SECTION_NAME_MAX - 1] = '\0'; - _loadAll(map, type, sectionName, config); + return _loadAll(map, type, sectionName, config); } void GBAInputProfileSave(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, const char* profile) { diff --git a/src/gba/input.h b/src/gba/input.h index 8610b6f24..b83237557 100644 --- a/src/gba/input.h +++ b/src/gba/input.h @@ -45,7 +45,7 @@ void GBAInputEnumerateAxes(const struct GBAInputMap*, uint32_t type, void (handl void GBAInputMapLoad(struct GBAInputMap*, uint32_t type, const struct Configuration*); void GBAInputMapSave(const struct GBAInputMap*, uint32_t type, struct Configuration*); -void GBAInputProfileLoad(struct GBAInputMap*, uint32_t type, const struct Configuration*, const char* profile); +bool GBAInputProfileLoad(struct GBAInputMap*, uint32_t type, const struct Configuration*, const char* profile); void GBAInputProfileSave(const struct GBAInputMap*, uint32_t type, struct Configuration*, const char* profile); const char* GBAInputGetPreferredDevice(const struct Configuration*, uint32_t type, int playerId); diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index a6ff2aea2..e7db28908 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -61,6 +61,7 @@ set(SOURCE_FILES GamepadAxisEvent.cpp GamepadButtonEvent.cpp InputController.cpp + InputProfile.cpp KeyEditor.cpp LoadSaveState.cpp LogController.cpp diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 6b0773673..e17cd16e6 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -8,6 +8,7 @@ #include "ConfigController.h" #include "GamepadAxisEvent.h" #include "GamepadButtonEvent.h" +#include "InputProfile.h" #include #include @@ -106,8 +107,14 @@ void InputController::loadConfiguration(uint32_t type) { } void InputController::loadProfile(uint32_t type, const QString& profile) { - GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData()); + bool loaded = GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData()); recalibrateAxes(); + if (!loaded) { + const InputProfile* ip = InputProfile::findProfile(profile); + if (ip) { + ip->apply(this); + } + } emit profileLoaded(profile); } diff --git a/src/platform/qt/InputProfile.cpp b/src/platform/qt/InputProfile.cpp new file mode 100644 index 000000000..3afa3d96d --- /dev/null +++ b/src/platform/qt/InputProfile.cpp @@ -0,0 +1,170 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "InputProfile.h" + +#include "InputController.h" + +#include + +using namespace QGBA; + +const InputProfile InputProfile::s_defaultMaps[] = { + { + "PLAYSTATION\\(R\\)3 Controller", // DualShock 3 (OS X) + (int[GBA_KEY_MAX]) { + /*keyA */ 13, + /*keyB */ 14, + /*keySelect */ 0, + /*keyStart */ 3, + /*keyRight */ -1, + /*keyLeft */ -1, + /*keyUp */ -1, + /*keyDown */ -1, + /*keyR */ 11, + /*keyL */ 10 + }, + (ShortcutButton[]) { + {"loadState", 15}, + {"saveState", 12}, + {"holdFastForward", 9}, + {"holdRewind", 8}, + {} + } + }, + { + "Wiimote \\(..-..-..-..-..-..\\)", // WJoy (OS X) + (int[GBA_KEY_MAX]) { + /*keyA */ 15, + /*keyB */ 16, + /*keySelect */ 7, + /*keyStart */ 6, + /*keyRight */ -1, + /*keyLeft */ -1, + /*keyUp */ -1, + /*keyDown */ -1, + /*keyR */ 20, + /*keyL */ 19 + }, + (ShortcutButton[]) { + {"loadState", 18}, + {"saveState", 17}, + {"holdFastForward", 22}, + {"holdRewind", 21}, + {} + } + }, + { + "Controller", // The Xbox 360 controller drivers on OS X are vague... + (int[GBA_KEY_MAX]) { + /*keyA */ 1, + /*keyB */ 0, + /*keySelect */ 9, + /*keyStart */ 8, + /*keyRight */ -1, + /*keyLeft */ -1, + /*keyUp */ -1, + /*keyDown */ -1, + /*keyR */ 5, + /*keyL */ 4 + }, + (ShortcutButton[]) { + {"loadState", 2}, + {"saveState", 3}, + {} + }, + (ShortcutAxis[]) { + {"holdFastForward", GamepadAxisEvent::Direction::POSITIVE, 5}, + {"holdRewind", GamepadAxisEvent::Direction::POSITIVE, 2}, + {} + } + }, +}; + +constexpr InputProfile::InputProfile(const char* name, + int keys[GBA_KEY_MAX], + const ShortcutButton* shortcutButtons, + const ShortcutAxis* shortcutAxes, + AxisValue axes[GBA_KEY_MAX], + const struct Coord& tiltAxis, + const struct Coord& gyroAxis, + float gyroSensitivity) + : m_profileName(name) + , m_keys { + keys[GBA_KEY_A], + keys[GBA_KEY_B], + keys[GBA_KEY_SELECT], + keys[GBA_KEY_START], + keys[GBA_KEY_RIGHT], + keys[GBA_KEY_LEFT], + keys[GBA_KEY_UP], + keys[GBA_KEY_DOWN], + keys[GBA_KEY_R], + keys[GBA_KEY_L] + } + , m_shortcutButtons(shortcutButtons) + , m_shortcutAxes(shortcutAxes) + , m_axes { + axes[GBA_KEY_A], + axes[GBA_KEY_B], + axes[GBA_KEY_SELECT], + axes[GBA_KEY_START], + axes[GBA_KEY_RIGHT], + axes[GBA_KEY_LEFT], + axes[GBA_KEY_UP], + axes[GBA_KEY_DOWN], + axes[GBA_KEY_R], + axes[GBA_KEY_L] + } + , m_tiltAxis(tiltAxis) + , m_gyroAxis(gyroAxis) + , m_gyroSensitivity(gyroSensitivity) +{ +} + +const InputProfile* InputProfile::findProfile(const QString& name) { + for (size_t i = 0; i < sizeof(s_defaultMaps) / sizeof(*s_defaultMaps); ++i) { + QRegExp re(s_defaultMaps[i].m_profileName); + if (re.exactMatch(name)) { + return &s_defaultMaps[i]; + } + } + return nullptr; +} + +void InputProfile::apply(InputController* controller) const { + for (size_t i = 0; i < GBA_KEY_MAX; ++i) { + controller->bindKey(SDL_BINDING_BUTTON, m_keys[i], static_cast(i)); + controller->bindAxis(SDL_BINDING_BUTTON, m_axes[i].axis, m_axes[i].direction, static_cast(i)); + } + controller->registerTiltAxisX(m_tiltAxis.x); + controller->registerTiltAxisY(m_tiltAxis.y); + controller->registerGyroAxisX(m_gyroAxis.x); + controller->registerGyroAxisY(m_gyroAxis.y); + controller->setGyroSensitivity(m_gyroSensitivity); +} + +bool InputProfile::lookupShortcutButton(const QString& shortcutName, int* button) const { + for (size_t i = 0; m_shortcutButtons[i].shortcut; ++i) { + const ShortcutButton& shortcut = m_shortcutButtons[i]; + if (QLatin1String(shortcut.shortcut) == shortcutName) { + *button = shortcut.button; + return true; + } + } + return false; +} + +bool InputProfile::lookupShortcutAxis(const QString& shortcutName, int* axis, GamepadAxisEvent::Direction* direction) const { + for (size_t i = 0; m_shortcutAxes[i].shortcut; ++i) { + const ShortcutAxis& shortcut = m_shortcutAxes[i]; + if (QLatin1String(shortcut.shortcut) == shortcutName) { + *axis = shortcut.axis; + *direction = shortcut.direction; + return true; + } + } + return false; +} diff --git a/src/platform/qt/InputProfile.h b/src/platform/qt/InputProfile.h new file mode 100644 index 000000000..8a710db5a --- /dev/null +++ b/src/platform/qt/InputProfile.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2013-2015 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef QGBA_INPUT_PROFILE +#define QGBA_INPUT_PROFILE + +#include "GamepadAxisEvent.h" + +extern "C" { +#include "gba/interface.h" +} + +namespace QGBA { + +class InputController; + +class InputProfile { +public: + static const InputProfile* findProfile(const QString& name); + + void apply(InputController*) const; + bool lookupShortcutButton(const QString& shortcut, int* button) const; + bool lookupShortcutAxis(const QString& shortcut, int* axis, GamepadAxisEvent::Direction* direction) const; + +private: + struct Coord { + int x; + int y; + }; + + struct AxisValue { + GamepadAxisEvent::Direction direction; + int axis; + }; + + struct ShortcutButton { + const char* shortcut; + int button; + }; + + struct ShortcutAxis { + const char* shortcut; + GamepadAxisEvent::Direction direction; + int axis; + }; + + constexpr InputProfile(const char* name, + int keys[GBA_KEY_MAX], + const ShortcutButton* shortcutButtons = (ShortcutButton[]) {{}}, + const ShortcutAxis* shortcutAxes = (ShortcutAxis[]) {{}}, + AxisValue axes[GBA_KEY_MAX] = (AxisValue[GBA_KEY_MAX]) { + {}, {}, {}, {}, + { GamepadAxisEvent::Direction::POSITIVE, 0 }, + { GamepadAxisEvent::Direction::NEGATIVE, 0 }, + { GamepadAxisEvent::Direction::NEGATIVE, 1 }, + { GamepadAxisEvent::Direction::POSITIVE, 1 }, + {}, {}}, + const struct Coord& tiltAxis = { 2, 3 }, + const struct Coord& gyroAxis = { 0, 1 }, + float gyroSensitivity = 2e+09f); + + static const InputProfile s_defaultMaps[]; + + const char* m_profileName; + const int m_keys[GBA_KEY_MAX]; + const AxisValue m_axes[GBA_KEY_MAX]; + const ShortcutButton* m_shortcutButtons; + const ShortcutAxis* m_shortcutAxes; + Coord m_tiltAxis; + Coord m_gyroAxis; + float m_gyroSensitivity; +}; + +} + +#endif diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index 68e27d374..2c0276956 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -7,6 +7,7 @@ #include "ConfigController.h" #include "GamepadButtonEvent.h" +#include "InputProfile.h" #include #include @@ -18,6 +19,7 @@ ShortcutController::ShortcutController(QObject* parent) : QAbstractItemModel(parent) , m_rootMenu(nullptr) , m_config(nullptr) + , m_profile(nullptr) { } @@ -239,8 +241,8 @@ void ShortcutController::updateButton(const QModelIndex& index, int button) { } if (m_config) { m_config->setQtOption(item->name(), button, BUTTON_SECTION); - if (!m_profile.isNull()) { - m_config->setQtOption(item->name(), button, BUTTON_PROFILE_SECTION + m_profile); + if (!m_profileName.isNull()) { + m_config->setQtOption(item->name(), button, BUTTON_PROFILE_SECTION + m_profileName); } } emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), @@ -275,8 +277,8 @@ void ShortcutController::updateAxis(const QModelIndex& index, int axis, GamepadA d = '-'; } m_config->setQtOption(item->name(), QString("%1%2").arg(d).arg(axis), AXIS_SECTION); - if (!m_profile.isNull()) { - m_config->setQtOption(item->name(), QString("%1%2").arg(d).arg(axis), AXIS_PROFILE_SECTION + m_profile); + if (!m_profileName.isNull()) { + m_config->setQtOption(item->name(), QString("%1%2").arg(d).arg(axis), AXIS_PROFILE_SECTION + m_profileName); } } emit dataChanged(createIndex(index.row(), 0, index.internalPointer()), @@ -393,24 +395,37 @@ void ShortcutController::loadGamepadShortcuts(ShortcutItem* item) { if (item->name().isNull()) { return; } - QVariant button = m_config->getQtOption(item->name(), !m_profile.isNull() ? BUTTON_PROFILE_SECTION + m_profile : BUTTON_SECTION); + QVariant button = m_config->getQtOption(item->name(), !m_profileName.isNull() ? BUTTON_PROFILE_SECTION + m_profileName : BUTTON_SECTION); int oldButton = item->button(); if (oldButton >= 0) { m_buttons.take(oldButton); item->setButton(-1); } + if (button.isNull() && m_profile) { + int buttonInt; + if (m_profile->lookupShortcutButton(item->name(), &buttonInt)) { + button = buttonInt; + } + } if (!button.isNull()) { item->setButton(button.toInt()); m_buttons[button.toInt()] = item; } - QVariant axis = m_config->getQtOption(item->name(), !m_profile.isNull() ? AXIS_PROFILE_SECTION + m_profile : AXIS_SECTION); + QVariant axis = m_config->getQtOption(item->name(), !m_profileName.isNull() ? AXIS_PROFILE_SECTION + m_profileName : AXIS_SECTION); int oldAxis = item->axis(); GamepadAxisEvent::Direction oldDirection = item->direction(); if (oldAxis >= 0) { m_axes.take(qMakePair(oldAxis, oldDirection)); item->setAxis(-1, GamepadAxisEvent::NEUTRAL); } + if (axis.isNull() && m_profile) { + int axisInt; + GamepadAxisEvent::Direction direction; + if (m_profile->lookupShortcutAxis(item->name(), &axisInt, &direction)) { + axis = QLatin1String(direction == GamepadAxisEvent::Direction::NEGATIVE ? "-" : "+") + QString::number(axisInt); + } + } if (!axis.isNull()) { QString axisDesc = axis.toString(); if (axisDesc.size() >= 2) { @@ -452,7 +467,8 @@ QKeySequence ShortcutController::keyEventToSequence(const QKeyEvent* event) { } void ShortcutController::loadProfile(const QString& profile) { - m_profile = profile; + m_profileName = profile; + m_profile = InputProfile::findProfile(profile); onSubitems(&m_rootMenu, [this](ShortcutItem* item) { loadGamepadShortcuts(item); }); diff --git a/src/platform/qt/ShortcutController.h b/src/platform/qt/ShortcutController.h index 5869c25f1..5cc6f06a4 100644 --- a/src/platform/qt/ShortcutController.h +++ b/src/platform/qt/ShortcutController.h @@ -21,6 +21,7 @@ class QString; namespace QGBA { class ConfigController; +class InputProfile; class ShortcutController : public QAbstractItemModel { Q_OBJECT @@ -133,7 +134,8 @@ private: QMap, ShortcutItem*> m_axes; QMap m_heldKeys; ConfigController* m_config; - QString m_profile; + QString m_profileName; + const InputProfile* m_profile; }; } diff --git a/src/util/configuration.c b/src/util/configuration.c index dd4efbb51..a96616ae0 100644 --- a/src/util/configuration.c +++ b/src/util/configuration.c @@ -100,6 +100,10 @@ void ConfigurationClearValue(struct Configuration* configuration, const char* se HashTableRemove(currentSection, key); } +bool ConfigurationHasSection(const struct Configuration* configuration, const char* section) { + return HashTableLookup(&configuration->sections, section); +} + const char* ConfigurationGetValue(const struct Configuration* configuration, const char* section, const char* key) { const struct Table* currentSection = &configuration->root; if (section) { diff --git a/src/util/configuration.h b/src/util/configuration.h index 098b143a9..14a0e7014 100644 --- a/src/util/configuration.h +++ b/src/util/configuration.h @@ -23,6 +23,7 @@ void ConfigurationSetIntValue(struct Configuration*, const char* section, const void ConfigurationSetUIntValue(struct Configuration*, const char* section, const char* key, unsigned value); void ConfigurationSetFloatValue(struct Configuration*, const char* section, const char* key, float value); +bool ConfigurationHasSection(const struct Configuration*, const char* section); const char* ConfigurationGetValue(const struct Configuration*, const char* section, const char* key); void ConfigurationClearValue(struct Configuration*, const char* section, const char* key); From f05cc8d139001e952e89b0c623ed10be48487a29 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 16 Jul 2015 23:48:36 -0700 Subject: [PATCH 25/52] Qt: Add missing separator --- src/platform/qt/Window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 8ce43001e..0a31f17ec 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -701,6 +701,7 @@ void Window::setupMenu(QMenuBar* menubar) { addControlledAction(quickSaveMenu, quickSave, "quickSave"); quickLoadMenu->addSeparator(); + quickSaveMenu->addSeparator(); QAction* undoLoadState = new QAction(tr("Undo load state"), quickLoadMenu); undoLoadState->setShortcut(tr("F11")); From 8ff8876e3724981739d1343223eb2347a0b28810 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 16 Jul 2015 23:50:33 -0700 Subject: [PATCH 26/52] Qt: Block a bug whereby undoing a save can overwrite the wrong save --- src/platform/qt/GameController.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 893a5bc02..cbb2b1718 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -559,8 +559,9 @@ void GameController::setUseBIOS(bool use) { } void GameController::loadState(int slot) { - if (slot > 0) { + if (slot > 0 && slot != m_stateSlot) { m_stateSlot = slot; + m_backupSaveState.clear(); } GBARunOnThread(&m_threadContext, [](GBAThread* context) { GameController* controller = static_cast(context->userData); From d0bc4d4f4e8e4f28ffbc741a0ee35076a2d70b1c Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 17 Jul 2015 00:07:28 -0700 Subject: [PATCH 27/52] Qt: Reduce time that it takes for the mouse to disappear --- src/platform/qt/Display.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index 551d8247c..cd3f1fd2a 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -58,7 +58,7 @@ protected: private: static Driver s_driver; - static const int MOUSE_DISAPPEAR_TIMER = 2000; + static const int MOUSE_DISAPPEAR_TIMER = 1000; MessagePainter m_messagePainter; bool m_lockAspectRatio; From b6889d77f838daf035e0618b30318113bbd859ea Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 17 Jul 2015 18:22:01 -0700 Subject: [PATCH 28/52] GBA Input: Allow axes and buttons to be mapped to the same key --- CHANGES | 1 + src/gba/input.c | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index af39fd1f2..5e75a27a6 100644 --- a/CHANGES +++ b/CHANGES @@ -103,6 +103,7 @@ Misc: - GBA Video: Slightly optimize mode 0 mosaic rendering - VFS: Add sync method to force syncing with backing - GBA: Savedata is now synced shortly after data finishes being written + - GBA Input: Allow axes and buttons to be mapped to the same key 0.2.1: (2015-05-13) Bugfixes: diff --git a/src/gba/input.c b/src/gba/input.c index 734d7f4e4..1d9ccba74 100644 --- a/src/gba/input.c +++ b/src/gba/input.c @@ -421,8 +421,6 @@ int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, in void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) { struct GBAInputMapImpl* impl = _guaranteeMap(map, type); struct GBAAxis* dup = malloc(sizeof(struct GBAAxis)); - GBAInputUnbindKey(map, type, description->lowDirection); - GBAInputUnbindKey(map, type, description->highDirection); *dup = *description; TableInsert(&impl->axes, axis, dup); } From 3a53e279276ec09df09e78d0dd4b39f749a8bd0d Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 17 Jul 2015 18:22:46 -0700 Subject: [PATCH 29/52] Qt: Update input profiles --- src/platform/qt/InputProfile.cpp | 91 ++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/src/platform/qt/InputProfile.cpp b/src/platform/qt/InputProfile.cpp index 3afa3d96d..ebf7ac252 100644 --- a/src/platform/qt/InputProfile.cpp +++ b/src/platform/qt/InputProfile.cpp @@ -12,6 +12,56 @@ using namespace QGBA; const InputProfile InputProfile::s_defaultMaps[] = { + { + "XInput Controller #\\d+", // XInput (Windows) + (int[GBA_KEY_MAX]) { + /*keyA */ 11, + /*keyB */ 10, + /*keySelect */ 5, + /*keyStart */ 4, + /*keyRight */ 3, + /*keyLeft */ 2, + /*keyUp */ 0, + /*keyDown */ 1, + /*keyR */ 9, + /*keyL */ 8 + }, + (ShortcutButton[]) { + {"loadState", 12}, + {"saveState", 13}, + {} + }, + (ShortcutAxis[]) { + {"holdFastForward", GamepadAxisEvent::Direction::POSITIVE, 5}, + {"holdRewind", GamepadAxisEvent::Direction::POSITIVE, 4}, + {} + } + }, + { + "Controller", // The Xbox 360 controller drivers on OS X are vague... + (int[GBA_KEY_MAX]) { + /*keyA */ 1, + /*keyB */ 0, + /*keySelect */ 9, + /*keyStart */ 8, + /*keyRight */ 14, + /*keyLeft */ 13, + /*keyUp */ 11, + /*keyDown */ 12, + /*keyR */ 5, + /*keyL */ 4 + }, + (ShortcutButton[]) { + {"loadState", 2}, + {"saveState", 3}, + {} + }, + (ShortcutAxis[]) { + {"holdFastForward", GamepadAxisEvent::Direction::POSITIVE, 5}, + {"holdRewind", GamepadAxisEvent::Direction::POSITIVE, 2}, + {} + } + }, { "PLAYSTATION\\(R\\)3 Controller", // DualShock 3 (OS X) (int[GBA_KEY_MAX]) { @@ -19,10 +69,10 @@ const InputProfile InputProfile::s_defaultMaps[] = { /*keyB */ 14, /*keySelect */ 0, /*keyStart */ 3, - /*keyRight */ -1, - /*keyLeft */ -1, - /*keyUp */ -1, - /*keyDown */ -1, + /*keyRight */ 5, + /*keyLeft */ 7, + /*keyUp */ 4, + /*keyDown */ 6, /*keyR */ 11, /*keyL */ 10 }, @@ -41,10 +91,10 @@ const InputProfile InputProfile::s_defaultMaps[] = { /*keyB */ 16, /*keySelect */ 7, /*keyStart */ 6, - /*keyRight */ -1, - /*keyLeft */ -1, - /*keyUp */ -1, - /*keyDown */ -1, + /*keyRight */ 14, + /*keyLeft */ 13, + /*keyUp */ 11, + /*keyDown */ 12, /*keyR */ 20, /*keyL */ 19 }, @@ -56,31 +106,6 @@ const InputProfile InputProfile::s_defaultMaps[] = { {} } }, - { - "Controller", // The Xbox 360 controller drivers on OS X are vague... - (int[GBA_KEY_MAX]) { - /*keyA */ 1, - /*keyB */ 0, - /*keySelect */ 9, - /*keyStart */ 8, - /*keyRight */ -1, - /*keyLeft */ -1, - /*keyUp */ -1, - /*keyDown */ -1, - /*keyR */ 5, - /*keyL */ 4 - }, - (ShortcutButton[]) { - {"loadState", 2}, - {"saveState", 3}, - {} - }, - (ShortcutAxis[]) { - {"holdFastForward", GamepadAxisEvent::Direction::POSITIVE, 5}, - {"holdRewind", GamepadAxisEvent::Direction::POSITIVE, 2}, - {} - } - }, }; constexpr InputProfile::InputProfile(const char* name, From 1975fc77062eb05c3482723556914803bc400066 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 17 Jul 2015 18:22:57 -0700 Subject: [PATCH 30/52] Qt: Fix SDL build --- src/platform/qt/InputProfile.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platform/qt/InputProfile.cpp b/src/platform/qt/InputProfile.cpp index ebf7ac252..1946159e0 100644 --- a/src/platform/qt/InputProfile.cpp +++ b/src/platform/qt/InputProfile.cpp @@ -161,8 +161,10 @@ const InputProfile* InputProfile::findProfile(const QString& name) { void InputProfile::apply(InputController* controller) const { for (size_t i = 0; i < GBA_KEY_MAX; ++i) { +#ifdef BUILD_SDL controller->bindKey(SDL_BINDING_BUTTON, m_keys[i], static_cast(i)); controller->bindAxis(SDL_BINDING_BUTTON, m_axes[i].axis, m_axes[i].direction, static_cast(i)); +#endif } controller->registerTiltAxisX(m_tiltAxis.x); controller->registerTiltAxisY(m_tiltAxis.y); From 85c4162ad1203c40d930f04b0b8ae550d7d2e353 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 17 Jul 2015 20:48:23 -0700 Subject: [PATCH 31/52] Libretro: BIOS loading --- CHANGES | 1 + src/platform/libretro/libretro.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/CHANGES b/CHANGES index 5e75a27a6..e79ad4979 100644 --- a/CHANGES +++ b/CHANGES @@ -27,6 +27,7 @@ Features: - Undo-able savestate loading and saving - Controller profiles now store shortcut settings - Default controller profiles for several common controllers + - Libretro now supports BIOS and rumble Bugfixes: - ARM7: Fix SWI and IRQ timings - GBA Audio: Force audio FIFOs to 32-bit diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index 3ac3a8777..92c1d51a1 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -39,6 +39,7 @@ static struct VFile* rom; static void* data; static struct VFile* save; static void* savedata; +static struct VFile* bios; static struct GBAAVStream stream; static int rumbleLevel; static struct CircleBuffer rumbleHistory; @@ -152,6 +153,16 @@ void retro_init(void) { } rom = 0; + const char* sysDir = 0; + if (environCallback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &sysDir)) { + char biosPath[PATH_MAX]; + snprintf(biosPath, sizeof(biosPath), "%s%s%s", sysDir, PATH_SEP, "gba_bios.bin"); + bios = VFileOpen(biosPath, O_RDONLY); + if (bios) { + GBALoadBIOS(&gba, bios); + } + } + GBAVideoSoftwareRendererCreate(&renderer); renderer.outputBuffer = malloc(256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL); renderer.outputBufferStride = 256; @@ -166,6 +177,10 @@ void retro_init(void) { } void retro_deinit(void) { + if (bios) { + bios->close(bios); + bios = 0; + } GBADestroy(&gba); } From 75fb2548bbb6dea49e6d1040e3b59d76f5548fa6 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 18 Jul 2015 00:20:54 -0700 Subject: [PATCH 32/52] Qt: Bind controllers to specific windows --- src/platform/qt/InputController.cpp | 24 +++++++++++++++++++----- src/platform/qt/InputController.h | 4 +++- src/platform/qt/Window.cpp | 2 +- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index e17cd16e6..93a8d58ef 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -25,7 +25,7 @@ int InputController::s_sdlInited = 0; GBASDLEvents InputController::s_sdlEvents; #endif -InputController::InputController(int playerId, QObject* parent) +InputController::InputController(int playerId, QWidget* topLevel, QObject* parent) : QObject(parent) , m_playerId(playerId) , m_config(nullptr) @@ -34,6 +34,7 @@ InputController::InputController(int playerId, QObject* parent) , m_playerAttached(false) #endif , m_allowOpposing(false) + , m_topLevel(topLevel) { GBAInputMapInit(&m_inputMap); @@ -432,7 +433,7 @@ void InputController::testGamepad(int type) { if (newlyAboveThreshold) { GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this); postPendingEvent(event->gbaKey()); - QApplication::sendEvent(QApplication::focusWidget(), event); + sendGamepadEvent(event); if (!event->isAccepted()) { clearPendingEvent(event->gbaKey()); } @@ -441,7 +442,7 @@ void InputController::testGamepad(int type) { for (auto axis : oldAxes) { GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this); clearPendingEvent(event->gbaKey()); - QApplication::sendEvent(QApplication::focusWidget(), event); + sendGamepadEvent(event); } if (!QApplication::focusWidget()) { @@ -454,7 +455,7 @@ void InputController::testGamepad(int type) { for (int button : activeButtons) { GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this); postPendingEvent(event->gbaKey()); - QApplication::sendEvent(QApplication::focusWidget(), event); + sendGamepadEvent(event); if (!event->isAccepted()) { clearPendingEvent(event->gbaKey()); } @@ -462,10 +463,23 @@ void InputController::testGamepad(int type) { for (int button : oldButtons) { GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this); clearPendingEvent(event->gbaKey()); - QApplication::sendEvent(QApplication::focusWidget(), event); + sendGamepadEvent(event); } } +void InputController::sendGamepadEvent(QEvent* event) { + QWidget* focusWidget = nullptr; + if (m_topLevel) { + focusWidget = m_topLevel->focusWidget(); + if (!focusWidget) { + focusWidget = m_topLevel; + } + } else { + focusWidget = QApplication::focusWidget(); + } + QApplication::sendEvent(focusWidget, event); +} + void InputController::postPendingEvent(GBAKey key) { m_pendingEvents.insert(key); } diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index 65bdec0f0..4a8501dd3 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -32,7 +32,7 @@ Q_OBJECT public: static const uint32_t KEYBOARD = 0x51545F4B; - InputController(int playerId = 0, QObject* parent = nullptr); + InputController(int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr); ~InputController(); void setConfiguration(ConfigController* config); @@ -94,11 +94,13 @@ private: void postPendingEvent(GBAKey); void clearPendingEvent(GBAKey); bool hasPendingEvent(GBAKey) const; + void sendGamepadEvent(QEvent*); GBAInputMap m_inputMap; ConfigController* m_config; int m_playerId; bool m_allowOpposing; + QWidget* m_topLevel; #ifdef BUILD_SDL static int s_sdlInited; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 0a31f17ec..d0e1e2fd3 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -53,7 +53,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) , m_screenWidget(new WindowBackground()) , m_logo(":/res/mgba-1024.png") , m_config(config) - , m_inputController(playerId) + , m_inputController(playerId, this) #ifdef USE_FFMPEG , m_videoView(nullptr) #endif From 90d215ea23b97d0732e0d1704ab4b12a90758d3c Mon Sep 17 00:00:00 2001 From: Dugan Chen Date: Sun, 19 Jul 2015 09:23:01 -0700 Subject: [PATCH 33/52] The library directory is overridable and defaults to lib. --- CMakeLists.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b52159bff..eedf014b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,11 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type (e.g. Release or Debug)" FORCE) endif() -set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") +if (NOT DEFINED LIBDIR) + set(LIBDIR "lib") +endif() + +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIBDIR}") include(GNUInstallDirs) @@ -365,7 +369,7 @@ if(BUILD_SHARED) if(BUILD_STATIC) add_library(${BINARY_NAME}-static STATIC ${SRC}) set_target_properties(${BINARY_NAME}-static PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES}") - install(TARGETS ${BINARY_NAME}-static DESTINATION lib COMPONENT lib${BINARY_NAME}) + install(TARGETS ${BINARY_NAME}-static DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME}) add_dependencies(${BINARY_NAME}-static version-info) endif() else() @@ -375,7 +379,7 @@ endif() add_dependencies(${BINARY_NAME} version-info) target_link_libraries(${BINARY_NAME} ${DEBUGGER_LIB} ${DEPENDENCY_LIB} ${OS_LIB}) -install(TARGETS ${BINARY_NAME} DESTINATION lib COMPONENT lib${BINARY_NAME}) +install(TARGETS ${BINARY_NAME} DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME}) if(UNIX AND NOT APPLE) install(FILES ${CMAKE_SOURCE_DIR}/res/mgba-16.png DESTINATION share/icons/hicolor/16x16/apps RENAME mgba.png COMPONENT lib${BINARY_NAME}) install(FILES ${CMAKE_SOURCE_DIR}/res/mgba-24.png DESTINATION share/icons/hicolor/24x24/apps RENAME mgba.png COMPONENT lib${BINARY_NAME}) From d588b8c462d780cea27489a0dc697bf0c5e36984 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 19 Jul 2015 18:12:56 -0700 Subject: [PATCH 34/52] GBA BIOS: Implement Stop --- CHANGES | 1 + src/gba/bios.c | 3 +++ src/gba/gba.c | 9 +++++++++ src/gba/gba.h | 2 ++ src/gba/interface.h | 4 ++++ src/gba/io.c | 2 +- 6 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index e79ad4979..ab17d9970 100644 --- a/CHANGES +++ b/CHANGES @@ -28,6 +28,7 @@ Features: - Controller profiles now store shortcut settings - Default controller profiles for several common controllers - Libretro now supports BIOS and rumble + - Implement BIOS call Stop, for sleep mode Bugfixes: - ARM7: Fix SWI and IRQ timings - GBA Audio: Force audio FIFOs to 32-bit diff --git a/src/gba/bios.c b/src/gba/bios.c index 3af31b9fd..05404af97 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -191,6 +191,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { case 0x2: GBAHalt(gba); break; + case 0x3: + GBAStop(gba); + break; case 0x05: // VBlankIntrWait // Fall through: diff --git a/src/gba/gba.c b/src/gba/gba.c index 9cf50a3ef..d4aa5d98e 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -82,6 +82,7 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) { gba->logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL; gba->stream = 0; gba->keyCallback = 0; + gba->stopCallback = 0; gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS); @@ -553,6 +554,14 @@ void GBAHalt(struct GBA* gba) { gba->cpu->halted = 1; } +void GBAStop(struct GBA* gba) { + if (!gba->stopCallback) { + return; + } + gba->cpu->nextEvent = 0; + gba->stopCallback->stop(gba->stopCallback); +} + static void _GBAVLog(struct GBA* gba, enum GBALogLevel level, const char* format, va_list args) { struct GBAThread* threadContext = GBAThreadGetContext(); enum GBALogLevel logLevel = GBA_LOG_ALL; diff --git a/src/gba/gba.h b/src/gba/gba.h index 0e5d92a42..46eea057c 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -112,6 +112,7 @@ struct GBA { enum GBALogLevel logLevel; struct GBAAVStream* stream; struct GBAKeyCallback* keyCallback; + struct GBAStopCallback* stopCallback; enum GBAIdleLoopOptimization idleOptimization; uint32_t idleLoop; @@ -155,6 +156,7 @@ void GBAWriteIME(struct GBA* gba, uint16_t value); void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq); void GBATestIRQ(struct ARMCore* cpu); void GBAHalt(struct GBA* gba); +void GBAStop(struct GBA* gba); void GBAAttachDebugger(struct GBA* gba, struct ARMDebugger* debugger); void GBADetachDebugger(struct GBA* gba); diff --git a/src/gba/interface.h b/src/gba/interface.h index 7b5e1c9f5..3daf0dcb8 100644 --- a/src/gba/interface.h +++ b/src/gba/interface.h @@ -66,6 +66,10 @@ struct GBAKeyCallback { uint16_t (*readKeys)(struct GBAKeyCallback*); }; +struct GBAStopCallback { + void (*stop)(struct GBAStopCallback*); +}; + struct GBARotationSource { void (*sample)(struct GBARotationSource*); diff --git a/src/gba/io.c b/src/gba/io.c index f5e383713..304a6253a 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -505,7 +505,7 @@ void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) { if (!value) { GBAHalt(gba); } else { - GBALog(gba, GBA_LOG_STUB, "Stop unimplemented"); + GBAStop(gba); } return; } From 2575097b7cb40d8871d57810a06e16b9bcccd585 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 19 Jul 2015 18:14:09 -0700 Subject: [PATCH 35/52] GBA BIOS: Stub out SoundBias --- CHANGES | 1 + src/gba/bios.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index ab17d9970..8d296b211 100644 --- a/CHANGES +++ b/CHANGES @@ -106,6 +106,7 @@ Misc: - VFS: Add sync method to force syncing with backing - GBA: Savedata is now synced shortly after data finishes being written - GBA Input: Allow axes and buttons to be mapped to the same key + - GBA BIOS: Stub out SoundBias 0.2.1: (2015-05-13) Bugfixes: diff --git a/src/gba/bios.c b/src/gba/bios.c index 05404af97..a20c5ff59 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -300,6 +300,10 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { break; } break; + case 0x19: + // SoundBias is mostly meaningless here + GBALog(gba, GBA_LOG_STUB, "Stub software interrupt: SoundBias (19)"); + break; case 0x1F: _MidiKey2Freq(gba); break; From 393252718fdabad0e8cb4902be3d2420c17c8005 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 19 Jul 2015 18:14:35 -0700 Subject: [PATCH 36/52] Qt: Ensure thread has not exited if we say it is open --- src/platform/qt/GameController.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index 0fb82ba14..bbd5c2171 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -55,7 +55,7 @@ public: void threadContinue(); bool isPaused(); - bool isLoaded() { return m_gameOpen; } + bool isLoaded() { return m_gameOpen && GBAThreadIsActive(&m_threadContext); } bool audioSync() const { return m_audioSync; } bool videoSync() const { return m_videoSync; } From 7015f38b37a7d33fc8412e4ef66911aae7e4936b Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 19 Jul 2015 18:35:18 -0700 Subject: [PATCH 37/52] Qt: Save and quit on Stop call --- src/gba/supervisor/thread.c | 20 ++++++++++++++ src/gba/supervisor/thread.h | 2 ++ src/platform/qt/GameController.cpp | 43 +++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/gba/supervisor/thread.c b/src/gba/supervisor/thread.c index e617b1afd..08b5b7402 100644 --- a/src/gba/supervisor/thread.c +++ b/src/gba/supervisor/thread.c @@ -96,6 +96,18 @@ static void _pauseThread(struct GBAThread* threadContext, bool onThread) { } } +struct GBAThreadStop { + struct GBAStopCallback d; + struct GBAThread* p; +}; + +static void _stopCallback(struct GBAStopCallback* stop) { + struct GBAThreadStop* callback = (struct GBAThreadStop*) stop; + if (callback->p->stopCallback(callback->p)) { + _changeState(callback->p, THREAD_EXITING, false); + } +} + static THREAD_ENTRY _GBAThreadRun(void* context) { #ifdef USE_PTHREADS pthread_once(&_contextOnce, _createTLS); @@ -129,6 +141,14 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { gba.logLevel = threadContext->logLevel; gba.logHandler = threadContext->logHandler; gba.stream = threadContext->stream; + + struct GBAThreadStop stop; + if (threadContext->stopCallback) { + stop.d.stop = _stopCallback; + stop.p = threadContext; + gba.stopCallback = &stop.d; + } + gba.idleOptimization = threadContext->idleOptimization; #ifdef USE_PTHREADS pthread_setspecific(_contextKey, threadContext); diff --git a/src/gba/supervisor/thread.h b/src/gba/supervisor/thread.h index aa86940f2..f9dab3248 100644 --- a/src/gba/supervisor/thread.h +++ b/src/gba/supervisor/thread.h @@ -21,6 +21,7 @@ struct GBACheatSet; struct GBAOptions; typedef void (*ThreadCallback)(struct GBAThread* threadContext); +typedef bool (*ThreadStopCallback)(struct GBAThread* threadContext); enum ThreadState { THREAD_INITIALIZED = -1, @@ -86,6 +87,7 @@ struct GBAThread { ThreadCallback startCallback; ThreadCallback cleanCallback; ThreadCallback frameCallback; + ThreadStopCallback stopCallback; void* userData; void (*run)(struct GBAThread*); diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index cbb2b1718..33ad8ba33 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -7,6 +7,7 @@ #include "AudioProcessor.h" #include "InputController.h" +#include "LogController.h" #include "MultiplayerController.h" #include "VFileDevice.h" @@ -92,6 +93,13 @@ GameController::GameController(QObject* parent) context->gba->rumble = controller->m_inputController->rumble(); context->gba->rotationSource = controller->m_inputController->rotationSource(); controller->m_fpsTarget = context->fpsTarget; + + if (GBALoadState(context, context->stateDir, 0)) { + VFile* vf = GBAGetState(context->gba, context->stateDir, 0, true); + if (vf) { + vf->truncate(vf, 0); + } + } controller->gameStarted(context); }; @@ -113,8 +121,22 @@ GameController::GameController(QObject* parent) } }; + m_threadContext.stopCallback = [](GBAThread* context) { + if (!context) { + return false; + } + GameController* controller = static_cast(context->userData); + if (!GBASaveState(context, context->stateDir, 0, true)) { + return false; + } + QMetaObject::invokeMethod(controller, "closeGame"); + return true; + }; + m_threadContext.logHandler = [](GBAThread* context, enum GBALogLevel level, const char* format, va_list args) { - static const char* stubMessage = "Stub software interrupt"; + static const char* stubMessage = "Stub software interrupt: %02X"; + static const char* savestateMessage = "State %i loaded"; + static const char* savestateFailedMessage = "State %i failed to load"; if (!context) { return; } @@ -125,6 +147,25 @@ GameController::GameController(QObject* parent) int immediate = va_arg(argc, int); va_end(argc); controller->unimplementedBiosCall(immediate); + } else if (level == GBA_LOG_STATUS) { + // Slot 0 is reserved for suspend points + if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) { + va_list argc; + va_copy(argc, args); + int slot = va_arg(argc, int); + va_end(argc); + if (slot == 0) { + format = "Loaded suspend state"; + } + } else if (strncmp(savestateFailedMessage, format, strlen(savestateFailedMessage)) == 0) { + va_list argc; + va_copy(argc, args); + int slot = va_arg(argc, int); + va_end(argc); + if (slot == 0) { + return; + } + } } if (level == GBA_LOG_FATAL) { QMetaObject::invokeMethod(controller, "crashGame", Q_ARG(const QString&, QString().vsprintf(format, args))); From 037e518f5c3bab017d9a4486ff9dc820c03793db Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Thu, 26 Mar 2015 08:04:20 +0100 Subject: [PATCH 38/52] (iOS) Fix build - to16Bit ARM routine not iOS ABI-compatible --- src/gba/renderers/video-software.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 8078293c5..f2b7d842c 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -530,7 +530,7 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render } #ifdef COLOR_16_BIT -#ifdef __ARM_NEON +#if defined(__ARM_NEON) && !defined(__APPLE__) _to16Bit(row, softwareRenderer->row, VIDEO_HORIZONTAL_PIXELS); #else for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) { From 0ecdc1ac44f60a1254fef93fd066b52f5274cb81 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 21 Jul 2015 19:22:02 -0700 Subject: [PATCH 39/52] GBA Config: Functions for loading and saving configs from a path --- src/gba/supervisor/config.c | 10 +++++++++- src/gba/supervisor/config.h | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/gba/supervisor/config.c b/src/gba/supervisor/config.c index 7e355e222..19e5c2bd6 100644 --- a/src/gba/supervisor/config.c +++ b/src/gba/supervisor/config.c @@ -116,13 +116,21 @@ bool GBAConfigLoad(struct GBAConfig* config) { char path[PATH_MAX]; GBAConfigDirectory(path, PATH_MAX); strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path)); - return ConfigurationRead(&config->configTable, path); + return GBAConfigLoadPath(config, path); } bool GBAConfigSave(const struct GBAConfig* config) { char path[PATH_MAX]; GBAConfigDirectory(path, PATH_MAX); strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path)); + return GBAConfigSavePath(config, path); +} + +bool GBAConfigLoadPath(struct GBAConfig* config, const char* path) { + return ConfigurationRead(&config->configTable, path); +} + +bool GBAConfigSavePath(const struct GBAConfig* config, const char* path) { return ConfigurationWrite(&config->configTable, path); } diff --git a/src/gba/supervisor/config.h b/src/gba/supervisor/config.h index c4183e122..07c824cd2 100644 --- a/src/gba/supervisor/config.h +++ b/src/gba/supervisor/config.h @@ -51,6 +51,8 @@ void GBAConfigDeinit(struct GBAConfig*); bool GBAConfigLoad(struct GBAConfig*); bool GBAConfigSave(const struct GBAConfig*); +bool GBAConfigLoadPath(struct GBAConfig*, const char* path); +bool GBAConfigSavePath(const struct GBAConfig*, const char* path); void GBAConfigMakePortable(const struct GBAConfig*); void GBAConfigDirectory(char* out, size_t outLength); From b9c942546475eb971a142fc9ec905beeb7a93815 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 22 Jul 2015 21:16:28 -0700 Subject: [PATCH 40/52] Qt: Hacky way to swap out focus for a gamepad (fixes #64) --- src/platform/qt/GBAKeyEditor.cpp | 14 ++++++++++++++ src/platform/qt/GBAKeyEditor.h | 2 ++ src/platform/qt/InputController.cpp | 17 ++++++++++++++--- src/platform/qt/InputController.h | 4 ++++ src/platform/qt/ShortcutView.cpp | 27 +++++++++++++++++++++++++++ src/platform/qt/ShortcutView.h | 5 +++++ src/platform/qt/Window.cpp | 1 + 7 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 4a8577979..6adad32f7 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -32,6 +32,7 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& setMinimumSize(300, 300); const GBAInputMap* map = controller->map(); + controller->stealFocus(this); m_keyDU = new KeyEditor(this); m_keyDD = new KeyEditor(this); @@ -151,6 +152,19 @@ void GBAKeyEditor::paintEvent(QPaintEvent* event) { painter.drawPicture(0, 0, m_background); } +void GBAKeyEditor::closeEvent(QCloseEvent*) { + m_controller->releaseFocus(this); +} + +bool GBAKeyEditor::event(QEvent* event) { + if (event->type() == QEvent::WindowActivate) { + m_controller->stealFocus(this); + } else if (event->type() == QEvent::WindowDeactivate) { + m_controller->releaseFocus(this); + } + return QWidget::event(event); +} + void GBAKeyEditor::setNext() { findFocus(); diff --git a/src/platform/qt/GBAKeyEditor.h b/src/platform/qt/GBAKeyEditor.h index 9496ffffe..65707d4eb 100644 --- a/src/platform/qt/GBAKeyEditor.h +++ b/src/platform/qt/GBAKeyEditor.h @@ -35,6 +35,8 @@ public slots: protected: virtual void resizeEvent(QResizeEvent*) override; virtual void paintEvent(QPaintEvent*) override; + virtual bool event(QEvent*) override; + virtual void closeEvent(QCloseEvent*) override; private slots: void setNext(); diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 93a8d58ef..c0d19d9fa 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -35,6 +35,7 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren #endif , m_allowOpposing(false) , m_topLevel(topLevel) + , m_focusParent(topLevel) { GBAInputMapInit(&m_inputMap); @@ -469,10 +470,10 @@ void InputController::testGamepad(int type) { void InputController::sendGamepadEvent(QEvent* event) { QWidget* focusWidget = nullptr; - if (m_topLevel) { - focusWidget = m_topLevel->focusWidget(); + if (m_focusParent) { + focusWidget = m_focusParent->focusWidget(); if (!focusWidget) { - focusWidget = m_topLevel; + focusWidget = m_focusParent; } } else { focusWidget = QApplication::focusWidget(); @@ -505,3 +506,13 @@ void InputController::setScreensaverSuspendable(bool suspendable) { GBASDLSetScreensaverSuspendable(&s_sdlEvents, suspendable); } #endif + +void InputController::stealFocus(QWidget* focus) { + m_focusParent = focus; +} + +void InputController::releaseFocus(QWidget* focus) { + if (focus == m_focusParent) { + m_focusParent = m_topLevel; + } +} diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index 4a8501dd3..572210b80 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -74,6 +74,9 @@ public: float gyroSensitivity() const; void setGyroSensitivity(float sensitivity); + void stealFocus(QWidget* focus); + void releaseFocus(QWidget* focus); + GBARumble* rumble(); GBARotationSource* rotationSource(); @@ -101,6 +104,7 @@ private: int m_playerId; bool m_allowOpposing; QWidget* m_topLevel; + QWidget* m_focusParent; #ifdef BUILD_SDL static int s_sdlInited; diff --git a/src/platform/qt/ShortcutView.cpp b/src/platform/qt/ShortcutView.cpp index b0ba9045f..036d9cf76 100644 --- a/src/platform/qt/ShortcutView.cpp +++ b/src/platform/qt/ShortcutView.cpp @@ -6,6 +6,7 @@ #include "ShortcutView.h" #include "GamepadButtonEvent.h" +#include "InputController.h" #include "ShortcutController.h" #include @@ -15,6 +16,7 @@ using namespace QGBA; ShortcutView::ShortcutView(QWidget* parent) : QWidget(parent) , m_controller(nullptr) + , m_input(nullptr) { m_ui.setupUi(this); m_ui.keyEdit->setValueButton(-1); @@ -32,6 +34,14 @@ void ShortcutView::setController(ShortcutController* controller) { m_ui.shortcutTable->setModel(controller); } +void ShortcutView::setInputController(InputController* controller) { + if (m_input) { + m_input->releaseFocus(this); + } + m_input = controller; + m_input->stealFocus(this); +} + bool ShortcutView::eventFilter(QObject*, QEvent* event) { if (event->type() == QEvent::KeyPress) { QKeyEvent* keyEvent = static_cast(event); @@ -111,3 +121,20 @@ void ShortcutView::updateAxis(int axis, int direction) { m_controller->updateAxis(m_ui.shortcutTable->selectionModel()->currentIndex(), axis, static_cast(direction)); } + +void ShortcutView::closeEvent(QCloseEvent*) { + if (m_input) { + m_input->releaseFocus(this); + } +} + +bool ShortcutView::event(QEvent* event) { + if (m_input) { + if (event->type() == QEvent::WindowActivate) { + m_input->stealFocus(this); + } else if (event->type() == QEvent::WindowDeactivate) { + m_input->releaseFocus(this); + } + } + return QWidget::event(event); +} diff --git a/src/platform/qt/ShortcutView.h b/src/platform/qt/ShortcutView.h index af6baadaa..59a997347 100644 --- a/src/platform/qt/ShortcutView.h +++ b/src/platform/qt/ShortcutView.h @@ -14,6 +14,7 @@ namespace QGBA { +class InputController; class ShortcutController; class ShortcutView : public QWidget { @@ -23,9 +24,12 @@ public: ShortcutView(QWidget* parent = nullptr); void setController(ShortcutController* controller); + void setInputController(InputController* input); protected: virtual bool eventFilter(QObject* obj, QEvent* event) override; + virtual bool event(QEvent*) override; + virtual void closeEvent(QCloseEvent*) override; private slots: void load(const QModelIndex&); @@ -38,6 +42,7 @@ private: Ui::ShortcutView m_ui; ShortcutController* m_controller; + InputController* m_input; }; } diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index d0e1e2fd3..29f800c0f 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -315,6 +315,7 @@ void Window::openShortcutWindow() { #endif ShortcutView* shortcutView = new ShortcutView(); shortcutView->setController(m_shortcutController); + shortcutView->setInputController(&m_inputController); openView(shortcutView); } From da226abba68654b05480d3e56a4d62d3fc95f00a Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 22 Jul 2015 21:22:50 -0700 Subject: [PATCH 41/52] Qt: Add 360 profile for Linux --- src/platform/qt/InputProfile.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/platform/qt/InputProfile.cpp b/src/platform/qt/InputProfile.cpp index 1946159e0..c4e542bc7 100644 --- a/src/platform/qt/InputProfile.cpp +++ b/src/platform/qt/InputProfile.cpp @@ -37,6 +37,31 @@ const InputProfile InputProfile::s_defaultMaps[] = { {} } }, + { + "(Microsoft X-Box 360 pad|Xbox Gamepad \\(userspace driver\\))", // Linux + (int[GBA_KEY_MAX]) { + /*keyA */ 1, + /*keyB */ 0, + /*keySelect */ 6, + /*keyStart */ 7, + /*keyRight */ -1, + /*keyLeft */ -1, + /*keyUp */ -1, + /*keyDown */ -1, + /*keyR */ 5, + /*keyL */ 4 + }, + (ShortcutButton[]) { + {"loadState", 2}, + {"saveState", 3}, + {} + }, + (ShortcutAxis[]) { + {"holdFastForward", GamepadAxisEvent::Direction::POSITIVE, 5}, + {"holdRewind", GamepadAxisEvent::Direction::POSITIVE, 2}, + {} + } + }, { "Controller", // The Xbox 360 controller drivers on OS X are vague... (int[GBA_KEY_MAX]) { From e9c82df5187212025d930d862beb523f373c74ed Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 22 Jul 2015 21:34:49 -0700 Subject: [PATCH 42/52] Qt: Fix rewinding enabling the menu bar --- src/platform/qt/GameController.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 33ad8ba33..32c5aa41d 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -519,7 +519,9 @@ void GameController::startRewinding() { return; } m_wasPaused = isPaused(); + bool signalsBlocked = blockSignals(true); setPaused(true); + blockSignals(signalsBlocked); m_rewindTimer.start(); } @@ -528,7 +530,9 @@ void GameController::stopRewinding() { return; } m_rewindTimer.stop(); + bool signalsBlocked = blockSignals(true); setPaused(m_wasPaused); + blockSignals(signalsBlocked); } void GameController::keyPressed(int key) { From 500eeb7ee3c06b0e3a2433eeaad67261736ac1f5 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 23 Jul 2015 23:59:53 -0700 Subject: [PATCH 43/52] GBA Input: Unbind axes at the appropriate time --- src/gba/input.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gba/input.c b/src/gba/input.c index 1d9ccba74..6695a619d 100644 --- a/src/gba/input.c +++ b/src/gba/input.c @@ -366,7 +366,6 @@ void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input if (impl) { impl->map[input] = GBA_NO_MAPPING; } - TableEnumerate(&impl->axes, _unbindAxis, &input); } int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) { @@ -420,6 +419,8 @@ int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, in void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) { struct GBAInputMapImpl* impl = _guaranteeMap(map, type); + TableEnumerate(&impl->axes, _unbindAxis, &description->highDirection); + TableEnumerate(&impl->axes, _unbindAxis, &description->lowDirection); struct GBAAxis* dup = malloc(sizeof(struct GBAAxis)); *dup = *description; TableInsert(&impl->axes, axis, dup); From a08f09291362f2e9c43d7e84e864fece1a7e25bb Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 24 Jul 2015 00:01:10 -0700 Subject: [PATCH 44/52] Qt: Allow keys and axes to be bound at the same time --- src/platform/qt/GBAKeyEditor.cpp | 9 +++------ src/platform/qt/KeyEditor.cpp | 30 +++++++++++++++++++++--------- src/platform/qt/KeyEditor.h | 4 ++++ 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 6adad32f7..35b7a0317 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -253,14 +253,11 @@ void GBAKeyEditor::lookupAxes(const GBAInputMap* map) { void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) { #ifdef BUILD_SDL - if (keyEditor->direction() != GamepadAxisEvent::NEUTRAL) { - m_controller->bindAxis(m_type, keyEditor->value(), keyEditor->direction(), key); - } else { -#endif - m_controller->bindKey(m_type, keyEditor->value(), key); -#ifdef BUILD_SDL + if (m_type == SDL_BINDING_BUTTON) { + m_controller->bindAxis(m_type, keyEditor->axis(), keyEditor->direction(), key); } #endif + m_controller->bindKey(m_type, keyEditor->value(), key); } bool GBAKeyEditor::findFocus() { diff --git a/src/platform/qt/KeyEditor.cpp b/src/platform/qt/KeyEditor.cpp index fd76b2def..f8323c0a3 100644 --- a/src/platform/qt/KeyEditor.cpp +++ b/src/platform/qt/KeyEditor.cpp @@ -15,22 +15,20 @@ using namespace QGBA; KeyEditor::KeyEditor(QWidget* parent) : QLineEdit(parent) , m_direction(GamepadAxisEvent::NEUTRAL) + , m_key(-1) + , m_axis(-1) , m_button(false) { setAlignment(Qt::AlignCenter); } void KeyEditor::setValue(int key) { + m_key = key; if (m_button) { - if (key < 0) { - clear(); - } else { - setText(QString::number(key)); - } + updateButtonText(); } else { setText(QKeySequence(key).toString(QKeySequence::NativeText)); } - m_key = key; emit valueChanged(key); } @@ -41,15 +39,14 @@ void KeyEditor::setValueKey(int key) { void KeyEditor::setValueButton(int button) { m_button = true; - m_direction = GamepadAxisEvent::NEUTRAL; setValue(button); } void KeyEditor::setValueAxis(int axis, int32_t value) { m_button = true; - m_key = axis; + m_axis = axis; m_direction = value < 0 ? GamepadAxisEvent::NEGATIVE : GamepadAxisEvent::POSITIVE; - setText((value < 0 ? "-" : "+") + QString::number(axis)); + updateButtonText(); emit axisChanged(axis, m_direction); } @@ -85,3 +82,18 @@ bool KeyEditor::event(QEvent* event) { } return QWidget::event(event); } + +void KeyEditor::updateButtonText() { + QStringList text; + if (m_key >= 0) { + text.append(QString::number(m_key)); + } + if (m_direction != GamepadAxisEvent::NEUTRAL) { + text.append((m_direction == GamepadAxisEvent::NEGATIVE ? "-" : "+") + QString::number(m_axis)); + } + if (text.isEmpty()) { + setText(tr("---")); + } else { + setText(text.join("/")); + } +} diff --git a/src/platform/qt/KeyEditor.h b/src/platform/qt/KeyEditor.h index 817c00c1c..66b12bb01 100644 --- a/src/platform/qt/KeyEditor.h +++ b/src/platform/qt/KeyEditor.h @@ -20,6 +20,7 @@ public: int value() const { return m_key; } GamepadAxisEvent::Direction direction() const { return m_direction; } + int axis() const { return m_axis; } virtual QSize sizeHint() const override; @@ -38,7 +39,10 @@ protected: virtual bool event(QEvent* event) override; private: + void updateButtonText(); + int m_key; + int m_axis; bool m_button; GamepadAxisEvent::Direction m_direction; }; From e9436e43dbc19b8f0e7e3a2a7e7c3d73cd6fcf14 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 24 Jul 2015 00:01:43 -0700 Subject: [PATCH 45/52] Qt: Reduce duplicated lines --- src/platform/qt/GBAKeyEditor.cpp | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 35b7a0317..d5b1042b8 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -68,28 +68,6 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& } #endif - connect(m_keyDU, SIGNAL(valueChanged(int)), this, SLOT(setNext())); - connect(m_keyDD, SIGNAL(valueChanged(int)), this, SLOT(setNext())); - connect(m_keyDL, SIGNAL(valueChanged(int)), this, SLOT(setNext())); - connect(m_keyDR, SIGNAL(valueChanged(int)), this, SLOT(setNext())); - connect(m_keySelect, SIGNAL(valueChanged(int)), this, SLOT(setNext())); - connect(m_keyStart, SIGNAL(valueChanged(int)), this, SLOT(setNext())); - connect(m_keyA, SIGNAL(valueChanged(int)), this, SLOT(setNext())); - connect(m_keyB, SIGNAL(valueChanged(int)), this, SLOT(setNext())); - connect(m_keyL, SIGNAL(valueChanged(int)), this, SLOT(setNext())); - connect(m_keyR, SIGNAL(valueChanged(int)), this, SLOT(setNext())); - - connect(m_keyDU, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); - connect(m_keyDD, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); - connect(m_keyDL, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); - connect(m_keyDR, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); - connect(m_keySelect, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); - connect(m_keyStart, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); - connect(m_keyA, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); - connect(m_keyB, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); - connect(m_keyL, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); - connect(m_keyR, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); - m_buttons = new QWidget(this); QVBoxLayout* layout = new QVBoxLayout; m_buttons->setLayout(layout); @@ -116,6 +94,11 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& m_keyR }; + for (auto& key : m_keyOrder) { + connect(key, SIGNAL(valueChanged(int)), this, SLOT(setNext())); + connect(key, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); + } + m_currentKey = m_keyOrder.end(); m_background.load(":/res/keymap.qpic"); From 97b82ae6cdd2202e41d3c91df07979048d46a79f Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 24 Jul 2015 19:57:05 -0700 Subject: [PATCH 46/52] VFS: Fix file handle leak in 7z --- src/util/vfs/vfs-lzma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util/vfs/vfs-lzma.c b/src/util/vfs/vfs-lzma.c index c9dafe7b3..c090ac921 100644 --- a/src/util/vfs/vfs-lzma.c +++ b/src/util/vfs/vfs-lzma.c @@ -95,6 +95,7 @@ struct VDir* VDirOpen7z(const char* path, int flags) { SzArEx_Init(&vd->db); SRes res = SzArEx_Open(&vd->db, &vd->lookStream.s, &vd->allocImp, &vd->allocTempImp); if (res != SZ_OK) { + File_Close(&vd->archiveStream.file); free(vd); return 0; } @@ -115,6 +116,7 @@ struct VDir* VDirOpen7z(const char* path, int flags) { bool _vf7zClose(struct VFile* vf) { struct VFile7z* vf7z = (struct VFile7z*) vf; IAlloc_Free(&vf7z->vd->allocImp, vf7z->outBuffer); + File_Close(&vf7z->vd->archiveStream.file); return true; } From 3c55784c549fd5b022a7ffd4dbb76ba2254a90b4 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 25 Jul 2015 12:19:19 -0700 Subject: [PATCH 47/52] GBA Hardware: Game Boy Player detection is off by default --- src/gba/gba.c | 4 +- src/gba/hardware.c | 19 +-- src/gba/hardware.h | 3 +- src/gba/supervisor/overrides.c | 6 + src/platform/qt/OverrideView.cpp | 5 + src/platform/qt/OverrideView.ui | 219 +++++++++++++++++++------------ 6 files changed, 164 insertions(+), 92 deletions(-) diff --git a/src/gba/gba.c b/src/gba/gba.c index d4aa5d98e..732da9eed 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -787,7 +787,9 @@ void GBAFrameEnded(struct GBA* gba) { gba->stream->postVideoFrame(gba->stream, gba->video.renderer); } - GBAHardwarePlayerUpdate(gba); + if (gba->memory.hw.devices & (HW_GB_PLAYER | HW_GB_PLAYER_DETECTION)) { + GBAHardwarePlayerUpdate(gba); + } struct GBAThread* thread = GBAThreadGetContext(); if (!thread) { diff --git a/src/gba/hardware.c b/src/gba/hardware.c index 7d5a16d7a..fc4875f47 100644 --- a/src/gba/hardware.c +++ b/src/gba/hardware.c @@ -44,6 +44,16 @@ static const int RTC_BYTES[8] = { void GBAHardwareInit(struct GBACartridgeHardware* hw, uint16_t* base) { hw->gpioBase = base; GBAHardwareClear(hw); + + hw->gbpCallback.d.readKeys = _gbpRead; + hw->gbpCallback.p = hw; + hw->gbpDriver.d.init = 0; + hw->gbpDriver.d.deinit = 0; + hw->gbpDriver.d.load = 0; + hw->gbpDriver.d.unload = 0; + hw->gbpDriver.d.writeRegister = _gbpSioWriteRegister; + hw->gbpDriver.d.processEvents = _gbpSioProcessEvents; + hw->gbpDriver.p = hw; } void GBAHardwareClear(struct GBACartridgeHardware* hw) { @@ -516,15 +526,6 @@ void GBAHardwarePlayerUpdate(struct GBA* gba) { } if (GBAHardwarePlayerCheckScreen(&gba->video)) { gba->memory.hw.devices |= HW_GB_PLAYER; - gba->memory.hw.gbpCallback.d.readKeys = _gbpRead; - gba->memory.hw.gbpCallback.p = &gba->memory.hw; - gba->memory.hw.gbpDriver.d.init = 0; - gba->memory.hw.gbpDriver.d.deinit = 0; - gba->memory.hw.gbpDriver.d.load = 0; - gba->memory.hw.gbpDriver.d.unload = 0; - gba->memory.hw.gbpDriver.d.writeRegister = _gbpSioWriteRegister; - gba->memory.hw.gbpDriver.d.processEvents = _gbpSioProcessEvents; - gba->memory.hw.gbpDriver.p = &gba->memory.hw; gba->memory.hw.gbpInputsPosted = 0; gba->memory.hw.gbpNextEvent = INT_MAX; gba->keyCallback = &gba->memory.hw.gbpCallback.d; diff --git a/src/gba/hardware.h b/src/gba/hardware.h index 53a4899a7..7923d0669 100644 --- a/src/gba/hardware.h +++ b/src/gba/hardware.h @@ -34,7 +34,8 @@ enum GBAHardwareDevice { HW_LIGHT_SENSOR = 4, HW_GYRO = 8, HW_TILT = 16, - HW_GB_PLAYER = 32 + HW_GB_PLAYER = 32, + HW_GB_PLAYER_DETECTION = 64 }; enum GPIORegister { diff --git a/src/gba/supervisor/overrides.c b/src/gba/supervisor/overrides.c index 5ed596644..3d48d10d5 100644 --- a/src/gba/supervisor/overrides.c +++ b/src/gba/supervisor/overrides.c @@ -285,6 +285,12 @@ void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* overri if (override->hardware & HW_TILT) { GBAHardwareInitTilt(&gba->memory.hw); } + + if (override->hardware & HW_GB_PLAYER_DETECTION) { + gba->memory.hw.devices |= HW_GB_PLAYER_DETECTION; + } else { + gba->memory.hw.devices &= ~HW_GB_PLAYER_DETECTION; + } } if (override->idleLoop != IDLE_LOOP_NONE) { diff --git a/src/platform/qt/OverrideView.cpp b/src/platform/qt/OverrideView.cpp index 76a6ca35a..738760684 100644 --- a/src/platform/qt/OverrideView.cpp +++ b/src/platform/qt/OverrideView.cpp @@ -39,6 +39,7 @@ OverrideView::OverrideView(GameController* controller, ConfigController* config, connect(m_ui.hwLight, SIGNAL(clicked()), this, SLOT(updateOverrides())); connect(m_ui.hwTilt, SIGNAL(clicked()), this, SLOT(updateOverrides())); connect(m_ui.hwRumble, SIGNAL(clicked()), this, SLOT(updateOverrides())); + connect(m_ui.hwGBPlayer, SIGNAL(clicked()), this, SLOT(updateOverrides())); connect(m_ui.save, SIGNAL(clicked()), this, SLOT(saveOverride())); @@ -80,6 +81,9 @@ void OverrideView::updateOverrides() { m_override.hardware |= HW_RUMBLE; } } + if (m_ui.hwGBPlayer->isChecked()) { + m_override.hardware |= HW_GB_PLAYER_DETECTION; + } bool ok; uint32_t parsedIdleLoop = m_ui.idleLoop->text().toInt(&ok, 16); @@ -115,6 +119,7 @@ void OverrideView::gameStarted(GBAThread* thread) { m_ui.hwLight->setChecked(thread->gba->memory.hw.devices & HW_LIGHT_SENSOR); m_ui.hwTilt->setChecked(thread->gba->memory.hw.devices & HW_TILT); m_ui.hwRumble->setChecked(thread->gba->memory.hw.devices & HW_RUMBLE); + m_ui.hwGBPlayer->setChecked(thread->gba->memory.hw.devices & HW_GB_PLAYER_DETECTION); if (thread->gba->idleLoop != IDLE_LOOP_NONE) { m_ui.idleLoop->setText(QString::number(thread->gba->idleLoop, 16)); diff --git a/src/platform/qt/OverrideView.ui b/src/platform/qt/OverrideView.ui index 6925ede96..bb99fc40a 100644 --- a/src/platform/qt/OverrideView.ui +++ b/src/platform/qt/OverrideView.ui @@ -6,8 +6,8 @@ 0 0 - 409 - 228 + 401 + 203 @@ -23,13 +23,19 @@ QLayout::SetFixedSize - + Qt::Horizontal + + + 0 + 0 + + @@ -44,7 +50,135 @@ - + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + + + + + + Save type + + + + + + + + Autodetect + + + + + None + + + + + SRAM + + + + + Flash 512kb + + + + + Flash 1Mb + + + + + EEPROM + + + + + + + + Idle loop + + + + + + + + + + Qt::Horizontal + + + + + + + + + Qt::Horizontal + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + Game Boy Player features + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + @@ -113,83 +247,6 @@ - - - - - - - - QFormLayout::AllNonFixedFieldsGrow - - - - - Save type - - - - - - - - Autodetect - - - - - None - - - - - SRAM - - - - - Flash 512kb - - - - - Flash 1Mb - - - - - EEPROM - - - - - - - - Idle loop - - - - - - - - - - Qt::Horizontal - - - - - - - - - - Qt::Vertical - - - From 24a910c9d3add3d82df03479dca39b76e62d1ac0 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 25 Jul 2015 21:55:28 -0700 Subject: [PATCH 48/52] Qt: Buttons for clearing analog and buttons --- src/platform/qt/GBAKeyEditor.cpp | 35 +++++++++++++++++++++++++++++++- src/platform/qt/GBAKeyEditor.h | 1 + src/platform/qt/KeyEditor.cpp | 13 ++++++++++++ src/platform/qt/KeyEditor.h | 2 ++ 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index d5b1042b8..26e30fa9d 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -6,6 +6,7 @@ #include "GBAKeyEditor.h" #include +#include #include #include #include @@ -24,6 +25,7 @@ const qreal GBAKeyEditor::DPAD_HEIGHT = 0.1; GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& profile, QWidget* parent) : QWidget(parent) , m_profileSelect(nullptr) + , m_clear(nullptr) , m_type(type) , m_profile(profile) , m_controller(controller) @@ -65,6 +67,33 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& m_controller->loadProfile(m_type, m_profile); refresh(); }); + + m_clear = new QWidget(this); + QHBoxLayout* layout = new QHBoxLayout; + m_clear->setLayout(layout); + layout->setSpacing(6); + + QPushButton* clearButton = new QPushButton(tr("Clear Button")); + layout->addWidget(clearButton); + connect(clearButton, &QAbstractButton::pressed, [this]() { + if (!findFocus()) { + return; + } + bool signalsBlocked = (*m_currentKey)->blockSignals(true); + (*m_currentKey)->clearButton(); + (*m_currentKey)->blockSignals(signalsBlocked); + }); + + QPushButton* clearAxis = new QPushButton(tr("Clear Analog")); + layout->addWidget(clearAxis); + connect(clearAxis, &QAbstractButton::pressed, [this]() { + if (!findFocus()) { + return; + } + bool signalsBlocked = (*m_currentKey)->blockSignals(true); + (*m_currentKey)->clearAxis(); + (*m_currentKey)->blockSignals(signalsBlocked); + }); } #endif @@ -125,7 +154,11 @@ void GBAKeyEditor::resizeEvent(QResizeEvent* event) { setLocation(m_keyR, 0.9, 0.1); if (m_profileSelect) { - setLocation(m_profileSelect, 0.5, 0.7); + setLocation(m_profileSelect, 0.5, 0.67); + } + + if (m_clear) { + setLocation(m_clear, 0.5, 0.77); } } diff --git a/src/platform/qt/GBAKeyEditor.h b/src/platform/qt/GBAKeyEditor.h index 65707d4eb..c1ba3229f 100644 --- a/src/platform/qt/GBAKeyEditor.h +++ b/src/platform/qt/GBAKeyEditor.h @@ -66,6 +66,7 @@ private: KeyEditor* keyById(GBAKey); QComboBox* m_profileSelect; + QWidget* m_clear; QWidget* m_buttons; KeyEditor* m_keyDU; KeyEditor* m_keyDD; diff --git a/src/platform/qt/KeyEditor.cpp b/src/platform/qt/KeyEditor.cpp index f8323c0a3..ef06d6e41 100644 --- a/src/platform/qt/KeyEditor.cpp +++ b/src/platform/qt/KeyEditor.cpp @@ -50,6 +50,19 @@ void KeyEditor::setValueAxis(int axis, int32_t value) { emit axisChanged(axis, m_direction); } +void KeyEditor::clearButton() { + m_button = true; + setValue(-1); +} + +void KeyEditor::clearAxis() { + m_button = true; + m_axis = -1; + m_direction = GamepadAxisEvent::NEUTRAL; + updateButtonText(); + emit axisChanged(m_axis, m_direction); +} + QSize KeyEditor::sizeHint() const { QSize hint = QLineEdit::sizeHint(); hint.setWidth(40); diff --git a/src/platform/qt/KeyEditor.h b/src/platform/qt/KeyEditor.h index 66b12bb01..8fdd04f34 100644 --- a/src/platform/qt/KeyEditor.h +++ b/src/platform/qt/KeyEditor.h @@ -29,6 +29,8 @@ public slots: void setValueKey(int key); void setValueButton(int button); void setValueAxis(int axis, int32_t value); + void clearButton(); + void clearAxis(); signals: void valueChanged(int key); From 8c1194244d298f3b546a85220f139db539667077 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 25 Jul 2015 22:02:16 -0700 Subject: [PATCH 49/52] Qt: Fix analog buttons not getting unmapped --- CHANGES | 1 + src/platform/qt/GBAKeyEditor.cpp | 4 ++++ src/platform/qt/InputController.cpp | 4 ++++ src/platform/qt/InputController.h | 1 + 4 files changed, 10 insertions(+) diff --git a/CHANGES b/CHANGES index 8d296b211..99b9dfdd6 100644 --- a/CHANGES +++ b/CHANGES @@ -63,6 +63,7 @@ Bugfixes: - Qt: Fix window being too tall after exiting fullscreen - Qt: Fix a missing va_end call in the log handler lambda within the GameController constructor - GBA Cheats: Fix Pro Action Replay and GameShark issues when used together + - Qt: Fix analog buttons not getting unmapped Misc: - Qt: Handle saving input settings better - Debugger: Free watchpoints in addition to breakpoints diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 26e30fa9d..c86ed2bab 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -197,6 +197,10 @@ void GBAKeyEditor::setNext() { } void GBAKeyEditor::save() { +#ifdef BUILD_SDL + m_controller->unbindAllAxes(m_type); +#endif + bindKey(m_keyDU, GBA_KEY_UP); bindKey(m_keyDD, GBA_KEY_DOWN); bindKey(m_keyDL, GBA_KEY_LEFT); diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index c0d19d9fa..ebc73e267 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -413,6 +413,10 @@ void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direct GBAInputBindAxis(&m_inputMap, type, axis, &description); } +void InputController::unbindAllAxes(uint32_t type) { + GBAInputUnbindAllAxes(&m_inputMap, type); +} + void InputController::testGamepad(int type) { auto activeAxes = activeGamepadAxes(type); auto oldAxes = m_activeAxes; diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index 572210b80..b0e25e92f 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -60,6 +60,7 @@ public: void recalibrateAxes(); void bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction, GBAKey); + void unbindAllAxes(uint32_t type); QStringList connectedGamepads(uint32_t type) const; int gamepad(uint32_t type) const; From 1c6d87f578edee720b4e9263403b39c7182917ed Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 25 Jul 2015 22:03:31 -0700 Subject: [PATCH 50/52] All: Update CHANGES --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 99b9dfdd6..540ad89d1 100644 --- a/CHANGES +++ b/CHANGES @@ -108,6 +108,7 @@ Misc: - GBA: Savedata is now synced shortly after data finishes being written - GBA Input: Allow axes and buttons to be mapped to the same key - GBA BIOS: Stub out SoundBias + - Qt: Gamepads can now have both buttons and analog axes mapped to the same key 0.2.1: (2015-05-13) Bugfixes: From f5092737ff65837d00b7e71f89b3e2396260adce Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 26 Jul 2015 11:19:10 -0700 Subject: [PATCH 51/52] GBA Input: Add GBAInputMapKeyBits for packed key information --- src/gba/input.c | 14 ++++++++++++++ src/gba/input.h | 1 + 2 files changed, 15 insertions(+) diff --git a/src/gba/input.c b/src/gba/input.c index 6695a619d..a204741a2 100644 --- a/src/gba/input.c +++ b/src/gba/input.c @@ -352,6 +352,20 @@ enum GBAKey GBAInputMapKey(const struct GBAInputMap* map, uint32_t type, int key return GBA_KEY_NONE; } +int GBAInputMapKeyBits(const struct GBAInputMap* map, uint32_t type, uint32_t bits, unsigned offset) { + int keys = 0; + for (; bits; bits >>= 1, ++offset) { + if (bits & 1) { + enum GBAKey key = GBAInputMapKey(map, type, offset); + if (key == GBA_KEY_NONE) { + continue; + } + keys |= 1 << key; + } + } + return keys; +} + void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) { struct GBAInputMapImpl* impl = _guaranteeMap(map, type); GBAInputUnbindKey(map, type, input); diff --git a/src/gba/input.h b/src/gba/input.h index b83237557..2332b7e75 100644 --- a/src/gba/input.h +++ b/src/gba/input.h @@ -30,6 +30,7 @@ void GBAInputMapInit(struct GBAInputMap*); void GBAInputMapDeinit(struct GBAInputMap*); enum GBAKey GBAInputMapKey(const struct GBAInputMap*, uint32_t type, int key); +int GBAInputMapKeyBits(const struct GBAInputMap* map, uint32_t type, uint32_t bits, unsigned offset); void GBAInputBindKey(struct GBAInputMap*, uint32_t type, int key, enum GBAKey input); void GBAInputUnbindKey(struct GBAInputMap*, uint32_t type, enum GBAKey input); int GBAInputQueryBinding(const struct GBAInputMap*, uint32_t type, enum GBAKey input); From 818bde5869d09f8ea502697bb86eb23568771c22 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 26 Jul 2015 18:22:17 -0700 Subject: [PATCH 52/52] GBA: Add function for loading default overrides --- src/gba/supervisor/overrides.c | 9 +++++++++ src/gba/supervisor/overrides.h | 1 + src/platform/libretro/libretro.c | 8 +------- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/gba/supervisor/overrides.c b/src/gba/supervisor/overrides.c index 3d48d10d5..85e0f8462 100644 --- a/src/gba/supervisor/overrides.c +++ b/src/gba/supervisor/overrides.c @@ -300,3 +300,12 @@ void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* overri } } } + +void GBAOverrideApplyDefaults(struct GBA* gba) { + struct GBACartridgeOverride override; + const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom; + memcpy(override.id, &cart->id, sizeof(override.id)); + if (GBAOverrideFind(0, &override)) { + GBAOverrideApply(gba, &override); + } +} diff --git a/src/gba/supervisor/overrides.h b/src/gba/supervisor/overrides.h index cd374572e..9dc7fb67a 100644 --- a/src/gba/supervisor/overrides.h +++ b/src/gba/supervisor/overrides.h @@ -25,5 +25,6 @@ void GBAOverrideSave(struct Configuration*, const struct GBACartridgeOverride* o struct GBA; void GBAOverrideApply(struct GBA*, const struct GBACartridgeOverride*); +void GBAOverrideApplyDefaults(struct GBA*); #endif diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index 92c1d51a1..1ae1a9fee 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -235,13 +235,7 @@ bool retro_load_game(const struct retro_game_info* game) { save = VFileFromMemory(savedata, SIZE_CART_FLASH1M); GBALoadROM(&gba, rom, save, game->path); - - struct GBACartridgeOverride override; - const struct GBACartridge* cart = (const struct GBACartridge*) gba.memory.rom; - memcpy(override.id, &cart->id, sizeof(override.id)); - if (GBAOverrideFind(0, &override)) { - GBAOverrideApply(&gba, &override); - } + GBAOverrideApplyDefaults(&gba); ARMReset(&cpu); return true;