From 2569c67a13a6ce855d27821cc6863ebea82c429d Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Fri, 18 Feb 2022 15:32:46 +0000 Subject: [PATCH] Add support for changing the DS and DSi battery level The DS battery level is configured via the SPI Power Management Device, and the DSi's is configured via the I2C BPTWL. Add support for changing these registers and add the "Power Management" dialog in the UI. --- src/DSi_I2C.cpp | 14 + src/DSi_I2C.h | 17 ++ src/SPI.cpp | 3 + src/SPI.h | 8 + src/frontend/qt_sdl/CMakeLists.txt | 2 + .../PowerManagement/PowerManagementDialog.cpp | 106 +++++++ .../PowerManagement/PowerManagementDialog.h | 69 +++++ .../PowerManagement/PowerManagementDialog.ui | 258 ++++++++++++++++++ .../PowerManagement/resources/battery.qrc | 9 + .../resources/dsi_battery2.svg | 171 ++++++++++++ .../resources/dsi_battery3.svg | 171 ++++++++++++ .../resources/dsi_batteryalmostempty.svg | 171 ++++++++++++ .../resources/dsi_batteryfull.svg | 171 ++++++++++++ .../resources/dsi_batterylow.svg | 171 ++++++++++++ src/frontend/qt_sdl/main.cpp | 17 ++ src/frontend/qt_sdl/main.h | 2 + 16 files changed, 1360 insertions(+) create mode 100644 src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp create mode 100644 src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h create mode 100644 src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.ui create mode 100644 src/frontend/qt_sdl/PowerManagement/resources/battery.qrc create mode 100644 src/frontend/qt_sdl/PowerManagement/resources/dsi_battery2.svg create mode 100644 src/frontend/qt_sdl/PowerManagement/resources/dsi_battery3.svg create mode 100644 src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryalmostempty.svg create mode 100644 src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryfull.svg create mode 100644 src/frontend/qt_sdl/PowerManagement/resources/dsi_batterylow.svg diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index a421430b..5d13b2a6 100644 --- a/src/DSi_I2C.cpp +++ b/src/DSi_I2C.cpp @@ -22,6 +22,7 @@ #include "DSi_I2C.h" #include "DSi_Camera.h" #include "ARM.h" +#include "SPI.h" namespace DSi_BPTWL @@ -82,6 +83,19 @@ void DoSavestate(Savestate* file) u8 GetBootFlag() { return Registers[0x70]; } +bool GetBatteryCharging() { return Registers[0x20] >> 7; } +void SetBatteryCharging(bool charging) +{ + Registers[0x20] = (((charging ? 0x8 : 0x0) << 4) | (Registers[0x20] & 0x0F)); +} + +u8 GetBatteryLevel() { return Registers[0x20] & 0xF; } +void SetBatteryLevel(u8 batteryLevel) +{ + Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F)); + SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false); +} + void Start() { //printf("BPTWL: start\n"); diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h index b6313032..7350fb5a 100644 --- a/src/DSi_I2C.h +++ b/src/DSi_I2C.h @@ -19,11 +19,28 @@ #ifndef DSI_I2C_H #define DSI_I2C_H +#include "types.h" + namespace DSi_BPTWL { u8 GetBootFlag(); +bool GetBatteryCharging(); +void SetBatteryCharging(bool charging); + +enum +{ + batteryLevel_Critical = 0x0, + batteryLevel_AlmostEmpty = 0x1, + batteryLevel_Low = 0x3, + batteryLevel_Half = 0x7, + batteryLevel_ThreeQuarters = 0xB, + batteryLevel_Full = 0xF +}; + +u8 GetBatteryLevel(); +void SetBatteryLevel(u8 batteryLevel); } namespace DSi_I2C diff --git a/src/SPI.cpp b/src/SPI.cpp index 26aab8f9..009fb32f 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -624,6 +624,9 @@ void Reset() RegMasks[4] = 0x0F; } +bool GetBatteryLevelOkay() { return !Registers[1]; } +void SetBatteryLevelOkay(bool okay) { Registers[1] = okay ? 0x00 : 0x01; } + void DoSavestate(Savestate* file) { file->Section("SPPW"); diff --git a/src/SPI.h b/src/SPI.h index 10a8a0dc..8ac39484 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -37,6 +37,14 @@ u8* GetWifiMAC(); } +namespace SPI_Powerman +{ + +bool GetBatteryLevelOkay(); +void SetBatteryLevelOkay(bool okay); + +} + namespace SPI_TSC { diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 8b29baec..ec88026f 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -6,6 +6,8 @@ SET(SOURCES_QT_SDL CheatsDialog.cpp Config.cpp EmuSettingsDialog.cpp + PowerManagement/PowerManagementDialog.cpp + PowerManagement/resources/battery.qrc InputConfig/InputConfigDialog.cpp InputConfig/MapButton.h InputConfig/resources/ds.qrc diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp new file mode 100644 index 00000000..b67736a0 --- /dev/null +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp @@ -0,0 +1,106 @@ +/* + Copyright 2016-2021 Rayyan Ansari + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include "PowerManagementDialog.h" +#include "ui_PowerManagementDialog.h" + +#include "SPI.h" +#include "DSi_I2C.h" +#include "NDS.h" + +#include "types.h" + +#include + +PowerManagementDialog* PowerManagementDialog::currentDlg = nullptr; + +PowerManagementDialog::PowerManagementDialog(QWidget* parent) : QDialog(parent), ui(new Ui::PowerManagementDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + if (NDS::ConsoleType) + ui->grpDSBattery->setEnabled(false); + else + ui->grpDSiBattery->setEnabled(false); + + updateDSBatteryLevelControls(); + + ui->cbDSiBatteryCharging->setChecked(DSi_BPTWL::GetBatteryCharging()); + int dsiBatterySliderPos; + switch (DSi_BPTWL::GetBatteryLevel()) + { + case DSi_BPTWL::batteryLevel_AlmostEmpty: dsiBatterySliderPos = 0; break; + case DSi_BPTWL::batteryLevel_Low: dsiBatterySliderPos = 1; break; + case DSi_BPTWL::batteryLevel_Half: dsiBatterySliderPos = 2; break; + case DSi_BPTWL::batteryLevel_ThreeQuarters: dsiBatterySliderPos = 3; break; + case DSi_BPTWL::batteryLevel_Full: dsiBatterySliderPos = 4; break; + } + ui->sliderDSiBatteryLevel->setValue(dsiBatterySliderPos); +} + +PowerManagementDialog::~PowerManagementDialog() +{ + delete ui; +} + +void PowerManagementDialog::done(int r) +{ + QDialog::done(r); + + closeDlg(); +} + +void PowerManagementDialog::on_rbDSBatteryLow_clicked() +{ + SPI_Powerman::SetBatteryLevelOkay(false); +} + +void PowerManagementDialog::on_rbDSBatteryOkay_clicked() +{ + SPI_Powerman::SetBatteryLevelOkay(true); +} + +void PowerManagementDialog::updateDSBatteryLevelControls() +{ + if (SPI_Powerman::GetBatteryLevelOkay()) + ui->rbDSBatteryOkay->setChecked(true); + else + ui->rbDSBatteryLow->setChecked(true); +} + +void PowerManagementDialog::on_cbDSiBatteryCharging_toggled() +{ + DSi_BPTWL::SetBatteryCharging(ui->cbDSiBatteryCharging->isChecked()); +} + +void PowerManagementDialog::on_sliderDSiBatteryLevel_valueChanged(int value) +{ + u8 newBatteryLevel; + switch (value) + { + case 0: newBatteryLevel = DSi_BPTWL::batteryLevel_AlmostEmpty; break; + case 1: newBatteryLevel = DSi_BPTWL::batteryLevel_Low; break; + case 2: newBatteryLevel = DSi_BPTWL::batteryLevel_Half; break; + case 3: newBatteryLevel = DSi_BPTWL::batteryLevel_ThreeQuarters; break; + case 4: newBatteryLevel = DSi_BPTWL::batteryLevel_Full; break; + } + DSi_BPTWL::SetBatteryLevel(newBatteryLevel); + updateDSBatteryLevelControls(); +} + diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h new file mode 100644 index 00000000..c9f3b4a8 --- /dev/null +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h @@ -0,0 +1,69 @@ +/* + Copyright 2016-2021 Rayyan Ansari + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef POWERMANAGEMENTDIALOG_H +#define POWERMANAGEMENTDIALOG_H + +#include +#include + +namespace Ui { class PowerManagementDialog; } +class PowerManagementDialog; + +class PowerManagementDialog : public QDialog +{ + Q_OBJECT + +public: + explicit PowerManagementDialog(QWidget* parent); + ~PowerManagementDialog(); + + static PowerManagementDialog* currentDlg; + static PowerManagementDialog* openDlg(QWidget* parent) + { + if (currentDlg) + { + currentDlg->activateWindow(); + return currentDlg; + } + + currentDlg = new PowerManagementDialog(parent); + currentDlg->open(); + return currentDlg; + } + static void closeDlg() + { + currentDlg = nullptr; + } + +private slots: + void done(int r); + + void on_rbDSBatteryLow_clicked(); + void on_rbDSBatteryOkay_clicked(); + + void on_cbDSiBatteryCharging_toggled(); + void on_sliderDSiBatteryLevel_valueChanged(int value); + +private: + Ui::PowerManagementDialog* ui; + + void updateDSBatteryLevelControls(); +}; + +#endif // POWERMANAGEMENTDIALOG_H + diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.ui b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.ui new file mode 100644 index 00000000..a2c12160 --- /dev/null +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.ui @@ -0,0 +1,258 @@ + + + PowerManagementDialog + + + + 0 + 0 + 562 + 279 + + + + + 0 + 0 + + + + Power management - melonDS + + + + + + DS Battery + + + + + + Low + + + + + + + Battery Level + + + + + + + Okay + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + DSi Battery + + + + + + Almost Empty + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + Charging + + + + + + + Full + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Battery Level + + + Qt::AlignCenter + + + + + + + + + + :/dsibattery/dsi_batteryalmostempty.svg + + + false + + + + + + + + + + :/dsibattery/dsi_batterylow.svg + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + :/dsibattery/dsi_battery2.svg + + + Qt::AlignCenter + + + + + + + + + + :/dsibattery/dsi_battery3.svg + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + :/dsibattery/dsi_batteryfull.svg + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 4 + + + 1 + + + Qt::Horizontal + + + QSlider::TicksBothSides + + + 1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + buttonBox + accepted() + PowerManagementDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PowerManagementDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/frontend/qt_sdl/PowerManagement/resources/battery.qrc b/src/frontend/qt_sdl/PowerManagement/resources/battery.qrc new file mode 100644 index 00000000..7f9c95b4 --- /dev/null +++ b/src/frontend/qt_sdl/PowerManagement/resources/battery.qrc @@ -0,0 +1,9 @@ + + + dsi_batteryalmostempty.svg + dsi_batterylow.svg + dsi_battery2.svg + dsi_battery3.svg + dsi_batteryfull.svg + + diff --git a/src/frontend/qt_sdl/PowerManagement/resources/dsi_battery2.svg b/src/frontend/qt_sdl/PowerManagement/resources/dsi_battery2.svg new file mode 100644 index 00000000..e9c4b758 --- /dev/null +++ b/src/frontend/qt_sdl/PowerManagement/resources/dsi_battery2.svg @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/qt_sdl/PowerManagement/resources/dsi_battery3.svg b/src/frontend/qt_sdl/PowerManagement/resources/dsi_battery3.svg new file mode 100644 index 00000000..d464ef36 --- /dev/null +++ b/src/frontend/qt_sdl/PowerManagement/resources/dsi_battery3.svg @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryalmostempty.svg b/src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryalmostempty.svg new file mode 100644 index 00000000..4f598fad --- /dev/null +++ b/src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryalmostempty.svg @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryfull.svg b/src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryfull.svg new file mode 100644 index 00000000..dbf84994 --- /dev/null +++ b/src/frontend/qt_sdl/PowerManagement/resources/dsi_batteryfull.svg @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/qt_sdl/PowerManagement/resources/dsi_batterylow.svg b/src/frontend/qt_sdl/PowerManagement/resources/dsi_batterylow.svg new file mode 100644 index 00000000..d337675b --- /dev/null +++ b/src/frontend/qt_sdl/PowerManagement/resources/dsi_batterylow.svg @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 7454ffc5..1296bb9c 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -62,6 +62,7 @@ #include "ROMInfoDialog.h" #include "RAMInfoDialog.h" #include "TitleManagerDialog.h" +#include "PowerManagement/PowerManagementDialog.h" #include "types.h" #include "version.h" @@ -1429,6 +1430,11 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) menu->addSeparator(); + actPowerManagement = menu->addAction("Power management"); + connect(actPowerManagement, &QAction::triggered, this, &MainWindow::onOpenPowerManagement); + + menu->addSeparator(); + actEnableCheats = menu->addAction("Enable cheats"); actEnableCheats->setCheckable(true); connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats); @@ -1669,6 +1675,8 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actStop->setEnabled(false); actFrameStep->setEnabled(false); + actPowerManagement->setEnabled(false); + actSetupCheats->setEnabled(false); actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); @@ -2609,6 +2617,11 @@ void MainWindow::onEmuSettingsDialogFinished(int res) actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); } +void MainWindow::onOpenPowerManagement() +{ + PowerManagementDialog* dlg = PowerManagementDialog::openDlg(this); +} + void MainWindow::onOpenInputConfig() { emuThread->emuPause(); @@ -2896,6 +2909,8 @@ void MainWindow::onEmuStart() actStop->setEnabled(true); actFrameStep->setEnabled(true); + actPowerManagement->setEnabled(true); + actTitleManager->setEnabled(false); } @@ -2915,6 +2930,8 @@ void MainWindow::onEmuStop() actStop->setEnabled(false); actFrameStep->setEnabled(false); + actPowerManagement->setEnabled(false); + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); } diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 6929d69a..6466e3b9 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -257,6 +257,7 @@ private slots: void onOpenEmuSettings(); void onEmuSettingsDialogFinished(int res); + void onOpenPowerManagement(); void onOpenInputConfig(); void onInputConfigFinished(int res); void onOpenVideoSettings(); @@ -346,6 +347,7 @@ public: QAction* actTitleManager; QAction* actEmuSettings; + QAction* actPowerManagement; QAction* actInputConfig; QAction* actVideoSettings; QAction* actAudioSettings;