From 613280d3b4c8c8ff8c9435099ec17843ec2b020c Mon Sep 17 00:00:00 2001 From: Ed_IT <63556948+Ed-1T@users.noreply.github.com> Date: Tue, 4 Apr 2023 12:31:58 +0200 Subject: DSi power button and volume switch support (#1630) * Add proper BPTWL interrupts * Added DSi power button and volume switch hotkeys * Added hardware reset workaround * Adjusted syntax to follow guidelines * Added DSi output volume synchronization * Fix trivial member function error --- .gitignore | 5 + src/DSi_I2C.cpp | 318 ++++++++++++++++++++- src/DSi_I2C.h | 50 ++++ src/NDS.h | 2 +- src/frontend/qt_sdl/AudioSettingsDialog.cpp | 65 ++++- src/frontend/qt_sdl/AudioSettingsDialog.h | 11 +- src/frontend/qt_sdl/AudioSettingsDialog.ui | 10 + src/frontend/qt_sdl/Config.cpp | 8 + src/frontend/qt_sdl/Config.h | 4 + .../qt_sdl/InputConfig/InputConfigDialog.h | 10 +- src/frontend/qt_sdl/main.cpp | 71 ++++- src/frontend/qt_sdl/main.h | 2 + 12 files changed, 534 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 7bbb40a..e76a094 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,8 @@ compile_commands.json *.exe .DS_Store + +.vs +.vscode +CMakeFiles +CMakeCache.txt diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index b4cfa89..7bb7e1d 100644 --- a/src/DSi_I2C.cpp +++ b/src/DSi_I2C.cpp @@ -18,6 +18,7 @@ #include #include +#include #include "DSi.h" #include "DSi_I2C.h" #include "DSi_Camera.h" @@ -31,6 +32,38 @@ using Platform::LogLevel; namespace DSi_BPTWL { +// TODO: These are purely approximations +const double PowerButtonShutdownTime = 0.5; +const double PowerButtonForcedShutdownTime = 5.0; +const double VolumeSwitchRepeatStart = 0.5; +const double VolumeSwitchRepeatRate = 1.0 / 6; + +// Could not find a pattern or a decent formula for these, +// regardless, they're only 64 bytes in size +const u8 VolumeDownTable[32] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x09, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, +}; + +const u8 VolumeUpTable[32] = +{ + 0x02, 0x03, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, + 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, + 0x1D, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, +}; + +double PowerButtonTime = 0.0; +bool PowerButtonDownFlag = false; +bool PowerButtonShutdownFlag = false; +double VolumeSwitchTime = 0.0; +double VolumeSwitchRepeatTime = 0.0; +bool VolumeSwitchDownFlag = false; +u32 VolumeSwitchKeysDown = 0; + u8 Registers[0x100]; u32 CurPos; @@ -51,9 +84,9 @@ void Reset() Registers[0x00] = 0x33; // TODO: support others?? Registers[0x01] = 0x00; Registers[0x02] = 0x50; - Registers[0x10] = 0x00; // power btn + Registers[0x10] = 0x00; // irq flag Registers[0x11] = 0x00; // reset - Registers[0x12] = 0x00; // power btn tap + Registers[0x12] = 0x00; // irq mode Registers[0x20] = 0x8F; // battery Registers[0x21] = 0x07; Registers[0x30] = 0x13; @@ -74,6 +107,15 @@ void Reset() Registers[0x77] = 0x00; Registers[0x80] = 0x10; Registers[0x81] = 0x64; + + // Ideally these should be replaced by a proper BPTWL core emulator + PowerButtonTime = 0.0; + PowerButtonDownFlag = false; + PowerButtonShutdownFlag = false; + VolumeSwitchTime = 0.0; + VolumeSwitchRepeatTime = 0.0; + VolumeSwitchKeysDown = 0; + } void DoSavestate(Savestate* file) @@ -84,6 +126,12 @@ void DoSavestate(Savestate* file) file->Var32(&CurPos); } +// TODO: Needs more investigation on the other bits +inline bool GetIRQMode() +{ + return Registers[0x12] & 0x01; +} + u8 GetBootFlag() { return Registers[0x70]; } bool GetBatteryCharging() { return Registers[0x20] >> 7; } @@ -97,6 +145,242 @@ void SetBatteryLevel(u8 batteryLevel) { Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F)); SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false); + + if (batteryLevel <= 1) + { + SetIRQ(batteryLevel ? IRQ_BatteryLow : IRQ_BatteryEmpty); + } + +} + +u8 GetVolumeLevel() { return Registers[0x40]; } +void SetVolumeLevel(u8 volume) +{ + Registers[0x40] = volume & 0x1F; +} + +u8 GetBacklightLevel() { return Registers[0x41]; } +void SetBacklightLevel(u8 backlight) +{ + Registers[0x41] = backlight > 4 ? 4 : backlight; +} + + +void ResetButtonState() +{ + PowerButtonTime = 0.0; + PowerButtonDownFlag = false; + PowerButtonShutdownFlag = false; + + VolumeSwitchKeysDown = 0; + VolumeSwitchDownFlag = false; + VolumeSwitchTime = 0.0; + VolumeSwitchRepeatTime = 0.0; +} + +void DoHardwareReset(bool direct) +{ + ResetButtonState(); + + Log(LogLevel::Debug, "BPTWL: soft-reset\n"); + + if (direct) + { + // TODO: This doesn't seem to stop the SPU + DSi::SoftReset(); + return; + } + + // TODO: soft-reset might need to be scheduled later! + // TODO: this has been moved for the JIT to work, nothing is confirmed here + NDS::ARM7->Halt(4); +} + +void DoShutdown() +{ + ResetButtonState(); + NDS::Stop(); +} + + +void SetPowerButtonHeld(double time) +{ + if (!PowerButtonDownFlag) + { + PowerButtonDownFlag = true; + PowerButtonTime = time; + DoPowerButtonPress(); + return; + } + + double elapsed = time - PowerButtonTime; + if (elapsed < 0) + return; + + if (elapsed >= PowerButtonForcedShutdownTime) + { + Log(LogLevel::Debug, "Force power off via DSi power button\n"); + DoPowerButtonForceShutdown(); + return; + } + + if (elapsed >= PowerButtonShutdownTime) + { + DoPowerButtonShutdown(); + } +} + +void SetPowerButtonReleased(double time) +{ + double elapsed = time - PowerButtonTime; + if (elapsed >= 0 && elapsed < PowerButtonShutdownTime) + { + DoPowerButtonReset(); + } + + PowerButtonTime = 0.0; + PowerButtonDownFlag = false; + PowerButtonShutdownFlag = false; +} + +void SetVolumeSwitchHeld(u32 key) +{ + VolumeSwitchKeysDown |= (1 << key); +} + +void SetVolumeSwitchReleased(u32 key) +{ + VolumeSwitchKeysDown &= ~(1 << key); + VolumeSwitchDownFlag = false; + VolumeSwitchTime = 0.0; + VolumeSwitchRepeatTime = 0.0; +} + +inline bool CheckVolumeSwitchKeysValid() +{ + bool up = VolumeSwitchKeysDown & (1 << volumeKey_Up); + bool down = VolumeSwitchKeysDown & (1 << volumeKey_Down); + + return up != down; +} + +s32 ProcessVolumeSwitchInput(double time) +{ + if (!CheckVolumeSwitchKeysValid()) + return -1; + + s32 key = VolumeSwitchKeysDown & (1 << volumeKey_Up) ? volumeKey_Up : volumeKey_Down; + + // Always fire an IRQ when first pressed + if (!VolumeSwitchDownFlag) + { + VolumeSwitchDownFlag = true; + VolumeSwitchTime = time; + DoVolumeSwitchPress(key); + return key; + } + + // Handle key repetition mechanic + if (VolumeSwitchRepeatTime == 0) + { + double elapsed = time - VolumeSwitchTime; + if (elapsed < VolumeSwitchRepeatStart) + return -1; + + VolumeSwitchRepeatTime = time; + DoVolumeSwitchPress(key); + return key; + } + + double elapsed = time - VolumeSwitchRepeatTime; + if (elapsed < VolumeSwitchRepeatRate) + return -1; + + double rem = fmod(elapsed, VolumeSwitchRepeatRate); + VolumeSwitchRepeatTime = time - rem; + DoVolumeSwitchPress(key); + return key; +} + + +void DoPowerButtonPress() +{ + // Set button pressed IRQ + SetIRQ(IRQ_PowerButtonPressed); + + // There is no default hardware behavior for pressing the power button +} + +void DoPowerButtonReset() +{ + // Reset via IRQ, handled by software + SetIRQ(IRQ_PowerButtonReset); + + // Reset automatically via hardware + if (!GetIRQMode()) + { + // Assumes this isn't called during normal CPU execution + DoHardwareReset(true); + } +} + +void DoPowerButtonShutdown() +{ + // Shutdown via IRQ, handled by software + if (!PowerButtonShutdownFlag) + { + SetIRQ(IRQ_PowerButtonShutdown); + } + + PowerButtonShutdownFlag = true; + + // Shutdown automatically via hardware + if (!GetIRQMode()) + { + DoShutdown(); + } + + // The IRQ is only fired once (hence the need for an if guard), + // but the hardware shutdown is continuously triggered. + // That way when switching the IRQ mode while holding + // down the power button, the DSi will still shut down +} + +void DoPowerButtonForceShutdown() +{ + DoShutdown(); +} + +void DoVolumeSwitchPress(u32 key) +{ + u8 volume = Registers[0x40]; + + switch (key) + { + + case volumeKey_Up: + volume = VolumeUpTable[volume]; + break; + + case volumeKey_Down: + volume = VolumeDownTable[volume]; + break; + + } + + Registers[0x40] = volume; + + SetIRQ(IRQ_VolumeSwitchPressed); +} + +void SetIRQ(u8 irqFlag) +{ + Registers[0x10] |= irqFlag & IRQ_ValidMask; + + if (GetIRQMode()) + { + NDS::SetIRQ2(NDS::IRQ2_DSi_BPTWL); + } } void Start() @@ -107,7 +391,15 @@ void Start() u8 Read(bool last) { //printf("BPTWL: read %02X -> %02X @ %08X\n", CurPos, Registers[CurPos], NDS::GetPC(1)); - u8 ret = Registers[CurPos++]; + u8 ret = Registers[CurPos]; + + // IRQ flags are automatically cleared upon read + if (CurPos == 0x10) + { + Registers[0x10] = 0; + } + + CurPos++; if (last) { @@ -134,19 +426,29 @@ void Write(u8 val, bool last) if (CurPos == 0x11 && val == 0x01) { - Log(LogLevel::Debug, "BPTWL: soft-reset\n"); + // Assumes this is called during normal CPU execution + DoHardwareReset(false); val = 0; // checkme - // TODO: soft-reset might need to be scheduled later! - // TODO: this has been moved for the JIT to work, nothing is confirmed here - NDS::ARM7->Halt(4); CurPos = -1; return; } + // Mask volume level + if (CurPos == 0x40) + { + val &= 0x1F; + } + + // Clamp backlight level + if (CurPos == 0x41) + { + val = val > 4 ? 4 : val; + } + if (CurPos == 0x11 || CurPos == 0x12 || CurPos == 0x21 || CurPos == 0x30 || CurPos == 0x31 || - CurPos == 0x40 || CurPos == 0x31 || + CurPos == 0x40 || CurPos == 0x41 || CurPos == 0x60 || CurPos == 0x63 || (CurPos >= 0x70 && CurPos <= 0x77) || CurPos == 0x80 || CurPos == 0x81) diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h index 48c8e88..8a54a2c 100644 --- a/src/DSi_I2C.h +++ b/src/DSi_I2C.h @@ -42,6 +42,56 @@ enum u8 GetBatteryLevel(); void SetBatteryLevel(u8 batteryLevel); + +// 0-31 +u8 GetVolumeLevel(); +void SetVolumeLevel(u8 volume); + +// 0-4 +u8 GetBacklightLevel(); +void SetBacklightLevel(u8 backlight); + +void DoHardwareReset(bool direct); +void DoShutdown(); + +enum +{ + volumeKey_Up, + volumeKey_Down, +}; + +// Used by hotkeys +void SetPowerButtonHeld(double time); +void SetPowerButtonReleased(double time); +void SetVolumeSwitchHeld(u32 key); +void SetVolumeSwitchReleased(u32 key); +s32 ProcessVolumeSwitchInput(double time); + +void DoPowerButtonPress(); +void DoPowerButtonReset(); +void DoPowerButtonShutdown(); +void DoPowerButtonForceShutdown(); +void DoVolumeSwitchPress(u32 key); + +enum +{ + IRQ_PowerButtonReset = 0x01, // Triggered after releasing the power button quickly + IRQ_PowerButtonShutdown = 0x02, // Triggered after holding the power button for less than a second + IRQ_PowerButtonPressed = 0x08, // Triggered after pressing the power button + IRQ_BatteryEmpty = 0x10, // + IRQ_BatteryLow = 0x20, // Triggered when the battery level reaches 1 + IRQ_VolumeSwitchPressed = 0x40, // Triggered once when the volume sliders are first pressed and repeatedly when held down + /* + Bit 2 (0x04) could be set when holding the power button for more than 5 seconds? (forced power off) + It is unknown whether it is set as the console powers off immediately. + Bit 7 (0x80) is unused? + Both bits are never used by the official ARM7 libraries, but could have some undocumented hardware functionality (?). + */ + IRQ_ValidMask = 0x7B, +}; + +void SetIRQ(u8 irqFlag); + } namespace DSi_I2C diff --git a/src/NDS.h b/src/NDS.h index 824c2bc..dc7809e 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -110,7 +110,7 @@ enum IRQ2_DSi_Unused3, IRQ2_DSi_GPIO33_0, IRQ2_DSi_Headphone, - IRQ2_DSi_PowerButton, + IRQ2_DSi_BPTWL, IRQ2_DSi_GPIO33_3, // "sound enable input" IRQ2_DSi_SDMMC, IRQ2_DSi_SD_Data1, diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index 4beefaf..198c06b 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -22,6 +22,8 @@ #include "types.h" #include "Platform.h" #include "Config.h" +#include "NDS.h" +#include "DSi_I2C.h" #include "AudioSettingsDialog.h" #include "ui_AudioSettingsDialog.h" @@ -32,7 +34,7 @@ AudioSettingsDialog* AudioSettingsDialog::currentDlg = nullptr; extern std::string EmuDirectory; -AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AudioSettingsDialog) +AudioSettingsDialog::AudioSettingsDialog(QWidget* parent, bool emuActive) : QDialog(parent), ui(new Ui::AudioSettingsDialog) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); @@ -40,6 +42,7 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui( oldInterp = Config::AudioInterp; oldBitrate = Config::AudioBitrate; oldVolume = Config::AudioVolume; + oldDSiSync = Config::DSiVolumeSync; ui->cbInterpolation->addItem("None"); ui->cbInterpolation->addItem("Linear"); @@ -52,7 +55,21 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui( ui->cbBitrate->addItem("16-bit"); ui->cbBitrate->setCurrentIndex(Config::AudioBitrate); + bool state = ui->slVolume->blockSignals(true); ui->slVolume->setValue(Config::AudioVolume); + ui->slVolume->blockSignals(state); + + ui->chkSyncDSiVolume->setChecked(Config::DSiVolumeSync); + + // Setup volume slider accordingly + if (emuActive && NDS::ConsoleType == 1) + { + on_chkSyncDSiVolume_clicked(Config::DSiVolumeSync); + } + else + { + ui->chkSyncDSiVolume->setEnabled(false); + } grpMicMode = new QButtonGroup(this); grpMicMode->addButton(ui->rbMicNone, 0); @@ -88,6 +105,22 @@ AudioSettingsDialog::~AudioSettingsDialog() delete ui; } +void AudioSettingsDialog::onSyncVolumeLevel() +{ + if (Config::DSiVolumeSync && NDS::ConsoleType == 1) + { + bool state = ui->slVolume->blockSignals(true); + ui->slVolume->setValue(DSi_BPTWL::GetVolumeLevel()); + ui->slVolume->blockSignals(state); + } +} + +void AudioSettingsDialog::onConsoleReset() +{ + on_chkSyncDSiVolume_clicked(Config::DSiVolumeSync); + ui->chkSyncDSiVolume->setEnabled(NDS::ConsoleType == 1); +} + void AudioSettingsDialog::on_AudioSettingsDialog_accepted() { Config::MicInputType = grpMicMode->checkedId(); @@ -102,6 +135,7 @@ void AudioSettingsDialog::on_AudioSettingsDialog_rejected() Config::AudioInterp = oldInterp; Config::AudioBitrate = oldBitrate; Config::AudioVolume = oldVolume; + Config::DSiVolumeSync = oldDSiSync; closeDlg(); } @@ -128,9 +162,38 @@ void AudioSettingsDialog::on_cbInterpolation_currentIndexChanged(int idx) void AudioSettingsDialog::on_slVolume_valueChanged(int val) { + if (Config::DSiVolumeSync && NDS::ConsoleType == 1) + { + DSi_BPTWL::SetVolumeLevel(val); + return; + } + Config::AudioVolume = val; } +void AudioSettingsDialog::on_chkSyncDSiVolume_clicked(bool checked) +{ + Config::DSiVolumeSync = checked; + + bool state = ui->slVolume->blockSignals(true); + if (Config::DSiVolumeSync && NDS::ConsoleType == 1) + { + ui->slVolume->setMaximum(31); + ui->slVolume->setValue(DSi_BPTWL::GetVolumeLevel()); + ui->slVolume->setPageStep(4); + ui->slVolume->setTickPosition(QSlider::TicksBelow); + } + else + { + Config::AudioVolume = oldVolume; + ui->slVolume->setMaximum(256); + ui->slVolume->setValue(Config::AudioVolume); + ui->slVolume->setPageStep(16); + ui->slVolume->setTickPosition(QSlider::NoTicks); + } + ui->slVolume->blockSignals(state); +} + void AudioSettingsDialog::onChangeMicMode(int mode) { bool iswav = (mode == 3); diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.h b/src/frontend/qt_sdl/AudioSettingsDialog.h index 498c152..14d1ad3 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.h +++ b/src/frontend/qt_sdl/AudioSettingsDialog.h @@ -30,11 +30,11 @@ class AudioSettingsDialog : public QDialog Q_OBJECT public: - explicit AudioSettingsDialog(QWidget* parent); + explicit AudioSettingsDialog(QWidget* parent, bool emuActive); ~AudioSettingsDialog(); static AudioSettingsDialog* currentDlg; - static AudioSettingsDialog* openDlg(QWidget* parent) + static AudioSettingsDialog* openDlg(QWidget* parent, bool emuActive) { if (currentDlg) { @@ -42,7 +42,7 @@ public: return currentDlg; } - currentDlg = new AudioSettingsDialog(parent); + currentDlg = new AudioSettingsDialog(parent, emuActive); currentDlg->show(); return currentDlg; } @@ -51,6 +51,9 @@ public: currentDlg = nullptr; } + void onSyncVolumeLevel(); + void onConsoleReset(); + signals: void updateAudioSettings(); @@ -61,6 +64,7 @@ private slots: void on_cbInterpolation_currentIndexChanged(int idx); void on_cbBitrate_currentIndexChanged(int idx); void on_slVolume_valueChanged(int val); + void on_chkSyncDSiVolume_clicked(bool checked); void onChangeMicMode(int mode); void on_btnMicWavBrowse_clicked(); @@ -70,6 +74,7 @@ private: int oldInterp; int oldBitrate; int oldVolume; + bool oldDSiSync; QButtonGroup* grpMicMode; }; diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.ui b/src/frontend/qt_sdl/AudioSettingsDialog.ui index 8fc38d9..862042f 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.ui +++ b/src/frontend/qt_sdl/AudioSettingsDialog.ui @@ -59,6 +59,16 @@ + + + + <html><head/><body><p>Synchronizes the output volume with the DSi hardware volume.</p></body></html> + + + Sync with DSi volume + + + diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 4e00046..49c41a1 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -118,6 +118,7 @@ bool SavestateRelocSRAM; int AudioInterp; int AudioBitrate; int AudioVolume; +bool DSiVolumeSync; int MicInputType; std::string MicWavPath; @@ -187,6 +188,9 @@ ConfigEntry ConfigFile[] = {"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1, true}, {"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1, true}, {"HKKey_FrameStep", 0, &HKKeyMapping[HK_FrameStep], -1, true}, + {"HKKey_PowerButton", 0, &HKKeyMapping[HK_PowerButton], -1, true}, + {"HKKey_VolumeUp", 0, &HKKeyMapping[HK_VolumeUp], -1, true}, + {"HKKey_VolumeDown", 0, &HKKeyMapping[HK_VolumeDown], -1, true}, {"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, true}, {"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, true}, @@ -200,6 +204,9 @@ ConfigEntry ConfigFile[] = {"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, true}, {"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, true}, {"HKJoy_FrameStep", 0, &HKJoyMapping[HK_FrameStep], -1, true}, + {"HKJoy_PowerButton", 0, &HKJoyMapping[HK_PowerButton], -1, true}, + {"HKJoy_VolumeUp", 0, &HKJoyMapping[HK_VolumeUp], -1, true}, + {"HKJoy_VolumeDown", 0, &HKJoyMapping[HK_VolumeDown], -1, true}, {"JoystickID", 0, &JoystickID, 0, true}, @@ -291,6 +298,7 @@ ConfigEntry ConfigFile[] = {"AudioInterp", 0, &AudioInterp, 0, false}, {"AudioBitrate", 0, &AudioBitrate, 0, false}, {"AudioVolume", 0, &AudioVolume, 256, true}, + {"DSiVolumeSync", 0, &DSiVolumeSync, 0, true}, {"MicInputType", 0, &MicInputType, 1, false}, {"MicWavPath", 2, &MicWavPath, (std::string)"", false}, diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index 42b0a6a..2d4022e 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -36,6 +36,9 @@ enum HK_SolarSensorDecrease, HK_SolarSensorIncrease, HK_FrameStep, + HK_PowerButton, + HK_VolumeUp, + HK_VolumeDown, HK_MAX }; @@ -163,6 +166,7 @@ extern bool SavestateRelocSRAM; extern int AudioInterp; extern int AudioBitrate; extern int AudioVolume; +extern bool DSiVolumeSync; extern int MicInputType; extern std::string MicWavPath; diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h index 76112e8..53ea876 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h @@ -52,7 +52,10 @@ static constexpr std::initializer_list hk_general = HK_Lid, HK_Mic, HK_SwapScreens, - HK_SwapScreenEmphasis + HK_SwapScreenEmphasis, + HK_PowerButton, + HK_VolumeUp, + HK_VolumeDown }; static constexpr std::initializer_list hk_general_labels = @@ -66,7 +69,10 @@ static constexpr std::initializer_list hk_general_labels = "Close/open lid", "Microphone", "Swap screens", - "Swap screen emphasis" + "Swap screen emphasis", + "DSi Power button", + "DSi Volume up", + "DSi Volume down" }; static_assert(hk_general.size() == hk_general_labels.size()); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 803d1f1..7015c6d 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -88,6 +88,7 @@ #include "Platform.h" #include "LocalMP.h" #include "Config.h" +#include "DSi_I2C.h" #include "Savestate.h" @@ -581,6 +582,7 @@ void EmuThread::run() double lastMeasureTime = lastTime; u32 winUpdateCount = 0, winUpdateFreq = 1; + u8 dsiVolumeLevel = 0x1F; char melontitle[100]; @@ -620,6 +622,42 @@ void EmuThread::run() } } + if (NDS::ConsoleType == 1) + { + double currentTime = SDL_GetPerformanceCounter() * perfCountsSec; + + // Handle power button + if (Input::HotkeyDown(HK_PowerButton)) + { + DSi_BPTWL::SetPowerButtonHeld(currentTime); + } + else if (Input::HotkeyReleased(HK_PowerButton)) + { + DSi_BPTWL::SetPowerButtonReleased(currentTime); + } + + // Handle volume buttons + if (Input::HotkeyDown(HK_VolumeUp)) + { + DSi_BPTWL::SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Up); + } + else if (Input::HotkeyReleased(HK_VolumeUp)) + { + DSi_BPTWL::SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Up); + } + + if (Input::HotkeyDown(HK_VolumeDown)) + { + DSi_BPTWL::SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Down); + } + else if (Input::HotkeyReleased(HK_VolumeDown)) + { + DSi_BPTWL::SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Down); + } + + DSi_BPTWL::ProcessVolumeSwitchInput(currentTime); + } + if (EmuRunning == 1 || EmuRunning == 3) { EmuStatus = 1; @@ -739,6 +777,18 @@ void EmuThread::run() oglContext->SetSwapInterval(0); } + if (Config::DSiVolumeSync && NDS::ConsoleType == 1) + { + u8 volumeLevel = DSi_BPTWL::GetVolumeLevel(); + if (volumeLevel != dsiVolumeLevel) + { + dsiVolumeLevel = volumeLevel; + emit syncVolumeLevel(); + } + + Config::AudioVolume = volumeLevel * (256.0 / 31.0); + } + if (Config::AudioSync && !fastforward && audioDevice) { SDL_LockMutex(audioSyncLock); @@ -2583,7 +2633,7 @@ void MainWindow::onBootFirmware() } if (!ROMManager::LoadBIOS()) -{ + { // TODO: better error reporting? QMessageBox::critical(this, "melonDS", "This firmware is not bootable."); emuThread->emuUnpause(); @@ -3027,7 +3077,9 @@ void MainWindow::onCameraSettingsFinished(int res) void MainWindow::onOpenAudioSettings() { - AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this); + AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this, emuThread->emuIsActive()); + connect(emuThread, &EmuThread::syncVolumeLevel, dlg, &AudioSettingsDialog::onSyncVolumeLevel); + connect(emuThread, &EmuThread::windowEmuStart, dlg, &AudioSettingsDialog::onConsoleReset); connect(dlg, &AudioSettingsDialog::updateAudioSettings, this, &MainWindow::onUpdateAudioSettings); connect(dlg, &AudioSettingsDialog::finished, this, &MainWindow::onAudioSettingsFinished); } @@ -3270,7 +3322,8 @@ void MainWindow::onTitleUpdate(QString title) setWindowTitle(title); } -void ToggleFullscreen(MainWindow* mainWindow) { +void ToggleFullscreen(MainWindow* mainWindow) +{ if (!mainWindow->isFullScreen()) { mainWindow->showFullScreen(); @@ -3289,11 +3342,15 @@ void MainWindow::onFullscreenToggled() ToggleFullscreen(this); } -void MainWindow::onScreenEmphasisToggled() { +void MainWindow::onScreenEmphasisToggled() +{ int currentSizing = Config::ScreenSizing; - if (currentSizing == screenSizing_EmphTop) { + if (currentSizing == screenSizing_EmphTop) + { Config::ScreenSizing = screenSizing_EmphBot; - } else if (currentSizing == screenSizing_EmphBot) { + } + else if (currentSizing == screenSizing_EmphBot) + { Config::ScreenSizing = screenSizing_EmphTop; } @@ -3436,7 +3493,7 @@ int main(int argc, char** argv) } SDL_JoystickEventState(SDL_ENABLE); - + SDL_InitSubSystem(SDL_INIT_VIDEO); SDL_EnableScreenSaver(); SDL_DisableScreenSaver(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index f9baea7..8a902ee 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -87,6 +87,8 @@ signals: void swapScreensToggle(); void screenEmphasisToggle(); + void syncVolumeLevel(); + private: void drawScreenGL(); void initOpenGL(); -- cgit v1.2.3