diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 51d549313df0aacbd68bb85a40f9fb791bbdc1f9..404de99082695f52094060b692cf48060a4953db 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -32,6 +32,7 @@ as release notes for end user on a Recalbox upgrade. - Enhance shader support by setting multi threaded renderer when shader enabled - Add new system 'Z-Machine' with new core frotz - Add new Kodi theme for lower resolutions +- Add Raspberry Pi Touch Display case support for Raspberry Pi 3 and 4 ### Improvements - Improve metadata storage, using far less memory for huge game collections diff --git a/projects/frontend/es-core/src/hardware/Case.cpp b/projects/frontend/es-core/src/hardware/Case.cpp index 728677fe7afb52ff415f6271dcf39df409fbfa25..44b172d50327538a6e2ca978c3ea1215bcfa389a 100644 --- a/projects/frontend/es-core/src/hardware/Case.cpp +++ b/projects/frontend/es-core/src/hardware/Case.cpp @@ -26,6 +26,7 @@ bool Case::Install() const case CaseModel::ArgonOne: case CaseModel::Nespi4CaseManual: case CaseModel::SuperPi4Case: + case CaseModel::RaspberryPiTouchDisplay: { SetCaseInBoot(mShortName); break; @@ -64,6 +65,7 @@ Case Case::FromShortName(const std::string& value) if (value == "SuperPiCase") return Create(CaseModel::SuperPiCase); if (value == "MegaPiCase") return Create(CaseModel::MegaPiCase); if (value == "ArgonOne") return Create(CaseModel::ArgonOne); + if (value == "RaspberryPiTouchDisplay") return Create(CaseModel::RaspberryPiTouchDisplay); return Create(CaseModel::None); } @@ -74,35 +76,37 @@ Case Case::Create(CaseModel model) switch (model) { case CaseModel::None: - return Case(CaseModel::None, false, true, _("NONE"), "", ""); + return Case(CaseModel::None, !CASE_DETECTION_AUTOMATIC, CASE_SHUTDOWN_SUPPORTED, _("NONE"), "", ""); case CaseModel::GPiV1: - return Case(CaseModel::GPiV1, true, true, "Gpi Case (v1)", "GPiV1",""); + return Case(CaseModel::GPiV1, CASE_DETECTION_AUTOMATIC, CASE_SHUTDOWN_SUPPORTED, "Gpi Case (v1)", "GPiV1",""); case CaseModel::GPiV2: - return Case(CaseModel::GPiV2, true, true, "Gpi Case (v2)", "GPiV2", ""); + return Case(CaseModel::GPiV2, CASE_DETECTION_AUTOMATIC, CASE_SHUTDOWN_SUPPORTED, "Gpi Case (v2)", "GPiV2", ""); case CaseModel::GPiV3: - return Case(CaseModel::GPiV3, true, true, "Gpi Case (v3)", "GPiV3", ""); + return Case(CaseModel::GPiV3, CASE_DETECTION_AUTOMATIC, CASE_SHUTDOWN_SUPPORTED, "Gpi Case (v3)", "GPiV3", ""); case CaseModel::GPi2: - return Case(CaseModel::GPi2, true, true, "Gpi Case 2", "GPi2", ""); + return Case(CaseModel::GPi2, CASE_DETECTION_AUTOMATIC, CASE_SHUTDOWN_SUPPORTED, "Gpi Case 2", "GPi2", ""); case CaseModel::Nuxii: - return Case(CaseModel::Nuxii, true, true, "Nuxii", "Nuxii", ""); + return Case(CaseModel::Nuxii, CASE_DETECTION_AUTOMATIC, CASE_SHUTDOWN_SUPPORTED, "Nuxii", "Nuxii", ""); case CaseModel::PiBoy: - return Case(CaseModel::PiBoy, true, true, "PiBoy DMG", "PiBoy", ""); + return Case(CaseModel::PiBoy, CASE_DETECTION_AUTOMATIC, CASE_SHUTDOWN_SUPPORTED, "PiBoy DMG", "PiBoy", ""); case CaseModel::Nespi4Case: - return Case(CaseModel::Nespi4Case, true, false, "Nespi4Case", "NESPi4", ""); + return Case(CaseModel::Nespi4Case, CASE_DETECTION_AUTOMATIC, !CASE_SHUTDOWN_SUPPORTED, "Nespi4Case", "NESPi4", ""); case CaseModel::Nespi4CaseManual: - return Case(CaseModel::Nespi4CaseManual, false, false, "Nespi4Case (Retroflag)", "NESPi4Manual", retroflagInstallMessage); + return Case(CaseModel::Nespi4CaseManual, !CASE_DETECTION_AUTOMATIC, !CASE_SHUTDOWN_SUPPORTED, "Nespi4Case (Retroflag)", "NESPi4Manual", retroflagInstallMessage); case CaseModel::SuperPi4Case: - return Case(CaseModel::SuperPi4Case, false, false, "SuperPi4Case (Retroflag)", "SuperPi4Case", retroflagInstallMessage); + return Case(CaseModel::SuperPi4Case, !CASE_DETECTION_AUTOMATIC, !CASE_SHUTDOWN_SUPPORTED, "SuperPi4Case (Retroflag)", "SuperPi4Case", retroflagInstallMessage); case CaseModel::NespiCasePlus: - return Case(CaseModel::NespiCasePlus, false, false, "Nespi Case + (Retroflag)", "NespiCasePlus", retroflagInstallMessage); + return Case(CaseModel::NespiCasePlus, !CASE_DETECTION_AUTOMATIC, !CASE_SHUTDOWN_SUPPORTED, "Nespi Case + (Retroflag)", "NespiCasePlus", retroflagInstallMessage); case CaseModel::PiStation: - return Case(CaseModel::PiStation, false, false, "PiStation (Retroflag)", "PiStation", retroflagInstallMessage); + return Case(CaseModel::PiStation, !CASE_DETECTION_AUTOMATIC, !CASE_SHUTDOWN_SUPPORTED, "PiStation (Retroflag)", "PiStation", retroflagInstallMessage); case CaseModel::SuperPiCase: - return Case(CaseModel::SuperPiCase, false, false, "Super Pi Case (Retroflag)", "SuperPiCase", retroflagInstallMessage); + return Case(CaseModel::SuperPiCase, !CASE_DETECTION_AUTOMATIC, !CASE_SHUTDOWN_SUPPORTED, "Super Pi Case (Retroflag)", "SuperPiCase", retroflagInstallMessage); case CaseModel::MegaPiCase: - return Case(CaseModel::MegaPiCase, false, false, "Mega Pi Case (Retroflag)", "MegaPiCase", retroflagInstallMessage); + return Case(CaseModel::MegaPiCase, !CASE_DETECTION_AUTOMATIC, !CASE_SHUTDOWN_SUPPORTED, "Mega Pi Case (Retroflag)", "MegaPiCase", retroflagInstallMessage); case CaseModel::ArgonOne: - return Case(CaseModel::ArgonOne, false, true, "Argon One (Argon40)", "ArgonOne", ""); + return Case(CaseModel::ArgonOne, !CASE_DETECTION_AUTOMATIC, CASE_SHUTDOWN_SUPPORTED, "Argon One (Argon40)", "ArgonOne", ""); + case CaseModel::RaspberryPiTouchDisplay: + return Case(CaseModel::RaspberryPiTouchDisplay, !CASE_DETECTION_AUTOMATIC, !CASE_SHUTDOWN_SUPPORTED, "Raspberry Pi Touch Display", "RaspberryPiTouchDisplay", ""); } return Case(CaseModel::None, false, true, _("NONE"), "", ""); } @@ -127,6 +131,7 @@ std::vector Case::SupportedManualCases() list.push_back(Case::Create(Case::CaseModel::ArgonOne)); list.push_back(Case::Create(Case::CaseModel::Nespi4CaseManual)); list.push_back(Case::Create(Case::CaseModel::SuperPi4Case)); + list.push_back(Case::Create(Case::CaseModel::RaspberryPiTouchDisplay)); list.push_back(Case::Create(Case::CaseModel::None)); } if (Board::Instance().GetBoardType() == BoardType::Pi3plus || Board::Instance().GetBoardType() == BoardType::Pi3) @@ -134,6 +139,7 @@ std::vector Case::SupportedManualCases() list.push_back(Case::Create(Case::CaseModel::MegaPiCase)); list.push_back(Case::Create(Case::CaseModel::SuperPiCase)); list.push_back(Case::Create(Case::CaseModel::NespiCasePlus)); + list.push_back(Case::Create(Case::CaseModel::RaspberryPiTouchDisplay)); list.push_back(Case::Create(Case::CaseModel::None)); } return list; diff --git a/projects/frontend/es-core/src/hardware/Case.h b/projects/frontend/es-core/src/hardware/Case.h index 23080faebde53b160b2b14ab81041f7dae76ebf5..0c6dfec7a4b0dd0093602bb0eed2fa2d443f1198 100644 --- a/projects/frontend/es-core/src/hardware/Case.h +++ b/projects/frontend/es-core/src/hardware/Case.h @@ -6,6 +6,9 @@ #include #include +#define CASE_DETECTION_AUTOMATIC true +#define CASE_SHUTDOWN_SUPPORTED true + /*! * Case management * @@ -32,6 +35,7 @@ class Case SuperPiCase, MegaPiCase, ArgonOne, + RaspberryPiTouchDisplay, None, }; @@ -131,4 +135,4 @@ class Case const enum CaseModel mModel; const bool mAutomatic; const bool mShutdownSupported; -}; \ No newline at end of file +}; diff --git a/projects/recalbox-hardware/case/cases.py b/projects/recalbox-hardware/case/cases.py index a687e45e6c981b071d8bd2d0c2141ae45fd285d7..702971329ca12e0ab39d0715a0b52ef0b4498c21 100644 --- a/projects/recalbox-hardware/case/cases.py +++ b/projects/recalbox-hardware/case/cases.py @@ -12,4 +12,5 @@ NESPICASEPLUS = "NespiCasePlus" SUPERPICASE = "SuperPiCase" MEGAPICASE = "MegaPiCase" ARGONONE = "ArgonOne" -NONE = "none" \ No newline at end of file +RPITOUCHDISPLAY = "RaspberryPiTouchDisplay" +NONE = "none" diff --git a/projects/recalbox-hardware/case/installer.py b/projects/recalbox-hardware/case/installer.py index 5c6c0f93b65cf61f9539d60bf125395a9d51a8d7..b48f0f19da6b1ffc81ed4439d0676c8b1f6330a7 100644 --- a/projects/recalbox-hardware/case/installer.py +++ b/projects/recalbox-hardware/case/installer.py @@ -9,6 +9,7 @@ MODULES = { "installers.gpi2.install": (cases.GPI2,), "installers.argonone.install": (cases.ARGONONE,), "installers.retroflags.install": (cases.SUPERPI4CASE, cases.NESPI4, cases.NESPI4MANUAL, cases.PISTATION, cases.NESPICASEPLUS, cases.SUPERPICASE, cases.MEGAPICASE), + "installers.raspberrypi-touch-display.install": (cases.RPITOUCHDISPLAY), } def processHardware(install, case, previousCase): @@ -24,7 +25,7 @@ def processHardware(install, case, previousCase): module = __import__(moduleName, fromlist=["Install"]) installClass = getattr(module, "Install") installer = installClass() - installer.UninstallSoftware() + installer.UninstallSoftware(case) uninstallReboot = installer.UninstallHardware(previousCase) # Call target installer/uninstaller diff --git a/projects/recalbox-hardware/case/installers/raspberrypi-touch-display/__init__.py b/projects/recalbox-hardware/case/installers/raspberrypi-touch-display/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/projects/recalbox-hardware/case/installers/raspberrypi-touch-display/install.py b/projects/recalbox-hardware/case/installers/raspberrypi-touch-display/install.py new file mode 100755 index 0000000000000000000000000000000000000000..dc93ca0a5f6987de9de9286593eb00b60b66ba0c --- /dev/null +++ b/projects/recalbox-hardware/case/installers/raspberrypi-touch-display/install.py @@ -0,0 +1,58 @@ +import os +import logger +from installers.base.install import InstallBase +from filemanipulation import sed + + +class Install(InstallBase): + + def __init__(self): + InstallBase.__init__(self) + + def InstallHardware(self, case): + logger.hardlog("Installing Raspberry Pi Touch Display Case hardware") + try: + os.system("mount -o remount,rw /boot") + if os.system('echo -e "\\ndtoverlay=vc4-kms-dsi-7inch" >> /boot/recalbox-user-config.txt') != 0: + raise "error adding dtoverlay to recalbox-user-config.txt" + sed(' *video=[^ ]+', '', '/boot/cmdline.txt') + sed('noswap', 'noswap video=DSI-1:800x480@60,rotate=180', '/boot/cmdline.txt') + except Exception as e: + logger.hardlog("Raspberry Pi Touch Display: Exception = {}".format(e)) + return False + + finally: + os.system("mount -o remount,ro /boot") + + logger.hardlog("Raspberry Pi Touch Display Case hardware installed successfully!") + return True + + def InstallSoftware(self, case): + logger.hardlog("Installing Raspberry Pi Touch Display Case software") + return case + + def UninstallHardware(self, case): + logger.hardlog("Uninstalling Raspberry Pi Touch Display Case hardware") + try: + os.system("mount -o remount,rw /boot") + # Uninstall /boot/recalbox-user-config.txt + if os.system('sed -i "/dtoverlay=vc4-kms-dsi-7inch/d" /boot/recalbox-user-config.txt') != 0: + raise "Raspberry Pi Touch Display: Error removing kms driver" + logger.hardlog("Raspberry Pi Touch Display: kms driver removed") + sed(' *video=[^ ]+', '', '/boot/cmdline.txt') + except Exception as e: + logger.hardlog("Raspberry Pi Touch Display: Exception = {}".format(e)) + return False + + finally: + os.system("mount -o remount,ro /boot") + + return True + + def UninstallSoftware(self, case): + logger.hardlog("Uninstalling Raspberry Pi Touch Display Case software") + return "" + + def GetInstallScript(self, case): + + return None diff --git a/projects/recalbox-hardware/case/manage.py b/projects/recalbox-hardware/case/manage.py index 3003e4563e5e0968421c0358d8f18cf91e2ccd0e..5268c470b6c4393938ae6c80e525eb6238d7880e 100644 --- a/projects/recalbox-hardware/case/manage.py +++ b/projects/recalbox-hardware/case/manage.py @@ -121,7 +121,7 @@ def DetectPiBoyCase(): # --------- Main -manualCases = (cases.SUPERPI4CASE, cases.NESPI4MANUAL, cases.PISTATION, cases.ARGONONE, cases.NESPICASEPLUS, cases.SUPERPICASE, cases.MEGAPICASE) +manualCases = (cases.SUPERPI4CASE, cases.NESPI4MANUAL, cases.PISTATION, cases.ARGONONE, cases.NESPICASEPLUS, cases.SUPERPICASE, cases.MEGAPICASE, cases.RPITOUCHDISPLAY) # Main identification routine def Identify(previousCase): case = cases.NONE @@ -145,7 +145,7 @@ def Identify(previousCase): if board in ("rpi3", "rpi4", "rpi4_64") and case == cases.NONE: case = DetectPiBoyCase() - + return case