Merge branch 'master' into medusa
jump to
@@ -49,6 +49,8 @@ - Qt: German translation (by Lothar Serra Mari)
- Savestates now contain any RTC override data - Command line ability to override configuration values - Add option to allow preloading the entire ROM before running + - GB: Video/audio channel enabling/disabling + - Add option to lock video to integer scaling Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior@@ -73,6 +75,11 @@ - SDL: Fix race condition with audio thread when starting
- GB: Fix flickering when screen is strobed quickly - FFmpeg: Fix overflow and general issues with audio encoding - Qt: Fix crash when changing audio settings after a game is closed + - GBA BIOS: Fix ArcTan sign in HLE BIOS + - GBA BIOS: Fix ArcTan2 sign in HLE BIOS (fixes mgba.io/i/689) + - GBA Video: Don't update background scanline params in mode 0 (fixes mgba.io/i/377) + - Qt: Ensure CLI backend is attached when submitting commands (fixes mgba.io/i/662) + - Core: Fix crash with rewind if savestates shrink Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers@@ -126,6 +133,7 @@ - Core: Restore sleep callback
- Qt: Add .gb/.gbc files to the extension list in Info.plist - Feature: Make -l option explicit - Core: Ability to enumerate and modify video and audio channels + - Debugger: Make attaching a backend idempotent medusa alpha 1: (2017-04-08) Features:
@@ -56,6 +56,11 @@ #include <unistd.h>
#include <sys/time.h> #endif +#ifdef PSP2 +// For PATH_MAX on modern toolchains +#include <sys/syslimits.h> +#endif + #ifndef SSIZE_MAX #define SSIZE_MAX ((ssize_t) (SIZE_MAX >> 1)) #endif
@@ -42,6 +42,7 @@ int fullscreen;
int width; int height; bool lockAspectRatio; + bool lockIntegerScaling; bool resampleVideo; bool suspendScreensaver; char* shader;
@@ -71,6 +71,10 @@
uint8_t* vram; union GBOAM* oam; struct mTileCache* cache; + + bool disableBG; + bool disableOBJ; + bool disableWIN; }; DECL_BITFIELD(GBRegisterLCDC, uint8_t);
@@ -349,6 +349,9 @@ }
if (_lookupIntValue(config, "lockAspectRatio", &fakeBool)) { opts->lockAspectRatio = fakeBool; } + if (_lookupIntValue(config, "lockIntegerScaling", &fakeBool)) { + opts->lockIntegerScaling = fakeBool; + } if (_lookupIntValue(config, "resampleVideo", &fakeBool)) { opts->resampleVideo = fakeBool; }@@ -396,6 +399,7 @@ ConfigurationSetIntValue(&config->defaultsTable, 0, "height", opts->height);
ConfigurationSetIntValue(&config->defaultsTable, 0, "volume", opts->volume); ConfigurationSetIntValue(&config->defaultsTable, 0, "mute", opts->mute); ConfigurationSetIntValue(&config->defaultsTable, 0, "lockAspectRatio", opts->lockAspectRatio); + ConfigurationSetIntValue(&config->defaultsTable, 0, "lockIntegerScaling", opts->lockIntegerScaling); ConfigurationSetIntValue(&config->defaultsTable, 0, "resampleVideo", opts->resampleVideo); ConfigurationSetIntValue(&config->defaultsTable, 0, "suspendScreensaver", opts->suspendScreensaver); }
@@ -50,6 +50,8 @@ size_t size = context->currentState->size(context->currentState);
if (size2 > size) { context->currentState->truncate(context->currentState, size2); size = size2; + } else if (size > size2) { + nextState->truncate(nextState, size); } void* current = context->currentState->map(context->currentState, size, MAP_READ); void* next = nextState->map(nextState, size, MAP_READ);
@@ -751,6 +751,9 @@ system->p = debugger;
} void CLIDebuggerAttachBackend(struct CLIDebugger* debugger, struct CLIDebuggerBackend* backend) { + if (debugger->backend == backend) { + return; + } if (debugger->backend && debugger->backend->deinit) { debugger->backend->deinit(debugger->backend); }
@@ -465,11 +465,13 @@ ARMHotplugAttach(ds->ds9.cpu, CPU_COMPONENT_DEBUGGER);
} void DSDetachDebugger(struct DS* ds) { - ds->debugger = NULL; - ARMHotplugDetach(ds->ds7.cpu, CPU_COMPONENT_DEBUGGER); - ARMHotplugDetach(ds->ds9.cpu, CPU_COMPONENT_DEBUGGER); + if (ds->debugger) { + ARMHotplugDetach(ds->ds7.cpu, CPU_COMPONENT_DEBUGGER); + ARMHotplugDetach(ds->ds9.cpu, CPU_COMPONENT_DEBUGGER); + } ds->ds7.cpu->components[CPU_COMPONENT_DEBUGGER] = NULL; ds->ds9.cpu->components[CPU_COMPONENT_DEBUGGER] = NULL; + ds->debugger = NULL; } bool DSLoadROM(struct DS* ds, struct VFile* vf) {
@@ -640,8 +640,8 @@ audio->lastLeft = sampleLeft;
audio->lastRight = sampleRight; audio->clock += audio->sampleInterval; if (audio->clock >= CLOCKS_PER_BLIP_FRAME) { - blip_end_frame(audio->left, audio->clock); - blip_end_frame(audio->right, audio->clock); + blip_end_frame(audio->left, CLOCKS_PER_BLIP_FRAME); + blip_end_frame(audio->right, CLOCKS_PER_BLIP_FRAME); audio->clock -= CLOCKS_PER_BLIP_FRAME; } }
@@ -629,7 +629,19 @@ }
static void _GBCoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) { struct GB* gb = core->board; - // TODO + switch (id) { + case 0: + gb->video.renderer->disableBG = !enable; + break; + case 1: + gb->video.renderer->disableOBJ = !enable; + break; + case 2: + gb->video.renderer->disableWIN = !enable; + break; + default: + break; + } } static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
@@ -49,6 +49,10 @@ renderer->d.finishFrame = GBVideoSoftwareRendererFinishFrame;
renderer->d.getPixels = GBVideoSoftwareRendererGetPixels; renderer->d.putPixels = GBVideoSoftwareRendererPutPixels; + renderer->d.disableBG = false; + renderer->d.disableOBJ = false; + renderer->d.disableWIN = false; + renderer->temporaryBuffer = 0; }@@ -128,9 +132,12 @@ uint8_t* maps = &softwareRenderer->d.vram[GB_BASE_MAP];
if (GBRegisterLCDCIsTileMap(softwareRenderer->lcdc)) { maps += GB_SIZE_MAP; } + if (softwareRenderer->d.disableBG) { + memset(&softwareRenderer->row[startX], 0, endX - startX); + } if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc) || softwareRenderer->model >= GB_MODEL_CGB) { if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && endX >= softwareRenderer->wx - 7) { - if (softwareRenderer->wx - 7 > 0) { + if (softwareRenderer->wx - 7 > 0 && !softwareRenderer->d.disableBG) { GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx, softwareRenderer->scy + y); }@@ -138,15 +145,17 @@ maps = &softwareRenderer->d.vram[GB_BASE_MAP];
if (GBRegisterLCDCIsWindowTileMap(softwareRenderer->lcdc)) { maps += GB_SIZE_MAP; } - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, softwareRenderer->currentWy); - } else { + if (!softwareRenderer->d.disableWIN) { + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, softwareRenderer->currentWy); + } + } else if (!softwareRenderer->d.disableBG) { GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx, softwareRenderer->scy + y); } - } else { + } else if (!softwareRenderer->d.disableBG) { memset(&softwareRenderer->row[startX], 0, endX - startX); } - if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc)) { + if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ) { size_t i; for (i = 0; i < oamMax; ++i) { GBVideoSoftwareRendererDrawObj(softwareRenderer, &obj[i], startX, endX, y);
@@ -297,8 +297,8 @@ audio->lastLeft = sampleLeft;
audio->lastRight = sampleRight; audio->clock += audio->sampleInterval; if (audio->clock >= CLOCKS_PER_FRAME) { - blip_end_frame(audio->psg.left, audio->clock); - blip_end_frame(audio->psg.right, audio->clock); + blip_end_frame(audio->psg.left, CLOCKS_PER_FRAME); + blip_end_frame(audio->psg.right, CLOCKS_PER_FRAME); audio->clock -= CLOCKS_PER_FRAME; } }
@@ -271,7 +271,7 @@ cpu->gprs[3] = 1;
} } -static int16_t _ArcTan(int16_t i) { +static int16_t _ArcTan(int32_t i, int32_t* r1, int32_t* r3) { int32_t a = -((i * i) >> 14); int32_t b = ((0xA9 * a) >> 14) + 0x390; b = ((b * a) >> 14) + 0x91C;@@ -280,10 +280,16 @@ b = ((b * a) >> 14) + 0x16AA;
b = ((b * a) >> 14) + 0x2081; b = ((b * a) >> 14) + 0x3651; b = ((b * a) >> 14) + 0xA2F9; + if (r1) { + *r1 = a; + } + if (r3) { + *r3 = b; + } return (i * b) >> 16; } -static int16_t _ArcTan2(int16_t x, int16_t y) { +static int16_t _ArcTan2(int32_t x, int32_t y, int32_t* r1) { if (!y) { if (x >= 0) { return 0;@@ -299,21 +305,21 @@ }
if (y >= 0) { if (x >= 0) { if (x >= y) { - return _ArcTan((y << 14)/ x); + return _ArcTan((y << 14) / x, r1, NULL); } } else if (-x >= y) { - return _ArcTan((y << 14) / x) + 0x8000; + return _ArcTan((y << 14) / x, r1, NULL) + 0x8000; } - return 0x4000 - _ArcTan((x << 14) / y); + return 0x4000 - _ArcTan((x << 14) / y, r1, NULL); } else { if (x <= 0) { if (-x > -y) { - return _ArcTan((y << 14) / x) + 0x8000; + return _ArcTan((y << 14) / x, r1, NULL) + 0x8000; } } else if (x >= -y) { - return _ArcTan((y << 14) / x) + 0x10000; + return _ArcTan((y << 14) / x, r1, NULL) + 0x10000; } - return 0xC000 - _ArcTan((x << 14) / y); + return 0xC000 - _ArcTan((x << 14) / y, r1, NULL); } }@@ -356,10 +362,11 @@ case 0x8:
cpu->gprs[0] = sqrt((uint32_t) cpu->gprs[0]); break; case 0x9: - cpu->gprs[0] = (uint16_t) _ArcTan(cpu->gprs[0]); + cpu->gprs[0] = _ArcTan(cpu->gprs[0], &cpu->gprs[1], &cpu->gprs[3]); break; case 0xA: - cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1]); + cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1], &cpu->gprs[1]); + cpu->gprs[3] = 0x170; break; case 0xB: case 0xC:
@@ -268,6 +268,7 @@ #endif
} } +#ifdef USE_DEBUGGERS void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) { gba->debugger = (struct ARMDebugger*) debugger->platform; gba->debugger->setSoftwareBreakpoint = _setSoftwareBreakpoint;@@ -277,10 +278,13 @@ ARMHotplugAttach(gba->cpu, CPU_COMPONENT_DEBUGGER);
} void GBADetachDebugger(struct GBA* gba) { - gba->debugger = 0; - ARMHotplugDetach(gba->cpu, CPU_COMPONENT_DEBUGGER); - gba->cpu->components[CPU_COMPONENT_DEBUGGER] = 0; + if (gba->debugger) { + ARMHotplugDetach(gba->cpu, CPU_COMPONENT_DEBUGGER); + } + gba->cpu->components[CPU_COMPONENT_DEBUGGER] = NULL; + gba->debugger = NULL; } +#endif bool GBALoadMB(struct GBA* gba, struct VFile* vf) { GBAUnloadROM(gba);
@@ -581,10 +581,12 @@ }
} } } - softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx; - softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy; - softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx; - softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy; + if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) { + softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx; + softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy; + softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx; + softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy; + } GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
@@ -67,6 +67,10 @@ } else if (w * v->height < h * v->width) {
drawH = w * v->height / v->width; } } + if (v->lockIntegerScaling) { + drawW -= drawW % v->width; + drawH -= drawH % v->height; + } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0, 0, 0, 0);
@@ -171,6 +171,10 @@ } else if (w * v->height < h * v->width) {
drawH = w * v->height / v->width; } } + if (v->lockIntegerScaling) { + drawW -= drawW % v->width; + drawH -= drawH % v->height; + } glViewport(0, 0, v->width, v->height); glClearColor(0.f, 0.f, 0.f, 1.f); glClear(GL_COLOR_BUFFER_BIT);
@@ -16,7 +16,6 @@ #include <psp2/ctrl.h>
#include <psp2/display.h> #include <psp2/kernel/processmgr.h> #include <psp2/kernel/threadmgr.h> -#include <psp2/moduleinfo.h> #include <psp2/power.h> #include <psp2/sysmodule.h> #include <psp2/touch.h>@@ -164,6 +163,7 @@ mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_LEFT, GUI_INPUT_LEFT);
mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_RIGHT, GUI_INPUT_RIGHT); mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_SQUARE, mGUI_INPUT_SCREEN_MODE); + scePowerSetArmClockFrequency(444); mGUIRunloop(&runner); vita2d_fini();
@@ -20,6 +20,7 @@ #include <mgba/internal/gba/input.h>
#include <mgba-util/memory.h> #include <mgba-util/circle-buffer.h> +#include <mgba-util/math.h> #include <mgba-util/ring-fifo.h> #include <mgba-util/threading.h> #include <mgba-util/vfs.h>@@ -31,7 +32,6 @@ #include <psp2/display.h>
#include <psp2/gxm.h> #include <psp2/kernel/sysmem.h> #include <psp2/motion.h> -#include <psp2/power.h> #include <vita2d.h>@@ -63,8 +63,8 @@
extern const uint8_t _binary_backdrop_png_start[]; static vita2d_texture* backdrop = 0; -#define PSP2_SAMPLES 128 -#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 40) +#define PSP2_SAMPLES 256 +#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 20) static struct mPSP2AudioContext { struct RingFIFO buffer;@@ -176,7 +176,6 @@ void mPSP2Setup(struct mGUIRunner* runner) {
mCoreConfigSetDefaultIntValue(&runner->config, "threadedVideo", 1); mCoreLoadForeignConfig(runner->core, &runner->config); - scePowerSetArmClockFrequency(333); mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_CROSS, GBA_KEY_A); mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_CIRCLE, GBA_KEY_B); mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_START, GBA_KEY_START);@@ -193,8 +192,10 @@ mInputBindAxis(&runner->core->inputMap, PSP2_INPUT, 0, &desc);
desc = (struct mInputAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 }; mInputBindAxis(&runner->core->inputMap, PSP2_INPUT, 1, &desc); - tex = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); - screenshot = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); + unsigned width, height; + runner->core->desiredVideoDimensions(runner->core, &width, &height); + tex = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); + screenshot = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); outputBuffer = vita2d_texture_get_datap(tex); runner->core->setVideoBuffer(runner->core, outputBuffer, 256);@@ -219,7 +220,6 @@ }
} void mPSP2LoadROM(struct mGUIRunner* runner) { - scePowerSetArmClockFrequency(444); float rate = 60.0f / 1.001f; sceDisplayGetRefreshRate(&rate); double ratio = GBAAudioCalculateRatio(1, rate, 1);@@ -262,7 +262,6 @@ blip_clear(runner->core->getAudioChannel(runner->core, 0));
blip_clear(runner->core->getAudioChannel(runner->core, 1)); break; } - sceKernelDelayThread(400); } blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &samples[0].left, PSP2_SAMPLES, true); blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &samples[0].right, PSP2_SAMPLES, true);@@ -297,7 +296,6 @@ #endif
default: break; } - scePowerSetArmClockFrequency(333); } void mPSP2Paused(struct mGUIRunner* runner) {
@@ -2,8 +2,13 @@ find_program(PYTHON python)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py) +get_property(INCLUDE_DIRECTORIES DIRECTORY PROPERTY INCLUDE_DIRECTORIES) +set(INCLUDE_FLAGS) +foreach(DIR IN LISTS INCLUDE_DIRECTORIES) + list(APPEND INCLUDE_FLAGS "-I${DIR}") +endforeach() add_custom_command(OUTPUT build/lib/${BINARY_NAME}/__init__.py - COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR} + COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${BINARY_NAME} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/setup.py
@@ -38,8 +38,16 @@ }
m_cond.wakeOne(); } +void DebuggerConsoleController::detach() { + m_lines.append(QString()); + m_cond.wakeOne(); + DebuggerController::detach(); +} + void DebuggerConsoleController::attachInternal() { + m_history.clear(); mCore* core = m_gameController->thread()->core; + CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend.d); CLIDebuggerAttachSystem(&m_cliDebugger, core->cliDebuggerSystem(core)); }@@ -60,6 +68,8 @@
void DebuggerConsoleController::deinit(struct CLIDebuggerBackend* be) { Backend* consoleBe = reinterpret_cast<Backend*>(be); DebuggerConsoleController* self = consoleBe->self; + self->m_lines.append(QString()); + self->m_cond.wakeOne(); } const char* DebuggerConsoleController::readLine(struct CLIDebuggerBackend* be, size_t* len) {@@ -71,6 +81,9 @@ while (self->m_lines.isEmpty()) {
self->m_cond.wait(&self->m_mutex); } self->m_last = self->m_lines.takeFirst().toUtf8(); + if (self->m_last.isEmpty()) { + self->m_last = "\n"; + } *len = self->m_last.size(); return self->m_last.constData();@@ -87,6 +100,9 @@ Backend* consoleBe = reinterpret_cast<Backend*>(be);
DebuggerConsoleController* self = consoleBe->self; GameController::Interrupter interrupter(self->m_gameController, true); QMutexLocker lock(&self->m_mutex); + if (self->m_history.isEmpty()) { + return "\n"; + } self->m_last = self->m_history.last().toUtf8(); return self->m_last.constData(); }
@@ -30,6 +30,7 @@ void lineAppend(const QString&);
public slots: void enterLine(const QString&); + virtual void detach() override; protected: virtual void attachInternal() override;
@@ -54,6 +54,7 @@
Display::Display(QWidget* parent) : QWidget(parent) , m_lockAspectRatio(false) + , m_lockIntegerScaling(false) , m_filter(false) { setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);@@ -78,6 +79,10 @@ } else if (s.width() * m_coreHeight < s.height() * m_coreWidth) {
ds.setHeight(s.width() * m_coreHeight / m_coreWidth); } } + if (isIntegerScalingLocked()) { + ds.setWidth(ds.width() - ds.width() % m_coreWidth); + ds.setHeight(ds.height() - ds.height() % m_coreHeight); + } return ds; }@@ -88,6 +93,10 @@
void Display::lockAspectRatio(bool lock) { m_lockAspectRatio = lock; m_messagePainter.resize(size(), m_lockAspectRatio, devicePixelRatio()); +} + +void Display::lockIntegerScaling(bool lock) { + m_lockIntegerScaling = lock; } void Display::filter(bool filter) {
@@ -38,6 +38,7 @@ static Display* create(QWidget* parent = nullptr);
static void setDriver(Driver driver) { s_driver = driver; } bool isAspectRatioLocked() const { return m_lockAspectRatio; } + bool isIntegerScalingLocked() const { return m_lockIntegerScaling; } bool isFiltered() const { return m_filter; } virtual bool isDrawing() const = 0;@@ -57,6 +58,7 @@ virtual void pauseDrawing() = 0;
virtual void unpauseDrawing() = 0; virtual void forceDraw() = 0; virtual void lockAspectRatio(bool lock); + virtual void lockIntegerScaling(bool lock); virtual void filter(bool filter); virtual void framePosted(const uint32_t*) = 0; virtual void setShaders(struct VDir*) = 0;@@ -78,6 +80,7 @@ static const int MOUSE_DISAPPEAR_TIMER = 1000;
MessagePainter m_messagePainter; bool m_lockAspectRatio; + bool m_lockIntegerScaling; bool m_filter; QTimer m_mouseTimer; int m_coreWidth;
@@ -73,6 +73,8 @@ m_drawThread->start();
mCoreSyncSetVideoSync(&m_context->sync, false); lockAspectRatio(isAspectRatioLocked()); + lockIntegerScaling(isIntegerScalingLocked()); + unsigned width, height; thread->core->desiredVideoDimensions(thread->core, &width, &height); setSystemDimensions(width, height);@@ -137,6 +139,13 @@ void DisplayGL::lockAspectRatio(bool lock) {
Display::lockAspectRatio(lock); if (m_drawThread) { QMetaObject::invokeMethod(m_painter, "lockAspectRatio", Q_ARG(bool, lock)); + } +} + +void DisplayGL::lockIntegerScaling(bool lock) { + Display::lockIntegerScaling(lock); + if (m_drawThread) { + QMetaObject::invokeMethod(m_painter, "lockIntegerScaling", Q_ARG(bool, lock)); } }@@ -289,6 +298,13 @@ }
void PainterGL::lockAspectRatio(bool lock) { m_backend->lockAspectRatio = lock; + if (m_started && !m_active) { + forceDraw(); + } +} + +void PainterGL::lockIntegerScaling(bool lock) { + m_backend->lockIntegerScaling = lock; if (m_started && !m_active) { forceDraw(); }
@@ -55,6 +55,7 @@ void pauseDrawing() override;
void unpauseDrawing() override; void forceDraw() override; void lockAspectRatio(bool lock) override; + void lockIntegerScaling(bool lock) override; void filter(bool filter) override; void framePosted(const uint32_t*) override; void setShaders(struct VDir*) override;@@ -96,6 +97,7 @@ void pause();
void unpause(); void resize(const QSize& size); void lockAspectRatio(bool lock); + void lockIntegerScaling(bool lock); void filter(bool filter); void setShaders(struct VDir*);
@@ -31,6 +31,11 @@ Display::lockAspectRatio(lock);
update(); } +void DisplayQt::lockIntegerScaling(bool lock) { + Display::lockIntegerScaling(lock); + update(); +} + void DisplayQt::filter(bool filter) { Display::filter(filter); update();
@@ -30,6 +30,7 @@ void pauseDrawing() override { m_isDrawing = false; }
void unpauseDrawing() override { m_isDrawing = true; } void forceDraw() override { update(); } void lockAspectRatio(bool lock) override; + void lockIntegerScaling(bool lock) override; void filter(bool filter) override; void framePosted(const uint32_t*) override; void setShaders(struct VDir*) override {}
@@ -623,6 +623,7 @@ if (!m_gameOpen) {
return; } + setDebugger(nullptr); if (mCoreThreadIsPaused(&m_threadContext)) { mCoreThreadUnpause(&m_threadContext); }
@@ -177,6 +177,7 @@ saveSetting("audioSync", m_ui.audioSync);
saveSetting("frameskip", m_ui.frameskip); saveSetting("fpsTarget", m_ui.fpsTarget); saveSetting("lockAspectRatio", m_ui.lockAspectRatio); + saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling); saveSetting("volume", m_ui.volume); saveSetting("mute", m_ui.mute); saveSetting("rewindEnable", m_ui.rewind);@@ -260,6 +261,7 @@ loadSetting("audioSync", m_ui.audioSync);
loadSetting("frameskip", m_ui.frameskip); loadSetting("fpsTarget", m_ui.fpsTarget); loadSetting("lockAspectRatio", m_ui.lockAspectRatio); + loadSetting("lockIntegerScaling", m_ui.lockIntegerScaling); loadSetting("volume", m_ui.volume); loadSetting("mute", m_ui.mute); loadSetting("rewindEnable", m_ui.rewind);
@@ -7,7 +7,7 @@ <rect>
<x>0</x> <y>0</y> <width>650</width> - <height>450</height> + <height>454</height> </rect> </property> <property name="sizePolicy">@@ -115,7 +115,7 @@ <widget class="QComboBox" name="audioBufferSize">
<property name="editable"> <bool>true</bool> </property> - <property name="currentText" stdset="0"> + <property name="currentText"> <string>1536</string> </property> <property name="currentIndex">@@ -181,7 +181,7 @@ <widget class="QComboBox" name="sampleRate">
<property name="editable"> <bool>true</bool> </property> - <property name="currentText" stdset="0"> + <property name="currentText"> <string>44100</string> </property> <property name="currentIndex">@@ -380,10 +380,17 @@ <string>Lock aspect ratio</string>
</property> </widget> </item> - <item row="11" column="1"> + <item row="12" column="1"> <widget class="QCheckBox" name="resampleVideo"> <property name="text"> <string>Bilinear filtering</string> + </property> + </widget> + </item> + <item row="11" column="1"> + <widget class="QCheckBox" name="lockIntegerScaling"> + <property name="text"> + <string>Force integer scaling</string> </property> </widget> </item>
@@ -493,6 +493,7 @@ }
void Window::openView(QWidget* widget) { connect(this, SIGNAL(shutdown()), widget, SLOT(close())); + connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), widget, SLOT(close())); widget->setAttribute(Qt::WA_DeleteOnClose); widget->show(); }@@ -811,6 +812,39 @@
m_hitUnimplementedBiosCall = false; m_fpsTimer.start(); m_focusCheck.start(); + + m_controller->threadInterrupt(); + if (m_controller->isLoaded()) { + mCore* core = m_controller->thread()->core; + const mCoreChannelInfo* videoLayers; + const mCoreChannelInfo* audioChannels; + size_t nVideo = core->listVideoLayers(core, &videoLayers); + size_t nAudio = core->listAudioChannels(core, &audioChannels); + + if (nVideo) { + for (size_t i = 0; i < nVideo; ++i) { + QAction* action = new QAction(videoLayers[i].visibleName, m_videoLayers); + action->setCheckable(true); + action->setChecked(true); + connect(action, &QAction::triggered, [this, videoLayers, i](bool enable) { + m_controller->setVideoLayerEnabled(videoLayers[i].id, enable); + }); + m_videoLayers->addAction(action); + } + } + if (nAudio) { + for (size_t i = 0; i < nAudio; ++i) { + QAction* action = new QAction(audioChannels[i].visibleName, m_audioChannels); + action->setCheckable(true); + action->setChecked(true); + connect(action, &QAction::triggered, [this, audioChannels, i](bool enable) { + m_controller->setAudioChannelEnabled(audioChannels[i].id, enable); + }); + m_audioChannels->addAction(action); + } + } + } + m_controller->threadContinue(); } void Window::gameStopped() {@@ -834,6 +868,9 @@ #endif
m_screenWidget->setMinimumSize(m_display->minimumSize()); setMouseTracking(false); + m_videoLayers->clear(); + m_audioChannels->clear(); + m_fpsTimer.stop(); m_focusCheck.stop(); }@@ -1301,6 +1338,14 @@ m_display->lockAspectRatio(value.toBool());
}, this); m_config->updateOption("lockAspectRatio"); + ConfigOption* lockIntegerScaling = m_config->addOption("lockIntegerScaling"); + lockIntegerScaling->addBoolean(tr("Force integer scaling"), avMenu); + lockIntegerScaling->connect([this](const QVariant& value) { + m_display->lockIntegerScaling(value.toBool()); + m_screenWidget->setLockIntegerScaling(value.toBool()); + }, this); + m_config->updateOption("lockIntegerScaling"); + ConfigOption* resampleVideo = m_config->addOption("resampleVideo"); resampleVideo->addBoolean(tr("Bilinear filtering"), avMenu); resampleVideo->connect([this](const QVariant& value) {@@ -1376,45 +1421,12 @@ addControlledAction(avMenu, recordGIF, "recordGIF");
#endif avMenu->addSeparator(); - QMenu* videoLayers = avMenu->addMenu(tr("Video layers")); - m_inputModel->addMenu(videoLayers, avMenu); - for (int i = 0; i < 4; ++i) { - QAction* enableBg = new QAction(tr("Background %0").arg(i), videoLayers); - enableBg->setCheckable(true); - enableBg->setChecked(true); - connect(enableBg, &QAction::triggered, [this, i](bool enable) { m_controller->setVideoLayerEnabled(i, enable); }); - addControlledAction(videoLayers, enableBg, QString("enableBG%0").arg(i)); - } - - QAction* enableObj = new QAction(tr("OBJ (sprites)"), videoLayers); - enableObj->setCheckable(true); - enableObj->setChecked(true); - connect(enableObj, &QAction::triggered, [this](bool enable) { m_controller->setVideoLayerEnabled(4, enable); }); - addControlledAction(videoLayers, enableObj, "enableOBJ"); - - QMenu* audioChannels = avMenu->addMenu(tr("Audio channels")); - m_inputModel->addMenu(audioChannels, avMenu); + m_videoLayers = avMenu->addMenu(tr("Video layers")); + m_inputModel->addMenu(m_videoLayers, avMenu); - for (int i = 0; i < 4; ++i) { - QAction* enableCh = new QAction(tr("Channel %0").arg(i + 1), audioChannels); - enableCh->setCheckable(true); - enableCh->setChecked(true); - connect(enableCh, &QAction::triggered, [this, i](bool enable) { m_controller->setAudioChannelEnabled(i, enable); }); - addControlledAction(audioChannels, enableCh, QString("enableCh%0").arg(i + 1)); - } - - QAction* enableChA = new QAction(tr("Channel A"), audioChannels); - enableChA->setCheckable(true); - enableChA->setChecked(true); - connect(enableChA, &QAction::triggered, [this, i](bool enable) { m_controller->setAudioChannelEnabled(4, enable); }); - addControlledAction(audioChannels, enableChA, QString("enableChA")); - - QAction* enableChB = new QAction(tr("Channel B"), audioChannels); - enableChB->setCheckable(true); - enableChB->setChecked(true); - connect(enableChB, &QAction::triggered, [this, i](bool enable) { m_controller->setAudioChannelEnabled(5, enable); }); - addControlledAction(audioChannels, enableChB, QString("enableChB")); + m_audioChannels = avMenu->addMenu(tr("Audio channels")); + m_inputModel->addMenu(m_audioChannels, avMenu); QMenu* toolsMenu = menubar->addMenu(tr("&Tools")); m_inputModel->addMenu(toolsMenu);@@ -1657,6 +1669,10 @@ m_aspectWidth = width;
m_aspectHeight = height; } +void WindowBackground::setLockIntegerScaling(bool lock) { + m_lockIntegerScaling = lock; +} + void WindowBackground::paintEvent(QPaintEvent*) { const QPixmap* logo = pixmap(); if (!logo) {@@ -1679,6 +1695,10 @@ ds.setWidth(ds.height() * m_aspectWidth / m_aspectHeight);
} else if (ds.width() * m_aspectHeight < ds.height() * m_aspectWidth) { ds.setHeight(ds.width() * m_aspectHeight / m_aspectWidth); } + } + if (m_lockIntegerScaling) { + ds.setWidth(ds.width() - ds.width() % m_aspectWidth); + ds.setHeight(ds.height() - ds.height() % m_aspectHeight); } QPoint origin = QPoint((s.width() - ds.width()) / 2, (s.height() - ds.height()) / 2); QRect full(origin, ds);
@@ -172,6 +172,8 @@ QList<QDateTime> m_frameList;
QTimer m_fpsTimer; QList<QString> m_mruFiles; QMenu* m_mruMenu; + QMenu* m_videoLayers; + QMenu* m_audioChannels; ShaderSelector* m_shaderView; bool m_fullscreenOnStart; QTimer m_focusCheck;@@ -207,6 +209,7 @@ void setSizeHint(const QSize& size);
virtual QSize sizeHint() const override; void setLockAspectRatio(int width, int height); void setCenteredAspectRatio(int width, int height); + void setLockIntegerScaling(bool lock); protected: virtual void paintEvent(QPaintEvent*) override;@@ -216,6 +219,7 @@ QSize m_sizeHint;
bool m_centered; int m_aspectWidth; int m_aspectHeight; + bool m_lockIntegerScaling; }; }
@@ -35,6 +35,7 @@ unsigned height;
bool filter; bool lockAspectRatio; + bool lockIntegerScaling; }; struct VideoShader {