From 4bbbd902be14b52d1f45c10881ed9d360f47bc72 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 1 Jul 2021 17:53:05 +0200 Subject: [PATCH 01/76] Single-byte change. --- es-app/src/Gamelist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-app/src/Gamelist.cpp b/es-app/src/Gamelist.cpp index 18329c31c..478d025cf 100644 --- a/es-app/src/Gamelist.cpp +++ b/es-app/src/Gamelist.cpp @@ -284,7 +284,7 @@ void updateGamelist(SystemData* system) continue; } - std::string nodePath =Utils::FileSystem::getCanonicalPath( + std::string nodePath = Utils::FileSystem::getCanonicalPath( Utils::FileSystem::resolveRelativePath(pathNode.text().get(), system->getStartPath(), true)); std::string gamePath = Utils::FileSystem::getCanonicalPath((*fit)->getPath()); From 369c9cdd0fa03b6b2f687235efeed64e0ac34f4f Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 1 Jul 2021 17:55:00 +0200 Subject: [PATCH 02/76] Added support for using the ROMPATH variable in the staticpath find rule. --- es-app/src/FileData.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/es-app/src/FileData.cpp b/es-app/src/FileData.cpp index 7b868ef28..22fcdfee6 100644 --- a/es-app/src/FileData.cpp +++ b/es-app/src/FileData.cpp @@ -1212,6 +1212,8 @@ std::string FileData::findEmulatorPath(std::string& command) path = Utils::FileSystem::expandHomePath(path); // If %ESPATH% is used for the rule, then expand it to the binary directory of ES-DE. path = Utils::String::replace(path, "%ESPATH%", Utils::FileSystem::getExePath()); + // Likewise for the %ROMPATH% variable which expands to the configured ROM directory. + path = Utils::String::replace(path, "%ROMPATH%", getROMDirectory()); #if defined(_WIN64) path = Utils::String::replace(path, "/", "\\"); #endif From 3a986b447dda9b696f85667bb5513d463d9205c7 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 1 Jul 2021 17:58:09 +0200 Subject: [PATCH 03/76] Game files that are actually recursive symlinks are now skipped during startup. --- es-app/src/SystemData.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/es-app/src/SystemData.cpp b/es-app/src/SystemData.cpp index bb415815b..fee01b1f9 100644 --- a/es-app/src/SystemData.cpp +++ b/es-app/src/SystemData.cpp @@ -278,6 +278,15 @@ bool SystemData::populateFolder(FileData* folder) it != dirContent.cend(); it++) { filePath = *it; + // Skip any recursive symlinks as those would hang the application at various places. + if (Utils::FileSystem::isSymlink(filePath)) { + if (Utils::FileSystem::resolveSymlink(filePath) == + Utils::FileSystem::getFileName(filePath)) { + LOG(LogWarning) << "Skipped \"" << filePath << "\" as it's a recursive symlink"; + continue; + } + } + // Skip hidden files and folders. if (!showHiddenFiles && Utils::FileSystem::isHidden(filePath)) { LOG(LogDebug) << "SystemData::populateFolder(): Skipping hidden " << From e4c0d493df93f15a462c2a21a6e494e25c4ddcc4 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 1 Jul 2021 18:00:05 +0200 Subject: [PATCH 04/76] Small documentation update. --- CHANGELOG.md | 1 + INSTALL-DEV.md | 2 +- USERGUIDE-DEV.md | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af8791f61..5cf54d972 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Apart from this, many small improvements and bug fixes are part of the release, * Default controller configuration is now automatically applied, input configuration should rarely if ever be required any longer except for deliberate button customization * Added support for selecting the controller type (Xbox, Xbox 360, PS4, PS5 and SNES), which changes the help icons, help text and the input configuration tool icons and text * Added an option to limit the input in ES-DE to only the first controller (does not affect the emulators) +* Added separate controller deadzone values for the triggers and thumbsticks * Removed the startup notification regarding default keyboard mappings being in use, instead default mappings are now considered the recommended input configuration * The controller input configuration is not automatically started any longer if there is no es_input.cfg file or if there are no applicable configuration entries in the file * Increased the max allowed size for images when scraping, which should now only downscale files which really need it diff --git a/INSTALL-DEV.md b/INSTALL-DEV.md index 24f848a02..2bb1830e6 100644 --- a/INSTALL-DEV.md +++ b/INSTALL-DEV.md @@ -1542,7 +1542,7 @@ As for `corepath` this rule is simply a path to search for the emulator core. Each rule supports multiple entry tags which are tried in the order that they are defined in the file. -The %ESPATH% variable can be used inside the staticpath rules and both the %EMUPATH% and %ESPATH% variables can be used inside the corepath rules. +The %ESPATH% and %ROMPATH% variables can be used inside the staticpath rules and the %ESPATH% and %EMUPATH% variables can be used inside the corepath rules. The tilde symbol `~` is supported for the staticpath and corepath rules and will expand to the user home directory. Be aware that if ES-DE has been started with the --home command line option, the home directory is considered to be whatever path was passed as an argument to that option. The same is true if using a portable.txt file. diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index 072271da2..782f77277 100644 --- a/USERGUIDE-DEV.md +++ b/USERGUIDE-DEV.md @@ -309,6 +309,8 @@ It's required that the ROM files are in one of the supported file extensions, or It's highly recommended to use filenames that are corresponding to the full name of the game, otherwise you will need to manually feed the scraper the game name when scraping which is very tedious. +**Note:** Symlinks are supported for both ROM directories and individual game files, but make sure to not symlink between files within the same system or there may be undefined application behavior when scraping, launching games etc. + The default game directory folder is ~/ROMs. On Unix this defaults to /home/\/ROMs, on macOS /Users/\/ROMs and on Windows C:\Users\\\ROMs\. Be aware that if the --home command line option was used to start ES-DE, the tilde `~` symbol will resolve to whatever directory was passed as an argument to this option. If ES-DE can't find any game files during startup, an error message will be displayed with the option to change the ROM directory path. From 4a3b24e770cabe07d7185e8bee6bf255d174acd2 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 2 Jul 2021 17:31:35 +0200 Subject: [PATCH 05/76] Removed a ComponentGrid assert that prevented portrait orientation to work at all. --- es-core/src/components/ComponentGrid.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/es-core/src/components/ComponentGrid.cpp b/es-core/src/components/ComponentGrid.cpp index bdaaf3b31..183975e73 100644 --- a/es-core/src/components/ComponentGrid.cpp +++ b/es-core/src/components/ComponentGrid.cpp @@ -73,7 +73,6 @@ float ComponentGrid::getRowHeight(int row) void ComponentGrid::setColWidthPerc(int col, float width, bool update) { - assert(width >= 0 && width <= 1); assert(col >= 0 && col < mGridSize.x()); mColWidths[col] = width; From 3dea4bc8ec46fe65714db8d1b33d72857791031a Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 2 Jul 2021 17:32:55 +0200 Subject: [PATCH 06/76] Minimal code cleanup of HelpComponent. --- es-core/src/components/HelpComponent.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/es-core/src/components/HelpComponent.cpp b/es-core/src/components/HelpComponent.cpp index df4381a9c..b784e7326 100644 --- a/es-core/src/components/HelpComponent.cpp +++ b/es-core/src/components/HelpComponent.cpp @@ -16,9 +16,6 @@ #include "Log.h" #include "Settings.h" -#define OFFSET_X 12 // Move the entire thing right by this amount (px). -#define OFFSET_Y 12 // Move the entire thing up by this amount (px). - #define ICON_TEXT_SPACING 8 // Space between [icon] and [text] (px). #define ENTRY_SPACING 16 // Space between [text] and next [icon] (px). @@ -144,7 +141,7 @@ void HelpComponent::updateGrid() mGrid->setSize(width, height); for (unsigned int i = 0; i < icons.size(); i++) { - const int col = i*4; + const int col = i * 4; mGrid->setColWidthPerc(col, icons.at(i)->getSize().x() / width); mGrid->setColWidthPerc(col + 1, (ICON_TEXT_SPACING * Renderer::getScreenWidthModifier()) / width); @@ -155,7 +152,6 @@ void HelpComponent::updateGrid() } mGrid->setPosition(Vector3f(mStyle.position.x(), mStyle.position.y(), 0.0f)); - //mGrid->setPosition(OFFSET_X, Renderer::getScreenHeight() - mGrid->getSize().y() - OFFSET_Y); mGrid->setOrigin(mStyle.origin); } From e957e96e207eb1a6aae275f8ce8dd2497ab4b88d Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 2 Jul 2021 17:36:05 +0200 Subject: [PATCH 07/76] Window corner sizes are now calculated based on the screen height instead of the screen width. --- es-core/src/components/NinePatchComponent.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/es-core/src/components/NinePatchComponent.cpp b/es-core/src/components/NinePatchComponent.cpp index 8f2f39576..f8df08ef3 100644 --- a/es-core/src/components/NinePatchComponent.cpp +++ b/es-core/src/components/NinePatchComponent.cpp @@ -54,7 +54,12 @@ void NinePatchComponent::buildVertices() // Scale the corner size relative to the screen resolution, but keep the scale factor // within reason as extreme resolutions may cause artifacts. Any "normal" resolution // (e.g. from 720p to 4K) will be within these boundaries though. - float scaleFactor = Math::clamp(Renderer::getScreenWidthModifier(), 0.4f, 3.0f); + float scaleFactor; + if (Renderer::getScreenWidth() > Renderer::getScreenHeight()) + scaleFactor = Math::clamp(Renderer::getScreenHeightModifier(), 0.4f, 3.0f); + else + scaleFactor = Math::clamp(Renderer::getScreenWidthModifier(), 0.4f, 3.0f); + mTexture = TextureResource::get(mPath, false, false, true, scaleFactor); if (mTexture->getSize() == Vector2i::Zero()) { From 6e28300a5ad7d2b49db4944a6dc87f92b1686abe Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 2 Jul 2021 17:44:27 +0200 Subject: [PATCH 08/76] The ComponentList padding is now scaled properly to the screen aspect ratio and resolution. --- es-core/src/components/ComponentList.cpp | 20 +++++++++++++------- es-core/src/components/ComponentList.h | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/es-core/src/components/ComponentList.cpp b/es-core/src/components/ComponentList.cpp index 54fec3c40..6edff9740 100644 --- a/es-core/src/components/ComponentList.cpp +++ b/es-core/src/components/ComponentList.cpp @@ -13,8 +13,14 @@ ComponentList::ComponentList(Window* window) : IList(window, LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP) { - mSelectorBarOffset = 0; - mCameraOffset = 0; + // Adjust the padding relative to the aspect ratio and screen resolution to make it look + // coherent regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference. + float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); + mHorizontalPadding = TOTAL_HORIZONTAL_PADDING_PX * aspectValue * + Renderer::getScreenWidthModifier(); + + mSelectorBarOffset = 0.0f; + mCameraOffset = 0.0f; mFocused = false; } @@ -179,14 +185,14 @@ void ComponentList::render(const Transform4x4f& parentTrans) Transform4x4f trans = parentTrans * getTransform(); // Clip everything to be inside our bounds. - Vector3f dim(mSize.x(), mSize.y(), 0); + Vector3f dim(mSize.x(), mSize.y(), 0.0f); dim = trans * dim - trans.translation(); Renderer::pushClipRect(Vector2i(static_cast(std::round(trans.translation().x())), static_cast(std::round(trans.translation().y()))), Vector2i(static_cast( std::round(dim.x())), static_cast(std::round(dim.y())))); // Scroll the camera. - trans.translate(Vector3f(0, -std::round(mCameraOffset), 0)); + trans.translate(Vector3f(0.0f, -std::round(mCameraOffset), 0.0f)); // Draw our entries. std::vector drawAfterCursor; @@ -313,20 +319,20 @@ void ComponentList::updateElementPosition(const ComponentListRow& row) // Assumes updateElementSize has already been called. float rowHeight = getRowHeight(row); + float x = mHorizontalPadding / 2.0f; - float x = (TOTAL_HORIZONTAL_PADDING_PX * Renderer::getScreenWidthModifier()) / 2; for (unsigned int i = 0; i < row.elements.size(); i++) { const auto comp = row.elements.at(i).component; // Center vertically. - comp->setPosition(x, (rowHeight - comp->getSize().y()) / 2 + yOffset); + comp->setPosition(x, (rowHeight - comp->getSize().y()) / 2.0f + yOffset); x += comp->getSize().x(); } } void ComponentList::updateElementSize(const ComponentListRow& row) { - float width = mSize.x() - (TOTAL_HORIZONTAL_PADDING_PX * Renderer::getScreenWidthModifier()); + float width = mSize.x() - mHorizontalPadding; std::vector> resizeVec; for (auto it = row.elements.cbegin(); it != row.elements.cend(); it++) { diff --git a/es-core/src/components/ComponentList.h b/es-core/src/components/ComponentList.h index 7cf17458a..e4b6e4b31 100644 --- a/es-core/src/components/ComponentList.h +++ b/es-core/src/components/ComponentList.h @@ -95,6 +95,7 @@ private: float getRowHeight(const ComponentListRow& row) const; + float mHorizontalPadding; float mSelectorBarOffset; float mCameraOffset; From 6627899f8827a67ad9283f35842cc90a5698e3aa Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 2 Jul 2021 17:51:07 +0200 Subject: [PATCH 09/76] Made the scraper GUIs narrower on ultrawide monitors. --- es-app/src/guis/GuiGameScraper.cpp | 7 ++++++- es-app/src/guis/GuiScraperMulti.cpp | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/es-app/src/guis/GuiGameScraper.cpp b/es-app/src/guis/GuiGameScraper.cpp index 1556daa95..91fb0b454 100644 --- a/es-app/src/guis/GuiGameScraper.cpp +++ b/es-app/src/guis/GuiGameScraper.cpp @@ -118,7 +118,12 @@ GuiGameScraper::GuiGameScraper( doneFunc(result); close(); }); mSearch->setCancelCallback([&] { delete this; }); - setSize(Renderer::getScreenWidth() * 0.95f, Renderer::getScreenHeight() * 0.747f); + // Limit the width of the GUI on ultrawide monitors. The 1.778 aspect ratio value is + // the 16:9 reference. + float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); + float width = Math::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth(); + + setSize(width, Renderer::getScreenHeight() * 0.747f); setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2); diff --git a/es-app/src/guis/GuiScraperMulti.cpp b/es-app/src/guis/GuiScraperMulti.cpp index f0196465f..20a459762 100644 --- a/es-app/src/guis/GuiScraperMulti.cpp +++ b/es-app/src/guis/GuiScraperMulti.cpp @@ -95,7 +95,12 @@ GuiScraperMulti::GuiScraperMulti( mButtonGrid = makeButtonGrid(mWindow, buttons); mGrid.setEntry(mButtonGrid, Vector2i(0, 4), true, false); - setSize(Renderer::getScreenWidth() * 0.95f, Renderer::getScreenHeight() * 0.849f); + // Limit the width of the GUI on ultrawide monitors. The 1.778 aspect ratio value is + // the 16:9 reference. + float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); + float width = Math::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth(); + + setSize(width, Renderer::getScreenHeight() * 0.849f); setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2); From 6178830504621116cafd5fa74c23dcc7e44e9078 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 2 Jul 2021 17:57:52 +0200 Subject: [PATCH 10/76] Improved scaling relative to the screen aspect ratio for various GUI components. --- es-core/src/guis/GuiComplexTextEditPopup.cpp | 14 ++++++++---- es-core/src/guis/GuiDetectDevice.cpp | 8 +++---- es-core/src/guis/GuiInputConfig.cpp | 8 +++---- es-core/src/guis/GuiMsgBox.cpp | 23 ++++++++++++-------- es-core/src/guis/GuiTextEditPopup.cpp | 8 +++---- 5 files changed, 36 insertions(+), 25 deletions(-) diff --git a/es-core/src/guis/GuiComplexTextEditPopup.cpp b/es-core/src/guis/GuiComplexTextEditPopup.cpp index 0d234c0ed..20d77fba7 100644 --- a/es-core/src/guis/GuiComplexTextEditPopup.cpp +++ b/es-core/src/guis/GuiComplexTextEditPopup.cpp @@ -88,11 +88,17 @@ GuiComplexTextEditPopup::GuiComplexTextEditPopup( if (multiLine) textHeight *= 6; - mText->setSize(0, textHeight); - mInfoString2->setSize(Renderer::getScreenWidth() * 0.70f, mInfoString2->getFont()->getHeight()); + // Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent + // regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference. + float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); + float infoWidth = Math::clamp(0.70f * aspectValue, 0.60f, 0.85f) * Renderer::getScreenWidth(); + float windowWidth = Math::clamp(0.75f * aspectValue, 0.65f, 0.90f) * Renderer::getScreenWidth(); - setSize(Renderer::getScreenWidth() * 0.75f, mTitle->getFont()->getHeight() + - textHeight + mButtonGrid->getSize().y() + mButtonGrid->getSize().y() * 1.85f); + mText->setSize(0, textHeight); + mInfoString2->setSize(infoWidth, mInfoString2->getFont()->getHeight()); + + setSize(windowWidth, mTitle->getFont()->getHeight() + textHeight + + mButtonGrid->getSize().y() + mButtonGrid->getSize().y() * 1.85f); setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2); mText->startEditing(); diff --git a/es-core/src/guis/GuiDetectDevice.cpp b/es-core/src/guis/GuiDetectDevice.cpp index f9135f672..f5973c510 100644 --- a/es-core/src/guis/GuiDetectDevice.cpp +++ b/es-core/src/guis/GuiDetectDevice.cpp @@ -77,10 +77,10 @@ GuiDetectDevice::GuiDetectDevice( Font::get(FONT_SIZE_MEDIUM), 0xFFFFFFFF, ALIGN_CENTER); mGrid.setEntry(mDeviceHeld, Vector2i(0, 4), false, true); - // For narrower displays (e.g. in 4:3 ratio), allow the window to fill 80% of the screen - // width rather than the 60% allowed for wider displays. - float width = Renderer::getScreenWidth() * - ((Renderer::getScreenAspectRatio() < 1.4f) ? 0.8f : 0.6f); + // Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent + // regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference. + float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); + float width = Math::clamp(0.60f * aspectValue, 0.50f, 0.80f) * Renderer::getScreenWidth(); setSize(width, Renderer::getScreenHeight() * 0.5f); setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, diff --git a/es-core/src/guis/GuiInputConfig.cpp b/es-core/src/guis/GuiInputConfig.cpp index efab129ff..5604c5f5e 100644 --- a/es-core/src/guis/GuiInputConfig.cpp +++ b/es-core/src/guis/GuiInputConfig.cpp @@ -214,10 +214,10 @@ GuiInputConfig::GuiInputConfig( mButtonGrid = makeButtonGrid(mWindow, buttons); mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false); - // For narrower displays (e.g. in 4:3 ratio), allow the window to fill 80% of the screen - // width rather than the 60% allowed for wider displays. - float width = Renderer::getScreenWidth() * - ((Renderer::getScreenAspectRatio() < 1.4f) ? 0.8f : 0.6f); + // Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent + // regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference. + float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); + float width = Math::clamp(0.60f * aspectValue, 0.50f, 0.80f) * Renderer::getScreenWidth(); setSize(width, Renderer::getScreenHeight() * 0.75f); setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, diff --git a/es-core/src/guis/GuiMsgBox.cpp b/es-core/src/guis/GuiMsgBox.cpp index d7d98eae1..16b87ef5b 100644 --- a/es-core/src/guis/GuiMsgBox.cpp +++ b/es-core/src/guis/GuiMsgBox.cpp @@ -27,11 +27,14 @@ GuiMsgBox::GuiMsgBox(Window* window, const HelpStyle& helpstyle, const std::stri mDisableBackButton(disableBackButton), mDeleteOnButtonPress(deleteOnButtonPress) { - // For narrower displays (e.g. in 4:3 ratio), allow the window to fill 80% of the screen - // width rather than the 60% allowed for wider displays. - float width = Renderer::getScreenWidth() * - ((Renderer::getScreenAspectRatio() < 1.4f) ? 0.8f : 0.6f); - float minWidth = Renderer::getScreenWidth() * 0.3f; + // Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent + // regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference. + float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); + + float width = floorf(Math::clamp(0.60f * aspectValue, 0.60f, 0.80f) * + Renderer::getScreenWidth()); + float minWidth = floorf(Math::clamp(0.30f * aspectValue, 0.10f, 0.50f) * + Renderer::getScreenWidth()); mMsg = std::make_shared(mWindow, text, Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); @@ -95,11 +98,13 @@ void GuiMsgBox::changeText(const std::string& newText) mMsg->setText(newText); mMsg->setSize(mMsg->getFont()->sizeText(newText)); - // For narrower displays (e.g. in 4:3 ratio), allow the window to fill 80% of the screen - // width rather than the 60% allowed for wider displays. - float width = Renderer::getScreenWidth() * - ((Renderer::getScreenAspectRatio() < 1.4f) ? 0.8f : 0.6f); + // Adjust the width depending on the aspect ratio of the screen, to make the screen look + // somewhat coherent regardless of screen type. The 1.778 aspect ratio value is the 16:9 + // reference. + float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); + float width = floorf(Math::clamp(0.60f * aspectValue, 0.60f, 0.80f) * + Renderer::getScreenWidth()); float minWidth = Renderer::getScreenWidth() * 0.3f; // Decide final width. diff --git a/es-core/src/guis/GuiTextEditPopup.cpp b/es-core/src/guis/GuiTextEditPopup.cpp index 71aee9be9..1bdb06961 100644 --- a/es-core/src/guis/GuiTextEditPopup.cpp +++ b/es-core/src/guis/GuiTextEditPopup.cpp @@ -62,10 +62,10 @@ GuiTextEditPopup::GuiTextEditPopup( textHeight *= 6; mText->setSize(0, textHeight); - // For narrower displays (e.g. in 4:3 ratio), allow the window to fill 70% of the screen - // width rather than the 50% allowed for wider displays. - float width = Renderer::getScreenWidth() * - ((Renderer::getScreenAspectRatio() < 1.4f) ? 0.7f : 0.5f); + // Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent + // regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference. + float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); + float width = Math::clamp(0.50f * aspectValue, 0.40f, 0.70f) * Renderer::getScreenWidth(); setSize(width, mTitle->getFont()->getHeight() + textHeight + mButtonGrid->getSize().y() + mButtonGrid->getSize().y() / 2); From 064e8f81d0da60249f933a4a295f592b0f182a5f Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 2 Jul 2021 18:02:39 +0200 Subject: [PATCH 11/76] Added a missing help prompt to the single-game scraper. --- es-app/src/guis/GuiGameScraper.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/es-app/src/guis/GuiGameScraper.cpp b/es-app/src/guis/GuiGameScraper.cpp index 91fb0b454..815dba315 100644 --- a/es-app/src/guis/GuiGameScraper.cpp +++ b/es-app/src/guis/GuiGameScraper.cpp @@ -175,7 +175,10 @@ void GuiGameScraper::update(int deltaTime) std::vector GuiGameScraper::getHelpPrompts() { - return mGrid.getHelpPrompts(); + std::vector prompts = mGrid.getHelpPrompts(); + prompts.push_back(HelpPrompt("b", "back (cancel)")); + + return prompts; } HelpStyle GuiGameScraper::getHelpStyle() From a277b9693bec8868c7ae993abdfb761d4c9d01b2 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 2 Jul 2021 18:34:04 +0200 Subject: [PATCH 12/76] Fixed an issue where missing theme font files would crash the application. --- es-core/src/resources/Font.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/es-core/src/resources/Font.cpp b/es-core/src/resources/Font.cpp index 6b1ed1a73..196f9ce23 100644 --- a/es-core/src/resources/Font.cpp +++ b/es-core/src/resources/Font.cpp @@ -724,5 +724,11 @@ std::shared_ptr Font::getFromTheme(const ThemeData::ThemeElement* elem, if (properties & FONT_PATH && elem->has("fontPath")) path = elem->get("fontPath"); + if (!Utils::FileSystem::exists(path)) { + LOG(LogError) << "Font file \"" << path << "\" defined by the theme does not exist, " + "falling back to \"" << getDefaultPath() << "\""; + path = getDefaultPath(); + } + return get(size, path); } From b59f2da3e37b6706ff100cbbbe806620bbda2013 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 2 Jul 2021 18:51:01 +0200 Subject: [PATCH 13/76] Now skipping missing font file checks for bundled fonts. --- es-core/src/resources/Font.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-core/src/resources/Font.cpp b/es-core/src/resources/Font.cpp index 196f9ce23..5f3f6f94b 100644 --- a/es-core/src/resources/Font.cpp +++ b/es-core/src/resources/Font.cpp @@ -724,7 +724,7 @@ std::shared_ptr Font::getFromTheme(const ThemeData::ThemeElement* elem, if (properties & FONT_PATH && elem->has("fontPath")) path = elem->get("fontPath"); - if (!Utils::FileSystem::exists(path)) { + if (!((path[0] == ':') && (path[1] == '/')) && !Utils::FileSystem::exists(path)) { LOG(LogError) << "Font file \"" << path << "\" defined by the theme does not exist, " "falling back to \"" << getDefaultPath() << "\""; path = getDefaultPath(); From 506a452d1be5f21f6b8179fe3c7cd70db44e39ba Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 2 Jul 2021 18:56:52 +0200 Subject: [PATCH 14/76] (Windows) Fixed an MSVC Unicode issue. --- es-app/src/views/UIModeController.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/es-app/src/views/UIModeController.cpp b/es-app/src/views/UIModeController.cpp index aebc63f48..1736769c9 100644 --- a/es-app/src/views/UIModeController.cpp +++ b/es-app/src/views/UIModeController.cpp @@ -141,10 +141,17 @@ std::string UIModeController::getFormattedPassKeyStr() else if (controllerType == "ps4" || controllerType == "ps5") { // These symbols are far from perfect but you can at least understand what // they are supposed to depict. + #if defined(_MSC_VER) // MSVC compiler. + symbolA = Utils::String::wideStringToString(L"\uF00D"); // Cross. + symbolB = Utils::String::wideStringToString(L"\uF111"); // Circle + symbolX = Utils::String::wideStringToString(L"\uF04D"); // Square. + symbolY = Utils::String::wideStringToString(L"\uF0D8"); // Triangle. + #else symbolA = "\uF00D"; // Cross. symbolB = "\uF111"; // Circle symbolX = "\uF04D"; // Square. symbolY = "\uF0D8"; // Triangle. + #endif } else { // Xbox controller. From 651b7a4d022a48d39d9f8865dfd3f42e17491cc5 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 2 Jul 2021 20:33:50 +0200 Subject: [PATCH 15/76] Fixed an issue with an endless loop when attempting to load a corrupt image file. --- es-core/src/components/ImageComponent.cpp | 17 +++++++++++++++-- es-core/src/resources/TextureData.cpp | 2 +- es-core/src/resources/TextureData.h | 1 + es-core/src/resources/TextureResource.cpp | 10 ++++++++++ es-core/src/resources/TextureResource.h | 2 ++ 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index 90eda6801..b53059b27 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -426,7 +426,8 @@ void ImageComponent::render(const Transform4x4f& parentTrans) mTargetSize.y(), 0xFF000033, 0xFF000033); Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0xFF000033, 0xFF000033); } - if (mTexture->isInitialized()) { + // An image with zero size would normally indicate a corrupt image file. + if (mTexture->isInitialized() && mTexture->getSize() != 0) { // Actually draw the image. // The bind() function returns false if the texture is not currently loaded. A blank // texture is bound in this case but we want to handle a fade so it doesn't just @@ -441,7 +442,19 @@ void ImageComponent::render(const Transform4x4f& parentTrans) Renderer::drawTriangleStrips(&mVertices[0], 4, trans); } else { - LOG(LogError) << "Image texture is not initialized!"; + if (!mTexture) { + LOG(LogError) << "Image texture is not initialized"; + } + else { + std::string textureFilePath = mTexture->getTextureFilePath(); + if (textureFilePath != "") { + LOG(LogError) << "Image texture for file \"" << textureFilePath << + "\" has zero size"; + } + else { + LOG(LogError) << "Image texture has zero size"; + } + } mTexture.reset(); } } diff --git a/es-core/src/resources/TextureData.cpp b/es-core/src/resources/TextureData.cpp index 1f8b020cd..c43298804 100644 --- a/es-core/src/resources/TextureData.cpp +++ b/es-core/src/resources/TextureData.cpp @@ -124,7 +124,7 @@ bool TextureData::initImageFromMemory(const unsigned char* fileData, size_t leng if (imageRGBA.size() == 0) { LOG(LogError) << "Couldn't initialize texture from memory, invalid data (" << - (mPath != "" ? "file path: " + mPath + ", " : "") << "data ptr: " << + (mPath != "" ? "file path: \"" + mPath + "\", " : "") << "data ptr: " << reinterpret_cast(fileData) << ", reported size: " << length << ")"; return false; } diff --git a/es-core/src/resources/TextureData.h b/es-core/src/resources/TextureData.h index ca7e52973..8a65c0b18 100644 --- a/es-core/src/resources/TextureData.h +++ b/es-core/src/resources/TextureData.h @@ -58,6 +58,7 @@ public: void setScaleDuringLoad(float scale) { mScaleDuringLoad = scale; } std::vector getRawRGBAData() { return mDataRGBA; } + std::string getTextureFilePath() { return mPath; } bool tiled() { return mTile; } private: diff --git a/es-core/src/resources/TextureResource.cpp b/es-core/src/resources/TextureResource.cpp index 887d6d408..5aef84b3f 100644 --- a/es-core/src/resources/TextureResource.cpp +++ b/es-core/src/resources/TextureResource.cpp @@ -115,6 +115,16 @@ std::vector TextureResource::getRawRGBAData() return std::vector(0); } +std::string TextureResource::getTextureFilePath() +{ + std::shared_ptr data = sTextureDataManager.get(this); + + if (data) + return data->getTextureFilePath(); + else + return ""; +} + const Vector2i TextureResource::getSize() const { return mSize; diff --git a/es-core/src/resources/TextureResource.h b/es-core/src/resources/TextureResource.h index 4db29adde..380be1787 100644 --- a/es-core/src/resources/TextureResource.h +++ b/es-core/src/resources/TextureResource.h @@ -39,6 +39,8 @@ public: // Returns the raw pixel values. std::vector getRawRGBAData(); + std::string getTextureFilePath(); + // For SVG graphics this function effectively rescales the image to the defined size. // It does unload and re-rasterize the texture though which may cause flickering in some // situations. An alternative is to set a scaling factor directly when loading the texture From 386001d09c62760576198e0b5f54e52d55dc4e44 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 2 Jul 2021 20:35:52 +0200 Subject: [PATCH 16/76] Fixed an issue where MediaViewer tried to render corrupt images. --- es-app/src/MediaViewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-app/src/MediaViewer.cpp b/es-app/src/MediaViewer.cpp index f9e1dcb8f..cf9aff0f4 100644 --- a/es-app/src/MediaViewer.cpp +++ b/es-app/src/MediaViewer.cpp @@ -116,7 +116,7 @@ void MediaViewer::render() Renderer::shaderPostprocessing(shaders, videoParameters); #endif } - else if (mImage && mImage->hasImage()) { + else if (mImage && mImage->hasImage() && mImage->getSize() != 0) { mImage->render(transform); #if defined(USE_OPENGL_21) From 64372a4a7024524e449141929b4c469f43f9aa8b Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 3 Jul 2021 11:53:30 +0200 Subject: [PATCH 17/76] Modified the input configuration dialog message. --- es-app/src/guis/GuiMenu.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index acd194935..f2be3c141 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -857,11 +857,10 @@ void GuiMenu::openConfigInput(GuiSettings* settings) settings->setNeedsSaving(false); std::string message = - "THE KEYBOARD AND ANY CONNECTED CONTROLLERS\n" - "ARE AUTOMATICALLY CONFIGURED ON STARTUP, BUT\n" - "USING THIS CONFIGURATION TOOL YOU ARE ABLE TO\n" - "OVERRIDE THE DEFAULT BUTTON MAPPINGS (NOTE\n" - "THAT THIS WILL NOT AFFECT THE HELP PROMPTS)\n" + "THE KEYBOARD AND CONTROLLERS ARE AUTOMATICALLY\n" + "CONFIGURED, BUT USING THIS CONFIGURATION TOOL\n" + "YOU CAN OVERRIDE THE DEFAULT BUTTON MAPPINGS\n" + "(THIS WILL NOT AFFECT THE HELP PROMPTS)\n" "CONTINUE?"; Window* window = mWindow; From 52e12da55a704873a1b9b37f2379ad80b8accc26 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 3 Jul 2021 12:24:23 +0200 Subject: [PATCH 18/76] Fixed an issue where the joystick counter would not decrease when removing a controller. --- es-core/src/InputManager.cpp | 25 ++++++++++--------------- es-core/src/InputManager.h | 3 ++- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/es-core/src/InputManager.cpp b/es-core/src/InputManager.cpp index b7c5e7d49..92fff54e7 100644 --- a/es-core/src/InputManager.cpp +++ b/es-core/src/InputManager.cpp @@ -272,20 +272,6 @@ std::string InputManager::getTemporaryConfigPath() return path; } -int InputManager::getNumJoysticks() -{ - int numJoysticks = 0; - - // This is a workaround to exclude the keyboard (ID -1) from the total joystick count. - // It's incorrectly added when configuring the keyboard in GuiInputConfig, but I've - // been unable to find a proper fix to not having it added to mJoysticks. - for (auto it = mJoysticks.cbegin(); it != mJoysticks.cend(); it++) { - if ((*it).first >= 0) - numJoysticks += 1; - } - return numJoysticks; -} - int InputManager::getNumConfiguredDevices() { int num = 0; @@ -672,7 +658,7 @@ void InputManager::removeControllerByJoystickID(SDL_JoystickID joyID) auto it = mInputConfigs.find(joyID); mInputConfigs.erase(it); - // Close the controllers. + // Close the controller and remove its entry. auto controllerIt = mControllers.find(joyID); if (controllerIt != mControllers.cend()) { SDL_GameControllerClose(controllerIt->second); @@ -681,4 +667,13 @@ void InputManager::removeControllerByJoystickID(SDL_JoystickID joyID) else { LOG(LogError) << "Couldn't find controller to close (instance ID: " << joyID << ")"; } + + // Remove the joystick entry. + auto joystickIt = mJoysticks.find(joyID); + if (joystickIt != mJoysticks.cend()) { + mJoysticks.erase(joystickIt); + } + else { + LOG(LogError) << "Couldn't find joystick entry to remove (instance ID: " << joyID << ")"; + } } diff --git a/es-core/src/InputManager.h b/es-core/src/InputManager.h index 399a21aec..f2810f4c5 100644 --- a/es-core/src/InputManager.h +++ b/es-core/src/InputManager.h @@ -43,7 +43,6 @@ public: static std::string getConfigPath(); static std::string getTemporaryConfigPath(); - int getNumJoysticks(); int getNumConfiguredDevices(); int getAxisCountByDevice(int deviceId); int getButtonCountByDevice(int deviceId); @@ -53,6 +52,8 @@ public: bool parseEvent(const SDL_Event& event, Window* window); + int getNumJoysticks() { return mJoysticks.size(); } + private: bool initialized() const; From f59223927e7539a2ef0759936bdf17f9ca8b3d6e Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 3 Jul 2021 12:25:36 +0200 Subject: [PATCH 19/76] Added a message to GuiDetectDevice if only accepting input from the first controller. --- es-core/src/guis/GuiDetectDevice.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/es-core/src/guis/GuiDetectDevice.cpp b/es-core/src/guis/GuiDetectDevice.cpp index f5973c510..06495d460 100644 --- a/es-core/src/guis/GuiDetectDevice.cpp +++ b/es-core/src/guis/GuiDetectDevice.cpp @@ -48,6 +48,10 @@ GuiDetectDevice::GuiDetectDevice( deviceInfo << numDevices << " GAMEPAD" << (numDevices > 1 ? "S" : "") << " DETECTED"; else deviceInfo << "NO GAMEPADS DETECTED"; + + if (numDevices > 1 && Settings::getInstance()->getBool("InputOnlyFirstController")) + deviceInfo << " (ONLY ACCEPTING INPUT FROM FIRST CONTROLLER)"; + mDeviceInfo = std::make_shared(mWindow, deviceInfo.str(), Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER); mGrid.setEntry(mDeviceInfo, Vector2i(0, 1), false, true); From a0e587c19bf21dc2110a60559db97c9b0319d5d9 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 3 Jul 2021 13:52:47 +0200 Subject: [PATCH 20/76] Fixed an issue where the static image would not always get rendered during Slide transitions. --- es-app/src/animations/MoveCameraAnimation.h | 3 ++- es-app/src/views/ViewController.cpp | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/es-app/src/animations/MoveCameraAnimation.h b/es-app/src/animations/MoveCameraAnimation.h index 8c308f5bd..3078ff495 100644 --- a/es-app/src/animations/MoveCameraAnimation.h +++ b/es-app/src/animations/MoveCameraAnimation.h @@ -27,7 +27,8 @@ public: { // Cubic ease out. t -= 1; - cameraOut.translation() = -Vector3f().lerp(-mCameraStart.translation(), mTarget, t*t*t + 1); + cameraOut.translation() = + -Vector3f().lerp(-mCameraStart.translation(), mTarget, t * t * t + 1); } private: diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index 213c28a7b..b4a29a32d 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -404,6 +404,11 @@ void ViewController::goToGameList(SystemData* system) mPreviousView.reset(); mPreviousView = nullptr; } + else if (!mPreviousView && mState.viewing == GAME_LIST) { + // This is needed as otherwise the static image would not get rendered during the + // first Slide transition when coming from the System view. + mSkipView = getGameListView(system); + } if (mState.viewing != SYSTEM_SELECT) { mPreviousView = mCurrentView; From 45d3bb9bad4d9b86ecc9b1f967c678e55fa4d82b Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 4 Jul 2021 11:36:16 +0200 Subject: [PATCH 21/76] Small refactoring of VideoFFmpegComponent. --- .../src/components/VideoFFmpegComponent.cpp | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/es-core/src/components/VideoFFmpegComponent.cpp b/es-core/src/components/VideoFFmpegComponent.cpp index dff5742b9..ef64069fc 100644 --- a/es-core/src/components/VideoFFmpegComponent.cpp +++ b/es-core/src/components/VideoFFmpegComponent.cpp @@ -13,6 +13,8 @@ #include "Settings.h" #include "Window.h" +#define DEBUG_VIDEO false + VideoFFmpegComponent::VideoFFmpegComponent( Window* window) : VideoComponent(window), @@ -631,10 +633,10 @@ void VideoFFmpegComponent::getProcessedFrames() mAudioFrameResampled) >= 0) { AudioFrame currFrame; + AVRational timeBase; mAudioFrameResampled->best_effort_timestamp = mAudioFrameResampled->pts; - AVRational timeBase; timeBase.num = 1; timeBase.den = mAudioFrameResampled->sample_rate; @@ -671,10 +673,12 @@ void VideoFFmpegComponent::outputFrames() while (!mAudioFrameQueue.empty()) { if (mAudioFrameQueue.front().pts < mAccumulatedTime + AUDIO_BUFFER) { // Enable only when needed, as this generates a lot of debug output. -// LOG(LogDebug) << "Processing audio frame with PTS: " << -// mAudioFrameQueue.front().pts; -// LOG(LogDebug) << "Total audio frames processed / audio frame queue size: " << -// mAudioFrameCount << " / " << std::to_string(mAudioFrameQueue.size()); + if (DEBUG_VIDEO) { + LOG(LogDebug) << "Processing audio frame with PTS: " << + mAudioFrameQueue.front().pts; + LOG(LogDebug) << "Total audio frames processed / audio frame queue size: " << + mAudioFrameCount << " / " << std::to_string(mAudioFrameQueue.size()); + } bool outputSound = false; @@ -712,10 +716,12 @@ void VideoFFmpegComponent::outputFrames() while (mIsActuallyPlaying && !mVideoFrameQueue.empty()) { if (mVideoFrameQueue.front().pts < mAccumulatedTime) { // Enable only when needed, as this generates a lot of debug output. -// LOG(LogDebug) << "Processing video frame with PTS: " << -// mVideoFrameQueue.front().pts; -// LOG(LogDebug) << "Total video frames processed / video frame queue size: " << -// mVideoFrameCount << " / " << std::to_string(mVideoFrameQueue.size()); + if (DEBUG_VIDEO) { + LOG(LogDebug) << "Processing video frame with PTS: " << + mVideoFrameQueue.front().pts; + LOG(LogDebug) << "Total video frames processed / video frame queue size: " << + mVideoFrameCount << " / " << std::to_string(mVideoFrameQueue.size()); + } mPictureMutex.lock(); From d5343ac80b34f2c0c8c1f52e422ad541cd1d970a Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 4 Jul 2021 12:30:00 +0200 Subject: [PATCH 22/76] Documentation update. --- CHANGELOG.md | 5 +++++ CONTRIBUTING.md | 15 +++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cf54d972..930014f4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ Apart from this, many small improvements and bug fixes are part of the release, * The quit menu is now disabled by default, instead showing the "Quit EmulationStation" entry unless configured otherwise * Removed the "Display game media from ROM directories" setting as it doesn't make sense to support this legacy functionality any longer * Added support for using the %ESPATH% and %ROMPATH% variables in the slideshow screensaver custom image directory setting +* Improved scaling relative to the screen aspect ratio for various GUI components which enhances the layout on 4:3 displays and ultrawide monitors * Removed the menu fade-in effect as it looked terrible * Enabled the menu scale-up effect for the OpenGL ES renderer * Renamed es_systems.cfg, es_settings.cfg and es_input.cfg to es_systems.xml, es_settings.xml and es_input.xml @@ -78,9 +79,11 @@ Apart from this, many small improvements and bug fixes are part of the release, ### Bug fixes * Marking all games as favorites for a system or folder or removing all favorite markings would sometimes crash the application +* Attempting to load a non-existent font file defined by the theme crashed the application instead of using the bundled fallback font * Games that were filtered out were included in the random game selection for the grouped custom collections view * After switching theme sets with only a single system available, diagonal slide transitions would sometimes play when moving to the system view * Ongoing slide transition animations would continue to play after switching theme sets +* When using the Video view style, the static image would not get rendered during the first Slide transition when coming from the System view * Long game names that were horizontally scrolling in the gamelist view would sometimes flicker when returning to the start position * On Windows, images with Unicode characters in the game name that were resized when scraping would not get saved with valid filenames * The glitches when configuring trigger buttons in GuiInputConfig have been fixed @@ -88,7 +91,9 @@ Apart from this, many small improvements and bug fixes are part of the release, * GuiInputConfig didn't correctly inform which buttons could be skipped for some rows * The scraper would sometimes consider very small images to be invalid * The Quick System Select help prompt was shown even when there was only a single game system present +* The "Back (cancel)" help prompt was missing for the single-game scraper * The "Y" button help prompt wasn't displayed correctly when using the Grid view style +* Encountering a corrupt image file would lead to a continuous loop of attempts to load the image while filling the log file with error messages * Cropping in ImageComponent didn't work correctly * The debug logging for the analog controller inputs had some inconsistent signs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ce04d3967..4bae964e8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,22 +43,22 @@ This plan is under constant review so expect it to change from time to time. Sti * Badges highlighting things like favorite games, completed games etc. (will require theme support) * On-screen keyboard * Web proxy support for the scraper -* Add GLM library dependency for matrix and vector operations, decommissioning the built-in functions +* Add GLM library dependency for matrix and vector operations, starting to decommission the built-in functions +* Flatpak and Snap releases on Linux #### v1.3 * Localization/multi-language support +* Overhaul of the theme handling, adding capabilities and improving compatibility with Recalbox and Batocera themes +* Scrapping the Grid view style and adding a general grid/wall component instead * Checksum support for the scraper for exact searches and for determining when to overwrite files -* Complete overhaul of the grid view style -* A nice and useful grid view implementation in rbsimple-DE -* Improved text and font functions, e.g. faster and cleaner line wrapping -* Flatpak and Snap support on Linux +* Improved text and font functions, e.g. faster and cleaner line wrapping and more exact sizing #### v1.4 * Authoring tools to clean up orphaned gamelist entries, media files etc. +* Scrollbars for menus and gamelists * Support for pre-defined alternative emulators and cores (configured in es_systems.xml) -* Simple file browsing component * Add 'time played' counter per game, similar to how it works in Steam * Preload all built-in resources and never clear them from the cache * Improved multi-threading @@ -66,8 +66,7 @@ This plan is under constant review so expect it to change from time to time. Sti #### v1.5 * Bulk metadata editor -* Overhaul of the GUI element scaling and placement logic to make ES-DE look more consistent across different resolutions -* Scrollbars for menus and gamelists +* Simple file browsing component * Improve the performance of the GLSL shader code * Animated menu elements like switches, tick boxes, smooth scrolling etc. * Support for additional scraper services (if feasible?) From 0ad903f2fa1a8a7f383345fb174fb2968643afa0 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 4 Jul 2021 12:32:35 +0200 Subject: [PATCH 23/76] Bumped the version to v1.1.0-beta2 --- es-app/CMakeLists.txt | 2 +- es-app/assets/EmulationStation-DE_Info.plist | 2 +- es-app/assets/emulationstation.6.gz | Bin 1052 -> 1050 bytes es-app/assets/emulationstation.desktop | 2 +- es-app/src/EmulationStation.h | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/es-app/CMakeLists.txt b/es-app/CMakeLists.txt index b8d3585c2..c1550f9c7 100644 --- a/es-app/CMakeLists.txt +++ b/es-app/CMakeLists.txt @@ -275,7 +275,7 @@ endif() set(CPACK_PACKAGE_VENDOR "Leon Styhre") # Update this when there has been a new release. -set(CPACK_PACKAGE_VERSION "1.1.0-beta2-dev") +set(CPACK_PACKAGE_VERSION "1.1.0-beta2") # Use the shorter x64 descriptor if on the x86_64/AMD64 architecture. if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL AMD64) diff --git a/es-app/assets/EmulationStation-DE_Info.plist b/es-app/assets/EmulationStation-DE_Info.plist index 335a39961..c67cffde1 100644 --- a/es-app/assets/EmulationStation-DE_Info.plist +++ b/es-app/assets/EmulationStation-DE_Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleVersion - 1.1.0-beta2-dev + 1.1.0-beta2 LSMinimumSystemVersion 10.14.0 LSUIPresentationMode diff --git a/es-app/assets/emulationstation.6.gz b/es-app/assets/emulationstation.6.gz index ee6a9026656966e0d5408b6d3baf5657cfeb24d5..375a38973695ca2c8839bb8bf51dcc7cde8657ed 100644 GIT binary patch delta 1030 zcmV+h1o``%2$~27ABzYGqm1E^2PA(G$#J^q0*j)6Q#*~?_yWt>ViEa)k|>KcMXJ2m zald|tl(TW%qP0U)ukrv^4mGEFTj zAwNLPM0?#(3B&l~r+D~dOyboYOr{U_qg67WrK|7{N3YGG1xe-7aE>ukPA zla$0aaBhPu11sy4{7K~^o-0W_YZzb=!7kDUCU7wAvYXgs+9Zh%6im9!C)B~(g7 zuGQXfxdDm-L7gmWf763e8Gz{czF+(D_t@zm@yuE^Zj&=hdvd1dN66;s{A!eU;Cp{XZVUh)Q z+q{nt^L&mE!dG-sx?mlv{M}oB3^_a`9d)pCx2Svrv*pTc-v#~QwQr)z|G<=QAh>KD zwh&iJ#Wye5?(BbbH@B*Sa1TnhFe5m(#EyM-QvJ`I6i-yq4NSjnwO(KLY4&djUV!*0y%$^I*UX<`gTv8T>*>SiK7# z)>h+ia~`1oT^4|ZjBwdQZzPndc@OSY^|$k^?kmkVT;iTOCUy}b>ZOwjjPBEUk9fvF zsS4L%=v{;SL$sq@P-H()`}rlT4Yw$bF^+*?{u%Vi49=fL3?0M%24BG$lnV#|0HX); A+W-In delta 1032 zcmV+j1o!)z2%HE9ABzYG;q%#%2PA)B$#J^q0*j)6Q#*}X-(WdgEFwQp5@oTLNR<~m z?$_^-a@Ln7?So-)hG(v4hGEiy(Hv&;6-?)&=;14@9-|l@qRIFNfXKj}YS=Kr6g4b| z^Z+HRn!B0`7=#}`g@YeM60RO$JbjuBSJ8YHulyf$e(FHycPkj&-QErGo7I1|qJ~SM zjsJt)ARL6ZLCOrh3v#x@JHOGI=?)3wN0<$#WAZOd0OMCtd=!nC-Wu7!IOp!{!SUDG zd=W=63Gd?Re&B04;plXpxUb;lU2ns=O82`G9NT>VbCsC z-bhFpSdE`t;6?El8up>=xhaoXN~J;DtORVDDH$x8QF5qIkV(Zfwra5;KH0g?6AQFj zkfgXzf+Wbf#%K;$4|wM2^92S;@NrE>t-N1L!O??@1j9H-qG?%K7mR-g?|E*@H9VJ$ zZ%VT!!=|Zt*4FWK_1wT7{mDJc5l;R*enTyD)&pHiTjehIOx7tEsPGBD<@(%YVXI1K ziZPJ{0t-wlcVXtg;H30k-ovF*W4yVVS zKo_Y|vy`U?!}JXn4XMRI?`hK@1<$PsnFOw_x^$_Hr1)T5srQ~Xtpbi2=2kiW;`k_I zn{y+MvlZ7S38bhTaY(%74Nz-jjqT1_QZMII(2ODrd*JRN2WWrZ4#KN}@TIQK4f74Q zjGZ|^(~+0?_+G_OaBK&3F?H;SA3o%}O;K1%X)x-G3cCD~pyYY(n(MJXW|wv=JsPBb zk}K@Cejo4W`5Nzq7j%*;V=b%P-3NE{IXp!zb+B^}sC)yns~(s=xuugKbLsMH2QPD&ot%~O10_#LWp0z z3+~reZE|Ol0x5b*%16#W!5wo;oIW<0I Date: Sun, 4 Jul 2021 13:52:25 +0200 Subject: [PATCH 24/76] Added the download links for v1.1.0-beta2 to README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 91b0ac4eb..965ba6d40 100644 --- a/README.md +++ b/README.md @@ -45,14 +45,14 @@ The latest stable version is 1.0.1 (released 2021-05-01) | macOS DMG installer | Legacy macOS 10.11 "El Capitan" to 10.13 "High Sierra" | x64 (x86) | [EmulationStation-DE-1.0.1-x64_legacy.dmg](https://es-de.org/releases/stable/macOS/EmulationStation-DE-1.0.1-x64_legacy.dmg)| | Windows installer | Windows 10 and 8.1 | x64 (x86) | [EmulationStation-DE-1.0.1-x64.exe](https://es-de.org/releases/stable/Windows/EmulationStation-DE-1.0.1-x64.exe)| -The latest pre-release version is 1.1.0-beta1 (released 2021-06-27) +The latest pre-release version is 1.1.0-beta2 (released 2021-07-04) | Package | Operating systems | Architecture | Download link | | :------------------ | :------------------------------------------------------ | :----------- | :------------- | -| Debian DEB package | Ubuntu 20.04 to 21.04, Linux Mint 20, possibly others | x64 (x86) | [emulationstation-de-1.1.0-beta1-x64.deb](https://es-de.org/releases/beta/Linux/emulationstation-de-1.1.0-beta1-x64.deb)| -| Fedora RPM package | Fedora Workstation 33, possibly others | x64 (x86) | [emulationstation-de-1.1.0-beta1-x64.rpm](https://es-de.org/releases/beta/Linux/emulationstation-de-1.1.0-beta1-x64.rpm)| -| macOS DMG installer | macOS 10.14 "Mojave" to 11 "Big Sur" | x64 (x86) | [EmulationStation-DE-1.1.0-beta1-x64.dmg](https://es-de.org/releases/beta/macOS/EmulationStation-DE-1.1.0-beta1-x64.dmg)| -| Windows installer | Windows 10 and 8.1 | x64 (x86) | [EmulationStation-DE-1.1.0-beta1-x64.exe](https://es-de.org/releases/beta/Windows/EmulationStation-DE-1.1.0-beta1-x64.exe)| +| Debian DEB package | Ubuntu 20.04 to 21.04, Linux Mint 20, possibly others | x64 (x86) | [emulationstation-de-1.1.0-beta2-x64.deb](https://es-de.org/releases/beta/Linux/emulationstation-de-1.1.0-beta2-x64.deb)| +| Fedora RPM package | Fedora Workstation 33, possibly others | x64 (x86) | [emulationstation-de-1.1.0-beta2-x64.rpm](https://es-de.org/releases/beta/Linux/emulationstation-de-1.1.0-beta2-x64.rpm)| +| macOS DMG installer | macOS 10.14 "Mojave" to 11 "Big Sur" | x64 (x86) | [EmulationStation-DE-1.1.0-beta2-x64.dmg](https://es-de.org/releases/beta/macOS/EmulationStation-DE-1.1.0-beta2-x64.dmg)| +| Windows installer | Windows 10 and 8.1 | x64 (x86) | [EmulationStation-DE-1.1.0-beta2-x64.exe](https://es-de.org/releases/beta/Windows/EmulationStation-DE-1.1.0-beta2-x64.exe)| Unfortunately due to technical reasons, v1.0.1 will be the last release for legacy macOS versions. From 47733467f3599d872ea2a47eddb3f0bb289471d7 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 4 Jul 2021 14:19:04 +0200 Subject: [PATCH 25/76] Bumped the version to v1.1.0-rc-dev --- es-app/CMakeLists.txt | 2 +- es-app/assets/EmulationStation-DE_Info.plist | 2 +- es-app/assets/emulationstation.6.gz | Bin 1050 -> 1051 bytes es-app/assets/emulationstation.desktop | 2 +- es-app/src/EmulationStation.h | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/es-app/CMakeLists.txt b/es-app/CMakeLists.txt index c1550f9c7..ec49edca4 100644 --- a/es-app/CMakeLists.txt +++ b/es-app/CMakeLists.txt @@ -275,7 +275,7 @@ endif() set(CPACK_PACKAGE_VENDOR "Leon Styhre") # Update this when there has been a new release. -set(CPACK_PACKAGE_VERSION "1.1.0-beta2") +set(CPACK_PACKAGE_VERSION "1.1.0-rc-dev") # Use the shorter x64 descriptor if on the x86_64/AMD64 architecture. if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL AMD64) diff --git a/es-app/assets/EmulationStation-DE_Info.plist b/es-app/assets/EmulationStation-DE_Info.plist index c67cffde1..f6d034d20 100644 --- a/es-app/assets/EmulationStation-DE_Info.plist +++ b/es-app/assets/EmulationStation-DE_Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleVersion - 1.1.0-beta2 + 1.1.0-rc-dev LSMinimumSystemVersion 10.14.0 LSUIPresentationMode diff --git a/es-app/assets/emulationstation.6.gz b/es-app/assets/emulationstation.6.gz index 375a38973695ca2c8839bb8bf51dcc7cde8657ed..a008bd6c04857878f8cde9024861b6c941a97a7a 100644 GIT binary patch literal 1051 zcmV+$1myc4iwFqOrr}@$17&S>Y+-b1Z*FsRVRUJ4ZZ0+eg;i~H;x-Weu3xd`%aNJb zg!HC2bJOVzDWq`=fx+ZDH!&ZCEn_vZ<0r}TC|I0w+c24oWV~n@uK_-4Tn(n+*XGyl{R2pUIBK@ zRt%QRYBka*$dzUW`?Oe)knB95beO!}qCm+;G za`fOM!7z@IXj%#9g9+e0FKo4j$BOZ7W!GfXG=k?{9bc~=8`z^iwPyvw$)AUBs8zu( z!BonL!sni=dc!3uLc(vkJ~ml8Axy3rlUXFOz*P7P_u&g(mBGt-X$fCa)IujmDfA0gQY41wm69ih zYWj@UG_8X%!~bDxGNaG%WjUH1dIEir1^4wCNOZ$gptQ^B2cQTh|;L@thUh*ep^~c;b-si8nx zEW8AN4>>@KZV+A!#Lu-jHq5u!GIrzuO(#JX;(HN8$*~>K$JDVSVfc{mZiB)~DT`5` zRM6*_MHMd!-`s!=G5fST>A@fklPs{?=6!sa=W~1zp3q6@oOP`7cW?bM02ejSPbQ{PH4B_< zD2_d@3ymvG>Gp+`xQqN1@amI&!!|DXTOhx@VLYM}zYXd6g4kgzx*5OhCVv-b+ifN| zXx{_-=js(12%owd*BP#E!=&Z-yTqn%eavn{!-5<=tnTJZGG%xzwQYse>z_XY=Y+-b1Z*FsRVRUJ4ZZ0+eg;m>b<2Dd|=T}VqvJMc* zak}UNi=u#2JB`}-0?XNA5&40VD2p{ks=U~7zkY|5vvJ&{eK0J}@XYnhFwS}~p2KXu zg6Vvm+y-H8KSUtJIJn>9{Q%at@X{K0M*596ySXO>>0{x{k%(<5g2}TeJ&MN6Y^`cxQgVL|;PmTkzDSdl#5ZtmgFI$cl3l7c$g)9JE!P1wo}|An zlZB6*{GS)-Pts^Sxd3i}LZy|o5tAiUN<*&I-f+1AiUL8MENXw+_}u)}1`f(;Ko6Ej zExJKa6Vy;BAz0xZ#DpaFg8?or+Qq6{1vvv}@RLitDE~skA(TD0^)XAO4H#F{fDN-X zgC(F%>nBHj~snI!7z(HuF1HQ4;m#o zdhn587^g@yt%UQz1n{1hwqC63e8Gz{czF+(D_t@zm@yuE^Zj&= zhdvd1dN6{gPD_iQ##riJ#qrVBwWmfrX9YJlizYDbOHWM7R z?}7bm^@0q9PhErS3|F^d(sKM=V$(N1W|z~jAV&|YyZMsbGrX4CLygqypFaZVZF>Pf z*VeY?^I*UX<`gTv8T>*>SiK7#)>h+ia~`1oT^4|ZjBwdQZzPndc@OSY^|$k^?kmkV zT;iTOCUy}b>ZOwjjPBEUk9fvFsS4L%=v{;SL$sq@P-H()`}rlT4Yw$bF^+*?{u%Vi U49=fL3?0M%24BG$lnV#|0PNoTi~s-t diff --git a/es-app/assets/emulationstation.desktop b/es-app/assets/emulationstation.desktop index 616fc9507..73ae2bb76 100644 --- a/es-app/assets/emulationstation.desktop +++ b/es-app/assets/emulationstation.desktop @@ -1,5 +1,5 @@ [Desktop Entry] -Version=1.1.0-beta2 +Version=1.1.0-rc-dev Name=EmulationStation Desktop Edition GenericName=Emulator Front-end Type=Application diff --git a/es-app/src/EmulationStation.h b/es-app/src/EmulationStation.h index c2021656d..bdb50ed76 100644 --- a/es-app/src/EmulationStation.h +++ b/es-app/src/EmulationStation.h @@ -13,7 +13,7 @@ #define PROGRAM_VERSION_MAJOR 1 #define PROGRAM_VERSION_MINOR 0 #define PROGRAM_VERSION_MAINTENANCE 0 -#define PROGRAM_VERSION_STRING "1.1.0-beta2" +#define PROGRAM_VERSION_STRING "1.1.0-rc-dev" #define PROGRAM_BUILT_STRING __DATE__ " - " __TIME__ From 483311f7f281441886628d3970d72ee69b216fee Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 4 Jul 2021 15:03:22 +0200 Subject: [PATCH 26/76] Switched the order of the Back and Start buttons in the input configurator. Also removed some legacy code. --- es-core/src/guis/GuiInputConfig.cpp | 51 ++++++----------------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/es-core/src/guis/GuiInputConfig.cpp b/es-core/src/guis/GuiInputConfig.cpp index 5604c5f5e..1c97db6ab 100644 --- a/es-core/src/guis/GuiInputConfig.cpp +++ b/es-core/src/guis/GuiInputConfig.cpp @@ -180,37 +180,6 @@ GuiInputConfig::GuiInputConfig( buttons.push_back(std::make_shared (mWindow, "OK", "ok", [this, okFunction] { okFunction(); })); -// This code is disabled as there is no intention to provide emulator configuration or -// similar control via ES-DE. Let's keep the code for reference though. -// buttons.push_back(std::make_shared(mWindow, "OK", "ok", [this, okFunction] { -// // Check if the hotkey enable button is set. if not prompt the -// // user to use select or nothing. -// Input input; -// okFunction(); -// if (!mTargetConfig->getInputByName("HotKeyEnable", &input)) { -// mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), -// "YOU DIDN'T CHOOSE A HOTKEY ENABLE BUTTON. THIS IS REQUIRED FOR EXITING " -// "GAMES WITH A CONTROLLER. DO YOU WANT TO USE THE SELECT BUTTON DEFAULT ? " -// "PLEASE ANSWER YES TO USE SELECT OR NO TO NOT SET A HOTKEY ENABLE BUTTON.", -// "YES", [this, okFunction] { -// Input input; -// mTargetConfig->getInputByName("Back", &input); -// mTargetConfig->mapInput("HotKeyEnable", input); -// okFunction(); -// }, -// "NO", [this, okFunction] { -// // For a disabled hotkey enable button, set to a key with id 0, -// // so the input configuration script can be backwards compatible. -// mTargetConfig->mapInput("HotKeyEnable", Input(DEVICE_KEYBOARD, -// TYPE_KEY, 0, 1, true)); -// okFunction(); -// })); -// } -// else { -// okFunction(); -// } -// })); - mButtonGrid = makeButtonGrid(mWindow, buttons); mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false); @@ -239,9 +208,9 @@ void GuiInputConfig::populateConfigList() if (controllerType == "snes") { sGuiInputConfigList[4] = - { "Start", false, "START", ":/help/button_start_SNES.svg" }; - sGuiInputConfigList[5] = { "Back", false, "SELECT", ":/help/button_back_SNES.svg" }; + sGuiInputConfigList[5] = + { "Start", false, "START", ":/help/button_start_SNES.svg" }; sGuiInputConfigList[6] = { "A", false, "B", ":/help/mbuttons_a_SNES.svg" }; sGuiInputConfigList[7] = @@ -253,9 +222,9 @@ void GuiInputConfig::populateConfigList() } else if (controllerType == "ps4") { sGuiInputConfigList[4] = - { "Start", false, "OPTIONS", ":/help/button_start_PS4.svg" }; - sGuiInputConfigList[5] = { "Back", false, "SHARE", ":/help/button_back_PS4.svg" }; + sGuiInputConfigList[5] = + { "Start", false, "OPTIONS", ":/help/button_start_PS4.svg" }; sGuiInputConfigList[6] = { "A", false, "CROSS", ":/help/mbuttons_a_PS.svg" }; sGuiInputConfigList[7] = @@ -267,9 +236,9 @@ void GuiInputConfig::populateConfigList() } else if (controllerType == "ps5") { sGuiInputConfigList[4] = - { "Start", false, "OPTIONS", ":/help/button_start_PS5.svg" }; - sGuiInputConfigList[5] = { "Back", false, "CREATE", ":/help/button_back_PS5.svg" }; + sGuiInputConfigList[5] = + { "Start", false, "OPTIONS", ":/help/button_start_PS5.svg" }; sGuiInputConfigList[6] = { "A", false, "CROSS", ":/help/mbuttons_a_PS.svg" }; sGuiInputConfigList[7] = @@ -281,9 +250,9 @@ void GuiInputConfig::populateConfigList() } else if (controllerType == "xbox360") { sGuiInputConfigList[4] = - { "Start", false, "START", ":/help/button_start_XBOX360.svg" }; - sGuiInputConfigList[5] = { "Back", false, "BACK", ":/help/button_back_XBOX360.svg" }; + sGuiInputConfigList[5] = + { "Start", false, "START", ":/help/button_start_XBOX360.svg" }; sGuiInputConfigList[6] = { "A", false, "A", ":/help/mbuttons_a_XBOX.svg" }; sGuiInputConfigList[7] = @@ -296,9 +265,9 @@ void GuiInputConfig::populateConfigList() else { // Xbox One and later. sGuiInputConfigList[4] = - { "Start", false, "MENU", ":/help/button_start_XBOX.svg" }; - sGuiInputConfigList[5] = { "Back", false, "VIEW", ":/help/button_back_XBOX.svg" }; + sGuiInputConfigList[5] = + { "Start", false, "MENU", ":/help/button_start_XBOX.svg" }; sGuiInputConfigList[6] = { "A", false, "A", ":/help/mbuttons_a_XBOX.svg" }; sGuiInputConfigList[7] = From 4b9dc1e0c475a4e4180340974d7c30dd030d22b5 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 4 Jul 2021 20:19:37 +0200 Subject: [PATCH 27/76] Added a clang-format style configuration file. --- .clang-format | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..ad3eebf19 --- /dev/null +++ b/.clang-format @@ -0,0 +1,144 @@ +# SPDX-License-Identifier: MIT +# +# EmulationStation Desktop Edition +# .clang-format +# +# Style configuration file for automatic C++ code formatting using clang-format. +# This file requires at least clang-format version 10.0. +# + +--- +Language: Cpp +# BasedOnStyle: WebKit +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: false +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +# The following option requires version >= 11.0. +#####AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: true + BeforeCatch: false + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + - Regex: '.*' + Priority: 1 + SortPriority: 0 +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: true +IndentGotoLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: c++14 +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +... From d49d0e3c8660c4125f9fd6eb86d3ecc0457ec2bc Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 4 Jul 2021 21:01:14 +0200 Subject: [PATCH 28/76] Documentation update. --- CHANGELOG.md | 2 ++ CONTRIBUTING.md | 34 +++++++++---------------------- INSTALL-DEV.md | 52 +++++++++++++++++++++++++++++++++++++++++------- USERGUIDE-DEV.md | 12 +++++------ 4 files changed, 62 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 930014f4c..0add9dc41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Apart from this, many small improvements and bug fixes are part of the release, * Default controller configuration is now automatically applied, input configuration should rarely if ever be required any longer except for deliberate button customization * Added support for selecting the controller type (Xbox, Xbox 360, PS4, PS5 and SNES), which changes the help icons, help text and the input configuration tool icons and text * Added an option to limit the input in ES-DE to only the first controller (does not affect the emulators) +* Switched the order of the "Back" and "Start" buttons (or equivalents) in the input configurator to align with the other button entries which go from left to right * Added separate controller deadzone values for the triggers and thumbsticks * Removed the startup notification regarding default keyboard mappings being in use, instead default mappings are now considered the recommended input configuration * The controller input configuration is not automatically started any longer if there is no es_input.cfg file or if there are no applicable configuration entries in the file @@ -73,6 +74,7 @@ Apart from this, many small improvements and bug fixes are part of the release, * Added a function to ImageComponent to crop fully transparent areas around an image * Added a CMake option to control whether the VLC video player should be built, and set this to off by default * Removed the pointless APPLE_SKIP_INSTALL_LIBS CMake option +* Added a clang-format style configuration file to use for automatic code formatting * Added the NanoSVG library as a proper Git subtree * Changed the language standard from C++11 to C++14 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4bae964e8..30fc86060 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -87,36 +87,20 @@ To see which features have been implemented in previous versions, please refer t ### Coding style -The coding style for ES-DE is mostly a combination of the Linux kernel style (although that's C it's close enough to C++ as far as I'm concerned) and Google's C++ guidelines. +The coding style for ES-DE is mostly a combination of the Webkit and Linux kernel guidelines. The cosmetic aspect of the style is auto-formatted using clang-format, so all code changes must first have been processed using this tool. You can read in [INSTALL-DEV.md](INSTALL-DEV.md#using-clang-format-for-automatic-code-formatting) how clang-format is installed and used. -Please refer to these documents here: +But of course clang-format won't change the actual code content or fix all code style choices so here are some additional key points: -https://www.kernel.org/doc/html/v4.10/process/coding-style.html \ -https://google.github.io/styleguide/cppguide.html - -**Some key points:** - -* Column width (line length) is 100 characters -* Indentation is 4 spaces, don't use tabs as they can be interpreted differently -* Line break is Unix-style (line feed only, no carriage return) -* Do not leave trailing whitespaces at the end of the lines (a good source code editor should have a setting to automatically trim these for you) -* When breaking up long lines into multiple lines, consider what could be useful data to grep for so you don't break in the middle of such a string -* Comments always in C++ style, i.e. `//` instead of `/* */` +* Always write comments in C++ style, i.e. `//` instead of `/* */` * Comments should be proper sentences, starting with a capital letter and ending with a dot -* Use K&R placements of braces, read the Linux kernel coding style document for clarifications -* Always use spaces between keywords and opening brackets, i.e. `if ()`, `for ()`, `while ()` etc. -* Indentation of switch/case statements is optional, but it's usually easier to read the code with indentations in place -* Use `std::string` or `std::vector` instead of `char *` or `char []` unless there is a specific reason requiring the latter -* Actually, try to use C++ syntax in general instead of C syntax, another example would be `static_cast(someFloatVariable)` instead of `(int)someFloatVariable` -* If the arguments (and initializer list) for a function or class exceeds 4 items, arrange them vertically to make the code easier to read +* As a general rule, use C++ syntax instead of C syntax, for example `static_cast(someFloatVariable)` instead of `(int)someFloatVariable` * Always declare one variable per line, never combine multiple declarations of the same type -* Name local variables with the first word in small letters and the proceeding words starting with capital letters, e.g. `myExampleVariable` -* Name member variables starting with an `m` such as `mMyMemberVariable` and name static variables with an `s` such as `sMyStaticVariable` -* Use the same naming convention for functions as for local variables, e.g. `someFunction()` -* Single-line function definitions are fine to put in the header files, but if it's more than one line, place it in the corresponding .cpp file -* Inline functions makes perfect sense to use, but don't overdo it by using them for functions that won't be called very frequently -* Don't put more than one statement on a single line (there are some exceptions though like lambda expressions and possibly switch statements) +* Name member variables starting with an `m` such as `mMyMemberVariable` and name static variables starting with an `s` such as `sMyStaticVariable` +* Single-line function definitions can be placed in either the .h or .cpp files depending on the situation + +* Inline functions makes perfect sense to use, but don't overdo it by using them for code that won't be called very frequently * Avoid overoptimizations, especially if it sacrifices readability, makes the code hard to expand on or is error prone +* Try to be coherent with the existing codebase regarding names, structure etc., it should not be obvious which person wrote what parts * For the rest, check the code and have fun! :) ### Building and configuring diff --git a/INSTALL-DEV.md b/INSTALL-DEV.md index 2bb1830e6..d2fb75280 100644 --- a/INSTALL-DEV.md +++ b/INSTALL-DEV.md @@ -14,6 +14,8 @@ ES-DE is developed and compiled using Clang/LLVM and GCC on Unix, Clang/LLVM on CMake is the build system for all the supported operating systems, used in conjunction with `make` on Unix and macOS and `nmake` and `make` on Windows. Xcode on macOS or Visual Studio on Windows are not required for building ES-DE and they have not been used during the development. The only exception is notarization of codesigned macOS packages which require the `altool` and `stapler` binaries that come bundled with Xcode. +For automatic code formatting clang-format is used. + Any code editor can be used of course, but I recommend [VSCode](https://code.visualstudio.com). @@ -27,7 +29,7 @@ The code has some dependencies. For building, you'll need CMake and development All of the required packages can be installed with apt-get: ``` -sudo apt-get install build-essential git cmake libsdl2-dev libavcodec-dev libavfilter-dev libavformat-dev libavutil-dev libfreeimage-dev libfreetype6-dev libcurl4-openssl-dev libpugixml-dev rapidjson-dev libasound2-dev libgl1-mesa-dev +sudo apt-get install build-essential clang-format git cmake libsdl2-dev libavcodec-dev libavfilter-dev libavformat-dev libavutil-dev libfreeimage-dev libfreetype6-dev libcurl4-openssl-dev libpugixml-dev rapidjson-dev libasound2-dev libgl1-mesa-dev ``` If building with the optional VLC video player, the following packages are also needed: @@ -39,7 +41,7 @@ sudo apt-get install vlc libvlc-dev Use dnf to install all the required packages: ``` -sudo dnf install gcc-c++ cmake SDL2-devel ffmpeg-devel freeimage-devel freetype-devel curl-devel pugixml-devel rapidjson-devel alsa-lib-devel mesa-libGL-devel +sudo dnf install gcc-c++ clang-tools-extra cmake SDL2-devel ffmpeg-devel freeimage-devel freetype-devel curl-devel pugixml-devel rapidjson-devel alsa-lib-devel mesa-libGL-devel ``` If building with the VLC video player, add the RPM Fusion repository. @@ -56,7 +58,7 @@ sudo dnf install vlc vlc-devel Use pacman to install all the required packages: ``` -sudo pacman -S gcc make cmake pkgconf sdl2 ffmpeg freeimage freetype2 pugixml rapidjson +sudo pacman -S gcc clang make cmake pkgconf sdl2 ffmpeg freeimage freetype2 pugixml rapidjson ``` If building with the optional VLC video player, the following package is also needed: @@ -82,7 +84,7 @@ Clang/LLVM and cURL should already be installed in the base OS image. Use pkgin to install the dependencies: ``` -pkgin install git cmake pkgconf SDL2 ffmpeg4 freeimage pugixml rapidjson +pkgin install clang git cmake pkgconf SDL2 ffmpeg4 freeimage pugixml rapidjson ``` If building with the optional VLC video player, the following package is also needed: @@ -90,13 +92,13 @@ If building with the optional VLC video player, the following package is also ne pkgin vlc ``` -NetBSD ships with GCC by default, and although you should be able to install and use Clang/LLVM, it's probably easier to just stick to the default compiler environment. +NetBSD ships with GCC by default, and although you should be able to use Clang/LLVM, it's probably easier to just stick to the default compiler environment. The reason why the clang package needs to be installed is to get clang-format onto the system. **On OpenBSD** Use pkg_add to install the dependencies: ``` -pkg_add cmake pkgconf sdl2 ffmpeg freeimage +pkg_add clang-tools-extra cmake pkgconf sdl2 ffmpeg freeimage ``` If building with the optional VLC video player, the following package is also needed: @@ -416,7 +418,7 @@ Be aware that Homebrew can be really slow, especially when it compiles packages Install the required tools and dependencies: ``` -brew install cmake pkg-config nasm fdk-aac libvpx sdl2 freeimage freetype pugixml rapidjson +brew install clang-format cmake pkg-config nasm fdk-aac libvpx sdl2 freeimage freetype pugixml rapidjson ``` If building with the optional VLC video player, then run this as well: @@ -768,6 +770,11 @@ Note that most GDB builds for Windows have broken Python support so that pretty **Other preparations:** +In order to get clang-format onto the system you need to download and install Clang: \ +[https://llvm.org/builds](https://llvm.org/builds) + +Just run the installer and make sure to select the option "Add LLVM to the system PATH for current user". + Install your editor of choice, I use [VSCode](https://code.visualstudio.com). Configure Git. I won't get into the details on how it's done, but there are many resources available online to support with this. The `Git Bash` shell shipped with Git for Windows is very useful though as it's somewhat reproducing a Unix environment using MinGW/MSYS2. @@ -1140,6 +1147,37 @@ The theme is not mandatory to start the application, but ES-DE will be basically So the home directory will always take precedence, and any resources or themes located there will override the ones in the path of the ES-DE executable. +## Using clang-format for automatic code formatting + +Although not completed yet as of writing this, ES-DE will have its entire codebase automatically formatted by clang-format. There is a style configuration file named .clang-format located directly at the root of the ES-DE source repository which can be used to apply the formatting anywhere in the source tree. + +How to install clang-format is detailed per operating system earlier in this document. + +There are two ways to run this tool, from the command line or from inside an editor such as VSCode. + +To format a file from the command line, simply run: + +```clang-format -i ``` + +The -i flag will make an inplace edit of the file. + +But the recommended way is to run clang-format from within the editor. If using VSCode, there is an extension available named Clang-Format. After installing this, simply open a source file, right click and choose `Format Document` or use the applicable keyboard shortcut. The first time you do this, you will have to make a choice to perform the formatting using clang-format. The rest should be completely automatic. + +In some instances you want to avoid getting code formatted, and you accomplish this by simply enclosing the code by the two comments "clang-format off" and "clang-format on", such as this: + +```c++ +// clang-format off +CollectionSystemDecl systemDecls[] = { +// Type Name Long name Theme folder isCustom + { AUTO_ALL_GAMES, "all", "all games", "auto-allgames", false }, + { AUTO_LAST_PLAYED, "recent", "last played", "auto-lastplayed", false }, + { AUTO_FAVORITES, "favorites", "favorites", "auto-favorites", false }, + { CUSTOM_COLLECTION, myCollectionsName, "collections", "custom-collections", true } +}; +// clang-format on +``` + + ## CA certificates and MAME ROM information **CA certificates:** diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index 782f77277..b99186bba 100644 --- a/USERGUIDE-DEV.md +++ b/USERGUIDE-DEV.md @@ -215,7 +215,7 @@ _The help system is displayed at the bottom of the screen, showing the various a ## General navigation -The built-in help system will provide a contextual summary of the available navigation options, but here's still a general overview. These are the inputs you mapped in the previous input device configuration step. Note that this is not an exhaustive list, but it covers most situations. +The built-in help system will provide a contextual summary of the available navigation options, but here's still a general overview. These are the buttons optionally mapped in the previous input device configuration step. Note that this is not an exhaustive list, but it covers most situations. The button names are based on the Xbox 360 controller as that is the naming convention used by the SDL library which handles the controller input in ES-DE. The default keyboard mappings are shown in brackets. @@ -234,7 +234,7 @@ _(Escape)_ Opens and closes the main menu. -**Select button**\ +**Back button**\ _(F1)_ Opens and closes the game options menu in the gamelist view, or toggles the screensaver in the system view (if the _Enable screensaver controls_ setting is activated). @@ -257,7 +257,7 @@ Jumps to a random game or system depending on whether pressed when in the system **A button**\ _(Enter)_ -Select button to open gamelists from the system view, launch games, choose menu entries etc. +Used to open gamelists from the system view, launch games, choose menu entries etc. **B button**\ _(Back key)_ @@ -988,7 +988,7 @@ The screensaven type to use; _Dim_, _Black_, _Slideshow_ or _Video_. **Enable screensaver controls** -This includes the ability to start the screensaver manually using the _Select_ button from the system view, but also while the screensaver is running to jump to a new random game using the _Left_ and _Right_ buttons, to launch the game currently shown using the _A_ button and to jump to the game in its gamelist using the _Y_ button. If this setting is disabled, any key or button press will stop the screensaver. +This includes the ability to start the screensaver manually using the _Back_ button from the system view, but also while the screensaver is running to jump to a new random game using the _Left_ and _Right_ buttons, to launch the game currently shown using the _A_ button and to jump to the game in its gamelist using the _Y_ button. If this setting is disabled, any key or button press will stop the screensaver. #### Slideshow screensaver settings @@ -1242,7 +1242,7 @@ Self explanatory. This menu is opened from the gamelist views, and can't be accessed from the system view. The menu changes slightly depending on the context, for example if a game file or a folder is selected, or whether the current system is a collection or a normal game platform. -You open the menu using the **Select** button, and by pressing **B** or selecting the **Apply** button any settings such as letter jumping using the quick selector or sorting changes are applied. If you instead press the Select button again, the menu is closed without applying any changes. +You open the menu using the **Back** button, and by pressing **B** or selecting the **Apply** button any settings such as letter jumping using the quick selector or sorting changes are applied. If you instead press the Back button again, the menu is closed without applying any changes. ![alt text](images/current/es-de_game_options_menu.png "ES-DE Game Options Menu") _The game options menu as laid out when opening it from within a custom collection, which adds the menu entry to add or remove games from the collection._ @@ -1449,7 +1449,7 @@ Numerous options can be set for these screensavers, as detailed [here](USERGUIDE The Dim screensaver simply dims and desaturates the current view and Black will show a black screen. The Slideshow and Video screensavers are more interesting as they can display images and videos from your game collection. In addition to this, the Slideshow screensaver can be configured to only show images from a specified directory. -If the option **Enable screensaver controls** has been activated, you can manually toggle the screensaver from the system view by pressing the **Select** key. In addition to this, for the Slideshow and Video screensavers, the controls will allow you to jump to a new random image or video using the **Left** and **Right** buttons on your keyboard or controller. It's also possible to launch the game currently displayed using the **A** button, and the **Y** button will jump to the game in its gamelist without starting it. +If the option **Enable screensaver controls** has been activated, you can manually toggle the screensaver from the system view by pressing the **Back** button. In addition to this, for the Slideshow and Video screensavers, the controls will allow you to jump to a new random image or video using the **Left** and **Right** buttons on your keyboard or controller. It's also possible to launch the game currently displayed using the **A** button, and the **Y** button will jump to the game in its gamelist without starting it. If the Video screensaver has been selected and there are no videos available, a fallback to the Dim screensaver will take place. The same is true for the Slideshow screensaver if no game images are available. From 83967a508d73ddd70ff9dab8e4f52cb0956c91e9 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 4 Jul 2021 21:05:43 +0200 Subject: [PATCH 29/76] Fixed a formatting issue in CONTRIBUTING.md. --- CONTRIBUTING.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 30fc86060..16eef9413 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -97,7 +97,6 @@ But of course clang-format won't change the actual code content or fix all code * Always declare one variable per line, never combine multiple declarations of the same type * Name member variables starting with an `m` such as `mMyMemberVariable` and name static variables starting with an `s` such as `sMyStaticVariable` * Single-line function definitions can be placed in either the .h or .cpp files depending on the situation - * Inline functions makes perfect sense to use, but don't overdo it by using them for code that won't be called very frequently * Avoid overoptimizations, especially if it sacrifices readability, makes the code hard to expand on or is error prone * Try to be coherent with the existing codebase regarding names, structure etc., it should not be obvious which person wrote what parts From aa670203266a30964f77202b016fefc76484e575 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 4 Jul 2021 21:34:21 +0200 Subject: [PATCH 30/76] (Windows) Fixed an MSVC compiler warning. --- es-core/src/InputManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-core/src/InputManager.h b/es-core/src/InputManager.h index f2810f4c5..b675db2e0 100644 --- a/es-core/src/InputManager.h +++ b/es-core/src/InputManager.h @@ -52,7 +52,7 @@ public: bool parseEvent(const SDL_Event& event, Window* window); - int getNumJoysticks() { return mJoysticks.size(); } + int getNumJoysticks() { return static_cast(mJoysticks.size()); } private: bool initialized() const; From f22ec86cd1e51c856bec1e57b47b146129d4ad3c Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 5 Jul 2021 20:53:35 +0200 Subject: [PATCH 31/76] Fixed an issue where update_version_string.sh did not properly update the version. --- tools/update_version_string.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tools/update_version_string.sh b/tools/update_version_string.sh index 23b213752..c3b4ff4aa 100755 --- a/tools/update_version_string.sh +++ b/tools/update_version_string.sh @@ -54,6 +54,25 @@ mv $TEMPFILE $MODIFYFILE ##### EmulationStation.h MODIFYFILE=../es-app/src/EmulationStation.h + +MODIFYSTRING=$(grep "PROGRAM_VERSION_MAJOR " $MODIFYFILE) +NEWSTRING="#define PROGRAM_VERSION_MAJOR ${1}" + +cat $MODIFYFILE | sed s/"${MODIFYSTRING}"/"${NEWSTRING}"/ > $TEMPFILE +mv $TEMPFILE $MODIFYFILE + +MODIFYSTRING=$(grep "PROGRAM_VERSION_MINOR " $MODIFYFILE) +NEWSTRING="#define PROGRAM_VERSION_MINOR ${2}" + +cat $MODIFYFILE | sed s/"${MODIFYSTRING}"/"${NEWSTRING}"/ > $TEMPFILE +mv $TEMPFILE $MODIFYFILE + +MODIFYSTRING=$(grep "PROGRAM_VERSION_MAINTENANCE " $MODIFYFILE) +NEWSTRING="#define PROGRAM_VERSION_MAINTENANCE ${3}" + +cat $MODIFYFILE | sed s/"${MODIFYSTRING}"/"${NEWSTRING}"/ > $TEMPFILE +mv $TEMPFILE $MODIFYFILE + MODIFYSTRING=$(grep "PROGRAM_VERSION_STRING" $MODIFYFILE) NEWSTRING="#define PROGRAM_VERSION_STRING \"${1}.${2}.${3}${SUFFIX}\"" From b86160081441817a8d3f640b5c4293059c762cc4 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 5 Jul 2021 20:55:23 +0200 Subject: [PATCH 32/76] Adjusted the clang-format rules. --- .clang-format | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.clang-format b/.clang-format index ad3eebf19..1e2200e58 100644 --- a/.clang-format +++ b/.clang-format @@ -11,7 +11,7 @@ Language: Cpp # BasedOnStyle: WebKit AccessModifierOffset: -4 -AlignAfterOpenBracket: DontAlign +AlignAfterOpenBracket: Align AlignConsecutiveMacros: false AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false @@ -25,15 +25,15 @@ AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: false # The following option requires version >= 11.0. #####AllowShortEnumsOnASingleLine: false -AllowShortFunctionsOnASingleLine: None -AllowShortLambdasOnASingleLine: Empty +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: MultiLine -BinPackArguments: false +BinPackArguments: true BinPackParameters: false BraceWrapping: AfterCaseLabel: false @@ -65,7 +65,7 @@ CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 8 +ContinuationIndentWidth: 4 Cpp11BracedListStyle: false DeriveLineEnding: true DerivePointerAlignment: false From 2d6f4505afea26f233361c4b387398b788e23a48 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 7 Jul 2021 17:46:44 +0200 Subject: [PATCH 33/76] Increased the minimum required GCC version to cover C++14. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 56b6d02fe..1da5f7a91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,8 +107,8 @@ if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") message("-- Compiler is GNU/GCC") execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpfullversion OUTPUT_VARIABLE G++_VERSION) - if(G++_VERSION VERSION_LESS 4.8) - message(SEND_ERROR "You need at least GCC 4.8 to compile EmulationStation-DE") + if(G++_VERSION VERSION_LESS 5.4) + message(SEND_ERROR "You need at least GCC 5.4 to compile EmulationStation-DE") endif() if(WIN32) set(CMAKE_CXX_FLAGS "-mwindows ${CMAKE_CXX_FLAGS}") From 3c47cde40a81580468ff4b28edb0c4cc28914a3f Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 7 Jul 2021 17:48:04 +0200 Subject: [PATCH 34/76] Some adjustments to the clang-format rules. --- .clang-format | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.clang-format b/.clang-format index 1e2200e58..71628c23e 100644 --- a/.clang-format +++ b/.clang-format @@ -45,8 +45,8 @@ BraceWrapping: AfterObjCDeclaration: false AfterStruct: false AfterUnion: false - AfterExternBlock: true - BeforeCatch: false + AfterExternBlock: false + BeforeCatch: true BeforeElse: true IndentBraces: false SplitEmptyFunction: true @@ -110,7 +110,7 @@ PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left ReflowComments: true SortIncludes: true From b63831147e12d10fe8ff7ed1e570f9acfbef1629 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 7 Jul 2021 17:49:08 +0200 Subject: [PATCH 35/76] Pedantic commit. --- tools/update_version_string.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/update_version_string.sh b/tools/update_version_string.sh index c3b4ff4aa..c4c15f004 100755 --- a/tools/update_version_string.sh +++ b/tools/update_version_string.sh @@ -20,7 +20,6 @@ # # This script is only intended to be used on Linux systems. # -# if [ ! -f ../es-app/CMakeLists.txt ]; then echo "You need to run this script from within the tools directory." From 745cf6ff92ea147bfff55dd246f2af5333fbe219 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 7 Jul 2021 17:53:05 +0200 Subject: [PATCH 36/76] Added a script to reformat the entire codebase using clang-format. --- tools/reformat_codebase.sh | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100755 tools/reformat_codebase.sh diff --git a/tools/reformat_codebase.sh b/tools/reformat_codebase.sh new file mode 100755 index 000000000..c5658dc75 --- /dev/null +++ b/tools/reformat_codebase.sh @@ -0,0 +1,45 @@ +#!/usr/bin/bash +# SPDX-License-Identifier: MIT +# +# EmulationStation Desktop Edition +# reformat_codebase.sh +# +# Automatically reformats the codebase using clang-format. +# The .clang-format style configuration file in the root of the repository will be +# used to apply the code formatting to the "es-app/src" and "es-core/src" trees. +# All files will be formatted in-place meaning they will be overwritten with any changes. +# +# The purpose of this script is primarily to apply updated formatting as clang-format +# improves over time, but potentially also to apply any changes to the ES-DE coding style. +# +# This script is only intended to be used on Linux systems. +# + +if [ ! $(which clang-format 2>/dev/null) ]; then + echo "Can't find clang-format which is required to run this script" + exit +fi + +if [ ! -d ../es-app/src ]; then + echo "Can't find the ../es-app/src directory, this script must run from the tools directory" + exit +fi + +APP_CPP_NUM_FILES=$(find ../es-app/src -name '*.cpp' | wc -l) +APP_H_NUM_FILES=$(find ../es-app/src -name '*.h' | wc -l) + +find ../es-app/src -name '*.cpp' -exec echo clang-format -i {} \; +find ../es-app/src -name '*.cpp' -exec clang-format -i {} \; +find ../es-app/src -name '*.h' -exec echo clang-format -i {} \; +find ../es-app/src -name '*.h' -exec clang-format -i {} \; + +CORE_CPP_NUM_FILES=$(find ../es-core/src -name '*.cpp' | wc -l) +CORE_H_NUM_FILES=$(find ../es-core/src -name '*.h' | wc -l) + +find ../es-core/src -name '*.cpp' -exec echo clang-format -i {} \; +find ../es-core/src -name '*.cpp' -exec clang-format -i {} \; +find ../es-core/src -name '*.h' -exec echo clang-format -i {} \; +find ../es-core/src -name '*.h' -exec clang-format -i {} \; + +echo "Processed" $APP_CPP_NUM_FILES ".cpp files and" $APP_H_NUM_FILES ".h files in es-app/src" +echo "Processed" $CORE_CPP_NUM_FILES ".cpp files and" $CORE_H_NUM_FILES ".h files in es-core/src" From af5e32e1216c93c592d150b092200abdc89d9d69 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 7 Jul 2021 20:03:42 +0200 Subject: [PATCH 37/76] Formatted the es-app source tree using clang-format. --- es-app/src/CollectionSystemsManager.cpp | 555 ++++++++-------- es-app/src/CollectionSystemsManager.h | 28 +- es-app/src/EmulationStation.h | 6 +- es-app/src/FileData.cpp | 445 +++++++------ es-app/src/FileData.h | 69 +- es-app/src/FileFilterIndex.cpp | 133 ++-- es-app/src/FileFilterIndex.h | 7 +- es-app/src/FileSorts.cpp | 26 +- es-app/src/FileSorts.h | 2 +- es-app/src/Gamelist.cpp | 122 ++-- es-app/src/MediaViewer.cpp | 33 +- es-app/src/MediaViewer.h | 4 +- es-app/src/MetaData.cpp | 37 +- es-app/src/MetaData.h | 27 +- es-app/src/MiximageGenerator.cpp | 230 ++++--- es-app/src/MiximageGenerator.h | 8 +- es-app/src/PlatformId.cpp | 6 +- es-app/src/PlatformId.h | 3 +- es-app/src/SystemData.cpp | 378 ++++++----- es-app/src/SystemData.h | 84 +-- es-app/src/SystemScreensaver.cpp | 209 +++--- es-app/src/SystemScreensaver.h | 13 +- es-app/src/VolumeControl.cpp | 89 +-- es-app/src/VolumeControl.h | 8 +- es-app/src/animations/MoveCameraAnimation.h | 14 +- .../src/guis/GuiCollectionSystemsOptions.cpp | 227 +++---- es-app/src/guis/GuiCollectionSystemsOptions.h | 3 +- es-app/src/guis/GuiGameScraper.cpp | 120 ++-- es-app/src/guis/GuiGameScraper.h | 7 +- es-app/src/guis/GuiGamelistFilter.cpp | 81 +-- es-app/src/guis/GuiGamelistFilter.h | 8 +- es-app/src/guis/GuiGamelistOptions.cpp | 202 +++--- es-app/src/guis/GuiGamelistOptions.h | 4 +- es-app/src/guis/GuiInfoPopup.cpp | 21 +- es-app/src/guis/GuiInfoPopup.h | 2 +- es-app/src/guis/GuiLaunchScreen.cpp | 88 +-- es-app/src/guis/GuiLaunchScreen.h | 5 +- es-app/src/guis/GuiMediaViewerOptions.cpp | 34 +- es-app/src/guis/GuiMenu.cpp | 603 +++++++++--------- es-app/src/guis/GuiMenu.h | 8 +- es-app/src/guis/GuiMetaDataEd.cpp | 329 +++++----- es-app/src/guis/GuiMetaDataEd.h | 20 +- es-app/src/guis/GuiOfflineGenerator.cpp | 185 +++--- es-app/src/guis/GuiOfflineGenerator.h | 4 +- es-app/src/guis/GuiScraperMenu.cpp | 240 ++++--- es-app/src/guis/GuiScraperMenu.h | 13 +- es-app/src/guis/GuiScraperMulti.cpp | 120 ++-- es-app/src/guis/GuiScraperMulti.h | 27 +- es-app/src/guis/GuiScraperSearch.cpp | 240 +++---- es-app/src/guis/GuiScraperSearch.h | 59 +- es-app/src/guis/GuiScreensaverOptions.cpp | 198 +++--- es-app/src/guis/GuiSettings.cpp | 92 ++- es-app/src/guis/GuiSettings.h | 50 +- es-app/src/main.cpp | 161 ++--- es-app/src/scrapers/GamesDBJSONScraper.cpp | 331 +++++----- es-app/src/scrapers/GamesDBJSONScraper.h | 37 +- .../scrapers/GamesDBJSONScraperResources.cpp | 110 ++-- .../scrapers/GamesDBJSONScraperResources.h | 20 +- es-app/src/scrapers/Scraper.cpp | 176 +++-- es-app/src/scrapers/Scraper.h | 95 ++- es-app/src/scrapers/ScreenScraper.cpp | 324 +++++----- es-app/src/scrapers/ScreenScraper.h | 65 +- es-app/src/views/SystemView.cpp | 416 ++++++------ es-app/src/views/SystemView.h | 16 +- es-app/src/views/UIModeController.cpp | 37 +- es-app/src/views/UIModeController.h | 7 +- es-app/src/views/ViewController.cpp | 326 +++++----- es-app/src/views/ViewController.h | 38 +- .../src/views/gamelist/BasicGameListView.cpp | 95 +-- es-app/src/views/gamelist/BasicGameListView.h | 29 +- .../views/gamelist/DetailedGameListView.cpp | 147 +++-- .../src/views/gamelist/GridGameListView.cpp | 230 +++---- es-app/src/views/gamelist/GridGameListView.h | 34 +- es-app/src/views/gamelist/IGameListView.cpp | 40 +- es-app/src/views/gamelist/IGameListView.h | 11 +- .../views/gamelist/ISimpleGameListView.cpp | 193 +++--- .../src/views/gamelist/ISimpleGameListView.h | 10 +- .../src/views/gamelist/VideoGameListView.cpp | 179 +++--- 78 files changed, 4345 insertions(+), 4308 deletions(-) diff --git a/es-app/src/CollectionSystemsManager.cpp b/es-app/src/CollectionSystemsManager.cpp index cacecc214..1547c9e43 100644 --- a/es-app/src/CollectionSystemsManager.cpp +++ b/es-app/src/CollectionSystemsManager.cpp @@ -21,19 +21,19 @@ #include "CollectionSystemsManager.h" -#include "guis/GuiInfoPopup.h" -#include "utils/FileSystemUtil.h" -#include "utils/StringUtil.h" -#include "utils/TimeUtil.h" -#include "views/gamelist/IGameListView.h" -#include "views/UIModeController.h" -#include "views/ViewController.h" #include "FileData.h" #include "FileFilterIndex.h" #include "Log.h" #include "Settings.h" #include "SystemData.h" #include "ThemeData.h" +#include "guis/GuiInfoPopup.h" +#include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" +#include "utils/TimeUtil.h" +#include "views/UIModeController.h" +#include "views/ViewController.h" +#include "views/gamelist/IGameListView.h" #include #include @@ -41,14 +41,16 @@ std::string myCollectionsName = "collections"; -#define LAST_PLAYED_MAX 50 +#define LAST_PLAYED_MAX 50 // Handles the getting, initialization, deinitialization, // saving and deletion of a CollectionSystemsManager instance. CollectionSystemsManager* CollectionSystemsManager::sInstance = nullptr; -CollectionSystemsManager::CollectionSystemsManager(Window* window) : mWindow(window) +CollectionSystemsManager::CollectionSystemsManager(Window* window) + : mWindow(window) { + // clang-format off CollectionSystemDecl systemDecls[] = { // Type Name Long name Theme folder isCustom { AUTO_ALL_GAMES, "all", "all games", "auto-allgames", false }, @@ -56,13 +58,14 @@ CollectionSystemsManager::CollectionSystemsManager(Window* window) : mWindow(win { AUTO_FAVORITES, "favorites", "favorites", "auto-favorites", false }, { CUSTOM_COLLECTION, myCollectionsName, "collections", "custom-collections", true } }; + // clang-format on // Create a map of the collections. - std::vector tempSystemDecl = std::vector - (systemDecls, systemDecls + sizeof(systemDecls) / sizeof(systemDecls[0])); + std::vector tempSystemDecl = std::vector( + systemDecls, systemDecls + sizeof(systemDecls) / sizeof(systemDecls[0])); for (std::vector::const_iterator it = tempSystemDecl.cbegin(); - it != tempSystemDecl.cend(); it++) + it != tempSystemDecl.cend(); it++) mCollectionSystemDeclsIndex[(*it).name] = (*it); // Setup the standard environment. @@ -95,9 +98,9 @@ CollectionSystemsManager::~CollectionSystemsManager() removeCollectionsFromDisplayedSystems(); // Delete all custom collections. - for (std::map::const_iterator - it = mCustomCollectionSystemsData.cbegin(); - it != mCustomCollectionSystemsData.cend(); it++) + for (std::map::const_iterator it = + mCustomCollectionSystemsData.cbegin(); + it != mCustomCollectionSystemsData.cend(); it++) delete it->second.system; // Delete the custom collections bundle. @@ -105,10 +108,9 @@ CollectionSystemsManager::~CollectionSystemsManager() delete mCustomCollectionsBundle; // Delete the auto collections systems. - for (auto it = mAutoCollectionSystemsData.cbegin(); - it != mAutoCollectionSystemsData.cend(); it++) { + for (auto it = mAutoCollectionSystemsData.cbegin(); // Line break. + it != mAutoCollectionSystemsData.cend(); it++) delete (*it).second.system; - } delete mCollectionEnvData; sInstance = nullptr; @@ -134,10 +136,10 @@ void CollectionSystemsManager::deinit() void CollectionSystemsManager::saveCustomCollection(SystemData* sys) { - const std::string rompath = FileData::getROMDirectory(); + const std::string rompath = FileData::getROMDirectory(); std::string name = sys->getName(); - std::unordered_map - games = sys->getRootFolder()->getChildrenByFilename(); + std::unordered_map games = + sys->getRootFolder()->getChildrenByFilename(); bool found = mCustomCollectionSystemsData.find(name) != mCustomCollectionSystemsData.cend(); if (found) { @@ -155,25 +157,25 @@ void CollectionSystemsManager::saveCustomCollection(SystemData* sys) std::ifstream configFileIn; std::ofstream configFileOut; - #if defined(_WIN64) - configFileIn.open(Utils::String:: - stringToWideString(getCustomCollectionConfigPath(name)).c_str()); - #else +#if defined(_WIN64) + configFileIn.open( + Utils::String::stringToWideString(getCustomCollectionConfigPath(name)).c_str()); +#else configFileIn.open(getCustomCollectionConfigPath(name)); - #endif - for (std::string gameEntry; getline(configFileIn, gameEntry); ) { +#endif + for (std::string gameEntry; getline(configFileIn, gameEntry);) { std::string gamePath = Utils::String::replace(gameEntry, "%ROMPATH%", rompath); gamePath = Utils::String::replace(gamePath, "//", "/"); // Only add the entry if it's not a regular file or a symlink, in other words // only add missing files. if (!Utils::FileSystem::isRegularFile(gamePath) && - !Utils::FileSystem::isSymlink(gamePath)) + !Utils::FileSystem::isSymlink(gamePath)) fileGameEntries.push_back(gameEntry); } configFileIn.close(); - for (std::unordered_map::const_iterator - it = games.cbegin(); it != games.cend(); it++) { + for (std::unordered_map::const_iterator it = games.cbegin(); + it != games.cend(); it++) { std::string path = it->first; // If the ROM path of the game begins with the path from the setting // ROMDirectory (or the default ROM directory), then replace it with %ROMPATH%. @@ -184,17 +186,17 @@ void CollectionSystemsManager::saveCustomCollection(SystemData* sys) } fileGameEntries.insert(fileGameEntries.cend(), activeGameEntries.cbegin(), - activeGameEntries.cend()); + activeGameEntries.cend()); std::sort(fileGameEntries.begin(), fileGameEntries.end()); auto last = std::unique(fileGameEntries.begin(), fileGameEntries.end()); fileGameEntries.erase(last, fileGameEntries.end()); - #if defined(_WIN64) - configFileOut.open(Utils::String:: - stringToWideString(getCustomCollectionConfigPath(name)).c_str()); - #else +#if defined(_WIN64) + configFileOut.open( + Utils::String::stringToWideString(getCustomCollectionConfigPath(name)).c_str()); +#else configFileOut.open(getCustomCollectionConfigPath(name)); - #endif +#endif for (auto it = fileGameEntries.cbegin(); it != fileGameEntries.cend(); it++) configFileOut << (*it) << std::endl; @@ -216,7 +218,7 @@ void CollectionSystemsManager::loadCollectionSystems() initCustomCollectionSystems(); if (Settings::getInstance()->getString("CollectionSystemsAuto") != "" || - Settings::getInstance()->getString("CollectionSystemsCustom") != "") { + Settings::getInstance()->getString("CollectionSystemsCustom") != "") { // Now see which ones are enabled. loadEnabledListFromSettings(); // Add to the main System Vector, and create Views as needed. @@ -228,30 +230,28 @@ void CollectionSystemsManager::loadEnabledListFromSettings() { // We parse the auto collection settings list. std::vector autoSelected = Utils::String::delimitedStringToVector( - Settings::getInstance()->getString("CollectionSystemsAuto"), ",", true); + Settings::getInstance()->getString("CollectionSystemsAuto"), ",", true); // Iterate the map. - for (std::map::iterator - it = mAutoCollectionSystemsData.begin(); - it != mAutoCollectionSystemsData.end(); it++) { - - it->second.isEnabled = (std::find(autoSelected.cbegin(), - autoSelected.cend(), it->first) != autoSelected.cend()); + for (std::map::iterator it = + mAutoCollectionSystemsData.begin(); + it != mAutoCollectionSystemsData.end(); it++) { + it->second.isEnabled = (std::find(autoSelected.cbegin(), autoSelected.cend(), it->first) != + autoSelected.cend()); } mHasEnabledCustomCollection = false; // Parse the custom collection settings list. std::vector customSelected = Utils::String::delimitedStringToVector( - Settings::getInstance()->getString("CollectionSystemsCustom"), ",", true); + Settings::getInstance()->getString("CollectionSystemsCustom"), ",", true); // Iterate the map. - for (std::map::iterator - it = mCustomCollectionSystemsData.begin(); - it != mCustomCollectionSystemsData.end(); it++) { - - it->second.isEnabled = (std::find(customSelected.cbegin(), - customSelected.cend(), it->first) != customSelected.cend()); + for (std::map::iterator it = + mCustomCollectionSystemsData.begin(); + it != mCustomCollectionSystemsData.end(); it++) { + it->second.isEnabled = (std::find(customSelected.cbegin(), customSelected.cend(), + it->first) != customSelected.cend()); if (it->second.isEnabled) mHasEnabledCustomCollection = true; } @@ -269,8 +269,8 @@ void CollectionSystemsManager::updateSystemsList() FileData* rootFolder = mCustomCollectionsBundle->getRootFolder(); // Sort the bundled custom collections. if (rootFolder->getChildren().size() > 0) { - rootFolder->sort(rootFolder->getSortTypeFromString(rootFolder-> - getSortTypeString()), Settings::getInstance()->getBool("FavFirstCustom")); + rootFolder->sort(rootFolder->getSortTypeFromString(rootFolder->getSortTypeString()), + Settings::getInstance()->getBool("FavFirstCustom")); SystemData::sSystemVector.push_back(mCustomCollectionsBundle); } } @@ -279,8 +279,8 @@ void CollectionSystemsManager::updateSystemsList() addEnabledCollectionsToDisplayedSystems(&mAutoCollectionSystemsData); // Create views for collections, before reload. - for (auto sysIt = SystemData::sSystemVector.cbegin(); - sysIt != SystemData::sSystemVector.cend(); sysIt++) { + for (auto sysIt = SystemData::sSystemVector.cbegin(); // Line break. + sysIt != SystemData::sSystemVector.cend(); sysIt++) { if ((*sysIt)->isCollection()) ViewController::get()->getGameListView((*sysIt)); } @@ -292,7 +292,7 @@ void CollectionSystemsManager::updateSystemsList() } void CollectionSystemsManager::refreshCollectionSystems(FileData* file, - bool refreshDisabledAutoCollections) + bool refreshDisabledAutoCollections) { if (!file->getSystem()->isGameSystem() || file->getType() != GAME) return; @@ -311,15 +311,14 @@ void CollectionSystemsManager::refreshCollectionSystems(FileData* file, } std::map allCollections; - allCollections.insert(mAutoCollectionSystemsData.cbegin(), - mAutoCollectionSystemsData.cend()); + allCollections.insert(mAutoCollectionSystemsData.cbegin(), mAutoCollectionSystemsData.cend()); allCollections.insert(mCustomCollectionSystemsData.cbegin(), - mCustomCollectionSystemsData.cend()); + mCustomCollectionSystemsData.cend()); - for (auto sysDataIt = allCollections.cbegin(); - sysDataIt != allCollections.cend(); sysDataIt++) { + for (auto sysDataIt = allCollections.cbegin(); // Line break. + sysDataIt != allCollections.cend(); sysDataIt++) { if (sysDataIt->second.isEnabled || (refreshDisabledAutoCollections && - !sysDataIt->second.system->isGroupedCustomCollection())) + !sysDataIt->second.system->isGroupedCustomCollection())) updateCollectionSystem(file, sysDataIt->second); } } @@ -347,7 +346,7 @@ void CollectionSystemsManager::updateCollectionSystem(FileData* file, Collection mFavoritesSorting = Settings::getInstance()->getBool("FavoritesFirst"); const std::unordered_map& children = - curSys->getRootFolder()->getChildrenByFilename(); + curSys->getRootFolder()->getChildrenByFilename(); bool found = children.find(key) != children.cend(); FileData* rootFolder = curSys->getRootFolder(); @@ -363,32 +362,35 @@ void CollectionSystemsManager::updateCollectionSystem(FileData* file, Collection // Found it, and we are removing it. if (name == "favorites" && file->metadata.get("favorite") == "false") { // Need to check if it is still marked as favorite, if not remove it. - ViewController::get()-> - getGameListView(curSys).get()->remove(collectionEntry, false); + ViewController::get()->getGameListView(curSys).get()->remove(collectionEntry, + false); } else if (name == "recent" && file->metadata.get("lastplayed") == "0") { // If lastplayed is set to 0 it means the entry has been cleared, and the // game should therefore be removed. - ViewController::get()-> - getGameListView(curSys).get()->remove(collectionEntry, false); + ViewController::get()->getGameListView(curSys).get()->remove(collectionEntry, + false); ViewController::get()->onFileChanged(rootFolder, true); } else if (curSys->isCollection() && !file->getCountAsGame()) { // If the countasgame flag has been set to false, then remove the game. if (curSys->isGroupedCustomCollection()) { - ViewController::get()->getGameListView(curSys->getRootFolder()->getParent()-> - getSystem()).get()->remove(collectionEntry, false); + ViewController::get() + ->getGameListView(curSys->getRootFolder()->getParent()->getSystem()) + .get() + ->remove(collectionEntry, false); FileData* parentRootFolder = - rootFolder->getParent()->getSystem()->getRootFolder(); + rootFolder->getParent()->getSystem()->getRootFolder(); parentRootFolder->sort(parentRootFolder->getSortTypeFromString( - parentRootFolder->getSortTypeString()), mFavoritesSorting); + parentRootFolder->getSortTypeString()), + mFavoritesSorting); } else { - ViewController::get()-> - getGameListView(curSys).get()->remove(collectionEntry, false); + ViewController::get()->getGameListView(curSys).get()->remove(collectionEntry, + false); } - rootFolder->sort(rootFolder->getSortTypeFromString( - rootFolder->getSortTypeString()), mFavoritesSorting); + rootFolder->sort(rootFolder->getSortTypeFromString(rootFolder->getSortTypeString()), + mFavoritesSorting); } else { // Re-index with new metadata. @@ -400,12 +402,14 @@ void CollectionSystemsManager::updateCollectionSystem(FileData* file, Collection bool addGame = false; // We didn't find the entry in the collection, so we need to check if we should add it. if ((name == "recent" && file->metadata.get("playcount") > "0" && - file->getCountAsGame() && includeFileInAutoCollections(file)) || - (name == "favorites" && file->metadata.get("favorite") == "true" && - file->getCountAsGame())) + file->getCountAsGame() && includeFileInAutoCollections(file)) || + (name == "favorites" && file->metadata.get("favorite") == "true" && + file->getCountAsGame())) { addGame = true; - else if (name == "all" && file->getCountAsGame()) + } + else if (name == "all" && file->getCountAsGame()) { addGame = true; + } if (addGame) { CollectionFileData* newGame = new CollectionFileData(file, curSys); rootFolder->addChild(newGame); @@ -418,29 +422,28 @@ void CollectionSystemsManager::updateCollectionSystem(FileData* file, Collection rootFolder->sort(rootFolder->getSortTypeFromString("last played, ascending")); } else if (sysData.decl.isCustom && - !Settings::getInstance()->getBool("UseCustomCollectionsSystem")) { + !Settings::getInstance()->getBool("UseCustomCollectionsSystem")) { rootFolder->sort(rootFolder->getSortTypeFromString(rootFolder->getSortTypeString()), - mFavoritesSorting); + mFavoritesSorting); } // If the game doesn't exist in the current system and it's a custom // collection, then skip the sorting. - else if (sysData.decl.isCustom && - children.find(file->getFullPath()) != children.cend()) { + else if (sysData.decl.isCustom && children.find(file->getFullPath()) != children.cend()) { // For custom collections, update either the actual system or its parent depending // on whether the collection is grouped or not. if (rootFolder->getSystem()->isGroupedCustomCollection()) { - rootFolder->getParent()->sort(rootFolder->getParent()-> - getSortTypeFromString(rootFolder->getParent()-> - getSortTypeString()), mFavoritesSorting); + rootFolder->getParent()->sort(rootFolder->getParent()->getSortTypeFromString( + rootFolder->getParent()->getSortTypeString()), + mFavoritesSorting); } else { rootFolder->sort(rootFolder->getSortTypeFromString(rootFolder->getSortTypeString()), - mFavoritesSorting); + mFavoritesSorting); } } else if (!sysData.decl.isCustom) { rootFolder->sort(rootFolder->getSortTypeFromString(rootFolder->getSortTypeString()), - mFavoritesSorting); + mFavoritesSorting); } if (name == "recent") { @@ -453,11 +456,11 @@ void CollectionSystemsManager::updateCollectionSystem(FileData* file, Collection // and therefore jump to the first line. The two seconds is incredibly generous // as normally it would rather be some milliseconds, but who knows what special // circumstances could cause a slight delay so let's keep a large margin. - if (Utils::Time::now() - - Utils::Time::stringToTime(file->metadata.get("lastplayed")) < 2) { + auto nTime = Utils::Time::now(); + if (nTime - Utils::Time::stringToTime(file->metadata.get("lastplayed")) < 2) { // Select the first row of the gamelist (the game just played). - IGameListView* gameList = ViewController::get()-> - getGameListView(getSystemToView(sysData.system)).get(); + IGameListView* gameList = + ViewController::get()->getGameListView(getSystemToView(sysData.system)).get(); gameList->setCursor(gameList->getFirstEntry()); } } @@ -482,22 +485,23 @@ void CollectionSystemsManager::deleteCollectionFiles(FileData* file) // Find games in collection systems. std::map allCollections; - allCollections.insert(mAutoCollectionSystemsData.cbegin(), - mAutoCollectionSystemsData.cend()); + allCollections.insert(mAutoCollectionSystemsData.cbegin(), mAutoCollectionSystemsData.cend()); allCollections.insert(mCustomCollectionSystemsData.cbegin(), - mCustomCollectionSystemsData.cend()); + mCustomCollectionSystemsData.cend()); for (auto sysDataIt = allCollections.begin(); sysDataIt != allCollections.end(); sysDataIt++) { if (sysDataIt->second.isPopulated) { const std::unordered_map& children = - (sysDataIt->second.system)->getRootFolder()->getChildrenByFilename(); + (sysDataIt->second.system)->getRootFolder()->getChildrenByFilename(); bool found = children.find(key) != children.cend(); if (found) { FileData* collectionEntry = children.at(key); SystemData* systemViewToUpdate = getSystemToView(sysDataIt->second.system); - ViewController::get()->getGameListView(systemViewToUpdate).get()-> - remove(collectionEntry, false); + ViewController::get() + ->getGameListView(systemViewToUpdate) + .get() + ->remove(collectionEntry, false); if (sysDataIt->second.decl.isCustom) saveCustomCollection(sysDataIt->second.system); } @@ -516,7 +520,7 @@ bool CollectionSystemsManager::isThemeGenericCollectionCompatible(bool genericCu } bool CollectionSystemsManager::isThemeCustomCollectionCompatible( - std::vector stringVector) + std::vector stringVector) { if (isThemeGenericCollectionCompatible(true)) return true; @@ -543,20 +547,18 @@ std::string CollectionSystemsManager::getValidNewCollectionName(std::string inNa if (index == 0) { size_t remove = std::string::npos; - // Get valid name. while ((remove = name.find_first_not_of( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-[]()' ")) - != std::string::npos) + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-[]()' ")) != + std::string::npos) name.erase(remove, 1); } else { name += " (" + std::to_string(index) + ")"; } - if (name == "") { + if (name == "") name = "new collection"; - } name = Utils::String::toLower(name); @@ -580,13 +582,13 @@ std::string CollectionSystemsManager::getValidNewCollectionName(std::string inNa for (auto sysIt = systemsInUse.cbegin(); sysIt != systemsInUse.cend(); sysIt++) { if (*sysIt == name) { if (index > 0) - name = name.substr(0, name.size()-4); - return getValidNewCollectionName(name, index+1); + name = name.substr(0, name.size() - 4); + return getValidNewCollectionName(name, index + 1); } } // If it matches one of the custom collections reserved names then return it. if (mCollectionSystemDeclsIndex.find(name) != mCollectionSystemDeclsIndex.cend()) - return getValidNewCollectionName(name, index+1); + return getValidNewCollectionName(name, index + 1); return name; } @@ -607,9 +609,11 @@ void CollectionSystemsManager::setEditMode(std::string collectionName, bool show mEditingCollectionSystemData = sysData; if (showPopup) { - GuiInfoPopup* s = new GuiInfoPopup(mWindow, "EDITING THE '" + - Utils::String::toUpper(collectionName) + - "' COLLECTION, ADD/REMOVE GAMES WITH 'Y'", 10000); + GuiInfoPopup* s = + new GuiInfoPopup(mWindow, + "EDITING '" + Utils::String::toUpper(collectionName) + + "' COLLECTION, ADD/REMOVE GAMES WITH 'Y'", + 10000); mWindow->setInfoPopup(s); } @@ -618,8 +622,10 @@ void CollectionSystemsManager::setEditMode(std::string collectionName, bool show void CollectionSystemsManager::exitEditMode(bool showPopup) { if (showPopup) { - GuiInfoPopup* s = new GuiInfoPopup(mWindow, "FINISHED EDITING THE '" + - Utils::String::toUpper(mEditingCollection) + "' COLLECTION", 4000); + GuiInfoPopup* s = new GuiInfoPopup( + mWindow, + "FINISHED EDITING '" + Utils::String::toUpper(mEditingCollection) + "' COLLECTION", + 4000); mWindow->setInfoPopup(s); } @@ -628,26 +634,24 @@ void CollectionSystemsManager::exitEditMode(bool showPopup) mEditingCollection = "Favorites"; // Remove all tick marks from the games that are part of the collection. - for (auto it = SystemData::sSystemVector.begin(); - it != SystemData::sSystemVector.end(); it++) { + for (auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); it++) { ViewController::get()->getGameListView((*it))->onFileChanged( - ViewController::get()->getGameListView((*it))->getCursor(), false); + ViewController::get()->getGameListView((*it))->getCursor(), false); } mEditingCollectionSystemData->system->onMetaDataSavePoint(); } -bool CollectionSystemsManager::inCustomCollection( - const std::string& collectionName, FileData* gameFile) +bool CollectionSystemsManager::inCustomCollection(const std::string& collectionName, + FileData* gameFile) { auto collectionEntry = mCustomCollectionSystemsData.find(collectionName); if (collectionEntry != mCustomCollectionSystemsData.end()) { const std::unordered_map& children = - collectionEntry->second.system->getRootFolder()->getChildrenByFilename(); + collectionEntry->second.system->getRootFolder()->getChildrenByFilename(); return children.find(gameFile->getFullPath()) != children.cend(); } - return false; } @@ -666,8 +670,8 @@ bool CollectionSystemsManager::toggleGameInCollection(FileData* file) std::string key = file->getFullPath(); FileData* rootFolder = sysData->getRootFolder(); - const std::unordered_map& - children = rootFolder->getChildrenByFilename(); + const std::unordered_map& children = + rootFolder->getChildrenByFilename(); bool found = children.find(key) != children.cend(); FileFilterIndex* fileIndex = sysData->getIndex(); std::string name = sysData->getName(); @@ -678,11 +682,13 @@ bool CollectionSystemsManager::toggleGameInCollection(FileData* file) adding = false; // If we found it, we need to remove it. FileData* collectionEntry = children.at(key); - ViewController::get()->getGameListView(systemViewToUpdate).get()-> - remove(collectionEntry, false); - systemViewToUpdate->getRootFolder()->sort(rootFolder->getSortTypeFromString( - rootFolder->getSortTypeString()), - Settings::getInstance()->getBool("FavFirstCustom")); + ViewController::get() + ->getGameListView(systemViewToUpdate) + .get() + ->remove(collectionEntry, false); + systemViewToUpdate->getRootFolder()->sort( + rootFolder->getSortTypeFromString(rootFolder->getSortTypeString()), + Settings::getInstance()->getBool("FavFirstCustom")); ViewController::get()->reloadGameListView(systemViewToUpdate); updateCollectionFolderMetadata(systemViewToUpdate); @@ -692,9 +698,9 @@ bool CollectionSystemsManager::toggleGameInCollection(FileData* file) CollectionFileData* newGame = new CollectionFileData(file, sysData); rootFolder->addChild(newGame); - systemViewToUpdate->getRootFolder()->sort(rootFolder->getSortTypeFromString( - rootFolder->getSortTypeString()), - Settings::getInstance()->getBool("FavFirstCustom")); + systemViewToUpdate->getRootFolder()->sort( + rootFolder->getSortTypeFromString(rootFolder->getSortTypeString()), + Settings::getInstance()->getBool("FavFirstCustom")); ViewController::get()->onFileChanged(systemViewToUpdate->getRootFolder(), true); fileIndex->addToIndex(newGame); @@ -706,7 +712,7 @@ bool CollectionSystemsManager::toggleGameInCollection(FileData* file) } else { file->getSourceFileData()->getSystem()->getIndex()->removeFromIndex( - file->getSourceFileData()); + file->getSourceFileData()); MetaDataList* md = &file->getSourceFileData()->metadata; std::string value = md->get("favorite"); if (value == "false") { @@ -718,21 +724,27 @@ bool CollectionSystemsManager::toggleGameInCollection(FileData* file) } file->getSourceFileData()->getSystem()->getIndex()->addToIndex( - file->getSourceFileData()); + file->getSourceFileData()); file->getSourceFileData()->getSystem()->onMetaDataSavePoint(); refreshCollectionSystems(file->getSourceFileData()); if (mAutoCollectionSystemsData["favorites"].isEnabled) - ViewController::get()-> - reloadGameListView(mAutoCollectionSystemsData["favorites"].system); + ViewController::get()->reloadGameListView( + mAutoCollectionSystemsData["favorites"].system); + } + if (adding) { + s = new GuiInfoPopup( + mWindow, + "ADDED '" + Utils::String::toUpper(Utils::String::removeParenthesis(name)) + + "' TO '" + Utils::String::toUpper(sysName) + "'", + 4000); + } + else { + s = new GuiInfoPopup( + mWindow, + "REMOVED '" + Utils::String::toUpper(Utils::String::removeParenthesis(name)) + + "' FROM '" + Utils::String::toUpper(sysName) + "'", + 4000); } - if (adding) - s = new GuiInfoPopup(mWindow, "ADDED '" + - Utils::String::toUpper(Utils::String::removeParenthesis(name)) + - "' TO '" + Utils::String::toUpper(sysName) + "'", 4000); - else - s = new GuiInfoPopup(mWindow, "REMOVED '" + - Utils::String::toUpper(Utils::String::removeParenthesis(name)) + - "' FROM '" + Utils::String::toUpper(sysName) + "'", 4000); mWindow->setInfoPopup(s); return true; } @@ -745,8 +757,8 @@ SystemData* CollectionSystemsManager::getSystemToView(SystemData* sys) FileData* rootFolder = sys->getRootFolder(); FileData* bundleRootFolder = mCustomCollectionsBundle->getRootFolder(); - const std::unordered_map& - bundleChildren = bundleRootFolder->getChildrenByFilename(); + const std::unordered_map& bundleChildren = + bundleRootFolder->getChildrenByFilename(); // Is the rootFolder bundled in the "My Collections" system? bool sysFoundInBundle = bundleChildren.find(rootFolder->getKey()) != bundleChildren.cend(); @@ -780,7 +792,7 @@ FileData* CollectionSystemsManager::updateCollectionFolderMetadata(SystemData* s if (gameCount > 1) { std::random_device randDev; // Mersenne Twister pseudorandom number generator. - std::mt19937 engine{randDev()}; + std::mt19937 engine { randDev() }; unsigned int target; for (unsigned int i = 0; i < 3; i++) { @@ -800,48 +812,54 @@ FileData* CollectionSystemsManager::updateCollectionFolderMetadata(SystemData* s if (gameCount > 0) { if (Settings::getInstance()->getBool("CollectionShowSystemInfo")) { switch (gameCount) { - case 1: + case 1: { desc = "This collection contains 1 game: '" + - gamesList[0]->metadata.get("name") + " [" + - gamesList[0]->getSourceFileData()->getSystem()->getName() + "]'"; - break; - case 2: + gamesList[0]->metadata.get("name") + " [" + + gamesList[0]->getSourceFileData()->getSystem()->getName() + "]'"; + break; + } + case 2: { desc = "This collection contains 2 games: '" + - gamesList[0]->metadata.get("name") + " [" + - gamesList[0]->getSourceFileData()->getSystem()->getName() + - "]' and '" + gamesList[1]->metadata.get("name") + " [" + - gamesList[1]->getSourceFileData()->getSystem()->getName() + "]'"; - break; - default: - desc = "This collection contains " + std::to_string(gameCount) + - " games: '" + gamesList[0]->metadata.get("name") + - " [" + gamesList[0]->getSourceFileData()->getSystem()->getName() + - "]', '" + gamesList[1]->metadata.get("name") + " [" + - gamesList[1]->getSourceFileData()->getSystem()->getName() + "]' and '" + - gamesList[2]->metadata.get("name") + " [" + - gamesList[2]->getSourceFileData()->getSystem()->getName() + "]'"; + gamesList[0]->metadata.get("name") + " [" + + gamesList[0]->getSourceFileData()->getSystem()->getName() + "]' and '" + + gamesList[1]->metadata.get("name") + " [" + + gamesList[1]->getSourceFileData()->getSystem()->getName() + "]'"; + break; + } + default: { + desc = "This collection contains " + std::to_string(gameCount) + " games: '" + + gamesList[0]->metadata.get("name") + " [" + + gamesList[0]->getSourceFileData()->getSystem()->getName() + "]', '" + + gamesList[1]->metadata.get("name") + " [" + + gamesList[1]->getSourceFileData()->getSystem()->getName() + "]' and '" + + gamesList[2]->metadata.get("name") + " [" + + gamesList[2]->getSourceFileData()->getSystem()->getName() + "]'"; desc += (gameCount == 3 ? "" : ", among others"); - break; + break; + } } } else { switch (gameCount) { - case 1: + case 1: { desc = "This collection contains 1 game: '" + - gamesList[0]->metadata.get("name") + " '"; - break; - case 2: + gamesList[0]->metadata.get("name") + " '"; + break; + } + case 2: { desc = "This collection contains 2 games: '" + - gamesList[0]->metadata.get("name") + - "' and '" + gamesList[1]->metadata.get("name") + "'"; - break; - default: - desc = "This collection contains " + std::to_string(gameCount) + - " games: '" + gamesList[0]->metadata.get("name") + - "', '" + gamesList[1]->metadata.get("name") + "' and '" + - gamesList[2]->metadata.get("name") + "'"; + gamesList[0]->metadata.get("name") + "' and '" + + gamesList[1]->metadata.get("name") + "'"; + break; + } + default: { + desc = "This collection contains " + std::to_string(gameCount) + " games: '" + + gamesList[0]->metadata.get("name") + "', '" + + gamesList[1]->metadata.get("name") + "' and '" + + gamesList[2]->metadata.get("name") + "'"; desc += (gameCount == 3 ? "" : ", among others"); - break; + break; + } } } } @@ -877,7 +895,7 @@ std::vector CollectionSystemsManager::getUnusedSystemsFromTheme() systemsInUse.insert(systemsInUse.cend(), customSys.cbegin(), customSys.cend()); systemsInUse.insert(systemsInUse.cend(), userSys.cbegin(), userSys.cend()); - for (auto sysIt = themeSys.cbegin(); sysIt != themeSys.cend(); ) { + for (auto sysIt = themeSys.cbegin(); sysIt != themeSys.cend();) { if (std::find(systemsInUse.cbegin(), systemsInUse.cend(), *sysIt) != systemsInUse.cend()) sysIt = themeSys.erase(sysIt); else @@ -920,15 +938,17 @@ void CollectionSystemsManager::deleteCustomCollection(std::string collectionName std::string configFile = getCustomCollectionConfigPath(collectionName); Utils::FileSystem::removeFile(configFile); LOG(LogDebug) << "CollectionSystemsManager::deleteCustomCollection(): Deleted the " - "configuration file '" << configFile << "'."; + "configuration file '" + << configFile << "'."; - GuiInfoPopup* s = new GuiInfoPopup(mWindow, "DELETED THE COLLECTION '" + - Utils::String::toUpper(collectionName) + "'", 5000); + GuiInfoPopup* s = new GuiInfoPopup( + mWindow, "DELETED COLLECTION '" + Utils::String::toUpper(collectionName) + "'", + 5000); mWindow->setInfoPopup(s); } else { LOG(LogError) << "Attempted to delete custom collection '" + collectionName + "' " + - "which doesn't exist."; + "which doesn't exist."; } } @@ -941,16 +961,16 @@ void CollectionSystemsManager::reactivateCustomCollectionEntry(FileData* game) // matching entries for the game passed as the parameter. If so, then enable it in each // of those collections. This is done also for disabled collections, as otherwise the // game would be missing if the collection was enabled during the program session. - for (std::map::const_iterator - it = mCustomCollectionSystemsData.cbegin(); - it != mCustomCollectionSystemsData.cend(); it++) { + for (std::map::const_iterator it = + mCustomCollectionSystemsData.cbegin(); + it != mCustomCollectionSystemsData.cend(); it++) { std::string path = getCustomCollectionConfigPath(it->first); if (Utils::FileSystem::exists(path)) { - #if defined(_WIN64) +#if defined(_WIN64) std::ifstream input(Utils::String::stringToWideString(path).c_str()); - #else +#else std::ifstream input(path); - #endif +#endif for (std::string gameKey; getline(input, gameKey);) { if (gameKey == gamePath) { setEditMode(it->first, false); @@ -966,15 +986,16 @@ void CollectionSystemsManager::reactivateCustomCollectionEntry(FileData* game) void CollectionSystemsManager::repopulateCollection(SystemData* sysData) { - for (auto it = mAutoCollectionSystemsData.cbegin(); - it != mAutoCollectionSystemsData.cend(); it++) { + for (auto it = mAutoCollectionSystemsData.cbegin(); // Line break. + it != mAutoCollectionSystemsData.cend(); it++) { if ((*it).second.system == sysData) { LOG(LogDebug) << "CollectionSystemsManager::repopulateCollection(): " - "Repopulating auto collection \"" << it->first << "\""; + "Repopulating auto collection \"" + << it->first << "\""; CollectionSystemData* autoSystem = &mAutoCollectionSystemsData[it->first]; std::vector systemEntries = - autoSystem->system->getRootFolder()->getFilesRecursive(true, true, false); + autoSystem->system->getRootFolder()->getFilesRecursive(true, true, false); // Flag the collection as not populated so it gets repopulated. autoSystem->isPopulated = false; @@ -1005,22 +1026,23 @@ void CollectionSystemsManager::repopulateCollection(SystemData* sysData) autoView->setCursor(autoView->getLastEntry()); } else { - autoView->setCursor(autoSystem->system->getRootFolder()-> - getChildrenRecursive().front()); + autoView->setCursor( + autoSystem->system->getRootFolder()->getChildrenRecursive().front()); autoView->setCursor(autoView->getFirstEntry()); } } } - for (auto it = mCustomCollectionSystemsData.cbegin(); - it != mCustomCollectionSystemsData.cend(); it++) { + for (auto it = mCustomCollectionSystemsData.cbegin(); // Line break. + it != mCustomCollectionSystemsData.cend(); it++) { if ((*it).second.system == sysData) { LOG(LogDebug) << "CollectionSystemsManager::repopulateCollection(): " - "Repopulating custom collection '" << it->first << "'."; + "Repopulating custom collection '" + << it->first << "'."; CollectionSystemData* customSystem = &mCustomCollectionSystemsData[it->first]; std::vector systemEntries = - customSystem->system->getRootFolder()->getFilesRecursive(true, true, false); + customSystem->system->getRootFolder()->getFilesRecursive(true, true, false); if (systemEntries.empty()) return; @@ -1035,8 +1057,8 @@ void CollectionSystemsManager::repopulateCollection(SystemData* sysData) populateCustomCollection(customSystem); auto autoView = ViewController::get()->getGameListView(customSystem->system).get(); - autoView->setCursor(customSystem->system->getRootFolder()-> - getChildrenRecursive().front()); + autoView->setCursor( + customSystem->system->getRootFolder()->getChildrenRecursive().front()); autoView->setCursor(autoView->getFirstEntry()); } } @@ -1044,9 +1066,9 @@ void CollectionSystemsManager::repopulateCollection(SystemData* sysData) void CollectionSystemsManager::initAutoCollectionSystems() { - for (std::map::const_iterator - it = mCollectionSystemDeclsIndex.cbegin(); - it != mCollectionSystemDeclsIndex.cend(); it++) { + for (std::map::const_iterator it = + mCollectionSystemDeclsIndex.cbegin(); + it != mCollectionSystemDeclsIndex.cend(); it++) { CollectionSystemDecl sysDecl = it->second; if (!sysDecl.isCustom) @@ -1071,11 +1093,13 @@ SystemData* CollectionSystemsManager::getAllGamesCollection() return allSysData->system; } -SystemData* CollectionSystemsManager::createNewCollectionEntry( - std::string name, CollectionSystemDecl sysDecl, bool index, bool custom) +SystemData* CollectionSystemsManager::createNewCollectionEntry(std::string name, + CollectionSystemDecl sysDecl, + bool index, + bool custom) { - SystemData* newSys = new SystemData(name, sysDecl.longName, - mCollectionEnvData, sysDecl.themeFolder, true, custom); + SystemData* newSys = new SystemData(name, sysDecl.longName, mCollectionEnvData, + sysDecl.themeFolder, true, custom); CollectionSystemData newCollectionData; newCollectionData.system = newSys; @@ -1099,8 +1123,8 @@ void CollectionSystemsManager::populateAutoCollection(CollectionSystemData* sysD CollectionSystemDecl sysDecl = sysData->decl; FileData* rootFolder = newSys->getRootFolder(); FileFilterIndex* index = newSys->getIndex(); - for (auto sysIt = SystemData::sSystemVector.cbegin(); - sysIt != SystemData::sSystemVector.cend(); sysIt++) { + for (auto sysIt = SystemData::sSystemVector.cbegin(); // Line break. + sysIt != SystemData::sSystemVector.cend(); sysIt++) { // We won't iterate all collections. if ((*sysIt)->isGameSystem() && !(*sysIt)->isCollection()) { std::vector files = (*sysIt)->getRootFolder()->getFilesRecursive(GAME); @@ -1108,16 +1132,19 @@ void CollectionSystemsManager::populateAutoCollection(CollectionSystemData* sysD bool include = includeFileInAutoCollections((*gameIt)); switch (sysDecl.type) { - case AUTO_LAST_PLAYED: + case AUTO_LAST_PLAYED: { include = include && (*gameIt)->metadata.get("playcount") > "0"; break; - case AUTO_FAVORITES: + } + case AUTO_FAVORITES: { // We may still want to add files we don't want in auto collections // to "favorites". include = (*gameIt)->metadata.get("favorite") == "true"; break; - default: + } + default: { break; + } } if (include) { @@ -1132,11 +1159,12 @@ void CollectionSystemsManager::populateAutoCollection(CollectionSystemData* sysD } } } + if (rootFolder->getName() == "recent") rootFolder->sort(rootFolder->getSortTypeFromString("last played, ascending")); else rootFolder->sort(rootFolder->getSortTypeFromString(rootFolder->getSortTypeString()), - Settings::getInstance()->getBool("FavoritesFirst")); + Settings::getInstance()->getBool("FavoritesFirst")); if (sysDecl.type == AUTO_LAST_PLAYED) trimCollectionCount(rootFolder, LAST_PLAYED_MAX); @@ -1148,12 +1176,14 @@ void CollectionSystemsManager::populateAutoCollection(CollectionSystemData* sysD // The following is needed to avoid a crash when repopulating the system as the previous // cursor pointer may point to a random memory address. auto recentGamelist = ViewController::get()->getGameListView(rootFolder->getSystem()).get(); - recentGamelist->setCursor(rootFolder->getSystem()->getRootFolder()-> - getChildrenRecursive().front()); + recentGamelist->setCursor( + rootFolder->getSystem()->getRootFolder()->getChildrenRecursive().front()); recentGamelist->setCursor(recentGamelist->getFirstEntry()); if (rootFolder->getChildren().size() > 0) - ViewController::get()->getGameListView(rootFolder->getSystem()).get()-> - onFileChanged(rootFolder->getChildren().front(), false); + ViewController::get() + ->getGameListView(rootFolder->getSystem()) + .get() + ->onFileChanged(rootFolder->getChildren().front(), false); } sysData->isPopulated = true; @@ -1176,15 +1206,16 @@ void CollectionSystemsManager::populateCustomCollection(CollectionSystemData* sy FileFilterIndex* index = newSys->getIndex(); // Get configuration for this custom collection. - #if defined (_WIN64) + +#if defined(_WIN64) std::ifstream input(Utils::String::stringToWideString(path).c_str()); - #else +#else std::ifstream input(path); - #endif +#endif // Get all files map. - std::unordered_map - allFilesMap = getAllGamesCollection()->getRootFolder()->getChildrenByFilename(); + std::unordered_map allFilesMap = + getAllGamesCollection()->getRootFolder()->getChildrenByFilename(); // Get the ROM directory, either as configured in es_settings.xml, or if no value // is set there, then use the default hardcoded path. @@ -1200,15 +1231,16 @@ void CollectionSystemsManager::populateCustomCollection(CollectionSystemData* sy gameKey = Utils::String::replace(gameKey, "%ROMPATH%", rompath); gameKey = Utils::String::replace(gameKey, "//", "/"); - std::unordered_map::const_iterator it = allFilesMap.find(gameKey); + std::unordered_map::const_iterator it = allFilesMap.find(gameKey); if (it != allFilesMap.cend()) { CollectionFileData* newGame = new CollectionFileData(it->second, newSys); rootFolder->addChild(newGame); index->addToIndex(newGame); } else { - LOG(LogWarning) << "File \"" << gameKey << - "\" does not exist, is hidden, or is not counted as a game, ignoring entry"; + LOG(LogWarning) + << "File \"" << gameKey + << "\" does not exist, is hidden, or is not counted as a game, ignoring entry"; } } @@ -1220,7 +1252,7 @@ void CollectionSystemsManager::removeCollectionsFromDisplayedSystems() { // Remove all collection Systems. for (auto sysIt = SystemData::sSystemVector.cbegin(); - sysIt != SystemData::sSystemVector.cend(); ) { + sysIt != SystemData::sSystemVector.cend();) { if ((*sysIt)->isCollection()) sysIt = SystemData::sSystemVector.erase(sysIt); else @@ -1241,11 +1273,12 @@ void CollectionSystemsManager::removeCollectionsFromDisplayedSystems() } void CollectionSystemsManager::addEnabledCollectionsToDisplayedSystems( - std::map* colSystemData) + std::map* colSystemData) { // Add auto enabled collections. - for (std::map::iterator - it = colSystemData->begin(); it != colSystemData->end(); it++) { + for (std::map::iterator it = + colSystemData->begin(); + it != colSystemData->end(); it++) { if (it->second.isEnabled) { // Check if populated, otherwise populate. if (!it->second.isPopulated) { @@ -1256,19 +1289,19 @@ void CollectionSystemsManager::addEnabledCollectionsToDisplayedSystems( } // Check if it has its own view. if (!it->second.decl.isCustom || themeFolderExists(it->first) || - !Settings::getInstance()->getBool("UseCustomCollectionsSystem")) { + !Settings::getInstance()->getBool("UseCustomCollectionsSystem")) { // Theme folder exists, or we chose not to bundle it under the // custom-collections system. So we need to create a view. SystemData::sSystemVector.push_back(it->second.system); // If this is a non-bundled custom collection, then sort it. if (it->second.decl.isCustom == true) { FileData* rootFolder = it->second.system->getRootFolder(); - rootFolder->sort(rootFolder->getSortTypeFromString( - rootFolder->getSortTypeString()), - Settings::getInstance()->getBool("FavFirstCustom")); + rootFolder->sort( + rootFolder->getSortTypeFromString(rootFolder->getSortTypeString()), + Settings::getInstance()->getBool("FavFirstCustom")); // Jump to the first row of the game list, assuming it's not empty. - IGameListView* gameList = ViewController::get()-> - getGameListView((it->second.system)).get(); + IGameListView* gameList = + ViewController::get()->getGameListView((it->second.system)).get(); if (!gameList->getCursor()->isPlaceHolder()) { gameList->setCursor(gameList->getFirstEntry()); } @@ -1294,11 +1327,11 @@ std::vector CollectionSystemsManager::getSystemsFromConfig() return systems; pugi::xml_document doc; - #if defined(_WIN64) +#if defined(_WIN64) pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str()); - #else +#else pugi::xml_parse_result res = doc.load_file(path.c_str()); - #endif +#endif if (!res) return systems; @@ -1309,8 +1342,8 @@ std::vector CollectionSystemsManager::getSystemsFromConfig() if (!systemList) return systems; - for (pugi::xml_node system = systemList.child("system"); - system; system = system.next_sibling("system")) { + for (pugi::xml_node system = systemList.child("system"); system; + system = system.next_sibling("system")) { // Theme folder. std::string themeFolder = system.child("theme").text().get(); systems.push_back(themeFolder); @@ -1328,8 +1361,8 @@ std::vector CollectionSystemsManager::getSystemsFromTheme() if (themeSets.empty()) return systems; // No theme sets available. - std::map::const_iterator - set = themeSets.find(Settings::getInstance()->getString("ThemeSet")); + std::map::const_iterator set = + themeSets.find(Settings::getInstance()->getString("ThemeSet")); if (set == themeSets.cend()) { // Currently selected theme set is missing, so just pick the first available set. set = themeSets.cbegin(); @@ -1341,12 +1374,12 @@ std::vector CollectionSystemsManager::getSystemsFromTheme() if (Utils::FileSystem::exists(themePath)) { Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(themePath); - for (Utils::FileSystem::stringList::const_iterator - it = dirContent.cbegin(); it != dirContent.cend(); it++) { + for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); + it != dirContent.cend(); it++) { if (Utils::FileSystem::isDirectory(*it)) { // ... here you have a directory. std::string folder = *it; - folder = folder.substr(themePath.size()+1); + folder = folder.substr(themePath.size() + 1); if (Utils::FileSystem::exists(set->second.getThemePath(folder))) systems.push_back(folder); @@ -1362,24 +1395,22 @@ std::vector CollectionSystemsManager::getCollectionsFromConfigFolde std::vector systems; std::string configPath = getCollectionsFolder(); - if (Utils::FileSystem::exists(configPath)) - { - Utils::FileSystem::stringList dirContent = - Utils::FileSystem::getDirContent(configPath); - for (Utils::FileSystem::stringList::const_iterator - it = dirContent.cbegin(); it != dirContent.cend(); it++) { + if (Utils::FileSystem::exists(configPath)) { + Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(configPath); + for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); + it != dirContent.cend(); it++) { if (Utils::FileSystem::isRegularFile(*it)) { // It's a file. std::string filename = Utils::FileSystem::getFileName(*it); // Need to confirm filename matches config format. - if (filename != "custom-.cfg" && Utils::String::startsWith( - filename, "custom-") && Utils::String::endsWith(filename, ".cfg")) { - filename = filename.substr(7, filename.size()-11); + if (filename != "custom-.cfg" && Utils::String::startsWith(filename, "custom-") && + Utils::String::endsWith(filename, ".cfg")) { + filename = filename.substr(7, filename.size() - 11); systems.push_back(filename); } else { LOG(LogInfo) << "Found non-collection config file in collections folder: " - << filename; + << filename; } } } @@ -1390,9 +1421,9 @@ std::vector CollectionSystemsManager::getCollectionsFromConfigFolde std::vector CollectionSystemsManager::getCollectionThemeFolders(bool custom) { std::vector systems; - for (std::map::const_iterator - it = mCollectionSystemDeclsIndex.cbegin(); - it != mCollectionSystemDeclsIndex.cend(); it++) { + for (std::map::const_iterator it = + mCollectionSystemDeclsIndex.cbegin(); + it != mCollectionSystemDeclsIndex.cend(); it++) { CollectionSystemDecl sysDecl = it->second; if (sysDecl.isCustom == custom) systems.push_back(sysDecl.themeFolder); @@ -1403,9 +1434,9 @@ std::vector CollectionSystemsManager::getCollectionThemeFolders(boo std::vector CollectionSystemsManager::getUserCollectionThemeFolders() { std::vector systems; - for (std::map::const_iterator - it = mCustomCollectionSystemsData.cbegin(); - it != mCustomCollectionSystemsData.cend(); it++) + for (std::map::const_iterator it = + mCustomCollectionSystemsData.cbegin(); + it != mCustomCollectionSystemsData.cend(); it++) systems.push_back(it->second.decl.themeFolder); return systems; } @@ -1415,7 +1446,7 @@ void CollectionSystemsManager::trimCollectionCount(FileData* rootFolder, int lim SystemData* curSys = rootFolder->getSystem(); while (static_cast(rootFolder->getChildrenListToDisplay().size()) > limit) { CollectionFileData* gameToRemove = - (CollectionFileData*)rootFolder->getChildrenListToDisplay().back(); + (CollectionFileData*)rootFolder->getChildrenListToDisplay().back(); ViewController::get()->getGameListView(curSys).get()->remove(gameToRemove, false); } } @@ -1442,5 +1473,5 @@ std::string CollectionSystemsManager::getCustomCollectionConfigPath(std::string std::string CollectionSystemsManager::getCollectionsFolder() { return Utils::FileSystem::getGenericPath(Utils::FileSystem::getHomePath() + - "/.emulationstation/collections"); + "/.emulationstation/collections"); } diff --git a/es-app/src/CollectionSystemsManager.h b/es-app/src/CollectionSystemsManager.h index 051a8dfdd..df976ef93 100644 --- a/es-app/src/CollectionSystemsManager.h +++ b/es-app/src/CollectionSystemsManager.h @@ -34,7 +34,7 @@ class Window; struct SystemEnvironmentData; enum CollectionSystemType { - AUTO_ALL_GAMES, + AUTO_ALL_GAMES, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). AUTO_LAST_PLAYED, AUTO_FAVORITES, CUSTOM_COLLECTION @@ -115,13 +115,17 @@ public: // Repopulate the collection, which is basically a forced update of its complete content. void repopulateCollection(SystemData* sysData); - inline std::map - getAutoCollectionSystems() { return mAutoCollectionSystemsData; }; - inline std::map - getCustomCollectionSystems() { return mCustomCollectionSystemsData; }; - inline SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; }; - inline bool isEditing() { return mIsEditingCustom; }; - inline std::string getEditingCollection() { return mEditingCollection; }; + std::map getAutoCollectionSystems() + { + return mAutoCollectionSystemsData; + } + std::map getCustomCollectionSystems() + { + return mCustomCollectionSystemsData; + } + SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; } + bool isEditing() { return mIsEditingCustom; } + std::string getEditingCollection() { return mEditingCollection; } private: static CollectionSystemsManager* sInstance; @@ -143,7 +147,9 @@ private: SystemData* getAllGamesCollection(); // Create a new empty collection system based on the name and declaration. SystemData* createNewCollectionEntry(std::string name, - CollectionSystemDecl sysDecl, bool index = true, bool custom = false); + CollectionSystemDecl sysDecl, + bool index = true, + bool custom = false); // Populate an automatic collection system. void populateAutoCollection(CollectionSystemData* sysData); // Populate a custom collection system. @@ -151,8 +157,8 @@ private: // Functions to handle System View removal and insertion of collections: void removeCollectionsFromDisplayedSystems(); - void addEnabledCollectionsToDisplayedSystems(std::map* colSystemData); + void addEnabledCollectionsToDisplayedSystems( + std::map* colSystemData); // Auxiliary functions: std::vector getSystemsFromConfig(); diff --git a/es-app/src/EmulationStation.h b/es-app/src/EmulationStation.h index bdb50ed76..49c986296 100644 --- a/es-app/src/EmulationStation.h +++ b/es-app/src/EmulationStation.h @@ -10,14 +10,16 @@ // These numbers and strings need to be manually updated for a new version. // Do this version number update as the very last commit for the new release version. +// clang-format off #define PROGRAM_VERSION_MAJOR 1 -#define PROGRAM_VERSION_MINOR 0 +#define PROGRAM_VERSION_MINOR 1 #define PROGRAM_VERSION_MAINTENANCE 0 +// clang-format on #define PROGRAM_VERSION_STRING "1.1.0-rc-dev" #define PROGRAM_BUILT_STRING __DATE__ " - " __TIME__ #define RESOURCE_VERSION_STRING "1,1,0\0" -#define RESOURCE_VERSION PROGRAM_VERSION_MAJOR,PROGRAM_VERSION_MINOR,PROGRAM_VERSION_MAINTENANCE +#define RESOURCE_VERSION PROGRAM_VERSION_MAJOR, PROGRAM_VERSION_MINOR, PROGRAM_VERSION_MAINTENANCE #endif // ES_APP_EMULATION_STATION_H diff --git a/es-app/src/FileData.cpp b/es-app/src/FileData.cpp index 22fcdfee6..92fff23ff 100644 --- a/es-app/src/FileData.cpp +++ b/es-app/src/FileData.cpp @@ -10,12 +10,6 @@ #include "FileData.h" -#include "guis/GuiInfoPopup.h" -#include "utils/FileSystemUtil.h" -#include "utils/StringUtil.h" -#include "utils/TimeUtil.h" -#include "views/UIModeController.h" -#include "views/ViewController.h" #include "AudioManager.h" #include "CollectionSystemsManager.h" #include "FileFilterIndex.h" @@ -26,33 +20,37 @@ #include "Scripting.h" #include "SystemData.h" #include "Window.h" +#include "guis/GuiInfoPopup.h" +#include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" +#include "utils/TimeUtil.h" +#include "views/UIModeController.h" +#include "views/ViewController.h" #include -FileData::FileData( - FileType type, - const std::string& path, - SystemEnvironmentData* envData, - SystemData* system) - : mType(type), - mPath(path), - mSystem(system), - mEnvData(envData), - mSourceFileData(nullptr), - mParent(nullptr), - mOnlyFolders(false), - mDeletionFlag(false), - // Metadata is set in the constructor. - metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA) +FileData::FileData(FileType type, + const std::string& path, + SystemEnvironmentData* envData, + SystemData* system) + : mType(type) + , mPath(path) + , mSystem(system) + , mEnvData(envData) + , mSourceFileData(nullptr) + , mParent(nullptr) + , mOnlyFolders(false) + , mDeletionFlag(false) + // Metadata is set in the constructor. + , metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA) { // Metadata needs at least a name field (since that's what getName() will return). if (metadata.get("name").empty()) { if ((system->hasPlatformId(PlatformIds::ARCADE) || - system->hasPlatformId(PlatformIds::SNK_NEO_GEO)) && - metadata.getType() != FOLDER_METADATA) { + system->hasPlatformId(PlatformIds::SNK_NEO_GEO)) && + metadata.getType() != FOLDER_METADATA) { // If it's a MAME or Neo Geo game, expand the game name accordingly. - metadata.set("name", - MameNames::getInstance()->getCleanName(getCleanName())); + metadata.set("name", MameNames::getInstance()->getCleanName(getCleanName())); } else { if (metadata.getType() == FOLDER_METADATA && Utils::FileSystem::isHidden(mPath)) { @@ -89,6 +87,7 @@ std::string FileData::getCleanName() const const std::string& FileData::getName() { + // Return metadata name. return metadata.get("name"); } @@ -144,14 +143,13 @@ const std::vector FileData::getChildrenRecursive() const { std::vector childrenRecursive; - for (auto it = mChildrenByFilename.cbegin(); - it != mChildrenByFilename.cend(); it++) { + for (auto it = mChildrenByFilename.cbegin(); it != mChildrenByFilename.cend(); it++) { childrenRecursive.push_back((*it).second); // Recurse through any subdirectories. if ((*it).second->getType() == FOLDER) { std::vector childrenSubdirectory = (*it).second->getChildrenRecursive(); - childrenRecursive.insert(childrenRecursive.end(), - childrenSubdirectory.begin(), childrenSubdirectory.end()); + childrenRecursive.insert(childrenRecursive.end(), childrenSubdirectory.begin(), + childrenSubdirectory.end()); } } @@ -171,13 +169,13 @@ const std::string FileData::getROMDirectory() // Expand home path if ~ is used. romDirPath = Utils::FileSystem::expandHomePath(romDirPath); - #if defined(_WIN64) - if (romDirPath.back() != '\\') +#if defined(_WIN64) + if (romDirPath.back() != '\\') romDirPath = romDirPath + "\\"; - #else - if (romDirPath.back() != '/') +#else + if (romDirPath.back() != '/') romDirPath = romDirPath + "/"; - #endif +#endif } // If %ESPATH% is used for the ROM path configuration, then expand it to the binary @@ -202,10 +200,10 @@ const std::string FileData::getMediaDirectory() // If %ESPATH% is used for the media directory configuration, then expand it to the // binary directory of ES-DE. - mediaDirPath = Utils::String::replace( - mediaDirPath, "%ESPATH%", Utils::FileSystem::getExePath()); + mediaDirPath = + Utils::String::replace(mediaDirPath, "%ESPATH%", Utils::FileSystem::getExePath()); - if (mediaDirPath.back() != '/') + if (mediaDirPath.back() != '/') mediaDirPath = mediaDirPath + "/"; } @@ -219,11 +217,11 @@ const std::string FileData::getMediafilePath(std::string subdirectory, std::stri // Extract possible subfolders from the path. if (mEnvData->mStartPath != "") - subFolders = Utils::String::replace( - Utils::FileSystem::getParent(mPath), mEnvData->mStartPath, ""); + subFolders = + Utils::String::replace(Utils::FileSystem::getParent(mPath), mEnvData->mStartPath, ""); const std::string tempPath = getMediaDirectory() + mSystemName + "/" + subdirectory + - subFolders + "/" + getDisplayName(); + subFolders + "/" + getDisplayName(); // Look for an image file in the media directory. for (int i = 0; i < extList.size(); i++) { @@ -253,31 +251,37 @@ const std::string FileData::getImagePath() const const std::string FileData::get3DBoxPath() const { + // Return path to the 3D box image. return getMediafilePath("3dboxes", "3dbox"); } const std::string FileData::getCoverPath() const { + // Return path to the cover image. return getMediafilePath("covers", "cover"); } const std::string FileData::getMarqueePath() const { + // Return path to the marquee image. return getMediafilePath("marquees", "marquee"); } const std::string FileData::getMiximagePath() const { + // Return path to the miximage. return getMediafilePath("miximages", "miximage"); } const std::string FileData::getScreenshotPath() const { + // Return path to the screenshot image. return getMediafilePath("screenshots", "screenshot"); } const std::string FileData::getThumbnailPath() const { + // Return path to the thumbnail image. return getMediafilePath("thumbnails", "thumbnail"); } @@ -288,11 +292,11 @@ const std::string FileData::getVideoPath() const // Extract possible subfolders from the path. if (mEnvData->mStartPath != "") - subFolders = Utils::String::replace( - Utils::FileSystem::getParent(mPath), mEnvData->mStartPath, ""); + subFolders = + Utils::String::replace(Utils::FileSystem::getParent(mPath), mEnvData->mStartPath, ""); const std::string tempPath = - getMediaDirectory() + mSystemName + "/videos" + subFolders + "/" + getDisplayName(); + getMediaDirectory() + mSystemName + "/videos" + subFolders + "/" + getDisplayName(); // Look for media in the media directory. for (int i = 0; i < extList.size(); i++) { @@ -322,7 +326,8 @@ const std::vector& FileData::getChildrenListToDisplay() } std::vector FileData::getFilesRecursive(unsigned int typeMask, - bool displayedOnly, bool countAllGames) const + bool displayedOnly, + bool countAllGames) const { std::vector out; FileFilterIndex* idx = mSystem->getIndex(); @@ -354,7 +359,8 @@ std::vector FileData::getFilesRecursive(unsigned int typeMask, } std::vector FileData::getScrapeFilesRecursive(bool includeFolders, - bool excludeRecursively, bool respectExclusions) const + bool excludeRecursively, + bool respectExclusions) const { std::vector out; @@ -375,7 +381,7 @@ std::vector FileData::getScrapeFilesRecursive(bool includeFolders, if ((*it)->getChildren().size() > 0) { std::vector subChildren = (*it)->getScrapeFilesRecursive( - includeFolders, excludeRecursively, respectExclusions); + includeFolders, excludeRecursively, respectExclusions); out.insert(out.cend(), subChildren.cbegin(), subChildren.cend()); } } @@ -383,32 +389,25 @@ std::vector FileData::getScrapeFilesRecursive(bool includeFolders, return out; } -std::string FileData::getKey() { - return getFileName(); -} +std::string FileData::getKey() { return getFileName(); } const bool FileData::isArcadeAsset() { const std::string stem = Utils::FileSystem::getStem(mPath); return ((mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) || - mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) && - (MameNames::getInstance()->isBios(stem) || - MameNames::getInstance()->isDevice(stem))); + mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) && + (MameNames::getInstance()->isBios(stem) || MameNames::getInstance()->isDevice(stem))); } const bool FileData::isArcadeGame() { const std::string stem = Utils::FileSystem::getStem(mPath); return ((mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) || - mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) && - (!MameNames::getInstance()->isBios(stem) && - !MameNames::getInstance()->isDevice(stem))); + mSystem->hasPlatformId(PlatformIds::SNK_NEO_GEO))) && + (!MameNames::getInstance()->isBios(stem) && !MameNames::getInstance()->isDevice(stem))); } -FileData* FileData::getSourceFileData() -{ - return this; -} +FileData* FileData::getSourceFileData() { return this; } void FileData::addChild(FileData* file) { @@ -441,7 +440,7 @@ void FileData::removeChild(FileData* file) } void FileData::sort(ComparisonFunction& comparator, - std::pair& gameCount) + std::pair& gameCount) { mOnlyFolders = true; mHasFolders = false; @@ -456,17 +455,17 @@ void FileData::sort(ComparisonFunction& comparator, if (!showHiddenGames) { for (auto it = mChildren.begin(); it != mChildren.end();) { - // If the option to hide hidden games has been set and the game is hidden, - // then skip it. Normally games are hidden during loading of the gamelists in - // Gamelist::parseGamelist() and this code should only run when a user has marked - // an entry manually as hidden. So upon the next application startup, this game - // should be filtered already at that earlier point. + // If the option to hide hidden games has been set and the game is hidden, + // then skip it. Normally games are hidden during loading of the gamelists in + // Gamelist::parseGamelist() and this code should only run when a user has marked + // an entry manually as hidden. So upon the next application startup, this game + // should be filtered already at that earlier point. if ((*it)->getHidden()) it = mChildren.erase(it); // Also hide folders where all its entries have been hidden, unless it's a // grouped custom collection. else if ((*it)->getType() == FOLDER && (*it)->getChildren().size() == 0 && - !(*it)->getSystem()->isGroupedCustomCollection()) + !(*it)->getSystem()->isGroupedCustomCollection()) it = mChildren.erase(it); else it++; @@ -502,11 +501,11 @@ void FileData::sort(ComparisonFunction& comparator, // If the requested sorting is not by filename, then sort in ascending filename order // as a first step, in order to get a correct secondary sorting. if (getSortTypeFromString("filename, ascending").comparisonFunction != comparator && - getSortTypeFromString("filename, descending").comparisonFunction != comparator) { + getSortTypeFromString("filename, descending").comparisonFunction != comparator) { std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), - getSortTypeFromString("filename, ascending").comparisonFunction); + getSortTypeFromString("filename, ascending").comparisonFunction); std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), - getSortTypeFromString("filename, ascending").comparisonFunction); + getSortTypeFromString("filename, ascending").comparisonFunction); } if (foldersOnTop && mOnlyFolders) @@ -523,9 +522,9 @@ void FileData::sort(ComparisonFunction& comparator, // If the requested sorting is not by filename, then sort in ascending filename order // as a first step, in order to get a correct secondary sorting. if (getSortTypeFromString("filename, ascending").comparisonFunction != comparator && - getSortTypeFromString("filename, descending").comparisonFunction != comparator) + getSortTypeFromString("filename, descending").comparisonFunction != comparator) std::stable_sort(mChildren.begin(), mChildren.end(), - getSortTypeFromString("filename, ascending").comparisonFunction); + getSortTypeFromString("filename, ascending").comparisonFunction); std::stable_sort(mChildren.begin(), mChildren.end(), comparator); } @@ -555,7 +554,7 @@ void FileData::sort(ComparisonFunction& comparator, } void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, - std::pair& gameCount) + std::pair& gameCount) { mOnlyFolders = true; mHasFolders = false; @@ -634,39 +633,39 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, // some folders as favorites is probably a rare situation. if (!mOnlyFolders && mChildrenFavoritesFolders.size() > 0) { mChildrenFolders.insert(mChildrenFolders.end(), mChildrenFavoritesFolders.begin(), - mChildrenFavoritesFolders.end()); + mChildrenFavoritesFolders.end()); mChildrenFavoritesFolders.erase(mChildrenFavoritesFolders.begin(), - mChildrenFavoritesFolders.end()); + mChildrenFavoritesFolders.end()); std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), - getSortTypeFromString("filename, ascending").comparisonFunction); + getSortTypeFromString("filename, ascending").comparisonFunction); } // If the requested sorting is not by filename, then sort in ascending filename order // as a first step, in order to get a correct secondary sorting. if (getSortTypeFromString("filename, ascending").comparisonFunction != comparator && - getSortTypeFromString("filename, descending").comparisonFunction != comparator) { + getSortTypeFromString("filename, descending").comparisonFunction != comparator) { std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), - getSortTypeFromString("filename, ascending").comparisonFunction); + getSortTypeFromString("filename, ascending").comparisonFunction); std::stable_sort(mChildrenFavoritesFolders.begin(), mChildrenFavoritesFolders.end(), - getSortTypeFromString("filename, ascending").comparisonFunction); + getSortTypeFromString("filename, ascending").comparisonFunction); std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), - getSortTypeFromString("filename, ascending").comparisonFunction); + getSortTypeFromString("filename, ascending").comparisonFunction); std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), - getSortTypeFromString("filename, ascending").comparisonFunction); + getSortTypeFromString("filename, ascending").comparisonFunction); } // Sort favorite games and the other games separately. if (foldersOnTop && mOnlyFolders) { - std::stable_sort(mChildrenFavoritesFolders.begin(), - mChildrenFavoritesFolders.end(), comparator); + std::stable_sort(mChildrenFavoritesFolders.begin(), mChildrenFavoritesFolders.end(), + comparator); std::stable_sort(mChildrenFolders.begin(), mChildrenFolders.end(), comparator); } std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), comparator); std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator); // Iterate through any child favorite folders. - for (auto it = mChildrenFavoritesFolders.cbegin(); it != - mChildrenFavoritesFolders.cend(); it++) { + for (auto it = mChildrenFavoritesFolders.cbegin(); // Line break. + it != mChildrenFavoritesFolders.cend(); it++) { if ((*it)->getChildren().size() > 0) (*it)->sortFavoritesOnTop(comparator, gameCount); } @@ -690,9 +689,9 @@ void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, // Combine the individually sorted favorite games and other games vectors. mChildren.erase(mChildren.begin(), mChildren.end()); mChildren.reserve(mChildrenFavoritesFolders.size() + mChildrenFolders.size() + - mChildrenFavorites.size() + mChildrenOthers.size()); + mChildrenFavorites.size() + mChildrenOthers.size()); mChildren.insert(mChildren.end(), mChildrenFavoritesFolders.begin(), - mChildrenFavoritesFolders.end()); + mChildrenFavoritesFolders.end()); mChildren.insert(mChildren.end(), mChildrenFolders.begin(), mChildrenFolders.end()); mChildren.insert(mChildren.end(), mChildrenFavorites.begin(), mChildrenFavorites.end()); mChildren.insert(mChildren.end(), mChildrenOthers.begin(), mChildrenOthers.end()); @@ -711,10 +710,10 @@ void FileData::sort(const SortType& type, bool mFavoritesOnTop) void FileData::countGames(std::pair& gameCount) { bool isKidMode = (Settings::getInstance()->getString("UIMode") == "kid" || - Settings::getInstance()->getBool("ForceKid")); + Settings::getInstance()->getBool("ForceKid")); (Settings::getInstance()->getString("UIMode") == "kid" || - Settings::getInstance()->getBool("ForceKid")); + Settings::getInstance()->getBool("ForceKid")); for (unsigned int i = 0; i < mChildren.size(); i++) { if (mChildren[i]->getType() == GAME && mChildren[i]->getCountAsGame()) { @@ -731,7 +730,8 @@ void FileData::countGames(std::pair& gameCount) mGameCount = gameCount; } -FileData::SortType FileData::getSortTypeFromString(std::string desc) { +FileData::SortType FileData::getSortTypeFromString(std::string desc) +{ std::vector SortTypes = FileSorts::SortTypes; for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) { @@ -752,10 +752,12 @@ void FileData::launchGame(Window* window) // Check if there is a launch command override for the game // and the corresponding option to use it has been set. if (Settings::getInstance()->getBool("LaunchCommandOverride") && - !metadata.get("launchcommand").empty()) + !metadata.get("launchcommand").empty()) { command = metadata.get("launchcommand"); - else + } + else { command = mEnvData->mLaunchCommand; + } std::string commandRaw = command; @@ -794,14 +796,14 @@ void FileData::launchGame(Window* window) // Hack to show an error message if there was no emulator entry in es_find_rules.xml. if (binaryPath.substr(0, 18) == "NO EMULATOR RULE: ") { std::string emulatorEntry = binaryPath.substr(18, binaryPath.size() - 18); - LOG(LogError) << "Couldn't launch game, either there is no emulator entry for \"" << - emulatorEntry << "\" in es_find_rules.xml or there are no systempath or staticpath " - "rules defined"; + LOG(LogError) + << "Couldn't launch game, either there is no emulator entry for \"" << emulatorEntry + << "\" in es_find_rules.xml or there are no systempath or staticpath rules defined"; LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << commandRaw; - GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: MISSING EMULATOR CONFIGURATION FOR '" + - emulatorEntry + "'", 6000); + GuiInfoPopup* s = new GuiInfoPopup( + window, "ERROR: MISSING EMULATOR CONFIGURATION FOR '" + emulatorEntry + "'", 6000); window->setInfoPopup(s); return; } @@ -810,20 +812,23 @@ void FileData::launchGame(Window* window) LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << commandRaw; - GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: COULDN'T FIND EMULATOR, HAS IT " \ - "BEEN PROPERLY INSTALLED?", 6000); + GuiInfoPopup* s = new GuiInfoPopup(window, + "ERROR: COULDN'T FIND EMULATOR, HAS IT " + "BEEN PROPERLY INSTALLED?", + 6000); window->setInfoPopup(s); return; } else { - #if defined(_WIN64) - LOG(LogDebug) << "FileData::launchGame(): Found emulator binary \"" << - Utils::String::replace(Utils::String::replace( - binaryPath, "%ESPATH%", esPath), "/", "\\") << "\""; - #else - LOG(LogDebug) << "FileData::launchGame(): Found emulator binary \"" << - Utils::String::replace(binaryPath, "%ESPATH%", esPath) << "\""; - #endif +#if defined(_WIN64) + LOG(LogDebug) << "FileData::launchGame(): Found emulator binary \"" + << Utils::String::replace( + Utils::String::replace(binaryPath, "%ESPATH%", esPath), "/", "\\") + << "\""; +#else + LOG(LogDebug) << "FileData::launchGame(): Found emulator binary \"" + << Utils::String::replace(binaryPath, "%ESPATH%", esPath) << "\""; +#endif } // If %EMUPATH% is used in es_systems.xml for this system, then check that the core @@ -834,8 +839,8 @@ void FileData::launchGame(Window* window) unsigned int quotationMarkPos = 0; if (command.find("\"%EMUPATH%", emuPathPos - 1) != std::string::npos) { hasQuotationMark = true; - quotationMarkPos = static_cast( - command.find("\"", emuPathPos + 9) - emuPathPos); + quotationMarkPos = + static_cast(command.find("\"", emuPathPos + 9) - emuPathPos); } size_t spacePos = command.find(" ", emuPathPos + quotationMarkPos); std::string coreRaw; @@ -843,22 +848,23 @@ void FileData::launchGame(Window* window) if (spacePos != std::string::npos) { coreRaw = command.substr(emuPathPos, spacePos - emuPathPos); coreFile = Utils::FileSystem::getParent(binaryPath) + - command.substr(emuPathPos + 9, spacePos - emuPathPos - 9); + command.substr(emuPathPos + 9, spacePos - emuPathPos - 9); if (hasQuotationMark) { coreRaw.pop_back(); coreFile.pop_back(); } if (!Utils::FileSystem::isRegularFile(coreFile) && - !Utils::FileSystem::isSymlink(coreFile)) { - LOG(LogError) << "Couldn't launch game, emulator core file \"" << - Utils::FileSystem::getFileName(coreFile) << "\" not found"; + !Utils::FileSystem::isSymlink(coreFile)) { + LOG(LogError) << "Couldn't launch game, emulator core file \"" + << Utils::FileSystem::getFileName(coreFile) << "\" not found"; LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << commandRaw; - GuiInfoPopup* s = new GuiInfoPopup(window, - "ERROR: COULDN'T FIND EMULATOR CORE FILE '" + - Utils::String::toUpper(Utils::FileSystem::getFileName(coreFile)) + - "'", 6000); + GuiInfoPopup* s = new GuiInfoPopup( + window, + "ERROR: COULDN'T FIND EMULATOR CORE FILE '" + + Utils::String::toUpper(Utils::FileSystem::getFileName(coreFile)) + "'", + 6000); window->setInfoPopup(s); return; } @@ -877,8 +883,10 @@ void FileData::launchGame(Window* window) LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << commandRaw; - GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: INVALID ENTRY IN SYSTEMS " \ - "CONFIGURATION FILE", 6000); + GuiInfoPopup* s = new GuiInfoPopup(window, + "ERROR: INVALID ENTRY IN SYSTEMS " + "CONFIGURATION FILE", + 6000); window->setInfoPopup(s); return; } @@ -886,13 +894,13 @@ void FileData::launchGame(Window* window) // Error handling in case of no core find rule. if (coreEntry != "" && emulatorCorePaths.empty()) { - LOG(LogError) << "Couldn't launch game, either there is no core entry for \"" << - coreEntry << "\" in es_find_rules.xml or there are no corepath rules defined"; + LOG(LogError) << "Couldn't launch game, either there is no core entry for \"" << coreEntry + << "\" in es_find_rules.xml or there are no corepath rules defined"; LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << commandRaw; - GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: MISSING CORE CONFIGURATION FOR '" + - coreEntry + "'", 6000); + GuiInfoPopup* s = new GuiInfoPopup( + window, "ERROR: MISSING CORE CONFIGURATION FOR '" + coreEntry + "'", 6000); window->setInfoPopup(s); return; } @@ -913,46 +921,47 @@ void FileData::launchGame(Window* window) separatorPos = quotePos; if (separatorPos != std::string::npos) { - coreName = command.substr(coreFilePos + 2, separatorPos - (coreFilePos + 2)); + coreName = command.substr(coreFilePos + 2, separatorPos - (coreFilePos + 2)); - #if defined(_WIN64) +#if defined(_WIN64) std::string coreFile = Utils::FileSystem::expandHomePath(path + "\\" + coreName); - #else +#else std::string coreFile = Utils::FileSystem::expandHomePath(path + "/" + coreName); - #endif +#endif // Expand %EMUPATH% if it has been used in the %CORE_ variable. size_t stringPos = coreFile.find("%EMUPATH%"); if (stringPos != std::string::npos) { - #if defined (_WIN64) - coreFile = Utils::String::replace(coreFile.replace(stringPos, 9, - Utils::FileSystem::getParent(binaryPath)), "/", "\\"); - #else +#if defined(_WIN64) + coreFile = Utils::String::replace( + coreFile.replace(stringPos, 9, Utils::FileSystem::getParent(binaryPath)), "/", + "\\"); +#else coreFile = coreFile.replace(stringPos, 9, Utils::FileSystem::getParent(binaryPath)); - #endif +#endif } // Expand %ESPATH% if it has been used in the %CORE_ variable. stringPos = coreFile.find("%ESPATH%"); if (stringPos != std::string::npos) { coreFile = coreFile.replace(stringPos, 8, esPath); - #if defined(_WIN64) +#if defined(_WIN64) coreFile = Utils::String::replace(coreFile, "/", "\\"); - #endif +#endif } if (Utils::FileSystem::isRegularFile(coreFile) || - Utils::FileSystem::isSymlink(coreFile)) { + Utils::FileSystem::isSymlink(coreFile)) { foundCoreFile = true; // Escape any blankspaces. if (coreFile.find(" ") != std::string::npos) coreFile = Utils::FileSystem::getEscapedPath(coreFile); command.replace(coreEntryPos, separatorPos - coreEntryPos, coreFile); - #if !defined(_WIN64) +#if !defined(_WIN64) // Remove any quotation marks as it would make the launch function fail. if (command.find("\"") != std::string::npos) command = Utils::String::replace(command, "\"", ""); - #endif +#endif break; } } @@ -961,23 +970,28 @@ void FileData::launchGame(Window* window) LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << commandRaw; - GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: INVALID ENTRY IN SYSTEMS " \ - "CONFIGURATION FILE", 6000); + GuiInfoPopup* s = new GuiInfoPopup(window, + "ERROR: INVALID ENTRY IN SYSTEMS " + "CONFIGURATION FILE", + 6000); window->setInfoPopup(s); return; } } if (!foundCoreFile && coreName.size() > 0) { - LOG(LogError) << "Couldn't launch game, emulator core file \"" << - coreName.substr(0, coreName.size()) << "\" not found"; + LOG(LogError) << "Couldn't launch game, emulator core file \"" + << coreName.substr(0, coreName.size()) << "\" not found"; LOG(LogError) << "Raw emulator launch command:"; LOG(LogError) << commandRaw; - LOG(LogError) << - "Tried to find the core file using these paths as defined by es_find_rules.xml:"; + LOG(LogError) + << "Tried to find the core file using these paths as defined by es_find_rules.xml:"; LOG(LogError) << Utils::String::vectorToDelimitedString(emulatorCorePaths, ", "); - GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR: COULDN'T FIND EMULATOR CORE FILE '" + - Utils::String::toUpper(coreName.substr(0, coreName.size()) + "'"), 6000); + GuiInfoPopup* s = + new GuiInfoPopup(window, + "ERROR: COULDN'T FIND EMULATOR CORE FILE '" + + Utils::String::toUpper(coreName.substr(0, coreName.size()) + "'"), + 6000); window->setInfoPopup(s); return; } @@ -990,12 +1004,13 @@ void FileData::launchGame(Window* window) // swapBuffers() is called here to turn the screen black to eliminate some potential // flickering and to avoid showing the game launch message briefly when returning // from the game. - #if defined(_WIN64) + +#if defined(_WIN64) if (!(Settings::getInstance()->getBool("LaunchWorkaround") || - ViewController::get()->runInBackground(mSystem))) - #else + ViewController::get()->runInBackground(mSystem))) +#else if (!ViewController::get()->runInBackground(mSystem)) - #endif +#endif Renderer::swapBuffers(); Scripting::fireEvent("game-start", romPath, getSourceFileData()->metadata.get("name")); @@ -1004,28 +1019,31 @@ void FileData::launchGame(Window* window) LOG(LogDebug) << "Raw emulator launch command:"; LOG(LogDebug) << commandRaw; LOG(LogInfo) << "Expanded emulator launch command:"; - LOG(LogInfo) << command; + // Possibly keep ES-DE running in the background while the game is launched. - #if defined(_WIN64) + +#if defined(_WIN64) returnValue = launchGameWindows(Utils::String::stringToWideString(command), - ViewController::get()->runInBackground(mSystem)); - #else + ViewController::get()->runInBackground(mSystem)); +#else returnValue = launchGameUnix(command, ViewController::get()->runInBackground(mSystem)); - #endif +#endif // Notify the user in case of a failed game launch using a popup window. if (returnValue != 0) { LOG(LogWarning) << "...launch terminated with nonzero return value " << returnValue; - GuiInfoPopup* s = new GuiInfoPopup(window, "ERROR LAUNCHING GAME '" + - Utils::String::toUpper(metadata.get("name")) + "' (ERROR CODE " + - Utils::String::toUpper(std::to_string(returnValue) + ")"), 6000); + GuiInfoPopup* s = new GuiInfoPopup( + window, + "ERROR LAUNCHING GAME '" + Utils::String::toUpper(metadata.get("name")) + + "' (ERROR CODE " + Utils::String::toUpper(std::to_string(returnValue) + ")"), + 6000); window->setInfoPopup(s); } else { // Stop showing the game launch notification. window->stopInfoPopup(); - #if defined(_WIN64) +#if defined(_WIN64) // For some game systems or if the "RunInBackground" setting has been enabled, keep // ES-DE running while the game is launched. This pauses any video and keeps the // screensaver from getting activated. @@ -1035,7 +1053,7 @@ void FileData::launchGame(Window* window) // Normalize deltaTime so that the screensaver does not start immediately // when returning from the game. window->normalizeNextUpdate(); - #else +#else // For some game systems we need to keep ES-DE running while the game is launched. // This pauses any video and keeps the screensaver from getting activated. if (ViewController::get()->runInBackground(mSystem)) @@ -1043,7 +1061,7 @@ void FileData::launchGame(Window* window) // Normalize deltaTime so that the screensaver does not start immediately // when returning from the game. window->normalizeNextUpdate(); - #endif +#endif } Scripting::fireEvent("game-end", romPath, getSourceFileData()->metadata.get("name")); @@ -1062,10 +1080,10 @@ void FileData::launchGame(Window* window) // If the parent is a folder and it's not the root of the system, then update its lastplayed // timestamp to the same time as the game that was just launched. - if (gameToUpdate->getParent()->getType() == FOLDER && gameToUpdate->getParent()->getName() != - gameToUpdate->getSystem()->getFullName()) { + if (gameToUpdate->getParent()->getType() == FOLDER && + gameToUpdate->getParent()->getName() != gameToUpdate->getSystem()->getFullName()) { gameToUpdate->getParent()->metadata.set("lastplayed", - gameToUpdate->metadata.get("lastplayed")); + gameToUpdate->metadata.get("lastplayed")); } CollectionSystemsManager::get()->refreshCollectionSystems(gameToUpdate); @@ -1086,9 +1104,9 @@ std::string FileData::findEmulatorPath(std::string& command) // Method 1, emulator binary is defined using find rules: - #if defined(_WIN64) +#if defined(_WIN64) std::vector emulatorWinRegistryPaths; - #endif +#endif std::vector emulatorSystemPaths; std::vector emulatorStaticPaths; std::string emulatorEntry; @@ -1101,10 +1119,10 @@ std::string FileData::findEmulatorPath(std::string& command) } if (emulatorEntry != "") { - #if defined(_WIN64) +#if defined(_WIN64) emulatorWinRegistryPaths = - SystemData::sFindRules.get()->mEmulators[emulatorEntry].winRegistryPaths; - #endif + SystemData::sFindRules.get()->mEmulators[emulatorEntry].winRegistryPaths; +#endif emulatorSystemPaths = SystemData::sFindRules.get()->mEmulators[emulatorEntry].systemPaths; emulatorStaticPaths = SystemData::sFindRules.get()->mEmulators[emulatorEntry].staticPaths; } @@ -1113,11 +1131,11 @@ std::string FileData::findEmulatorPath(std::string& command) if (emulatorEntry != "" && emulatorSystemPaths.empty() && emulatorStaticPaths.empty()) return "NO EMULATOR RULE: " + emulatorEntry; - #if defined(_WIN64) +#if defined(_WIN64) for (std::string path : emulatorWinRegistryPaths) { // Search for the emulator using the App Paths keys in the Windows Registry. std::string registryKeyPath = - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" + path; + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" + path; HKEY registryKey; LSTATUS keyStatus = -1; @@ -1126,33 +1144,19 @@ std::string FileData::findEmulatorPath(std::string& command) DWORD pathSize = 1024; // First look in HKEY_CURRENT_USER. - keyStatus = RegOpenKeyEx( - HKEY_CURRENT_USER, - registryKeyPath.c_str(), - 0, - KEY_QUERY_VALUE, - ®istryKey); + keyStatus = RegOpenKeyEx(HKEY_CURRENT_USER, registryKeyPath.c_str(), 0, KEY_QUERY_VALUE, + ®istryKey); // If not found, then try in HKEY_LOCAL_MACHINE. if (keyStatus != ERROR_SUCCESS) { - keyStatus = RegOpenKeyEx( - HKEY_LOCAL_MACHINE, - registryKeyPath.c_str(), - 0, - KEY_QUERY_VALUE, - ®istryKey); + keyStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registryKeyPath.c_str(), 0, + KEY_QUERY_VALUE, ®istryKey); } // If the key exists, then try to retrieve the value. if (keyStatus == ERROR_SUCCESS) { - pathStatus = RegGetValue( - registryKey, - nullptr, - nullptr, - RRF_RT_REG_SZ, - nullptr, - ®istryPath, - &pathSize); + pathStatus = RegGetValue(registryKey, nullptr, nullptr, RRF_RT_REG_SZ, nullptr, + ®istryPath, &pathSize); } else { RegCloseKey(registryKey); @@ -1163,7 +1167,7 @@ std::string FileData::findEmulatorPath(std::string& command) // so check for that as well. if (pathStatus == ERROR_SUCCESS) { if (Utils::FileSystem::isRegularFile(registryPath) || - Utils::FileSystem::isSymlink(registryPath)) { + Utils::FileSystem::isSymlink(registryPath)) { command.replace(0, endPos + 1, registryPath); RegCloseKey(registryKey); return registryPath; @@ -1171,25 +1175,24 @@ std::string FileData::findEmulatorPath(std::string& command) } RegCloseKey(registryKey); } - #endif +#endif for (std::string path : emulatorSystemPaths) { - #if defined(_WIN64) +#if defined(_WIN64) std::wstring pathWide = Utils::String::stringToWideString(path); // Search for the emulator using the PATH environmental variable. DWORD size = SearchPathW(nullptr, pathWide.c_str(), L".exe", 0, nullptr, nullptr); if (size) { - std::vector pathBuffer(static_cast(size) + 1 ); + std::vector pathBuffer(static_cast(size) + 1); wchar_t* fileName = nullptr; - SearchPathW(nullptr, pathWide.c_str(), L".exe", size + 1 , - pathBuffer.data(), &fileName); + SearchPathW(nullptr, pathWide.c_str(), L".exe", size + 1, pathBuffer.data(), &fileName); std::wstring pathString = pathBuffer.data(); if (pathString.length()) { - exePath = Utils::String::wideStringToString(pathString.substr(0, - pathString.size() - std::wstring(fileName).size())); + exePath = Utils::String::wideStringToString( + pathString.substr(0, pathString.size() - std::wstring(fileName).size())); exePath.pop_back(); } } @@ -1198,14 +1201,14 @@ std::string FileData::findEmulatorPath(std::string& command) command.replace(0, endPos + 1, exePath); return exePath; } - #else +#else exePath = Utils::FileSystem::getPathToBinary(path); if (exePath != "") { exePath += "/" + path; command.replace(0, endPos + 1, exePath); return exePath; } - #endif +#endif } for (std::string path : emulatorStaticPaths) { @@ -1214,11 +1217,10 @@ std::string FileData::findEmulatorPath(std::string& command) path = Utils::String::replace(path, "%ESPATH%", Utils::FileSystem::getExePath()); // Likewise for the %ROMPATH% variable which expands to the configured ROM directory. path = Utils::String::replace(path, "%ROMPATH%", getROMDirectory()); - #if defined(_WIN64) +#if defined(_WIN64) path = Utils::String::replace(path, "/", "\\"); - #endif - if (Utils::FileSystem::isRegularFile(path) || - Utils::FileSystem::isSymlink(path)) { +#endif + if (Utils::FileSystem::isRegularFile(path) || Utils::FileSystem::isSymlink(path)) { command.replace(0, endPos + 1, path); return path; } @@ -1228,9 +1230,9 @@ std::string FileData::findEmulatorPath(std::string& command) // If %ESPATH% is used, then expand it to the binary directory of ES-DE. command = Utils::String::replace(command, "%ESPATH%", Utils::FileSystem::getExePath()); - #if defined(_WIN64) +#if defined(_WIN64) command = Utils::String::replace(command, "/", "\\"); - #endif +#endif // If the first character is a quotation mark, then we need to extract up to the // next quotation mark, otherwise we'll only extract up to the first space character. @@ -1242,23 +1244,23 @@ std::string FileData::findEmulatorPath(std::string& command) emuExecutable = command.substr(0, command.find(' ')); } - #if defined(_WIN64) +#if defined(_WIN64) std::wstring emuExecutableWide = Utils::String::stringToWideString(emuExecutable); // Search for the emulator using the PATH environmental variable. DWORD size = SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", 0, nullptr, nullptr); if (size) { - std::vector pathBuffer(static_cast(size) + 1 ); + std::vector pathBuffer(static_cast(size) + 1); wchar_t* fileName = nullptr; - SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", size + 1 , - pathBuffer.data(), &fileName); + SearchPathW(nullptr, emuExecutableWide.c_str(), L".exe", size + 1, pathBuffer.data(), + &fileName); exePath = Utils::String::wideStringToString(pathBuffer.data()); } - #else +#else if (Utils::FileSystem::isRegularFile(emuExecutable) || - Utils::FileSystem::isSymlink(emuExecutable)) { + Utils::FileSystem::isSymlink(emuExecutable)) { exePath = emuExecutable; } else { @@ -1266,14 +1268,16 @@ std::string FileData::findEmulatorPath(std::string& command) if (exePath != "") exePath += "/" + emuExecutable; } - #endif +#endif return exePath; } CollectionFileData::CollectionFileData(FileData* file, SystemData* system) - : FileData(file->getSourceFileData()->getType(), file->getSourceFileData()->getPath(), - file->getSourceFileData()->getSystemEnvData(), system) + : FileData(file->getSourceFileData()->getType(), + file->getSourceFileData()->getPath(), + file->getSourceFileData()->getSystemEnvData(), + system) { // We use this constructor to create a clone of the filedata, and change its system. mSourceFileData = file->getSourceFileData(); @@ -1291,15 +1295,6 @@ CollectionFileData::~CollectionFileData() mParent = nullptr; } -std::string CollectionFileData::getKey() { - return getFullPath(); -} - -FileData* CollectionFileData::getSourceFileData() -{ - return mSourceFileData; -} - void CollectionFileData::refreshMetadata() { metadata = mSourceFileData->metadata; @@ -1310,9 +1305,9 @@ const std::string& CollectionFileData::getName() { if (mDirty) { mCollectionFileName = - Utils::String::removeParenthesis(mSourceFileData->metadata.get("name")); + Utils::String::removeParenthesis(mSourceFileData->metadata.get("name")); mCollectionFileName += - " [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]"; + " [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]"; mDirty = false; } diff --git a/es-app/src/FileData.h b/es-app/src/FileData.h index b476c1fd5..9621fd006 100644 --- a/es-app/src/FileData.h +++ b/es-app/src/FileData.h @@ -11,8 +11,8 @@ #ifndef ES_APP_FILE_DATA_H #define ES_APP_FILE_DATA_H -#include "utils/FileSystemUtil.h" #include "MetaData.h" +#include "utils/FileSystemUtil.h" #include @@ -30,8 +30,10 @@ enum FileType { class FileData { public: - FileData(FileType type, const std::string& path, - SystemEnvironmentData* envData, SystemData* system); + FileData(FileType type, + const std::string& path, + SystemEnvironmentData* envData, + SystemData* system); virtual ~FileData(); @@ -41,17 +43,19 @@ public: const bool getKidgame(); const bool getHidden(); const bool getCountAsGame(); - const std::pair getGameCount() { return mGameCount; }; + const std::pair getGameCount() { return mGameCount; } const bool getExcludeFromScraper(); const std::vector getChildrenRecursive() const; - inline FileType getType() const { return mType; } - inline const std::string& getPath() const { return mPath; } - inline FileData* getParent() const { return mParent; } - inline const std::unordered_map& getChildrenByFilename() const - { return mChildrenByFilename; } - inline const std::vector& getChildren() const { return mChildren; } - inline SystemData* getSystem() const { return mSystem; } - inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; } + FileType getType() const { return mType; } + const std::string& getPath() const { return mPath; } + FileData* getParent() const { return mParent; } + const std::unordered_map& getChildrenByFilename() const + { + return mChildrenByFilename; + } + const std::vector& getChildren() const { return mChildren; } + SystemData* getSystem() const { return mSystem; } + SystemEnvironmentData* getSystemEnvData() const { return mEnvData; } const bool getOnlyFoldersFlag() { return mOnlyFolders; } const bool getHasFoldersFlag() { return mHasFolders; } static const std::string getROMDirectory(); @@ -66,29 +70,31 @@ public: const std::string getThumbnailPath() const; const std::string getVideoPath() const; - bool getDeletionFlag() { return mDeletionFlag; }; - void setDeletionFlag(bool setting) { mDeletionFlag = setting; }; + bool getDeletionFlag() { return mDeletionFlag; } + void setDeletionFlag(bool setting) { mDeletionFlag = setting; } const std::vector& getChildrenListToDisplay(); std::vector getFilesRecursive(unsigned int typeMask, - bool displayedOnly = false, bool countAllGames = true) const; - std::vector getScrapeFilesRecursive(bool includeFolders, bool excludeRecursively, - bool respectExclusions) const; + bool displayedOnly = false, + bool countAllGames = true) const; + std::vector getScrapeFilesRecursive(bool includeFolders, + bool excludeRecursively, + bool respectExclusions) const; void addChild(FileData* file); // Error if mType != FOLDER - void removeChild(FileData* file); //Error if mType != FOLDER + void removeChild(FileData* file); // Error if mType != FOLDER - inline bool isPlaceHolder() { return mType == PLACEHOLDER; }; + bool isPlaceHolder() { return mType == PLACEHOLDER; } - virtual inline void refreshMetadata() { return; }; + virtual void refreshMetadata() { return; } virtual std::string getKey(); const bool isArcadeAsset(); const bool isArcadeGame(); - inline std::string getFullPath() { return getPath(); }; - inline std::string getFileName() { return Utils::FileSystem::getFileName(getPath()); }; + std::string getFullPath() { return getPath(); } + std::string getFileName() { return Utils::FileSystem::getFileName(getPath()); } virtual FileData* getSourceFileData(); - inline std::string getSystemName() const { return mSystemName; }; + std::string getSystemName() const { return mSystemName; } // Returns our best guess at the "real" name for this file. std::string getDisplayName() const; @@ -104,19 +110,22 @@ public: ComparisonFunction* comparisonFunction; std::string description; SortType(ComparisonFunction* sortFunction, const std::string& sortDescription) - : comparisonFunction(sortFunction), description(sortDescription) {} + : comparisonFunction(sortFunction) + , description(sortDescription) + { + } }; void sort(ComparisonFunction& comparator, std::pair& gameCount); void sortFavoritesOnTop(ComparisonFunction& comparator, - std::pair& gameCount); + std::pair& gameCount); void sort(const SortType& type, bool mFavoritesOnTop = false); MetaDataList metadata; // Only count the games, a cheaper alternative to a full sort when that is not required. void countGames(std::pair& gameCount); - inline void setSortTypeString(std::string typestring) { mSortTypeString = typestring; } - inline std::string getSortTypeString() { return mSortTypeString; } + void setSortTypeString(std::string typestring) { mSortTypeString = typestring; } + std::string getSortTypeString() { return mSortTypeString; } FileData::SortType getSortTypeFromString(std::string desc); protected: @@ -130,7 +139,7 @@ private: std::string mPath; SystemEnvironmentData* mEnvData; SystemData* mSystem; - std::unordered_map mChildrenByFilename; + std::unordered_map mChildrenByFilename; std::vector mChildren; std::vector mFilteredChildren; // The pair includes all games, and favorite games. @@ -148,8 +157,8 @@ public: ~CollectionFileData(); const std::string& getName(); void refreshMetadata(); - FileData* getSourceFileData(); - std::string getKey(); + FileData* getSourceFileData() { return mSourceFileData; } + std::string getKey() { return getFullPath(); } private: // Needs to be updated when metadata changes. diff --git a/es-app/src/FileFilterIndex.cpp b/es-app/src/FileFilterIndex.cpp index 8d4b53688..0f83b7990 100644 --- a/es-app/src/FileFilterIndex.cpp +++ b/es-app/src/FileFilterIndex.cpp @@ -8,12 +8,12 @@ #include "FileFilterIndex.h" -#include "math/Misc.h" -#include "utils/StringUtil.h" -#include "views/UIModeController.h" #include "FileData.h" #include "Log.h" #include "Settings.h" +#include "math/Misc.h" +#include "utils/StringUtil.h" +#include "views/UIModeController.h" #include @@ -21,19 +21,20 @@ #define INCLUDE_UNKNOWN false; FileFilterIndex::FileFilterIndex() - : mFilterByText(false), - mFilterByFavorites(false), - mFilterByGenre(false), - mFilterByPlayers(false), - mFilterByPubDev(false), - mFilterByRatings(false), - mFilterByKidGame(false), - mFilterByCompleted(false), - mFilterByBroken(false), - mFilterByHidden(false) + : mFilterByText(false) + , mFilterByFavorites(false) + , mFilterByGenre(false) + , mFilterByPlayers(false) + , mFilterByPubDev(false) + , mFilterByRatings(false) + , mFilterByKidGame(false) + , mFilterByCompleted(false) + , mFilterByBroken(false) + , mFilterByHidden(false) { clearAllFilters(); + // clang-format off FilterDataDecl filterDecls[] = { //type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel { FAVORITES_FILTER, &mFavoritesIndexAllKeys, &mFilterByFavorites, &mFavoritesIndexFilteredKeys, "favorite", false, "", "FAVORITES" }, @@ -46,21 +47,18 @@ FileFilterIndex::FileFilterIndex() { BROKEN_FILTER, &mBrokenIndexAllKeys, &mFilterByBroken, &mBrokenIndexFilteredKeys, "broken", false, "", "BROKEN" }, { HIDDEN_FILTER, &mHiddenIndexAllKeys, &mFilterByHidden, &mHiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN" } }; + // clang-format on - filterDataDecl = std::vector(filterDecls, filterDecls + - sizeof(filterDecls) / sizeof(filterDecls[0])); + filterDataDecl = std::vector( + filterDecls, filterDecls + sizeof(filterDecls) / sizeof(filterDecls[0])); } FileFilterIndex::~FileFilterIndex() { + // Reset the index when destroyed. resetIndex(); } -std::vector& FileFilterIndex::getFilterDataDecls() -{ - return filterDataDecl; -} - void FileFilterIndex::importIndex(FileFilterIndex* indexToImport) { struct IndexImportStructure { @@ -80,22 +78,23 @@ void FileFilterIndex::importIndex(FileFilterIndex* indexToImport) { &mHiddenIndexAllKeys, &(indexToImport->mHiddenIndexAllKeys) }, }; - std::vector indexImportDecl = - std::vector(indexStructDecls, indexStructDecls + - sizeof(indexStructDecls) / sizeof(indexStructDecls[0])); + std::vector indexImportDecl = std::vector( + indexStructDecls, + indexStructDecls + sizeof(indexStructDecls) / sizeof(indexStructDecls[0])); - for (std::vector::const_iterator indexesIt = - indexImportDecl.cbegin(); indexesIt != indexImportDecl.cend(); indexesIt++) - { + for (std::vector::const_iterator indexesIt = indexImportDecl.cbegin(); + indexesIt != indexImportDecl.cend(); indexesIt++) { for (std::map::const_iterator sourceIt = - (*indexesIt).sourceIndex->cbegin(); sourceIt != - (*indexesIt).sourceIndex->cend(); sourceIt++) { + (*indexesIt).sourceIndex->cbegin(); + sourceIt != (*indexesIt).sourceIndex->cend(); sourceIt++) { if ((*indexesIt).destinationIndex->find((*sourceIt).first) == - (*indexesIt).destinationIndex->cend()) + (*indexesIt).destinationIndex->cend()) { // Entry doesn't exist. (*((*indexesIt).destinationIndex))[(*sourceIt).first] = (*sourceIt).second; - else + } + else { (*((*indexesIt).destinationIndex))[(*sourceIt).first] += (*sourceIt).second; + } } } } @@ -115,7 +114,8 @@ void FileFilterIndex::resetIndex() } std::string FileFilterIndex::getIndexableKey(FileData* game, - FilterIndexType type, bool getSecondary) + FilterIndexType type, + bool getSecondary) { std::string key = ""; switch (type) { @@ -166,8 +166,8 @@ std::string FileFilterIndex::getIndexableKey(FileData* game, // These values should only exist if a third party application has // been used for scraping the ratings, or if the gamelist.xml file // has been manually edited. - ratingNumber = static_cast( - (ceilf(stof(ratingString) / 0.1f) / 10) * 5); + ratingNumber = + static_cast((ceilf(stof(ratingString) / 0.1f) / 10.0f) * 5.0f); if (ratingNumber < 0) ratingNumber = 0; @@ -176,11 +176,11 @@ std::string FileFilterIndex::getIndexableKey(FileData* game, key = "5 STARS"; else key = std::to_string(ratingNumber) + " - " + - std::to_string(ratingNumber) + ".5 STARS"; + std::to_string(ratingNumber) + ".5 STARS"; } catch (int e) { - LOG(LogError) << "Error parsing Rating (invalid value, exception nr.): " << - ratingString << ", " << e; + LOG(LogError) << "Error parsing Rating (invalid value, exception nr.): " + << ratingString << ", " << e; } } } @@ -254,13 +254,13 @@ void FileFilterIndex::setFilter(FilterIndexType type, std::vector* } else { for (std::vector::const_iterator it = filterDataDecl.cbegin(); - it != filterDataDecl.cend(); it++) { + it != filterDataDecl.cend(); it++) { if ((*it).type == type) { FilterDataDecl filterData = (*it); *(filterData.filteredByRef) = values->size() > 0; filterData.currentFilteredKeys->clear(); - for (std::vector::const_iterator vit = - values->cbegin(); vit != values->cend(); vit++) { + for (std::vector::const_iterator vit = values->cbegin(); + vit != values->cend(); vit++) { // Check if it exists. if (filterData.allIndexKeys->find(*vit) != filterData.allIndexKeys->cend()) { filterData.currentFilteredKeys->push_back(std::string(*vit)); @@ -272,20 +272,20 @@ void FileFilterIndex::setFilter(FilterIndexType type, std::vector* return; } - void FileFilterIndex::setTextFilter(std::string textFilter) - { +void FileFilterIndex::setTextFilter(std::string textFilter) +{ mTextFilter = textFilter; if (textFilter == "") mFilterByText = false; else mFilterByText = true; - }; +}; void FileFilterIndex::clearAllFilters() { for (std::vector::const_iterator it = filterDataDecl.cbegin(); - it != filterDataDecl.cend(); it++) { + it != filterDataDecl.cend(); it++) { FilterDataDecl filterData = (*it); *(filterData.filteredByRef) = false; filterData.currentFilteredKeys->clear(); @@ -312,19 +312,19 @@ void FileFilterIndex::setKidModeFilters() void FileFilterIndex::debugPrintIndexes() { LOG(LogInfo) << "Printing Indexes..."; - for (auto x: mFavoritesIndexAllKeys) { + for (auto x : mFavoritesIndexAllKeys) { LOG(LogInfo) << "Favorites Index: " << x.first << ": " << x.second; } - for (auto x: mGenreIndexAllKeys) { + for (auto x : mGenreIndexAllKeys) { LOG(LogInfo) << "Genre Index: " << x.first << ": " << x.second; } - for (auto x: mPlayersIndexAllKeys) { + for (auto x : mPlayersIndexAllKeys) { LOG(LogInfo) << "Multiplayer Index: " << x.first << ": " << x.second; } - for (auto x: mPubDevIndexAllKeys) { + for (auto x : mPubDevIndexAllKeys) { LOG(LogInfo) << "PubDev Index: " << x.first << ": " << x.second; } - for (auto x: mRatingsIndexAllKeys) { + for (auto x : mRatingsIndexAllKeys) { LOG(LogInfo) << "Ratings Index: " << x.first << ": " << x.second; } for (auto x : mKidGameIndexAllKeys) { @@ -348,8 +348,8 @@ bool FileFilterIndex::showFile(FileData* game) if (game->getType() == FOLDER) { std::vector children = game->getChildren(); // Iterate through all of the children, until there's a match. - for (std::vector::const_iterator it = children.cbegin(); - it != children.cend(); it++) { + for (std::vector::const_iterator it = children.cbegin(); it != children.cend(); + it++) { if (showFile(*it)) return true; } @@ -361,8 +361,8 @@ bool FileFilterIndex::showFile(FileData* game) // Name filters take precedence over all other filters, so if there is no match for // the game name, then always return false. - if (mTextFilter != "" && !(Utils::String::toUpper(game-> - getName()).find(mTextFilter) != std::string::npos)) { + if (mTextFilter != "" && + !(Utils::String::toUpper(game->getName()).find(mTextFilter) != std::string::npos)) { return false; } else if (mTextFilter != "") { @@ -370,7 +370,7 @@ bool FileFilterIndex::showFile(FileData* game) } for (std::vector::const_iterator it = filterDataDecl.cbegin(); - it != filterDataDecl.cend(); it++) { + it != filterDataDecl.cend(); it++) { FilterDataDecl filterData = (*it); if (filterData.primaryKey == "kidgame" && UIModeController::getInstance()->isUIModeKid()) { return (getIndexableKey(game, filterData.type, false) != "FALSE"); @@ -419,18 +419,19 @@ bool FileFilterIndex::isFiltered() bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type) { - const FilterIndexType filterTypes[9] = { FAVORITES_FILTER, GENRE_FILTER, - PLAYER_FILTER, PUBDEV_FILTER, RATINGS_FILTER, KIDGAME_FILTER, - COMPLETED_FILTER, BROKEN_FILTER, HIDDEN_FILTER }; - std::vector filterKeysList[9] = { mFavoritesIndexFilteredKeys, - mGenreIndexFilteredKeys, mPlayersIndexFilteredKeys, mPubDevIndexFilteredKeys, - mRatingsIndexFilteredKeys, mKidGameIndexFilteredKeys, mCompletedIndexFilteredKeys, - mBrokenIndexFilteredKeys, mHiddenIndexFilteredKeys }; + const FilterIndexType filterTypes[9] = { FAVORITES_FILTER, GENRE_FILTER, PLAYER_FILTER, + PUBDEV_FILTER, RATINGS_FILTER, KIDGAME_FILTER, + COMPLETED_FILTER, BROKEN_FILTER, HIDDEN_FILTER }; + std::vector filterKeysList[9] = { + mFavoritesIndexFilteredKeys, mGenreIndexFilteredKeys, mPlayersIndexFilteredKeys, + mPubDevIndexFilteredKeys, mRatingsIndexFilteredKeys, mKidGameIndexFilteredKeys, + mCompletedIndexFilteredKeys, mBrokenIndexFilteredKeys, mHiddenIndexFilteredKeys + }; for (int i = 0; i < 9; i++) { if (filterTypes[i] == type) { for (std::vector::const_iterator it = filterKeysList[i].cbegin(); - it != filterKeysList[i].cend(); it++) { + it != filterKeysList[i].cend(); it++) { if (key == (*it)) return true; } @@ -589,7 +590,8 @@ void FileFilterIndex::manageHiddenEntryInIndex(FileData* game, bool remove) } void FileFilterIndex::manageIndexEntry(std::map* index, - std::string key, bool remove) + std::string key, + bool remove) { bool includeUnknown = INCLUDE_UNKNOWN; if (!includeUnknown && key == UNKNOWN_LABEL) @@ -600,7 +602,7 @@ void FileFilterIndex::manageIndexEntry(std::map* index, if (index->find(key) == index->cend()) { // Disabled for now as this could happen because default values are assigned as // filters, for example 'FALSE' for favorites and kidgames for non-game entries. -// LOG(LogDebug) << "Couldn't find entry in index! " << key; + // LOG(LogDebug) << "Couldn't find entry in index! " << key; } else { (index->at(key))--; @@ -617,8 +619,3 @@ void FileFilterIndex::manageIndexEntry(std::map* index, (index->at(key))++; } } - -void FileFilterIndex::clearIndex(std::map& indexMap) -{ - indexMap.clear(); -} diff --git a/es-app/src/FileFilterIndex.h b/es-app/src/FileFilterIndex.h index f7c081edb..4bbcd2832 100644 --- a/es-app/src/FileFilterIndex.h +++ b/es-app/src/FileFilterIndex.h @@ -52,13 +52,13 @@ public: void removeFromIndex(FileData* game); void setFilter(FilterIndexType type, std::vector* values); void setTextFilter(std::string textFilter); - std::string getTextFilter() { return mTextFilter; }; + std::string getTextFilter() { return mTextFilter; } void clearAllFilters(); void debugPrintIndexes(); bool showFile(FileData* game); bool isFiltered(); bool isKeyBeingFilteredBy(std::string key, FilterIndexType type); - std::vector& getFilterDataDecls(); + std::vector& getFilterDataDecls() { return filterDataDecl; } void importIndex(FileFilterIndex* indexToImport); void resetIndex(); @@ -81,7 +81,7 @@ private: void manageIndexEntry(std::map* index, std::string key, bool remove); - void clearIndex(std::map& indexMap); + void clearIndex(std::map& indexMap) { indexMap.clear(); } std::string mTextFilter; bool mFilterByText; @@ -117,7 +117,6 @@ private: std::vector mHiddenIndexFilteredKeys; FileData* mRootFolder; - }; #endif // ES_APP_FILE_FILTER_INDEX_H diff --git a/es-app/src/FileSorts.cpp b/es-app/src/FileSorts.cpp index c5bf9bdea..8c84f3d78 100644 --- a/es-app/src/FileSorts.cpp +++ b/es-app/src/FileSorts.cpp @@ -48,8 +48,9 @@ namespace FileSorts FileData::SortType(&compareSystemDescending, "system, descending") }; - const std::vector SortTypes(typesArr, typesArr + - sizeof(typesArr)/sizeof(typesArr[0])); + const std::vector SortTypes(typesArr, + typesArr + + sizeof(typesArr) / sizeof(typesArr[0])); bool compareName(const FileData* file1, const FileData* file2) { @@ -155,11 +156,13 @@ namespace FileSorts file2Players = file2Players.substr(dashPos + 1, file2Players.size() - dashPos - 1); // Any non-numeric value will end up as zero. if (!file1Players.empty() && - std::all_of(file1Players.begin(), file1Players.end(), ::isdigit)) + std::all_of(file1Players.begin(), file1Players.end(), ::isdigit)) { file1Int = stoi(file1Players); + } if (!file2Players.empty() && - std::all_of(file2Players.begin(), file2Players.end(), ::isdigit)) + std::all_of(file2Players.begin(), file2Players.end(), ::isdigit)) { file2Int = stoi(file2Players); + } return file1Int < file2Int; } @@ -177,11 +180,13 @@ namespace FileSorts if (dashPos != std::string::npos) file2Players = file2Players.substr(dashPos + 1, file2Players.size() - dashPos - 1); if (!file1Players.empty() && - std::all_of(file1Players.begin(), file1Players.end(), ::isdigit)) + std::all_of(file1Players.begin(), file1Players.end(), ::isdigit)) { file1Int = stoi(file1Players); + } if (!file2Players.empty() && - std::all_of(file2Players.begin(), file2Players.end(), ::isdigit)) + std::all_of(file2Players.begin(), file2Players.end(), ::isdigit)) { file2Int = stoi(file2Players); + } return file1Int > file2Int; } @@ -201,16 +206,18 @@ namespace FileSorts { // Only games have playcount metadata. if (file1->metadata.getType() == GAME_METADATA && - file2->metadata.getType() == GAME_METADATA) + file2->metadata.getType() == GAME_METADATA) { return (file1)->metadata.getInt("playcount") < (file2)->metadata.getInt("playcount"); + } return false; } bool compareTimesPlayedDescending(const FileData* file1, const FileData* file2) { if (file1->metadata.getType() == GAME_METADATA && - file2->metadata.getType() == GAME_METADATA) + file2->metadata.getType() == GAME_METADATA) { return (file1)->metadata.getInt("playcount") > (file2)->metadata.getInt("playcount"); + } return false; } @@ -227,4 +234,5 @@ namespace FileSorts std::string system2 = Utils::String::toUpper(file2->getSystemName()); return system1.compare(system2) > 0; } -}; + +}; // namespace FileSorts diff --git a/es-app/src/FileSorts.h b/es-app/src/FileSorts.h index 367946a5a..28b0fcd6a 100644 --- a/es-app/src/FileSorts.h +++ b/es-app/src/FileSorts.h @@ -38,6 +38,6 @@ namespace FileSorts bool compareSystemDescending(const FileData* file1, const FileData* file2); extern const std::vector SortTypes; -}; +}; // namespace FileSorts #endif // ES_APP_FILE_SORTS_H diff --git a/es-app/src/Gamelist.cpp b/es-app/src/Gamelist.cpp index 478d025cf..31ac2b9e8 100644 --- a/es-app/src/Gamelist.cpp +++ b/es-app/src/Gamelist.cpp @@ -8,12 +8,12 @@ #include "Gamelist.h" -#include "utils/FileSystemUtil.h" -#include "utils/StringUtil.h" #include "FileData.h" #include "Log.h" #include "Settings.h" #include "SystemData.h" +#include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" #include @@ -25,8 +25,8 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType std::string relative = Utils::FileSystem::removeCommonPath(path, root->getPath(), contains); if (!contains) { - LOG(LogError) << "Path \"" << path << "\" is outside system path \"" << - system->getStartPath() << "\""; + LOG(LogError) << "Path \"" << path << "\" is outside system path \"" + << system->getStartPath() << "\""; return nullptr; } @@ -36,7 +36,7 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType bool found = false; while (path_it != pathList.end()) { const std::unordered_map& children = - treeNode->getChildrenByFilename(); + treeNode->getChildrenByFilename(); std::string key = *path_it; found = children.find(key) != children.cend(); @@ -71,8 +71,9 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType } // Create missing folder. - FileData* folder = new FileData(FOLDER, Utils::FileSystem::getStem(treeNode->getPath()) - + "/" + *path_it, system->getSystemEnvData(), system); + FileData* folder = new FileData( + FOLDER, Utils::FileSystem::getStem(treeNode->getPath()) + "/" + *path_it, + system->getSystemEnvData(), system); treeNode->addChild(folder); treeNode = folder; } @@ -89,24 +90,24 @@ void parseGamelist(SystemData* system) std::string xmlpath = system->getGamelistPath(false); if (!Utils::FileSystem::exists(xmlpath)) { - LOG(LogDebug) << "Gamelist::parseGamelist(): System \"" << system->getName() << - "\" does not have a gamelist.xml file"; + LOG(LogDebug) << "Gamelist::parseGamelist(): System \"" << system->getName() + << "\" does not have a gamelist.xml file"; return; } LOG(LogInfo) << "Parsing gamelist file \"" << xmlpath << "\"..."; pugi::xml_document doc; - #if defined(_WIN64) +#if defined(_WIN64) pugi::xml_parse_result result = - doc.load_file(Utils::String::stringToWideString(xmlpath).c_str()); - #else + doc.load_file(Utils::String::stringToWideString(xmlpath).c_str()); +#else pugi::xml_parse_result result = doc.load_file(xmlpath.c_str()); - #endif +#endif if (!result) { - LOG(LogError) << "Error parsing gamelist file \"" << xmlpath << - "\": " << result.description(); + LOG(LogError) << "Error parsing gamelist file \"" << xmlpath + << "\": " << result.description(); return; } @@ -124,24 +125,24 @@ void parseGamelist(SystemData* system) for (int i = 0; i < 2; i++) { std::string tag = tagList[i]; FileType type = typeList[i]; - for (pugi::xml_node fileNode = root.child(tag.c_str()); fileNode; fileNode = - fileNode.next_sibling(tag.c_str())) { - const std::string path = - Utils::FileSystem::resolveRelativePath(fileNode.child("path").text().get(), - relativeTo, false); + for (pugi::xml_node fileNode = root.child(tag.c_str()); fileNode; + fileNode = fileNode.next_sibling(tag.c_str())) { + const std::string path = Utils::FileSystem::resolveRelativePath( + fileNode.child("path").text().get(), relativeTo, false); if (!trustGamelist && !Utils::FileSystem::exists(path)) { - LOG(LogWarning) << (type == GAME ? "File \"" : "Folder \"") << path << - "\" does not exist, ignoring entry"; + LOG(LogWarning) << (type == GAME ? "File \"" : "Folder \"") << path + << "\" does not exist, ignoring entry"; continue; } // Skip hidden files, check both the file itself and the directory in which // it is located. - if (!showHiddenFiles && (Utils::FileSystem::isHidden(path) || - Utils::FileSystem::isHidden(Utils::FileSystem::getParent(path)))) { - LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping hidden file \"" << - path << "\""; + if (!showHiddenFiles && + (Utils::FileSystem::isHidden(path) || + Utils::FileSystem::isHidden(Utils::FileSystem::getParent(path)))) { + LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping hidden file \"" << path + << "\""; continue; } @@ -163,8 +164,8 @@ void parseGamelist(SystemData* system) else { // Skip arcade asset entries as these will not be used in any way inside // the application. - LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping arcade asset \"" << - file->getName() << "\""; + LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping arcade asset \"" + << file->getName() << "\""; delete file; continue; } @@ -174,9 +175,10 @@ void parseGamelist(SystemData* system) // application restart. if (!Settings::getInstance()->getBool("ShowHiddenGames")) { if (file->getHidden()) { - LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping hidden " << - (type == GAME ? "file" : "folder") << " entry \"" << - file->getName() << "\"" << " (\"" << file->getPath() << "\")"; + LOG(LogDebug) << "Gamelist::parseGamelist(): Skipping hidden " + << (type == GAME ? "file" : "folder") << " entry \"" + << file->getName() << "\"" + << " (\"" << file->getPath() << "\")"; delete file; } // Also delete any folders which are empty, i.e. all their entries are hidden. @@ -188,8 +190,10 @@ void parseGamelist(SystemData* system) } } -void addFileDataNode(pugi::xml_node& parent, const FileData* file, - const std::string& tag, SystemData* system) +void addFileDataNode(pugi::xml_node& parent, + const FileData* file, + const std::string& tag, + SystemData* system) { // Create game and add to parent node. pugi::xml_node newNode = parent.append_child(tag.c_str()); @@ -199,8 +203,8 @@ void addFileDataNode(pugi::xml_node& parent, const FileData* file, // First element is "name", there's only one element and the name is the default. if (newNode.children().begin() == newNode.child("name") && - ++newNode.children().begin() == newNode.children().end() && - newNode.child("name").text().get() == file->getDisplayName()) { + ++newNode.children().begin() == newNode.children().end() && + newNode.child("name").text().get() == file->getDisplayName()) { // If the only info is the default name, don't bother // with this node, delete it and ultimately do nothing. @@ -211,8 +215,9 @@ void addFileDataNode(pugi::xml_node& parent, const FileData* file, // Try and make the path relative if we can so things still // work if we change the ROM folder location in the future. - newNode.prepend_child("path").text().set(Utils::FileSystem::createRelativePath(file-> - getPath(), system->getStartPath(), false).c_str()); + newNode.prepend_child("path").text().set( + Utils::FileSystem::createRelativePath(file->getPath(), system->getStartPath(), false) + .c_str()); } } @@ -232,23 +237,23 @@ void updateGamelist(SystemData* system) if (Utils::FileSystem::exists(xmlReadPath)) { // Parse an existing file first. - #if defined(_WIN64) + +#if defined(_WIN64) pugi::xml_parse_result result = - doc.load_file(Utils::String::stringToWideString(xmlReadPath).c_str()); - #else + doc.load_file(Utils::String::stringToWideString(xmlReadPath).c_str()); +#else pugi::xml_parse_result result = doc.load_file(xmlReadPath.c_str()); - #endif +#endif if (!result) { - LOG(LogError) << "Error parsing gamelist file \"" << xmlReadPath << "\": " << - result.description(); + LOG(LogError) << "Error parsing gamelist file \"" << xmlReadPath + << "\": " << result.description(); return; } root = doc.child("gameList"); if (!root) { - LOG(LogError) << "Couldn't find node in gamelist \"" << - xmlReadPath << "\""; + LOG(LogError) << "Couldn't find node in gamelist \"" << xmlReadPath << "\""; return; } } @@ -266,8 +271,8 @@ void updateGamelist(SystemData* system) // Get only files, no folders. std::vector files = rootFolder->getFilesRecursive(GAME | FOLDER); // Iterate through all files, checking if they're already in the XML file. - for (std::vector::const_iterator fit = files.cbegin(); - fit != files.cend(); fit++) { + for (std::vector::const_iterator fit = files.cbegin(); // Line break. + fit != files.cend(); fit++) { const std::string tag = ((*fit)->getType() == GAME) ? "game" : "folder"; // Do not touch if it wasn't changed and is not flagged for deletion. @@ -277,16 +282,16 @@ void updateGamelist(SystemData* system) // Check if the file already exists in the XML file. // If it does, remove the entry before adding it back. for (pugi::xml_node fileNode = root.child(tag.c_str()); fileNode; - fileNode = fileNode.next_sibling(tag.c_str())) { + fileNode = fileNode.next_sibling(tag.c_str())) { pugi::xml_node pathNode = fileNode.child("path"); if (!pathNode) { LOG(LogError) << "<" << tag << "> node contains no child"; continue; } - std::string nodePath = Utils::FileSystem::getCanonicalPath( - Utils::FileSystem::resolveRelativePath(pathNode.text().get(), - system->getStartPath(), true)); + std::string nodePath = + Utils::FileSystem::getCanonicalPath(Utils::FileSystem::resolveRelativePath( + pathNode.text().get(), system->getStartPath(), true)); std::string gamePath = Utils::FileSystem::getCanonicalPath((*fit)->getPath()); if (nodePath == gamePath) { @@ -312,16 +317,17 @@ void updateGamelist(SystemData* system) std::string xmlWritePath(system->getGamelistPath(true)); Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(xmlWritePath)); - LOG(LogDebug) << "Gamelist::updateGamelist(): Added/updated " << numUpdated << - (numUpdated == 1 ? " entity in \"" : " entities in \"") << xmlReadPath << "\""; + LOG(LogDebug) << "Gamelist::updateGamelist(): Added/updated " << numUpdated + << (numUpdated == 1 ? " entity in \"" : " entities in \"") << xmlReadPath + << "\""; - #if defined(_WIN64) +#if defined(_WIN64) if (!doc.save_file(Utils::String::stringToWideString(xmlWritePath).c_str())) { - #else +#else if (!doc.save_file(xmlWritePath.c_str())) { - #endif - LOG(LogError) << "Error saving gamelist.xml to \"" << - xmlWritePath << "\" (for system " << system->getName() << ")"; +#endif + LOG(LogError) << "Error saving gamelist.xml to \"" << xmlWritePath + << "\" (for system " << system->getName() << ")"; } } } diff --git a/es-app/src/MediaViewer.cpp b/es-app/src/MediaViewer.cpp index cf9aff0f4..d62630ff2 100644 --- a/es-app/src/MediaViewer.cpp +++ b/es-app/src/MediaViewer.cpp @@ -12,11 +12,14 @@ #if defined(BUILD_VLC_PLAYER) #include "components/VideoVlcComponent.h" #endif -#include "views/ViewController.h" #include "AudioManager.h" #include "Sound.h" +#include "views/ViewController.h" -MediaViewer::MediaViewer(Window* window) : mWindow(window), mVideo(nullptr), mImage(nullptr) +MediaViewer::MediaViewer(Window* window) + : mWindow(window) + , mVideo(nullptr) + , mImage(nullptr) { mWindow->setMediaViewer(this); } @@ -85,12 +88,12 @@ void MediaViewer::render() // Render a black background below the game media. Renderer::drawRect(0.0f, 0.0f, static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); + static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); if (mVideo && !mDisplayingImage) { mVideo->render(transform); - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) Renderer::shaderParameters videoParameters; unsigned int shaders = 0; if (Settings::getInstance()->getBool("MediaViewerVideoScanlines")) @@ -98,6 +101,7 @@ void MediaViewer::render() if (Settings::getInstance()->getBool("MediaViewerVideoBlur")) { shaders |= Renderer::SHADER_BLUR_HORIZONTAL; float heightModifier = Renderer::getScreenHeightModifier(); + // clang-format off if (heightModifier < 1) videoParameters.blurPasses = 2; // Below 1080 else if (heightModifier >= 4) @@ -112,18 +116,19 @@ void MediaViewer::render() videoParameters.blurPasses = 3; // 1440 else if (heightModifier >= 1) videoParameters.blurPasses = 2; // 1080 + // clang-format on } Renderer::shaderPostprocessing(shaders, videoParameters); - #endif +#endif } else if (mImage && mImage->hasImage() && mImage->getSize() != 0) { mImage->render(transform); - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) if (mCurrentImageIndex == mScreenShotIndex && - Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) + Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES); - #endif +#endif // This is necessary so that the video loops if viewing an image when // the video ends. @@ -244,14 +249,14 @@ void MediaViewer::playVideo() mDisplayingImage = false; ViewController::get()->onStopVideo(); - #if defined(BUILD_VLC_PLAYER) +#if defined(BUILD_VLC_PLAYER) if (Settings::getInstance()->getString("VideoPlayer") == "ffmpeg") mVideo = new VideoFFmpegComponent(mWindow); else mVideo = new VideoVlcComponent(mWindow); - #else +#else mVideo = new VideoFFmpegComponent(mWindow); - #endif +#endif mVideo->topWindow(true); mVideo->setOrigin(0.5f, 0.5f); @@ -259,10 +264,10 @@ void MediaViewer::playVideo() if (Settings::getInstance()->getBool("MediaViewerStretchVideos")) mVideo->setResize(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); else mVideo->setMaxSize(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); mVideo->setVideo(mVideoFile); mVideo->setMediaViewerMode(true); @@ -282,6 +287,6 @@ void MediaViewer::showImage(int index) mImage->setOrigin(0.5f, 0.5f); mImage->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f); mImage->setMaxSize(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); } } diff --git a/es-app/src/MediaViewer.h b/es-app/src/MediaViewer.h index fb6922851..3fecd9c3d 100644 --- a/es-app/src/MediaViewer.h +++ b/es-app/src/MediaViewer.h @@ -9,10 +9,10 @@ #ifndef ES_APP_MEDIA_VIEWER_H #define ES_APP_MEDIA_VIEWER_H -#include "components/ImageComponent.h" -#include "components/VideoComponent.h" #include "FileData.h" #include "Window.h" +#include "components/ImageComponent.h" +#include "components/VideoComponent.h" class MediaViewer : public Window::MediaViewer { diff --git a/es-app/src/MetaData.cpp b/es-app/src/MetaData.cpp index 29848888a..4b8b97432 100644 --- a/es-app/src/MetaData.cpp +++ b/es-app/src/MetaData.cpp @@ -9,11 +9,12 @@ #include "MetaData.h" -#include "utils/FileSystemUtil.h" #include "Log.h" +#include "utils/FileSystemUtil.h" #include +// clang-format off MetaDataDecl gameDecls[] = { // key, type, default, statistic, name in GuiMetaDataEd, prompt in GuiMetaDataEd, shouldScrape {"name", MD_STRING, "", false, "name", "enter name", true}, @@ -39,9 +40,6 @@ MetaDataDecl gameDecls[] = { {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; -const std::vector gameMDD(gameDecls, gameDecls + - sizeof(gameDecls) / sizeof(gameDecls[0])); - MetaDataDecl folderDecls[] = { {"name", MD_STRING, "", false, "name", "enter name", true}, {"desc", MD_MULTILINE_STRING, "", false, "description", "enter description", true}, @@ -59,13 +57,18 @@ MetaDataDecl folderDecls[] = { {"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false}, {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; +// clang-format on -const std::vector folderMDD(folderDecls, folderDecls + - sizeof(folderDecls) / sizeof(folderDecls[0])); +const std::vector gameMDD(gameDecls, + gameDecls + sizeof(gameDecls) / sizeof(gameDecls[0])); + +const std::vector folderMDD(folderDecls, + folderDecls + + sizeof(folderDecls) / sizeof(folderDecls[0])); const std::vector& getMDDByType(MetaDataListType type) { - switch(type) { + switch (type) { case GAME_METADATA: return gameMDD; case FOLDER_METADATA: @@ -77,7 +80,8 @@ const std::vector& getMDDByType(MetaDataListType type) } MetaDataList::MetaDataList(MetaDataListType type) - : mType(type), mWasChanged(false) + : mType(type) + , mWasChanged(false) { const std::vector& mdd = getMDD(); for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) @@ -85,7 +89,8 @@ MetaDataList::MetaDataList(MetaDataListType type) } MetaDataList MetaDataList::createFromXML(MetaDataListType type, - pugi::xml_node& node, const std::string& relativeTo) + pugi::xml_node& node, + const std::string& relativeTo) { MetaDataList mdl(type); @@ -107,8 +112,9 @@ MetaDataList MetaDataList::createFromXML(MetaDataListType type, return mdl; } -void MetaDataList::appendToXML(pugi::xml_node& parent, bool ignoreDefaults, - const std::string& relativeTo) const +void MetaDataList::appendToXML(pugi::xml_node& parent, + bool ignoreDefaults, + const std::string& relativeTo) const { const std::vector& mdd = getMDD(); @@ -138,30 +144,33 @@ void MetaDataList::set(const std::string& key, const std::string& value) const std::string& MetaDataList::get(const std::string& key) const { - // Check that the key actually exists, otherwise return empty string. + // Check that the key actually exists, otherwise return an empty string. if (mMap.count(key) > 0) return mMap.at(key); else - - return mNoResult; + return mNoResult; } int MetaDataList::getInt(const std::string& key) const { + // Return integer value. return atoi(get(key).c_str()); } float MetaDataList::getFloat(const std::string& key) const { + // Return float value. return static_cast(atof(get(key).c_str())); } bool MetaDataList::wasChanged() const { + // Return whether the metadata was changed. return mWasChanged; } void MetaDataList::resetChangedFlag() { + // Reset the change flag. mWasChanged = false; } diff --git a/es-app/src/MetaData.h b/es-app/src/MetaData.h index 84220b6c2..1342c1d35 100644 --- a/es-app/src/MetaData.h +++ b/es-app/src/MetaData.h @@ -18,7 +18,10 @@ #include #include -namespace pugi { class xml_node; } +namespace pugi +{ + class xml_node; +} enum MetaDataType { // Generic types. @@ -40,7 +43,7 @@ struct MetaDataDecl { std::string key; MetaDataType type; std::string defaultValue; - // If true, ignore values for this metadata. + // If true, ignore values for this metadata. bool isStatistic; // Displayed as this in editors. std::string displayName; @@ -51,7 +54,7 @@ struct MetaDataDecl { }; enum MetaDataListType { - GAME_METADATA, + GAME_METADATA, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). FOLDER_METADATA }; @@ -61,9 +64,11 @@ class MetaDataList { public: static MetaDataList createFromXML(MetaDataListType type, - pugi::xml_node& node, const std::string& relativeTo); - void appendToXML(pugi::xml_node& parent, bool ignoreDefaults, - const std::string& relativeTo) const; + pugi::xml_node& node, + const std::string& relativeTo); + void appendToXML(pugi::xml_node& parent, + bool ignoreDefaults, + const std::string& relativeTo) const; MetaDataList(MetaDataListType type); @@ -76,10 +81,12 @@ public: bool wasChanged() const; void resetChangedFlag(); - inline MetaDataListType getType() const { return mType; } - inline const std::vector& getMDD() const { return getMDDByType(getType()); } - inline const std::vector& getMDD(MetaDataListType type) const - { return getMDDByType(type); } + MetaDataListType getType() const { return mType; } + const std::vector& getMDD() const { return getMDDByType(getType()); } + const std::vector& getMDD(MetaDataListType type) const + { + return getMDDByType(type); + } private: MetaDataListType mType; diff --git a/es-app/src/MiximageGenerator.cpp b/es-app/src/MiximageGenerator.cpp index 0fbc0301c..d16105bef 100644 --- a/es-app/src/MiximageGenerator.cpp +++ b/es-app/src/MiximageGenerator.cpp @@ -9,47 +9,45 @@ #include "MiximageGenerator.h" -#include "math/Misc.h" -#include "utils/StringUtil.h" #include "Log.h" #include "Settings.h" #include "SystemData.h" +#include "math/Misc.h" +#include "utils/StringUtil.h" #include MiximageGenerator::MiximageGenerator(FileData* game, std::string& resultMessage) - : mGame(game), - mResultMessage(resultMessage), - mWidth(1280), - mHeight(960), - mMarquee(false), - mBox3D(false), - mCover(false) + : mGame(game) + , mResultMessage(resultMessage) + , mWidth(1280) + , mHeight(960) + , mMarquee(false) + , mBox3D(false) + , mCover(false) { } -MiximageGenerator::~MiximageGenerator() -{ -} +MiximageGenerator::~MiximageGenerator() {} void MiximageGenerator::startThread(std::promise* miximagePromise) { mMiximagePromise = miximagePromise; LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): Creating miximage for \"" - << mGame->getFileName() << "\""; + << mGame->getFileName() << "\""; if (mGame->getMiximagePath() != "" && !Settings::getInstance()->getBool("MiximageOverwrite")) { LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): File already exists and miximage " - "overwriting has not been enabled, aborting"; + "overwriting has not been enabled, aborting"; mMiximagePromise->set_value(true); return; } if ((mScreenshotPath = mGame->getScreenshotPath()) == "") { LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): " - "No screenshot image found, aborting"; - mResultMessage = "No screenshot image found, couldn't generate miximage"; + "No screenshot image found, aborting"; + mResultMessage = "No screenshot image found, couldn't generate miximage"; mMiximagePromise->set_value(true); return; } @@ -68,14 +66,14 @@ void MiximageGenerator::startThread(std::promise* miximagePromise) mBox3D = true; } else if (Settings::getInstance()->getBool("MiximageCoverFallback") && - (mCoverPath= mGame->getCoverPath()) != "") { + (mCoverPath = mGame->getCoverPath()) != "") { LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): " - "No 3D box image found, using cover image as fallback"; + "No 3D box image found, using cover image as fallback"; mCover = true; } else if (Settings::getInstance()->getBool("MiximageCoverFallback")) { LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): " - "No 3D box or cover images found"; + "No 3D box or cover images found"; } else { LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): No 3D box image found"; @@ -93,9 +91,10 @@ void MiximageGenerator::startThread(std::promise* miximagePromise) else { const auto endTime = std::chrono::system_clock::now(); - LOG(LogDebug) << "MiximageGenerator::MiximageGenerator(): Processing completed in: " << - std::chrono::duration_cast - (endTime - startTime).count() << " ms"; + LOG(LogDebug) + << "MiximageGenerator::MiximageGenerator(): Processing completed in: " + << std::chrono::duration_cast(endTime - startTime).count() + << " ms"; } mResultMessage = mMessage; @@ -113,19 +112,19 @@ bool MiximageGenerator::generateImage() unsigned int fileHeight = 0; unsigned int filePitch = 0; - #if defined(_WIN64) +#if defined(_WIN64) fileFormat = FreeImage_GetFileTypeU(Utils::String::stringToWideString(mScreenshotPath).c_str()); - #else +#else fileFormat = FreeImage_GetFileType(mScreenshotPath.c_str()); - #endif +#endif if (fileFormat == FIF_UNKNOWN) - #if defined(_WIN64) +#if defined(_WIN64) fileFormat = FreeImage_GetFIFFromFilenameU( - Utils::String::stringToWideString(mScreenshotPath).c_str()); - #else + Utils::String::stringToWideString(mScreenshotPath).c_str()); +#else fileFormat = FreeImage_GetFIFFromFilename(mScreenshotPath.c_str()); - #endif +#endif if (fileFormat == FIF_UNKNOWN) { LOG(LogError) << "Screenshot image in unknown image format, aborting"; @@ -135,12 +134,12 @@ bool MiximageGenerator::generateImage() // Make sure that we can actually read this format. if (FreeImage_FIFSupportsReading(fileFormat)) { - #if defined(_WIN64) - screenshotFile = FreeImage_LoadU(fileFormat, - Utils::String::stringToWideString(mScreenshotPath).c_str()); - #else +#if defined(_WIN64) + screenshotFile = + FreeImage_LoadU(fileFormat, Utils::String::stringToWideString(mScreenshotPath).c_str()); +#else screenshotFile = FreeImage_Load(fileFormat, mScreenshotPath.c_str()); - #endif +#endif } else { LOG(LogError) << "Screenshot file format not supported"; @@ -155,20 +154,20 @@ bool MiximageGenerator::generateImage() } if (mMarquee) { - #if defined(_WIN64) - fileFormat = FreeImage_GetFileTypeU( - Utils::String::stringToWideString(mMarqueePath).c_str()); - #else +#if defined(_WIN64) + fileFormat = + FreeImage_GetFileTypeU(Utils::String::stringToWideString(mMarqueePath).c_str()); +#else fileFormat = FreeImage_GetFileType(mMarqueePath.c_str()); - #endif +#endif if (fileFormat == FIF_UNKNOWN) - #if defined(_WIN64) +#if defined(_WIN64) fileFormat = FreeImage_GetFIFFromFilenameU( - Utils::String::stringToWideString(mMarqueePath).c_str()); - #else + Utils::String::stringToWideString(mMarqueePath).c_str()); +#else fileFormat = FreeImage_GetFIFFromFilename(mMarqueePath.c_str()); - #endif +#endif if (fileFormat == FIF_UNKNOWN) { LOG(LogDebug) << "Marquee in unknown format, skipping image"; @@ -180,12 +179,12 @@ bool MiximageGenerator::generateImage() mMarquee = false; } else { - #if defined(_WIN64) +#if defined(_WIN64) marqueeFile = FreeImage_LoadU(fileFormat, - Utils::String::stringToWideString(mMarqueePath).c_str()); - #else + Utils::String::stringToWideString(mMarqueePath).c_str()); +#else marqueeFile = FreeImage_Load(fileFormat, mMarqueePath.c_str()); - #endif +#endif if (!marqueeFile) { LOG(LogError) << "Couldn't load marquee image, corrupt file?"; mMessage = "Error loading marquee image, corrupt file?"; @@ -195,19 +194,19 @@ bool MiximageGenerator::generateImage() } if (mBox3D) { - #if defined(_WIN64) +#if defined(_WIN64) fileFormat = FreeImage_GetFileTypeU(Utils::String::stringToWideString(mBox3DPath).c_str()); - #else +#else fileFormat = FreeImage_GetFileType(mBox3DPath.c_str()); - #endif +#endif if (fileFormat == FIF_UNKNOWN) - #if defined(_WIN64) +#if defined(_WIN64) fileFormat = FreeImage_GetFIFFromFilenameU( - Utils::String::stringToWideString(mBox3DPath).c_str()); - #else + Utils::String::stringToWideString(mBox3DPath).c_str()); +#else fileFormat = FreeImage_GetFIFFromFilename(mBox3DPath.c_str()); - #endif +#endif if (fileFormat == FIF_UNKNOWN) { LOG(LogDebug) << "3D box in unknown format, skipping image"; @@ -219,12 +218,12 @@ bool MiximageGenerator::generateImage() mBox3D = false; } else { - #if defined(_WIN64) - boxFile = FreeImage_LoadU(fileFormat, - Utils::String::stringToWideString(mBox3DPath).c_str()); - #else +#if defined(_WIN64) + boxFile = + FreeImage_LoadU(fileFormat, Utils::String::stringToWideString(mBox3DPath).c_str()); +#else boxFile = FreeImage_Load(fileFormat, mBox3DPath.c_str()); - #endif +#endif if (!boxFile) { LOG(LogError) << "Couldn't load 3D box image, corrupt file?"; mMessage = "Error loading 3d box image, corrupt file?"; @@ -233,20 +232,19 @@ bool MiximageGenerator::generateImage() } } else if (mCover) { - #if defined(_WIN64) - fileFormat = FreeImage_GetFileTypeU( - Utils::String::stringToWideString(mCoverPath).c_str()); - #else +#if defined(_WIN64) + fileFormat = FreeImage_GetFileTypeU(Utils::String::stringToWideString(mCoverPath).c_str()); +#else fileFormat = FreeImage_GetFileType(mCoverPath.c_str()); - #endif +#endif if (fileFormat == FIF_UNKNOWN) - #if defined(_WIN64) +#if defined(_WIN64) fileFormat = FreeImage_GetFIFFromFilenameU( - Utils::String::stringToWideString(mCoverPath).c_str()); - #else + Utils::String::stringToWideString(mCoverPath).c_str()); +#else fileFormat = FreeImage_GetFIFFromFilename(mCoverPath.c_str()); - #endif +#endif if (fileFormat == FIF_UNKNOWN) { LOG(LogDebug) << "Box cover in unknown format, skipping image"; @@ -258,12 +256,12 @@ bool MiximageGenerator::generateImage() mCover = false; } else { - #if defined(_WIN64) - boxFile = FreeImage_LoadU(fileFormat, - Utils::String::stringToWideString(mCoverPath).c_str()); - #else +#if defined(_WIN64) + boxFile = + FreeImage_LoadU(fileFormat, Utils::String::stringToWideString(mCoverPath).c_str()); +#else boxFile = FreeImage_Load(fileFormat, mCoverPath.c_str()); - #endif +#endif if (!boxFile) { LOG(LogError) << "Couldn't load box cover image, corrupt file?"; mMessage = "Error loading box cover image, corrupt file?"; @@ -318,7 +316,7 @@ bool MiximageGenerator::generateImage() std::vector screenshotVector(fileWidth * fileHeight * 4); FreeImage_ConvertToRawBits(reinterpret_cast(&screenshotVector.at(0)), screenshotFile, - filePitch, 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1); + filePitch, 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1); CImg screenshotImage(fileWidth, fileHeight, 1, 4, 0); @@ -386,10 +384,10 @@ bool MiximageGenerator::generateImage() std::vector marqueeVector(fileWidth * fileHeight * 4); FreeImage_ConvertToRawBits(reinterpret_cast(&marqueeVector.at(0)), marqueeFile, - filePitch, 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1); + filePitch, 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1); marqueeImage = CImg(FreeImage_GetWidth(marqueeFile), - FreeImage_GetHeight(marqueeFile), 1, 4, 0); + FreeImage_GetHeight(marqueeFile), 1, 4, 0); Utils::CImg::convertRGBAToCImg(marqueeVector, marqueeImage); Utils::CImg::removeTransparentPadding(marqueeImage); @@ -409,7 +407,7 @@ bool MiximageGenerator::generateImage() yPosMarquee = 0; // Only RGB channels for the image. - marqueeImageRGB = CImg(marqueeImage.get_shared_channels(0,2)); + marqueeImageRGB = CImg(marqueeImage.get_shared_channels(0, 2)); // Only alpha channel for the image. marqueeImageAlpha = CImg(marqueeImage.get_shared_channel(3)); } @@ -427,17 +425,17 @@ bool MiximageGenerator::generateImage() std::vector boxVector(fileWidth * fileHeight * 4); - FreeImage_ConvertToRawBits(reinterpret_cast(&boxVector.at(0)), boxFile, - filePitch, 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1); + FreeImage_ConvertToRawBits(reinterpret_cast(&boxVector.at(0)), boxFile, filePitch, + 32, FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE, 1); - boxImage = CImg(FreeImage_GetWidth(boxFile), - FreeImage_GetHeight(boxFile), 1, 4); + boxImage = + CImg(FreeImage_GetWidth(boxFile), FreeImage_GetHeight(boxFile), 1, 4); Utils::CImg::convertRGBAToCImg(boxVector, boxImage); Utils::CImg::removeTransparentPadding(boxImage); - float scaleFactor = static_cast(boxTargetHeight) / - static_cast(boxImage.height()); + float scaleFactor = + static_cast(boxTargetHeight) / static_cast(boxImage.height()); unsigned int width = static_cast(static_cast(boxImage.width()) * scaleFactor); unsigned int targetWidth = 0; @@ -464,7 +462,7 @@ bool MiximageGenerator::generateImage() yPosBox = canvasImage.height() - boxImage.height(); // Only RGB channels for the image. - boxImageRGB = CImg(boxImage.get_shared_channels(0,2)); + boxImageRGB = CImg(boxImage.get_shared_channels(0, 2)); // Only alpha channel for the image. boxImageAlpha = CImg(boxImage.get_shared_channel(3)); } @@ -478,20 +476,15 @@ bool MiximageGenerator::generateImage() sampleFrameColor(screenshotImage, frameColor); // Upper / lower frame. - frameImage.draw_rectangle( - xPosScreenshot + 2, - yPosScreenshot - screenshotFrameWidth, - xPosScreenshot + screenshotWidth - 2, - yPosScreenshot + screenshotHeight + screenshotFrameWidth - 1, - frameColor); + frameImage.draw_rectangle(xPosScreenshot + 2, yPosScreenshot - screenshotFrameWidth, + xPosScreenshot + screenshotWidth - 2, + yPosScreenshot + screenshotHeight + screenshotFrameWidth - 1, + frameColor); // Left / right frame. - frameImage.draw_rectangle( - xPosScreenshot - screenshotFrameWidth, - yPosScreenshot + 2, - xPosScreenshot + screenshotWidth + screenshotFrameWidth - 1, - yPosScreenshot + screenshotHeight - 2, - frameColor); + frameImage.draw_rectangle(xPosScreenshot - screenshotFrameWidth, yPosScreenshot + 2, + xPosScreenshot + screenshotWidth + screenshotFrameWidth - 1, + yPosScreenshot + screenshotHeight - 2, frameColor); // We draw circles in order to get rounded corners for the frame. const unsigned int circleRadius = 8 * resolutionMultiplier; @@ -499,16 +492,18 @@ bool MiximageGenerator::generateImage() // Upper left corner. frameImage.draw_circle(xPosScreenshot + circleOffset, yPosScreenshot + circleOffset, - circleRadius, frameColor); + circleRadius, frameColor); // Upper right corner. frameImage.draw_circle(xPosScreenshot + screenshotWidth - circleOffset - 1, - yPosScreenshot + circleOffset, circleRadius, frameColor); + yPosScreenshot + circleOffset, circleRadius, frameColor); // Lower right corner. frameImage.draw_circle(xPosScreenshot + screenshotWidth - circleOffset - 1, - yPosScreenshot + screenshotHeight - circleOffset - 1, circleRadius, frameColor); + yPosScreenshot + screenshotHeight - circleOffset - 1, circleRadius, + frameColor); // Lower left corner. frameImage.draw_circle(xPosScreenshot + circleOffset, - yPosScreenshot + screenshotHeight - circleOffset - 1, circleRadius, frameColor); + yPosScreenshot + screenshotHeight - circleOffset - 1, circleRadius, + frameColor); CImg frameImageRGB(frameImage.get_shared_channels(0, 2)); @@ -516,8 +511,8 @@ bool MiximageGenerator::generateImage() canvasImage.draw_image(xPosScreenshot, yPosScreenshot, screenshotImage); if (mMarquee) - canvasImage.draw_image(xPosMarquee, yPosMarquee, marqueeImageRGB, - marqueeImageAlpha, 1, 255); + canvasImage.draw_image(xPosMarquee, yPosMarquee, marqueeImageRGB, marqueeImageAlpha, 1, + 255); if (mBox3D || mCover) canvasImage.draw_image(xPosBox, yPosBox, boxImageRGB, boxImageAlpha, 1, 255); @@ -528,15 +523,16 @@ bool MiximageGenerator::generateImage() FIBITMAP* mixImage = nullptr; mixImage = FreeImage_ConvertFromRawBits(&canvasVector.at(0), canvasImage.width(), - canvasImage.height(), canvasImage.width() * 4, 32, - FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE); + canvasImage.height(), canvasImage.width() * 4, 32, + FI_RGBA_RED, FI_RGBA_GREEN, FI_RGBA_BLUE); - #if defined(_WIN64) - bool savedImage = (FreeImage_SaveU(FIF_PNG, mixImage, - Utils::String::stringToWideString(getSavePath()).c_str()) != 0); - #else +#if defined(_WIN64) + bool savedImage = + (FreeImage_SaveU(FIF_PNG, mixImage, + Utils::String::stringToWideString(getSavePath()).c_str()) != 0); +#else bool savedImage = (FreeImage_Save(FIF_PNG, mixImage, getSavePath().c_str()) != 0); - #endif +#endif if (!savedImage) { LOG(LogError) << "Couldn't save miximage, permission problems or disk full?"; @@ -555,7 +551,9 @@ bool MiximageGenerator::generateImage() } void MiximageGenerator::calculateMarqueeSize(const unsigned int& targetWidth, - const unsigned int& targetHeight, unsigned int& width, unsigned int& height) + const unsigned int& targetHeight, + unsigned int& width, + unsigned int& height) { unsigned int adjustedTargetWidth = 0; float widthModifier = 0.5f; @@ -572,13 +570,13 @@ void MiximageGenerator::calculateMarqueeSize(const unsigned int& targetWidth, if (widthRatio >= 4) widthModifier += Math::clamp(widthRatio / 40.0f, 0.0f, 0.3f); - adjustedTargetWidth = static_cast( - static_cast(targetWidth) * widthModifier); + adjustedTargetWidth = + static_cast(static_cast(targetWidth) * widthModifier); scaleFactor = static_cast(adjustedTargetWidth) / static_cast(width); // For really tall and narrow images, we may have exceeded the target height. if (static_cast(scaleFactor * static_cast(height)) > - static_cast(targetHeight)) + static_cast(targetHeight)) scaleFactor = static_cast(targetHeight) / static_cast(height); width = static_cast(static_cast(width) * scaleFactor); @@ -586,7 +584,7 @@ void MiximageGenerator::calculateMarqueeSize(const unsigned int& targetWidth, } void MiximageGenerator::sampleFrameColor(CImg& screenshotImage, - unsigned char (&frameColor)[4]) + unsigned char (&frameColor)[4]) { // Calculate the number of samples relative to the configured resolution so we get // the same result regardless of miximage target size seting. @@ -627,7 +625,7 @@ void MiximageGenerator::sampleFrameColor(CImg& screenshotImage, unsigned char blueC = Math::clamp(static_cast(blueLine / 255), 0, 255); // Convert to the HSL color space to be able to modify saturation and lightness. - CImg colorHSL = CImg<>(1,1,1,3).fill(redC, greenC, blueC).RGBtoHSL(); + CImg colorHSL = CImg<>(1, 1, 1, 3).fill(redC, greenC, blueC).RGBtoHSL(); float hue = colorHSL(0, 0, 0, 0); float saturation = colorHSL(0, 0, 0, 1); @@ -656,7 +654,7 @@ std::string MiximageGenerator::getSavePath() // Extract possible subfolders from the path. if (mGame->getSystemEnvData()->mStartPath != "") subFolders = Utils::String::replace(Utils::FileSystem::getParent(mGame->getPath()), - mGame->getSystemEnvData()->mStartPath, ""); + mGame->getSystemEnvData()->mStartPath, ""); std::string path = FileData::getMediaDirectory(); diff --git a/es-app/src/MiximageGenerator.h b/es-app/src/MiximageGenerator.h index 4d444ca45..5da3dbca8 100644 --- a/es-app/src/MiximageGenerator.h +++ b/es-app/src/MiximageGenerator.h @@ -10,9 +10,9 @@ #ifndef ES_APP_SCRAPERS_MIXIMAGE_GENERATOR_H #define ES_APP_SCRAPERS_MIXIMAGE_GENERATOR_H -#include "utils/CImgUtil.h" #include "FileData.h" #include "GuiComponent.h" +#include "utils/CImgUtil.h" #include #include @@ -29,8 +29,10 @@ public: private: bool generateImage(); - void calculateMarqueeSize(const unsigned int& targetWidth, const unsigned int& targetHeight, - unsigned int& width, unsigned int& height); + void calculateMarqueeSize(const unsigned int& targetWidth, + const unsigned int& targetHeight, + unsigned int& width, + unsigned int& height); void sampleFrameColor(CImg& screenshotImage, unsigned char (&frameColor)[4]); std::string getSavePath(); diff --git a/es-app/src/PlatformId.cpp b/es-app/src/PlatformId.cpp index a10aa4971..cc3b20f49 100644 --- a/es-app/src/PlatformId.cpp +++ b/es-app/src/PlatformId.cpp @@ -13,6 +13,7 @@ namespace PlatformIds { + // clang-format off std::vector platformNames = { "unknown", // Nothing set. @@ -132,6 +133,7 @@ namespace PlatformIds "ignore", // Do not allow scraping for this system. "invalid" }; + // clang-format on PlatformId getPlatformId(const std::string& str) { @@ -148,6 +150,8 @@ namespace PlatformIds const std::string getPlatformName(PlatformId id) { + // Return the platform name. return platformNames[id]; } -} + +} // namespace PlatformIds diff --git a/es-app/src/PlatformId.h b/es-app/src/PlatformId.h index e8be86f4e..dd596682d 100644 --- a/es-app/src/PlatformId.h +++ b/es-app/src/PlatformId.h @@ -135,6 +135,7 @@ namespace PlatformIds PlatformId getPlatformId(const std::string& str); const std::string getPlatformName(PlatformId id); -} + +} // namespace PlatformIds #endif // ES_APP_PLATFORM_ID_H diff --git a/es-app/src/SystemData.cpp b/es-app/src/SystemData.cpp index fee01b1f9..1a2f661c0 100644 --- a/es-app/src/SystemData.cpp +++ b/es-app/src/SystemData.cpp @@ -11,12 +11,6 @@ #include "SystemData.h" -#include "resources/ResourceManager.h" -#include "utils/FileSystemUtil.h" -#include "utils/StringUtil.h" -#include "views/gamelist/IGameListView.h" -#include "views/UIModeController.h" -#include "views/ViewController.h" #include "CollectionSystemsManager.h" #include "FileFilterIndex.h" #include "FileSorts.h" @@ -25,6 +19,12 @@ #include "Platform.h" #include "Settings.h" #include "ThemeData.h" +#include "resources/ResourceManager.h" +#include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" +#include "views/UIModeController.h" +#include "views/ViewController.h" +#include "views/gamelist/IGameListView.h" #include #include @@ -39,14 +39,10 @@ FindRules::FindRules() loadFindRules(); } -FindRules::~FindRules() -{ -} - void FindRules::loadFindRules() { std::string customSystemsDirectory = - Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems"; + Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems"; std::string path = customSystemsDirectory + "/es_find_rules.xml"; @@ -54,16 +50,16 @@ void FindRules::loadFindRules() LOG(LogInfo) << "Found custom find rules configuration file"; } else { - #if defined(_WIN64) - path = ResourceManager::getInstance()-> - getResourcePath(":/systems/windows/es_find_rules.xml", false); - #elif defined(__APPLE__) - path = ResourceManager::getInstance()-> - getResourcePath(":/systems/macos/es_find_rules.xml", false); - #else - path = ResourceManager::getInstance()-> - getResourcePath(":/systems/unix/es_find_rules.xml", false); - #endif +#if defined(_WIN64) + path = ResourceManager::getInstance()->getResourcePath( + ":/systems/windows/es_find_rules.xml", false); +#elif defined(__APPLE__) + path = ResourceManager::getInstance()->getResourcePath(":/systems/macos/es_find_rules.xml", + false); +#else + path = ResourceManager::getInstance()->getResourcePath(":/systems/unix/es_find_rules.xml", + false); +#endif } if (path == "") { @@ -74,11 +70,11 @@ void FindRules::loadFindRules() LOG(LogInfo) << "Parsing find rules configuration file \"" << path << "\"..."; pugi::xml_document doc; - #if defined(_WIN64) +#if defined(_WIN64) pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str()); - #else +#else pugi::xml_parse_result res = doc.load_file(path.c_str()); - #endif +#endif if (!res) { LOG(LogError) << "Couldn't parse es_find_rules.xml: " << res.description(); @@ -97,81 +93,79 @@ void FindRules::loadFindRules() CoreRules coreRules; for (pugi::xml_node emulator = ruleList.child("emulator"); emulator; - emulator = emulator.next_sibling("emulator")) { + emulator = emulator.next_sibling("emulator")) { std::string emulatorName = emulator.attribute("name").as_string(); if (emulatorName.empty()) { LOG(LogWarning) << "Found emulator tag without name attribute, skipping entry"; continue; } if (mEmulators.find(emulatorName) != mEmulators.end()) { - LOG(LogWarning) << "Found repeating emulator tag \"" << emulatorName << - "\", skipping entry"; + LOG(LogWarning) << "Found repeating emulator tag \"" << emulatorName + << "\", skipping entry"; continue; } for (pugi::xml_node rule = emulator.child("rule"); rule; rule = rule.next_sibling("rule")) { std::string ruleType = rule.attribute("type").as_string(); if (ruleType.empty()) { - LOG(LogWarning) << "Found rule tag without type attribute for emulator \"" << - emulatorName << "\", skipping entry"; + LOG(LogWarning) << "Found rule tag without type attribute for emulator \"" + << emulatorName << "\", skipping entry"; continue; } - #if defined(_WIN64) +#if defined(_WIN64) if (ruleType != "winregistrypath" && ruleType != "systempath" && - ruleType != "staticpath") { - #else + ruleType != "staticpath") { +#else if (ruleType != "systempath" && ruleType != "staticpath") { - #endif - LOG(LogWarning) << "Found invalid rule type \"" << ruleType << - "\" for emulator \"" << emulatorName << "\", skipping entry"; +#endif + LOG(LogWarning) << "Found invalid rule type \"" << ruleType << "\" for emulator \"" + << emulatorName << "\", skipping entry"; continue; } for (pugi::xml_node entry = rule.child("entry"); entry; - entry = entry.next_sibling("entry")) { + entry = entry.next_sibling("entry")) { std::string entryValue = entry.text().get(); if (ruleType == "systempath") emulatorRules.systemPaths.push_back(entryValue); else if (ruleType == "staticpath") emulatorRules.staticPaths.push_back(entryValue); - #if defined(_WIN64) +#if defined(_WIN64) else if (ruleType == "winregistrypath") emulatorRules.winRegistryPaths.push_back(entryValue); - #endif +#endif } } mEmulators[emulatorName] = emulatorRules; emulatorRules.systemPaths.clear(); emulatorRules.staticPaths.clear(); - #if defined(_WIN64) +#if defined(_WIN64) emulatorRules.winRegistryPaths.clear(); - #endif +#endif } - for (pugi::xml_node core = ruleList.child("core"); core; - core = core.next_sibling("core")) { + for (pugi::xml_node core = ruleList.child("core"); core; core = core.next_sibling("core")) { std::string coreName = core.attribute("name").as_string(); if (coreName.empty()) { LOG(LogWarning) << "Found core tag without name attribute, skipping entry"; continue; } if (mCores.find(coreName) != mCores.end()) { - LOG(LogWarning) << "Found repeating core tag \"" << coreName << - "\", skipping entry"; + LOG(LogWarning) << "Found repeating core tag \"" << coreName << "\", skipping entry"; continue; } for (pugi::xml_node rule = core.child("rule"); rule; rule = rule.next_sibling("rule")) { std::string ruleType = rule.attribute("type").as_string(); if (ruleType.empty()) { - LOG(LogWarning) << "Found rule tag without type attribute for core \"" << - coreName << "\", skipping entry"; + LOG(LogWarning) << "Found rule tag without type attribute for core \"" << coreName + << "\", skipping entry"; continue; } if (ruleType != "corepath") { - LOG(LogWarning) << "Found invalid rule type \"" << ruleType << - "\" for core \"" << coreName << "\", skipping entry"; + LOG(LogWarning) << "Found invalid rule type \"" << ruleType << "\" for core \"" + << coreName << "\", skipping entry"; continue; } for (pugi::xml_node entry = rule.child("entry"); entry; - entry = entry.next_sibling("entry")) { + entry = entry.next_sibling("entry")) { std::string entryValue = entry.text().get(); if (ruleType == "corepath") coreRules.corePaths.push_back(entryValue); @@ -182,23 +176,22 @@ void FindRules::loadFindRules() } } -SystemData::SystemData( - const std::string& name, - const std::string& fullName, - SystemEnvironmentData* envData, - const std::string& themeFolder, - bool CollectionSystem, - bool CustomCollectionSystem) - : mName(name), - mFullName(fullName), - mEnvData(envData), - mThemeFolder(themeFolder), - mIsCollectionSystem(CollectionSystem), - mIsCustomCollectionSystem(CustomCollectionSystem), - mIsGroupedCustomCollectionSystem(false), - mIsGameSystem(true), - mScrapeFlag(false), - mPlaceholder(nullptr) +SystemData::SystemData(const std::string& name, + const std::string& fullName, + SystemEnvironmentData* envData, + const std::string& themeFolder, + bool CollectionSystem, + bool CustomCollectionSystem) + : mName(name) + , mFullName(fullName) + , mEnvData(envData) + , mThemeFolder(themeFolder) + , mIsCollectionSystem(CollectionSystem) + , mIsCustomCollectionSystem(CustomCollectionSystem) + , mIsGroupedCustomCollectionSystem(false) + , mIsGameSystem(true) + , mScrapeFlag(false) + , mPlaceholder(nullptr) { mFilterIndex = new FileFilterIndex(); @@ -220,7 +213,7 @@ SystemData::SystemData( setupSystemSortType(mRootFolder); mRootFolder->sort(mRootFolder->getSortTypeFromString(mRootFolder->getSortTypeString()), - Settings::getInstance()->getBool("FavoritesFirst")); + Settings::getInstance()->getBool("FavoritesFirst")); indexAllGameFilters(mRootFolder); } @@ -275,13 +268,13 @@ bool SystemData::populateFolder(FileData* folder) return false; for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); - it != dirContent.cend(); it++) { + it != dirContent.cend(); it++) { filePath = *it; // Skip any recursive symlinks as those would hang the application at various places. if (Utils::FileSystem::isSymlink(filePath)) { if (Utils::FileSystem::resolveSymlink(filePath) == - Utils::FileSystem::getFileName(filePath)) { + Utils::FileSystem::getFileName(filePath)) { LOG(LogWarning) << "Skipped \"" << filePath << "\" as it's a recursive symlink"; continue; } @@ -289,9 +282,9 @@ bool SystemData::populateFolder(FileData* folder) // Skip hidden files and folders. if (!showHiddenFiles && Utils::FileSystem::isHidden(filePath)) { - LOG(LogDebug) << "SystemData::populateFolder(): Skipping hidden " << - (Utils::FileSystem::isDirectory(filePath) ? "directory \"" : "file \"") << - filePath << "\""; + LOG(LogDebug) << "SystemData::populateFolder(): Skipping hidden " + << (Utils::FileSystem::isDirectory(filePath) ? "directory \"" : "file \"") + << filePath << "\""; continue; } @@ -306,7 +299,7 @@ bool SystemData::populateFolder(FileData* folder) isGame = false; if (std::find(mEnvData->mSearchExtensions.cbegin(), mEnvData->mSearchExtensions.cend(), - extension) != mEnvData->mSearchExtensions.cend()) { + extension) != mEnvData->mSearchExtensions.cend()) { FileData* newGame = new FileData(GAME, filePath, mEnvData, this); // Prevent new arcade assets from being added. @@ -326,15 +319,17 @@ bool SystemData::populateFolder(FileData* folder) if (Utils::FileSystem::isSymlink(filePath)) { const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(filePath); const std::string canonicalStartPath = - Utils::FileSystem::getCanonicalPath(mEnvData->mStartPath); - const std::string combinedPath = mEnvData->mStartPath + - canonicalPath.substr(canonicalStartPath.size(), - canonicalStartPath.size() - canonicalPath.size()); + Utils::FileSystem::getCanonicalPath(mEnvData->mStartPath); + const std::string combinedPath = + mEnvData->mStartPath + + canonicalPath.substr(canonicalStartPath.size(), + canonicalStartPath.size() - canonicalPath.size()); if (filePath.find(combinedPath) == 0) { LOG(LogWarning) << "Skipped \"" << filePath << "\" as it's a recursive symlink"; continue; } } + FileData* newFolder = new FileData(FOLDER, filePath, mEnvData, this); populateFolder(newFolder); @@ -352,17 +347,15 @@ void SystemData::indexAllGameFilters(const FileData* folder) { const std::vector& children = folder->getChildren(); - for (std::vector::const_iterator it = children.cbegin(); - it != children.cend(); it++) { + for (std::vector::const_iterator it = children.cbegin(); // Line break. + it != children.cend(); it++) { switch ((*it)->getType()) { - case GAME: { + case GAME: mFilterIndex->addToIndex(*it); - } - break; - case FOLDER: { + break; + case FOLDER: indexAllGameFilters(*it); - } - break; + break; default: break; } @@ -400,11 +393,11 @@ bool SystemData::loadConfig() LOG(LogInfo) << "Parsing systems configuration file \"" << path << "\"..."; pugi::xml_document doc; - #if defined(_WIN64) +#if defined(_WIN64) pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str()); - #else +#else pugi::xml_parse_result res = doc.load_file(path.c_str()); - #endif +#endif if (!res) { LOG(LogError) << "Couldn't parse es_systems.xml: " << res.description(); @@ -420,7 +413,7 @@ bool SystemData::loadConfig() } for (pugi::xml_node system = systemList.child("system"); system; - system = system.next_sibling("system")) { + system = system.next_sibling("system")) { std::string name; std::string fullname; std::string path; @@ -436,21 +429,21 @@ bool SystemData::loadConfig() // the ROM path configured as ROMDirectory in es_settings.xml. If it's set to "" // in this configuration file, the default hardcoded path $HOME/ROMs/ will be used. path = Utils::String::replace(path, "%ROMPATH%", rompath); - #if defined(_WIN64) +#if defined(_WIN64) path = Utils::String::replace(path, "\\", "/"); - #endif +#endif path = Utils::String::replace(path, "//", "/"); // Check that the ROM directory for the system is valid or otherwise abort the processing. if (!Utils::FileSystem::exists(path)) { - LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << - name << "\" as the defined ROM directory \"" << path << "\" does not exist"; + LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << name + << "\" as the defined ROM directory \"" << path << "\" does not exist"; continue; } if (!Utils::FileSystem::isDirectory(path)) { - LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << - name << "\" as the defined ROM directory \"" << path << - "\" is not actually a directory"; + LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << name + << "\" as the defined ROM directory \"" << path + << "\" is not actually a directory"; continue; } if (Utils::FileSystem::isSymlink(path)) { @@ -458,9 +451,9 @@ bool SystemData::loadConfig() // as that would lead to an infite loop, meaning the application would never start. std::string resolvedRompath = Utils::FileSystem::getCanonicalPath(rompath); if (resolvedRompath.find(Utils::FileSystem::getCanonicalPath(path)) == 0) { - LOG(LogWarning) << "Skipping system \"" << name << - "\" as the defined ROM directory \"" << path << - "\" is an infinitely recursive symlink"; + LOG(LogWarning) << "Skipping system \"" << name + << "\" as the defined ROM directory \"" << path + << "\" is an infinitely recursive symlink"; continue; } } @@ -472,7 +465,7 @@ bool SystemData::loadConfig() // Platform ID list const std::string platformList = - Utils::String::toLower(system.child("platform").text().get()); + Utils::String::toLower(system.child("platform").text().get()); std::vector platformStrs = readList(platformList); std::vector platformIds; for (auto it = platformStrs.cbegin(); it != platformStrs.cend(); it++) { @@ -489,8 +482,8 @@ bool SystemData::loadConfig() // If there's a platform entry defined but it does not match the list of supported // platforms, then generate a warning. if (str != "" && platformId == PlatformIds::PLATFORM_UNKNOWN) - LOG(LogWarning) << "Unknown platform \"" << str << "\" defined for system \"" << - name << "\""; + LOG(LogWarning) << "Unknown platform \"" << str << "\" defined for system \"" + << name << "\""; else if (platformId != PlatformIds::PLATFORM_UNKNOWN) platformIds.push_back(platformId); } @@ -501,26 +494,27 @@ bool SystemData::loadConfig() // Validate. if (name.empty()) { - LOG(LogError) << - "A system in the es_systems.xml file has no name defined, skipping entry"; + LOG(LogError) + << "A system in the es_systems.xml file has no name defined, skipping entry"; continue; } else if (fullname.empty() || path.empty() || extensions.empty() || cmd.empty()) { - LOG(LogError) << "System \"" << name << "\" is missing the fullname, path, " - "extension, or command tag, skipping entry"; + LOG(LogError) << "System \"" << name + << "\" is missing the fullname, path, " + "extension, or command tag, skipping entry"; continue; } // Convert path to generic directory seperators. path = Utils::FileSystem::getGenericPath(path); - #if defined(_WIN64) +#if defined(_WIN64) if (!Settings::getInstance()->getBool("ShowHiddenFiles") && - Utils::FileSystem::isHidden(path)) { + Utils::FileSystem::isHidden(path)) { LOG(LogWarning) << "Skipping hidden ROM folder \"" << path << "\""; continue; } - #endif +#endif // Create the system runtime environment data. SystemEnvironmentData* envData = new SystemEnvironmentData; @@ -535,8 +529,7 @@ bool SystemData::loadConfig() // If the option to show hidden games has been disabled, then check whether all // games for the system are hidden. That will flag the system as empty. if (!Settings::getInstance()->getBool("ShowHiddenGames")) { - std::vector recursiveGames = - newSys->getRootFolder()->getChildrenRecursive(); + std::vector recursiveGames = newSys->getRootFolder()->getChildrenRecursive(); onlyHidden = true; for (auto it = recursiveGames.cbegin(); it != recursiveGames.cend(); it++) { if ((*it)->getType() != FOLDER) { @@ -548,8 +541,8 @@ bool SystemData::loadConfig() } if (newSys->getRootFolder()->getChildrenByFilename().size() == 0 || onlyHidden) { - LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << - name << "\" as no files matched any of the defined file extensions"; + LOG(LogDebug) << "SystemData::loadConfig(): Skipping system \"" << name + << "\" as no files matched any of the defined file extensions"; delete newSys; } else { @@ -559,8 +552,7 @@ bool SystemData::loadConfig() // Sort systems by their full names. std::sort(std::begin(sSystemVector), std::end(sSystemVector), - [](SystemData* a, SystemData* b) { - return a->getFullName() < b->getFullName(); }); + [](SystemData* a, SystemData* b) { return a->getFullName() < b->getFullName(); }); // Don't load any collections if there are no systems available. if (sSystemVector.size() > 0) @@ -580,18 +572,18 @@ void SystemData::deleteSystems() std::string SystemData::getConfigPath(bool legacyWarning) { if (legacyWarning) { - std::string legacyConfigFile = Utils::FileSystem::getHomePath() + - "/.emulationstation/es_systems.cfg"; + std::string legacyConfigFile = + Utils::FileSystem::getHomePath() + "/.emulationstation/es_systems.cfg"; if (Utils::FileSystem::exists(legacyConfigFile)) { - LOG(LogInfo) << "Found legacy systems configuration file \"" << legacyConfigFile << - "\", to retain your customizations move it to " - "\"custom_systems/es_systems.xml\" or otherwise delete the file"; + LOG(LogInfo) << "Found legacy systems configuration file \"" << legacyConfigFile + << "\", to retain your customizations move it to " + "\"custom_systems/es_systems.xml\" or otherwise delete the file"; } } std::string customSystemsDirectory = - Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems"; + Utils::FileSystem::getHomePath() + "/.emulationstation/custom_systems"; if (!Utils::FileSystem::exists(customSystemsDirectory)) { LOG(LogInfo) << "Creating custom systems directory \"" << customSystemsDirectory << "\"..."; @@ -608,16 +600,14 @@ std::string SystemData::getConfigPath(bool legacyWarning) return path; } - #if defined(_WIN64) - path = ResourceManager::getInstance()-> - getResourcePath(":/systems/windows/es_systems.xml", true); - #elif defined(__APPLE__) - path = ResourceManager::getInstance()-> - getResourcePath(":/systems/macos/es_systems.xml", true); - #else - path = ResourceManager::getInstance()-> - getResourcePath(":/systems/unix/es_systems.xml", true); - #endif +#if defined(_WIN64) + path = + ResourceManager::getInstance()->getResourcePath(":/systems/windows/es_systems.xml", true); +#elif defined(__APPLE__) + path = ResourceManager::getInstance()->getResourcePath(":/systems/macos/es_systems.xml", true); +#else + path = ResourceManager::getInstance()->getResourcePath(":/systems/unix/es_systems.xml", true); +#endif return path; } @@ -627,16 +617,16 @@ bool SystemData::createSystemDirectories() std::string path = getConfigPath(false); const std::string rompath = FileData::getROMDirectory(); - if (!Utils::FileSystem::exists(path)) { + if (!Utils::FileSystem::exists(path)) { LOG(LogInfo) << "Systems configuration file does not exist, aborting"; return true; - } + } LOG(LogInfo) << "Generating ROM directory structure..."; if (Utils::FileSystem::exists(rompath) && Utils::FileSystem::isRegularFile(rompath)) { - LOG(LogError) << - "Requested ROM directory \"" << rompath << "\" is actually a file, aborting"; + LOG(LogError) << "Requested ROM directory \"" << rompath + << "\" is actually a file, aborting"; return true; } @@ -654,11 +644,11 @@ bool SystemData::createSystemDirectories() LOG(LogInfo) << "Parsing systems configuration file \"" << path << "\"..."; pugi::xml_document doc; - #if defined(_WIN64) +#if defined(_WIN64) pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str()); - #else +#else pugi::xml_parse_result res = doc.load_file(path.c_str()); - #endif +#endif if (!res) { LOG(LogError) << "Couldn't parse es_systems.xml"; @@ -677,7 +667,7 @@ bool SystemData::createSystemDirectories() std::vector systemsVector; for (pugi::xml_node system = systemList.child("system"); system; - system = system.next_sibling("system")) { + system = system.next_sibling("system")) { std::string systemDir; std::string name; std::string fullname; @@ -701,8 +691,9 @@ bool SystemData::createSystemDirectories() // Check that the %ROMPATH% variable is actually used for the path element. // If not, skip the system. if (path.find("%ROMPATH%") != 0) { - LOG(LogWarning) << "The path element for system \"" << name << "\" does not " - "utilize the %ROMPATH% variable, skipping entry"; + LOG(LogWarning) << "The path element for system \"" << name + << "\" does not " + "utilize the %ROMPATH% variable, skipping entry"; continue; } else { @@ -711,14 +702,13 @@ bool SystemData::createSystemDirectories() // Trim any leading directory separator characters. systemDir.erase(systemDir.begin(), - std::find_if(systemDir.begin(), systemDir.end(), [](char c) { - return c != '/' && c != '\\'; - })); + std::find_if(systemDir.begin(), systemDir.end(), + [](char c) { return c != '/' && c != '\\'; })); if (!Utils::FileSystem::exists(rompath + systemDir)) { if (!Utils::FileSystem::createDirectory(rompath + systemDir)) { - LOG(LogError) << "Couldn't create system directory \"" << systemDir << - "\", permission problems or disk full?"; + LOG(LogError) << "Couldn't create system directory \"" << systemDir + << "\", permission problems or disk full?"; return true; } else { @@ -739,16 +729,17 @@ bool SystemData::createSystemDirectories() return true; } - #if defined(_WIN64) - systemInfoFile.open(Utils::String::stringToWideString(rompath + - systemDir + systemInfoFileName).c_str()); - #else +#if defined(_WIN64) + systemInfoFile.open( + Utils::String::stringToWideString(rompath + systemDir + systemInfoFileName).c_str()); +#else systemInfoFile.open(rompath + systemDir + systemInfoFileName); - #endif +#endif if (systemInfoFile.fail()) { - LOG(LogError) << "Couldn't create system information file \"" << rompath + - systemDir + systemInfoFileName << "\", permission problems or disk full?"; + LOG(LogError) << "Couldn't create system information file \"" + << rompath + systemDir + systemInfoFileName + << "\", permission problems or disk full?"; systemInfoFile.close(); return true; } @@ -770,12 +761,12 @@ bool SystemData::createSystemDirectories() systemsVector.push_back(systemDir + ": " + fullname); if (replaceInfoFile) { - LOG(LogInfo) << "Replaced existing system information file \"" << - rompath + systemDir + systemInfoFileName << "\""; + LOG(LogInfo) << "Replaced existing system information file \"" + << rompath + systemDir + systemInfoFileName << "\""; } else { - LOG(LogInfo) << "Created system information file \"" << - rompath + systemDir + systemInfoFileName << "\""; + LOG(LogInfo) << "Created system information file \"" + << rompath + systemDir + systemInfoFileName << "\""; } } @@ -793,11 +784,11 @@ bool SystemData::createSystemDirectories() if (systemsFileSuccess) { std::ofstream systemsFile; - #if defined(_WIN64) +#if defined(_WIN64) systemsFile.open(Utils::String::stringToWideString(rompath + systemsFileName).c_str()); - #else +#else systemsFile.open(rompath + systemsFileName); - #endif +#endif if (systemsFile.fail()) { systemsFileSuccess = false; } @@ -811,7 +802,7 @@ bool SystemData::createSystemDirectories() if (!systemsFileSuccess) { LOG(LogWarning) << "System directories successfully created but couldn't create " - "the systems.txt file in the ROM directory root"; + "the systems.txt file in the ROM directory root"; return false; } } @@ -867,8 +858,8 @@ std::string SystemData::getGamelistPath(bool forWrite) const if (Utils::FileSystem::exists(filePath)) return filePath; - filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" + - mName + "/gamelist.xml"; + filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" + mName + + "/gamelist.xml"; // Make sure the directory exists if we're going to write to it, // or crashes will happen. @@ -899,17 +890,12 @@ std::string SystemData::getThemePath() const return localThemePath; // Not system theme, try default system theme in theme set. - localThemePath = Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) + - "/theme.xml"; + localThemePath = + Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) + "/theme.xml"; return localThemePath; } -bool SystemData::hasGamelist() const -{ - return (Utils::FileSystem::exists(getGamelistPath(false))); -} - SystemData* SystemData::getRandomSystem(const SystemData* currentSystem) { unsigned int total = 0; @@ -927,7 +913,7 @@ SystemData* SystemData::getRandomSystem(const SystemData* currentSystem) // Get a random number in range. std::random_device randDev; // Mersenne Twister pseudorandom number generator. - std::mt19937 engine{randDev()}; + std::mt19937 engine { randDev() }; std::uniform_int_distribution uniform_dist(0, total - 1); int target = uniform_dist(engine); @@ -942,8 +928,7 @@ SystemData* SystemData::getRandomSystem(const SystemData* currentSystem) } } } - } - while (randomSystem == currentSystem); + } while (randomSystem == currentSystem); return randomSystem; } @@ -956,13 +941,17 @@ FileData* SystemData::getRandomGame(const FileData* currentGame) // If we're in the custom collection group list, then get the list of collections, // otherwise get a list of all the folder and file entries in the view. - if (currentGame && currentGame->getType() == FOLDER && currentGame-> - getSystem()->isGroupedCustomCollection()) { + if (currentGame && currentGame->getType() == FOLDER && + currentGame->getSystem()->isGroupedCustomCollection()) { gameList = mRootFolder->getParent()->getChildrenListToDisplay(); } else { - gameList = ViewController::get()->getGameListView(mRootFolder-> - getSystem()).get()->getCursor()->getParent()->getChildrenListToDisplay(); + gameList = ViewController::get() + ->getGameListView(mRootFolder->getSystem()) + .get() + ->getCursor() + ->getParent() + ->getChildrenListToDisplay(); } if (gameList.size() > 0 && gameList.front()->getParent()->getOnlyFoldersFlag()) @@ -1003,11 +992,10 @@ FileData* SystemData::getRandomGame(const FileData* currentGame) // Get a random number in range. std::random_device randDev; // Mersenne Twister pseudorandom number generator. - std::mt19937 engine{randDev()}; + std::mt19937 engine { randDev() }; std::uniform_int_distribution uniform_dist(0, total - 1); target = uniform_dist(engine); - } - while (currentGame && gameList.at(target) == currentGame); + } while (currentGame && gameList.at(target) == currentGame); return gameList.at(target); } @@ -1020,23 +1008,25 @@ void SystemData::sortSystem(bool reloadGamelist, bool jumpToFirstRow) bool favoritesSorting; if (this->isCustomCollection() || - (this->isCollection() && this->getFullName() == "collections")) + (this->isCollection() && this->getFullName() == "collections")) { favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom"); - else + } + else { favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst"); + } FileData* rootFolder = getRootFolder(); // Assign the sort type to all grouped custom collections. if (mIsCollectionSystem && mFullName == "collections") { - for (auto it = rootFolder->getChildren().begin(); - it != rootFolder->getChildren().end(); it++) { + for (auto it = rootFolder->getChildren().begin(); // Line break. + it != rootFolder->getChildren().end(); it++) { setupSystemSortType((*it)->getSystem()->getRootFolder()); } } setupSystemSortType(rootFolder); - rootFolder->sort(rootFolder->getSortTypeFromString( - rootFolder->getSortTypeString()), favoritesSorting); + rootFolder->sort(rootFolder->getSortTypeFromString(rootFolder->getSortTypeString()), + favoritesSorting); if (reloadGamelist) ViewController::get()->reloadGameListView(this, false); @@ -1079,7 +1069,8 @@ void SystemData::loadTheme() } } -void SystemData::writeMetaData() { +void SystemData::writeMetaData() +{ if (Settings::getInstance()->getBool("IgnoreGamelist") || mIsCollectionSystem) return; @@ -1087,7 +1078,8 @@ void SystemData::writeMetaData() { updateGamelist(this); } -void SystemData::onMetaDataSavePoint() { +void SystemData::onMetaDataSavePoint() +{ if (Settings::getInstance()->getString("SaveGamelistsMode") != "always") return; @@ -1100,15 +1092,15 @@ void SystemData::setupSystemSortType(FileData* mRootFolder) if (Settings::getInstance()->getString("DefaultSortOrder") != "") { for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) { if (FileSorts::SortTypes.at(i).description == - Settings::getInstance()->getString("DefaultSortOrder")) { - mRootFolder->setSortTypeString(Settings::getInstance()-> - getString("DefaultSortOrder")); + Settings::getInstance()->getString("DefaultSortOrder")) { + mRootFolder->setSortTypeString( + Settings::getInstance()->getString("DefaultSortOrder")); break; } } } // If no valid sort type was defined in the configuration file, set to default sorting. if (mRootFolder->getSortTypeString() == "") - mRootFolder->setSortTypeString(Settings::getInstance()-> - getDefaultString("DefaultSortOrder")); + mRootFolder->setSortTypeString( + Settings::getInstance()->getDefaultString("DefaultSortOrder")); } diff --git a/es-app/src/SystemData.h b/es-app/src/SystemData.h index 8d138769e..28a44721a 100644 --- a/es-app/src/SystemData.h +++ b/es-app/src/SystemData.h @@ -35,15 +35,14 @@ class FindRules { public: FindRules(); - ~FindRules(); void loadFindRules(); private: struct EmulatorRules { - #if defined(_WIN64) +#if defined(_WIN64) std::vector winRegistryPaths; - #endif +#endif std::vector systemPaths; std::vector staticPaths; }; @@ -61,38 +60,41 @@ private: class SystemData { public: - SystemData( - const std::string& name, - const std::string& fullName, - SystemEnvironmentData* envData, - const std::string& themeFolder, - bool CollectionSystem = false, - bool CustomCollectionSystem = false); + SystemData(const std::string& name, + const std::string& fullName, + SystemEnvironmentData* envData, + const std::string& themeFolder, + bool CollectionSystem = false, + bool CustomCollectionSystem = false); ~SystemData(); - inline FileData* getRootFolder() const { return mRootFolder; }; - inline const std::string& getName() const { return mName; } - inline const std::string& getFullName() const { return mFullName; } - inline const std::string& getStartPath() const { return mEnvData->mStartPath; } - inline const std::vector& getExtensions() const - { return mEnvData->mSearchExtensions; } - inline const std::string& getThemeFolder() const { return mThemeFolder; } - inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; } - inline const std::vector& getPlatformIds() const - { return mEnvData->mPlatformIds; } - inline bool hasPlatformId(PlatformIds::PlatformId id) { if (!mEnvData) return false; - return std::find(mEnvData->mPlatformIds.cbegin(), mEnvData->mPlatformIds.cend(), id) - != mEnvData->mPlatformIds.cend(); } + FileData* getRootFolder() const { return mRootFolder; } + const std::string& getName() const { return mName; } + const std::string& getFullName() const { return mFullName; } + const std::string& getStartPath() const { return mEnvData->mStartPath; } + const std::vector& getExtensions() const { return mEnvData->mSearchExtensions; } + const std::string& getThemeFolder() const { return mThemeFolder; } + SystemEnvironmentData* getSystemEnvData() const { return mEnvData; } + const std::vector& getPlatformIds() const + { + return mEnvData->mPlatformIds; + } + bool hasPlatformId(PlatformIds::PlatformId id) + { + if (!mEnvData) + return false; + return std::find(mEnvData->mPlatformIds.cbegin(), mEnvData->mPlatformIds.cend(), id) != + mEnvData->mPlatformIds.cend(); + } - inline const std::shared_ptr& getTheme() const { return mTheme; } + const std::shared_ptr& getTheme() const { return mTheme; } std::string getGamelistPath(bool forWrite) const; - bool hasGamelist() const; std::string getThemePath() const; std::pair getDisplayedGameCount() const; - bool getScrapeFlag() { return mScrapeFlag; }; + bool getScrapeFlag() { return mScrapeFlag; } void setScrapeFlag(bool scrapeflag) { mScrapeFlag = scrapeflag; } static void deleteSystems(); @@ -106,16 +108,22 @@ public: static std::vector sSystemVector; static std::unique_ptr sFindRules; - inline std::vector::const_iterator getIterator() const - { return std::find(sSystemVector.cbegin(), sSystemVector.cend(), this); }; - inline std::vector::const_reverse_iterator getRevIterator() const - { return std::find(sSystemVector.crbegin(), sSystemVector.crend(), this); }; - inline bool isCollection() { return mIsCollectionSystem; }; - inline bool isCustomCollection() { return mIsCustomCollectionSystem; }; - inline bool isGroupedCustomCollection() { return mIsGroupedCustomCollectionSystem; }; + std::vector::const_iterator getIterator() const + { + return std::find(sSystemVector.cbegin(), sSystemVector.cend(), this); + } + std::vector::const_reverse_iterator getRevIterator() const + { + return std::find(sSystemVector.crbegin(), sSystemVector.crend(), this); + } + bool isCollection() { return mIsCollectionSystem; } + bool isCustomCollection() { return mIsCustomCollectionSystem; } + bool isGroupedCustomCollection() { return mIsGroupedCustomCollectionSystem; } void setIsGroupedCustomCollection(bool isGroupedCustom) - { mIsGroupedCustomCollectionSystem = isGroupedCustom; }; - inline bool isGameSystem() { return mIsGameSystem; }; + { + mIsGroupedCustomCollectionSystem = isGroupedCustom; + }; + bool isGameSystem() { return mIsGameSystem; } bool isVisible(); @@ -123,14 +131,14 @@ public: SystemData* getPrev() const; static SystemData* getRandomSystem(const SystemData* currentSystem); FileData* getRandomGame(const FileData* currentGame = nullptr); - FileData* getPlaceholder() { return mPlaceholder; }; + FileData* getPlaceholder() { return mPlaceholder; } void sortSystem(bool reloadGamelist = true, bool jumpToFirstRow = false); // Load or re-load theme. void loadTheme(); - FileFilterIndex* getIndex() { return mFilterIndex; }; + FileFilterIndex* getIndex() { return mFilterIndex; } void onMetaDataSavePoint(); void writeMetaData(); @@ -141,7 +149,7 @@ private: bool mIsCustomCollectionSystem; bool mIsGroupedCustomCollectionSystem; bool mIsGameSystem; - bool mScrapeFlag; // Only used by scraper GUI to remember which systems to scrape. + bool mScrapeFlag; // Only used by scraper GUI to remember which systems to scrape. std::string mName; std::string mFullName; SystemEnvironmentData* mEnvData; diff --git a/es-app/src/SystemScreensaver.cpp b/es-app/src/SystemScreensaver.cpp index 85ffc624c..e6aa7ea5c 100644 --- a/es-app/src/SystemScreensaver.cpp +++ b/es-app/src/SystemScreensaver.cpp @@ -16,15 +16,15 @@ #if defined(BUILD_VLC_PLAYER) #include "components/VideoVlcComponent.h" #endif -#include "resources/Font.h" -#include "utils/FileSystemUtil.h" -#include "utils/StringUtil.h" -#include "views/gamelist/IGameListView.h" -#include "views/ViewController.h" #include "AudioManager.h" #include "FileData.h" #include "Log.h" #include "SystemData.h" +#include "resources/Font.h" +#include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" +#include "views/ViewController.h" +#include "views/gamelist/IGameListView.h" #include #include @@ -36,24 +36,23 @@ #define FADE_TIME 300 -SystemScreensaver::SystemScreensaver( - Window* window) - : mWindow(window), - mState(STATE_INACTIVE), - mImageScreensaver(nullptr), - mVideoScreensaver(nullptr), - mCurrentGame(nullptr), - mPreviousGame(nullptr), - mTimer(0), - mMediaSwapTime(0), - mTriggerNextGame(false), - mHasMediaFiles(false), - mFallbackScreensaver(false), - mOpacity(0.0f), - mDimValue(1.0), - mRectangleFadeIn(50), - mTextFadeIn(0), - mSaturationAmount(1.0) +SystemScreensaver::SystemScreensaver(Window* window) + : mWindow(window) + , mState(STATE_INACTIVE) + , mImageScreensaver(nullptr) + , mVideoScreensaver(nullptr) + , mCurrentGame(nullptr) + , mPreviousGame(nullptr) + , mTimer(0) + , mMediaSwapTime(0) + , mTriggerNextGame(false) + , mHasMediaFiles(false) + , mFallbackScreensaver(false) + , mOpacity(0.0f) + , mDimValue(1.0) + , mRectangleFadeIn(50) + , mTextFadeIn(0) + , mSaturationAmount(1.0) { mWindow->setScreensaver(this); } @@ -65,21 +64,6 @@ SystemScreensaver::~SystemScreensaver() delete mImageScreensaver; } -bool SystemScreensaver::allowSleep() -{ - return ((mVideoScreensaver == nullptr) && (mImageScreensaver == nullptr)); -} - -bool SystemScreensaver::isScreensaverActive() -{ - return (mState != STATE_INACTIVE); -} - -bool SystemScreensaver::isFallbackScreensaver() -{ - return mFallbackScreensaver; -} - void SystemScreensaver::startScreensaver(bool generateMediaList) { std::string path = ""; @@ -146,14 +130,14 @@ void SystemScreensaver::startScreensaver(bool generateMediaList) mImageScreensaver->setImage(path); mImageScreensaver->setOrigin(0.5f, 0.5f); mImageScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f, - Renderer::getScreenHeight() / 2.0f); + Renderer::getScreenHeight() / 2.0f); if (Settings::getInstance()->getBool("ScreensaverStretchImages")) mImageScreensaver->setResize(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); else mImageScreensaver->setMaxSize(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); } mTimer = 0; return; @@ -179,7 +163,7 @@ void SystemScreensaver::startScreensaver(bool generateMediaList) if (Settings::getInstance()->getBool("ScreensaverVideoGameInfo")) generateOverlayInfo(); - #if defined(_RPI_) +#if defined(_RPI_) // Create the correct type of video component. if (Settings::getInstance()->getBool("ScreensaverOmxPlayer")) mVideoScreensaver = new VideoOmxComponent(mWindow); @@ -187,26 +171,26 @@ void SystemScreensaver::startScreensaver(bool generateMediaList) mVideoScreensaver = new VideoVlcComponent(mWindow); else mVideoScreensaver = new VideoFFmpegComponent(mWindow); - #elif defined(BUILD_VLC_PLAYER) +#elif defined(BUILD_VLC_PLAYER) if (Settings::getInstance()->getString("VideoPlayer") == "vlc") mVideoScreensaver = new VideoVlcComponent(mWindow); else mVideoScreensaver = new VideoFFmpegComponent(mWindow); - #else +#else mVideoScreensaver = new VideoFFmpegComponent(mWindow); - #endif +#endif mVideoScreensaver->topWindow(true); mVideoScreensaver->setOrigin(0.5f, 0.5f); mVideoScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f, - Renderer::getScreenHeight() / 2.0f); + Renderer::getScreenHeight() / 2.0f); if (Settings::getInstance()->getBool("ScreensaverStretchVideos")) mVideoScreensaver->setResize(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); else mVideoScreensaver->setMaxSize(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); mVideoScreensaver->setVideo(path); mVideoScreensaver->setScreensaverMode(true); @@ -229,16 +213,17 @@ void SystemScreensaver::stopScreensaver() mState = STATE_INACTIVE; - mDimValue = 1.0; + mDimValue = 1.0f; mRectangleFadeIn = 50; mTextFadeIn = 0; - mSaturationAmount = 1.0; + mSaturationAmount = 1.0f; if (mGameOverlay) mGameOverlay.reset(); } -void SystemScreensaver::nextGame() { +void SystemScreensaver::nextGame() +{ stopScreensaver(); startScreensaver(false); } @@ -249,8 +234,8 @@ void SystemScreensaver::launchGame() // Launching game ViewController::get()->triggerGameLaunch(mCurrentGame); ViewController::get()->goToGameList(mCurrentGame->getSystem()); - IGameListView* view = ViewController::get()-> - getGameListView(mCurrentGame->getSystem()).get(); + IGameListView* view = + ViewController::get()->getGameListView(mCurrentGame->getSystem()).get(); view->setCursor(mCurrentGame); ViewController::get()->cancelViewTransitions(); } @@ -261,8 +246,8 @@ void SystemScreensaver::goToGame() if (mCurrentGame != nullptr) { // Go to the game in the gamelist view, but don't launch it. ViewController::get()->goToGameList(mCurrentGame->getSystem()); - IGameListView* view = ViewController::get()-> - getGameListView(mCurrentGame->getSystem()).get(); + IGameListView* view = + ViewController::get()->getGameListView(mCurrentGame->getSystem()).get(); view->setCursor(mCurrentGame); ViewController::get()->cancelViewTransitions(); } @@ -276,7 +261,7 @@ void SystemScreensaver::renderScreensaver() // Render a black background below the video. Renderer::setMatrix(Transform4x4f::Identity()); Renderer::drawRect(0.0f, 0.0f, static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); + static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); // Only render the video if the state requires it. if (static_cast(mState) >= STATE_FADE_IN_VIDEO) { @@ -288,7 +273,7 @@ void SystemScreensaver::renderScreensaver() // Render a black background below the image. Renderer::setMatrix(Transform4x4f::Identity()); Renderer::drawRect(0.0f, 0.0f, static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); + static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); // Only render the image if the state requires it. if (static_cast(mState) >= STATE_FADE_IN_VIDEO) { @@ -305,20 +290,20 @@ void SystemScreensaver::renderScreensaver() Renderer::setMatrix(Transform4x4f::Identity()); if (Settings::getInstance()->getString("ScreensaverType") == "slideshow") { if (mHasMediaFiles) { - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) if (Settings::getInstance()->getBool("ScreensaverSlideshowScanlines")) Renderer::shaderPostprocessing(Renderer::SHADER_SCANLINES); - #endif +#endif if (Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo") && - mGameOverlay) { + mGameOverlay) { if (mGameOverlayRectangleCoords.size() == 4) { - Renderer::drawRect(mGameOverlayRectangleCoords[0], - mGameOverlayRectangleCoords[1], mGameOverlayRectangleCoords[2], - mGameOverlayRectangleCoords[3], 0x00000000 | mRectangleFadeIn, - 0x00000000 | mRectangleFadeIn ); + Renderer::drawRect( + mGameOverlayRectangleCoords[0], mGameOverlayRectangleCoords[1], + mGameOverlayRectangleCoords[2], mGameOverlayRectangleCoords[3], + 0x00000000 | mRectangleFadeIn, 0x00000000 | mRectangleFadeIn); } - mRectangleFadeIn = Math::clamp(mRectangleFadeIn + 6 + - mRectangleFadeIn / 20, 0, 170); + mRectangleFadeIn = + Math::clamp(mRectangleFadeIn + 6 + mRectangleFadeIn / 20, 0, 170); mGameOverlay.get()->setColor(0xFFFFFF00 | mTextFadeIn); if (mTextFadeIn > 50) @@ -333,7 +318,7 @@ void SystemScreensaver::renderScreensaver() } else if (Settings::getInstance()->getString("ScreensaverType") == "video") { if (mHasMediaFiles) { - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) Renderer::shaderParameters videoParameters; unsigned int shaders = 0; if (Settings::getInstance()->getBool("ScreensaverVideoScanlines")) @@ -341,6 +326,7 @@ void SystemScreensaver::renderScreensaver() if (Settings::getInstance()->getBool("ScreensaverVideoBlur")) { shaders |= Renderer::SHADER_BLUR_HORIZONTAL; float heightModifier = Renderer::getScreenHeightModifier(); + // clang-format off if (heightModifier < 1) videoParameters.blurPasses = 2; // Below 1080 else if (heightModifier >= 4) @@ -355,21 +341,22 @@ void SystemScreensaver::renderScreensaver() videoParameters.blurPasses = 3; // 1440 else if (heightModifier >= 1) videoParameters.blurPasses = 2; // 1080 + // clang-format on } Renderer::shaderPostprocessing(shaders, videoParameters); - #endif +#endif if (Settings::getInstance()->getBool("ScreensaverVideoGameInfo") && mGameOverlay) { if (mGameOverlayRectangleCoords.size() == 4) { - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) Renderer::shaderPostprocessing(Renderer::SHADER_OPACITY); - #endif - Renderer::drawRect(mGameOverlayRectangleCoords[0], - mGameOverlayRectangleCoords[1], mGameOverlayRectangleCoords[2], - mGameOverlayRectangleCoords[3], 0x00000000 | mRectangleFadeIn, - 0x00000000 | mRectangleFadeIn ); +#endif + Renderer::drawRect( + mGameOverlayRectangleCoords[0], mGameOverlayRectangleCoords[1], + mGameOverlayRectangleCoords[2], mGameOverlayRectangleCoords[3], + 0x00000000 | mRectangleFadeIn, 0x00000000 | mRectangleFadeIn); } - mRectangleFadeIn = Math::clamp(mRectangleFadeIn + 6 + - mRectangleFadeIn / 20, 0, 170); + mRectangleFadeIn = + Math::clamp(mRectangleFadeIn + 6 + mRectangleFadeIn / 20, 0, 170); mGameOverlay.get()->setColor(0xFFFFFF00 | mTextFadeIn); if (mTextFadeIn > 50) @@ -383,8 +370,8 @@ void SystemScreensaver::renderScreensaver() } } if (mFallbackScreensaver || - Settings::getInstance()->getString("ScreensaverType") == "dim") { - #if defined(USE_OPENGL_21) + Settings::getInstance()->getString("ScreensaverType") == "dim") { +#if defined(USE_OPENGL_21) Renderer::shaderParameters dimParameters; dimParameters.fragmentDimValue = mDimValue; Renderer::shaderPostprocessing(Renderer::SHADER_DIM, dimParameters); @@ -394,22 +381,22 @@ void SystemScreensaver::renderScreensaver() Renderer::shaderPostprocessing(Renderer::SHADER_DESATURATE, dimParameters); if (mSaturationAmount > 0.0) mSaturationAmount = Math::clamp(mSaturationAmount - 0.035f, 0.0f, 1.0f); - #else - Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), - Renderer::getScreenHeight(), 0x000000A0, 0x000000A0); - #endif +#else + Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), + 0x000000A0, 0x000000A0); +#endif } else if (Settings::getInstance()->getString("ScreensaverType") == "black") { - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) Renderer::shaderParameters blackParameters; blackParameters.fragmentDimValue = mDimValue; Renderer::shaderPostprocessing(Renderer::SHADER_DIM, blackParameters); if (mDimValue > 0.0) mDimValue = Math::clamp(mDimValue - 0.045f, 0.0f, 1.0f); - #else - Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), - Renderer::getScreenHeight(), 0x000000FF, 0x000000FF); - #endif +#else + Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), + 0x000000FF, 0x000000FF); +#endif } } } @@ -458,8 +445,8 @@ void SystemScreensaver::update(int deltaTime) void SystemScreensaver::generateImageList() { - for (auto it = SystemData::sSystemVector.cbegin(); - it != SystemData::sSystemVector.cend(); it++) { + for (auto it = SystemData::sSystemVector.cbegin(); // Line break. + it != SystemData::sSystemVector.cend(); it++) { // We only want nodes from game systems that are not collections. if (!(*it)->isGameSystem() || (*it)->isCollection()) continue; @@ -475,8 +462,8 @@ void SystemScreensaver::generateImageList() void SystemScreensaver::generateVideoList() { - for (auto it = SystemData::sSystemVector.cbegin(); - it != SystemData::sSystemVector.cend(); it++) { + for (auto it = SystemData::sSystemVector.cbegin(); // Line break. + it != SystemData::sSystemVector.cend(); it++) { // We only want nodes from game systems that are not collections. if (!(*it)->isGameSystem() || (*it)->isCollection()) continue; @@ -493,7 +480,7 @@ void SystemScreensaver::generateVideoList() void SystemScreensaver::generateCustomImageList() { std::string imageDir = Utils::FileSystem::expandHomePath( - Settings::getInstance()->getString("ScreensaverSlideshowImageDir")); + Settings::getInstance()->getString("ScreensaverSlideshowImageDir")); // This makes it possible to set the custom image directory relative to the ES-DE binary // directory or the ROM directory. @@ -503,7 +490,7 @@ void SystemScreensaver::generateCustomImageList() if (imageDir != "" && Utils::FileSystem::isDirectory(imageDir)) { std::string imageFilter = ".jpg, .JPG, .png, .PNG"; Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent( - imageDir, Settings::getInstance()->getBool("ScreensaverSlideshowRecurse")); + imageDir, Settings::getInstance()->getBool("ScreensaverSlideshowRecurse")); for (auto it = dirContent.begin(); it != dirContent.end(); it++) { if (Utils::FileSystem::isRegularFile(*it)) { @@ -513,8 +500,7 @@ void SystemScreensaver::generateCustomImageList() } } else { - LOG(LogWarning) << "Custom screensaver image directory '" << - imageDir << "' does not exist."; + LOG(LogWarning) << "Custom screensaver image directory '" << imageDir << "' does not exist"; } } @@ -540,12 +526,11 @@ void SystemScreensaver::pickRandomImage(std::string& path) // Get a random number in range. std::random_device randDev; // Mersenne Twister pseudorandom number generator. - std::mt19937 engine{randDev()}; - std::uniform_int_distribution - uniform_dist(0, static_cast(mImageFiles.size()) - 1); + std::mt19937 engine { randDev() }; + std::uniform_int_distribution uniform_dist(0, + static_cast(mImageFiles.size()) - 1); index = uniform_dist(engine); - } - while (mPreviousGame && mImageFiles.at(index) == mPreviousGame); + } while (mPreviousGame && mImageFiles.at(index) == mPreviousGame); path = mImageFiles.at(index)->getImagePath(); mGameName = mImageFiles.at(index)->getName(); @@ -575,12 +560,11 @@ void SystemScreensaver::pickRandomVideo(std::string& path) // Get a random number in range. std::random_device randDev; // Mersenne Twister pseudorandom number generator. - std::mt19937 engine{randDev()}; - std::uniform_int_distribution - uniform_dist(0, static_cast(mVideoFiles.size()) - 1); + std::mt19937 engine { randDev() }; + std::uniform_int_distribution uniform_dist(0, + static_cast(mVideoFiles.size()) - 1); index = uniform_dist(engine); - } - while (mPreviousGame && mVideoFiles.at(index) == mPreviousGame); + } while (mPreviousGame && mVideoFiles.at(index) == mPreviousGame); path = mVideoFiles.at(index)->getVideoPath(); mGameName = mVideoFiles.at(index)->getName(); @@ -604,12 +588,11 @@ void SystemScreensaver::pickRandomCustomImage(std::string& path) // Get a random number in range. std::random_device randDev; // Mersenne Twister pseudorandom number generator. - std::mt19937 engine{randDev()}; - std::uniform_int_distribution - uniform_dist(0, static_cast(mImageCustomFiles.size()) - 1); + std::mt19937 engine { randDev() }; + std::uniform_int_distribution uniform_dist( + 0, static_cast(mImageCustomFiles.size()) - 1); index = uniform_dist(engine); - } - while (mPreviousCustomImage != "" && mImageCustomFiles.at(index) == mPreviousCustomImage); + } while (mPreviousCustomImage != "" && mImageCustomFiles.at(index) == mPreviousCustomImage); path = mImageCustomFiles.at(index); mPreviousCustomImage = path; @@ -633,8 +616,8 @@ void SystemScreensaver::generateOverlayInfo() const std::string systemName = Utils::String::toUpper(mSystemName); const std::string overlayText = gameName + "\n" + systemName; - mGameOverlay = std::unique_ptr(mGameOverlayFont.at(0)-> - buildTextCache(overlayText, posX, posY, 0xFFFFFFFF)); + mGameOverlay = std::unique_ptr( + mGameOverlayFont.at(0)->buildTextCache(overlayText, posX, posY, 0xFFFFFFFF)); float textSizeX; float textSizeY = mGameOverlayFont[0].get()->sizeText(overlayText).y(); @@ -645,7 +628,7 @@ void SystemScreensaver::generateOverlayInfo() // injected in the size calculation. Regardless, this workaround is working // fine for the time being. if (mGameOverlayFont[0].get()->sizeText(gameName).x() > - mGameOverlayFont[0].get()->sizeText(systemName).x()) + mGameOverlayFont[0].get()->sizeText(systemName).x()) textSizeX = mGameOverlayFont[0].get()->sizeText(gameName).x(); else textSizeX = mGameOverlayFont[0].get()->sizeText(systemName).x(); diff --git a/es-app/src/SystemScreensaver.h b/es-app/src/SystemScreensaver.h index 5ec3cddaa..087397f95 100644 --- a/es-app/src/SystemScreensaver.h +++ b/es-app/src/SystemScreensaver.h @@ -22,9 +22,12 @@ public: SystemScreensaver(Window* window); virtual ~SystemScreensaver(); - virtual bool allowSleep(); - virtual bool isScreensaverActive(); - virtual bool isFallbackScreensaver(); + virtual bool allowSleep() + { + return ((mVideoScreensaver == nullptr) && (mImageScreensaver == nullptr)); + } + virtual bool isScreensaverActive() { return (mState != STATE_INACTIVE); } + virtual bool isFallbackScreensaver() { return mFallbackScreensaver; } virtual void startScreensaver(bool generateMediaList); virtual void stopScreensaver(); @@ -35,8 +38,8 @@ public: virtual void renderScreensaver(); virtual void update(int deltaTime); - virtual FileData* getCurrentGame() { return mCurrentGame; }; - virtual void triggerNextGame() { mTriggerNextGame = true; }; + virtual FileData* getCurrentGame() { return mCurrentGame; } + virtual void triggerNextGame() { mTriggerNextGame = true; } private: void generateImageList(); diff --git a/es-app/src/VolumeControl.cpp b/es-app/src/VolumeControl.cpp index c164d5b31..2083f1688 100644 --- a/es-app/src/VolumeControl.cpp +++ b/es-app/src/VolumeControl.cpp @@ -8,8 +8,8 @@ #include "VolumeControl.h" -#include "math/Misc.h" #include "Log.h" +#include "math/Misc.h" #if defined(_RPI_) #include "Settings.h" @@ -38,15 +38,15 @@ std::string VolumeControl::mixerCard = "default"; VolumeControl* VolumeControl::sInstance = nullptr; VolumeControl::VolumeControl() - #if defined(__linux__) - : mixerIndex(0), - mixerHandle(nullptr), - mixerElem(nullptr), - mixerSelemId(nullptr) - #elif defined(_WIN64) - : mixerHandle(nullptr), - endpointVolume(nullptr) - #endif +#if defined(__linux__) + : mixerIndex(0) + , mixerHandle(nullptr) + , mixerElem(nullptr) + , mixerSelemId(nullptr) +#elif defined(_WIN64) + : mixerHandle(nullptr) + , endpointVolume(nullptr) +#endif { init(); } @@ -54,9 +54,9 @@ VolumeControl::VolumeControl() VolumeControl::~VolumeControl() { deinit(); - #if defined(__linux__) +#if defined(__linux__) snd_config_update_free_global(); - #endif +#endif } VolumeControl* VolumeControl::getInstance() @@ -79,14 +79,15 @@ void VolumeControl::deleteInstance() void VolumeControl::init() { // Initialize audio mixer interface. - #if defined(__linux__) + +#if defined(__linux__) // Try to open mixer device. if (mixerHandle == nullptr) { +#if defined(_RPI_) // Allow user to override the AudioCard and AudioDevice in es_settings.xml. - #if defined(_RPI_) mixerCard = Settings::getInstance()->getString("AudioCard"); mixerName = Settings::getInstance()->getString("AudioDevice"); - #endif +#endif snd_mixer_selem_id_alloca(&mixerSelemId); // Sets simple-mixer index and name. @@ -106,8 +107,8 @@ void VolumeControl::init() LOG(LogDebug) << "VolumeControl::init(): Mixer initialized"; } else { - LOG(LogError) << - "VolumeControl::init(): Failed to find mixer elements!"; + LOG(LogError) + << "VolumeControl::init(): Failed to find mixer elements!"; snd_mixer_close(mixerHandle); mixerHandle = nullptr; } @@ -119,8 +120,8 @@ void VolumeControl::init() } } else { - LOG(LogError) << - "VolumeControl::init(): Failed to register simple element class!"; + LOG(LogError) + << "VolumeControl::init(): Failed to register simple element class!"; snd_mixer_close(mixerHandle); mixerHandle = nullptr; } @@ -135,31 +136,30 @@ void VolumeControl::init() LOG(LogError) << "VolumeControl::init(): Failed to open ALSA mixer!"; } } - #elif defined(_WIN64) +#elif defined(_WIN64) // Windows Vista or above. if (endpointVolume == nullptr) { CoInitialize(nullptr); IMMDeviceEnumerator* deviceEnumerator = nullptr; CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, - __uuidof(IMMDeviceEnumerator), reinterpret_cast(&deviceEnumerator)); + __uuidof(IMMDeviceEnumerator), + reinterpret_cast(&deviceEnumerator)); if (deviceEnumerator != nullptr) { // Get default endpoint. - IMMDevice * defaultDevice = nullptr; + IMMDevice* defaultDevice = nullptr; deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice); if (defaultDevice != nullptr) { // Retrieve endpoint volume. - defaultDevice->Activate(__uuidof(IAudioEndpointVolume), - CLSCTX_INPROC_SERVER, nullptr, - reinterpret_cast(&endpointVolume)); + defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, + nullptr, reinterpret_cast(&endpointVolume)); if (endpointVolume == nullptr) LOG(LogError) << "VolumeControl::init(): " - "Failed to get default audio endpoint volume!"; + "Failed to get default audio endpoint volume!"; // Release default device. we don't need it anymore. defaultDevice->Release(); } else { - LOG(LogError) << - "VolumeControl::init(): Failed to get default audio endpoint!"; + LOG(LogError) << "VolumeControl::init(): Failed to get default audio endpoint!"; } // Release device enumerator. we don't need it anymore. deviceEnumerator->Release(); @@ -169,13 +169,14 @@ void VolumeControl::init() CoUninitialize(); } } - #endif +#endif } void VolumeControl::deinit() { // Deinitialize audio mixer interface. - #if defined(__linux__) + +#if defined(__linux__) if (mixerHandle != nullptr) { snd_mixer_detach(mixerHandle, mixerCard.c_str()); snd_mixer_free(mixerHandle); @@ -183,28 +184,28 @@ void VolumeControl::deinit() mixerHandle = nullptr; mixerElem = nullptr; } - #elif defined(_WIN64) +#elif defined(_WIN64) if (endpointVolume != nullptr) { endpointVolume->Release(); endpointVolume = nullptr; CoUninitialize(); } - #endif +#endif } int VolumeControl::getVolume() const { int volume = 0; - #if defined(__linux__) +#if defined(__linux__) if (mixerElem != nullptr) { // Get volume range. long minVolume; long maxVolume; if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) { long rawVolume; - if (snd_mixer_selem_get_playback_volume(mixerElem, - SND_MIXER_SCHN_MONO, &rawVolume) == 0) { + if (snd_mixer_selem_get_playback_volume(mixerElem, SND_MIXER_SCHN_MONO, &rawVolume) == + 0) { // Bring into range 0-100. rawVolume -= minVolume; if (rawVolume > 0) @@ -218,7 +219,7 @@ int VolumeControl::getVolume() const LOG(LogError) << "VolumeControl::getVolume(): Failed to get volume range"; } } - #elif defined(_WIN64) +#elif defined(_WIN64) if (endpointVolume != nullptr) { // Windows Vista or above, uses EndpointVolume API. float floatVolume = 0.0f; // 0-1 @@ -230,7 +231,7 @@ int VolumeControl::getVolume() const LOG(LogError) << "VolumeControl::getVolume(): Failed to get master volume!"; } } - #endif +#endif volume = Math::clamp(volume, 0, 100); return volume; @@ -240,7 +241,7 @@ void VolumeControl::setVolume(int volume) { volume = Math::clamp(volume, 0, 100); - #if defined(__linux__) +#if defined(__linux__) if (mixerElem != nullptr) { // Get volume range. long minVolume; @@ -248,10 +249,10 @@ void VolumeControl::setVolume(int volume) if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) { // Bring into minVolume-maxVolume range and set. long rawVolume = (volume * (maxVolume - minVolume) / 100) + minVolume; - if (snd_mixer_selem_set_playback_volume(mixerElem, - SND_MIXER_SCHN_FRONT_LEFT, rawVolume) < 0 || - snd_mixer_selem_set_playback_volume(mixerElem, - SND_MIXER_SCHN_FRONT_RIGHT, rawVolume) < 0) { + if (snd_mixer_selem_set_playback_volume(mixerElem, SND_MIXER_SCHN_FRONT_LEFT, + rawVolume) < 0 || + snd_mixer_selem_set_playback_volume(mixerElem, SND_MIXER_SCHN_FRONT_RIGHT, + rawVolume) < 0) { LOG(LogError) << "VolumeControl::getVolume(): Failed to set mixer volume"; } } @@ -259,7 +260,7 @@ void VolumeControl::setVolume(int volume) LOG(LogError) << "VolumeControl::getVolume(): Failed to get volume range"; } } - #elif defined(_WIN64) +#elif defined(_WIN64) if (endpointVolume != nullptr) { // Windows Vista or above, uses EndpointVolume API. float floatVolume = 0.0f; // 0-1 @@ -268,5 +269,5 @@ void VolumeControl::setVolume(int volume) if (endpointVolume->SetMasterVolumeLevelScalar(floatVolume, nullptr) != S_OK) LOG(LogError) << "VolumeControl::setVolume(): Failed to set master volume"; } - #endif +#endif } diff --git a/es-app/src/VolumeControl.h b/es-app/src/VolumeControl.h index 624a98b02..24cd416b5 100644 --- a/es-app/src/VolumeControl.h +++ b/es-app/src/VolumeControl.h @@ -38,18 +38,18 @@ public: static VolumeControl* sInstance; - #if defined(__linux__) +#if defined(__linux__) static std::string mixerName; static std::string mixerCard; int mixerIndex; snd_mixer_t* mixerHandle; snd_mixer_elem_t* mixerElem; snd_mixer_selem_id_t* mixerSelemId; - #elif defined(_WIN64) +#elif defined(_WIN64) HMIXER mixerHandle; MIXERCONTROL mixerControl; - IAudioEndpointVolume * endpointVolume; - #endif + IAudioEndpointVolume* endpointVolume; +#endif }; #endif // ES_APP_VOLUME_CONTROL_H diff --git a/es-app/src/animations/MoveCameraAnimation.h b/es-app/src/animations/MoveCameraAnimation.h index 3078ff495..82fd76152 100644 --- a/es-app/src/animations/MoveCameraAnimation.h +++ b/es-app/src/animations/MoveCameraAnimation.h @@ -14,12 +14,12 @@ class MoveCameraAnimation : public Animation { public: - MoveCameraAnimation( - Transform4x4f& camera, - const Vector3f& target) - : mCameraStart(camera), - mTarget(target), - cameraOut(camera) {} + MoveCameraAnimation(Transform4x4f& camera, const Vector3f& target) + : mCameraStart(camera) + , mTarget(target) + , cameraOut(camera) + { + } int getDuration() const override { return 400; } @@ -28,7 +28,7 @@ public: // Cubic ease out. t -= 1; cameraOut.translation() = - -Vector3f().lerp(-mCameraStart.translation(), mTarget, t * t * t + 1); + -Vector3f().lerp(-mCameraStart.translation(), mTarget, t * t * t + 1); } private: diff --git a/es-app/src/guis/GuiCollectionSystemsOptions.cpp b/es-app/src/guis/GuiCollectionSystemsOptions.cpp index aded41fc0..86e8404d7 100644 --- a/es-app/src/guis/GuiCollectionSystemsOptions.cpp +++ b/es-app/src/guis/GuiCollectionSystemsOptions.cpp @@ -9,6 +9,7 @@ #include "guis/GuiCollectionSystemsOptions.h" +#include "CollectionSystemsManager.h" #include "components/OptionListComponent.h" #include "components/SwitchComponent.h" #include "guis/GuiMsgBox.h" @@ -16,21 +17,23 @@ #include "guis/GuiTextEditPopup.h" #include "utils/StringUtil.h" #include "views/ViewController.h" -#include "CollectionSystemsManager.h" -GuiCollectionSystemsOptions::GuiCollectionSystemsOptions( - Window* window, - std::string title) - : GuiSettings(window, title), - mAddedCustomCollection(false), - mDeletedCustomCollection(false) +GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window, std::string title) + : GuiSettings(window, title) + , mAddedCustomCollection(false) + , mDeletedCustomCollection(false) { // Finish editing custom collection. if (CollectionSystemsManager::get()->isEditing()) { ComponentListRow row; - row.addElement(std::make_shared(mWindow, "FINISH EDITING '" + - Utils::String::toUpper(CollectionSystemsManager::get()->getEditingCollection()) + - "' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared( + mWindow, + "FINISH EDITING '" + + Utils::String::toUpper( + CollectionSystemsManager::get()->getEditingCollection()) + + "' COLLECTION", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.makeAcceptInputHandler([this] { CollectionSystemsManager::get()->exitEditMode(); mWindow->invalidateCachedBackground(); @@ -40,19 +43,20 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions( } // Automatic collections. - collection_systems_auto = std::make_shared> - (mWindow, getHelpStyle(), "SELECT COLLECTIONS", true); + collection_systems_auto = std::make_shared>( + mWindow, getHelpStyle(), "SELECT COLLECTIONS", true); std::map autoSystems = - CollectionSystemsManager::get()->getAutoCollectionSystems(); + CollectionSystemsManager::get()->getAutoCollectionSystems(); // Add automatic systems. - for (std::map::const_iterator - it = autoSystems.cbegin(); it != autoSystems.cend() ; it++) + for (std::map::const_iterator it = + autoSystems.cbegin(); + it != autoSystems.cend(); it++) collection_systems_auto->add(it->second.decl.longName, it->second.decl.name, - it->second.isEnabled); + it->second.isEnabled); addWithLabel("AUTOMATIC GAME COLLECTIONS", collection_systems_auto); addSaveFunc([this, autoSystems] { std::string autoSystemsSelected = Utils::String::vectorToDelimitedString( - collection_systems_auto->getSelectedObjects(), ",", true); + collection_systems_auto->getSelectedObjects(), ",", true); std::string autoSystemsConfig = Settings::getInstance()->getString("CollectionSystemsAuto"); if (autoSystemsSelected != autoSystemsConfig) { if (CollectionSystemsManager::get()->isEditing()) @@ -67,19 +71,19 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions( } else if (autoSystemsSelected != "") { std::vector selectedVector = - Utils::String::delimitedStringToVector(autoSystemsSelected, ","); + Utils::String::delimitedStringToVector(autoSystemsSelected, ","); std::vector configuredVector = - Utils::String::delimitedStringToVector(autoSystemsConfig, ","); + Utils::String::delimitedStringToVector(autoSystemsConfig, ","); for (std::string system : selectedVector) { if (std::find(configuredVector.begin(), configuredVector.end(), system) == - configuredVector.end()) + configuredVector.end()) addedAutoSystems.push_back(system); } } if (!addedAutoSystems.empty()) { for (std::string system : addedAutoSystems) - CollectionSystemsManager::get()-> - repopulateCollection(autoSystems.find(system)->second.system); + CollectionSystemsManager::get()->repopulateCollection( + autoSystems.find(system)->second.system); } setNeedsSaving(); setNeedsReloading(); @@ -88,50 +92,52 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions( }); // Custom collections. - collection_systems_custom = std::make_shared> - (mWindow, getHelpStyle(), "SELECT COLLECTIONS", true); + collection_systems_custom = std::make_shared>( + mWindow, getHelpStyle(), "SELECT COLLECTIONS", true); std::map customSystems = - CollectionSystemsManager::get()->getCustomCollectionSystems(); + CollectionSystemsManager::get()->getCustomCollectionSystems(); // Add custom systems. - for (std::map::const_iterator - it = customSystems.cbegin(); it != customSystems.cend() ; it++) + for (std::map::const_iterator it = + customSystems.cbegin(); + it != customSystems.cend(); it++) collection_systems_custom->add(it->second.decl.longName, it->second.decl.name, - it->second.isEnabled); + it->second.isEnabled); + addWithLabel("CUSTOM GAME COLLECTIONS", collection_systems_custom); addSaveFunc([this, customSystems] { if (!mDeletedCustomCollection) { std::string customSystemsSelected = Utils::String::vectorToDelimitedString( - collection_systems_custom->getSelectedObjects(), ",", true); - std::string customSystemsConfig = Settings::getInstance()-> - getString("CollectionSystemsCustom"); + collection_systems_custom->getSelectedObjects(), ",", true); + std::string customSystemsConfig = + Settings::getInstance()->getString("CollectionSystemsCustom"); if (customSystemsSelected != customSystemsConfig) { if (CollectionSystemsManager::get()->isEditing()) CollectionSystemsManager::get()->exitEditMode(); Settings::getInstance()->setString("CollectionSystemsCustom", - customSystemsSelected); + customSystemsSelected); // Check if any systems have been enabled, and if so repopulate them, which // results in a complete initialization of their content. This is necessary as // collections aren't updated while they are disabled. std::vector addedCustomSystems; if (customSystemsConfig == "") { addedCustomSystems = - Utils::String::delimitedStringToVector(customSystemsSelected, ","); + Utils::String::delimitedStringToVector(customSystemsSelected, ","); } else if (customSystemsSelected != "") { std::vector selectedVector = - Utils::String::delimitedStringToVector(customSystemsSelected, ","); + Utils::String::delimitedStringToVector(customSystemsSelected, ","); std::vector configuredVector = - Utils::String::delimitedStringToVector(customSystemsConfig, ","); + Utils::String::delimitedStringToVector(customSystemsConfig, ","); for (std::string system : selectedVector) { if (std::find(configuredVector.begin(), configuredVector.end(), system) == - configuredVector.end()) + configuredVector.end()) addedCustomSystems.push_back(system); } } if (!mAddedCustomCollection && !addedCustomSystems.empty()) { for (std::string system : addedCustomSystems) - CollectionSystemsManager::get()-> - repopulateCollection(customSystems.find(system)->second.system); + CollectionSystemsManager::get()->repopulateCollection( + customSystems.find(system)->second.system); } setNeedsSaving(); setNeedsReloading(); @@ -143,32 +149,33 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions( // Create custom collection from theme. std::vector unusedFolders = - CollectionSystemsManager::get()->getUnusedSystemsFromTheme(); + CollectionSystemsManager::get()->getUnusedSystemsFromTheme(); if (unusedFolders.size() > 0) { ComponentListRow row; - auto themeCollection = std::make_shared(mWindow, - "CREATE NEW CUSTOM COLLECTION FROM THEME", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + auto themeCollection = + std::make_shared(mWindow, "CREATE NEW CUSTOM COLLECTION FROM THEME", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); auto bracketThemeCollection = std::make_shared(mWindow); bracketThemeCollection->setImage(":/graphics/arrow.svg"); - bracketThemeCollection->setResize(Vector2f(0, - Font::get(FONT_SIZE_MEDIUM)->getLetterHeight())); + bracketThemeCollection->setResize( + Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight())); row.addElement(themeCollection, true); row.addElement(bracketThemeCollection, false); row.makeAcceptInputHandler([this, unusedFolders] { auto ss = new GuiSettings(mWindow, "SELECT THEME FOLDER"); std::shared_ptr> folderThemes = - std::make_shared>(mWindow, - getHelpStyle(), "SELECT THEME FOLDER", true); + std::make_shared>(mWindow, getHelpStyle(), + "SELECT THEME FOLDER", true); // Add custom systems. - for (auto it = unusedFolders.cbegin() ; it != unusedFolders.cend() ; it++ ) { + for (auto it = unusedFolders.cbegin(); it != unusedFolders.cend(); it++) { ComponentListRow row; std::string name = *it; std::function createCollectionCall = [this, name] { createCustomCollection(name); }; row.makeAcceptInputHandler(createCollectionCall); - auto themeFolder = std::make_shared(mWindow, - Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF); + auto themeFolder = std::make_shared( + mWindow, Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF); row.addElement(themeFolder, true); // This transparent bracket is only added to generate the correct help prompts. auto bracket = std::make_shared(mWindow); @@ -184,12 +191,11 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions( // Create new custom collection. ComponentListRow row; - auto newCollection = std::make_shared(mWindow, - "CREATE NEW CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + auto newCollection = std::make_shared(mWindow, "CREATE NEW CUSTOM COLLECTION", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); auto bracketNewCollection = std::make_shared(mWindow); bracketNewCollection->setImage(":/graphics/arrow.svg"); - bracketNewCollection->setResize(Vector2f(0, - Font::get(FONT_SIZE_MEDIUM)->getLetterHeight())); + bracketNewCollection->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight())); row.addElement(newCollection, true); row.addElement(bracketNewCollection, false); auto createCollectionCall = [this](const std::string& newVal) { @@ -202,72 +208,72 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions( createCustomCollection(name); }; row.makeAcceptInputHandler([this, createCollectionCall] { - mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), - "New Collection Name", "", createCollectionCall, false, "SAVE")); + mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "New Collection Name", "", + createCollectionCall, false, "SAVE")); }); addRow(row); // Delete custom collection. row.elements.clear(); - auto deleteCollection = std::make_shared(mWindow, - "DELETE CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + auto deleteCollection = std::make_shared( + mWindow, "DELETE CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); auto bracketDeleteCollection = std::make_shared(mWindow); bracketDeleteCollection->setImage(":/graphics/arrow.svg"); - bracketDeleteCollection->setResize(Vector2f(0, - Font::get(FONT_SIZE_MEDIUM)->getLetterHeight())); + bracketDeleteCollection->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight())); row.addElement(deleteCollection, true); row.addElement(bracketDeleteCollection, false); row.makeAcceptInputHandler([this, customSystems] { auto ss = new GuiSettings(mWindow, "SELECT COLLECTION TO DELETE"); std::shared_ptr> customCollections = - std::make_shared>(mWindow, - getHelpStyle(), "", true); - for (std::map::const_iterator - it = customSystems.cbegin(); it != customSystems.cend() ; it++) { + std::make_shared>(mWindow, getHelpStyle(), "", true); + for (std::map::const_iterator it = + customSystems.cbegin(); + it != customSystems.cend(); it++) { ComponentListRow row; std::string name = (*it).first; std::function deleteCollectionCall = [this, name] { - mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - "THIS WILL PERMANENTLY\nDELETE THE COLLECTION\n'" + - Utils::String::toUpper(name) + "'\n" + mWindow->pushGui(new GuiMsgBox( + mWindow, getHelpStyle(), + "THIS WILL PERMANENTLY\nDELETE THE COLLECTION\n'" + + Utils::String::toUpper(name) + + "'\n" "ARE YOU SURE?", - "YES", [this, name] { - if (CollectionSystemsManager::get()->isEditing()) - CollectionSystemsManager::get()->exitEditMode(); - mDeletedCustomCollection = true; - std::vector selectedCustomCollections = - collection_systems_custom->getSelectedObjects(); - std::string collectionsConfigEntry; - // Create the configuration file entry. If the collection to be - // deleted was activated, then exclude it. - for (auto it = selectedCustomCollections.begin(); - it != selectedCustomCollections.end(); it++) { - if ((*it) != name) { - if ((*it) != selectedCustomCollections.front() && - collectionsConfigEntry != "") - collectionsConfigEntry += ","; - collectionsConfigEntry += (*it); - } + "YES", + [this, name] { + if (CollectionSystemsManager::get()->isEditing()) + CollectionSystemsManager::get()->exitEditMode(); + mDeletedCustomCollection = true; + std::vector selectedCustomCollections = + collection_systems_custom->getSelectedObjects(); + std::string collectionsConfigEntry; + // Create the configuration file entry. If the collection to be + // deleted was activated, then exclude it. + for (auto it = selectedCustomCollections.begin(); + it != selectedCustomCollections.end(); it++) { + if ((*it) != name) { + if ((*it) != selectedCustomCollections.front() && + collectionsConfigEntry != "") + collectionsConfigEntry += ","; + collectionsConfigEntry += (*it); } - // If the system to be deleted was present in es_settings.xml, we - // need to re-write it. - if (collectionsConfigEntry != - Settings::getInstance()->getString("CollectionSystemsCustom")) { - Settings::getInstance()->setString("CollectionSystemsCustom", - collectionsConfigEntry); - setNeedsSaving(); - setNeedsGoToStart(); - } - CollectionSystemsManager::get()->deleteCustomCollection(name); - return true; - }, - "NO", [this] { - return false; - })); + } + // If the system to be deleted was present in es_settings.xml, we + // need to re-write it. + if (collectionsConfigEntry != + Settings::getInstance()->getString("CollectionSystemsCustom")) { + Settings::getInstance()->setString("CollectionSystemsCustom", + collectionsConfigEntry); + setNeedsSaving(); + setNeedsGoToStart(); + } + CollectionSystemsManager::get()->deleteCustomCollection(name); + return true; + }, + "NO", [this] { return false; })); }; row.makeAcceptInputHandler(deleteCollectionCall); - auto customCollection = std::make_shared(mWindow, - Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF); + auto customCollection = std::make_shared( + mWindow, Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF); row.addElement(customCollection, true); // This transparent bracket is only added generate the correct help prompts. auto bracket = std::make_shared(mWindow); @@ -310,14 +316,14 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions( // Group unthemed custom collections. auto use_custom_collections_system = std::make_shared(mWindow); - use_custom_collections_system->setState(Settings::getInstance()-> - getBool("UseCustomCollectionsSystem")); + use_custom_collections_system->setState( + Settings::getInstance()->getBool("UseCustomCollectionsSystem")); addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", use_custom_collections_system); addSaveFunc([this, use_custom_collections_system] { if (use_custom_collections_system->getState() != - Settings::getInstance()->getBool("UseCustomCollectionsSystem")) { + Settings::getInstance()->getBool("UseCustomCollectionsSystem")) { Settings::getInstance()->setBool("UseCustomCollectionsSystem", - use_custom_collections_system->getState()); + use_custom_collections_system->getState()); if (CollectionSystemsManager::get()->isEditing()) CollectionSystemsManager::get()->exitEditMode(); setNeedsSaving(); @@ -330,14 +336,14 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions( // Show system names in collections. auto collection_show_system_info = std::make_shared(mWindow); - collection_show_system_info->setState(Settings::getInstance()-> - getBool("CollectionShowSystemInfo")); + collection_show_system_info->setState( + Settings::getInstance()->getBool("CollectionShowSystemInfo")); addWithLabel("SHOW SYSTEM NAMES IN COLLECTIONS", collection_show_system_info); addSaveFunc([this, collection_show_system_info] { if (collection_show_system_info->getState() != - Settings::getInstance()->getBool("CollectionShowSystemInfo")) { + Settings::getInstance()->getBool("CollectionShowSystemInfo")) { Settings::getInstance()->setBool("CollectionShowSystemInfo", - collection_show_system_info->getState()); + collection_show_system_info->getState()); setNeedsSaving(); setNeedsReloading(); setInvalidateCachedBackground(); @@ -350,10 +356,9 @@ void GuiCollectionSystemsOptions::createCustomCollection(std::string inName) if (CollectionSystemsManager::get()->isEditing()) CollectionSystemsManager::get()->exitEditMode(); - std::string collectionName = CollectionSystemsManager::get()-> - getValidNewCollectionName(inName); - SystemData* newCollection = CollectionSystemsManager::get()-> - addNewCustomCollection(collectionName); + std::string collectionName = CollectionSystemsManager::get()->getValidNewCollectionName(inName); + SystemData* newCollection = + CollectionSystemsManager::get()->addNewCustomCollection(collectionName); CollectionSystemsManager::get()->saveCustomCollection(newCollection); collection_systems_custom->add(collectionName, collectionName, true); diff --git a/es-app/src/guis/GuiCollectionSystemsOptions.h b/es-app/src/guis/GuiCollectionSystemsOptions.h index 1499a6b8c..734d99211 100644 --- a/es-app/src/guis/GuiCollectionSystemsOptions.h +++ b/es-app/src/guis/GuiCollectionSystemsOptions.h @@ -12,8 +12,7 @@ #include "GuiSettings.h" -template -class OptionListComponent; +template class OptionListComponent; class GuiCollectionSystemsOptions : public GuiSettings { diff --git a/es-app/src/guis/GuiGameScraper.cpp b/es-app/src/guis/GuiGameScraper.cpp index 815dba315..ded44f56b 100644 --- a/es-app/src/guis/GuiGameScraper.cpp +++ b/es-app/src/guis/GuiGameScraper.cpp @@ -10,23 +10,22 @@ #include "guis/GuiGameScraper.h" +#include "FileData.h" +#include "MameNames.h" +#include "SystemData.h" #include "components/ButtonComponent.h" #include "components/MenuComponent.h" #include "components/TextComponent.h" #include "views/ViewController.h" -#include "FileData.h" -#include "MameNames.h" -#include "SystemData.h" -GuiGameScraper::GuiGameScraper( - Window* window, - ScraperSearchParams params, - std::function doneFunc) - : GuiComponent(window), - mGrid(window, Vector2i(1, 7)), - mBox(window, ":/graphics/frame.svg"), - mSearchParams(params), - mClose(false) +GuiGameScraper::GuiGameScraper(Window* window, + ScraperSearchParams params, + std::function doneFunc) + : GuiComponent(window) + , mGrid(window, Vector2i(1, 7)) + , mBox(window, ":/graphics/frame.svg") + , mSearchParams(params) + , mClose(false) { addChild(&mBox); addChild(&mGrid); @@ -40,82 +39,62 @@ GuiGameScraper::GuiGameScraper( } else { if (params.game->isArcadeGame() && - Settings::getInstance()->getString("Scraper") == "thegamesdb") - scrapeName = Utils::FileSystem::getFileName(mSearchParams.game->getPath()) + " (" + - MameNames::getInstance()->getCleanName(mSearchParams.game->getCleanName()) + - ")"; + Settings::getInstance()->getString("Scraper") == "thegamesdb") + scrapeName = + Utils::FileSystem::getFileName(mSearchParams.game->getPath()) + " (" + + MameNames::getInstance()->getCleanName(mSearchParams.game->getCleanName()) + ")"; else scrapeName = Utils::FileSystem::getFileName(mSearchParams.game->getPath()); } - mGameName = std::make_shared(mWindow, scrapeName + + mGameName = std::make_shared( + mWindow, + scrapeName + ((mSearchParams.game->getType() == FOLDER) ? " " + ViewController::FOLDER_CHAR : ""), - Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); + Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); mGrid.setEntry(mGameName, Vector2i(0, 1), false, true); // Row 2 is a spacer. - mSystemName = std::make_shared(mWindow, Utils::String::toUpper( - mSearchParams.system->getFullName()), Font::get(FONT_SIZE_SMALL), - 0x888888FF, ALIGN_CENTER); + mSystemName = std::make_shared( + mWindow, Utils::String::toUpper(mSearchParams.system->getFullName()), + Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); mGrid.setEntry(mSystemName, Vector2i(0, 3), false, true); // Row 4 is a spacer. // GuiScraperSearch. - mSearch = std::make_shared(window, - GuiScraperSearch::NEVER_AUTO_ACCEPT, 1); + mSearch = std::make_shared(window, GuiScraperSearch::NEVER_AUTO_ACCEPT, 1); mGrid.setEntry(mSearch, Vector2i(0, 5), true); // Buttons std::vector> buttons; - buttons.push_back(std::make_shared(mWindow, "REFINE SEARCH", - "refine search", [&] { - mSearch->openInputScreen(mSearchParams); - mGrid.resetCursor(); + buttons.push_back( + std::make_shared(mWindow, "REFINE SEARCH", "refine search", [&] { + mSearch->openInputScreen(mSearchParams); + mGrid.resetCursor(); + })); + buttons.push_back(std::make_shared(mWindow, "CANCEL", "cancel", [&] { + if (mSearch->getSavedNewMedia()) { + // If the user aborted the scraping but there was still some media downloaded, + // then force an unload of the textures for the game image and marquee, and make + // an update of the game entry. Otherwise the images would not get updated until + // the user scrolls up and down the gamelist. + TextureResource::manualUnload(mSearchParams.game->getImagePath(), false); + TextureResource::manualUnload(mSearchParams.game->getMarqueePath(), false); + ViewController::get()->onFileChanged(mSearchParams.game, true); + } + delete this; })); - buttons.push_back(std::make_shared( - mWindow, "CANCEL", "cancel", [&] { - if (mSearch->getSavedNewMedia()) { - // If the user aborted the scraping but there was still some media downloaded, - // then force an unload of the textures for the game image and marquee, and make - // an update of the game entry. Otherwise the images would not get updated until - // the user scrolls up and down the gamelist. - TextureResource::manualUnload(mSearchParams.game->getImagePath(), false); - TextureResource::manualUnload(mSearchParams.game->getMarqueePath(), false); - ViewController::get()->onFileChanged(mSearchParams.game, true); - } - delete this; })); mButtonGrid = makeButtonGrid(mWindow, buttons); mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false); - // We call this->close() instead of just 'delete this' in the accept callback. - // This is because of how GuiComponent::update works. If it was just 'delete this', - // the following would happen when the metadata resolver is done: - // GuiGameScraper::update() - // GuiComponent::update() - // it = mChildren.cbegin(); - // mBox::update() - // it++; - // mSearchComponent::update() - // acceptCallback -> delete this - // it++; // Error, mChildren has been deleted because it was part of 'this'. - - // So instead we do this: - // GuiGameScraper::update() - // GuiComponent::update() - // it = mChildren.cbegin(); - // mBox::update() - // it++; - // mSearchComponent::update() - // acceptCallback -> close() -> mClose = true - // it++; // OK. - // if (mClose) - // delete this; mSearch->setAcceptCallback([this, doneFunc](const ScraperSearchResult& result) { - doneFunc(result); close(); }); + doneFunc(result); + close(); + }); mSearch->setCancelCallback([&] { delete this; }); // Limit the width of the GUI on ultrawide monitors. The 1.778 aspect ratio value is @@ -124,8 +103,8 @@ GuiGameScraper::GuiGameScraper( float width = Math::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth(); setSize(width, Renderer::getScreenHeight() * 0.747f); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - - mSize.y()) / 2); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, + (Renderer::getScreenHeight() - mSize.y()) / 2.0f); mGrid.resetCursor(); mSearch->search(params); // Start the search. @@ -133,14 +112,14 @@ GuiGameScraper::GuiGameScraper( void GuiGameScraper::onSizeChanged() { - mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); mGrid.setRowHeightPerc(0, 0.04f, false); - mGrid.setRowHeightPerc(1, mGameName->getFont()->getLetterHeight() / - mSize.y(), false); // Game name. + mGrid.setRowHeightPerc(1, mGameName->getFont()->getLetterHeight() / mSize.y(), + false); // Game name. mGrid.setRowHeightPerc(2, 0.04f, false); - mGrid.setRowHeightPerc(3, mSystemName->getFont()->getLetterHeight() / - mSize.y(), false); // System name. + mGrid.setRowHeightPerc(3, mSystemName->getFont()->getLetterHeight() / mSize.y(), + false); // System name. mGrid.setRowHeightPerc(4, 0.04f, false); mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y() / mSize.y(), false); // Buttons. mGrid.setSize(mSize); @@ -190,5 +169,6 @@ HelpStyle GuiGameScraper::getHelpStyle() void GuiGameScraper::close() { + // This will cause update() to close the GUI. mClose = true; } diff --git a/es-app/src/guis/GuiGameScraper.h b/es-app/src/guis/GuiGameScraper.h index d7507b040..b910c1351 100644 --- a/es-app/src/guis/GuiGameScraper.h +++ b/es-app/src/guis/GuiGameScraper.h @@ -11,15 +11,16 @@ #ifndef ES_APP_GUIS_GUI_GAME_SCRAPER_H #define ES_APP_GUIS_GUI_GAME_SCRAPER_H +#include "GuiComponent.h" #include "components/NinePatchComponent.h" #include "guis/GuiScraperSearch.h" -#include "GuiComponent.h" class GuiGameScraper : public GuiComponent { public: - GuiGameScraper(Window* window, ScraperSearchParams params, - std::function doneFunc); + GuiGameScraper(Window* window, + ScraperSearchParams params, + std::function doneFunc); void onSizeChanged() override; diff --git a/es-app/src/guis/GuiGamelistFilter.cpp b/es-app/src/guis/GuiGamelistFilter.cpp index 135516d8c..2c48660cf 100644 --- a/es-app/src/guis/GuiGamelistFilter.cpp +++ b/es-app/src/guis/GuiGamelistFilter.cpp @@ -10,21 +10,20 @@ #include "guis/GuiGamelistFilter.h" +#include "SystemData.h" #include "components/OptionListComponent.h" #include "guis/GuiTextEditPopup.h" #include "views/UIModeController.h" #include "views/ViewController.h" -#include "SystemData.h" -GuiGamelistFilter::GuiGamelistFilter( - Window* window, - SystemData* system, - std::function filterChangedCallback) - : GuiComponent(window), - mMenu(window, "FILTER GAMELIST BY"), - mSystem(system), - mFiltersChangedCallback(filterChangedCallback), - mFiltersChanged(false) +GuiGamelistFilter::GuiGamelistFilter(Window* window, + SystemData* system, + std::function filterChangedCallback) + : GuiComponent(window) + , mMenu(window, "FILTER GAMELIST BY") + , mSystem(system) + , mFiltersChangedCallback(filterChangedCallback) + , mFiltersChanged(false) { initializeMenu(); } @@ -41,7 +40,8 @@ void GuiGamelistFilter::initializeMenu() // Show filtered menu. row.elements.clear(); row.addElement(std::make_shared(mWindow, "RESET ALL FILTERS", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.makeAcceptInputHandler(std::bind(&GuiGamelistFilter::resetAllFilters, this)); mMenu.addRow(row); row.elements.clear(); @@ -51,13 +51,15 @@ void GuiGamelistFilter::initializeMenu() mMenu.addButton("BACK", "back", std::bind(&GuiGamelistFilter::applyFilters, this)); mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2.0f, - Renderer::getScreenHeight() * 0.13f); + Renderer::getScreenHeight() * 0.13f); // Save the initial filter values to be able to check later if any changes were made. mInitialTextFilter = mTextFilterField->getValue(); - for (std::map>>:: - const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); it++) { + for (std::map>>::const_iterator it = + mFilterOptions.cbegin(); + it != mFilterOptions.cend(); it++) { std::shared_ptr> optionList = it->second; std::vector filters = optionList->getSelectedObjects(); mInitialFilters.push_back(filters); @@ -67,8 +69,10 @@ void GuiGamelistFilter::initializeMenu() void GuiGamelistFilter::resetAllFilters() { mFilterIndex->resetFilters(); - for (std::map>>:: - const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); it++) { + for (std::map>>::const_iterator it = + mFilterOptions.cbegin(); + it != mFilterOptions.cend(); it++) { std::shared_ptr> optionList = it->second; optionList->selectNone(); } @@ -78,21 +82,18 @@ void GuiGamelistFilter::resetAllFilters() mFiltersChanged = true; } -GuiGamelistFilter::~GuiGamelistFilter() -{ - mFilterOptions.clear(); -} +GuiGamelistFilter::~GuiGamelistFilter() { mFilterOptions.clear(); } void GuiGamelistFilter::addFiltersToMenu() { ComponentListRow row; - auto lbl = std::make_shared(mWindow, - Utils::String::toUpper("TEXT FILTER (GAME NAME)"), - Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + auto lbl = + std::make_shared(mWindow, Utils::String::toUpper("TEXT FILTER (GAME NAME)"), + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); - mTextFilterField = std::make_shared(mWindow, "", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT); + mTextFilterField = std::make_shared(mWindow, "", Font::get(FONT_SIZE_MEDIUM), + 0x777777FF, ALIGN_RIGHT); // Don't show the free text filter entry unless there are any games in the system. if (mSystem->getRootFolder()->getChildren().size() > 0) { @@ -113,25 +114,25 @@ void GuiGamelistFilter::addFiltersToMenu() // Callback function. auto updateVal = [this](const std::string& newVal) { - mTextFilterField->setValue(Utils::String::toUpper(newVal)); - mFilterIndex->setTextFilter(Utils::String::toUpper(newVal)); + mTextFilterField->setValue(Utils::String::toUpper(newVal)); + mFilterIndex->setTextFilter(Utils::String::toUpper(newVal)); }; row.makeAcceptInputHandler([this, updateVal] { - mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), - "TEXT FILTER (GAME NAME)", mTextFilterField->getValue(), - updateVal, false, "OK", "APPLY CHANGES?")); + mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "TEXT FILTER (GAME NAME)", + mTextFilterField->getValue(), updateVal, false, "OK", + "APPLY CHANGES?")); }); mMenu.addRow(row); std::vector decls = mFilterIndex->getFilterDataDecls(); - for (std::vector::const_iterator it = - decls.cbegin(); it != decls.cend(); it++) { - + for (std::vector::const_iterator it = decls.cbegin(); // Line break. + it != decls.cend(); it++) { FilterIndexType type = (*it).type; // Type of filter. - // All possible filters for this type. + + // All possible filters for this type. std::map* allKeys = (*it).allIndexKeys; std::string menuLabel = (*it).menuLabel; // Text to show in menu. std::shared_ptr> optionList; @@ -140,9 +141,9 @@ void GuiGamelistFilter::addFiltersToMenu() ComponentListRow row; // Add genres. - optionList = std::make_shared> - (mWindow, getHelpStyle(), menuLabel, true); - for (auto it: *allKeys) + optionList = std::make_shared>(mWindow, getHelpStyle(), + menuLabel, true); + for (auto it : *allKeys) optionList->add(it.first, it.first, mFilterIndex->isKeyBeingFilteredBy(it.first, type)); if (allKeys->size() > 0) mMenu.addWithLabel(menuLabel, optionList); @@ -157,8 +158,10 @@ void GuiGamelistFilter::applyFilters() mFiltersChanged = true; std::vector decls = mFilterIndex->getFilterDataDecls(); - for (std::map>>:: - const_iterator it = mFilterOptions.cbegin(); it != mFilterOptions.cend(); it++) { + for (std::map>>::const_iterator it = + mFilterOptions.cbegin(); + it != mFilterOptions.cend(); it++) { std::shared_ptr> optionList = it->second; std::vector filters = optionList->getSelectedObjects(); auto iteratorDistance = std::distance(mFilterOptions.cbegin(), it); diff --git a/es-app/src/guis/GuiGamelistFilter.h b/es-app/src/guis/GuiGamelistFilter.h index 57328877e..90fdc0c02 100644 --- a/es-app/src/guis/GuiGamelistFilter.h +++ b/es-app/src/guis/GuiGamelistFilter.h @@ -11,19 +11,19 @@ #ifndef ES_APP_GUIS_GUI_GAME_LIST_FILTER_H #define ES_APP_GUIS_GUI_GAME_LIST_FILTER_H -#include "components/MenuComponent.h" #include "FileFilterIndex.h" #include "GuiComponent.h" +#include "components/MenuComponent.h" -template -class OptionListComponent; +template class OptionListComponent; class SystemData; class GuiGamelistFilter : public GuiComponent { public: GuiGamelistFilter(Window* window, - SystemData* system, std::function filtersChangedCallback); + SystemData* system, + std::function filtersChangedCallback); ~GuiGamelistFilter(); bool input(InputConfig* config, Input input) override; diff --git a/es-app/src/guis/GuiGamelistOptions.cpp b/es-app/src/guis/GuiGamelistOptions.cpp index dd1e99e83..0db9424a3 100644 --- a/es-app/src/guis/GuiGamelistOptions.cpp +++ b/es-app/src/guis/GuiGamelistOptions.cpp @@ -12,11 +12,6 @@ #include "GuiGamelistOptions.h" -#include "guis/GuiGamelistFilter.h" -#include "scrapers/Scraper.h" -#include "views/gamelist/IGameListView.h" -#include "views/UIModeController.h" -#include "views/ViewController.h" #include "CollectionSystemsManager.h" #include "FileFilterIndex.h" #include "FileSorts.h" @@ -24,18 +19,21 @@ #include "MameNames.h" #include "Sound.h" #include "SystemData.h" +#include "guis/GuiGamelistFilter.h" +#include "scrapers/Scraper.h" +#include "views/UIModeController.h" +#include "views/ViewController.h" +#include "views/gamelist/IGameListView.h" -GuiGamelistOptions::GuiGamelistOptions( - Window* window, - SystemData* system) - : GuiComponent(window), - mSystem(system), - mMenu(window, "OPTIONS"), - mFiltersChanged(false), - mCancelled(false), - mIsCustomCollection(false), - mIsCustomCollectionGroup(false), - mCustomCollectionSystem(nullptr) +GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) + : GuiComponent(window) + , mSystem(system) + , mMenu(window, "OPTIONS") + , mFiltersChanged(false) + , mCancelled(false) + , mIsCustomCollection(false) + , mIsCustomCollectionGroup(false) + , mCustomCollectionSystem(nullptr) { addChild(&mMenu); @@ -46,11 +44,10 @@ GuiGamelistOptions::GuiGamelistOptions( ComponentListRow row; // There is some special logic required for custom collections. - if (file->getSystem()->isCustomCollection() && - file->getPath() != file->getSystem()->getName()) + if (file->getSystem()->isCustomCollection() && file->getPath() != file->getSystem()->getName()) mIsCustomCollection = true; else if (file->getSystem()->isCustomCollection() && - file->getPath() == file->getSystem()->getName()) + file->getPath() == file->getSystem()->getName()) mIsCustomCollectionGroup = true; if (mFromPlaceholder && file->getSystem()->isGroupedCustomCollection()) @@ -84,12 +81,12 @@ GuiGamelistOptions::GuiGamelistOptions( else { // Check if the currently selected game is a favorite. bool isFavorite = false; - if (mFirstLetterIndex.size() == 1 && mFirstLetterIndex.front() == - ViewController::FAVORITE_CHAR) + if (mFirstLetterIndex.size() == 1 && + mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR) isFavorite = true; - else if (mFirstLetterIndex.size() > 1 && (mFirstLetterIndex.front() == - ViewController::FAVORITE_CHAR || mFirstLetterIndex[1] == - ViewController::FAVORITE_CHAR)) + else if (mFirstLetterIndex.size() > 1 && + (mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR || + mFirstLetterIndex[1] == ViewController::FAVORITE_CHAR)) isFavorite = true; // Get the first character of the game name (which could be a Unicode character). @@ -99,8 +96,8 @@ GuiGamelistOptions::GuiGamelistOptions( mCurrentFirstCharacter = Utils::String::getFirstCharacter(file->getSortName()); } - mJumpToLetterList = std::make_shared(mWindow, getHelpStyle(), - "JUMP TO...", false); + mJumpToLetterList = + std::make_shared(mWindow, getHelpStyle(), "JUMP TO...", false); // Populate the quick selector. for (unsigned int i = 0; i < mFirstLetterIndex.size(); i++) { @@ -146,8 +143,9 @@ GuiGamelistOptions::GuiGamelistOptions( if (!mIsCustomCollectionGroup && system->getRootFolder()->getChildren().size() > 0) { if (system->getName() != "recent" && Settings::getInstance()->getBool("GamelistFilters")) { row.elements.clear(); - row.addElement(std::make_shared - (mWindow, "FILTER GAMELIST", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(mWindow, "FILTER GAMELIST", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.addElement(makeArrow(mWindow), false); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openGamelistFilter, this)); mMenu.addRow(row); @@ -155,12 +153,12 @@ GuiGamelistOptions::GuiGamelistOptions( } // Add a dummy entry when applicable as the menu looks quite ugly if it's just blank. else if (!CollectionSystemsManager::get()->isEditing() && - mSystem->getRootFolder()->getChildren().size() == 0 && - !mIsCustomCollectionGroup && !mIsCustomCollection) { + mSystem->getRootFolder()->getChildren().size() == 0 && !mIsCustomCollectionGroup && + !mIsCustomCollection) { row.elements.clear(); - row.addElement(std::make_shared - (mWindow, "THIS SYSTEM HAS NO GAMES", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(mWindow, "THIS SYSTEM HAS NO GAMES", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); mMenu.addRow(row); } @@ -171,34 +169,40 @@ GuiGamelistOptions::GuiGamelistOptions( customSystem = system->getName(); if (UIModeController::getInstance()->isUIModeFull() && - (mIsCustomCollection || mIsCustomCollectionGroup)) { + (mIsCustomCollection || mIsCustomCollectionGroup)) { if (CollectionSystemsManager::get()->getEditingCollection() != customSystem) { row.elements.clear(); - row.addElement(std::make_shared( - mWindow, "ADD/REMOVE GAMES TO THIS COLLECTION", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(mWindow, + "ADD/REMOVE GAMES TO THIS COLLECTION", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::startEditMode, this)); mMenu.addRow(row); } } if (UIModeController::getInstance()->isUIModeFull() && - CollectionSystemsManager::get()->isEditing()) { + CollectionSystemsManager::get()->isEditing()) { row.elements.clear(); row.addElement(std::make_shared( - mWindow, "FINISH EDITING '" + Utils::String::toUpper( - CollectionSystemsManager::get()->getEditingCollection()) + - "' COLLECTION",Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + mWindow, + "FINISH EDITING '" + + Utils::String::toUpper( + CollectionSystemsManager::get()->getEditingCollection()) + + "' COLLECTION", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::exitEditMode, this)); mMenu.addRow(row); } if (file->getType() == FOLDER) { if (UIModeController::getInstance()->isUIModeFull() && !mFromPlaceholder && - !(mSystem->isCollection() && file->getType() == FOLDER)) { + !(mSystem->isCollection() && file->getType() == FOLDER)) { row.elements.clear(); - row.addElement(std::make_shared(mWindow, - "EDIT THIS FOLDER'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(mWindow, "EDIT THIS FOLDER'S METADATA", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.addElement(makeArrow(mWindow), false); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this)); mMenu.addRow(row); @@ -206,10 +210,11 @@ GuiGamelistOptions::GuiGamelistOptions( } else { if (UIModeController::getInstance()->isUIModeFull() && !mFromPlaceholder && - !(mSystem->isCollection() && file->getType() == FOLDER)) { + !(mSystem->isCollection() && file->getType() == FOLDER)) { row.elements.clear(); - row.addElement(std::make_shared(mWindow, - "EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(mWindow, "EDIT THIS GAME'S METADATA", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.addElement(makeArrow(mWindow), false); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this)); mMenu.addRow(row); @@ -218,19 +223,25 @@ GuiGamelistOptions::GuiGamelistOptions( // Buttons. The logic to apply or cancel settings are handled by the destructor. if ((!mIsCustomCollectionGroup && system->getRootFolder()->getChildren().size() == 0) || - system->getName() == "recent") { - mMenu.addButton("CLOSE", "close", [&] { mCancelled = true; delete this; }); + system->getName() == "recent") { + mMenu.addButton("CLOSE", "close", [&] { + mCancelled = true; + delete this; + }); } else { mMenu.addButton("APPLY", "apply", [&] { delete this; }); - mMenu.addButton("CANCEL", "cancel", [&] { mCancelled = true; delete this; }); + mMenu.addButton("CANCEL", "cancel", [&] { + mCancelled = true; + delete this; + }); } // Center the menu. setSize(static_cast(Renderer::getScreenWidth()), static_cast(Renderer::getScreenHeight())); - mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2.0f, (mSize.y() - - mMenu.getSize().y()) / 2.0f); + mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2.0f, + (mSize.y() - mMenu.getSize().y()) / 2.0f); } GuiGamelistOptions::~GuiGamelistOptions() @@ -248,11 +259,12 @@ GuiGamelistOptions::~GuiGamelistOptions() if (!mFromPlaceholder) { ViewController::get()->reloadGameListView(mSystem); } - else if (!mCustomCollectionSystem->getRootFolder()-> - getChildrenListToDisplay().empty()) { - ViewController::get()->reloadGameListView(mSystem); - getGamelist()->setCursor(mCustomCollectionSystem-> - getRootFolder()->getChildrenListToDisplay().front()); + else if (!mCustomCollectionSystem->getRootFolder() + ->getChildrenListToDisplay() + .empty()) { + ViewController::get()->reloadGameListView(mSystem); + getGamelist()->setCursor( + mCustomCollectionSystem->getRootFolder()->getChildrenListToDisplay().front()); } } } @@ -262,6 +274,7 @@ GuiGamelistOptions::~GuiGamelistOptions() if (!mFromPlaceholder) { FileData* root; + if (mIsCustomCollection) root = getGamelist()->getCursor()->getSystem()->getRootFolder(); else @@ -269,7 +282,7 @@ GuiGamelistOptions::~GuiGamelistOptions() // If a new sorting type was selected, then sort and update mSortTypeString for the system. if (!mIsCustomCollectionGroup && - (*mListSort->getSelected()).description != root->getSortTypeString()) { + (*mListSort->getSelected()).description != root->getSortTypeString()) { // This will also recursively sort children. root->sort(*mListSort->getSelected(), mFavoritesSorting); root->setSortTypeString((*mListSort->getSelected()).description); @@ -281,7 +294,7 @@ GuiGamelistOptions::~GuiGamelistOptions() // Has the user changed the letter using the quick selector? if (mCurrentFirstCharacter != mJumpToLetterList->getSelected()) { if (mJumpToLetterList->getSelected() == ViewController::FAVORITE_CHAR || - mJumpToLetterList->getSelected() == ViewController::FOLDER_CHAR) + mJumpToLetterList->getSelected() == ViewController::FOLDER_CHAR) jumpToFirstRow(); else jumpToLetter(); @@ -303,7 +316,7 @@ void GuiGamelistOptions::openGamelistFilter() if (mIsCustomCollection) ggf = new GuiGamelistFilter(mWindow, getGamelist()->getCursor()->getSystem(), - filtersChangedFunc); + filtersChangedFunc); else ggf = new GuiGamelistFilter(mWindow, mSystem, filtersChangedFunc); @@ -329,10 +342,9 @@ void GuiGamelistOptions::startEditMode() // Display the indication icons which show what games are part of the custom collection // currently being edited. This is done cheaply using onFileChanged() which will trigger // populateList(). - for (auto it = SystemData::sSystemVector.begin(); - it != SystemData::sSystemVector.end(); it++) { + for (auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); it++) { ViewController::get()->getGameListView((*it))->onFileChanged( - ViewController::get()->getGameListView((*it))->getCursor(), false); + ViewController::get()->getGameListView((*it))->getCursor(), false); } if (mSystem->getRootFolder()->getChildren().size() == 0) @@ -362,12 +374,12 @@ void GuiGamelistOptions::openMetaDataEd() clearGameBtnFunc = [this, file] { if (file->getType() == FOLDER) { - LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the folder \"" << - file->getFullPath() << "\""; + LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the folder \"" + << file->getFullPath() << "\""; } else { - LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the file \"" << - file->getFullPath() << "\""; + LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the file \"" + << file->getFullPath() << "\""; } ViewController::get()->getGameListView(file->getSystem()).get()->removeMedia(file); @@ -375,10 +387,10 @@ void GuiGamelistOptions::openMetaDataEd() const std::vector& mdd = file->metadata.getMDD(); for (auto it = mdd.cbegin(); it != mdd.cend(); it++) { if (it->key == "name") { - if (file->isArcadeGame()) { + if (file->isArcadeGame()) { // If it's a MAME or Neo Geo game, expand the game name accordingly. - file->metadata.set(it->key, MameNames::getInstance()-> - getCleanName(file->getCleanName())); + file->metadata.set( + it->key, MameNames::getInstance()->getCleanName(file->getCleanName())); } else { file->metadata.set(it->key, file->getDisplayName()); @@ -408,8 +420,8 @@ void GuiGamelistOptions::openMetaDataEd() }; deleteGameBtnFunc = [this, file] { - LOG(LogInfo) << "Deleting the game file \"" << file->getFullPath() << - "\", all its media files and its gamelist.xml entry."; + LOG(LogInfo) << "Deleting the game file \"" << file->getFullPath() + << "\", all its media files and its gamelist.xml entry."; CollectionSystemsManager::get()->deleteCollectionFiles(file); ViewController::get()->getGameListView(file->getSystem()).get()->removeMedia(file); ViewController::get()->getGameListView(file->getSystem()).get()->remove(file, true); @@ -420,20 +432,20 @@ void GuiGamelistOptions::openMetaDataEd() }; if (file->getType() == FOLDER) { - mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, - file->metadata.getMDD(FOLDER_METADATA), p, - Utils::FileSystem::getFileName(file->getPath()), std::bind( - &IGameListView::onFileChanged, ViewController::get()->getGameListView( - file->getSystem()).get(), file, true), - clearGameBtnFunc, deleteGameBtnFunc)); + mWindow->pushGui(new GuiMetaDataEd( + mWindow, &file->metadata, file->metadata.getMDD(FOLDER_METADATA), p, + Utils::FileSystem::getFileName(file->getPath()), + std::bind(&IGameListView::onFileChanged, + ViewController::get()->getGameListView(file->getSystem()).get(), file, true), + clearGameBtnFunc, deleteGameBtnFunc)); } else { - mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, - file->metadata.getMDD(GAME_METADATA), p, - Utils::FileSystem::getFileName(file->getPath()), std::bind( - &IGameListView::onFileChanged, ViewController::get()->getGameListView( - file->getSystem()).get(), file, true), - clearGameBtnFunc, deleteGameBtnFunc)); + mWindow->pushGui(new GuiMetaDataEd( + mWindow, &file->metadata, file->metadata.getMDD(GAME_METADATA), p, + Utils::FileSystem::getFileName(file->getPath()), + std::bind(&IGameListView::onFileChanged, + ViewController::get()->getGameListView(file->getSystem()).get(), file, true), + clearGameBtnFunc, deleteGameBtnFunc)); } } @@ -442,14 +454,14 @@ void GuiGamelistOptions::jumpToLetter() char letter = mJumpToLetterList->getSelected().front(); // Get the gamelist. - const std::vector& files = getGamelist()->getCursor()-> - getParent()->getChildrenListToDisplay(); + const std::vector& files = + getGamelist()->getCursor()->getParent()->getChildrenListToDisplay(); for (unsigned int i = 0; i < files.size(); i++) { if (mFavoritesSorting && (mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR || - mFirstLetterIndex.front() == ViewController::FOLDER_CHAR)) { - if (static_cast(toupper(files.at(i)->getSortName().front())) == - letter && !files.at(i)->getFavorite()) { + mFirstLetterIndex.front() == ViewController::FOLDER_CHAR)) { + if (static_cast(toupper(files.at(i)->getSortName().front())) == letter && + !files.at(i)->getFavorite()) { if (!mOnlyHasFolders && mFoldersOnTop && files.at(i)->getType() == FOLDER) { continue; } @@ -477,8 +489,8 @@ void GuiGamelistOptions::jumpToFirstRow() { if (mFoldersOnTop && mJumpToLetterList->getSelected() == ViewController::FAVORITE_CHAR) { // Get the gamelist. - const std::vector& files = getGamelist()->getCursor()-> - getParent()->getChildrenListToDisplay(); + const std::vector& files = + getGamelist()->getCursor()->getParent()->getChildrenListToDisplay(); // Select the first game that is not a folder, unless it's a folder-only list in // which case the first line overall is selected. for (auto it = files.cbegin(); it != files.cend(); it++) { @@ -502,8 +514,7 @@ bool GuiGamelistOptions::input(InputConfig* config, Input input) if (input.value != 0 && config->isMappedTo("back", input)) mCancelled = true; - if (input.value != 0 && (config->isMappedTo("b", input) || - config->isMappedTo("back", input))) { + if (input.value != 0 && (config->isMappedTo("b", input) || config->isMappedTo("back", input))) { delete this; return true; } @@ -521,9 +532,8 @@ HelpStyle GuiGamelistOptions::getHelpStyle() std::vector GuiGamelistOptions::getHelpPrompts() { auto prompts = mMenu.getHelpPrompts(); - if (mSystem->getRootFolder()->getChildren().size() > 0 || - mIsCustomCollectionGroup || mIsCustomCollection || - CollectionSystemsManager::get()->isEditing()) + if (mSystem->getRootFolder()->getChildren().size() > 0 || mIsCustomCollectionGroup || + mIsCustomCollection || CollectionSystemsManager::get()->isEditing()) prompts.push_back(HelpPrompt("a", "select")); if (mSystem->getRootFolder()->getChildren().size() > 0 && mSystem->getName() != "recent") { prompts.push_back(HelpPrompt("b", "close (apply)")); diff --git a/es-app/src/guis/GuiGamelistOptions.h b/es-app/src/guis/GuiGamelistOptions.h index edb9eb693..3226ae38d 100644 --- a/es-app/src/guis/GuiGamelistOptions.h +++ b/es-app/src/guis/GuiGamelistOptions.h @@ -13,11 +13,11 @@ #ifndef ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H #define ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H +#include "FileData.h" +#include "GuiComponent.h" #include "components/MenuComponent.h" #include "components/OptionListComponent.h" #include "utils/StringUtil.h" -#include "FileData.h" -#include "GuiComponent.h" class IGameListView; class SystemData; diff --git a/es-app/src/guis/GuiInfoPopup.cpp b/es-app/src/guis/GuiInfoPopup.cpp index 2802a2975..c7ab4e9e4 100644 --- a/es-app/src/guis/GuiInfoPopup.cpp +++ b/es-app/src/guis/GuiInfoPopup.cpp @@ -14,21 +14,18 @@ #include -GuiInfoPopup::GuiInfoPopup( - Window* window, - std::string message, - int duration) - : GuiComponent(window), - mMessage(message), - mDuration(duration), - mRunning(true) +GuiInfoPopup::GuiInfoPopup(Window* window, std::string message, int duration) + : GuiComponent(window) + , mMessage(message) + , mDuration(duration) + , mRunning(true) { mFrame = new NinePatchComponent(window); float maxWidth = Renderer::getScreenWidth() * 0.9f; float maxHeight = Renderer::getScreenHeight() * 0.2f; - std::shared_ptr s = std::make_shared(mWindow, "", - Font::get(FONT_SIZE_MINI), 0x444444FF, ALIGN_CENTER); + std::shared_ptr s = std::make_shared( + mWindow, "", Font::get(FONT_SIZE_MINI), 0x444444FF, ALIGN_CENTER); // We do this to force the text container to resize and return the actual expected popup size. s->setSize(0.0f, 0.0f); @@ -51,13 +48,13 @@ GuiInfoPopup::GuiInfoPopup( mSize[0] = mSize.x() + paddingX; mSize[1] = mSize.y() + paddingY; - float posX = Renderer::getScreenWidth()* 0.5f - mSize.x() * 0.5f; + float posX = Renderer::getScreenWidth() * 0.5f - mSize.x() * 0.5f; float posY = Renderer::getScreenHeight() * 0.02f; setPosition(posX, posY, 0); mFrame->setImagePath(":/graphics/frame.svg"); - mFrame->fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mFrame->fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); addChild(mFrame); // We only initialize the actual time when we first start to render. diff --git a/es-app/src/guis/GuiInfoPopup.h b/es-app/src/guis/GuiInfoPopup.h index 87b381864..8de60ee3a 100644 --- a/es-app/src/guis/GuiInfoPopup.h +++ b/es-app/src/guis/GuiInfoPopup.h @@ -22,7 +22,7 @@ public: ~GuiInfoPopup(); void render(const Transform4x4f& parentTrans) override; - inline void stop() override { mRunning = false; } + void stop() override { mRunning = false; } private: bool updateState(); diff --git a/es-app/src/guis/GuiLaunchScreen.cpp b/es-app/src/guis/GuiLaunchScreen.cpp index 94d4a7754..b20977841 100644 --- a/es-app/src/guis/GuiLaunchScreen.cpp +++ b/es-app/src/guis/GuiLaunchScreen.cpp @@ -8,20 +8,19 @@ #include "guis/GuiLaunchScreen.h" +#include "FileData.h" +#include "SystemData.h" #include "components/ComponentGrid.h" #include "components/TextComponent.h" #include "math/Misc.h" #include "utils/StringUtil.h" -#include "FileData.h" -#include "SystemData.h" -GuiLaunchScreen::GuiLaunchScreen( - Window* window) - : GuiComponent(window), - mBackground(window, ":/graphics/frame.svg"), - mGrid(nullptr), - mMarquee(nullptr), - mWindow(window) +GuiLaunchScreen::GuiLaunchScreen(Window* window) + : GuiComponent(window) + , mBackground(window, ":/graphics/frame.svg") + , mGrid(nullptr) + , mMarquee(nullptr) + , mWindow(window) { addChild(&mBackground); mWindow->setLaunchScreen(this); @@ -29,6 +28,7 @@ GuiLaunchScreen::GuiLaunchScreen( GuiLaunchScreen::~GuiLaunchScreen() { + // This only executes when exiting the application. closeLaunchScreen(); } @@ -51,49 +51,53 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game) const float gameNameFontSize = 0.073f; // Spacer row. - mGrid->setEntry(std::make_shared(mWindow), Vector2i(1, 0), - false, false, Vector2i(1, 1)); + mGrid->setEntry(std::make_shared(mWindow), Vector2i(1, 0), false, false, + Vector2i(1, 1)); // Title. - mTitle = std::make_shared(mWindow, "LAUNCHING GAME", - Font::get(static_cast(titleFontSize * std::min(Renderer::getScreenHeight(), - Renderer::getScreenWidth()))), 0x666666FF, ALIGN_CENTER); + mTitle = std::make_shared( + mWindow, "LAUNCHING GAME", + Font::get(static_cast( + titleFontSize * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))), + 0x666666FF, ALIGN_CENTER); mGrid->setEntry(mTitle, Vector2i(1, 1), false, true, Vector2i(1, 1)); // Spacer row. - mGrid->setEntry(std::make_shared(mWindow), Vector2i(1, 2), - false, false, Vector2i(1, 1)); + mGrid->setEntry(std::make_shared(mWindow), Vector2i(1, 2), false, false, + Vector2i(1, 1)); // Row for the marquee. - mGrid->setEntry(std::make_shared(mWindow), Vector2i(1, 3), - false, false, Vector2i(1, 1)); + mGrid->setEntry(std::make_shared(mWindow), Vector2i(1, 3), false, false, + Vector2i(1, 1)); // Spacer row. - mGrid->setEntry(std::make_shared(mWindow), Vector2i(1, 4), - false, false, Vector2i(1, 1)); + mGrid->setEntry(std::make_shared(mWindow), Vector2i(1, 4), false, false, + Vector2i(1, 1)); // Game name. - mGameName = std::make_shared(mWindow, "GAME NAME", - Font::get(static_cast(gameNameFontSize * std::min(Renderer::getScreenHeight(), - Renderer::getScreenWidth()))), 0x444444FF, ALIGN_CENTER); + mGameName = std::make_shared( + mWindow, "GAME NAME", + Font::get(static_cast( + gameNameFontSize * std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))), + 0x444444FF, ALIGN_CENTER); mGrid->setEntry(mGameName, Vector2i(1, 5), false, true, Vector2i(1, 1)); // System name. - mSystemName = std::make_shared(mWindow, "SYSTEM NAME", - Font::get(FONT_SIZE_MEDIUM), 0x666666FF, ALIGN_CENTER); + mSystemName = std::make_shared( + mWindow, "SYSTEM NAME", Font::get(FONT_SIZE_MEDIUM), 0x666666FF, ALIGN_CENTER); mGrid->setEntry(mSystemName, Vector2i(1, 6), false, true, Vector2i(1, 1)); // Spacer row. - mGrid->setEntry(std::make_shared(mWindow), Vector2i(1, 7), - false, false, Vector2i(1, 1)); + mGrid->setEntry(std::make_shared(mWindow), Vector2i(1, 7), false, false, + Vector2i(1, 1)); // Left spacer. - mGrid->setEntry(std::make_shared(mWindow), Vector2i(0, 0), - false, false, Vector2i(1, 8)); + mGrid->setEntry(std::make_shared(mWindow), Vector2i(0, 0), false, false, + Vector2i(1, 8)); // Right spacer. - mGrid->setEntry(std::make_shared(mWindow), Vector2i(2, 0), - false, false, Vector2i(1, 8)); + mGrid->setEntry(std::make_shared(mWindow), Vector2i(2, 0), false, false, + Vector2i(1, 8)); // Adjust the width depending on the aspect ratio of the screen, to make the screen look // somewhat coherent regardless of screen type. The 1.778 aspect ratio value is the 16:9 @@ -106,9 +110,11 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game) float maxWidth = static_cast(Renderer::getScreenWidth()) * maxWidthModifier; float minWidth = static_cast(Renderer::getScreenWidth()) * minWidthModifier; - float fontWidth = Font::get(static_cast(gameNameFontSize * - std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth())))-> - sizeText(Utils::String::toUpper(game->getName())).x(); + float fontWidth = + Font::get(static_cast(gameNameFontSize * std::min(Renderer::getScreenHeight(), + Renderer::getScreenWidth()))) + ->sizeText(Utils::String::toUpper(game->getName())) + .x(); // Add a bit of width to compensate for the left and right spacers. fontWidth += static_cast(Renderer::getScreenWidth()) * 0.05f; @@ -159,7 +165,8 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game) if (mImagePath != "") { mMarquee->setImage(game->getMarqueePath(), false); mMarquee->cropTransparentPadding(static_cast(Renderer::getScreenWidth()) * - (0.25f * (1.778f / Renderer::getScreenAspectRatio())), mGrid->getRowHeight(3)); + (0.25f * (1.778f / Renderer::getScreenAspectRatio())), + mGrid->getRowHeight(3)); mMarquee->setOrigin(0.5f, 0.5f); Vector3f currentPos = mMarquee->getPosition(); @@ -167,18 +174,18 @@ void GuiLaunchScreen::displayLaunchScreen(FileData* game) // Position the image in the middle of row four. currentPos.x() = mSize.x() / 2.0f; - currentPos.y() = mGrid->getRowHeight(0) + mGrid->getRowHeight(1) + - mGrid->getRowHeight(2) + mGrid->getRowHeight(3) / 2.0f; + currentPos.y() = mGrid->getRowHeight(0) + mGrid->getRowHeight(1) + mGrid->getRowHeight(2) + + mGrid->getRowHeight(3) / 2.0f; mMarquee->setPosition(currentPos); } - setOrigin({0.5f, 0.5f}); + setOrigin({ 0.5f, 0.5f }); // Center on the X axis and keep slightly off-center on the Y axis. setPosition(static_cast(Renderer::getScreenWidth()) / 2.0f, - static_cast(Renderer::getScreenHeight()) / 2.25f); + static_cast(Renderer::getScreenHeight()) / 2.25f); - mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); mBackground.setEdgeColor(0xEEEEEEFF); } @@ -203,6 +210,7 @@ void GuiLaunchScreen::closeLaunchScreen() void GuiLaunchScreen::onSizeChanged() { + // Update mGrid size. mGrid->setSize(mSize); } diff --git a/es-app/src/guis/GuiLaunchScreen.h b/es-app/src/guis/GuiLaunchScreen.h index 56a844fc9..d7572a7e4 100644 --- a/es-app/src/guis/GuiLaunchScreen.h +++ b/es-app/src/guis/GuiLaunchScreen.h @@ -9,12 +9,12 @@ #ifndef ES_APP_GUIS_GUI_LAUNCH_SCREEN_H #define ES_APP_GUIS_GUI_LAUNCH_SCREEN_H +#include "GuiComponent.h" +#include "Window.h" #include "components/ComponentGrid.h" #include "components/ImageComponent.h" #include "components/NinePatchComponent.h" #include "components/TextComponent.h" -#include "GuiComponent.h" -#include "Window.h" class FileData; @@ -35,7 +35,6 @@ public: private: Window* mWindow; ComponentGrid* mGrid; - NinePatchComponent mBackground; std::shared_ptr mTitle; diff --git a/es-app/src/guis/GuiMediaViewerOptions.cpp b/es-app/src/guis/GuiMediaViewerOptions.cpp index 76aadb7f6..38468beca 100644 --- a/es-app/src/guis/GuiMediaViewerOptions.cpp +++ b/es-app/src/guis/GuiMediaViewerOptions.cpp @@ -9,11 +9,11 @@ #include "guis/GuiMediaViewerOptions.h" -#include "components/SwitchComponent.h" #include "Settings.h" +#include "components/SwitchComponent.h" GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string& title) - : GuiSettings(window, title) + : GuiSettings(window, title) { // Keep videos running when viewing images. auto keep_video_running = std::make_shared(mWindow); @@ -21,9 +21,9 @@ GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string& addWithLabel("KEEP VIDEOS RUNNING WHEN VIEWING IMAGES", keep_video_running); addSaveFunc([keep_video_running, this] { if (keep_video_running->getState() != - Settings::getInstance()->getBool("MediaViewerKeepVideoRunning")) { + Settings::getInstance()->getBool("MediaViewerKeepVideoRunning")) { Settings::getInstance()->setBool("MediaViewerKeepVideoRunning", - keep_video_running->getState()); + keep_video_running->getState()); setNeedsSaving(); } }); @@ -34,23 +34,23 @@ GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string& addWithLabel("STRETCH VIDEOS TO SCREEN RESOLUTION", stretch_videos); addSaveFunc([stretch_videos, this] { if (stretch_videos->getState() != - Settings::getInstance()->getBool("MediaViewerStretchVideos")) { + Settings::getInstance()->getBool("MediaViewerStretchVideos")) { Settings::getInstance()->setBool("MediaViewerStretchVideos", - stretch_videos->getState()); + stretch_videos->getState()); setNeedsSaving(); } }); - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) // Render scanlines for videos using a shader. auto video_scanlines = std::make_shared(mWindow); video_scanlines->setState(Settings::getInstance()->getBool("MediaViewerVideoScanlines")); addWithLabel("RENDER SCANLINES FOR VIDEOS", video_scanlines); addSaveFunc([video_scanlines, this] { if (video_scanlines->getState() != - Settings::getInstance()->getBool("MediaViewerVideoScanlines")) { + Settings::getInstance()->getBool("MediaViewerVideoScanlines")) { Settings::getInstance()->setBool("MediaViewerVideoScanlines", - video_scanlines->getState()); + video_scanlines->getState()); setNeedsSaving(); } }); @@ -60,26 +60,24 @@ GuiMediaViewerOptions::GuiMediaViewerOptions(Window* window, const std::string& video_blur->setState(Settings::getInstance()->getBool("MediaViewerVideoBlur")); addWithLabel("RENDER BLUR FOR VIDEOS", video_blur); addSaveFunc([video_blur, this] { - if (video_blur->getState() != - Settings::getInstance()->getBool("MediaViewerVideoBlur")) { - Settings::getInstance()->setBool("MediaViewerVideoBlur", - video_blur->getState()); + if (video_blur->getState() != Settings::getInstance()->getBool("MediaViewerVideoBlur")) { + Settings::getInstance()->setBool("MediaViewerVideoBlur", video_blur->getState()); setNeedsSaving(); } }); // Render scanlines for screenshots using a shader. auto screenshot_scanlines = std::make_shared(mWindow); - screenshot_scanlines->setState(Settings::getInstance()-> - getBool("MediaViewerScreenshotScanlines")); + screenshot_scanlines->setState( + Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")); addWithLabel("RENDER SCANLINES FOR SCREENSHOTS", screenshot_scanlines); addSaveFunc([screenshot_scanlines, this] { if (screenshot_scanlines->getState() != - Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) { + Settings::getInstance()->getBool("MediaViewerScreenshotScanlines")) { Settings::getInstance()->setBool("MediaViewerScreenshotScanlines", - screenshot_scanlines->getState()); + screenshot_scanlines->getState()); setNeedsSaving(); } }); - #endif +#endif } diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index f2be3c141..2333b83ed 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -9,6 +9,14 @@ #include "guis/GuiMenu.h" +#include "CollectionSystemsManager.h" +#include "EmulationStation.h" +#include "FileFilterIndex.h" +#include "FileSorts.h" +#include "Platform.h" +#include "Scripting.h" +#include "SystemData.h" +#include "VolumeControl.h" #include "components/OptionListComponent.h" #include "components/SliderComponent.h" #include "components/SwitchComponent.h" @@ -19,23 +27,17 @@ #include "guis/GuiMsgBox.h" #include "guis/GuiScraperMenu.h" #include "guis/GuiScreensaverOptions.h" -#include "views/gamelist/IGameListView.h" #include "views/UIModeController.h" #include "views/ViewController.h" -#include "CollectionSystemsManager.h" -#include "EmulationStation.h" -#include "FileFilterIndex.h" -#include "FileSorts.h" -#include "Platform.h" -#include "Scripting.h" -#include "SystemData.h" -#include "VolumeControl.h" +#include "views/gamelist/IGameListView.h" -#include #include +#include -GuiMenu::GuiMenu(Window* window) : GuiComponent(window), - mMenu(window, "MAIN MENU"), mVersion(window) +GuiMenu::GuiMenu(Window* window) + : GuiComponent(window) + , mMenu(window, "MAIN MENU") + , mVersion(window) { bool isFullUI = UIModeController::getInstance()->isUIModeFull(); @@ -48,34 +50,33 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), addEntry("SOUND SETTINGS", 0x777777FF, true, [this] { openSoundOptions(); }); if (isFullUI) - addEntry("INPUT DEVICE SETTINGS", 0x777777FF, true, [this] { - openInputDeviceOptions(); }); + addEntry("INPUT DEVICE SETTINGS", 0x777777FF, true, [this] { openInputDeviceOptions(); }); if (isFullUI) - addEntry("GAME COLLECTION SETTINGS", 0x777777FF, true, [this] { - openCollectionSystemOptions(); }); + addEntry("GAME COLLECTION SETTINGS", 0x777777FF, true, + [this] { openCollectionSystemOptions(); }); if (isFullUI) addEntry("OTHER SETTINGS", 0x777777FF, true, [this] { openOtherOptions(); }); // TEMPORARY - disabled for now, will be used in the future. -// if (isFullUI) -// addEntry("UTILITIES", 0x777777FF, true, [this] { -// openUtilitiesMenu(); }); + // if (isFullUI) + // addEntry("UTILITIES", 0x777777FF, true, [this] { + // openUtilitiesMenu(); }); if (!Settings::getInstance()->getBool("ForceKiosk") && - Settings::getInstance()->getString("UIMode") != "kiosk") { + Settings::getInstance()->getString("UIMode") != "kiosk") { if (Settings::getInstance()->getBool("ShowQuitMenu")) - addEntry("QUIT", 0x777777FF, true, [this] {openQuitMenu(); }); + addEntry("QUIT", 0x777777FF, true, [this] { openQuitMenu(); }); else - addEntry("QUIT EMULATIONSTATION", 0x777777FF, false, [this] {openQuitMenu(); }); + addEntry("QUIT EMULATIONSTATION", 0x777777FF, false, [this] { openQuitMenu(); }); } addChild(&mMenu); addVersionInfo(); setSize(mMenu.getSize()); setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, - Renderer::getScreenHeight() * 0.13f); + Renderer::getScreenHeight() * 0.13f); } GuiMenu::~GuiMenu() @@ -86,28 +87,26 @@ GuiMenu::~GuiMenu() ViewController::get()->stopScrolling(); } -void GuiMenu::openScraperOptions() -{ - mWindow->pushGui(new GuiScraperMenu(mWindow, "SCRAPER")); -} +void GuiMenu::openScraperOptions() { mWindow->pushGui(new GuiScraperMenu(mWindow, "SCRAPER")); } void GuiMenu::openUIOptions() { auto s = new GuiSettings(mWindow, "UI SETTINGS"); // Optionally start in selected system/gamelist. - auto startup_system = std::make_shared> - (mWindow, getHelpStyle(), "GAMELIST ON STARTUP", false); + auto startup_system = std::make_shared>( + mWindow, getHelpStyle(), "GAMELIST ON STARTUP", false); startup_system->add("NONE", "", Settings::getInstance()->getString("StartupSystem") == ""); float dotsSize = Font::get(FONT_SIZE_MEDIUM)->sizeText("...").x(); - for (auto it = SystemData::sSystemVector.cbegin(); - it != SystemData::sSystemVector.cend(); it++) { + for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); + it++) { if ((*it)->getName() != "retropie") { // If required, abbreviate the system name so it doesn't overlap the setting name. - std::string abbreviatedString = Font::get(FONT_SIZE_MEDIUM)-> - getTextMaxWidth((*it)->getFullName(), mSize.x() * 0.47f); + std::string abbreviatedString = + Font::get(FONT_SIZE_MEDIUM) + ->getTextMaxWidth((*it)->getFullName(), mSize.x() * 0.47f); float sizeDifference = Font::get(FONT_SIZE_MEDIUM)->sizeText((*it)->getFullName()).x() - - Font::get(FONT_SIZE_MEDIUM)->sizeText(abbreviatedString).x(); + Font::get(FONT_SIZE_MEDIUM)->sizeText(abbreviatedString).x(); if (sizeDifference > 0) { // It doesn't make sense to abbreviate if the number of pixels removed by // the abbreviation is less or equal to the size of the three dots that @@ -122,7 +121,8 @@ void GuiMenu::openUIOptions() } } startup_system->add(abbreviatedString, (*it)->getName(), - Settings::getInstance()->getString("StartupSystem") == (*it)->getName()); + Settings::getInstance()->getString("StartupSystem") == + (*it)->getName()); } } s->addWithLabel("GAMELIST ON STARTUP", startup_system); @@ -134,8 +134,8 @@ void GuiMenu::openUIOptions() }); // GameList view style. - auto gamelist_view_style = std::make_shared> - (mWindow, getHelpStyle(), "GAMELIST VIEW STYLE", false); + auto gamelist_view_style = std::make_shared>( + mWindow, getHelpStyle(), "GAMELIST VIEW STYLE", false); std::string selectedViewStyle = Settings::getInstance()->getString("GamelistViewStyle"); gamelist_view_style->add("automatic", "automatic", selectedViewStyle == "automatic"); gamelist_view_style->add("basic", "basic", selectedViewStyle == "basic"); @@ -149,9 +149,9 @@ void GuiMenu::openUIOptions() s->addWithLabel("GAMELIST VIEW STYLE", gamelist_view_style); s->addSaveFunc([gamelist_view_style, s] { if (gamelist_view_style->getSelected() != - Settings::getInstance()->getString("GamelistViewStyle")) { + Settings::getInstance()->getString("GamelistViewStyle")) { Settings::getInstance()->setString("GamelistViewStyle", - gamelist_view_style->getSelected()); + gamelist_view_style->getSelected()); s->setNeedsSaving(); s->setNeedsReloading(); s->setInvalidateCachedBackground(); @@ -159,19 +159,19 @@ void GuiMenu::openUIOptions() }); // Transition style. - auto transition_style = std::make_shared> - (mWindow, getHelpStyle(), "TRANSITION STYLE", false); + auto transition_style = std::make_shared>( + mWindow, getHelpStyle(), "TRANSITION STYLE", false); std::vector transitions; transitions.push_back("slide"); transitions.push_back("fade"); transitions.push_back("instant"); for (auto it = transitions.cbegin(); it != transitions.cend(); it++) - transition_style->add(*it, *it, Settings::getInstance()-> - getString("TransitionStyle") == *it); + transition_style->add(*it, *it, + Settings::getInstance()->getString("TransitionStyle") == *it); s->addWithLabel("TRANSITION STYLE", transition_style); s->addSaveFunc([transition_style, s] { if (transition_style->getSelected() != - Settings::getInstance()->getString("TransitionStyle")) { + Settings::getInstance()->getString("TransitionStyle")) { Settings::getInstance()->setString("TransitionStyle", transition_style->getSelected()); s->setNeedsSaving(); } @@ -181,18 +181,18 @@ void GuiMenu::openUIOptions() auto themeSets = ThemeData::getThemeSets(); if (!themeSets.empty()) { std::map::const_iterator selectedSet = - themeSets.find(Settings::getInstance()->getString("ThemeSet")); + themeSets.find(Settings::getInstance()->getString("ThemeSet")); if (selectedSet == themeSets.cend()) selectedSet = themeSets.cbegin(); - auto theme_set = std::make_shared> - (mWindow, getHelpStyle(), "THEME SET", false); + auto theme_set = std::make_shared>(mWindow, getHelpStyle(), + "THEME SET", false); for (auto it = themeSets.cbegin(); it != themeSets.cend(); it++) theme_set->add(it->first, it->first, it == selectedSet); s->addWithLabel("THEME SET", theme_set); s->addSaveFunc([this, theme_set, s] { if (theme_set->getSelected() != Settings::getInstance()->getString("ThemeSet")) { Scripting::fireEvent("theme-changed", theme_set->getSelected(), - Settings::getInstance()->getString("ThemeSet")); + Settings::getInstance()->getString("ThemeSet")); Settings::getInstance()->setString("ThemeSet", theme_set->getSelected()); CollectionSystemsManager::get()->updateSystemsList(); mWindow->setChangedThemeSet(); @@ -211,8 +211,8 @@ void GuiMenu::openUIOptions() } // UI mode. - auto ui_mode = std::make_shared> - (mWindow, getHelpStyle(), "UI MODE", false); + auto ui_mode = std::make_shared>(mWindow, getHelpStyle(), + "UI MODE", false); std::vector uiModes; uiModes.push_back("full"); uiModes.push_back("kiosk"); @@ -231,14 +231,14 @@ void GuiMenu::openUIOptions() std::string selectedMode = ui_mode->getSelected(); // If any of the force flags are set, then always apply and save the setting. if (selectedMode == Settings::getInstance()->getString("UIMode") && - !Settings::getInstance()->getBool("ForceFull") && - !Settings::getInstance()->getBool("ForceKiosk") && - !Settings::getInstance()->getBool("ForceKid")) { + !Settings::getInstance()->getBool("ForceFull") && + !Settings::getInstance()->getBool("ForceKiosk") && + !Settings::getInstance()->getBool("ForceKid")) { return; } else if (selectedMode != "full") { std::string msg = "YOU ARE CHANGING THE UI TO THE RESTRICTED MODE\n'" + - Utils::String::toUpper(selectedMode) + "'\n"; + Utils::String::toUpper(selectedMode) + "'\n"; if (selectedMode == "kiosk") { msg += "THIS WILL HIDE MOST MENU OPTIONS TO PREVENT\n"; msg += "CHANGES TO THE SYSTEM\n"; @@ -250,34 +250,36 @@ void GuiMenu::openUIOptions() msg += "TO UNLOCK AND RETURN TO THE FULL UI, ENTER THIS CODE: \n"; msg += UIModeController::getInstance()->getFormattedPassKeyStr() + "\n\n"; msg += "DO YOU WANT TO PROCEED?"; - mWindow->pushGui(new GuiMsgBox(mWindow, this->getHelpStyle(), msg, - "YES", [this, selectedMode] { - LOG(LogDebug) << "GuiMenu::openUISettings(): Setting UI mode to '" - << selectedMode << "'."; - Settings::getInstance()->setString("UIMode", selectedMode); - Settings::getInstance()->setBool("ForceFull", false); - Settings::getInstance()->setBool("ForceKiosk", false); - Settings::getInstance()->setBool("ForceKid", false); - Settings::getInstance()->saveFile(); - UIModeController::getInstance()->setCurrentUIMode(selectedMode); - for (auto it = SystemData::sSystemVector.cbegin(); - it != SystemData::sSystemVector.cend(); it++) { - if ((*it)->getThemeFolder() == "custom-collections") { - for (FileData* customSystem : - (*it)->getRootFolder()->getChildrenListToDisplay()) - customSystem->getSystem()->getIndex()->resetFilters(); + mWindow->pushGui(new GuiMsgBox( + mWindow, this->getHelpStyle(), msg, "YES", + [this, selectedMode] { + LOG(LogDebug) << "GuiMenu::openUISettings(): Setting UI mode to '" + << selectedMode << "'."; + Settings::getInstance()->setString("UIMode", selectedMode); + Settings::getInstance()->setBool("ForceFull", false); + Settings::getInstance()->setBool("ForceKiosk", false); + Settings::getInstance()->setBool("ForceKid", false); + Settings::getInstance()->saveFile(); + UIModeController::getInstance()->setCurrentUIMode(selectedMode); + for (auto it = SystemData::sSystemVector.cbegin(); + it != SystemData::sSystemVector.cend(); it++) { + if ((*it)->getThemeFolder() == "custom-collections") { + for (FileData* customSystem : + (*it)->getRootFolder()->getChildrenListToDisplay()) + customSystem->getSystem()->getIndex()->resetFilters(); + } + (*it)->sortSystem(); + (*it)->getIndex()->resetFilters(); } - (*it)->sortSystem(); - (*it)->getIndex()->resetFilters(); - } - ViewController::get()->reloadAll(); - ViewController::get()->goToSystem(SystemData::sSystemVector.front(), false); - mWindow->invalidateCachedBackground(); - }, "NO", nullptr)); + ViewController::get()->reloadAll(); + ViewController::get()->goToSystem(SystemData::sSystemVector.front(), false); + mWindow->invalidateCachedBackground(); + }, + "NO", nullptr)); } else { - LOG(LogDebug) << "GuiMenu::openUISettings(): Setting UI mode to '" << - selectedMode << "'."; + LOG(LogDebug) << "GuiMenu::openUISettings(): Setting UI mode to '" << selectedMode + << "'."; Settings::getInstance()->setString("UIMode", ui_mode->getSelected()); Settings::getInstance()->setBool("ForceFull", false); Settings::getInstance()->setBool("ForceKiosk", false); @@ -296,13 +298,13 @@ void GuiMenu::openUIOptions() // Default gamelist sort order. typedef OptionListComponent SortList; std::string sortOrder; - auto default_sort_order = std::make_shared - (mWindow, getHelpStyle(), "DEFAULT SORT ORDER", false); + auto default_sort_order = + std::make_shared(mWindow, getHelpStyle(), "DEFAULT SORT ORDER", false); // Exclude the System sort options. unsigned int numSortTypes = static_cast(FileSorts::SortTypes.size() - 2); for (unsigned int i = 0; i < numSortTypes; i++) { if (FileSorts::SortTypes[i].description == - Settings::getInstance()->getString("DefaultSortOrder")) { + Settings::getInstance()->getString("DefaultSortOrder")) { sortOrder = FileSorts::SortTypes[i].description; break; } @@ -331,8 +333,8 @@ void GuiMenu::openUIOptions() }); // Open menu effect. - auto menu_opening_effect = std::make_shared> - (mWindow, getHelpStyle(), "MENU OPENING EFFECT", false); + auto menu_opening_effect = std::make_shared>( + mWindow, getHelpStyle(), "MENU OPENING EFFECT", false); std::string selectedMenuEffect = Settings::getInstance()->getString("MenuOpeningEffect"); menu_opening_effect->add("SCALE-UP", "scale-up", selectedMenuEffect == "scale-up"); menu_opening_effect->add("NONE", "none", selectedMenuEffect == "none"); @@ -343,16 +345,16 @@ void GuiMenu::openUIOptions() s->addWithLabel("MENU OPENING EFFECT", menu_opening_effect); s->addSaveFunc([menu_opening_effect, s] { if (menu_opening_effect->getSelected() != - Settings::getInstance()->getString("MenuOpeningEffect")) { + Settings::getInstance()->getString("MenuOpeningEffect")) { Settings::getInstance()->setString("MenuOpeningEffect", - menu_opening_effect->getSelected()); + menu_opening_effect->getSelected()); s->setNeedsSaving(); } }); // Launch screen duration. - auto launch_screen_duration = std::make_shared> - (mWindow, getHelpStyle(), "LAUNCH SCREEN DURATION", false); + auto launch_screen_duration = std::make_shared>( + mWindow, getHelpStyle(), "LAUNCH SCREEN DURATION", false); std::string selectedDuration = Settings::getInstance()->getString("LaunchScreenDuration"); launch_screen_duration->add("NORMAL", "normal", selectedDuration == "normal"); launch_screen_duration->add("BRIEF", "brief", selectedDuration == "brief"); @@ -365,28 +367,28 @@ void GuiMenu::openUIOptions() s->addWithLabel("LAUNCH SCREEN DURATION", launch_screen_duration); s->addSaveFunc([launch_screen_duration, s] { if (launch_screen_duration->getSelected() != - Settings::getInstance()->getString("LaunchScreenDuration")) { + Settings::getInstance()->getString("LaunchScreenDuration")) { Settings::getInstance()->setString("LaunchScreenDuration", - launch_screen_duration->getSelected()); + launch_screen_duration->getSelected()); s->setNeedsSaving(); } }); - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) // Blur background when the menu is open. auto menu_blur_background = std::make_shared(mWindow); menu_blur_background->setState(Settings::getInstance()->getBool("MenuBlurBackground")); s->addWithLabel("BLUR BACKGROUND WHEN MENU IS OPEN", menu_blur_background); s->addSaveFunc([menu_blur_background, s] { if (menu_blur_background->getState() != - Settings::getInstance()->getBool("MenuBlurBackground")) { + Settings::getInstance()->getBool("MenuBlurBackground")) { Settings::getInstance()->setBool("MenuBlurBackground", - menu_blur_background->getState()); + menu_blur_background->getState()); s->setNeedsSaving(); s->setInvalidateCachedBackground(); } }); - #endif +#endif // Display pillarboxes (and letterboxes) for videos in the gamelists. auto gamelist_video_pillarbox = std::make_shared(mWindow); @@ -394,35 +396,34 @@ void GuiMenu::openUIOptions() s->addWithLabel("DISPLAY PILLARBOXES FOR GAMELIST VIDEOS", gamelist_video_pillarbox); s->addSaveFunc([gamelist_video_pillarbox, s] { if (gamelist_video_pillarbox->getState() != - Settings::getInstance()->getBool("GamelistVideoPillarbox")) { + Settings::getInstance()->getBool("GamelistVideoPillarbox")) { Settings::getInstance()->setBool("GamelistVideoPillarbox", - gamelist_video_pillarbox->getState()); + gamelist_video_pillarbox->getState()); s->setNeedsSaving(); } }); - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) // Render scanlines for videos in the gamelists. auto gamelist_video_scanlines = std::make_shared(mWindow); gamelist_video_scanlines->setState(Settings::getInstance()->getBool("GamelistVideoScanlines")); s->addWithLabel("RENDER SCANLINES FOR GAMELIST VIDEOS", gamelist_video_scanlines); s->addSaveFunc([gamelist_video_scanlines, s] { if (gamelist_video_scanlines->getState() != - Settings::getInstance()->getBool("GamelistVideoScanlines")) { + Settings::getInstance()->getBool("GamelistVideoScanlines")) { Settings::getInstance()->setBool("GamelistVideoScanlines", - gamelist_video_scanlines->getState()); + gamelist_video_scanlines->getState()); s->setNeedsSaving(); } }); - #endif +#endif // Sort folders on top of the gamelists. auto folders_on_top = std::make_shared(mWindow); folders_on_top->setState(Settings::getInstance()->getBool("FoldersOnTop")); s->addWithLabel("SORT FOLDERS ON TOP OF GAMELISTS", folders_on_top); s->addSaveFunc([folders_on_top, s] { - if (folders_on_top->getState() != - Settings::getInstance()->getBool("FoldersOnTop")) { + if (folders_on_top->getState() != Settings::getInstance()->getBool("FoldersOnTop")) { Settings::getInstance()->setBool("FoldersOnTop", folders_on_top->getState()); s->setNeedsSaving(); s->setNeedsSorting(); @@ -434,9 +435,8 @@ void GuiMenu::openUIOptions() auto favorites_first = std::make_shared(mWindow); favorites_first->setState(Settings::getInstance()->getBool("FavoritesFirst")); s->addWithLabel("SORT FAVORITE GAMES ABOVE NON-FAVORITES", favorites_first); - s->addSaveFunc([favorites_first,s ] { - if (favorites_first->getState() != - Settings::getInstance()->getBool("FavoritesFirst")) { + s->addSaveFunc([favorites_first, s] { + if (favorites_first->getState() != Settings::getInstance()->getBool("FavoritesFirst")) { Settings::getInstance()->setBool("FavoritesFirst", favorites_first->getState()); s->setNeedsSaving(); s->setNeedsSorting(); @@ -464,9 +464,8 @@ void GuiMenu::openUIOptions() s->addWithLabel("USE PLAIN ASCII FOR SPECIAL GAMELIST CHARACTERS", special_chars_ascii); s->addSaveFunc([special_chars_ascii, s] { if (special_chars_ascii->getState() != - Settings::getInstance()->getBool("SpecialCharsASCII")) { - Settings::getInstance()->setBool("SpecialCharsASCII", - special_chars_ascii->getState()); + Settings::getInstance()->getBool("SpecialCharsASCII")) { + Settings::getInstance()->setBool("SpecialCharsASCII", special_chars_ascii->getState()); s->setNeedsSaving(); s->setNeedsReloading(); s->setInvalidateCachedBackground(); @@ -479,7 +478,7 @@ void GuiMenu::openUIOptions() s->addWithLabel("ENABLE QUICK LIST SCROLLING OVERLAY", list_scroll_overlay); s->addSaveFunc([list_scroll_overlay, s] { if (list_scroll_overlay->getState() != - Settings::getInstance()->getBool("ListScrollOverlay")) { + Settings::getInstance()->getBool("ListScrollOverlay")) { Settings::getInstance()->setBool("ListScrollOverlay", list_scroll_overlay->getState()); s->setNeedsSaving(); } @@ -491,9 +490,9 @@ void GuiMenu::openUIOptions() s->addWithLabel("ENABLE TOGGLE FAVORITES BUTTON", favorites_add_button); s->addSaveFunc([favorites_add_button, s] { if (Settings::getInstance()->getBool("FavoritesAddButton") != - favorites_add_button->getState()) { + favorites_add_button->getState()) { Settings::getInstance()->setBool("FavoritesAddButton", - favorites_add_button->getState()); + favorites_add_button->getState()); s->setNeedsSaving(); } }); @@ -503,10 +502,8 @@ void GuiMenu::openUIOptions() random_add_button->setState(Settings::getInstance()->getBool("RandomAddButton")); s->addWithLabel("ENABLE RANDOM SYSTEM OR GAME BUTTON", random_add_button); s->addSaveFunc([random_add_button, s] { - if (Settings::getInstance()->getBool("RandomAddButton") != - random_add_button->getState()) { - Settings::getInstance()->setBool("RandomAddButton", - random_add_button->getState()); + if (Settings::getInstance()->getBool("RandomAddButton") != random_add_button->getState()) { + Settings::getInstance()->setBool("RandomAddButton", random_add_button->getState()); s->setNeedsSaving(); } }); @@ -516,8 +513,7 @@ void GuiMenu::openUIOptions() gamelist_filters->setState(Settings::getInstance()->getBool("GamelistFilters")); s->addWithLabel("ENABLE GAMELIST FILTERS", gamelist_filters); s->addSaveFunc([gamelist_filters, s] { - if (Settings::getInstance()->getBool("GamelistFilters") != - gamelist_filters->getState()) { + if (Settings::getInstance()->getBool("GamelistFilters") != gamelist_filters->getState()) { Settings::getInstance()->setBool("GamelistFilters", gamelist_filters->getState()); s->setNeedsSaving(); s->setNeedsReloading(); @@ -530,7 +526,7 @@ void GuiMenu::openUIOptions() s->addWithLabel("ENABLE QUICK SYSTEM SELECT", quick_system_select); s->addSaveFunc([quick_system_select, s] { if (Settings::getInstance()->getBool("QuickSystemSelect") != - quick_system_select->getState()) { + quick_system_select->getState()) { Settings::getInstance()->setBool("QuickSystemSelect", quick_system_select->getState()); s->setNeedsSaving(); } @@ -553,9 +549,9 @@ void GuiMenu::openUIOptions() s->addWithLabel("PLAY VIDEOS IMMEDIATELY (OVERRIDE THEME)", play_videos_immediately); s->addSaveFunc([play_videos_immediately, s] { if (Settings::getInstance()->getBool("PlayVideosImmediately") != - play_videos_immediately->getState()) { + play_videos_immediately->getState()) { Settings::getInstance()->setBool("PlayVideosImmediately", - play_videos_immediately->getState()); + play_videos_immediately->getState()); s->setNeedsSaving(); } }); @@ -563,8 +559,10 @@ void GuiMenu::openUIOptions() // Media viewer. ComponentListRow media_viewer_row; media_viewer_row.elements.clear(); - media_viewer_row.addElement(std::make_shared - (mWindow, "MEDIA VIEWER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + media_viewer_row.addElement(std::make_shared(mWindow, "MEDIA VIEWER SETTINGS", + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); media_viewer_row.addElement(makeArrow(mWindow), false); media_viewer_row.makeAcceptInputHandler(std::bind(&GuiMenu::openMediaViewerOptions, this)); s->addRow(media_viewer_row); @@ -572,8 +570,10 @@ void GuiMenu::openUIOptions() // Screensaver. ComponentListRow screensaver_row; screensaver_row.elements.clear(); - screensaver_row.addElement(std::make_shared - (mWindow, "SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + screensaver_row.addElement(std::make_shared(mWindow, "SCREENSAVER SETTINGS", + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); screensaver_row.addElement(makeArrow(mWindow), false); screensaver_row.makeAcceptInputHandler(std::bind(&GuiMenu::openScreensaverOptions, this)); s->addRow(screensaver_row); @@ -585,73 +585,71 @@ void GuiMenu::openSoundOptions() { auto s = new GuiSettings(mWindow, "SOUND SETTINGS"); - // TEMPORARY - Hide the volume slider on macOS and BSD Unix until the volume control logic - // has been implemented for these operating systems. - #if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +// TEMPORARY - Hide the volume slider on macOS and BSD Unix until the volume control logic +// has been implemented for these operating systems. +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) // System volume. auto system_volume = std::make_shared(mWindow, 0.f, 100.f, 1.f, "%"); system_volume->setValue(static_cast(VolumeControl::getInstance()->getVolume())); s->addWithLabel("SYSTEM VOLUME", system_volume); s->addSaveFunc([system_volume] { - VolumeControl::getInstance()-> - setVolume(static_cast(std::round(system_volume->getValue()))); + VolumeControl::getInstance()->setVolume( + static_cast(std::round(system_volume->getValue()))); // Explicitly delete the VolumeControl instance so that it will reinitialize the // next time the menu is entered. This is the easiest way to detect new default // audio devices or changes to the audio volume done by the operating system. VolumeControl::getInstance()->deleteInstance(); }); - #endif +#endif // Volume for navigation sounds. - auto sound_volume_navigation = - std::make_shared(mWindow, 0.f, 100.f, 1.f, "%"); - sound_volume_navigation->setValue(static_cast(Settings::getInstance()-> - getInt("SoundVolumeNavigation"))); + auto sound_volume_navigation = std::make_shared(mWindow, 0.f, 100.f, 1.f, "%"); + sound_volume_navigation->setValue( + static_cast(Settings::getInstance()->getInt("SoundVolumeNavigation"))); s->addWithLabel("NAVIGATION SOUNDS VOLUME", sound_volume_navigation); s->addSaveFunc([sound_volume_navigation, s] { if (sound_volume_navigation->getValue() != - static_cast(Settings::getInstance()->getInt("SoundVolumeNavigation"))) { + static_cast(Settings::getInstance()->getInt("SoundVolumeNavigation"))) { Settings::getInstance()->setInt("SoundVolumeNavigation", - static_cast(sound_volume_navigation->getValue())); + static_cast(sound_volume_navigation->getValue())); s->setNeedsSaving(); } }); // Volume for videos. - auto sound_volume_videos = - std::make_shared(mWindow, 0.f, 100.f, 1.f, "%"); - sound_volume_videos->setValue(static_cast(Settings::getInstance()-> - getInt("SoundVolumeVideos"))); + auto sound_volume_videos = std::make_shared(mWindow, 0.f, 100.f, 1.f, "%"); + sound_volume_videos->setValue( + static_cast(Settings::getInstance()->getInt("SoundVolumeVideos"))); s->addWithLabel("VIDEO PLAYER VOLUME", sound_volume_videos); s->addSaveFunc([sound_volume_videos, s] { if (sound_volume_videos->getValue() != - static_cast(Settings::getInstance()->getInt("SoundVolumeVideos"))) { + static_cast(Settings::getInstance()->getInt("SoundVolumeVideos"))) { Settings::getInstance()->setInt("SoundVolumeVideos", - static_cast(sound_volume_videos->getValue())); + static_cast(sound_volume_videos->getValue())); s->setNeedsSaving(); } }); if (UIModeController::getInstance()->isUIModeFull()) { - // The ALSA Audio Card and Audio Device selection code is disabled at the moment. - // As PulseAudio controls the sound devices for the desktop environment, it doesn't - // make much sense to be able to select ALSA devices directly. Normally (always?) - // the selection doesn't make any difference at all. But maybe some PulseAudio - // settings could be added later on, if needed. - // The code is still active for Raspberry Pi though as I'm not sure if this is - // useful for that device. - // #if defined(__linux__) - #if defined(_RPI_) + // The ALSA Audio Card and Audio Device selection code is disabled at the moment. + // As PulseAudio controls the sound devices for the desktop environment, it doesn't + // make much sense to be able to select ALSA devices directly. Normally (always?) + // the selection doesn't make any difference at all. But maybe some PulseAudio + // settings could be added later on, if needed. + // The code is still active for Raspberry Pi though as I'm not sure if this is + // useful for that device. + // #if defined(__linux__) +#if defined(_RPI_) // Audio card. - auto audio_card = std::make_shared> - (mWindow, getHelpStyle(), "AUDIO CARD", false); + auto audio_card = std::make_shared>( + mWindow, getHelpStyle(), "AUDIO CARD", false); std::vector audio_cards; - #if defined(_RPI_) +#if defined(_RPI_) // RPi Specific Audio Cards. audio_cards.push_back("local"); audio_cards.push_back("hdmi"); audio_cards.push_back("both"); - #endif +#endif audio_cards.push_back("default"); audio_cards.push_back("sysdefault"); audio_cards.push_back("dmix"); @@ -660,7 +658,7 @@ void GuiMenu::openSoundOptions() audio_cards.push_back("null"); if (Settings::getInstance()->getString("AudioCard") != "") { if (std::find(audio_cards.begin(), audio_cards.end(), - Settings::getInstance()->getString("AudioCard")) == audio_cards.end()) { + Settings::getInstance()->getString("AudioCard")) == audio_cards.end()) { audio_cards.push_back(Settings::getInstance()->getString("AudioCard")); } } @@ -677,8 +675,8 @@ void GuiMenu::openSoundOptions() }); // Volume control device. - auto vol_dev = std::make_shared> - (mWindow, getHelpStyle(), "AUDIO DEVICE", false); + auto vol_dev = std::make_shared>(mWindow, getHelpStyle(), + "AUDIO DEVICE", false); std::vector transitions; transitions.push_back("PCM"); transitions.push_back("Speaker"); @@ -687,7 +685,7 @@ void GuiMenu::openSoundOptions() transitions.push_back("Analogue"); if (Settings::getInstance()->getString("AudioDevice") != "") { if (std::find(transitions.begin(), transitions.end(), - Settings::getInstance()->getString("AudioDevice")) == transitions.end()) { + Settings::getInstance()->getString("AudioDevice")) == transitions.end()) { transitions.push_back(Settings::getInstance()->getString("AudioDevice")); } } @@ -702,12 +700,12 @@ void GuiMenu::openSoundOptions() s->setNeedsSaving(); } }); - #endif +#endif - #if defined(_RPI_) +#if defined(_RPI_) // OMXPlayer audio device. - auto omx_audio_dev = std::make_shared> - (mWindow, getHelpStyle(), "OMX PLAYER AUDIO DEVICE", false); + auto omx_audio_dev = std::make_shared>( + mWindow, getHelpStyle(), "OMX PLAYER AUDIO DEVICE", false); std::vector omx_cards; // RPi Specific Audio Cards omx_cards.push_back("local"); @@ -717,7 +715,7 @@ void GuiMenu::openSoundOptions() omx_cards.push_back("alsa:hw:1,0"); if (Settings::getInstance()->getString("OMXAudioDev") != "") { if (std::find(omx_cards.begin(), omx_cards.end(), - Settings::getInstance()->getString("OMXAudioDev")) == omx_cards.end()) { + Settings::getInstance()->getString("OMXAudioDev")) == omx_cards.end()) { omx_cards.push_back(Settings::getInstance()->getString("OMXAudioDev")); } } @@ -725,13 +723,12 @@ void GuiMenu::openSoundOptions() omx_audio_dev->add(*it, *it, Settings::getInstance()->getString("OMXAudioDev") == *it); s->addWithLabel("OMX PLAYER AUDIO DEVICE", omx_audio_dev); s->addSaveFunc([omx_audio_dev, s] { - if (omx_audio_dev->getSelected() != - Settings::getInstance()->getString("OMXAudioDev")) { + if (omx_audio_dev->getSelected() != Settings::getInstance()->getString("OMXAudioDev")) { Settings::getInstance()->setString("OMXAudioDev", omx_audio_dev->getSelected()); s->setNeedsSaving(); } }); - #endif +#endif // Play audio for gamelist videos. auto gamelist_video_audio = std::make_shared(mWindow); @@ -739,51 +736,49 @@ void GuiMenu::openSoundOptions() s->addWithLabel("PLAY AUDIO FOR VIDEOS IN THE GAMELIST VIEW", gamelist_video_audio); s->addSaveFunc([gamelist_video_audio, s] { if (gamelist_video_audio->getState() != - Settings::getInstance()->getBool("GamelistVideoAudio")) { + Settings::getInstance()->getBool("GamelistVideoAudio")) { Settings::getInstance()->setBool("GamelistVideoAudio", - gamelist_video_audio->getState()); + gamelist_video_audio->getState()); s->setNeedsSaving(); } }); // Play audio for media viewer videos. auto media_viewer_video_audio = std::make_shared(mWindow); - media_viewer_video_audio->setState(Settings::getInstance()-> - getBool("MediaViewerVideoAudio")); + media_viewer_video_audio->setState( + Settings::getInstance()->getBool("MediaViewerVideoAudio")); s->addWithLabel("PLAY AUDIO FOR MEDIA VIEWER VIDEOS", media_viewer_video_audio); s->addSaveFunc([media_viewer_video_audio, s] { if (media_viewer_video_audio->getState() != - Settings::getInstance()->getBool("MediaViewerVideoAudio")) { + Settings::getInstance()->getBool("MediaViewerVideoAudio")) { Settings::getInstance()->setBool("MediaViewerVideoAudio", - media_viewer_video_audio->getState()); + media_viewer_video_audio->getState()); s->setNeedsSaving(); } }); // Play audio for screensaver videos. auto screensaver_video_audio = std::make_shared(mWindow); - screensaver_video_audio->setState(Settings::getInstance()-> - getBool("ScreensaverVideoAudio")); + screensaver_video_audio->setState( + Settings::getInstance()->getBool("ScreensaverVideoAudio")); s->addWithLabel("PLAY AUDIO FOR SCREENSAVER VIDEOS", screensaver_video_audio); s->addSaveFunc([screensaver_video_audio, s] { if (screensaver_video_audio->getState() != - Settings::getInstance()->getBool("ScreensaverVideoAudio")) { + Settings::getInstance()->getBool("ScreensaverVideoAudio")) { Settings::getInstance()->setBool("ScreensaverVideoAudio", - screensaver_video_audio->getState()); + screensaver_video_audio->getState()); s->setNeedsSaving(); } }); // Navigation sounds. auto navigation_sounds = std::make_shared(mWindow); - navigation_sounds->setState(Settings::getInstance()-> - getBool("NavigationSounds")); + navigation_sounds->setState(Settings::getInstance()->getBool("NavigationSounds")); s->addWithLabel("ENABLE NAVIGATION SOUNDS", navigation_sounds); s->addSaveFunc([navigation_sounds, s] { if (navigation_sounds->getState() != - Settings::getInstance()->getBool("NavigationSounds")) { - Settings::getInstance()->setBool("NavigationSounds", - navigation_sounds->getState()); + Settings::getInstance()->getBool("NavigationSounds")) { + Settings::getInstance()->setBool("NavigationSounds", navigation_sounds->getState()); s->setNeedsSaving(); } }); @@ -797,8 +792,8 @@ void GuiMenu::openInputDeviceOptions() auto s = new GuiSettings(mWindow, "INPUT DEVICE SETTINGS"); // Controller type. - auto input_controller_type = std::make_shared> - (mWindow, getHelpStyle(), "CONTROLLER TYPE", false); + auto input_controller_type = std::make_shared>( + mWindow, getHelpStyle(), "CONTROLLER TYPE", false); std::string selectedPlayer = Settings::getInstance()->getString("InputControllerType"); input_controller_type->add("XBOX", "xbox", selectedPlayer == "xbox"); input_controller_type->add("XBOX 360", "xbox360", selectedPlayer == "xbox360"); @@ -812,9 +807,9 @@ void GuiMenu::openInputDeviceOptions() s->addWithLabel("CONTROLLER TYPE", input_controller_type); s->addSaveFunc([input_controller_type, s] { if (input_controller_type->getSelected() != - Settings::getInstance()->getString("InputControllerType")) { + Settings::getInstance()->getString("InputControllerType")) { Settings::getInstance()->setString("InputControllerType", - input_controller_type->getSelected()); + input_controller_type->getSelected()); s->setNeedsReloadHelpPrompts(); s->setNeedsSaving(); } @@ -822,14 +817,14 @@ void GuiMenu::openInputDeviceOptions() // Whether to only accept input from the first controller. auto input_only_first_controller = std::make_shared(mWindow); - input_only_first_controller->setState(Settings::getInstance()-> - getBool("InputOnlyFirstController")); + input_only_first_controller->setState( + Settings::getInstance()->getBool("InputOnlyFirstController")); s->addWithLabel("ONLY ACCEPT INPUT FROM FIRST CONTROLLER", input_only_first_controller); s->addSaveFunc([input_only_first_controller, s] { if (Settings::getInstance()->getBool("InputOnlyFirstController") != - input_only_first_controller->getState()) { + input_only_first_controller->getState()) { Settings::getInstance()->setBool("InputOnlyFirstController", - input_only_first_controller->getState()); + input_only_first_controller->getState()); s->setNeedsSaving(); } }); @@ -837,9 +832,10 @@ void GuiMenu::openInputDeviceOptions() // Configure keyboard and controllers. ComponentListRow configure_input_row; configure_input_row.elements.clear(); - configure_input_row.addElement(std::make_shared - (mWindow, "CONFIGURE KEYBOARD AND CONTROLLERS", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + configure_input_row.addElement( + std::make_shared(mWindow, "CONFIGURE KEYBOARD AND CONTROLLERS", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); configure_input_row.addElement(makeArrow(mWindow), false); configure_input_row.makeAcceptInputHandler(std::bind(&GuiMenu::openConfigInput, this, s)); s->addRow(configure_input_row); @@ -856,18 +852,17 @@ void GuiMenu::openConfigInput(GuiSettings* settings) // the input device settings menu later on. settings->setNeedsSaving(false); - std::string message = - "THE KEYBOARD AND CONTROLLERS ARE AUTOMATICALLY\n" - "CONFIGURED, BUT USING THIS CONFIGURATION TOOL\n" - "YOU CAN OVERRIDE THE DEFAULT BUTTON MAPPINGS\n" - "(THIS WILL NOT AFFECT THE HELP PROMPTS)\n" - "CONTINUE?"; + std::string message = "THE KEYBOARD AND CONTROLLERS ARE AUTOMATICALLY\n" + "CONFIGURED, BUT USING THIS CONFIGURATION TOOL\n" + "YOU CAN OVERRIDE THE DEFAULT BUTTON MAPPINGS\n" + "(THIS WILL NOT AFFECT THE HELP PROMPTS)\n" + "CONTINUE?"; Window* window = mWindow; - window->pushGui(new GuiMsgBox(window, getHelpStyle(), - message, "YES", [window] { - window->pushGui(new GuiDetectDevice(window, false, false, nullptr)); - }, "NO", nullptr)); + window->pushGui(new GuiMsgBox( + window, getHelpStyle(), message, "YES", + [window] { window->pushGui(new GuiDetectDevice(window, false, false, nullptr)); }, "NO", + nullptr)); } void GuiMenu::openOtherOptions() @@ -881,14 +876,14 @@ void GuiMenu::openOtherOptions() s->addSaveFunc([max_vram, s] { if (max_vram->getValue() != Settings::getInstance()->getInt("MaxVRAM")) { Settings::getInstance()->setInt("MaxVRAM", - static_cast(std::round(max_vram->getValue()))); + static_cast(std::round(max_vram->getValue()))); s->setNeedsSaving(); } }); // Display/monitor. - auto display_index = std::make_shared> - (mWindow, getHelpStyle(), "DISPLAY/MONITOR INDEX", false); + auto display_index = std::make_shared>( + mWindow, getHelpStyle(), "DISPLAY/MONITOR INDEX", false); std::vector displayIndex; displayIndex.push_back("1"); displayIndex.push_back("2"); @@ -896,21 +891,21 @@ void GuiMenu::openOtherOptions() displayIndex.push_back("4"); for (auto it = displayIndex.cbegin(); it != displayIndex.cend(); it++) display_index->add(*it, *it, - Settings::getInstance()->getInt("DisplayIndex") == atoi((*it).c_str())); + Settings::getInstance()->getInt("DisplayIndex") == atoi((*it).c_str())); s->addWithLabel("DISPLAY/MONITOR INDEX (REQUIRES RESTART)", display_index); s->addSaveFunc([display_index, s] { if (atoi(display_index->getSelected().c_str()) != - Settings::getInstance()->getInt("DisplayIndex")) { + Settings::getInstance()->getInt("DisplayIndex")) { Settings::getInstance()->setInt("DisplayIndex", - atoi(display_index->getSelected().c_str())); + atoi(display_index->getSelected().c_str())); s->setNeedsSaving(); } }); - #if defined(__unix__) +#if defined(__unix__) // Fullscreen mode. - auto fullscreen_mode = std::make_shared> - (mWindow, getHelpStyle(), "FULLSCREEN MODE", false); + auto fullscreen_mode = std::make_shared>( + mWindow, getHelpStyle(), "FULLSCREEN MODE", false); std::vector screenmode; screenmode.push_back("normal"); screenmode.push_back("borderless"); @@ -919,17 +914,17 @@ void GuiMenu::openOtherOptions() s->addWithLabel("FULLSCREEN MODE (REQUIRES RESTART)", fullscreen_mode); s->addSaveFunc([fullscreen_mode, s] { if (fullscreen_mode->getSelected() != - Settings::getInstance()->getString("FullscreenMode")) { + Settings::getInstance()->getString("FullscreenMode")) { Settings::getInstance()->setString("FullscreenMode", fullscreen_mode->getSelected()); s->setNeedsSaving(); } }); - #endif +#endif - #if defined(BUILD_VLC_PLAYER) +#if defined(BUILD_VLC_PLAYER) // Video player. - auto video_player = std::make_shared> - (mWindow, getHelpStyle(), "FULLSCREEN MODE", false); + auto video_player = std::make_shared>( + mWindow, getHelpStyle(), "FULLSCREEN MODE", false); std::string selectedPlayer = Settings::getInstance()->getString("VideoPlayer"); video_player->add("FFmpeg", "ffmpeg", selectedPlayer == "ffmpeg"); video_player->add("VLC", "vlc", selectedPlayer == "vlc"); @@ -945,30 +940,30 @@ void GuiMenu::openOtherOptions() s->setNeedsReloading(); } }); - #endif +#endif // When to save game metadata. - auto save_gamelist_mode = std::make_shared> - (mWindow, getHelpStyle(), "WHEN TO SAVE METADATA", false); + auto save_gamelist_mode = std::make_shared>( + mWindow, getHelpStyle(), "WHEN TO SAVE METADATA", false); std::vector saveModes; saveModes.push_back("on exit"); saveModes.push_back("always"); saveModes.push_back("never"); for (auto it = saveModes.cbegin(); it != saveModes.cend(); it++) { - save_gamelist_mode->add(*it, *it, Settings::getInstance()-> - getString("SaveGamelistsMode") == *it); + save_gamelist_mode->add(*it, *it, + Settings::getInstance()->getString("SaveGamelistsMode") == *it); } s->addWithLabel("WHEN TO SAVE GAME METADATA", save_gamelist_mode); s->addSaveFunc([save_gamelist_mode, s] { if (save_gamelist_mode->getSelected() != - Settings::getInstance()->getString("SaveGamelistsMode")) { + Settings::getInstance()->getString("SaveGamelistsMode")) { Settings::getInstance()->setString("SaveGamelistsMode", - save_gamelist_mode->getSelected()); - // Always save the gamelist.xml files if switching to 'always' as there may + save_gamelist_mode->getSelected()); + // Always save the gamelist.xml files if switching to "always" as there may // be changes that will otherwise be lost. if (Settings::getInstance()->getString("SaveGamelistsMode") == "always") { for (auto it = SystemData::sSystemVector.cbegin(); - it != SystemData::sSystemVector.cend(); it++) + it != SystemData::sSystemVector.cend(); it++) (*it)->writeMetaData(); } s->setNeedsSaving(); @@ -978,7 +973,7 @@ void GuiMenu::openOtherOptions() // Game media directory. ComponentListRow rowMediaDir; auto media_directory = std::make_shared(mWindow, "GAME MEDIA DIRECTORY", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); auto bracketMediaDirectory = std::make_shared(mWindow); bracketMediaDirectory->setImage(":/graphics/arrow.svg"); bracketMediaDirectory->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight())); @@ -996,56 +991,55 @@ void GuiMenu::openOtherOptions() mWindow->invalidateCachedBackground(); }; rowMediaDir.makeAcceptInputHandler([this, titleMediaDir, mediaDirectoryStaticText, - defaultDirectoryText, initValueMediaDir, updateValMediaDir, multiLineMediaDir] { - mWindow->pushGui(new GuiComplexTextEditPopup(mWindow, getHelpStyle(), - titleMediaDir, mediaDirectoryStaticText, defaultDirectoryText, - Settings::getInstance()->getString("MediaDirectory"), - updateValMediaDir, multiLineMediaDir, "SAVE", "SAVE CHANGES?")); + defaultDirectoryText, initValueMediaDir, updateValMediaDir, + multiLineMediaDir] { + mWindow->pushGui(new GuiComplexTextEditPopup( + mWindow, getHelpStyle(), titleMediaDir, mediaDirectoryStaticText, defaultDirectoryText, + Settings::getInstance()->getString("MediaDirectory"), updateValMediaDir, + multiLineMediaDir, "SAVE", "SAVE CHANGES?")); }); s->addRow(rowMediaDir); - #if defined(_RPI_) +#if defined(_RPI_) // Video playing using OMXPlayer. auto video_omx_player = std::make_shared(mWindow); video_omx_player->setState(Settings::getInstance()->getBool("VideoOmxPlayer")); s->addWithLabel("USE OMX PLAYER (HW ACCELERATED)", video_omx_player); s->addSaveFunc([video_omx_player, s] { - if (video_omx_player->getState() != - Settings::getInstance()->getBool("VideoOmxPlayer")) { + if (video_omx_player->getState() != Settings::getInstance()->getBool("VideoOmxPlayer")) { Settings::getInstance()->setBool("VideoOmxPlayer", video_omx_player->getState()); s->setNeedsSaving(); // Need to reload all views to re-create the right video components. s->setNeedsReloading(); } }); - #endif +#endif - #if defined(_WIN64) +#if defined(_WIN64) // Hide taskbar during the ES program session. auto hide_taskbar = std::make_shared(mWindow); hide_taskbar->setState(Settings::getInstance()->getBool("HideTaskbar")); s->addWithLabel("HIDE TASKBAR (REQUIRES RESTART)", hide_taskbar); s->addSaveFunc([hide_taskbar, s] { - if (hide_taskbar->getState() != - Settings::getInstance()->getBool("HideTaskbar")) { - Settings::getInstance()-> setBool("HideTaskbar", hide_taskbar->getState()); + if (hide_taskbar->getState() != Settings::getInstance()->getBool("HideTaskbar")) { + Settings::getInstance()->setBool("HideTaskbar", hide_taskbar->getState()); s->setNeedsSaving(); } }); - #endif +#endif // Run ES in the background when a game has been launched. auto run_in_background = std::make_shared(mWindow); run_in_background->setState(Settings::getInstance()->getBool("RunInBackground")); s->addWithLabel("RUN IN BACKGROUND (WHILE GAME IS LAUNCHED)", run_in_background); - s->addSaveFunc([run_in_background,s] { + s->addSaveFunc([run_in_background, s] { if (run_in_background->getState() != Settings::getInstance()->getBool("RunInBackground")) { Settings::getInstance()->setBool("RunInBackground", run_in_background->getState()); s->setNeedsSaving(); } }); - #if defined(_WIN64) +#if defined(_WIN64) // Workaround for launching games on AMD and Intel graphics drivers. auto launch_workaround = std::make_shared(mWindow); launch_workaround->setState(Settings::getInstance()->getBool("LaunchWorkaround")); @@ -1061,24 +1055,25 @@ void GuiMenu::openOtherOptions() if (Settings::getInstance()->getBool("RunInBackground")) { launch_workaround->setEnabled(false); launch_workaround->setOpacity(DISABLED_OPACITY); - launch_workaround->getParent()->getChild(launch_workaround-> - getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); + launch_workaround->getParent() + ->getChild(launch_workaround->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); } - #endif +#endif // Whether to upscale the video frame rate to 60 FPS. auto video_upscale_frame_rate = std::make_shared(mWindow); video_upscale_frame_rate->setState(Settings::getInstance()->getBool("VideoUpscaleFrameRate")); - #if defined(BUILD_VLC_PLAYER) +#if defined(BUILD_VLC_PLAYER) s->addWithLabel("UPSCALE VIDEO FRAME RATE TO 60 FPS (FFMPEG)", video_upscale_frame_rate); - #else +#else s->addWithLabel("UPSCALE VIDEO FRAME RATE TO 60 FPS", video_upscale_frame_rate); - #endif +#endif s->addSaveFunc([video_upscale_frame_rate, s] { if (video_upscale_frame_rate->getState() != - Settings::getInstance()->getBool("VideoUpscaleFrameRate")) { - Settings::getInstance()-> - setBool("VideoUpscaleFrameRate", video_upscale_frame_rate->getState()); + Settings::getInstance()->getBool("VideoUpscaleFrameRate")) { + Settings::getInstance()->setBool("VideoUpscaleFrameRate", + video_upscale_frame_rate->getState()); s->setNeedsSaving(); } }); @@ -1090,9 +1085,9 @@ void GuiMenu::openOtherOptions() s->addWithLabel("PER GAME LAUNCH COMMAND OVERRIDE", launchcommand_override); s->addSaveFunc([launchcommand_override, s] { if (launchcommand_override->getState() != - Settings::getInstance()->getBool("LaunchCommandOverride")) { - Settings::getInstance()-> - setBool("LaunchCommandOverride", launchcommand_override->getState()); + Settings::getInstance()->getBool("LaunchCommandOverride")) { + Settings::getInstance()->setBool("LaunchCommandOverride", + launchcommand_override->getState()); s->setNeedsSaving(); } }); @@ -1125,7 +1120,7 @@ void GuiMenu::openOtherOptions() s->addWithLabel("ENABLE CUSTOM EVENT SCRIPTS", custom_eventscripts); s->addSaveFunc([custom_eventscripts, s] { if (custom_eventscripts->getState() != - Settings::getInstance()->getBool("CustomEventScripts")) { + Settings::getInstance()->getBool("CustomEventScripts")) { Settings::getInstance()->setBool("CustomEventScripts", custom_eventscripts->getState()); s->setNeedsSaving(); } @@ -1137,26 +1132,25 @@ void GuiMenu::openOtherOptions() s->addWithLabel("ONLY SHOW ROMS FROM GAMELIST.XML FILES", parse_gamelist_only); s->addSaveFunc([parse_gamelist_only, s] { if (parse_gamelist_only->getState() != - Settings::getInstance()->getBool("ParseGamelistOnly")) { + Settings::getInstance()->getBool("ParseGamelistOnly")) { Settings::getInstance()->setBool("ParseGamelistOnly", parse_gamelist_only->getState()); s->setNeedsSaving(); } }); - #if defined(__unix__) +#if defined(__unix__) // Whether to disable desktop composition. auto disable_composition = std::make_shared(mWindow); disable_composition->setState(Settings::getInstance()->getBool("DisableComposition")); s->addWithLabel("DISABLE DESKTOP COMPOSITION (REQUIRES RESTART)", disable_composition); s->addSaveFunc([disable_composition, s] { if (disable_composition->getState() != - Settings::getInstance()->getBool("DisableComposition")) { - Settings::getInstance()->setBool("DisableComposition", - disable_composition->getState()); + Settings::getInstance()->getBool("DisableComposition")) { + Settings::getInstance()->setBool("DisableComposition", disable_composition->getState()); s->setNeedsSaving(); } }); - #endif +#endif // GPU statistics overlay. auto display_gpu_statistics = std::make_shared(mWindow); @@ -1164,9 +1158,9 @@ void GuiMenu::openOtherOptions() s->addWithLabel("DISPLAY GPU STATISTICS OVERLAY", display_gpu_statistics); s->addSaveFunc([display_gpu_statistics, s] { if (display_gpu_statistics->getState() != - Settings::getInstance()->getBool("DisplayGPUStatistics")) { + Settings::getInstance()->getBool("DisplayGPUStatistics")) { Settings::getInstance()->setBool("DisplayGPUStatistics", - display_gpu_statistics->getState()); + display_gpu_statistics->getState()); s->setNeedsSaving(); } }); @@ -1177,47 +1171,48 @@ void GuiMenu::openOtherOptions() s->addWithLabel("ENABLE MENU IN KID MODE", enable_menu_kid_mode); s->addSaveFunc([enable_menu_kid_mode, s] { if (Settings::getInstance()->getBool("EnableMenuKidMode") != - enable_menu_kid_mode->getState()) { + enable_menu_kid_mode->getState()) { Settings::getInstance()->setBool("EnableMenuKidMode", enable_menu_kid_mode->getState()); s->setNeedsSaving(); } }); - // macOS requires root privileges to reboot and power off so it doesn't make much - // sense to enable this setting and menu entry for that operating system. - #if !defined(__APPLE__) +// macOS requires root privileges to reboot and power off so it doesn't make much +// sense to enable this setting and menu entry for that operating system. +#if !defined(__APPLE__) // Whether to show the quit menu with the options to reboot and shutdown the computer. auto show_quit_menu = std::make_shared(mWindow); show_quit_menu->setState(Settings::getInstance()->getBool("ShowQuitMenu")); s->addWithLabel("SHOW QUIT MENU (REBOOT AND POWER OFF ENTRIES)", show_quit_menu); s->addSaveFunc([this, show_quit_menu, s] { - if (show_quit_menu->getState() != - Settings::getInstance()->getBool("ShowQuitMenu")) { + if (show_quit_menu->getState() != Settings::getInstance()->getBool("ShowQuitMenu")) { Settings::getInstance()->setBool("ShowQuitMenu", show_quit_menu->getState()); s->setNeedsSaving(); GuiMenu::close(false); } }); - #endif +#endif - #if defined(_WIN64) +#if defined(_WIN64) // Switch callback. auto launchWorkaroundToggleFunc = [launch_workaround]() { if (launch_workaround->getEnabled()) { launch_workaround->setEnabled(false); launch_workaround->setOpacity(DISABLED_OPACITY); - launch_workaround->getParent()->getChild(launch_workaround-> - getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); + launch_workaround->getParent() + ->getChild(launch_workaround->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); } else { launch_workaround->setEnabled(true); launch_workaround->setOpacity(255); - launch_workaround->getParent()->getChild(launch_workaround-> - getChildIndex() - 1)->setOpacity(255); + launch_workaround->getParent() + ->getChild(launch_workaround->getChildIndex() - 1) + ->setOpacity(255); } }; run_in_background->setCallback(launchWorkaroundToggleFunc); - #endif +#endif mWindow->pushGui(s); } @@ -1225,19 +1220,20 @@ void GuiMenu::openOtherOptions() void GuiMenu::openUtilitiesMenu() { auto s = new GuiSettings(mWindow, "UTILITIES"); - mWindow->pushGui(s); } void GuiMenu::openQuitMenu() { if (!Settings::getInstance()->getBool("ShowQuitMenu")) { - mWindow->pushGui(new GuiMsgBox(mWindow, this->getHelpStyle(), - "REALLY QUIT?", "YES", [this] { - Scripting::fireEvent("quit"); - close(true); - quitES(); - }, "NO", nullptr)); + mWindow->pushGui(new GuiMsgBox( + mWindow, this->getHelpStyle(), "REALLY QUIT?", "YES", + [this] { + Scripting::fireEvent("quit"); + close(true); + quitES(); + }, + "NO", nullptr)); } else { auto s = new GuiSettings(mWindow, "QUIT"); @@ -1253,47 +1249,56 @@ void GuiMenu::openQuitMenu() ComponentListRow row; row.makeAcceptInputHandler([window, this] { - window->pushGui(new GuiMsgBox(window, this->getHelpStyle(), - "REALLY QUIT?", "YES", [this] { + window->pushGui(new GuiMsgBox( + window, this->getHelpStyle(), "REALLY QUIT?", "YES", + [this] { Scripting::fireEvent("quit"); close(true); quitES(); - }, "NO", nullptr)); + }, + "NO", nullptr)); }); row.addElement(std::make_shared(window, "QUIT EMULATIONSTATION", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.addElement(bracket, false); s->addRow(row); row.elements.clear(); row.makeAcceptInputHandler([window, this] { - window->pushGui(new GuiMsgBox(window, this->getHelpStyle(), - "REALLY REBOOT?", "YES", [] { + window->pushGui(new GuiMsgBox( + window, this->getHelpStyle(), "REALLY REBOOT?", "YES", + [] { Scripting::fireEvent("quit", "reboot"); Scripting::fireEvent("reboot"); if (quitES(QuitMode::REBOOT) != 0) { LOG(LogWarning) << "Reboot terminated with non-zero result!"; } - }, "NO", nullptr)); + }, + "NO", nullptr)); }); row.addElement(std::make_shared(window, "REBOOT SYSTEM", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.addElement(bracket, false); s->addRow(row); row.elements.clear(); row.makeAcceptInputHandler([window, this] { - window->pushGui(new GuiMsgBox(window, this->getHelpStyle(), - "REALLY POWER OFF?", "YES", [] { + window->pushGui(new GuiMsgBox( + window, this->getHelpStyle(), "REALLY POWER OFF?", "YES", + [] { Scripting::fireEvent("quit", "poweroff"); Scripting::fireEvent("poweroff"); if (quitES(QuitMode::POWEROFF) != 0) { LOG(LogWarning) << "Power off terminated with non-zero result!"; } - }, "NO", nullptr)); + }, + "NO", nullptr)); }); row.addElement(std::make_shared(window, "POWER OFF SYSTEM", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.addElement(bracket, false); s->addRow(row); @@ -1331,8 +1336,10 @@ void GuiMenu::onSizeChanged() mVersion.setPosition(0, mSize.y() - mVersion.getSize().y()); } -void GuiMenu::addEntry(const std::string& name, unsigned int color, - bool add_arrow, const std::function& func) +void GuiMenu::addEntry(const std::string& name, + unsigned int color, + bool add_arrow, + const std::function& func) { std::shared_ptr font = Font::get(FONT_SIZE_MEDIUM); diff --git a/es-app/src/guis/GuiMenu.h b/es-app/src/guis/GuiMenu.h index 47096498d..4b361d770 100644 --- a/es-app/src/guis/GuiMenu.h +++ b/es-app/src/guis/GuiMenu.h @@ -10,9 +10,9 @@ #ifndef ES_APP_GUIS_GUI_MENU_H #define ES_APP_GUIS_GUI_MENU_H +#include "GuiComponent.h" #include "components/MenuComponent.h" #include "guis/GuiSettings.h" -#include "GuiComponent.h" class GuiMenu : public GuiComponent { @@ -27,8 +27,10 @@ public: private: void close(bool closeAllWindows); - void addEntry(const std::string& name, unsigned int color, - bool add_arrow, const std::function& func); + void addEntry(const std::string& name, + unsigned int color, + bool add_arrow, + const std::function& func); void addVersionInfo(); void openScraperOptions(); diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index 57af195fa..42fa42030 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -11,6 +11,12 @@ #include "guis/GuiMetaDataEd.h" +#include "CollectionSystemsManager.h" +#include "FileData.h" +#include "FileFilterIndex.h" +#include "Gamelist.h" +#include "SystemData.h" +#include "Window.h" #include "components/ButtonComponent.h" #include "components/ComponentList.h" #include "components/DateTimeEditComponent.h" @@ -18,69 +24,63 @@ #include "components/RatingComponent.h" #include "components/SwitchComponent.h" #include "components/TextComponent.h" +#include "guis/GuiComplexTextEditPopup.h" #include "guis/GuiGameScraper.h" #include "guis/GuiMsgBox.h" #include "guis/GuiTextEditPopup.h" -#include "guis/GuiComplexTextEditPopup.h" #include "resources/Font.h" #include "utils/StringUtil.h" #include "views/ViewController.h" -#include "CollectionSystemsManager.h" -#include "FileData.h" -#include "FileFilterIndex.h" -#include "Gamelist.h" -#include "SystemData.h" -#include "Window.h" -GuiMetaDataEd::GuiMetaDataEd( - Window* window, - MetaDataList* md, - const std::vector& mdd, - ScraperSearchParams scraperParams, - const std::string& /*header*/, - std::function saveCallback, - std::function clearGameFunc, - std::function deleteGameFunc) - : GuiComponent(window), - mScraperParams(scraperParams), - mBackground(window, ":/graphics/frame.svg"), - mGrid(window, Vector2i(1, 3)), - mMetaDataDecl(mdd), - mMetaData(md), - mSavedCallback(saveCallback), - mClearGameFunc(clearGameFunc), - mDeleteGameFunc(deleteGameFunc), - mMediaFilesUpdated(false) +GuiMetaDataEd::GuiMetaDataEd(Window* window, + MetaDataList* md, + const std::vector& mdd, + ScraperSearchParams scraperParams, + const std::string& /*header*/, + std::function saveCallback, + std::function clearGameFunc, + std::function deleteGameFunc) + : GuiComponent(window) + , mScraperParams(scraperParams) + , mBackground(window, ":/graphics/frame.svg") + , mGrid(window, Vector2i(1, 3)) + , mMetaDataDecl(mdd) + , mMetaData(md) + , mSavedCallback(saveCallback) + , mClearGameFunc(clearGameFunc) + , mDeleteGameFunc(deleteGameFunc) + , mMediaFilesUpdated(false) { addChild(&mBackground); addChild(&mGrid); mHeaderGrid = std::make_shared(mWindow, Vector2i(1, 5)); - mTitle = std::make_shared(mWindow, "EDIT METADATA", - Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); + mTitle = std::make_shared(mWindow, "EDIT METADATA", Font::get(FONT_SIZE_LARGE), + 0x555555FF, ALIGN_CENTER); // Extract possible subfolders from the path. - std::string folderPath = Utils::String::replace( - Utils::FileSystem::getParent(scraperParams.game->getPath()), - scraperParams.system->getSystemEnvData()->mStartPath, ""); + std::string folderPath = + Utils::String::replace(Utils::FileSystem::getParent(scraperParams.game->getPath()), + scraperParams.system->getSystemEnvData()->mStartPath, ""); if (folderPath.size() >= 2) { folderPath.erase(0, 1); - #if defined(_WIN64) +#if defined(_WIN64) folderPath.push_back('\\'); folderPath = Utils::String::replace(folderPath, "/", "\\"); - #else +#else folderPath.push_back('/'); - #endif +#endif } - mSubtitle = std::make_shared(mWindow, folderPath + - Utils::FileSystem::getFileName(scraperParams.game->getPath()) + - " [" + Utils::String::toUpper(scraperParams.system->getName()) + "]" + + mSubtitle = std::make_shared( + mWindow, + folderPath + Utils::FileSystem::getFileName(scraperParams.game->getPath()) + " [" + + Utils::String::toUpper(scraperParams.system->getName()) + "]" + (scraperParams.game->getType() == FOLDER ? " " + ViewController::FOLDER_CHAR : ""), - Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER, Vector3f(0.0f, 0.0f, 0.0f), - Vector2f(0.0f, 0.0f), 0x00000000, 0.05f); + Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER, Vector3f(0.0f, 0.0f, 0.0f), + Vector2f(0.0f, 0.0f), 0x00000000, 0.05f); mHeaderGrid->setEntry(mTitle, Vector2i(0, 1), false, true); mHeaderGrid->setEntry(mSubtitle, Vector2i(0, 3), false, true); @@ -102,27 +102,26 @@ GuiMetaDataEd::GuiMetaDataEd( // Don't show the launch command override entry if this option has been disabled. if (!Settings::getInstance()->getBool("LaunchCommandOverride") && - iter->type == MD_LAUNCHCOMMAND) { - ed = std::make_shared(window, "", Font::get(FONT_SIZE_SMALL, - FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); + iter->type == MD_LAUNCHCOMMAND) { + ed = std::make_shared( + window, "", Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); assert(ed); ed->setValue(mMetaData->get(iter->key)); mEditors.push_back(ed); continue; } - // Create ed and add it (and any related components) to mMenu. - // ed's value will be set below. // It's very important to put the element with the help prompt as the last row // entry instead of for instance the spacer. That is so because ComponentList // always looks for the help prompt at the back of the element stack. ComponentListRow row; - auto lbl = std::make_shared(mWindow, - Utils::String::toUpper(iter->displayName), Font::get(FONT_SIZE_SMALL), 0x777777FF); + auto lbl = + std::make_shared(mWindow, Utils::String::toUpper(iter->displayName), + Font::get(FONT_SIZE_SMALL), 0x777777FF); row.addElement(lbl, true); // Label. switch (iter->type) { - case MD_BOOL: { + case MD_BOOL: { ed = std::make_shared(window); // Make the switches slightly smaller. auto switchSize = ed->getSize() * 0.9f; @@ -133,9 +132,9 @@ GuiMetaDataEd::GuiMetaDataEd( row.addElement(ed, false, true); break; } - case MD_RATING: { + case MD_RATING: { auto spacer = std::make_shared(mWindow); - spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0); + spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0.0f); row.addElement(spacer, false); ed = std::make_shared(window, true); @@ -145,17 +144,17 @@ GuiMetaDataEd::GuiMetaDataEd( row.addElement(ed, false, true); auto ratingSpacer = std::make_shared(mWindow); - ratingSpacer->setSize(Renderer::getScreenWidth() * 0.001f, 0); + ratingSpacer->setSize(Renderer::getScreenWidth() * 0.001f, 0.0f); row.addElement(ratingSpacer, false); // Pass input to the actual RatingComponent instead of the spacer. - row.input_handler = std::bind(&GuiComponent::input, - ed.get(), std::placeholders::_1, std::placeholders::_2); + row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1, + std::placeholders::_2); break; } - case MD_DATE: { + case MD_DATE: { auto spacer = std::make_shared(mWindow); - spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0); + spacer->setSize(Renderer::getScreenWidth() * 0.0025f, 0.0f); row.addElement(spacer, false); ed = std::make_shared(window, true); @@ -164,29 +163,22 @@ GuiMetaDataEd::GuiMetaDataEd( row.addElement(ed, false); auto dateSpacer = std::make_shared(mWindow); - dateSpacer->setSize(Renderer::getScreenWidth() * 0.0035f, 0); + dateSpacer->setSize(Renderer::getScreenWidth() * 0.0035f, 0.0f); row.addElement(dateSpacer, false); // Pass input to the actual DateTimeEditComponent instead of the spacer. - row.input_handler = std::bind(&GuiComponent::input, ed.get(), - std::placeholders::_1, std::placeholders::_2); + row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1, + std::placeholders::_2); break; } -// Not in use as 'lastplayed' is flagged as statistics and these are skipped. -// Let's still keep the code because it may be needed in the future. -// case MD_TIME: { -// ed = std::make_shared(window, -// DateTimeEditComponent::DISP_RELATIVE_TO_NOW); -// row.addElement(ed, false); -// break; -// } - case MD_LAUNCHCOMMAND: { + case MD_LAUNCHCOMMAND: { ed = std::make_shared(window, "", - Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); + Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), + 0x777777FF, ALIGN_RIGHT); row.addElement(ed, true); auto spacer = std::make_shared(mWindow); - spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0); + spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0.0f); row.addElement(spacer, false); auto bracket = std::make_shared(mWindow); @@ -207,26 +199,27 @@ GuiMetaDataEd::GuiMetaDataEd( }; std::string staticTextString = "Default value from es_systems.xml:"; - std::string defaultLaunchCommand = scraperParams.system-> - getSystemEnvData()->mLaunchCommand; + std::string defaultLaunchCommand = + scraperParams.system->getSystemEnvData()->mLaunchCommand; - row.makeAcceptInputHandler([this, title, staticTextString, - defaultLaunchCommand, ed, updateVal, multiLine] { - mWindow->pushGui(new GuiComplexTextEditPopup(mWindow, getHelpStyle(), - title, staticTextString, defaultLaunchCommand, ed->getValue(), - updateVal, multiLine, "APPLY", "APPLY CHANGES?")); + row.makeAcceptInputHandler([this, title, staticTextString, defaultLaunchCommand, ed, + updateVal, multiLine] { + mWindow->pushGui(new GuiComplexTextEditPopup( + mWindow, getHelpStyle(), title, staticTextString, defaultLaunchCommand, + ed->getValue(), updateVal, multiLine, "APPLY", "APPLY CHANGES?")); }); break; } - case MD_MULTILINE_STRING: - default: { + case MD_MULTILINE_STRING: + default: { // MD_STRING. - ed = std::make_shared(window, "", Font::get(FONT_SIZE_SMALL, - FONT_PATH_LIGHT), 0x777777FF, ALIGN_RIGHT); + ed = std::make_shared(window, "", + Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), + 0x777777FF, ALIGN_RIGHT); row.addElement(ed, true); auto spacer = std::make_shared(mWindow); - spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0); + spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0.0f); row.addElement(spacer, false); auto bracket = std::make_shared(mWindow); @@ -239,9 +232,9 @@ GuiMetaDataEd::GuiMetaDataEd( gamePath = Utils::FileSystem::getStem(mScraperParams.game->getPath()); - // OK callback (apply new value to ed). - auto updateVal = [ed, currentKey, originalValue, gamePath] - (const std::string& newVal) { + // OK callback (apply new value to ed). + auto updateVal = [ed, currentKey, originalValue, + gamePath](const std::string& newVal) { // If the user has entered a blank game name, then set the name to the ROM // filename (minus the extension). if (currentKey == "name" && newVal == "") { @@ -251,27 +244,28 @@ GuiMetaDataEd::GuiMetaDataEd( else ed->setColor(TEXTCOLOR_USERMARKED); } - else if (newVal == "" && (currentKey == "developer" || - currentKey == "publisher" || currentKey == "genre" || - currentKey == "players")) { + else if (newVal == "" && + (currentKey == "developer" || currentKey == "publisher" || + currentKey == "genre" || currentKey == "players")) { ed->setValue("unknown"); if (originalValue == "unknown") ed->setColor(DEFAULT_TEXTCOLOR); else - ed->setColor(TEXTCOLOR_USERMARKED); + ed->setColor(TEXTCOLOR_USERMARKED); } else { ed->setValue(newVal); if (newVal == originalValue) ed->setColor(DEFAULT_TEXTCOLOR); else - ed->setColor(TEXTCOLOR_USERMARKED); + ed->setColor(TEXTCOLOR_USERMARKED); } - }; + }; row.makeAcceptInputHandler([this, title, ed, updateVal, multiLine] { mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), title, - ed->getValue(), updateVal, multiLine, "APPLY", "APPLY CHANGES?")); + ed->getValue(), updateVal, multiLine, + "APPLY", "APPLY CHANGES?")); }); break; } @@ -286,8 +280,8 @@ GuiMetaDataEd::GuiMetaDataEd( std::vector> buttons; if (!scraperParams.system->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) - buttons.push_back(std::make_shared(mWindow, "SCRAPE", "scrape", - std::bind(&GuiMetaDataEd::fetch, this))); + buttons.push_back(std::make_shared( + mWindow, "SCRAPE", "scrape", std::bind(&GuiMetaDataEd::fetch, this))); buttons.push_back(std::make_shared(mWindow, "SAVE", "save metadata", [&] { save(); @@ -295,49 +289,60 @@ GuiMetaDataEd::GuiMetaDataEd( delete this; })); buttons.push_back(std::make_shared(mWindow, "CANCEL", "cancel changes", - [&] { delete this; })); - + [&] { delete this; })); if (scraperParams.game->getType() == FOLDER) { if (mClearGameFunc) { - auto clearSelf = [&] { mClearGameFunc(); delete this; }; + auto clearSelf = [&] { + mClearGameFunc(); + delete this; + }; auto clearSelfBtnFunc = [this, clearSelf] { mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - "THIS WILL DELETE ANY MEDIA FILES AND\n" - "THE GAMELIST.XML ENTRY FOR THIS FOLDER,\n" - "BUT NEITHER THE FOLDER ITSELF OR ANY\n" - "CONTENT INSIDE IT WILL BE REMOVED\n" - "ARE YOU SURE?", - "YES", clearSelf, "NO", nullptr)); }; - buttons.push_back(std::make_shared(mWindow, "CLEAR", - "clear folder", clearSelfBtnFunc)); + "THIS WILL DELETE ANY MEDIA FILES AND\n" + "THE GAMELIST.XML ENTRY FOR THIS FOLDER,\n" + "BUT NEITHER THE FOLDER ITSELF OR ANY\n" + "CONTENT INSIDE IT WILL BE REMOVED\n" + "ARE YOU SURE?", + "YES", clearSelf, "NO", nullptr)); + }; + buttons.push_back(std::make_shared(mWindow, "CLEAR", "clear folder", + clearSelfBtnFunc)); } } else { if (mClearGameFunc) { - auto clearSelf = [&] { mClearGameFunc(); delete this; }; + auto clearSelf = [&] { + mClearGameFunc(); + delete this; + }; auto clearSelfBtnFunc = [this, clearSelf] { mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - "THIS WILL DELETE ANY MEDIA FILES\n" - "AND THE GAMELIST.XML ENTRY FOR\n" - "THIS GAME, BUT THE GAME FILE\n" - "ITSELF WILL NOT BE REMOVED\n" - "ARE YOU SURE?", - "YES", clearSelf, "NO", nullptr)); }; - buttons.push_back(std::make_shared(mWindow, "CLEAR", - "clear file", clearSelfBtnFunc)); + "THIS WILL DELETE ANY MEDIA FILES\n" + "AND THE GAMELIST.XML ENTRY FOR\n" + "THIS GAME, BUT THE GAME FILE\n" + "ITSELF WILL NOT BE REMOVED\n" + "ARE YOU SURE?", + "YES", clearSelf, "NO", nullptr)); + }; + buttons.push_back(std::make_shared(mWindow, "CLEAR", "clear file", + clearSelfBtnFunc)); } if (mDeleteGameFunc) { - auto deleteFilesAndSelf = [&] { mDeleteGameFunc(); delete this; }; + auto deleteFilesAndSelf = [&] { + mDeleteGameFunc(); + delete this; + }; auto deleteGameBtnFunc = [this, deleteFilesAndSelf] { mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - "THIS WILL DELETE THE GAME\n" - "FILE, ANY MEDIA FILES AND\n" - "THE GAMELIST.XML ENTRY\n" - "ARE YOU SURE?", - "YES", deleteFilesAndSelf, "NO", nullptr)); }; - buttons.push_back(std::make_shared(mWindow, "DELETE", - "delete game", deleteGameBtnFunc)); + "THIS WILL DELETE THE GAME\n" + "FILE, ANY MEDIA FILES AND\n" + "THE GAMELIST.XML ENTRY\n" + "ARE YOU SURE?", + "YES", deleteFilesAndSelf, "NO", nullptr)); + }; + buttons.push_back(std::make_shared(mWindow, "DELETE", "delete game", + deleteGameBtnFunc)); } } @@ -345,11 +350,12 @@ GuiMetaDataEd::GuiMetaDataEd( mGrid.setEntry(mButtons, Vector2i(0, 2), true, false); // Resize + center. - float width = static_cast(std::min(static_cast(Renderer::getScreenHeight() * - 1.05f), static_cast(Renderer::getScreenWidth() * 0.90f))); + float width = + static_cast(std::min(static_cast(Renderer::getScreenHeight() * 1.05f), + static_cast(Renderer::getScreenWidth() * 0.90f))); setSize(width, Renderer::getScreenHeight() * 0.83f); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, - (Renderer::getScreenHeight() - mSize.y()) / 2); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, + (Renderer::getScreenHeight() - mSize.y()) / 2.0f); } void GuiMetaDataEd::onSizeChanged() @@ -360,8 +366,8 @@ void GuiMetaDataEd::onSizeChanged() const float subtitleHeight = mSubtitle->getFont()->getLetterHeight(); const float titleSubtitleSpacing = mSize.y() * 0.03f; - mGrid.setRowHeightPerc(0, (titleHeight + titleSubtitleSpacing + subtitleHeight + - TITLE_VERT_PADDING) / mSize.y()); + mGrid.setRowHeightPerc( + 0, (titleHeight + titleSubtitleSpacing + subtitleHeight + TITLE_VERT_PADDING) / mSize.y()); mGrid.setRowHeightPerc(2, mButtons->getSize().y() / mSize.y()); // Snap list size to the row height to prevent a fraction of a row from being displayed. @@ -383,7 +389,7 @@ void GuiMetaDataEd::onSizeChanged() mList->setSize(mList->getSize().x(), listHeight); Vector2f newWindowSize = mSize; newWindowSize.y() -= heightAdjustment; - mBackground.fitTo(newWindowSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBackground.fitTo(newWindowSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); // Move the buttons up as well to make the layout align correctly after the resize. Vector3f newButtonPos = mButtons->getPosition(); @@ -411,13 +417,13 @@ void GuiMetaDataEd::save() continue; if (!showHiddenGames && mMetaDataDecl.at(i).key == "hidden" && - mEditors.at(i)->getValue() != mMetaData->get("hidden")) + mEditors.at(i)->getValue() != mMetaData->get("hidden")) hideGameWhileHidden = true; // Check whether the flag to count the entry as a game was set to enabled. if (mMetaDataDecl.at(i).key == "nogamecount" && - mEditors.at(i)->getValue() != mMetaData->get("nogamecount") && - mMetaData->get("nogamecount") == "true") { + mEditors.at(i)->getValue() != mMetaData->get("nogamecount") && + mMetaData->get("nogamecount") == "true") { setGameAsCounted = true; } @@ -450,7 +456,7 @@ void GuiMetaDataEd::save() for (FileData* child : mScraperParams.game->getChildrenRecursive()) { if (!child->getHidden()) child->metadata.set("hidden", "true"); - hideGames.push_back(child); + hideGames.push_back(child); } } else { @@ -501,8 +507,8 @@ void GuiMetaDataEd::save() void GuiMetaDataEd::fetch() { - GuiGameScraper* scr = new GuiGameScraper(mWindow, mScraperParams, - std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1)); + GuiGameScraper* scr = new GuiGameScraper( + mWindow, mScraperParams, std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1)); mWindow->pushGui(scr); } @@ -514,10 +520,6 @@ void GuiMetaDataEd::fetchDone(const ScraperSearchResult& result) mMediaFilesUpdated = result.savedNewMedia; -// Curently disabled as I'm not sure if this is more annoying than helpful. -// // Select the Save button. -// mButtons->moveCursor(Vector2i(1, 0)); - // Check if any values were manually changed before starting the scraping. // If so, it's these values we should compare against when scraping, not // the values previously saved for the game. @@ -561,42 +563,31 @@ void GuiMetaDataEd::close() } } -// Keep code for potential future use. -// std::function closeFunc; -// if (!closeAllWindows) { -// closeFunc = [this] { delete this; }; -// } -// else { -// Window* window = mWindow; -// closeFunc = [window, this] { -// while (window->peekGui() != ViewController::get()) -// delete window->peekGui(); -// }; -// } - std::function closeFunc; - closeFunc = [this] { - if (mMediaFilesUpdated) { - // Always reload the gamelist if media files were updated, even if the user - // chose to not save any metadata changes. Also manually unload the game image - // and marquee, as otherwise they would not get updated until the user scrolls up - // and down the gamelist. - TextureResource::manualUnload(mScraperParams.game->getImagePath(), false); - TextureResource::manualUnload(mScraperParams.game->getMarqueePath(), false); - ViewController::get()->reloadGameListView(mScraperParams.system); - mWindow->invalidateCachedBackground(); - } - ViewController::get()->onPauseVideo(); - delete this; - }; + closeFunc = [this] { + if (mMediaFilesUpdated) { + // Always reload the gamelist if media files were updated, even if the user + // chose to not save any metadata changes. Also manually unload the game image + // and marquee, as otherwise they would not get updated until the user scrolls up + // and down the gamelist. + TextureResource::manualUnload(mScraperParams.game->getImagePath(), false); + TextureResource::manualUnload(mScraperParams.game->getMarqueePath(), false); + ViewController::get()->reloadGameListView(mScraperParams.system); + mWindow->invalidateCachedBackground(); + } + ViewController::get()->onPauseVideo(); + delete this; + }; if (metadataUpdated) { // Changes were made, ask if the user wants to save them. - mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - "SAVE CHANGES?", - "YES", [this, closeFunc] { save(); closeFunc(); }, - "NO", closeFunc - )); + mWindow->pushGui(new GuiMsgBox( + mWindow, getHelpStyle(), "SAVE CHANGES?", "YES", + [this, closeFunc] { + save(); + closeFunc(); + }, + "NO", closeFunc)); } else { // Always save if the media files have been changed (i.e. newly scraped images). diff --git a/es-app/src/guis/GuiMetaDataEd.h b/es-app/src/guis/GuiMetaDataEd.h index 036a85cf7..56173649d 100644 --- a/es-app/src/guis/GuiMetaDataEd.h +++ b/es-app/src/guis/GuiMetaDataEd.h @@ -12,11 +12,11 @@ #ifndef ES_APP_GUIS_GUI_META_DATA_ED_H #define ES_APP_GUIS_GUI_META_DATA_ED_H +#include "GuiComponent.h" +#include "MetaData.h" #include "components/ComponentGrid.h" #include "components/NinePatchComponent.h" #include "scrapers/Scraper.h" -#include "GuiComponent.h" -#include "MetaData.h" class ComponentList; class TextComponent; @@ -24,14 +24,14 @@ class TextComponent; class GuiMetaDataEd : public GuiComponent { public: - GuiMetaDataEd( - Window* window, - MetaDataList* md, const std::vector&mdd, - ScraperSearchParams params, - const std::string& header, - std::function savedCallback, - std::function clearGameFunc, - std::function deleteGameFunc); + GuiMetaDataEd(Window* window, + MetaDataList* md, + const std::vector& mdd, + ScraperSearchParams params, + const std::string& header, + std::function savedCallback, + std::function clearGameFunc, + std::function deleteGameFunc); bool input(InputConfig* config, Input input) override; void onSizeChanged() override; diff --git a/es-app/src/guis/GuiOfflineGenerator.cpp b/es-app/src/guis/GuiOfflineGenerator.cpp index 412a73081..8b6f3c506 100644 --- a/es-app/src/guis/GuiOfflineGenerator.cpp +++ b/es-app/src/guis/GuiOfflineGenerator.cpp @@ -9,17 +9,15 @@ #include "guis/GuiOfflineGenerator.h" +#include "SystemData.h" #include "components/MenuComponent.h" #include "views/ViewController.h" -#include "SystemData.h" -GuiOfflineGenerator::GuiOfflineGenerator( - Window* window, - const std::queue& gameQueue) - : GuiComponent(window), - mBackground(window, ":/graphics/frame.svg"), - mGrid(window, Vector2i(6, 13)), - mGameQueue(gameQueue) +GuiOfflineGenerator::GuiOfflineGenerator(Window* window, const std::queue& gameQueue) + : GuiComponent(window) + , mBackground(window, ":/graphics/frame.svg") + , mGrid(window, Vector2i(6, 13)) + , mGameQueue(gameQueue) { addChild(&mBackground); addChild(&mGrid); @@ -39,140 +37,139 @@ GuiOfflineGenerator::GuiOfflineGenerator( // Header. mTitle = std::make_shared(mWindow, "MIXIMAGE OFFLINE GENERATOR", - Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); + Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(6, 1)); - mStatus = std::make_shared(mWindow, "NOT STARTED", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); + mStatus = std::make_shared(mWindow, "NOT STARTED", Font::get(FONT_SIZE_MEDIUM), + 0x777777FF, ALIGN_CENTER); mGrid.setEntry(mStatus, Vector2i(0, 1), false, true, Vector2i(6, 1)); - mGameCounter = std::make_shared(mWindow, - std::to_string(mGamesProcessed) + " OF " + std::to_string(mTotalGames) + + mGameCounter = std::make_shared( + mWindow, + std::to_string(mGamesProcessed) + " OF " + std::to_string(mTotalGames) + (mTotalGames == 1 ? " GAME " : " GAMES ") + "PROCESSED", - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); + Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); mGrid.setEntry(mGameCounter, Vector2i(0, 2), false, true, Vector2i(6, 1)); // Spacer row with top border. - mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 3), - false, false, Vector2i(6, 1), GridFlags::BORDER_TOP); + mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 3), false, false, + Vector2i(6, 1), GridFlags::BORDER_TOP); // Left spacer. - mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 4), - false, false, Vector2i(1, 7)); + mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 4), false, false, + Vector2i(1, 7)); // Generated label. - mGeneratedLbl = std::make_shared(mWindow, "Generated:", - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); + mGeneratedLbl = std::make_shared( + mWindow, "Generated:", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mGrid.setEntry(mGeneratedLbl, Vector2i(1, 4), false, true, Vector2i(1, 1)); // Generated value/counter. - mGeneratedVal = std::make_shared(mWindow, - std::to_string(mGamesProcessed), - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); + mGeneratedVal = + std::make_shared(mWindow, std::to_string(mGamesProcessed), + Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mGrid.setEntry(mGeneratedVal, Vector2i(2, 4), false, true, Vector2i(1, 1)); // Overwritten label. - mOverwrittenLbl = std::make_shared(mWindow, "Overwritten:", - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); + mOverwrittenLbl = std::make_shared( + mWindow, "Overwritten:", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mGrid.setEntry(mOverwrittenLbl, Vector2i(1, 5), false, true, Vector2i(1, 1)); // Overwritten value/counter. - mOverwrittenVal = std::make_shared(mWindow, - std::to_string(mImagesOverwritten), - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); + mOverwrittenVal = + std::make_shared(mWindow, std::to_string(mImagesOverwritten), + Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mGrid.setEntry(mOverwrittenVal, Vector2i(2, 5), false, true, Vector2i(1, 1)); // Skipping label. - mSkippedLbl = std::make_shared(mWindow, "Skipped (existing):", - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); + mSkippedLbl = std::make_shared( + mWindow, "Skipped (existing):", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mGrid.setEntry(mSkippedLbl, Vector2i(1, 6), false, true, Vector2i(1, 1)); // Skipping value/counter. - mSkippedVal= std::make_shared(mWindow, - std::to_string(mGamesSkipped), - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); + mSkippedVal = std::make_shared( + mWindow, std::to_string(mGamesSkipped), Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mGrid.setEntry(mSkippedVal, Vector2i(2, 6), false, true, Vector2i(1, 1)); // Failed label. - mFailedLbl = std::make_shared(mWindow, "Failed:", - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); + mFailedLbl = std::make_shared(mWindow, "Failed:", Font::get(FONT_SIZE_SMALL), + 0x888888FF, ALIGN_LEFT); mGrid.setEntry(mFailedLbl, Vector2i(1, 7), false, true, Vector2i(1, 1)); // Failed value/counter. - mFailedVal = std::make_shared(mWindow, - std::to_string(mGamesFailed), - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); + mFailedVal = std::make_shared( + mWindow, std::to_string(mGamesFailed), Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mGrid.setEntry(mFailedVal, Vector2i(2, 7), false, true, Vector2i(1, 1)); // Processing label. - mProcessingLbl = std::make_shared(mWindow, "Processing: ", - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); + mProcessingLbl = std::make_shared( + mWindow, "Processing: ", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mGrid.setEntry(mProcessingLbl, Vector2i(3, 4), false, true, Vector2i(1, 1)); // Processing value. - mProcessingVal = std::make_shared(mWindow, "", - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); + mProcessingVal = std::make_shared(mWindow, "", Font::get(FONT_SIZE_SMALL), + 0x888888FF, ALIGN_LEFT); mGrid.setEntry(mProcessingVal, Vector2i(4, 4), false, true, Vector2i(1, 1)); // Spacer row. - mGrid.setEntry(std::make_shared(mWindow), Vector2i(1, 8), - false, false, Vector2i(4, 1)); + mGrid.setEntry(std::make_shared(mWindow), Vector2i(1, 8), false, false, + Vector2i(4, 1)); // Last error message label. - mLastErrorLbl = std::make_shared(mWindow, "Last error message:", - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); + mLastErrorLbl = std::make_shared( + mWindow, "Last error message:", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); mGrid.setEntry(mLastErrorLbl, Vector2i(1, 9), false, true, Vector2i(4, 1)); // Last error message value. - mLastErrorVal = std::make_shared(mWindow, "", - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_LEFT); + mLastErrorVal = std::make_shared(mWindow, "", Font::get(FONT_SIZE_SMALL), + 0x888888FF, ALIGN_LEFT); mGrid.setEntry(mLastErrorVal, Vector2i(1, 10), false, true, Vector2i(4, 1)); // Right spacer. - mGrid.setEntry(std::make_shared(mWindow), Vector2i(5, 4), - false, false, Vector2i(1, 7)); + mGrid.setEntry(std::make_shared(mWindow), Vector2i(5, 4), false, false, + Vector2i(1, 7)); // Spacer row with bottom border. - mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 11), - false, false, Vector2i(6, 1), GridFlags::BORDER_BOTTOM); + mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 11), false, false, + Vector2i(6, 1), GridFlags::BORDER_BOTTOM); // Buttons. std::vector> buttons; - mStartPauseButton = std::make_shared(mWindow, "START", - "start processing", [this](){ - if (!mProcessing) { - mProcessing = true; - mPaused = false; - mStartPauseButton->setText("PAUSE", "pause processing"); - mCloseButton->setText("CLOSE", "close (abort processing)"); - mStatus->setText("RUNNING..."); - if (mGamesProcessed == 0) { - LOG(LogInfo) << "GuiOfflineGenerator: Processing " << mTotalGames << " games"; + mStartPauseButton = + std::make_shared(mWindow, "START", "start processing", [this]() { + if (!mProcessing) { + mProcessing = true; + mPaused = false; + mStartPauseButton->setText("PAUSE", "pause processing"); + mCloseButton->setText("CLOSE", "close (abort processing)"); + mStatus->setText("RUNNING..."); + if (mGamesProcessed == 0) { + LOG(LogInfo) << "GuiOfflineGenerator: Processing " << mTotalGames << " games"; + } } - } - else { - if (mMiximageGeneratorThread.joinable()) - mMiximageGeneratorThread.join(); - mPaused = true; - update(1); - mProcessing = false; - this->mStartPauseButton->setText("START", "start processing"); - this->mCloseButton->setText("CLOSE", "close (abort processing)"); - mStatus->setText("PAUSED"); - } - }); + else { + if (mMiximageGeneratorThread.joinable()) + mMiximageGeneratorThread.join(); + mPaused = true; + update(1); + mProcessing = false; + this->mStartPauseButton->setText("START", "start processing"); + this->mCloseButton->setText("CLOSE", "close (abort processing)"); + mStatus->setText("PAUSED"); + } + }); buttons.push_back(mStartPauseButton); - mCloseButton = std::make_shared(mWindow, "CLOSE", "close", [this](){ + mCloseButton = std::make_shared(mWindow, "CLOSE", "close", [this]() { if (mGamesProcessed != 0 && mGamesProcessed != mTotalGames) { - LOG(LogInfo) << "GuiOfflineGenerator: Aborted after processing " << - mGamesProcessed << (mGamesProcessed == 1 ? " game (" : " games (") << - mImagesGenerated << (mImagesGenerated == 1 ? " image " : " images ") << - "generated, " << mGamesSkipped << - (mGamesSkipped == 1 ? " game " : " games ") << "skipped, " << mGamesFailed << - (mGamesFailed == 1 ? " game " : " games ") << "failed)"; + LOG(LogInfo) << "GuiOfflineGenerator: Aborted after processing " << mGamesProcessed + << (mGamesProcessed == 1 ? " game (" : " games (") << mImagesGenerated + << (mImagesGenerated == 1 ? " image " : " images ") << "generated, " + << mGamesSkipped << (mGamesSkipped == 1 ? " game " : " games ") + << "skipped, " << mGamesFailed + << (mGamesFailed == 1 ? " game " : " games ") << "failed)"; } delete this; }); @@ -184,12 +181,12 @@ GuiOfflineGenerator::GuiOfflineGenerator( // For narrower displays (e.g. in 4:3 ratio), allow the window to fill 95% of the screen // width rather than the 85% allowed for wider displays. - float width = Renderer::getScreenWidth() * - ((Renderer::getScreenAspectRatio() < 1.4f) ? 0.95f : 0.85f); + float width = + Renderer::getScreenWidth() * ((Renderer::getScreenAspectRatio() < 1.4f) ? 0.95f : 0.85f); setSize(width, Renderer::getScreenHeight() * 0.75f); setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, - (Renderer::getScreenHeight() - mSize.y()) / 2.0f); + (Renderer::getScreenHeight() - mSize.y()) / 2.0f); } GuiOfflineGenerator::~GuiOfflineGenerator() @@ -281,12 +278,12 @@ void GuiOfflineGenerator::update(int deltaTime) mGame = mGameQueue.front(); mGameQueue.pop(); - mGameName = mGame->getName() + " [" + - Utils::String::toUpper(mGame->getSystem()->getName()) + "]"; + mGameName = + mGame->getName() + " [" + Utils::String::toUpper(mGame->getSystem()->getName()) + "]"; mProcessingVal->setText(mGameName); if (!Settings::getInstance()->getBool("MiximageOverwrite") && - mGame->getMiximagePath() != "") { + mGame->getMiximagePath() != "") { mGamesProcessed++; mGamesSkipped++; mSkippedVal->setText(std::to_string(mGamesSkipped)); @@ -303,14 +300,14 @@ void GuiOfflineGenerator::update(int deltaTime) mGeneratorFuture = mGeneratorPromise.get_future(); mMiximageGeneratorThread = std::thread(&MiximageGenerator::startThread, - mMiximageGenerator.get(), &mGeneratorPromise); + mMiximageGenerator.get(), &mGeneratorPromise); } } // Update the statistics. mStatus->setText("RUNNING..."); mGameCounter->setText(std::to_string(mGamesProcessed) + " OF " + std::to_string(mTotalGames) + - (mTotalGames == 1 ? " GAME " : " GAMES ") + "PROCESSED"); + (mTotalGames == 1 ? " GAME " : " GAMES ") + "PROCESSED"); mGeneratedVal->setText(std::to_string(mImagesGenerated)); mFailedVal->setText(std::to_string(mGamesFailed)); @@ -319,13 +316,13 @@ void GuiOfflineGenerator::update(int deltaTime) if (mGamesProcessed == mTotalGames) { mStatus->setText("COMPLETED"); mStartPauseButton->setText("DONE", "done (close)"); - mStartPauseButton->setPressedFunc([this](){ delete this; }); + mStartPauseButton->setPressedFunc([this]() { delete this; }); mCloseButton->setText("CLOSE", "close"); mProcessingVal->setText(""); - LOG(LogInfo) << "GuiOfflineGenerator: Completed processing (" << mImagesGenerated << - (mImagesGenerated == 1 ? " image " : " images ") << "generated, " << - mGamesSkipped << (mGamesSkipped == 1 ? " game " : " games ") << "skipped, " << - mGamesFailed << (mGamesFailed == 1 ? " game " : " games ") << "failed)"; + LOG(LogInfo) << "GuiOfflineGenerator: Completed processing (" << mImagesGenerated + << (mImagesGenerated == 1 ? " image " : " images ") << "generated, " + << mGamesSkipped << (mGamesSkipped == 1 ? " game " : " games ") << "skipped, " + << mGamesFailed << (mGamesFailed == 1 ? " game " : " games ") << "failed)"; mProcessing = false; } } diff --git a/es-app/src/guis/GuiOfflineGenerator.h b/es-app/src/guis/GuiOfflineGenerator.h index 667d03fdb..88860ee0f 100644 --- a/es-app/src/guis/GuiOfflineGenerator.h +++ b/es-app/src/guis/GuiOfflineGenerator.h @@ -10,10 +10,10 @@ #ifndef ES_APP_GUIS_GUI_OFFLINE_GENERATOR_H #define ES_APP_GUIS_GUI_OFFLINE_GENERATOR_H -#include "components/ButtonComponent.h" -#include "components/ComponentGrid.h" #include "GuiComponent.h" #include "MiximageGenerator.h" +#include "components/ButtonComponent.h" +#include "components/ComponentGrid.h" #include diff --git a/es-app/src/guis/GuiScraperMenu.cpp b/es-app/src/guis/GuiScraperMenu.cpp index 6d402fdd7..c4a8889ab 100644 --- a/es-app/src/guis/GuiScraperMenu.cpp +++ b/es-app/src/guis/GuiScraperMenu.cpp @@ -10,23 +10,23 @@ #include "guis/GuiScraperMenu.h" +#include "FileData.h" +#include "FileSorts.h" +#include "SystemData.h" #include "components/OptionListComponent.h" #include "components/SwitchComponent.h" #include "guis/GuiMsgBox.h" #include "guis/GuiOfflineGenerator.h" #include "guis/GuiScraperMulti.h" #include "views/ViewController.h" -#include "FileData.h" -#include "FileSorts.h" -#include "SystemData.h" - GuiScraperMenu::GuiScraperMenu(Window* window, std::string title) - : GuiComponent(window), mMenu(window, title) + : GuiComponent(window) + , mMenu(window, title) { // Scraper service. - mScraper = std::make_shared> - (mWindow, getHelpStyle(), "SCRAPE FROM", false); + mScraper = std::make_shared>(mWindow, getHelpStyle(), + "SCRAPE FROM", false); std::vector scrapers = getScraperList(); // Select either the first entry or the one read from the settings, // just in case the scraper from settings has vanished. @@ -36,22 +36,50 @@ GuiScraperMenu::GuiScraperMenu(Window* window, std::string title) // Search filters, getSearches() will generate a queue of games to scrape // based on the outcome of the checks below. - mFilters = std::make_shared< OptionListComponent> - (mWindow, getHelpStyle(), "SCRAPE THESE GAMES", false); - mFilters->add("ALL GAMES", [](SystemData*, FileData*) -> bool { return true; }, false); - mFilters->add("FAVORITE GAMES", [](SystemData*, FileData* g) -> bool { - return g->getFavorite(); }, false); - mFilters->add("NO METADATA", [](SystemData*, FileData* g) -> bool { - return g->metadata.get("desc").empty(); }, false); - mFilters->add("NO GAME IMAGE", - [](SystemData*, FileData* g) -> bool { - return g->getImagePath().empty(); }, false); - mFilters->add("NO GAME VIDEO", - [](SystemData*, FileData* g) -> bool { - return g->getVideoPath().empty(); }, false); - mFilters->add("FOLDERS ONLY", - [](SystemData*, FileData* g) -> bool { - return g->getType() == FOLDER; }, false); + mFilters = std::make_shared>(mWindow, getHelpStyle(), + "SCRAPE THESE GAMES", false); + mFilters->add( + "ALL GAMES", + [](SystemData*, FileData*) -> bool { + // All games. + return true; + }, + false); + mFilters->add( + "FAVORITE GAMES", + [](SystemData*, FileData* g) -> bool { + // Favorite games. + return g->getFavorite(); + }, + false); + mFilters->add( + "NO METADATA", + [](SystemData*, FileData* g) -> bool { + // No metadata. + return g->metadata.get("desc").empty(); + }, + false); + mFilters->add( + "NO GAME IMAGE", + [](SystemData*, FileData* g) -> bool { + // No game image. + return g->getImagePath().empty(); + }, + false); + mFilters->add( + "NO GAME VIDEO", + [](SystemData*, FileData* g) -> bool { + // No game video. + return g->getVideoPath().empty(); + }, + false); + mFilters->add( + "FOLDERS ONLY", + [](SystemData*, FileData* g) -> bool { + // Folders only. + return g->getType() == FOLDER; + }, + false); mFilters->selectEntry(Settings::getInstance()->getInt("ScraperFilter")); mMenu.addWithLabel("SCRAPE THESE GAMES", mFilters); @@ -68,20 +96,20 @@ GuiScraperMenu::GuiScraperMenu(Window* window, std::string title) }); // Add systems (all systems with an existing platform ID are listed). - mSystems = std::make_shared< OptionListComponent> - (mWindow, getHelpStyle(), "SCRAPE THESE SYSTEMS", true); + mSystems = std::make_shared>(mWindow, getHelpStyle(), + "SCRAPE THESE SYSTEMS", true); for (unsigned int i = 0; i < SystemData::sSystemVector.size(); i++) { if (!SystemData::sSystemVector[i]->hasPlatformId(PlatformIds::PLATFORM_IGNORE)) { - mSystems->add(SystemData::sSystemVector[i]->getFullName(), - SystemData::sSystemVector[i], - !SystemData::sSystemVector[i]->getPlatformIds().empty()); - SystemData::sSystemVector[i]->getScrapeFlag() ? - mSystems->selectEntry(i) : mSystems->unselectEntry(i); + mSystems->add(SystemData::sSystemVector[i]->getFullName(), SystemData::sSystemVector[i], + !SystemData::sSystemVector[i]->getPlatformIds().empty()); + SystemData::sSystemVector[i]->getScrapeFlag() ? mSystems->selectEntry(i) : + mSystems->unselectEntry(i); } } mMenu.addWithLabel("SCRAPE THESE SYSTEMS", mSystems); addEntry("ACCOUNT SETTINGS", 0x777777FF, true, [this] { + // Open the account options menu. openAccountOptions(); }); addEntry("CONTENT SETTINGS", 0x777777FF, true, [this] { @@ -93,6 +121,7 @@ GuiScraperMenu::GuiScraperMenu(Window* window, std::string title) openContentOptions(); }); addEntry("MIXIMAGE SETTINGS", 0x777777FF, true, [this] { + // Open the miximage options menu. openMiximageOptions(); }); addEntry("OTHER SETTINGS", 0x777777FF, true, [this] { @@ -111,8 +140,8 @@ GuiScraperMenu::GuiScraperMenu(Window* window, std::string title) setSize(mMenu.getSize()); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, - Renderer::getScreenHeight() * 0.13f); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, + Renderer::getScreenHeight() * 0.13f); } GuiScraperMenu::~GuiScraperMenu() @@ -120,9 +149,9 @@ GuiScraperMenu::~GuiScraperMenu() // Save the scrape flags to the system settings so that they are // remembered throughout the program session. std::vector sys = mSystems->getSelectedObjects(); - for (auto it = SystemData::sSystemVector.cbegin(); - it != SystemData::sSystemVector.cend(); it++) { - (*it)->setScrapeFlag(false); + for (auto it = SystemData::sSystemVector.cbegin(); // Line break. + it != SystemData::sSystemVector.cend(); it++) { + (*it)->setScrapeFlag(false); for (auto it_sys = sys.cbegin(); it_sys != sys.cend(); it_sys++) { if ((*it)->getFullName() == (*it_sys)->getFullName()) (*it)->setScrapeFlag(true); @@ -136,48 +165,48 @@ void GuiScraperMenu::openAccountOptions() // Whether to use the ScreenScraper account when scraping. auto scraper_use_account_screenscraper = std::make_shared(mWindow); - scraper_use_account_screenscraper->setState(Settings::getInstance()-> - getBool("ScraperUseAccountScreenScraper")); + scraper_use_account_screenscraper->setState( + Settings::getInstance()->getBool("ScraperUseAccountScreenScraper")); s->addWithLabel("USE THIS ACCOUNT FOR SCREENSCRAPER", scraper_use_account_screenscraper); s->addSaveFunc([scraper_use_account_screenscraper, s] { if (scraper_use_account_screenscraper->getState() != - Settings::getInstance()->getBool("ScraperUseAccountScreenScraper")) { + Settings::getInstance()->getBool("ScraperUseAccountScreenScraper")) { Settings::getInstance()->setBool("ScraperUseAccountScreenScraper", - scraper_use_account_screenscraper->getState()); + scraper_use_account_screenscraper->getState()); s->setNeedsSaving(); } }); // ScreenScraper username. - auto scraper_username_screenscraper = std::make_shared(mWindow, "", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT); + auto scraper_username_screenscraper = std::make_shared( + mWindow, "", Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT); s->addEditableTextComponent("SCREENSCRAPER USERNAME", scraper_username_screenscraper, - Settings::getInstance()->getString("ScraperUsernameScreenScraper")); + Settings::getInstance()->getString("ScraperUsernameScreenScraper")); s->addSaveFunc([scraper_username_screenscraper, s] { if (scraper_username_screenscraper->getValue() != - Settings::getInstance()->getString("ScraperUsernameScreenScraper")) { + Settings::getInstance()->getString("ScraperUsernameScreenScraper")) { Settings::getInstance()->setString("ScraperUsernameScreenScraper", - scraper_username_screenscraper->getValue()); + scraper_username_screenscraper->getValue()); s->setNeedsSaving(); } }); // ScreenScraper password. - auto scraper_password_screenscraper = std::make_shared(mWindow, "", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT); + auto scraper_password_screenscraper = std::make_shared( + mWindow, "", Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_RIGHT); std::string passwordMasked; if (Settings::getInstance()->getString("ScraperPasswordScreenScraper") != "") { passwordMasked = "********"; scraper_password_screenscraper->setHiddenValue( - Settings::getInstance()->getString("ScraperPasswordScreenScraper")); + Settings::getInstance()->getString("ScraperPasswordScreenScraper")); } - s->addEditableTextComponent("SCREENSCRAPER PASSWORD", - scraper_password_screenscraper, passwordMasked, "", true); + s->addEditableTextComponent("SCREENSCRAPER PASSWORD", scraper_password_screenscraper, + passwordMasked, "", true); s->addSaveFunc([scraper_password_screenscraper, s] { if (scraper_password_screenscraper->getHiddenValue() != - Settings::getInstance()->getString("ScraperPasswordScreenScraper")) { + Settings::getInstance()->getString("ScraperPasswordScreenScraper")) { Settings::getInstance()->setString("ScraperPasswordScreenScraper", - scraper_password_screenscraper->getHiddenValue()); + scraper_password_screenscraper->getHiddenValue()); s->setNeedsSaving(); } }); @@ -215,8 +244,9 @@ void GuiScraperMenu::openContentOptions() if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { scrape_ratings->setEnabled(false); scrape_ratings->setOpacity(DISABLED_OPACITY); - scrape_ratings->getParent()->getChild(scrape_ratings-> - getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); + scrape_ratings->getParent() + ->getChild(scrape_ratings->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); } // Scrape other metadata. @@ -245,8 +275,9 @@ void GuiScraperMenu::openContentOptions() if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { scrape_videos->setEnabled(false); scrape_videos->setOpacity(DISABLED_OPACITY); - scrape_videos->getParent()->getChild(scrape_videos-> - getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); + scrape_videos->getParent() + ->getChild(scrape_videos->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); } // Scrape screenshots images. @@ -255,7 +286,7 @@ void GuiScraperMenu::openContentOptions() s->addWithLabel("SCRAPE SCREENSHOT IMAGES", scrape_screenshots); s->addSaveFunc([scrape_screenshots, s] { if (scrape_screenshots->getState() != - Settings::getInstance()->getBool("ScrapeScreenshots")) { + Settings::getInstance()->getBool("ScrapeScreenshots")) { Settings::getInstance()->setBool("ScrapeScreenshots", scrape_screenshots->getState()); s->setNeedsSaving(); } @@ -299,8 +330,9 @@ void GuiScraperMenu::openContentOptions() if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { scrape_3dboxes->setEnabled(false); scrape_3dboxes->setOpacity(DISABLED_OPACITY); - scrape_3dboxes->getParent()->getChild(scrape_3dboxes-> - getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); + scrape_3dboxes->getParent() + ->getChild(scrape_3dboxes->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); } mWindow->pushGui(s); @@ -311,12 +343,12 @@ void GuiScraperMenu::openMiximageOptions() auto s = new GuiSettings(mWindow, "MIXIMAGE SETTINGS"); // Miximage resolution. - auto miximage_resolution = std::make_shared> - (mWindow, getHelpStyle(), "MIXIMAGE RESOLUTION", false); + auto miximage_resolution = std::make_shared>( + mWindow, getHelpStyle(), "MIXIMAGE RESOLUTION", false); std::string selectedResolution = Settings::getInstance()->getString("MiximageResolution"); - miximage_resolution->add("1280x960", "1280x960", selectedResolution == "1280x960"); - miximage_resolution->add("1920x1440", "1920x1440", selectedResolution == "1920x1440"); - miximage_resolution->add("640x480", "640x480", selectedResolution == "640x480"); + miximage_resolution->add("1280x960", "1280x960", selectedResolution == "1280x960"); + miximage_resolution->add("1920x1440", "1920x1440", selectedResolution == "1920x1440"); + miximage_resolution->add("640x480", "640x480", selectedResolution == "640x480"); // If there are no objects returned, then there must be a manually modified entry in the // configuration file. Simply set the resolution to "1280x960" in this case. if (miximage_resolution->getSelectedObjects().size() == 0) @@ -324,19 +356,19 @@ void GuiScraperMenu::openMiximageOptions() s->addWithLabel("MIXIMAGE RESOLUTION", miximage_resolution); s->addSaveFunc([miximage_resolution, s] { if (miximage_resolution->getSelected() != - Settings::getInstance()->getString("MiximageResolution")) { - Settings::getInstance()-> - setString("MiximageResolution", miximage_resolution->getSelected()); + Settings::getInstance()->getString("MiximageResolution")) { + Settings::getInstance()->setString("MiximageResolution", + miximage_resolution->getSelected()); s->setNeedsSaving(); } }); // Screenshot scaling method. - auto miximage_scaling = std::make_shared> - (mWindow, getHelpStyle(), "SCREENSHOT SCALING", false); + auto miximage_scaling = std::make_shared>( + mWindow, getHelpStyle(), "SCREENSHOT SCALING", false); std::string selectedScaling = Settings::getInstance()->getString("MiximageScreenshotScaling"); - miximage_scaling->add("sharp", "sharp", selectedScaling == "sharp"); - miximage_scaling->add("smooth", "smooth", selectedScaling == "smooth"); + miximage_scaling->add("sharp", "sharp", selectedScaling == "sharp"); + miximage_scaling->add("smooth", "smooth", selectedScaling == "smooth"); // If there are no objects returned, then there must be a manually modified entry in the // configuration file. Simply set the scaling method to "sharp" in this case. if (miximage_scaling->getSelectedObjects().size() == 0) @@ -344,9 +376,9 @@ void GuiScraperMenu::openMiximageOptions() s->addWithLabel("SCREENSHOT SCALING METHOD", miximage_scaling); s->addSaveFunc([miximage_scaling, s] { if (miximage_scaling->getSelected() != - Settings::getInstance()->getString("MiximageScreenshotScaling")) { - Settings::getInstance()-> - setString("MiximageScreenshotScaling", miximage_scaling->getSelected()); + Settings::getInstance()->getString("MiximageScreenshotScaling")) { + Settings::getInstance()->setString("MiximageScreenshotScaling", + miximage_scaling->getSelected()); s->setNeedsSaving(); } }); @@ -356,8 +388,7 @@ void GuiScraperMenu::openMiximageOptions() miximage_generate->setState(Settings::getInstance()->getBool("MiximageGenerate")); s->addWithLabel("GENERATE MIXIMAGES WHEN SCRAPING", miximage_generate); s->addSaveFunc([miximage_generate, s] { - if (miximage_generate->getState() != - Settings::getInstance()->getBool("MiximageGenerate")) { + if (miximage_generate->getState() != Settings::getInstance()->getBool("MiximageGenerate")) { Settings::getInstance()->setBool("MiximageGenerate", miximage_generate->getState()); s->setNeedsSaving(); } @@ -369,7 +400,7 @@ void GuiScraperMenu::openMiximageOptions() s->addWithLabel("OVERWRITE MIXIMAGES (SCRAPER/OFFLINE GENERATOR)", miximage_overwrite); s->addSaveFunc([miximage_overwrite, s] { if (miximage_overwrite->getState() != - Settings::getInstance()->getBool("MiximageOverwrite")) { + Settings::getInstance()->getBool("MiximageOverwrite")) { Settings::getInstance()->setBool("MiximageOverwrite", miximage_overwrite->getState()); s->setNeedsSaving(); } @@ -381,9 +412,9 @@ void GuiScraperMenu::openMiximageOptions() s->addWithLabel("REMOVE LETTERBOXES FROM SCREENSHOTS", remove_letterboxes); s->addSaveFunc([remove_letterboxes, s] { if (remove_letterboxes->getState() != - Settings::getInstance()->getBool("MiximageRemoveLetterboxes")) { + Settings::getInstance()->getBool("MiximageRemoveLetterboxes")) { Settings::getInstance()->setBool("MiximageRemoveLetterboxes", - remove_letterboxes->getState()); + remove_letterboxes->getState()); s->setNeedsSaving(); } }); @@ -394,9 +425,9 @@ void GuiScraperMenu::openMiximageOptions() s->addWithLabel("REMOVE PILLARBOXES FROM SCREENSHOTS", remove_pillarboxes); s->addSaveFunc([remove_pillarboxes, s] { if (remove_pillarboxes->getState() != - Settings::getInstance()->getBool("MiximageRemovePillarboxes")) { + Settings::getInstance()->getBool("MiximageRemovePillarboxes")) { Settings::getInstance()->setBool("MiximageRemovePillarboxes", - remove_pillarboxes->getState()); + remove_pillarboxes->getState()); s->setNeedsSaving(); } }); @@ -407,9 +438,9 @@ void GuiScraperMenu::openMiximageOptions() s->addWithLabel("INCLUDE MARQUEE IMAGE", miximage_marquee); s->addSaveFunc([miximage_marquee, s] { if (miximage_marquee->getState() != - Settings::getInstance()->getBool("MiximageIncludeMarquee")) { - Settings::getInstance()-> - setBool("MiximageIncludeMarquee", miximage_marquee->getState()); + Settings::getInstance()->getBool("MiximageIncludeMarquee")) { + Settings::getInstance()->setBool("MiximageIncludeMarquee", + miximage_marquee->getState()); s->setNeedsSaving(); } }); @@ -419,10 +450,8 @@ void GuiScraperMenu::openMiximageOptions() miximage_box->setState(Settings::getInstance()->getBool("MiximageIncludeBox")); s->addWithLabel("INCLUDE BOX IMAGE", miximage_box); s->addSaveFunc([miximage_box, s] { - if (miximage_box->getState() != - Settings::getInstance()->getBool("MiximageIncludeBox")) { - Settings::getInstance()-> - setBool("MiximageIncludeBox", miximage_box->getState()); + if (miximage_box->getState() != Settings::getInstance()->getBool("MiximageIncludeBox")) { + Settings::getInstance()->setBool("MiximageIncludeBox", miximage_box->getState()); s->setNeedsSaving(); } }); @@ -433,9 +462,9 @@ void GuiScraperMenu::openMiximageOptions() s->addWithLabel("USE COVER IMAGE IF 3D BOX IS MISSING", miximage_cover_fallback); s->addSaveFunc([miximage_cover_fallback, s] { if (miximage_cover_fallback->getState() != - Settings::getInstance()->getBool("MiximageCoverFallback")) { - Settings::getInstance()-> - setBool("MiximageCoverFallback", miximage_cover_fallback->getState()); + Settings::getInstance()->getBool("MiximageCoverFallback")) { + Settings::getInstance()->setBool("MiximageCoverFallback", + miximage_cover_fallback->getState()); s->setNeedsSaving(); } }); @@ -443,11 +472,13 @@ void GuiScraperMenu::openMiximageOptions() // Miximage offline generator. ComponentListRow offline_generator_row; offline_generator_row.elements.clear(); - offline_generator_row.addElement(std::make_shared - (mWindow, "OFFLINE GENERATOR", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + offline_generator_row.addElement(std::make_shared(mWindow, "OFFLINE GENERATOR", + Font::get(FONT_SIZE_MEDIUM), + 0x777777FF), + true); offline_generator_row.addElement(makeArrow(mWindow), false); offline_generator_row.makeAcceptInputHandler( - std::bind(&GuiScraperMenu::openOfflineGenerator, this, s)); + std::bind(&GuiScraperMenu::openOfflineGenerator, this, s)); s->addRow(offline_generator_row); mWindow->pushGui(s); @@ -457,9 +488,9 @@ void GuiScraperMenu::openOfflineGenerator(GuiSettings* settings) { if (mSystems->getSelectedObjects().empty()) { mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - "THE MIXIMAGE GENERATOR USES THE SAME SYSTEM\n" - "SELECTIONS AS THE SCRAPER, SO PLEASE SELECT\n" - "AT LEAST ONE SYSTEM TO GENERATE IMAGES FOR")); + "THE MIXIMAGE GENERATOR USES THE SAME SYSTEM\n" + "SELECTIONS AS THE SCRAPER, SO PLEASE SELECT\n" + "AT LEAST ONE SYSTEM TO GENERATE IMAGES FOR")); return; } @@ -492,13 +523,15 @@ void GuiScraperMenu::openOtherOptions() auto s = new GuiSettings(mWindow, "OTHER SETTINGS"); // Scraper region. - auto scraper_region = std::make_shared> - (mWindow, getHelpStyle(), "REGION", false); + auto scraper_region = std::make_shared>( + mWindow, getHelpStyle(), "REGION", false); std::string selectedScraperRegion = Settings::getInstance()->getString("ScraperRegion"); + // clang-format off scraper_region->add("Europe", "eu", selectedScraperRegion == "eu"); scraper_region->add("Japan", "jp", selectedScraperRegion == "jp"); scraper_region->add("USA", "us", selectedScraperRegion == "us"); scraper_region->add("World", "wor", selectedScraperRegion == "wor"); + // clang-format on // If there are no objects returned, then there must be a manually modified entry in the // configuration file. Simply set the region to "Europe" in this case. if (scraper_region->getSelectedObjects().size() == 0) @@ -515,14 +548,16 @@ void GuiScraperMenu::openOtherOptions() if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { scraper_region->setEnabled(false); scraper_region->setOpacity(DISABLED_OPACITY); - scraper_region->getParent()->getChild(scraper_region-> - getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); + scraper_region->getParent() + ->getChild(scraper_region->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); } // Scraper language. - auto scraper_language = std::make_shared> - (mWindow, getHelpStyle(), "PREFERRED LANGUAGE", false); + auto scraper_language = std::make_shared>( + mWindow, getHelpStyle(), "PREFERRED LANGUAGE", false); std::string selectedScraperLanguage = Settings::getInstance()->getString("ScraperLanguage"); + // clang-format off scraper_language->add("English", "en", selectedScraperLanguage == "en"); scraper_language->add("Español", "es", selectedScraperLanguage == "es"); scraper_language->add("Português", "pt", selectedScraperLanguage == "pt"); @@ -543,6 +578,7 @@ void GuiScraperMenu::openOtherOptions() scraper_language->add("Čeština", "cz", selectedScraperLanguage == "cz"); scraper_language->add("Slovenčina", "sk", selectedScraperLanguage == "sk"); scraper_language->add("Türkçe", "tr", selectedScraperLanguage == "tr"); + //clang-format on // If there are no objects returned, then there must be a manually modified entry in the // configuration file. Simply set the language to "English" in this case. if (scraper_language->getSelectedObjects().size() == 0) diff --git a/es-app/src/guis/GuiScraperMenu.h b/es-app/src/guis/GuiScraperMenu.h index 08763832a..2f950d47a 100644 --- a/es-app/src/guis/GuiScraperMenu.h +++ b/es-app/src/guis/GuiScraperMenu.h @@ -16,11 +16,10 @@ #include "scrapers/Scraper.h" class FileData; -template -class OptionListComponent; class SwitchComponent; class SystemData; +template class OptionListComponent; typedef std::function GameFilterFunc; class GuiScraperMenu : public GuiComponent @@ -38,16 +37,18 @@ private: void pressedStart(); void start(); - void addEntry(const std::string&, unsigned int color, - bool add_arrow, const std::function& func); + void addEntry(const std::string&, + unsigned int color, + bool add_arrow, + const std::function& func); void openAccountOptions(); void openContentOptions(); void openMiximageOptions(); void openOfflineGenerator(GuiSettings* settings); void openOtherOptions(); - std::queue getSearches( - std::vector systems, GameFilterFunc selector); + std::queue getSearches(std::vector systems, + GameFilterFunc selector); std::shared_ptr> mScraper; std::shared_ptr> mFilters; diff --git a/es-app/src/guis/GuiScraperMulti.cpp b/es-app/src/guis/GuiScraperMulti.cpp index 20a459762..ce1e20574 100644 --- a/es-app/src/guis/GuiScraperMulti.cpp +++ b/es-app/src/guis/GuiScraperMulti.cpp @@ -11,27 +11,26 @@ #include "guis/GuiScraperMulti.h" +#include "CollectionSystemsManager.h" +#include "Gamelist.h" +#include "MameNames.h" +#include "SystemData.h" +#include "Window.h" #include "components/ButtonComponent.h" #include "components/MenuComponent.h" #include "components/TextComponent.h" #include "guis/GuiMsgBox.h" #include "guis/GuiScraperSearch.h" #include "views/ViewController.h" -#include "CollectionSystemsManager.h" -#include "Gamelist.h" -#include "MameNames.h" -#include "SystemData.h" -#include "Window.h" -GuiScraperMulti::GuiScraperMulti( - Window* window, - const std::queue& searches, - bool approveResults) - : GuiComponent(window), - mBackground(window, ":/graphics/frame.svg"), - mGrid(window, Vector2i(1, 5)), - mSearchQueue(searches), - mApproveResults(approveResults) +GuiScraperMulti::GuiScraperMulti(Window* window, + const std::queue& searches, + bool approveResults) + : GuiComponent(window) + , mBackground(window, ":/graphics/frame.svg") + , mGrid(window, Vector2i(1, 5)) + , mSearchQueue(searches) + , mApproveResults(approveResults) { assert(mSearchQueue.size()); @@ -47,41 +46,42 @@ GuiScraperMulti::GuiScraperMulti( // Set up grid. mTitle = std::make_shared(mWindow, "SCRAPING IN PROGRESS", - Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); + Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); mGrid.setEntry(mTitle, Vector2i(0, 0), false, true); - mSystem = std::make_shared(mWindow, "SYSTEM", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); + mSystem = std::make_shared(mWindow, "SYSTEM", Font::get(FONT_SIZE_MEDIUM), + 0x777777FF, ALIGN_CENTER); mGrid.setEntry(mSystem, Vector2i(0, 1), false, true); - mSubtitle = std::make_shared(mWindow, "subtitle text", - Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); + mSubtitle = std::make_shared( + mWindow, "subtitle text", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); mGrid.setEntry(mSubtitle, Vector2i(0, 2), false, true); if (mApproveResults && !Settings::getInstance()->getBool("ScraperSemiautomatic")) - mSearchComp = std::make_shared(mWindow, - GuiScraperSearch::NEVER_AUTO_ACCEPT, mTotalGames); + mSearchComp = std::make_shared( + mWindow, GuiScraperSearch::NEVER_AUTO_ACCEPT, mTotalGames); else if (mApproveResults && Settings::getInstance()->getBool("ScraperSemiautomatic")) - mSearchComp = std::make_shared(mWindow, - GuiScraperSearch::ACCEPT_SINGLE_MATCHES, mTotalGames); + mSearchComp = std::make_shared( + mWindow, GuiScraperSearch::ACCEPT_SINGLE_MATCHES, mTotalGames); else if (!mApproveResults) - mSearchComp = std::make_shared(mWindow, - GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT, mTotalGames); - mSearchComp->setAcceptCallback(std::bind(&GuiScraperMulti::acceptResult, - this, std::placeholders::_1)); + mSearchComp = std::make_shared( + mWindow, GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT, mTotalGames); + mSearchComp->setAcceptCallback( + std::bind(&GuiScraperMulti::acceptResult, this, std::placeholders::_1)); mSearchComp->setSkipCallback(std::bind(&GuiScraperMulti::skip, this)); mSearchComp->setCancelCallback(std::bind(&GuiScraperMulti::finish, this)); - mGrid.setEntry(mSearchComp, Vector2i(0, 3), mSearchComp->getSearchType() != - GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT, true); + mGrid.setEntry(mSearchComp, Vector2i(0, 3), + mSearchComp->getSearchType() != GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT, + true); std::vector> buttons; if (mApproveResults) { - buttons.push_back(std::make_shared(mWindow, "REFINE SEARCH", - "refine search", [&] { - mSearchComp->openInputScreen(mSearchQueue.front()); - mGrid.resetCursor(); - })); + buttons.push_back( + std::make_shared(mWindow, "REFINE SEARCH", "refine search", [&] { + mSearchComp->openInputScreen(mSearchQueue.front()); + mGrid.resetCursor(); + })); buttons.push_back(std::make_shared(mWindow, "SKIP", "skip game", [&] { skip(); @@ -89,8 +89,8 @@ GuiScraperMulti::GuiScraperMulti( })); } - buttons.push_back(std::make_shared(mWindow, "STOP", - "stop", std::bind(&GuiScraperMulti::finish, this))); + buttons.push_back(std::make_shared(mWindow, "STOP", "stop", + std::bind(&GuiScraperMulti::finish, this))); mButtonGrid = makeButtonGrid(mWindow, buttons); mGrid.setEntry(mButtonGrid, Vector2i(0, 4), true, false); @@ -101,8 +101,8 @@ GuiScraperMulti::GuiScraperMulti( float width = Math::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth(); setSize(width, Renderer::getScreenHeight() * 0.849f); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - - mSize.y()) / 2); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, + (Renderer::getScreenHeight() - mSize.y()) / 2.0f); doNextSearch(); } @@ -111,8 +111,8 @@ GuiScraperMulti::~GuiScraperMulti() { if (mTotalSuccessful > 0) { // Sort all systems to possibly update their view style from Basic to Detailed or Video. - for (auto it = SystemData::sSystemVector.cbegin(); - it !=SystemData::sSystemVector.cend(); it++) { + for (auto it = SystemData::sSystemVector.cbegin(); // Line break. + it != SystemData::sSystemVector.cend(); it++) { (*it)->sortSystem(); } } @@ -121,10 +121,10 @@ GuiScraperMulti::~GuiScraperMulti() void GuiScraperMulti::onSizeChanged() { - mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); mGrid.setRowHeightPerc(0, mTitle->getFont()->getLetterHeight() * 1.9725f / mSize.y(), false); - mGrid.setRowHeightPerc(1, (mSystem->getFont()->getLetterHeight() + 2) / mSize.y(), false); + mGrid.setRowHeightPerc(1, (mSystem->getFont()->getLetterHeight() + 2.0f) / mSize.y(), false); mGrid.setRowHeightPerc(2, mSubtitle->getFont()->getHeight() * 1.75f / mSize.y(), false); mGrid.setRowHeightPerc(4, mButtonGrid->getSize().y() / mSize.y(), false); mGrid.setSize(mSize); @@ -148,34 +148,36 @@ void GuiScraperMulti::doNextSearch() } else { if (mSearchQueue.front().game->isArcadeGame() && - Settings::getInstance()->getString("Scraper") == "thegamesdb") - scrapeName = Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath()) + - " (" + MameNames::getInstance()->getCleanName(mSearchQueue.front().game-> - getCleanName()) + ")"; + Settings::getInstance()->getString("Scraper") == "thegamesdb") + scrapeName = + Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath()) + " (" + + MameNames::getInstance()->getCleanName(mSearchQueue.front().game->getCleanName()) + + ")"; else scrapeName = Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath()); } // Extract possible subfolders from the path. - std::string folderPath = Utils::String::replace( - Utils::FileSystem::getParent(mSearchQueue.front().game->getPath()), - mSearchQueue.front().system->getSystemEnvData()->mStartPath, ""); + std::string folderPath = + Utils::String::replace(Utils::FileSystem::getParent(mSearchQueue.front().game->getPath()), + mSearchQueue.front().system->getSystemEnvData()->mStartPath, ""); if (folderPath.size() >= 2) { folderPath.erase(0, 1); - #if defined(_WIN64) +#if defined(_WIN64) folderPath.push_back('\\'); folderPath = Utils::String::replace(folderPath, "/", "\\"); - #else +#else folderPath.push_back('/'); - #endif +#endif } // Update subtitle. ss.str(""); - ss << "GAME " << (mCurrentGame + 1) << " OF " << mTotalGames << " - " << folderPath << - scrapeName << ((mSearchQueue.front().game->getType() == FOLDER) ? " " + - ViewController::FOLDER_CHAR : ""); + ss << "GAME " << (mCurrentGame + 1) << " OF " << mTotalGames << " - " << folderPath + << scrapeName + << ((mSearchQueue.front().game->getType() == FOLDER) ? " " + ViewController::FOLDER_CHAR : + ""); mSubtitle->setText(ss.str()); mSearchComp->search(mSearchQueue.front()); @@ -212,12 +214,12 @@ void GuiScraperMulti::finish() ss << "NO GAMES WERE SCRAPED"; } else { - ss << mTotalSuccessful << " GAME" << - ((mTotalSuccessful > 1) ? "S" : "") << " SUCCESSFULLY SCRAPED"; + ss << mTotalSuccessful << " GAME" << ((mTotalSuccessful > 1) ? "S" : "") + << " SUCCESSFULLY SCRAPED"; if (mTotalSkipped > 0) - ss << "\n" << mTotalSkipped << " GAME" - << ((mTotalSkipped > 1) ? "S" : "") << " SKIPPED"; + ss << "\n" + << mTotalSkipped << " GAME" << ((mTotalSkipped > 1) ? "S" : "") << " SKIPPED"; } mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), ss.str(), "OK", [&] { diff --git a/es-app/src/guis/GuiScraperMulti.h b/es-app/src/guis/GuiScraperMulti.h index 53b276968..63c755f6b 100644 --- a/es-app/src/guis/GuiScraperMulti.h +++ b/es-app/src/guis/GuiScraperMulti.h @@ -12,11 +12,11 @@ #ifndef ES_APP_GUIS_GUI_SCRAPER_MULTI_H #define ES_APP_GUIS_GUI_SCRAPER_MULTI_H +#include "GuiComponent.h" +#include "MetaData.h" #include "components/ComponentGrid.h" #include "components/NinePatchComponent.h" #include "scrapers/Scraper.h" -#include "GuiComponent.h" -#include "MetaData.h" class GuiScraperSearch; class TextComponent; @@ -24,10 +24,9 @@ class TextComponent; class GuiScraperMulti : public GuiComponent { public: - GuiScraperMulti( - Window* window, - const std::queue& searches, - bool approveResults); + GuiScraperMulti(Window* window, + const std::queue& searches, + bool approveResults); virtual ~GuiScraperMulti(); @@ -40,15 +39,7 @@ private: void acceptResult(const ScraperSearchResult& result); void skip(); void doNextSearch(); - void finish(); - unsigned int mTotalGames; - unsigned int mCurrentGame; - unsigned int mTotalSuccessful; - unsigned int mTotalSkipped; - std::queue mSearchQueue; - std::vector mMetaDataDecl; - bool mApproveResults; NinePatchComponent mBackground; ComponentGrid mGrid; @@ -58,6 +49,14 @@ private: std::shared_ptr mSubtitle; std::shared_ptr mSearchComp; std::shared_ptr mButtonGrid; + + std::queue mSearchQueue; + std::vector mMetaDataDecl; + unsigned int mTotalGames; + unsigned int mCurrentGame; + unsigned int mTotalSuccessful; + unsigned int mTotalSkipped; + bool mApproveResults; }; #endif // ES_APP_GUIS_GUI_SCRAPER_MULTI_H diff --git a/es-app/src/guis/GuiScraperSearch.cpp b/es-app/src/guis/GuiScraperSearch.cpp index d56238233..046a9c192 100644 --- a/es-app/src/guis/GuiScraperSearch.cpp +++ b/es-app/src/guis/GuiScraperSearch.cpp @@ -15,6 +15,13 @@ #include "guis/GuiScraperSearch.h" +#include "CollectionSystemsManager.h" +#include "FileData.h" +#include "Log.h" +#include "MameNames.h" +#include "PlatformId.h" +#include "SystemData.h" +#include "Window.h" #include "components/ComponentList.h" #include "components/DateTimeEditComponent.h" #include "components/ImageComponent.h" @@ -26,28 +33,18 @@ #include "resources/Font.h" #include "utils/StringUtil.h" #include "views/ViewController.h" -#include "CollectionSystemsManager.h" -#include "FileData.h" -#include "Log.h" -#include "MameNames.h" -#include "PlatformId.h" -#include "SystemData.h" -#include "Window.h" #define FAILED_VERIFICATION_RETRIES 8 -GuiScraperSearch::GuiScraperSearch( - Window* window, - SearchType type, - unsigned int scrapeCount) - : GuiComponent(window), - mGrid(window, Vector2i(4, 3)), - mBusyAnim(window), - mSearchType(type), - mScrapeCount(scrapeCount), - mScrapeRatings(false), - mRefinedSearch(false), - mFoundGame(false) +GuiScraperSearch::GuiScraperSearch(Window* window, SearchType type, unsigned int scrapeCount) + : GuiComponent(window) + , mGrid(window, Vector2i(4, 3)) + , mBusyAnim(window) + , mSearchType(type) + , mScrapeCount(scrapeCount) + , mScrapeRatings(false) + , mRefinedSearch(false) + , mFoundGame(false) { addChild(&mGrid); @@ -56,12 +53,12 @@ GuiScraperSearch::GuiScraperSearch( mRetryCount = 0; // Left spacer (empty component, needed for borders). - mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 0), - false, false, Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 0), false, false, + Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); // Selected result name. mResultName = std::make_shared(mWindow, "Result name", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); // Selected result thumbnail. mResultThumbnail = std::make_shared(mWindow); @@ -77,7 +74,7 @@ GuiScraperSearch::GuiScraperSearch( mDescContainer->setScrollParameters(6000, 3000, 85); mResultDesc = std::make_shared(mWindow, "Result desc", - Font::get(FONT_SIZE_SMALL), 0x777777FF); + Font::get(FONT_SIZE_SMALL), 0x777777FF); mDescContainer->addChild(mResultDesc.get()); mDescContainer->setAutoScroll(true); @@ -89,40 +86,47 @@ GuiScraperSearch::GuiScraperSearch( mMD_ReleaseDate = std::make_shared(mWindow); mMD_ReleaseDate->setColor(mdColor); mMD_ReleaseDate->setUppercase(true); - mMD_Developer = std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, - Vector3f::Zero(), Vector2f::Zero(), 0x00000000, 0.02f); - mMD_Publisher = std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, - Vector3f::Zero(), Vector2f::Zero(), 0x00000000, 0.02f); - mMD_Genre = std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, - Vector3f::Zero(), Vector2f::Zero(), 0x00000000, 0.02f); - mMD_Players = std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, - Vector3f::Zero(), Vector2f::Zero(), 0x00000000, 0.02f); + mMD_Developer = + std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(), + Vector2f::Zero(), 0x00000000, 0.02f); + mMD_Publisher = + std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(), + Vector2f::Zero(), 0x00000000, 0.02f); + mMD_Genre = + std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(), + Vector2f::Zero(), 0x00000000, 0.02f); + mMD_Players = + std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, Vector3f::Zero(), + Vector2f::Zero(), 0x00000000, 0.02f); mMD_Filler = std::make_shared(mWindow, "", font, mdColor); if (Settings::getInstance()->getString("Scraper") != "thegamesdb") mScrapeRatings = true; if (mScrapeRatings) - mMD_Pairs.push_back(MetaDataPair(std::make_shared - (mWindow, "RATING:", font, mdLblColor), mMD_Rating, false)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared - (mWindow, "RELEASED:", font, mdLblColor), mMD_ReleaseDate)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared - (mWindow, "DEVELOPER:", font, mdLblColor), mMD_Developer)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared - (mWindow, "PUBLISHER:", font, mdLblColor), mMD_Publisher)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared - (mWindow, "GENRE:", font, mdLblColor), mMD_Genre)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared - (mWindow, "PLAYERS:", font, mdLblColor), mMD_Players)); + mMD_Pairs.push_back( + MetaDataPair(std::make_shared(mWindow, "RATING:", font, mdLblColor), + mMD_Rating, false)); + + mMD_Pairs.push_back(MetaDataPair( + std::make_shared(mWindow, "RELEASED:", font, mdLblColor), mMD_ReleaseDate)); + mMD_Pairs.push_back(MetaDataPair( + std::make_shared(mWindow, "DEVELOPER:", font, mdLblColor), mMD_Developer)); + mMD_Pairs.push_back(MetaDataPair( + std::make_shared(mWindow, "PUBLISHER:", font, mdLblColor), mMD_Publisher)); + mMD_Pairs.push_back(MetaDataPair( + std::make_shared(mWindow, "GENRE:", font, mdLblColor), mMD_Genre)); + mMD_Pairs.push_back(MetaDataPair( + std::make_shared(mWindow, "PLAYERS:", font, mdLblColor), mMD_Players)); + // If no rating is being scraped, add a filler to make sure that the fonts keep the same // size so the GUI looks consistent. if (!mScrapeRatings) - mMD_Pairs.push_back(MetaDataPair(std::make_shared - (mWindow, "", font, mdLblColor), mMD_Filler)); + mMD_Pairs.push_back(MetaDataPair( + std::make_shared(mWindow, "", font, mdLblColor), mMD_Filler)); - mMD_Grid = std::make_shared(mWindow, - Vector2i(2, static_cast(mMD_Pairs.size()*2 - 1))); + mMD_Grid = std::make_shared( + mWindow, Vector2i(2, static_cast(mMD_Pairs.size() * 2 - 1))); unsigned int i = 0; for (auto it = mMD_Pairs.cbegin(); it != mMD_Pairs.cend(); it++) { mMD_Grid->setEntry(it->first, Vector2i(0, i), false, true); @@ -135,7 +139,9 @@ GuiScraperSearch::GuiScraperSearch( // Result list. mResultList = std::make_shared(mWindow); mResultList->setCursorChangedCallback([this](CursorState state) { - if (state == CURSOR_STOPPED) updateInfoPane(); }); + if (state == CURSOR_STOPPED) + updateInfoPane(); + }); updateViewStyle(); } @@ -163,7 +169,7 @@ GuiScraperSearch::~GuiScraperSearch() // This is required to properly refresh the gamelist view if the user aborted the // scraping when the miximage was getting generated. if (Settings::getInstance()->getBool("MiximageGenerate") && - mMiximageGeneratorThread.joinable()) { + mMiximageGeneratorThread.joinable()) { mScrapeResult.savedNewMedia = true; // We always let the miximage generator thread complete. mMiximageGeneratorThread.join(); @@ -196,7 +202,7 @@ void GuiScraperSearch::onSizeChanged() // Row heights. if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) // Show name. mGrid.setRowHeightPerc(0, (mResultName->getFont()->getHeight() * 1.6f) / - mGrid.getSize().y()); // Result name. + mGrid.getSize().y()); // Result name. else mGrid.setRowHeightPerc(0, 0.0825f); // Hide name but do padding. @@ -216,11 +222,11 @@ void GuiScraperSearch::onSizeChanged() resizeMetadata(); if (mSearchType != ALWAYS_ACCEPT_FIRST_RESULT) - mDescContainer->setSize(mGrid.getColWidth(1) * boxartCellScale + - mGrid.getColWidth(2), mResultDesc->getFont()->getHeight() * 3); + mDescContainer->setSize(mGrid.getColWidth(1) * boxartCellScale + mGrid.getColWidth(2), + mResultDesc->getFont()->getHeight() * 3.0f); else mDescContainer->setSize(mGrid.getColWidth(3) * boxartCellScale, - mResultDesc->getFont()->getHeight() * 6); + mResultDesc->getFont()->getHeight() * 6.0f); // Make description text wrap at edge of container. mResultDesc->setSize(mDescContainer->getSize().x(), 0); @@ -247,13 +253,14 @@ void GuiScraperSearch::resizeMetadata() it->first->setFont(fontLbl); it->first->setSize(0, 0); if (it->first->getSize().x() > maxLblWidth) - maxLblWidth = it->first->getSize().x() + - (16.0f * Renderer::getScreenWidthModifier()); + maxLblWidth = + it->first->getSize().x() + (16.0f * Renderer::getScreenWidthModifier()); } for (unsigned int i = 0; i < mMD_Pairs.size(); i++) - mMD_Grid->setRowHeightPerc(i * 2, (fontLbl->getLetterHeight() + - (2.0f * Renderer::getScreenHeightModifier())) / mMD_Grid->getSize().y()); + mMD_Grid->setRowHeightPerc( + i * 2, (fontLbl->getLetterHeight() + (2.0f * Renderer::getScreenHeightModifier())) / + mMD_Grid->getSize().y()); // Update component fonts. mMD_ReleaseDate->setFont(fontComp); @@ -286,30 +293,30 @@ void GuiScraperSearch::updateViewStyle() if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) { // Show name. mGrid.setEntry(mResultName, Vector2i(1, 0), false, false, Vector2i(2, 1), - GridFlags::BORDER_TOP); + GridFlags::BORDER_TOP); // Need a border on the bottom left. - mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 2), - false, false, Vector2i(3, 1), GridFlags::BORDER_BOTTOM); + mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 2), false, false, + Vector2i(3, 1), GridFlags::BORDER_BOTTOM); // Show description on the right. mGrid.setEntry(mDescContainer, Vector2i(3, 0), false, false, Vector2i(1, 3), - GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); // Make description text wrap at edge of container. mResultDesc->setSize(mDescContainer->getSize().x(), 0.0f); } else { // Fake row where name would be. - mGrid.setEntry(std::make_shared(mWindow), Vector2i(1, 0), - false, true, Vector2i(2, 1), GridFlags::BORDER_TOP); + mGrid.setEntry(std::make_shared(mWindow), Vector2i(1, 0), false, true, + Vector2i(2, 1), GridFlags::BORDER_TOP); // Show result list on the right. mGrid.setEntry(mResultList, Vector2i(3, 0), true, true, Vector2i(1, 3), - GridFlags::BORDER_LEFT | GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + GridFlags::BORDER_LEFT | GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); // Show description under image/info. mGrid.setEntry(mDescContainer, Vector2i(1, 2), false, false, Vector2i(2, 1), - GridFlags::BORDER_BOTTOM); + GridFlags::BORDER_BOTTOM); // Make description text wrap at edge of container. mResultDesc->setSize(mDescContainer->getSize().x(), 0); } @@ -355,16 +362,17 @@ void GuiScraperSearch::onSearchDone(const std::vector& resu if (results.empty()) { // Check if the scraper used is still valid. if (!isValidConfiguredScraper()) { - mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - Utils::String::toUpper("Configured scraper is no longer available.\n" - "Please change the scraping source in the settings."), + mWindow->pushGui(new GuiMsgBox( + mWindow, getHelpStyle(), + Utils::String::toUpper("Configured scraper is no longer available.\n" + "Please change the scraping source in the settings."), "FINISH", mSkipCallback)); } else { mFoundGame = false; ComponentListRow row; - row.addElement(std::make_shared(mWindow, "NO GAMES FOUND", - font, color), true); + row.addElement(std::make_shared(mWindow, "NO GAMES FOUND", font, color), + true); if (mSkipCallback) row.makeAcceptInputHandler(mSkipCallback); @@ -379,8 +387,10 @@ void GuiScraperSearch::onSearchDone(const std::vector& resu for (size_t i = 0; i < results.size(); i++) { row.elements.clear(); - row.addElement(std::make_shared(mWindow, - Utils::String::toUpper(results.at(i).mdl.get("name")), font, color), true); + row.addElement( + std::make_shared( + mWindow, Utils::String::toUpper(results.at(i).mdl.get("name")), font, color), + true); row.makeAcceptInputHandler([this, i] { returnResult(mScraperResults.at(i)); }); mResultList->addRow(row); } @@ -393,14 +403,14 @@ void GuiScraperSearch::onSearchDone(const std::vector& resu // If there is no thumbnail to download and we're in semi-automatic mode, proceed to return // the results or we'll get stuck forever waiting for a thumbnail to be downloaded. if (mSearchType == ACCEPT_SINGLE_MATCHES && results.size() == 1 && - mScraperResults.front().thumbnailImageUrl == "") + mScraperResults.front().thumbnailImageUrl == "") returnResult(mScraperResults.front()); // For automatic mode, if there's no thumbnail to download or no matching games found, // proceed directly or we'll get stuck forever. if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) { - if (mScraperResults.size() == 0 || (mScraperResults.size() > 0 && - mScraperResults.front().thumbnailImageUrl == "")) { + if (mScraperResults.size() == 0 || + (mScraperResults.size() > 0 && mScraperResults.front().thumbnailImageUrl == "")) { if (mScraperResults.size() == 0) mSkipCallback(); else @@ -420,12 +430,12 @@ void GuiScraperSearch::onSearchError(const std::string& error, HttpReq::Status s // the error dialog will be presented to the user, and if the "Retry" button is pressed, // a new round of retries will take place. if (status == HttpReq::REQ_FAILED_VERIFICATION && mRetryCount < FAILED_VERIFICATION_RETRIES && - Settings::getInstance()->getBool("ScraperRetryPeerVerification")) { + Settings::getInstance()->getBool("ScraperRetryPeerVerification")) { LOG(LogError) << "GuiScraperSearch: " << Utils::String::replace(error, "\n", ""); mRetrySearch = true; mRetryCount++; - LOG(LogError) << "GuiScraperSearch: Attempting automatic retry " << mRetryCount << - " of " << FAILED_VERIFICATION_RETRIES; + LOG(LogError) << "GuiScraperSearch: Attempting automatic retry " << mRetryCount << " of " + << FAILED_VERIFICATION_RETRIES; return; } else { @@ -435,15 +445,16 @@ void GuiScraperSearch::onSearchError(const std::string& error, HttpReq::Status s if (mScrapeCount > 1) { LOG(LogError) << "GuiScraperSearch: " << Utils::String::replace(error, "\n", ""); mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), Utils::String::toUpper(error), - "RETRY", std::bind(&GuiScraperSearch::search, this, mLastSearch), - "SKIP", mSkipCallback, - "CANCEL", mCancelCallback, true)); + "RETRY", + std::bind(&GuiScraperSearch::search, this, mLastSearch), + "SKIP", mSkipCallback, "CANCEL", mCancelCallback, true)); } else { LOG(LogError) << "GuiScraperSearch: " << Utils::String::replace(error, "\n", ""); mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), Utils::String::toUpper(error), - "RETRY", std::bind(&GuiScraperSearch::search, this, mLastSearch), - "CANCEL", mCancelCallback, "", nullptr, true)); + "RETRY", + std::bind(&GuiScraperSearch::search, this, mLastSearch), + "CANCEL", mCancelCallback, "", nullptr, true)); } } @@ -489,9 +500,9 @@ void GuiScraperSearch::updateInfoPane() // Add an entry into the thumbnail map, this way we can track and download // each thumbnail separately even as they're downloading while scrolling // through the result list. - mThumbnailReqMap.insert(std::pair>(mScraperResults[i].thumbnailImageUrl, - std::unique_ptr(new HttpReq(thumb)))); + mThumbnailReqMap.insert(std::pair>( + mScraperResults[i].thumbnailImageUrl, + std::unique_ptr(new HttpReq(thumb)))); } } } @@ -593,8 +604,8 @@ void GuiScraperSearch::update(int deltaTime) // Check if the thumbnail for the currently selected game has finished downloading. if (mScraperResults.size() > 0) { - auto it = mThumbnailReqMap.find(mScraperResults[mResultList-> - getCursorId()].thumbnailImageUrl); + auto it = + mThumbnailReqMap.find(mScraperResults[mResultList->getCursorId()].thumbnailImageUrl); if (it != mThumbnailReqMap.end() && it->second->status() != HttpReq::REQ_IN_PROGRESS) updateThumbnail(); } @@ -681,13 +692,14 @@ void GuiScraperSearch::update(int deltaTime) mMDResolveHandle.reset(); if (mScrapeResult.mediaFilesDownloadStatus == COMPLETED && - Settings::getInstance()->getBool("MiximageGenerate")) { + Settings::getInstance()->getBool("MiximageGenerate")) { std::string currentMiximage = mLastSearch.game->getMiximagePath(); - if (currentMiximage == "" || (currentMiximage != "" && - Settings::getInstance()->getBool("MiximageOverwrite"))) { + if (currentMiximage == "" || + (currentMiximage != "" && + Settings::getInstance()->getBool("MiximageOverwrite"))) { - mMiximageGenerator = std::make_unique(mLastSearch.game, - mResultMessage); + mMiximageGenerator = + std::make_unique(mLastSearch.game, mResultMessage); // The promise/future mechanism is used as signaling for the thread to // indicate that processing has been completed. The reason to run a separate @@ -697,8 +709,9 @@ void GuiScraperSearch::update(int deltaTime) std::promise().swap(mGeneratorPromise); mGeneratorFuture = mGeneratorPromise.get_future(); - mMiximageGeneratorThread = std::thread(&MiximageGenerator::startThread, - mMiximageGenerator.get(), &mGeneratorPromise); + mMiximageGeneratorThread = + std::thread(&MiximageGenerator::startThread, mMiximageGenerator.get(), + &mGeneratorPromise); } else { returnResult(mScrapeResult); @@ -724,7 +737,7 @@ void GuiScraperSearch::updateThumbnail() // thumbnail download has been completed for this game. if (mScraperResults[mResultList->getCursorId()].thumbnailDownloadStatus == IN_PROGRESS) { mScraperResults[mResultList->getCursorId()].thumbnailImageData = - it->second->getContent(); + it->second->getContent(); mScraperResults[mResultList->getCursorId()].thumbnailDownloadStatus = COMPLETED; } // Activate the thumbnail in the GUI. @@ -737,7 +750,7 @@ void GuiScraperSearch::updateThumbnail() else { mResultThumbnail->setImage(""); onSearchError("Error downloading thumbnail:\n " + it->second->getErrorMsg(), - it->second->status()); + it->second->status()); } mThumbnailReqMap.erase(it); @@ -746,9 +759,9 @@ void GuiScraperSearch::updateThumbnail() // we are in semi-automatic mode with a single matching game result, we proceed // to immediately download the rest of the media files. if ((mSearchType == ALWAYS_ACCEPT_FIRST_RESULT || - (mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() == 1 && - mRefinedSearch == false)) && - mScraperResults.front().thumbnailDownloadStatus == COMPLETED) { + (mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() == 1 && + mRefinedSearch == false)) && + mScraperResults.front().thumbnailDownloadStatus == COMPLETED) { mRefinedSearch = false; if (mScraperResults.size() == 0) mSkipCallback(); @@ -781,22 +794,23 @@ void GuiScraperSearch::openInputScreen(ScraperSearchParams& params) // in case the scraper is set to TheGamesDB and it's an arcade game. This is required // as TheGamesDB has issues with searches using the short MAME names. if (params.game->isArcadeGame() && - Settings::getInstance()->getString("Scraper") == "thegamesdb") + Settings::getInstance()->getString("Scraper") == "thegamesdb") searchString = MameNames::getInstance()->getCleanName(params.game->getCleanName()); else searchString = params.game->getCleanName(); } } - else { + else { searchString = params.nameOverride; } - mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "REFINE SEARCH", - searchString, searchForFunc, false, "SEARCH", "APPLY CHANGES?")); + mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "REFINE SEARCH", searchString, + searchForFunc, false, "SEARCH", "APPLY CHANGES?")); } -bool GuiScraperSearch::saveMetadata( - const ScraperSearchResult& result, MetaDataList& metadata, FileData* scrapedGame) +bool GuiScraperSearch::saveMetadata(const ScraperSearchResult& result, + MetaDataList& metadata, + FileData* scrapedGame) { bool metadataUpdated = false; bool hasDefaultName = false; @@ -825,7 +839,7 @@ bool GuiScraperSearch::saveMetadata( // Skip element if the setting to not scrape metadata has been set, // unless its type is rating or name. if (!Settings::getInstance()->getBool("ScrapeMetadata") && - (key != "rating" && key != "name")) + (key != "rating" && key != "name")) continue; // Skip saving of rating if the corresponding option has been set to false. @@ -885,7 +899,7 @@ std::vector GuiScraperSearch::getHelpPrompts() if (mScrapeCount > 1) prompts.push_back(HelpPrompt("x", "skip")); if (mFoundGame && (mRefinedSearch || mSearchType != ACCEPT_SINGLE_MATCHES || - (mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() > 1))) + (mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() > 1))) prompts.push_back(HelpPrompt("a", "accept result")); return prompts; @@ -897,13 +911,3 @@ HelpStyle GuiScraperSearch::getHelpStyle() style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system"); return style; } - -void GuiScraperSearch::onFocusGained() -{ - mGrid.onFocusGained(); -} - -void GuiScraperSearch::onFocusLost() -{ - mGrid.onFocusLost(); -} diff --git a/es-app/src/guis/GuiScraperSearch.h b/es-app/src/guis/GuiScraperSearch.h index 99053f733..aeb607b79 100644 --- a/es-app/src/guis/GuiScraperSearch.h +++ b/es-app/src/guis/GuiScraperSearch.h @@ -16,11 +16,11 @@ #ifndef ES_APP_GUIS_GUI_SCRAPER_SEARCH_H #define ES_APP_GUIS_GUI_SCRAPER_SEARCH_H +#include "GuiComponent.h" +#include "MiximageGenerator.h" #include "components/BusyComponent.h" #include "components/ComponentGrid.h" #include "scrapers/Scraper.h" -#include "GuiComponent.h" -#include "MiximageGenerator.h" #include #include @@ -36,9 +36,9 @@ class GuiScraperSearch : public GuiComponent { public: enum SearchType { - ALWAYS_ACCEPT_FIRST_RESULT, - ACCEPT_SINGLE_MATCHES, - NEVER_AUTO_ACCEPT + ALWAYS_ACCEPT_FIRST_RESULT, // Automatic mode. + ACCEPT_SINGLE_MATCHES, // Semi-automatic mode. + NEVER_AUTO_ACCEPT // Manual mode. }; GuiScraperSearch(Window* window, SearchType searchType, unsigned int scrapeCount = 1); @@ -47,20 +47,29 @@ public: void search(const ScraperSearchParams& params); void openInputScreen(ScraperSearchParams& from); void stop(); - inline SearchType getSearchType() const { return mSearchType; } + SearchType getSearchType() const { return mSearchType; } bool getSavedNewMedia() - { return (mMDResolveHandle ? mMDResolveHandle->getSavedNewMedia() : false); }; + { + return (mMDResolveHandle ? mMDResolveHandle->getSavedNewMedia() : false); + } static bool saveMetadata(const ScraperSearchResult& result, - MetaDataList& metadata, FileData* scrapedGame); + MetaDataList& metadata, + FileData* scrapedGame); - // Metadata assets will be resolved before calling the accept callback - // (e.g. result.mdl's "image" is automatically downloaded and properly set). - inline void setAcceptCallback(const std::function& - acceptCallback) { mAcceptCallback = acceptCallback; } - inline void setSkipCallback(const std::function& - skipCallback) { mSkipCallback = skipCallback; }; - inline void setCancelCallback(const std::function& - cancelCallback) { mScrapeCount -= 1; mCancelCallback = cancelCallback; } + // Metadata assets will be resolved before calling the accept callback. + void setAcceptCallback(const std::function& acceptCallback) + { + mAcceptCallback = acceptCallback; + } + void setSkipCallback(const std::function& skipCallback) + { + mSkipCallback = skipCallback; + } + void setCancelCallback(const std::function& cancelCallback) + { + mScrapeCount -= 1; + mCancelCallback = cancelCallback; + } bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; @@ -68,20 +77,19 @@ public: std::vector getHelpPrompts() override; HelpStyle getHelpStyle() override; void onSizeChanged() override; - void onFocusGained() override; - void onFocusLost() override; void unsetRefinedSearch() { mRefinedSearch = false; } + void onFocusGained() override { mGrid.onFocusGained(); } + void onFocusLost() override { mGrid.onFocusLost(); } private: void updateViewStyle(); void updateThumbnail(); void updateInfoPane(); - void resizeMetadata(); - void onSearchError(const std::string& error, HttpReq::Status status = - HttpReq::REQ_UNDEFINED_ERROR); + void onSearchError(const std::string& error, + HttpReq::Status status = HttpReq::REQ_UNDEFINED_ERROR); void onSearchDone(const std::vector& results); int getSelectedIndex(); @@ -117,8 +125,13 @@ private: bool resize; MetaDataPair(const std::shared_ptr& f, - const std::shared_ptr& s, bool r = true) - : first(f), second(s), resize(r) {}; + const std::shared_ptr& s, + bool r = true) + : first(f) + , second(s) + , resize(r) + { + } }; std::vector mMD_Pairs; diff --git a/es-app/src/guis/GuiScreensaverOptions.cpp b/es-app/src/guis/GuiScreensaverOptions.cpp index a8a4f3802..41491e7d5 100644 --- a/es-app/src/guis/GuiScreensaverOptions.cpp +++ b/es-app/src/guis/GuiScreensaverOptions.cpp @@ -9,54 +9,55 @@ #include "guis/GuiScreensaverOptions.h" +#include "Settings.h" #include "components/OptionListComponent.h" #include "components/SliderComponent.h" #include "components/SwitchComponent.h" #include "guis/GuiMsgBox.h" -#include "Settings.h" GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string& title) - : GuiSettings(window, title) + : GuiSettings(window, title) { // Screensaver timer. - auto screensaver_timer = std::make_shared(mWindow, 0.f, 30.f, 1.f, "m"); - screensaver_timer->setValue(static_cast(Settings::getInstance()-> - getInt("ScreensaverTimer") / (1000 * 60))); + auto screensaver_timer = std::make_shared(mWindow, 0.0f, 30.0f, 1.0f, "m"); + screensaver_timer->setValue( + static_cast(Settings::getInstance()->getInt("ScreensaverTimer") / (1000 * 60))); addWithLabel("START SCREENSAVER AFTER (MINUTES)", screensaver_timer); addSaveFunc([screensaver_timer, this] { if (static_cast(std::round(screensaver_timer->getValue()) * (1000 * 60)) != - Settings::getInstance()->getInt("ScreensaverTimer")) { - Settings::getInstance()->setInt("ScreensaverTimer", - static_cast(std::round(screensaver_timer->getValue()) * (1000 * 60))); + Settings::getInstance()->getInt("ScreensaverTimer")) { + Settings::getInstance()->setInt( + "ScreensaverTimer", + static_cast(std::round(screensaver_timer->getValue()) * (1000 * 60))); setNeedsSaving(); } }); // Screensaver type. - auto screensaver_type = std::make_shared> - (mWindow, getHelpStyle(), "SCREENSAVER TYPE", false); + auto screensaver_type = std::make_shared>( + mWindow, getHelpStyle(), "SCREENSAVER TYPE", false); std::vector screensavers; screensavers.push_back("dim"); screensavers.push_back("black"); screensavers.push_back("slideshow"); screensavers.push_back("video"); for (auto it = screensavers.cbegin(); it != screensavers.cend(); it++) - screensaver_type->add(*it, *it, Settings::getInstance()-> - getString("ScreensaverType") == *it); + screensaver_type->add(*it, *it, + Settings::getInstance()->getString("ScreensaverType") == *it); addWithLabel("SCREENSAVER TYPE", screensaver_type); addSaveFunc([screensaver_type, this] { if (screensaver_type->getSelected() != - Settings::getInstance()->getString("ScreensaverType")) { + Settings::getInstance()->getString("ScreensaverType")) { if (screensaver_type->getSelected() == "video") { // If before it wasn't risky but now there's a risk of problems, show warning. - mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - "THE 'VIDEO' SCREENSAVER SHOWS\nVIDEOS FROM YOUR GAMELISTS\n\n" - "IF YOU DO NOT HAVE ANY VIDEOS, THE\n" - "SCREENSAVER WILL DEFAULT TO 'DIM'", - "OK", [] { return; }, "", nullptr, "", nullptr)); - } - Settings::getInstance()->setString("ScreensaverType", - screensaver_type->getSelected()); + mWindow->pushGui(new GuiMsgBox( + mWindow, getHelpStyle(), + "THE 'VIDEO' SCREENSAVER SHOWS\nVIDEOS FROM YOUR GAMELISTS\n\n" + "IF YOU DO NOT HAVE ANY VIDEOS, THE\n" + "SCREENSAVER WILL DEFAULT TO 'DIM'", + "OK", [] { return; }, "", nullptr, "", nullptr)); + } + Settings::getInstance()->setString("ScreensaverType", screensaver_type->getSelected()); setNeedsSaving(); } }); @@ -67,9 +68,9 @@ GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string& addWithLabel("ENABLE SCREENSAVER CONTROLS", screensaver_controls); addSaveFunc([screensaver_controls, this] { if (screensaver_controls->getState() != - Settings::getInstance()->getBool("ScreensaverControls")) { + Settings::getInstance()->getBool("ScreensaverControls")) { Settings::getInstance()->setBool("ScreensaverControls", - screensaver_controls->getState()); + screensaver_controls->getState()); setNeedsSaving(); } }); @@ -77,19 +78,21 @@ GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string& // Show filtered menu. ComponentListRow row; row.elements.clear(); - row.addElement(std::make_shared(mWindow, - "SLIDESHOW SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(mWindow, "SLIDESHOW SCREENSAVER SETTINGS", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.addElement(makeArrow(mWindow), false); - row.makeAcceptInputHandler(std::bind( - &GuiScreensaverOptions::openSlideshowScreensaverOptions, this)); + row.makeAcceptInputHandler( + std::bind(&GuiScreensaverOptions::openSlideshowScreensaverOptions, this)); addRow(row); row.elements.clear(); - row.addElement(std::make_shared(mWindow, - "VIDEO SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(mWindow, "VIDEO SCREENSAVER SETTINGS", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.addElement(makeArrow(mWindow), false); - row.makeAcceptInputHandler(std::bind( - &GuiScreensaverOptions::openVideoScreensaverOptions, this)); + row.makeAcceptInputHandler( + std::bind(&GuiScreensaverOptions::openVideoScreensaverOptions, this)); addRow(row); } @@ -99,104 +102,105 @@ void GuiScreensaverOptions::openSlideshowScreensaverOptions() // Timer for swapping images (in seconds). auto screensaver_swap_image_timeout = - std::make_shared(mWindow, 2.f, 120.f, 2.f, "s"); - screensaver_swap_image_timeout->setValue(static_cast(Settings::getInstance()-> - getInt("ScreensaverSwapImageTimeout") / (1000))); + std::make_shared(mWindow, 2.0f, 120.0f, 2.0f, "s"); + screensaver_swap_image_timeout->setValue(static_cast( + Settings::getInstance()->getInt("ScreensaverSwapImageTimeout") / (1000))); s->addWithLabel("SWAP IMAGES AFTER (SECONDS)", screensaver_swap_image_timeout); s->addSaveFunc([screensaver_swap_image_timeout, s] { if (screensaver_swap_image_timeout->getValue() != - static_cast(Settings::getInstance()-> - getInt("ScreensaverSwapImageTimeout") / (1000))) { - Settings::getInstance()->setInt("ScreensaverSwapImageTimeout", - static_cast(std::round(screensaver_swap_image_timeout->getValue()) * - (1000))); + static_cast(Settings::getInstance()->getInt("ScreensaverSwapImageTimeout") / + (1000))) { + Settings::getInstance()->setInt( + "ScreensaverSwapImageTimeout", + static_cast(std::round(screensaver_swap_image_timeout->getValue()) * (1000))); s->setNeedsSaving(); } }); // Stretch images to screen resolution. auto screensaver_stretch_images = std::make_shared(mWindow); - screensaver_stretch_images-> - setState(Settings::getInstance()->getBool("ScreensaverStretchImages")); + screensaver_stretch_images->setState( + Settings::getInstance()->getBool("ScreensaverStretchImages")); s->addWithLabel("STRETCH IMAGES TO SCREEN RESOLUTION", screensaver_stretch_images); s->addSaveFunc([screensaver_stretch_images, s] { if (screensaver_stretch_images->getState() != - Settings::getInstance()->getBool("ScreensaverStretchImages")) { + Settings::getInstance()->getBool("ScreensaverStretchImages")) { Settings::getInstance()->setBool("ScreensaverStretchImages", - screensaver_stretch_images->getState()); + screensaver_stretch_images->getState()); s->setNeedsSaving(); } }); // Show game info overlay for slideshow screensaver. auto screensaver_slideshow_game_info = std::make_shared(mWindow); - screensaver_slideshow_game_info-> - setState(Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo")); + screensaver_slideshow_game_info->setState( + Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo")); s->addWithLabel("DISPLAY GAME INFO OVERLAY", screensaver_slideshow_game_info); s->addSaveFunc([screensaver_slideshow_game_info, s] { if (screensaver_slideshow_game_info->getState() != - Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo")) { + Settings::getInstance()->getBool("ScreensaverSlideshowGameInfo")) { Settings::getInstance()->setBool("ScreensaverSlideshowGameInfo", - screensaver_slideshow_game_info->getState()); + screensaver_slideshow_game_info->getState()); s->setNeedsSaving(); } }); - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) // Render scanlines using a shader. auto screensaver_slideshow_scanlines = std::make_shared(mWindow); - screensaver_slideshow_scanlines-> - setState(Settings::getInstance()->getBool("ScreensaverSlideshowScanlines")); + screensaver_slideshow_scanlines->setState( + Settings::getInstance()->getBool("ScreensaverSlideshowScanlines")); s->addWithLabel("RENDER SCANLINES", screensaver_slideshow_scanlines); s->addSaveFunc([screensaver_slideshow_scanlines, s] { if (screensaver_slideshow_scanlines->getState() != - Settings::getInstance()->getBool("ScreensaverSlideshowScanlines")) { + Settings::getInstance()->getBool("ScreensaverSlideshowScanlines")) { Settings::getInstance()->setBool("ScreensaverSlideshowScanlines", - screensaver_slideshow_scanlines->getState()); + screensaver_slideshow_scanlines->getState()); s->setNeedsSaving(); } }); - #endif +#endif // Whether to use custom images. auto screensaver_slideshow_custom_images = std::make_shared(mWindow); - screensaver_slideshow_custom_images->setState(Settings::getInstance()-> - getBool("ScreensaverSlideshowCustomImages")); + screensaver_slideshow_custom_images->setState( + Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages")); s->addWithLabel("USE CUSTOM IMAGES", screensaver_slideshow_custom_images); s->addSaveFunc([screensaver_slideshow_custom_images, s] { if (screensaver_slideshow_custom_images->getState() != - Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages")) { + Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages")) { Settings::getInstance()->setBool("ScreensaverSlideshowCustomImages", - screensaver_slideshow_custom_images->getState()); + screensaver_slideshow_custom_images->getState()); s->setNeedsSaving(); } }); // Whether to recurse the custom image directory. auto screensaver_slideshow_recurse = std::make_shared(mWindow); - screensaver_slideshow_recurse->setState(Settings::getInstance()-> - getBool("ScreensaverSlideshowRecurse")); + screensaver_slideshow_recurse->setState( + Settings::getInstance()->getBool("ScreensaverSlideshowRecurse")); s->addWithLabel("CUSTOM IMAGE DIRECTORY RECURSIVE SEARCH", screensaver_slideshow_recurse); s->addSaveFunc([screensaver_slideshow_recurse, s] { if (screensaver_slideshow_recurse->getState() != - Settings::getInstance()->getBool("ScreensaverSlideshowRecurse")) { + Settings::getInstance()->getBool("ScreensaverSlideshowRecurse")) { Settings::getInstance()->setBool("ScreensaverSlideshowRecurse", - screensaver_slideshow_recurse->getState()); + screensaver_slideshow_recurse->getState()); s->setNeedsSaving(); } }); // Custom image directory. - auto screensaver_slideshow_image_dir = std::make_shared(mWindow, "", - Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_RIGHT); - s->addEditableTextComponent("CUSTOM IMAGE DIRECTORY", screensaver_slideshow_image_dir, - Settings::getInstance()->getString("ScreensaverSlideshowImageDir"), - Settings::getInstance()->getDefaultString("ScreensaverSlideshowImageDir")); + auto screensaver_slideshow_image_dir = std::make_shared( + mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_RIGHT); + s->addEditableTextComponent( + "CUSTOM IMAGE DIRECTORY", screensaver_slideshow_image_dir, + Settings::getInstance()->getString("ScreensaverSlideshowImageDir"), + Settings::getInstance()->getDefaultString("ScreensaverSlideshowImageDir")); s->addSaveFunc([screensaver_slideshow_image_dir, s] { if (screensaver_slideshow_image_dir->getValue() != - Settings::getInstance()->getString("ScreensaverSlideshowImageDir")) { + Settings::getInstance()->getString("ScreensaverSlideshowImageDir")) { Settings::getInstance()->setString("ScreensaverSlideshowImageDir", - screensaver_slideshow_image_dir->getValue()); + screensaver_slideshow_image_dir->getValue()); s->setNeedsSaving(); } }); @@ -210,75 +214,75 @@ void GuiScreensaverOptions::openVideoScreensaverOptions() // Timer for swapping videos (in seconds). auto screensaver_swap_video_timeout = - std::make_shared(mWindow, 0.f, 120.f, 2.f, "s"); - screensaver_swap_video_timeout->setValue(static_cast(Settings::getInstance()-> - getInt("ScreensaverSwapVideoTimeout") / (1000))); + std::make_shared(mWindow, 0.0f, 120.0f, 2.0f, "s"); + screensaver_swap_video_timeout->setValue(static_cast( + Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") / (1000))); s->addWithLabel("SWAP VIDEOS AFTER (SECONDS)", screensaver_swap_video_timeout); s->addSaveFunc([screensaver_swap_video_timeout, s] { if (screensaver_swap_video_timeout->getValue() != - static_cast(Settings::getInstance()-> - getInt("ScreensaverSwapVideoTimeout") / (1000))) { - Settings::getInstance()->setInt("ScreensaverSwapVideoTimeout", - static_cast(std::round(screensaver_swap_video_timeout->getValue()) * - (1000))); + static_cast(Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") / + (1000))) { + Settings::getInstance()->setInt( + "ScreensaverSwapVideoTimeout", + static_cast(std::round(screensaver_swap_video_timeout->getValue()) * (1000))); s->setNeedsSaving(); } }); // Stretch videos to screen resolution. auto screensaver_stretch_videos = std::make_shared(mWindow); - screensaver_stretch_videos-> - setState(Settings::getInstance()->getBool("ScreensaverStretchVideos")); + screensaver_stretch_videos->setState( + Settings::getInstance()->getBool("ScreensaverStretchVideos")); s->addWithLabel("STRETCH VIDEOS TO SCREEN RESOLUTION", screensaver_stretch_videos); s->addSaveFunc([screensaver_stretch_videos, s] { if (screensaver_stretch_videos->getState() != - Settings::getInstance()->getBool("ScreensaverStretchVideos")) { + Settings::getInstance()->getBool("ScreensaverStretchVideos")) { Settings::getInstance()->setBool("ScreensaverStretchVideos", - screensaver_stretch_videos->getState()); + screensaver_stretch_videos->getState()); s->setNeedsSaving(); } }); // Show game info overlay for video screensaver. auto screensaver_video_game_info = std::make_shared(mWindow); - screensaver_video_game_info-> - setState(Settings::getInstance()->getBool("ScreensaverVideoGameInfo")); + screensaver_video_game_info->setState( + Settings::getInstance()->getBool("ScreensaverVideoGameInfo")); s->addWithLabel("DISPLAY GAME INFO OVERLAY", screensaver_video_game_info); s->addSaveFunc([screensaver_video_game_info, s] { if (screensaver_video_game_info->getState() != - Settings::getInstance()->getBool("ScreensaverVideoGameInfo")) { + Settings::getInstance()->getBool("ScreensaverVideoGameInfo")) { Settings::getInstance()->setBool("ScreensaverVideoGameInfo", - screensaver_video_game_info->getState()); + screensaver_video_game_info->getState()); s->setNeedsSaving(); } }); - #if defined(_RPI_) +#if defined(_RPI_) // Use OMX player for screensaver. auto screensaver_omx_player = std::make_shared(mWindow); screensaver_omx_player->setState(Settings::getInstance()->getBool("ScreensaverOmxPlayer")); s->addWithLabel("USE OMX PLAYER FOR SCREENSAVER", screensaver_omx_player); s->addSaveFunc([screensaver_omx_player, s] { if (screensaver_omx_player->getState() != - Settings::getInstance()->getBool("ScreensaverOmxPlayer")) { - Settings::getInstance()-> - setBool("ScreensaverOmxPlayer", screensaver_omx_player->getState()); + Settings::getInstance()->getBool("ScreensaverOmxPlayer")) { + Settings::getInstance()->setBool("ScreensaverOmxPlayer", + screensaver_omx_player->getState()); s->setNeedsSaving(); } }); - #endif +#endif - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) // Render scanlines using a shader. auto screensaver_video_scanlines = std::make_shared(mWindow); - screensaver_video_scanlines-> - setState(Settings::getInstance()->getBool("ScreensaverVideoScanlines")); + screensaver_video_scanlines->setState( + Settings::getInstance()->getBool("ScreensaverVideoScanlines")); s->addWithLabel("RENDER SCANLINES", screensaver_video_scanlines); s->addSaveFunc([screensaver_video_scanlines, s] { if (screensaver_video_scanlines->getState() != - Settings::getInstance()->getBool("ScreensaverVideoScanlines")) { + Settings::getInstance()->getBool("ScreensaverVideoScanlines")) { Settings::getInstance()->setBool("ScreensaverVideoScanlines", - screensaver_video_scanlines->getState()); + screensaver_video_scanlines->getState()); s->setNeedsSaving(); } }); @@ -289,13 +293,13 @@ void GuiScreensaverOptions::openVideoScreensaverOptions() s->addWithLabel("RENDER BLUR", screensaver_video_blur); s->addSaveFunc([screensaver_video_blur, s] { if (screensaver_video_blur->getState() != - Settings::getInstance()->getBool("ScreensaverVideoBlur")) { + Settings::getInstance()->getBool("ScreensaverVideoBlur")) { Settings::getInstance()->setBool("ScreensaverVideoBlur", - screensaver_video_blur->getState()); + screensaver_video_blur->getState()); s->setNeedsSaving(); } }); - #endif +#endif mWindow->pushGui(s); } diff --git a/es-app/src/guis/GuiSettings.cpp b/es-app/src/guis/GuiSettings.cpp index 2b6e20077..3f65efd8c 100644 --- a/es-app/src/guis/GuiSettings.cpp +++ b/es-app/src/guis/GuiSettings.cpp @@ -10,33 +10,31 @@ #include "guis/GuiSettings.h" -#include "components/HelpComponent.h" -#include "guis/GuiTextEditPopup.h" -#include "views/gamelist/IGameListView.h" -#include "views/ViewController.h" #include "CollectionSystemsManager.h" #include "FileFilterIndex.h" #include "Settings.h" #include "SystemData.h" #include "Window.h" +#include "components/HelpComponent.h" +#include "guis/GuiTextEditPopup.h" +#include "views/ViewController.h" +#include "views/gamelist/IGameListView.h" -GuiSettings::GuiSettings( - Window* window, - std::string title) - : GuiComponent(window), - mMenu(window, title), - mNeedsSaving(false), - mNeedsReloadHelpPrompts(false), - mNeedsCollectionsUpdate(false), - mNeedsSorting(false), - mNeedsSortingCollections(false), - mNeedsResetFilters(false), - mNeedsReloading(false), - mNeedsGoToStart(false), - mNeedsGoToSystem(false), - mNeedsGoToGroupedCollections(false), - mInvalidateCachedBackground(false), - mGoToSystem(nullptr) +GuiSettings::GuiSettings(Window* window, std::string title) + : GuiComponent(window) + , mMenu(window, title) + , mNeedsSaving(false) + , mNeedsReloadHelpPrompts(false) + , mNeedsCollectionsUpdate(false) + , mNeedsSorting(false) + , mNeedsSortingCollections(false) + , mNeedsResetFilters(false) + , mNeedsReloading(false) + , mNeedsGoToStart(false) + , mNeedsGoToSystem(false) + , mNeedsGoToGroupedCollections(false) + , mInvalidateCachedBackground(false) + , mGoToSystem(nullptr) { addChild(&mMenu); mMenu.addButton("BACK", "back", [this] { delete this; }); @@ -44,11 +42,12 @@ GuiSettings::GuiSettings( setSize(static_cast(Renderer::getScreenWidth()), static_cast(Renderer::getScreenHeight())); mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2.0f, - Renderer::getScreenHeight() * 0.13f); + Renderer::getScreenHeight() * 0.13f); } GuiSettings::~GuiSettings() { + // Save on exit. save(); } @@ -72,11 +71,11 @@ void GuiSettings::save() } if (mNeedsSorting) { - for (auto it = SystemData::sSystemVector.cbegin(); it != - SystemData::sSystemVector.cend(); it++) { - if (!(!mNeedsSortingCollections && (*it)->isCollection())) { + for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); + it++) { + if (!(!mNeedsSortingCollections && (*it)->isCollection())) (*it)->sortSystem(true); - } + // Jump to the first row of the gamelist. IGameListView* gameList = ViewController::get()->getGameListView((*it)).get(); gameList->setCursor(gameList->getFirstEntry()); @@ -84,11 +83,10 @@ void GuiSettings::save() } if (mNeedsResetFilters) { - for (auto it = SystemData::sSystemVector.cbegin(); - it != SystemData::sSystemVector.cend(); it++) { + for (auto it = SystemData::sSystemVector.cbegin(); // Line break. + it != SystemData::sSystemVector.cend(); it++) { if ((*it)->getThemeFolder() == "custom-collections") { - for (FileData* customSystem : - (*it)->getRootFolder()->getChildrenListToDisplay()) + for (FileData* customSystem : (*it)->getRootFolder()->getChildrenListToDisplay()) customSystem->getSystem()->getIndex()->resetFilters(); } (*it)->getIndex()->resetFilters(); @@ -125,7 +123,7 @@ void GuiSettings::save() // these views can behave a bit strange during collection changes so it's better to be on // the safe side. if (state.getSystem()->isCollection() && - state.getSystem()->getThemeFolder() != "custom-collections") { + state.getSystem()->getThemeFolder() != "custom-collections") { ViewController::get()->goToStart(); ViewController::get()->goToSystem(SystemData::sSystemVector.front(), false); // We don't want to invalidate the cached background when there has been a collection @@ -135,7 +133,7 @@ void GuiSettings::save() // If the last displayed custom collection was just disabled, then go to start (to the // system view). if (std::find(SystemData::sSystemVector.begin(), SystemData::sSystemVector.end(), - state.getSystem()) == SystemData::sSystemVector.end()) { + state.getSystem()) == SystemData::sSystemVector.end()) { ViewController::get()->goToStart(); return; } @@ -152,18 +150,17 @@ void GuiSettings::save() } } -void GuiSettings::addEditableTextComponent( - const std::string label, - std::shared_ptr ed, - std::string value, - std::string defaultValue, - bool isPassword) +void GuiSettings::addEditableTextComponent(const std::string label, + std::shared_ptr ed, + std::string value, + std::string defaultValue, + bool isPassword) { ComponentListRow row; row.elements.clear(); auto lbl = std::make_shared(mWindow, Utils::String::toUpper(label), - Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); row.addElement(lbl, true); row.addElement(ed, true); @@ -200,11 +197,11 @@ void GuiSettings::addEditableTextComponent( row.makeAcceptInputHandler([this, label, ed, updateVal, isPassword] { // Never display the value if it's a password, instead set it to blank. if (isPassword) - mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label, - "", updateVal, false)); + mWindow->pushGui( + new GuiTextEditPopup(mWindow, getHelpStyle(), label, "", updateVal, false)); else - mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label, - ed->getValue(), updateVal, false)); + mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label, ed->getValue(), + updateVal, false)); }); assert(ed); addRow(row); @@ -219,15 +216,6 @@ bool GuiSettings::input(InputConfig* config, Input input) return true; } -// Keep code for potential future use. -// if (config->isMappedTo("start", input) && input.value != 0) { -// // Close everything. -// Window* window = mWindow; -// while (window->peekGui() && window->peekGui() != ViewController::get()) -// delete window->peekGui(); -// return true; -// } - return GuiComponent::input(config, input); } diff --git a/es-app/src/guis/GuiSettings.h b/es-app/src/guis/GuiSettings.h index 73b5b75fa..0010d5689 100644 --- a/es-app/src/guis/GuiSettings.h +++ b/es-app/src/guis/GuiSettings.h @@ -11,8 +11,8 @@ #ifndef ES_APP_GUIS_GUI_SETTINGS_H #define ES_APP_GUIS_GUI_SETTINGS_H -#include "components/MenuComponent.h" #include "SystemData.h" +#include "components/MenuComponent.h" // This is just a really simple template for a GUI that calls some save functions when closed. class GuiSettings : public GuiComponent @@ -22,29 +22,33 @@ public: virtual ~GuiSettings(); void save(); - inline void addRow(const ComponentListRow& row) { mMenu.addRow(row); }; - inline void addWithLabel(const std::string& label, - const std::shared_ptr& comp) { mMenu.addWithLabel(label, comp); }; - void addEditableTextComponent( - const std::string label, - std::shared_ptr ed, - std::string value, - std::string defaultValue = "", - bool isPassword = false); - inline void addSaveFunc(const std::function& func) { mSaveFuncs.push_back(func); }; + void addRow(const ComponentListRow& row) { mMenu.addRow(row); } + void addWithLabel(const std::string& label, const std::shared_ptr& comp) + { + mMenu.addWithLabel(label, comp); + } + void addEditableTextComponent(const std::string label, + std::shared_ptr ed, + std::string value, + std::string defaultValue = "", + bool isPassword = false); + void addSaveFunc(const std::function& func) { mSaveFuncs.push_back(func); } - void setNeedsSaving(bool state = true) { mNeedsSaving = state; }; - void setNeedsReloadHelpPrompts() { mNeedsReloadHelpPrompts = true; }; - void setNeedsCollectionsUpdate() { mNeedsCollectionsUpdate = true; }; - void setNeedsSorting() { mNeedsSorting = true; }; - void setNeedsSortingCollections() { mNeedsSortingCollections = true; }; + void setNeedsSaving(bool state = true) { mNeedsSaving = state; } + void setNeedsReloadHelpPrompts() { mNeedsReloadHelpPrompts = true; } + void setNeedsCollectionsUpdate() { mNeedsCollectionsUpdate = true; } + void setNeedsSorting() { mNeedsSorting = true; } + void setNeedsSortingCollections() { mNeedsSortingCollections = true; } void setNeedsResetFilters() { mNeedsResetFilters = true; } - void setNeedsReloading() { mNeedsReloading = true; }; - void setNeedsGoToStart() { mNeedsGoToStart = true; }; + void setNeedsReloading() { mNeedsReloading = true; } + void setNeedsGoToStart() { mNeedsGoToStart = true; } void setNeedsGoToSystem(SystemData* goToSystem) - { mNeedsGoToSystem = true; mGoToSystem = goToSystem; }; - void setNeedsGoToGroupedCollections() { mNeedsGoToGroupedCollections = true; }; - void setInvalidateCachedBackground() { mInvalidateCachedBackground = true; }; + { + mNeedsGoToSystem = true; + mGoToSystem = goToSystem; + }; + void setNeedsGoToGroupedCollections() { mNeedsGoToGroupedCollections = true; } + void setInvalidateCachedBackground() { mInvalidateCachedBackground = true; } bool input(InputConfig* config, Input input) override; std::vector getHelpPrompts() override; @@ -53,6 +57,8 @@ public: private: MenuComponent mMenu; std::vector> mSaveFuncs; + SystemData* mGoToSystem; + bool mNeedsSaving; bool mNeedsReloadHelpPrompts; bool mNeedsCollectionsUpdate; @@ -64,8 +70,6 @@ private: bool mNeedsGoToSystem; bool mNeedsGoToGroupedCollections; bool mInvalidateCachedBackground; - - SystemData* mGoToSystem; }; #endif // ES_APP_GUIS_GUI_SETTINGS_H diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index aa61a3c62..ea58e90d7 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -18,13 +18,6 @@ // environment and starts listening to SDL events. // -#include "guis/GuiDetectDevice.h" -#include "guis/GuiMsgBox.h" -#include "guis/GuiComplexTextEditPopup.h" -#include "guis/GuiLaunchScreen.h" -#include "utils/FileSystemUtil.h" -#include "utils/StringUtil.h" -#include "views/ViewController.h" #include "AudioManager.h" #include "CollectionSystemsManager.h" #include "EmulationStation.h" @@ -37,6 +30,13 @@ #include "Sound.h" #include "SystemData.h" #include "SystemScreensaver.h" +#include "guis/GuiComplexTextEditPopup.h" +#include "guis/GuiDetectDevice.h" +#include "guis/GuiLaunchScreen.h" +#include "guis/GuiMsgBox.h" +#include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" +#include "views/ViewController.h" #include #include @@ -56,14 +56,14 @@ bool forceInputConfig = false; bool settingsNeedSaving = false; enum loadSystemsReturnCode { - LOADING_OK, + LOADING_OK, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). INVALID_FILE, NO_ROMS }; #if defined(_WIN64) enum win64ConsoleType { - NO_CONSOLE, + NO_CONSOLE, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). PARENT_CONSOLE, ALLOCATED_CONSOLE }; @@ -85,7 +85,7 @@ win64ConsoleType outputToConsole(bool allocConsole) // Try to attach to a parent console process. if (AttachConsole(ATTACH_PARENT_PROCESS)) - outputHandle = GetStdHandle(STD_OUTPUT_HANDLE); + outputHandle = GetStdHandle(STD_OUTPUT_HANDLE); // If there is a parent console process, then attempt to retrieve its handle. if (outputHandle != INVALID_HANDLE_VALUE && outputHandle != nullptr) { @@ -131,11 +131,11 @@ bool parseArgs(int argc, char* argv[]) { Utils::FileSystem::setExePath(argv[0]); - #if defined(_WIN64) +#if defined(_WIN64) // Print any command line output to the console. if (argc > 1) win64ConsoleType consoleType = outputToConsole(false); - #endif +#endif std::string portableFilePath = Utils::FileSystem::getExePath() + "/portable.txt"; @@ -145,11 +145,11 @@ bool parseArgs(int argc, char* argv[]) std::cout << "Found portable.txt in the ES-DE executable directory\n"; std::ifstream portableFile; std::string homePath; - #if defined(_WIN64) +#if defined(_WIN64) portableFile.open(Utils::String::stringToWideString(portableFilePath).c_str()); - #else +#else portableFile.open(portableFilePath.c_str()); - #endif +#endif if (!portableFile.fail()) { std::string relativePath; getline(portableFile, relativePath); @@ -159,9 +159,9 @@ bool parseArgs(int argc, char* argv[]) else homePath = Utils::FileSystem::getExePath() + "/" + relativePath; - #if defined(_WIN64) +#if defined(_WIN64) homePath = Utils::String::replace(homePath, "/", "\\"); - #endif +#endif if (!Utils::FileSystem::exists(homePath)) { std::cerr << "Error: Defined home path \"" << homePath << "\" does not exist\n"; @@ -185,18 +185,18 @@ bool parseArgs(int argc, char* argv[]) std::cerr << "Error: No home path supplied with \'--home'\n"; return false; } - #if defined(_WIN64) +#if defined(_WIN64) if (!Utils::FileSystem::exists(argv[i + 1]) && - (!Utils::FileSystem::driveExists(argv[i + 1]))) { - #else + (!Utils::FileSystem::driveExists(argv[i + 1]))) { +#else if (!Utils::FileSystem::exists(argv[i + 1])) { - #endif +#endif std::cerr << "Error: Home path \'" << argv[i + 1] << "\' does not exist\n"; return false; } if (Utils::FileSystem::isRegularFile(argv[i + 1])) { - std::cerr << "Error: Home path \'" << argv[i + 1] << - "\' is a file and not a directory\n"; + std::cerr << "Error: Home path \'" << argv[i + 1] + << "\' is a file and not a directory\n"; return false; } Utils::FileSystem::setHomePath(argv[i + 1]); @@ -228,16 +228,16 @@ bool parseArgs(int argc, char* argv[]) std::string widthArg = argv[i + 1]; std::string heightArg = argv[i + 2]; if (widthArg.find_first_not_of("0123456789") != std::string::npos || - heightArg.find_first_not_of("0123456789") != std::string::npos) { + heightArg.find_first_not_of("0123456789") != std::string::npos) { std::cerr << "Error: Invalid resolution values supplied.\n"; return false; } int width = atoi(argv[i + 1]); int height = atoi(argv[i + 2]); if (width < 640 || height < 480 || width > 7680 || height > 4320 || - height < width / 4 || width < height / 2) { - std::cerr << "Error: Unsupported resolution " - << width << "x" << height << " supplied.\n"; + height < width / 4 || width < height / 2) { + std::cerr << "Error: Unsupported resolution " << width << "x" << height + << " supplied.\n"; return false; } Settings::getInstance()->setInt("WindowWidth", width); @@ -277,7 +277,8 @@ bool parseArgs(int argc, char* argv[]) } // On Unix, enable settings for the fullscreen mode. // On macOS and Windows only windowed mode is supported. - #if defined(__unix__) + +#if defined(__unix__) else if (strcmp(argv[i], "--windowed") == 0) { Settings::getInstance()->setBool("Windowed", true); } @@ -289,7 +290,7 @@ bool parseArgs(int argc, char* argv[]) Settings::getInstance()->setString("FullscreenMode", "borderless"); settingsNeedSaving = true; } - #endif +#endif else if (strcmp(argv[i], "--vsync") == 0) { if (i >= argc - 1) { std::cerr << "Error: No VSync value supplied.\n"; @@ -297,7 +298,7 @@ bool parseArgs(int argc, char* argv[]) } std::string vSyncValue = argv[i + 1]; if (vSyncValue != "on" && vSyncValue != "off" && vSyncValue != "1" && - vSyncValue != "0") { + vSyncValue != "0") { std::cerr << "Error: Invalid VSync value supplied.\n"; return false; } @@ -350,12 +351,12 @@ bool parseArgs(int argc, char* argv[]) Log::setReportingLevel(LogDebug); } else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) { - std::cout << - "EmulationStation Desktop Edition v" << PROGRAM_VERSION_STRING << "\n"; + std::cout << "EmulationStation Desktop Edition v" << PROGRAM_VERSION_STRING << "\n"; return false; } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { std::cout << + // clang-format off "Usage: emulationstation [options]\n" "EmulationStation Desktop Edition, Emulator Front-end\n\n" "Options:\n" @@ -381,6 +382,7 @@ bool parseArgs(int argc, char* argv[]) " --debug Print debug information\n" " --version, -v Display version information\n" " --help, -h Summon a sentient, angry tuba\n"; + // clang-format on return false; // Exit after printing help. } else { @@ -400,13 +402,13 @@ bool checkApplicationHomeDirectory() std::string home = Utils::FileSystem::getHomePath(); std::string applicationHome = home + "/.emulationstation"; if (!Utils::FileSystem::exists(applicationHome)) { - #if defined(_WIN64) - std::cout << "First startup, creating application home directory \"" << - Utils::String::replace(applicationHome, "/", "\\") << "\"\n"; - #else - std::cout << "First startup, creating application home directory \"" << - applicationHome << "\"\n"; - #endif +#if defined(_WIN64) + std::cout << "First startup, creating application home directory \"" + << Utils::String::replace(applicationHome, "/", "\\") << "\"\n"; +#else + std::cout << "First startup, creating application home directory \"" << applicationHome + << "\"\n"; +#endif Utils::FileSystem::createDirectory(applicationHome); if (!Utils::FileSystem::exists(applicationHome)) { std::cerr << "Fatal error: Couldn't create directory, permission problems?\n"; @@ -424,16 +426,16 @@ loadSystemsReturnCode loadSystemConfigFile() if (SystemData::sSystemVector.size() == 0) { LOG(LogError) << "No game files were found, make sure that the system directories are " - "setup correctly and that the file extensions are supported"; + "setup correctly and that the file extensions are supported"; return NO_ROMS; } return LOADING_OK; } -// Called on exit, assuming we get far enough to have the log initialized. void onExit() { + // Called on exit, assuming we get far enough to have the log initialized. Log::close(); } @@ -443,12 +445,12 @@ int main(int argc, char* argv[]) std::locale::global(std::locale("C")); - #if defined(__APPLE__) +#if defined(__APPLE__) // This is a workaround to disable the incredibly annoying save state functionality in // macOS which forces a restore of the previous window state. The problem is that this // removes the splash screen on startup and it may have other adverse effects as well. std::string saveStateDir = Utils::FileSystem::expandHomePath( - "~/Library/Saved Application State/org.es-de.EmulationStation.savedState"); + "~/Library/Saved Application State/org.es-de.EmulationStation.savedState"); // Deletion of the state files should normally not be required as there shouldn't be any // files to begin with. But maybe the files can still be created for unknown reasons // as macOS really really loves to restore windows. Let's therefore include this deletion @@ -465,22 +467,22 @@ int main(int argc, char* argv[]) // the functionality. std::string chmodCommand = "chmod 500 \"" + saveStateDir + "\""; system(chmodCommand.c_str()); - #endif +#endif if (!parseArgs(argc, argv)) { - #if defined(_WIN64) +#if defined(_WIN64) FreeConsole(); - #endif +#endif return 0; } - #if defined(_WIN64) +#if defined(_WIN64) // Send debug output to the console.. if (Settings::getInstance()->getBool("Debug")) outputToConsole(true); - #endif +#endif - #if defined(_WIN64) +#if defined(_WIN64) // Hide taskbar if the setting for this is enabled. bool taskbarStateChanged = false; unsigned int taskbarState; @@ -490,12 +492,12 @@ int main(int argc, char* argv[]) taskbarState = getTaskbarState(); hideTaskbar(); } - #endif +#endif +#if defined(FREEIMAGE_LIB) // Call this ONLY when linking with FreeImage as a static library. - #if defined(FREEIMAGE_LIB) FreeImage_Initialise(); - #endif +#endif // If ~/.emulationstation doesn't exist and cannot be created, bail. if (!checkApplicationHomeDirectory()) @@ -504,8 +506,8 @@ int main(int argc, char* argv[]) // Start the logger. Log::init(); Log::open(); - LOG(LogInfo) << "EmulationStation Desktop Edition v" << PROGRAM_VERSION_STRING << - ", built " << PROGRAM_BUILT_STRING; + LOG(LogInfo) << "EmulationStation Desktop Edition v" << PROGRAM_VERSION_STRING << ", built " + << PROGRAM_BUILT_STRING; // Always close the log on exit. atexit(&onExit); @@ -513,7 +515,7 @@ int main(int argc, char* argv[]) // Check if the configuration file exists, and if not, create it. // This should only happen on first application startup. if (!Utils::FileSystem::exists(Utils::FileSystem::getHomePath() + - "/.emulationstation/es_settings.xml")) { + "/.emulationstation/es_settings.xml")) { LOG(LogInfo) << "Settings file es_settings.xml does not exist, creating it..."; Settings::getInstance()->saveFile(); } @@ -525,15 +527,15 @@ int main(int argc, char* argv[]) // Check if the application version has changed, which would normally mean that the // user has upgraded to a newer release. std::string applicationVersion; - if ((applicationVersion = Settings::getInstance()-> - getString("ApplicationVersion")) != PROGRAM_VERSION_STRING) { + if ((applicationVersion = Settings::getInstance()->getString("ApplicationVersion")) != + PROGRAM_VERSION_STRING) { if (applicationVersion != "") { - LOG(LogInfo) << "Application version changed from previous startup, from \"" << - applicationVersion << "\" to \"" << PROGRAM_VERSION_STRING << "\""; + LOG(LogInfo) << "Application version changed from previous startup, from \"" + << applicationVersion << "\" to \"" << PROGRAM_VERSION_STRING << "\""; } else { - LOG(LogInfo) << "Application version setting is blank, changing it to \"" << - PROGRAM_VERSION_STRING << "\""; + LOG(LogInfo) << "Application version setting is blank, changing it to \"" + << PROGRAM_VERSION_STRING << "\""; } Settings::getInstance()->setString("ApplicationVersion", PROGRAM_VERSION_STRING); Settings::getInstance()->saveFile(); @@ -582,11 +584,11 @@ int main(int argc, char* argv[]) if (event.type == SDL_QUIT) return 1; +#if !defined(__APPLE__) // This hides the mouse cursor during startup, i.e. before we have begun to capture SDL events. // On macOS this causes the mouse cursor to jump back to the Dock so don't do it on this OS. - #if !defined(__APPLE__) SDL_SetRelativeMouseMode(SDL_TRUE); - #endif +#endif if (splashScreen) { std::string progressText = "Loading..."; @@ -625,8 +627,8 @@ int main(int argc, char* argv[]) // Open the input configuration GUI if the flag to force this was passed from the command line. if (!loadSystemsStatus) { if (forceInputConfig) { - window.pushGui(new GuiDetectDevice(&window, false, true, [] { - ViewController::get()->goToStart(); })); + window.pushGui(new GuiDetectDevice(&window, false, true, + [] { ViewController::get()->goToStart(); })); } else { ViewController::get()->goToStart(); @@ -639,16 +641,19 @@ int main(int argc, char* argv[]) int lastTime = SDL_GetTicks(); const auto applicationEndTime = std::chrono::system_clock::now(); - LOG(LogInfo) << "Application startup time: " << - std::chrono::duration_cast - (applicationEndTime - applicationStartTime).count() << " ms"; + LOG(LogInfo) << "Application startup time: " + << std::chrono::duration_cast(applicationEndTime - + applicationStartTime) + .count() + << " ms"; bool running = true; + +#if !defined(__APPLE__) // Now that we've finished loading, disable the relative mouse mode or otherwise mouse // input wouldn't work in any games that are launched. - #if !defined(__APPLE__) SDL_SetRelativeMouseMode(SDL_FALSE); - #endif +#endif while (running) { if (SDL_PollEvent(&event)) { @@ -657,8 +662,8 @@ int main(int argc, char* argv[]) if (event.type == SDL_QUIT) running = false; - } - while (SDL_PollEvent(&event)); + + } while (SDL_PollEvent(&event)); } if (window.isSleeping()) { @@ -694,24 +699,24 @@ int main(int argc, char* argv[]) NavigationSounds::getInstance()->deinit(); Settings::deinit(); +#if defined(FREEIMAGE_LIB) // Call this ONLY when linking with FreeImage as a static library. - #if defined(FREEIMAGE_LIB) FreeImage_DeInitialise(); - #endif +#endif - #if defined(_WIN64) +#if defined(_WIN64) // If the taskbar state was changed (taskbar was hidden), then revert it. if (taskbarStateChanged) revertTaskbarState(taskbarState); - #endif +#endif processQuitMode(); LOG(LogInfo) << "EmulationStation cleanly shutting down"; - #if defined(_WIN64) +#if defined(_WIN64) FreeConsole(); - #endif +#endif return 0; } diff --git a/es-app/src/scrapers/GamesDBJSONScraper.cpp b/es-app/src/scrapers/GamesDBJSONScraper.cpp index 81950303a..36b7e43ee 100644 --- a/es-app/src/scrapers/GamesDBJSONScraper.cpp +++ b/es-app/src/scrapers/GamesDBJSONScraper.cpp @@ -10,20 +10,20 @@ #include "scrapers/GamesDBJSONScraper.h" #include "scrapers/GamesDBJSONScraperResources.h" -#include "utils/StringUtil.h" -#include "utils/TimeUtil.h" #include "FileData.h" #include "Log.h" #include "MameNames.h" #include "PlatformId.h" #include "Settings.h" #include "SystemData.h" +#include "utils/StringUtil.h" +#include "utils/TimeUtil.h" -#include -#include #include #include #include +#include +#include using namespace PlatformIds; using namespace rapidjson; @@ -103,10 +103,10 @@ const std::map gamesdb_new_platformid_map { { SONY_PLAYSTATION_PORTABLE, "13" }, { SUPER_NINTENDO, "6" }, { SHARP_X1, "4977" }, - { SHARP_X68000, "4931"}, + { SHARP_X68000, "4931" }, { NEC_SUPERGRAFX, "34" }, - { NEC_PC_8800, "4933"}, - { NEC_PC_9800, "4934"}, + { NEC_PC_8800, "4933" }, + { NEC_PC_9800, "4934" }, { NEC_PC_ENGINE, "34" }, { NEC_PC_ENGINE_CD, "4955" }, { BANDAI_WONDERSWAN, "4925" }, @@ -118,9 +118,10 @@ const std::map gamesdb_new_platformid_map { { TANDY_TRS80, "4941" }, }; -void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params, - std::queue>& requests, - std::vector& results) +void thegamesdb_generate_json_scraper_requests( + const ScraperSearchParams& params, + std::queue>& requests, + std::vector& results) { resources.prepare(); std::string path = "https://api.thegamesdb.net/v1"; @@ -168,8 +169,8 @@ void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params if (!platforms.empty()) { bool first = true; platformQueryParam += "&filter%5Bplatform%5D="; - for (auto platformIt = platforms.cbegin(); - platformIt != platforms.cend(); platformIt++) { + for (auto platformIt = platforms.cbegin(); // Line break. + platformIt != platforms.cend(); platformIt++) { auto mapIt = gamesdb_new_platformid_map.find(*platformIt); if (mapIt != gamesdb_new_platformid_map.cend()) { if (!first) @@ -178,8 +179,9 @@ void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params first = false; } else { - LOG(LogWarning) << "TheGamesDB scraper: No support for platform \"" << - getPlatformName(*platformIt) << "\", search will be inaccurate"; + LOG(LogWarning) + << "TheGamesDB scraper: No support for platform \"" + << getPlatformName(*platformIt) << "\", search will be inaccurate"; } } path += platformQueryParam; @@ -188,15 +190,15 @@ void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params LOG(LogWarning) << "TheGamesDB scraper: No platform defined, search will be inaccurate"; } - requests.push(std::unique_ptr - (new TheGamesDBJSONRequest(requests, results, path))); + requests.push( + std::unique_ptr(new TheGamesDBJSONRequest(requests, results, path))); } } void thegamesdb_generate_json_scraper_requests( - const std::string& gameIDs, - std::queue>& requests, - std::vector& results) + const std::string& gameIDs, + std::queue>& requests, + std::vector& results) { resources.prepare(); std::string path = "https://api.thegamesdb.net/v1"; @@ -204,158 +206,158 @@ void thegamesdb_generate_json_scraper_requests( path += "/Games/Images/GamesImages?" + apiKey + "&games_id=" + gameIDs; - requests.push(std::unique_ptr - (new TheGamesDBJSONRequest(requests, results, path))); + requests.push( + std::unique_ptr(new TheGamesDBJSONRequest(requests, results, path))); } namespace { - -std::string getStringOrThrow(const Value& v, const std::string& key) -{ - if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsString()) { - throw std::runtime_error( + std::string getStringOrThrow(const Value& v, const std::string& key) + { + if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsString()) { + throw std::runtime_error( "rapidjson internal assertion failure: missing or non string key:" + key); + } + return v[key.c_str()].GetString(); } - return v[key.c_str()].GetString(); -} -int getIntOrThrow(const Value& v, const std::string& key) -{ - if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsInt()) { - throw std::runtime_error( + int getIntOrThrow(const Value& v, const std::string& key) + { + if (!v.HasMember(key.c_str()) || !v[key.c_str()].IsInt()) { + throw std::runtime_error( "rapidjson internal assertion failure: missing or non int key:" + key); - } - return v[key.c_str()].GetInt(); -} - -int getIntOrThrow(const Value& v) -{ - if (!v.IsInt()) { - throw std::runtime_error("rapidjson internal assertion failure: not an int"); - } - return v.GetInt(); -} - -std::string getDeveloperString(const Value& v) -{ - if (!v.IsArray()) - return ""; - - std::string out = ""; - bool first = true; - for (int i = 0; i < static_cast(v.Size()); i++) { - auto mapIt = resources.gamesdb_new_developers_map.find(getIntOrThrow(v[i])); - - if (mapIt == resources.gamesdb_new_developers_map.cend()) - continue; - - if (!first) - out += ", "; - - out += mapIt->second; - first = false; - } - return out; -} - -std::string getPublisherString(const Value& v) -{ - if (!v.IsArray()) - return ""; - - std::string out = ""; - bool first = true; - for (int i = 0; i < static_cast(v.Size()); i++) { - auto mapIt = resources.gamesdb_new_publishers_map.find(getIntOrThrow(v[i])); - - if (mapIt == resources.gamesdb_new_publishers_map.cend()) - continue; - - if (!first) - out += ", "; - - out += mapIt->second; - first = false; - } - return out; -} - -std::string getGenreString(const Value& v) -{ - if (!v.IsArray()) - return ""; - - std::string out = ""; - bool first = true; - for (int i = 0; i < static_cast(v.Size()); i++) { - auto mapIt = resources.gamesdb_new_genres_map.find(getIntOrThrow(v[i])); - - if (mapIt == resources.gamesdb_new_genres_map.cend()) - continue; - - if (!first) - out += ", "; - - out += mapIt->second; - first = false; - } - return out; -} - -void processGame(const Value& game, std::vector& results) -{ - ScraperSearchResult result; - - if (game.HasMember("id") && game["id"].IsInt()) - result.gameID = std::to_string(getIntOrThrow(game, "id")); - - result.mdl.set("name", getStringOrThrow(game, "game_title")); - LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Name: " << result.mdl.get("name"); - - if (game.HasMember("overview") && game["overview"].IsString()) - result.mdl.set("desc", Utils::String::replace(game["overview"].GetString(), "\r", "")); - - if (game.HasMember("release_date") && game["release_date"].IsString()) { - result.mdl.set("releasedate", Utils::Time::DateTime(Utils::Time::stringToTime( - game["release_date"].GetString(), "%Y-%m-%d"))); - LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Release Date (unparsed): " << - game["release_date"].GetString(); - LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Release Date (parsed): " << - result.mdl.get("releasedate"); + } + return v[key.c_str()].GetInt(); } - if (game.HasMember("developers") && game["developers"].IsArray()) { - result.mdl.set("developer", getDeveloperString(game["developers"])); - LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Developer: " << - result.mdl.get("developer"); + int getIntOrThrow(const Value& v) + { + if (!v.IsInt()) { + throw std::runtime_error("rapidjson internal assertion failure: not an int"); + } + return v.GetInt(); } - if (game.HasMember("publishers") && game["publishers"].IsArray()) { - result.mdl.set("publisher", getPublisherString(game["publishers"])); - LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Publisher: " << - result.mdl.get("publisher"); + std::string getDeveloperString(const Value& v) + { + if (!v.IsArray()) + return ""; + + std::string out = ""; + bool first = true; + for (int i = 0; i < static_cast(v.Size()); i++) { + auto mapIt = resources.gamesdb_new_developers_map.find(getIntOrThrow(v[i])); + + if (mapIt == resources.gamesdb_new_developers_map.cend()) + continue; + + if (!first) + out += ", "; + + out += mapIt->second; + first = false; + } + return out; } - if (game.HasMember("genres") && game["genres"].IsArray()) { - result.mdl.set("genre", getGenreString(game["genres"])); - LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Genre: " << - result.mdl.get("genre"); + std::string getPublisherString(const Value& v) + { + if (!v.IsArray()) + return ""; + + std::string out = ""; + bool first = true; + for (int i = 0; i < static_cast(v.Size()); i++) { + auto mapIt = resources.gamesdb_new_publishers_map.find(getIntOrThrow(v[i])); + + if (mapIt == resources.gamesdb_new_publishers_map.cend()) + continue; + + if (!first) + out += ", "; + + out += mapIt->second; + first = false; + } + return out; } - if (game.HasMember("players") && game["players"].IsInt()) { - result.mdl.set("players", std::to_string(game["players"].GetInt())); - LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Players: " << - result.mdl.get("players"); + std::string getGenreString(const Value& v) + { + if (!v.IsArray()) + return ""; + + std::string out = ""; + bool first = true; + for (int i = 0; i < static_cast(v.Size()); i++) { + auto mapIt = resources.gamesdb_new_genres_map.find(getIntOrThrow(v[i])); + + if (mapIt == resources.gamesdb_new_genres_map.cend()) + continue; + + if (!first) + out += ", "; + + out += mapIt->second; + first = false; + } + return out; } - result.mediaURLFetch = NOT_STARTED; - results.push_back(result); -} + void processGame(const Value& game, std::vector& results) + { + ScraperSearchResult result; + + if (game.HasMember("id") && game["id"].IsInt()) + result.gameID = std::to_string(getIntOrThrow(game, "id")); + + result.mdl.set("name", getStringOrThrow(game, "game_title")); + LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Name: " << result.mdl.get("name"); + + if (game.HasMember("overview") && game["overview"].IsString()) + result.mdl.set("desc", Utils::String::replace(game["overview"].GetString(), "\r", "")); + + if (game.HasMember("release_date") && game["release_date"].IsString()) { + result.mdl.set("releasedate", Utils::Time::DateTime(Utils::Time::stringToTime( + game["release_date"].GetString(), "%Y-%m-%d"))); + LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Release Date (unparsed): " + << game["release_date"].GetString(); + LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Release Date (parsed): " + << result.mdl.get("releasedate"); + } + + if (game.HasMember("developers") && game["developers"].IsArray()) { + result.mdl.set("developer", getDeveloperString(game["developers"])); + LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Developer: " + << result.mdl.get("developer"); + } + + if (game.HasMember("publishers") && game["publishers"].IsArray()) { + result.mdl.set("publisher", getPublisherString(game["publishers"])); + LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Publisher: " + << result.mdl.get("publisher"); + } + + if (game.HasMember("genres") && game["genres"].IsArray()) { + result.mdl.set("genre", getGenreString(game["genres"])); + LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Genre: " + << result.mdl.get("genre"); + } + + if (game.HasMember("players") && game["players"].IsInt()) { + result.mdl.set("players", std::to_string(game["players"].GetInt())); + LOG(LogDebug) << "GamesDBJSONScraper::processGame(): Players: " + << result.mdl.get("players"); + } + + result.mediaURLFetch = NOT_STARTED; + results.push_back(result); + } } // namespace -void processMediaURLs(const Value& images, const std::string& base_url, - std::vector& results) +void processMediaURLs(const Value& images, + const std::string& base_url, + std::vector& results) { ScraperSearchResult result; @@ -367,9 +369,8 @@ void processMediaURLs(const Value& images, const std::string& base_url, result.marqueeUrl = ""; result.screenshotUrl = ""; - // Quite excessive testing for valid values, but you never know - // what the server has returned and we don't want to crash the - // program due to malformed data. + // Quite excessive testing for valid values, but you never know what the server has + // returned and we don't want to crash the program due to malformed data. if (gameMedia.IsArray()) { for (SizeType i = 0; i < gameMedia.Size(); i++) { std::string mediatype; @@ -390,13 +391,13 @@ void processMediaURLs(const Value& images, const std::string& base_url, result.screenshotUrl = base_url + gameMedia[i]["filename"].GetString(); } } - result.mediaURLFetch = COMPLETED; - results.push_back(result); + result.mediaURLFetch = COMPLETED; + results.push_back(result); } } void TheGamesDBJSONRequest::process(const std::unique_ptr& req, - std::vector& results) + std::vector& results) { assert(req->status() == HttpReq::REQ_SUCCESS); @@ -404,9 +405,8 @@ void TheGamesDBJSONRequest::process(const std::unique_ptr& req, doc.Parse(req->getContent().c_str()); if (doc.HasParseError()) { - std::string err = - std::string("TheGamesDBJSONRequest - Error parsing JSON \n\t") + - GetParseError_En(doc.GetParseError()); + std::string err = std::string("TheGamesDBJSONRequest - Error parsing JSON \n\t") + + GetParseError_En(doc.GetParseError()); setError(err); LOG(LogError) << err; return; @@ -414,7 +414,7 @@ void TheGamesDBJSONRequest::process(const std::unique_ptr& req, // If the response contains the 'images' object, then it's a game media URL request. if (doc.HasMember("data") && doc["data"].HasMember("images") && - doc["data"]["images"].IsObject()) { + doc["data"]["images"].IsObject()) { const Value& images = doc["data"]["images"]; const Value& base_url = doc["data"]["base_url"]; @@ -440,19 +440,18 @@ void TheGamesDBJSONRequest::process(const std::unique_ptr& req, if (doc.HasMember("remaining_monthly_allowance") && doc.HasMember("extra_allowance")) { for (auto i = 0; i < results.size(); i++) { results[i].scraperRequestAllowance = - doc["remaining_monthly_allowance"].GetInt() + - doc["extra_allowance"].GetInt(); + doc["remaining_monthly_allowance"].GetInt() + doc["extra_allowance"].GetInt(); } LOG(LogDebug) << "TheGamesDBJSONRequest::process(): " - "Remaining monthly scraping allowance: " << - results.back().scraperRequestAllowance; + "Remaining monthly scraping allowance: " + << results.back().scraperRequestAllowance; } return; } // These process steps are for the initial scraping response. if (!doc.HasMember("data") || !doc["data"].HasMember("games") || - !doc["data"]["games"].IsArray()) { + !doc["data"]["games"].IsArray()) { LOG(LogWarning) << "TheGamesDBJSONRequest - Response had no game data\n"; return; } diff --git a/es-app/src/scrapers/GamesDBJSONScraper.h b/es-app/src/scrapers/GamesDBJSONScraper.h index 3e174ec3d..4b854c75d 100644 --- a/es-app/src/scrapers/GamesDBJSONScraper.h +++ b/es-app/src/scrapers/GamesDBJSONScraper.h @@ -12,39 +12,42 @@ #include "scrapers/Scraper.h" -namespace pugi { +namespace pugi +{ class xml_document; } -void thegamesdb_generate_json_scraper_requests(const ScraperSearchParams& params, - std::queue>& requests, - std::vector& results); +void thegamesdb_generate_json_scraper_requests( + const ScraperSearchParams& params, + std::queue>& requests, + std::vector& results); -void thegamesdb_generate_json_scraper_requests(const std::string& gameIDs, - std::queue>& requests, - std::vector& results); +void thegamesdb_generate_json_scraper_requests( + const std::string& gameIDs, + std::queue>& requests, + std::vector& results); class TheGamesDBJSONRequest : public ScraperHttpRequest { - public: +public: // Constructor for a GetGameList request. - TheGamesDBJSONRequest( - std::queue>& requestsWrite, - std::vector& resultsWrite, - const std::string& url) - : ScraperHttpRequest(resultsWrite, url), - mRequestQueue(&requestsWrite) + TheGamesDBJSONRequest(std::queue>& requestsWrite, + std::vector& resultsWrite, + const std::string& url) + : ScraperHttpRequest(resultsWrite, url) + , mRequestQueue(&requestsWrite) { } // Constructior for a GetGame request TheGamesDBJSONRequest(std::vector& resultsWrite, const std::string& url) - : ScraperHttpRequest(resultsWrite, url), mRequestQueue(nullptr) + : ScraperHttpRequest(resultsWrite, url) + , mRequestQueue(nullptr) { } - protected: +protected: void process(const std::unique_ptr& req, - std::vector& results) override; + std::vector& results) override; bool isGameRequest() { return !mRequestQueue; } std::queue>* mRequestQueue; diff --git a/es-app/src/scrapers/GamesDBJSONScraperResources.cpp b/es-app/src/scrapers/GamesDBJSONScraperResources.cpp index 984530f61..aa806d6e2 100644 --- a/es-app/src/scrapers/GamesDBJSONScraperResources.cpp +++ b/es-app/src/scrapers/GamesDBJSONScraperResources.cpp @@ -14,52 +14,53 @@ #include "scrapers/GamesDBJSONScraperResources.h" -#include "utils/FileSystemUtil.h" #include "Log.h" +#include "utils/FileSystemUtil.h" -#include -#include #include #include #include +#include +#include #include using namespace rapidjson; -namespace { -constexpr char GamesDBAPIKey[] = +namespace +{ + constexpr char GamesDBAPIKey[] = "445fcbc3f32bb2474bc27016b99eb963d318ee3a608212c543b9a79de1041600"; -constexpr int MAX_WAIT_MS = 90000; -constexpr int POLL_TIME_MS = 500; -constexpr int MAX_WAIT_ITER = MAX_WAIT_MS / POLL_TIME_MS; + constexpr int MAX_WAIT_MS = 90000; + constexpr int POLL_TIME_MS = 500; + constexpr int MAX_WAIT_ITER = MAX_WAIT_MS / POLL_TIME_MS; -constexpr char SCRAPER_RESOURCES_DIR[] = "scrapers"; -constexpr char DEVELOPERS_JSON_FILE[] = "gamesdb_developers.json"; -constexpr char PUBLISHERS_JSON_FILE[] = "gamesdb_publishers.json"; -constexpr char GENRES_JSON_FILE[] = "gamesdb_genres.json"; -constexpr char DEVELOPERS_ENDPOINT[] = "/Developers"; -constexpr char PUBLISHERS_ENDPOINT[] = "/Publishers"; -constexpr char GENRES_ENDPOINT[] = "/Genres"; + constexpr char SCRAPER_RESOURCES_DIR[] = "scrapers"; + constexpr char DEVELOPERS_JSON_FILE[] = "gamesdb_developers.json"; + constexpr char PUBLISHERS_JSON_FILE[] = "gamesdb_publishers.json"; + constexpr char GENRES_JSON_FILE[] = "gamesdb_genres.json"; + constexpr char DEVELOPERS_ENDPOINT[] = "/Developers"; + constexpr char PUBLISHERS_ENDPOINT[] = "/Publishers"; + constexpr char GENRES_ENDPOINT[] = "/Genres"; -std::string genFilePath(const std::string& file_name) -{ - return Utils::FileSystem::getGenericPath(getScrapersResouceDir() + "/" + file_name); -} + std::string genFilePath(const std::string& file_name) + { + return Utils::FileSystem::getGenericPath(getScrapersResouceDir() + "/" + file_name); + } -void ensureScrapersResourcesDir() -{ - std::string path = getScrapersResouceDir(); - if (!Utils::FileSystem::exists(path)) - Utils::FileSystem::createDirectory(path); -} + void ensureScrapersResourcesDir() + { + std::string path = getScrapersResouceDir(); + if (!Utils::FileSystem::exists(path)) + Utils::FileSystem::createDirectory(path); + } } // namespace std::string getScrapersResouceDir() { - return Utils::FileSystem::getGenericPath( - Utils::FileSystem::getHomePath() + "/.emulationstation/" + SCRAPER_RESOURCES_DIR); + return Utils::FileSystem::getGenericPath(Utils::FileSystem::getHomePath() + + "/.emulationstation/" + SCRAPER_RESOURCES_DIR); } std::string TheGamesDBJSONRequestResources::getApiKey() const { return GamesDBAPIKey; } @@ -69,16 +70,16 @@ void TheGamesDBJSONRequestResources::prepare() if (checkLoaded()) return; - if (loadResource(gamesdb_new_developers_map, "developers", - genFilePath(DEVELOPERS_JSON_FILE)) && !gamesdb_developers_resource_request) + if (loadResource(gamesdb_new_developers_map, "developers", genFilePath(DEVELOPERS_JSON_FILE)) && + !gamesdb_developers_resource_request) gamesdb_developers_resource_request = fetchResource(DEVELOPERS_ENDPOINT); - if (loadResource(gamesdb_new_publishers_map, "publishers", - genFilePath(PUBLISHERS_JSON_FILE)) && !gamesdb_publishers_resource_request) + if (loadResource(gamesdb_new_publishers_map, "publishers", genFilePath(PUBLISHERS_JSON_FILE)) && + !gamesdb_publishers_resource_request) gamesdb_publishers_resource_request = fetchResource(PUBLISHERS_ENDPOINT); - if (loadResource(gamesdb_new_genres_map, "genres", - genFilePath(GENRES_JSON_FILE)) && !gamesdb_genres_resource_request) + if (loadResource(gamesdb_new_genres_map, "genres", genFilePath(GENRES_JSON_FILE)) && + !gamesdb_genres_resource_request) gamesdb_genres_resource_request = fetchResource(GENRES_ENDPOINT); } @@ -90,21 +91,22 @@ void TheGamesDBJSONRequestResources::ensureResources() for (int i = 0; i < MAX_WAIT_ITER; i++) { if (gamesdb_developers_resource_request && - saveResource(gamesdb_developers_resource_request.get(), - gamesdb_new_developers_map, "developers", genFilePath(DEVELOPERS_JSON_FILE))) + saveResource(gamesdb_developers_resource_request.get(), gamesdb_new_developers_map, + "developers", genFilePath(DEVELOPERS_JSON_FILE))) gamesdb_developers_resource_request.reset(nullptr); if (gamesdb_publishers_resource_request && - saveResource(gamesdb_publishers_resource_request.get(), - gamesdb_new_publishers_map, "publishers", genFilePath(PUBLISHERS_JSON_FILE))) + saveResource(gamesdb_publishers_resource_request.get(), gamesdb_new_publishers_map, + "publishers", genFilePath(PUBLISHERS_JSON_FILE))) gamesdb_publishers_resource_request.reset(nullptr); - if (gamesdb_genres_resource_request && saveResource(gamesdb_genres_resource_request.get(), - gamesdb_new_genres_map, "genres", genFilePath(GENRES_JSON_FILE))) + if (gamesdb_genres_resource_request && + saveResource(gamesdb_genres_resource_request.get(), gamesdb_new_genres_map, "genres", + genFilePath(GENRES_JSON_FILE))) gamesdb_genres_resource_request.reset(nullptr); if (!gamesdb_developers_resource_request && !gamesdb_publishers_resource_request && - !gamesdb_genres_resource_request) + !gamesdb_genres_resource_request) return; std::this_thread::sleep_for(std::chrono::milliseconds(POLL_TIME_MS)); @@ -115,14 +117,13 @@ void TheGamesDBJSONRequestResources::ensureResources() bool TheGamesDBJSONRequestResources::checkLoaded() { return !gamesdb_new_genres_map.empty() && !gamesdb_new_developers_map.empty() && - !gamesdb_new_publishers_map.empty(); + !gamesdb_new_publishers_map.empty(); } -bool TheGamesDBJSONRequestResources::saveResource( - HttpReq* req, - std::unordered_map& resource, - const std::string& resource_name, - const std::string& file_name) +bool TheGamesDBJSONRequestResources::saveResource(HttpReq* req, + std::unordered_map& resource, + const std::string& resource_name, + const std::string& file_name) { if (req == nullptr) { @@ -133,8 +134,8 @@ bool TheGamesDBJSONRequestResources::saveResource( return false; // Not ready: wait some more. } if (req->status() != HttpReq::REQ_SUCCESS) { - LOG(LogError) << "Resource request for " << file_name << - " failed:\n\t" << req->getErrorMsg(); + LOG(LogError) << "Resource request for " << file_name << " failed:\n\t" + << req->getErrorMsg(); return true; // Request failed, resetting request.. } @@ -156,10 +157,9 @@ std::unique_ptr TheGamesDBJSONRequestResources::fetchResource(const std return std::unique_ptr(new HttpReq(path)); } -int TheGamesDBJSONRequestResources::loadResource( - std::unordered_map& resource, - const std::string& resource_name, - const std::string& file_name) +int TheGamesDBJSONRequestResources::loadResource(std::unordered_map& resource, + const std::string& resource_name, + const std::string& file_name) { std::ifstream fin(file_name); if (!fin.good()) @@ -172,8 +172,8 @@ int TheGamesDBJSONRequestResources::loadResource( if (doc.HasParseError()) { std::string err = std::string("TheGamesDBJSONRequest - " - "Error parsing JSON for resource file ") + file_name + - ":\n\t" + GetParseError_En(doc.GetParseError()); + "Error parsing JSON for resource file ") + + file_name + ":\n\t" + GetParseError_En(doc.GetParseError()); LOG(LogError) << err; return 1; } @@ -189,7 +189,7 @@ int TheGamesDBJSONRequestResources::loadResource( for (Value::ConstMemberIterator itr = data.MemberBegin(); itr != data.MemberEnd(); itr++) { auto& entry = itr->value; if (!entry.IsObject() || !entry.HasMember("id") || !entry["id"].IsInt() || - !entry.HasMember("name") || !entry["name"].IsString()) + !entry.HasMember("name") || !entry["name"].IsString()) continue; resource[entry["id"].GetInt()] = entry["name"].GetString(); diff --git a/es-app/src/scrapers/GamesDBJSONScraperResources.h b/es-app/src/scrapers/GamesDBJSONScraperResources.h index ba6e7e614..631b85cc6 100644 --- a/es-app/src/scrapers/GamesDBJSONScraperResources.h +++ b/es-app/src/scrapers/GamesDBJSONScraperResources.h @@ -33,20 +33,18 @@ struct TheGamesDBJSONRequestResources { std::unordered_map gamesdb_new_publishers_map; std::unordered_map gamesdb_new_genres_map; - private: +private: bool checkLoaded(); - bool saveResource( - HttpReq* req, - std::unordered_map& resource, - const std::string& resource_name, - const std::string& file_name); - std::unique_ptr fetchResource(const std::string& endpoint); + bool saveResource(HttpReq* req, + std::unordered_map& resource, + const std::string& resource_name, + const std::string& file_name); + std::unique_ptr fetchResource(const std::string& endpoint); - int loadResource( - std::unordered_map& resource, - const std::string& resource_name, - const std::string& file_name); + int loadResource(std::unordered_map& resource, + const std::string& resource_name, + const std::string& file_name); std::unique_ptr gamesdb_developers_resource_request; std::unique_ptr gamesdb_publishers_resource_request; diff --git a/es-app/src/scrapers/Scraper.cpp b/es-app/src/scrapers/Scraper.cpp index 97db18bb9..234fd8a6a 100644 --- a/es-app/src/scrapers/Scraper.cpp +++ b/es-app/src/scrapers/Scraper.cpp @@ -10,20 +10,20 @@ #include "scrapers/Scraper.h" -#include "utils/StringUtil.h" #include "FileData.h" #include "GamesDBJSONScraper.h" #include "Log.h" #include "ScreenScraper.h" #include "Settings.h" #include "SystemData.h" +#include "utils/StringUtil.h" #if defined(_WIN64) #include "views/ViewController.h" #endif -#include #include +#include #include const std::map scraper_request_funcs { @@ -41,9 +41,9 @@ std::unique_ptr startScraperSearch(const ScraperSearchParam LOG(LogError) << "Configured scraper (" << name << ") unavailable, scraping aborted"; } else { - LOG(LogDebug) << "Scraper::startScraperSearch(): Scraping system \"" << - params.system->getName() << "\", game file \"" << - params.game->getFileName() << "\""; + LOG(LogDebug) << "Scraper::startScraperSearch(): Scraping system \"" + << params.system->getName() << "\", game file \"" + << params.game->getFileName() << "\""; scraper_request_funcs.at(name)(params, handle->mRequestQueue, handle->mResults); } @@ -84,12 +84,6 @@ bool isValidConfiguredScraper() return scraper_request_funcs.find(name) != scraper_request_funcs.end(); } -// ScraperSearchHandle. -ScraperSearchHandle::ScraperSearchHandle() -{ - setStatus(ASYNC_IN_PROGRESS); -} - void ScraperSearchHandle::update() { if (mStatus == ASYNC_DONE) @@ -128,13 +122,14 @@ void ScraperSearchHandle::update() // ScraperRequest. ScraperRequest::ScraperRequest(std::vector& resultsWrite) - : mResults(resultsWrite) + : mResults(resultsWrite) { } // ScraperHttpRequest. ScraperHttpRequest::ScraperHttpRequest(std::vector& resultsWrite, - const std::string& url) : ScraperRequest(resultsWrite) + const std::string& url) + : ScraperRequest(resultsWrite) { setStatus(ASYNC_IN_PROGRESS); mReq = std::unique_ptr(new HttpReq(url)); @@ -155,20 +150,21 @@ void ScraperHttpRequest::update() return; // Everything else is some sort of error. - LOG(LogError) << "ScraperHttpRequest network error (status: " << status<< ") - " - << mReq->getErrorMsg(); + LOG(LogError) << "ScraperHttpRequest network error (status: " << status << ") - " + << mReq->getErrorMsg(); setError("Network error: " + mReq->getErrorMsg()); } // Download and write the media files to disk. std::unique_ptr resolveMetaDataAssets(const ScraperSearchResult& result, - const ScraperSearchParams& search) + const ScraperSearchParams& search) { return std::unique_ptr(new MDResolveHandle(result, search)); } MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, - const ScraperSearchParams& search) : mResult(result) + const ScraperSearchParams& search) + : mResult(result) { struct mediaFileInfoStruct { std::string fileURL; @@ -221,10 +217,10 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, mediaFileInfo.existingMediaFile = search.game->getVideoPath(); mediaFileInfo.resizeFile = false; scrapeFiles.push_back(mediaFileInfo); - #if defined(_WIN64) +#if defined(_WIN64) // Required due to the idiotic file locking that exists on this operating system. ViewController::get()->onStopVideo(); - #endif +#endif } for (auto it = scrapeFiles.cbegin(); it != scrapeFiles.cend(); it++) { @@ -248,13 +244,12 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, // If there is an existing media file on disk and the setting to overwrite data // has been set to no, then don't proceed with downloading or saving a new file. if (it->existingMediaFile != "" && - !Settings::getInstance()->getBool("ScraperOverwriteData")) + !Settings::getInstance()->getBool("ScraperOverwriteData")) continue; // If the image is cached already as the thumbnail, then we don't need // to download it again, in this case just save it to disk and resize it. - if (mResult.thumbnailImageUrl == it->fileURL && - mResult.thumbnailImageData.size() > 0) { + if (mResult.thumbnailImageUrl == it->fileURL && mResult.thumbnailImageData.size() > 0) { // This is just a temporary workaround to avoid saving media files to disk that // are actually just containing error messages from the scraper service. The @@ -265,19 +260,19 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, // are sometimes returned from the scraper service and these can actually be // less than 350 bytes in size. if (Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia") && - mResult.thumbnailImageData.size() < 350) { + mResult.thumbnailImageData.size() < 350) { - FIMEMORY* memoryStream = FreeImage_OpenMemory( - reinterpret_cast(&mResult.thumbnailImageData.at(0)), - static_cast(mResult.thumbnailImageData.size())); + FIMEMORY* memoryStream = + FreeImage_OpenMemory(reinterpret_cast(&mResult.thumbnailImageData.at(0)), + static_cast(mResult.thumbnailImageData.size())); FREE_IMAGE_FORMAT imageFormat = FreeImage_GetFileTypeFromMemory(memoryStream, 0); FreeImage_CloseMemory(memoryStream); if (imageFormat == FIF_UNKNOWN) { setError("The file \"" + Utils::FileSystem::getFileName(filePath) + - "\" returned by the scraper seems to be invalid as it's less than " + - "350 bytes in size"); + "\" returned by the scraper seems to be invalid as it's less than " + + "350 bytes in size"); return; } } @@ -293,18 +288,18 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, // problems or the MediaDirectory setting points to a file instead of a directory. if (!Utils::FileSystem::isDirectory(Utils::FileSystem::getParent(filePath))) { setError("Media directory does not exist and can't be created. " - "Permission problems?"); - LOG(LogError) << "Couldn't create media directory: \"" << - Utils::FileSystem::getParent(filePath) << "\""; - return; + "Permission problems?"); + LOG(LogError) << "Couldn't create media directory: \"" + << Utils::FileSystem::getParent(filePath) << "\""; + return; } - #if defined(_WIN64) +#if defined(_WIN64) std::ofstream stream(Utils::String::stringToWideString(filePath).c_str(), - std::ios_base::out | std::ios_base::binary); - #else + std::ios_base::out | std::ios_base::binary); +#else std::ofstream stream(filePath, std::ios_base::out | std::ios_base::binary); - #endif +#endif if (!stream || stream.bad()) { setError("Failed to open path for writing media file.\nPermission error?"); return; @@ -331,8 +326,9 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, // If it's not cached, then initiate the download. else { mFuncs.push_back(ResolvePair(downloadMediaAsync(it->fileURL, filePath, - it->existingMediaFile, it->subDirectory, it->resizeFile, mResult.savedNewMedia), - [this, filePath] {})); + it->existingMediaFile, it->subDirectory, + it->resizeFile, mResult.savedNewMedia), + [this, filePath] {})); } } } @@ -361,37 +357,30 @@ void MDResolveHandle::update() setStatus(ASYNC_DONE); } -std::unique_ptr downloadMediaAsync( - const std::string& url, - const std::string& saveAs, - const std::string& existingMediaPath, - const std::string& mediaType, - const bool resizeFile, - bool& savedNewMedia) +std::unique_ptr downloadMediaAsync(const std::string& url, + const std::string& saveAs, + const std::string& existingMediaPath, + const std::string& mediaType, + const bool resizeFile, + bool& savedNewMedia) { return std::unique_ptr(new MediaDownloadHandle( - url, - saveAs, - existingMediaPath, - mediaType, - resizeFile, - savedNewMedia)); + url, saveAs, existingMediaPath, mediaType, resizeFile, savedNewMedia)); } -MediaDownloadHandle::MediaDownloadHandle( - const std::string& url, - const std::string& path, - const std::string& existingMediaPath, - const std::string& mediaType, - const bool resizeFile, - bool& savedNewMedia) - : mSavePath(path), - mExistingMediaFile(existingMediaPath), - mMediaType(mediaType), - mResizeFile(resizeFile), - mReq(new HttpReq(url)) +MediaDownloadHandle::MediaDownloadHandle(const std::string& url, + const std::string& path, + const std::string& existingMediaPath, + const std::string& mediaType, + const bool resizeFile, + bool& savedNewMedia) + : mSavePath(path) + , mExistingMediaFile(existingMediaPath) + , mMediaType(mediaType) + , mResizeFile(resizeFile) + , mReq(new HttpReq(url)) { - mSavedNewMediaPtr = &savedNewMedia; + mSavedNewMediaPtr = &savedNewMedia; } void MediaDownloadHandle::update() @@ -421,25 +410,22 @@ void MediaDownloadHandle::update() // Black/empty images are sometimes returned from the scraper service and these can actually // be less than 350 bytes in size. if (Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia") && - mReq->getContent().size() < 350) { + mReq->getContent().size() < 350) { FREE_IMAGE_FORMAT imageFormat = FIF_UNKNOWN; if (mMediaType != "videos") { std::string imageData = mReq->getContent(); - - FIMEMORY* memoryStream = FreeImage_OpenMemory( - reinterpret_cast(&imageData.at(0)), - static_cast(imageData.size())); - + FIMEMORY* memoryStream = FreeImage_OpenMemory(reinterpret_cast(&imageData.at(0)), + static_cast(imageData.size())); imageFormat = FreeImage_GetFileTypeFromMemory(memoryStream, 0); FreeImage_CloseMemory(memoryStream); } if (imageFormat == FIF_UNKNOWN) { setError("The file \"" + Utils::FileSystem::getFileName(mSavePath) + - "\" returned by the scraper seems to be invalid as it's less than " + - "350 bytes in size"); + "\" returned by the scraper seems to be invalid as it's less than " + + "350 bytes in size"); return; } } @@ -455,17 +441,17 @@ void MediaDownloadHandle::update() // problems or the MediaDirectory setting points to a file instead of a directory. if (!Utils::FileSystem::isDirectory(Utils::FileSystem::getParent(mSavePath))) { setError("Media directory does not exist and can't be created. Permission problems?"); - LOG(LogError) << "Couldn't create media directory: \"" << - Utils::FileSystem::getParent(mSavePath) << "\""; + LOG(LogError) << "Couldn't create media directory: \"" + << Utils::FileSystem::getParent(mSavePath) << "\""; return; } - #if defined(_WIN64) +#if defined(_WIN64) std::ofstream stream(Utils::String::stringToWideString(mSavePath).c_str(), - std::ios_base::out | std::ios_base::binary); - #else + std::ios_base::out | std::ios_base::binary); +#else std::ofstream stream(mSavePath, std::ios_base::out | std::ios_base::binary); - #endif +#endif if (!stream || stream.bad()) { setError("Failed to open path for writing media file.\nPermission error?"); return; @@ -512,15 +498,16 @@ bool resizeImage(const std::string& path, const std::string& mediaType) FIBITMAP* image = nullptr; // Detect the file format. - #if defined(_WIN64) + +#if defined(_WIN64) format = FreeImage_GetFileTypeU(Utils::String::stringToWideString(path).c_str(), 0); if (format == FIF_UNKNOWN) format = FreeImage_GetFIFFromFilenameU(Utils::String::stringToWideString(path).c_str()); - #else +#else format = FreeImage_GetFileType(path.c_str(), 0); if (format == FIF_UNKNOWN) format = FreeImage_GetFIFFromFilename(path.c_str()); - #endif +#endif if (format == FIF_UNKNOWN) { LOG(LogError) << "Could not detect filetype for image \"" << path << "\"!"; return false; @@ -528,11 +515,11 @@ bool resizeImage(const std::string& path, const std::string& mediaType) // Make sure we can read this format, and if so, then load it. if (FreeImage_FIFSupportsReading(format)) { - #if defined(_WIN64) +#if defined(_WIN64) image = FreeImage_LoadU(format, Utils::String::stringToWideString(path).c_str()); - #else +#else image = FreeImage_Load(format, path.c_str()); - #endif +#endif } else { LOG(LogError) << "File format not supported for image \"" << path << "\""; @@ -545,8 +532,8 @@ bool resizeImage(const std::string& path, const std::string& mediaType) // If the image is smaller than (or the same size as) maxWidth and maxHeight, then don't // do any scaling. It doesn't make sense to upscale the image and waste disk space. if (maxWidth >= width && maxHeight >= height) { - LOG(LogDebug) << "Scraper::resizeImage(): Saving image \"" << path << - "\" at its original resolution " << width << "x" << height; + LOG(LogDebug) << "Scraper::resizeImage(): Saving image \"" << path + << "\" at its original resolution " << width << "x" << height; FreeImage_Unload(image); return true; } @@ -568,7 +555,7 @@ bool resizeImage(const std::string& path, const std::string& mediaType) // We use Lanczos3 which is the highest quality resampling method available in FreeImage. FIBITMAP* imageRescaled = FreeImage_Rescale(image, static_cast(maxWidth), - static_cast(maxHeight), FILTER_LANCZOS3); + static_cast(maxHeight), FILTER_LANCZOS3); FreeImage_Unload(image); if (imageRescaled == nullptr) { @@ -576,12 +563,12 @@ bool resizeImage(const std::string& path, const std::string& mediaType) return false; } - #if defined(_WIN64) +#if defined(_WIN64) bool saved = (FreeImage_SaveU(format, imageRescaled, - Utils::String::stringToWideString(path).c_str()) != 0); - #else + Utils::String::stringToWideString(path).c_str()) != 0); +#else bool saved = (FreeImage_Save(format, imageRescaled, path.c_str()) != 0); - #endif +#endif FreeImage_Unload(imageRescaled); if (!saved) { @@ -589,14 +576,15 @@ bool resizeImage(const std::string& path, const std::string& mediaType) } else { LOG(LogDebug) << "Scraper::resizeImage(): Downscaled image \"" << path << "\" from " - << width << "x" << height << " to " << maxWidth << "x" << maxHeight; + << width << "x" << height << " to " << maxWidth << "x" << maxHeight; } return saved; } std::string getSaveAsPath(const ScraperSearchParams& params, - const std::string& filetypeSubdirectory, const std::string& extension) + const std::string& filetypeSubdirectory, + const std::string& extension) { const std::string systemsubdirectory = params.system->getName(); const std::string name = Utils::FileSystem::getStem(params.game->getPath()); @@ -605,7 +593,7 @@ std::string getSaveAsPath(const ScraperSearchParams& params, // Extract possible subfolders from the path. if (params.system->getSystemEnvData()->mStartPath != "") subFolders = Utils::String::replace(Utils::FileSystem::getParent(params.game->getPath()), - params.system->getSystemEnvData()->mStartPath, ""); + params.system->getSystemEnvData()->mStartPath, ""); std::string path = FileData::getMediaDirectory(); diff --git a/es-app/src/scrapers/Scraper.h b/es-app/src/scrapers/Scraper.h index 90b76b4dd..4218effc4 100644 --- a/es-app/src/scrapers/Scraper.h +++ b/es-app/src/scrapers/Scraper.h @@ -27,7 +27,7 @@ class FileData; class SystemData; enum downloadStatus { - NOT_STARTED, + NOT_STARTED, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). IN_PROGRESS, COMPLETED }; @@ -40,7 +40,10 @@ struct ScraperSearchParams { }; struct ScraperSearchResult { - ScraperSearchResult() : mdl(GAME_METADATA) {}; + ScraperSearchResult() + : mdl(GAME_METADATA) + { + } MetaDataList mdl; std::string gameID; @@ -73,34 +76,6 @@ struct ScraperSearchResult { bool savedNewMedia; }; -// So let me explain why I've abstracted this so heavily. -// There are two ways I can think of that you'd want to write a scraper. - -// 1. Do some HTTP request(s) -> process it -> return the results. -// 2. Do some local filesystem queries (an offline scraper) -> return the results. - -// The first way needs to be asynchronous while it's waiting for the HTTP request to return. -// The second doesn't. - -// It would be nice if we could write it like this: -// search = generate_http_request(searchparams); -// wait_until_done(search); -// ... process search ... -// return results; - -// We could do this if we used threads. Right now ES doesn't because I'm pretty sure I'll -// fuck it up, and I'm not sure of the performance of threads on the Pi (single-core ARM). -// We could also do this if we used coroutines. -// I can't find a really good cross-platform coroutine library (x86/64/ARM Linux + Windows), -// and I don't want to spend more time chasing libraries than just writing it the long way once. - -// So, I did it the "long" way. -// ScraperSearchHandle - one logical search, e.g. "search for mario". -// ScraperRequest - encapsulates some sort of asynchronous request that will ultimately -// return some results. -// ScraperHttpRequest - implementation of ScraperRequest that waits on an HttpReq, then -// processes it with some processing function. - // A scraper search gathers results from (potentially multiple) ScraperRequests. class ScraperRequest : public AsyncHandle { @@ -123,7 +98,7 @@ public: protected: virtual void process(const std::unique_ptr& req, - std::vector& results) = 0; + std::vector& results) = 0; private: std::unique_ptr mReq; @@ -133,21 +108,20 @@ private: class ScraperSearchHandle : public AsyncHandle { public: - ScraperSearchHandle(); + ScraperSearchHandle() { setStatus(ASYNC_IN_PROGRESS); } void update(); - inline const std::vector& getResults() const + const std::vector& getResults() const { assert(mStatus != ASYNC_IN_PROGRESS); return mResults; } protected: - friend std::unique_ptr - startScraperSearch(const ScraperSearchParams& params); + friend std::unique_ptr startScraperSearch( + const ScraperSearchParams& params); - friend std::unique_ptr - startMediaURLsFetch(const std::string& gameIDs); + friend std::unique_ptr startMediaURLsFetch(const std::string& gameIDs); std::queue> mRequestQueue; std::vector mResults; @@ -164,9 +138,10 @@ std::vector getScraperList(); // Returns true if the scraper configured in the settings is still valid. bool isValidConfiguredScraper(); -typedef void (*generate_scraper_requests_func)(const ScraperSearchParams& params, - std::queue>& requests, - std::vector& results); +typedef void (*generate_scraper_requests_func)( + const ScraperSearchParams& params, + std::queue>& requests, + std::vector& results); // ------------------------------------------------------------------------- @@ -177,8 +152,11 @@ public: MDResolveHandle(const ScraperSearchResult& result, const ScraperSearchParams& search); void update() override; - inline const ScraperSearchResult& getResult() const - { assert(mStatus == ASYNC_DONE); return mResult; } + const ScraperSearchResult& getResult() const + { + assert(mStatus == ASYNC_DONE); + return mResult; + } bool getSavedNewMedia() { return mResult.savedNewMedia; } private: @@ -191,13 +169,12 @@ private: class MediaDownloadHandle : public AsyncHandle { public: - MediaDownloadHandle( - const std::string& url, - const std::string& path, - const std::string& existingMediaPath, - const std::string& mediaType, - const bool resizeFile, - bool& savedNewMedia); + MediaDownloadHandle(const std::string& url, + const std::string& path, + const std::string& existingMediaPath, + const std::string& mediaType, + const bool resizeFile, + bool& savedNewMedia); void update() override; @@ -207,26 +184,26 @@ private: std::string mExistingMediaFile; std::string mMediaType; bool mResizeFile; - bool *mSavedNewMediaPtr; + bool* mSavedNewMediaPtr; }; // Downloads to the home directory, using this subdirectory structure: // ".emulationstation/downloaded_media/[system_name]/[media_type]/[game_name].[file_extension]". // The subdirectories are automatically created if they do not exist. std::string getSaveAsPath(const ScraperSearchParams& params, - const std::string& filetypeSubdirectory, const std::string& url); + const std::string& filetypeSubdirectory, + const std::string& url); -std::unique_ptr downloadMediaAsync( - const std::string& url, - const std::string& saveAs, - const std::string& existingMediaPath, - const std::string& mediaType, - const bool resizeFile, - bool& savedNewMedia); +std::unique_ptr downloadMediaAsync(const std::string& url, + const std::string& saveAs, + const std::string& existingMediaPath, + const std::string& mediaType, + const bool resizeFile, + bool& savedNewMedia); // Resolves all metadata assets that need to be downloaded. std::unique_ptr resolveMetaDataAssets(const ScraperSearchResult& result, - const ScraperSearchParams& search); + const ScraperSearchParams& search); bool resizeImage(const std::string& path, const std::string& mediaType); diff --git a/es-app/src/scrapers/ScreenScraper.cpp b/es-app/src/scrapers/ScreenScraper.cpp index 01d38b4ff..cf642aec5 100644 --- a/es-app/src/scrapers/ScreenScraper.cpp +++ b/es-app/src/scrapers/ScreenScraper.cpp @@ -9,14 +9,14 @@ #include "scrapers/ScreenScraper.h" -#include "math/Misc.h" -#include "utils/StringUtil.h" -#include "utils/TimeUtil.h" #include "FileData.h" #include "Log.h" #include "PlatformId.h" #include "Settings.h" #include "SystemData.h" +#include "math/Misc.h" +#include "utils/StringUtil.h" +#include "utils/TimeUtil.h" #include #include @@ -42,7 +42,7 @@ const std::map screenscraper_platformid_map { { ATARI_JAGUAR, 27 }, { ATARI_JAGUAR_CD, 171 }, { ATARI_LYNX, 28 }, - { ATARI_ST, 42}, + { ATARI_ST, 42 }, { ATARI_XE, 43 }, { ATOMISWAVE, 53 }, { BBC_MICRO, 37 }, @@ -62,7 +62,7 @@ const std::map screenscraper_platformid_map { { MSX_TURBO_R, 118 }, { SNK_NEO_GEO, 142 }, { SNK_NEO_GEO_CD, 142 }, - { SNK_NEO_GEO_POCKET, 25}, + { SNK_NEO_GEO_POCKET, 25 }, { SNK_NEO_GEO_POCKET_COLOR, 82 }, { NINTENDO_3DS, 17 }, { NINTENDO_64, 14 }, @@ -88,7 +88,7 @@ const std::map screenscraper_platformid_map { { NEC_PCFX, 72 }, { GAMEENGINE_OPENBOR, 214 }, { TANGERINE_ORIC, 131 }, - { GAMEENGINE_SCUMMVM, 123}, + { GAMEENGINE_SCUMMVM, 123 }, { SEGA_32X, 19 }, { SEGA_CD, 20 }, { SEGA_DREAMCAST, 23 }, @@ -98,8 +98,8 @@ const std::map screenscraper_platformid_map { { SEGA_MEGA_DRIVE, 1 }, { SEGA_SATURN, 22 }, { SEGA_SG1000, 109 }, - { SHARP_X1, 220}, - { SHARP_X68000, 79}, + { SHARP_X1, 220 }, + { SHARP_X68000, 79 }, { GAMEENGINE_SOLARUS, 223 }, { SONY_PLAYSTATION, 57 }, { SONY_PLAYSTATION_2, 58 }, @@ -110,8 +110,8 @@ const std::map screenscraper_platformid_map { { SUPER_NINTENDO, 4 }, { NEC_SUPERGRAFX, 105 }, { GAMEENGINE_TIC80, 222 }, - { NEC_PC_8800, 221}, - { NEC_PC_9800, 208}, + { NEC_PC_8800, 221 }, + { NEC_PC_9800, 208 }, { NEC_PC_ENGINE, 31 }, { NEC_PC_ENGINE_CD, 114 }, { BANDAI_WONDERSWAN, 45 }, @@ -131,7 +131,8 @@ const std::map screenscraper_platformid_map { // Helper XML parsing method, finding a node-by-name recursively. pugi::xml_node find_node_by_name_re(const pugi::xml_node& node, - const std::vector node_names) { + const std::vector node_names) +{ for (const std::string& _val : node_names) { pugi::xpath_query query_node_name((static_cast("//") + _val).c_str()); @@ -147,8 +148,9 @@ pugi::xml_node find_node_by_name_re(const pugi::xml_node& node, // Help XML parsing method, finding an direct child XML node starting from the parent and // filtering by an attribute value list. pugi::xml_node find_child_by_attribute_list(const pugi::xml_node& node_parent, - const std::string& node_name, const std::string& attribute_name, - const std::vector attribute_values) + const std::string& node_name, + const std::string& attribute_name, + const std::vector attribute_values) { for (auto _val : attribute_values) { for (pugi::xml_node node : node_parent.children(node_name.c_str())) { @@ -161,22 +163,22 @@ pugi::xml_node find_child_by_attribute_list(const pugi::xml_node& node_parent, } void screenscraper_generate_scraper_requests(const ScraperSearchParams& params, - std::queue>& requests, - std::vector& results) + std::queue>& requests, + std::vector& results) { std::string path; ScreenScraperRequest::ScreenScraperConfig ssConfig; if (params.game->isArcadeGame()) - ssConfig.isArcadeSystem = true; + ssConfig.isArcadeSystem = true; else ssConfig.isArcadeSystem = false; if (params.nameOverride == "") { if (Settings::getInstance()->getBool("ScraperSearchMetadataName")) path = ssConfig.getGameSearchUrl( - Utils::String::removeParenthesis(params.game->metadata.get("name"))); + Utils::String::removeParenthesis(params.game->metadata.get("name"))); else path = ssConfig.getGameSearchUrl(params.game->getCleanName()); } @@ -195,19 +197,19 @@ void screenscraper_generate_scraper_requests(const ScraperSearchParams& params, p_ids.push_back(mapIt->second); } else { - LOG(LogWarning) << "ScreenScraper: No support for platform \"" << - getPlatformName(*platformIt) << "\", search will be inaccurate"; + LOG(LogWarning) << "ScreenScraper: No support for platform \"" + << getPlatformName(*platformIt) << "\", search will be inaccurate"; // Add the scrape request without a platform/system ID. - requests.push(std::unique_ptr - (new ScreenScraperRequest(requests, results, path))); + requests.push( + std::unique_ptr(new ScreenScraperRequest(requests, results, path))); } } if (p_ids.size() == 0) { LOG(LogWarning) << "ScreenScraper: No platform defined, search will be inaccurate"; // Add the scrape request without a platform/system ID. - requests.push(std::unique_ptr - (new ScreenScraperRequest(requests, results, path))); + requests.push( + std::unique_ptr(new ScreenScraperRequest(requests, results, path))); } // Sort the platform IDs and remove duplicates. @@ -218,13 +220,13 @@ void screenscraper_generate_scraper_requests(const ScraperSearchParams& params, for (auto platform = p_ids.cbegin(); platform != p_ids.cend(); platform++) { path += "&systemeid="; path += HttpReq::urlEncode(std::to_string(*platform)); - requests.push(std::unique_ptr - (new ScreenScraperRequest(requests, results, path))); + requests.push( + std::unique_ptr(new ScreenScraperRequest(requests, results, path))); } } void ScreenScraperRequest::process(const std::unique_ptr& req, - std::vector& results) + std::vector& results) { assert(req->status() == HttpReq::REQ_SUCCESS); @@ -263,7 +265,7 @@ void ScreenScraperRequest::process(const std::unique_ptr& req, std::string gameName = Utils::String::toUpper((*it).mdl.get("name")); if (gameName.substr(0, 12) == "ZZZ(NOTGAME)") { LOG(LogWarning) << "ScreenScraperRequest - Received \"ZZZ(notgame)\" as game name, " - "ignoring response"; + "ignoring response"; results.pop_back(); return; } @@ -271,7 +273,7 @@ void ScreenScraperRequest::process(const std::unique_ptr& req, } void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, - std::vector& out_results) + std::vector& out_results) { pugi::xml_node data = xmldoc.child("Data"); @@ -280,17 +282,18 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, // also seems to correlate with missing scraper allowance data. This is however a scraper // service issue so we're not attempting to compensate for it here. if (Settings::getInstance()->getBool("ScraperUseAccountScreenScraper") && - Settings::getInstance()->getString("ScraperUsernameScreenScraper") != "" && - Settings::getInstance()->getString("ScraperPasswordScreenScraper") != "") { + Settings::getInstance()->getString("ScraperUsernameScreenScraper") != "" && + Settings::getInstance()->getString("ScraperPasswordScreenScraper") != "") { std::string userID = data.child("ssuser").child("id").text().get(); if (userID != "") { - LOG(LogDebug) << "ScreenScraperRequest::processGame(): Scraping using account \"" << - userID << "\""; + LOG(LogDebug) << "ScreenScraperRequest::processGame(): Scraping using account \"" + << userID << "\""; } else { - LOG(LogDebug) << "ScreenScraperRequest::processGame(): The configured account '" << - Settings::getInstance()->getString("ScraperUsernameScreenScraper") << - "' was not included in the scraper response, wrong username or password?"; + LOG(LogDebug) + << "ScreenScraperRequest::processGame(): The configured account '" + << Settings::getInstance()->getString("ScraperUsernameScreenScraper") + << "' was not included in the scraper response, wrong username or password?"; } } @@ -304,17 +307,17 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, // Scraping allowance. if (maxRequestsPerDay > 0) { - LOG(LogDebug) << "ScreenScraperRequest::processGame(): Daily scraping allowance: " << - requestsToday << "/" << maxRequestsPerDay << " (" << - scraperRequestAllowance << " remaining)"; + LOG(LogDebug) << "ScreenScraperRequest::processGame(): Daily scraping allowance: " + << requestsToday << "/" << maxRequestsPerDay << " (" + << scraperRequestAllowance << " remaining)"; } else { LOG(LogDebug) << "ScreenScraperRequest::processGame(): Daily scraping allowance: " - "No statistics were provided with the response"; + "No statistics were provided with the response"; } if (data.child("jeux")) - data = data.child("jeux"); + data = data.child("jeux"); for (pugi::xml_node game = data.child("jeu"); game; game = game.next_sibling("jeu")) { ScraperSearchResult result; @@ -324,13 +327,16 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, result.gameID = game.attribute("id").as_string(); std::string region = - Utils::String::toLower(Settings::getInstance()->getString("ScraperRegion")); + Utils::String::toLower(Settings::getInstance()->getString("ScraperRegion")); std::string language = - Utils::String::toLower(Settings::getInstance()->getString("ScraperLanguage")); + Utils::String::toLower(Settings::getInstance()->getString("ScraperLanguage")); // Name fallback: US, WOR(LD). (Xpath: Data/jeu[0]/noms/nom[*]). - result.mdl.set("name", find_child_by_attribute_list(game.child("noms"), - "nom", "region", { region, "wor", "us" , "ss", "eu", "jp" }).text().get()); + result.mdl.set("name", + find_child_by_attribute_list(game.child("noms"), "nom", "region", + { region, "wor", "us", "ss", "eu", "jp" }) + .text() + .get()); LOG(LogDebug) << "ScreenScraperRequest::processGame(): Name: " << result.mdl.get("name"); // Validate rating. @@ -346,14 +352,16 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, ss << ratingVal; if (ratingVal > 0) { result.mdl.set("rating", ss.str()); - LOG(LogDebug) << "ScreenScraperRequest::processGame(): Rating: " << - result.mdl.get("rating"); + LOG(LogDebug) << "ScreenScraperRequest::processGame(): Rating: " + << result.mdl.get("rating"); } } // Description fallback language: EN, WOR(LD). - std::string description = find_child_by_attribute_list(game.child("synopsis"), - "synopsis", "langue", { language, "en", "wor" }).text().get(); + std::string description = find_child_by_attribute_list(game.child("synopsis"), "synopsis", + "langue", { language, "en", "wor" }) + .text() + .get(); // Translate some HTML character codes to UTF-8 characters. if (!description.empty()) { @@ -362,80 +370,83 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, } // Get the date proper. The API returns multiple 'date' children nodes to the 'dates' - // main child of 'jeu'. - // Date fallback: WOR(LD), US, SS, JP, EU. + // main child of 'jeu'. Date fallback: WOR(LD), US, SS, JP, EU. std::string _date = find_child_by_attribute_list(game.child("dates"), "date", "region", - { region, "wor", "us", "ss", "jp", "eu" }).text().get(); + { region, "wor", "us", "ss", "jp", "eu" }) + .text() + .get(); // Date can be YYYY-MM-DD or just YYYY. if (_date.length() > 4) { - result.mdl.set("releasedate", Utils::Time::DateTime( - Utils::Time::stringToTime(_date, "%Y-%m-%d"))); + result.mdl.set("releasedate", + Utils::Time::DateTime(Utils::Time::stringToTime(_date, "%Y-%m-%d"))); } else if (_date.length() > 0) { - result.mdl.set("releasedate", Utils::Time::DateTime( - Utils::Time::stringToTime(_date, "%Y"))); + result.mdl.set("releasedate", + Utils::Time::DateTime(Utils::Time::stringToTime(_date, "%Y"))); } if (_date.length() > 0) { - LOG(LogDebug) << "ScreenScraperRequest::processGame(): Release Date (unparsed): " << - _date; - LOG(LogDebug) << "ScreenScraperRequest::processGame(): Release Date (parsed): " << - result.mdl.get("releasedate"); + LOG(LogDebug) << "ScreenScraperRequest::processGame(): Release Date (unparsed): " + << _date; + LOG(LogDebug) << "ScreenScraperRequest::processGame(): Release Date (parsed): " + << result.mdl.get("releasedate"); } // Developer for the game (Xpath: Data/jeu[0]/developpeur). std::string developer = game.child("developpeur").text().get(); if (!developer.empty()) { result.mdl.set("developer", Utils::String::replace(developer, " ", " ")); - LOG(LogDebug) << "ScreenScraperRequest::processGame(): Developer: " << - result.mdl.get("developer"); + LOG(LogDebug) << "ScreenScraperRequest::processGame(): Developer: " + << result.mdl.get("developer"); } // Publisher for the game (Xpath: Data/jeu[0]/editeur). std::string publisher = game.child("editeur").text().get(); if (!publisher.empty()) { result.mdl.set("publisher", Utils::String::replace(publisher, " ", " ")); - LOG(LogDebug) << "ScreenScraperRequest::processGame(): Publisher: " << - result.mdl.get("publisher"); + LOG(LogDebug) << "ScreenScraperRequest::processGame(): Publisher: " + << result.mdl.get("publisher"); } // Genre fallback language: EN. (Xpath: Data/jeu[0]/genres/genre[*]). - std::string genre = find_child_by_attribute_list(game.child("genres"), - "genre", "langue", { language, "en" }).text().get(); + std::string genre = find_child_by_attribute_list(game.child("genres"), "genre", "langue", + { language, "en" }) + .text() + .get(); if (!genre.empty()) { result.mdl.set("genre", genre); - LOG(LogDebug) << "ScreenScraperRequest::processGame(): Genre: " << - result.mdl.get("genre"); + LOG(LogDebug) << "ScreenScraperRequest::processGame(): Genre: " + << result.mdl.get("genre"); } // Players. std::string players = game.child("joueurs").text().get(); if (!players.empty()) { result.mdl.set("players", players); - LOG(LogDebug) << "ScreenScraperRequest::processGame(): Players: " << - result.mdl.get("players"); + LOG(LogDebug) << "ScreenScraperRequest::processGame(): Players: " + << result.mdl.get("players"); } // Media super-node. pugi::xml_node media_list = game.child("medias"); if (media_list) { - // 3D box - processMedia(result, media_list, ssConfig.media_3dbox, - result.box3DUrl, result.box3DFormat, region); - // Cover - processMedia(result, media_list, ssConfig.media_cover, - result.coverUrl, result.coverFormat, region); - // Marquee (wheel) - processMedia(result, media_list, ssConfig.media_marquee, - result.marqueeUrl, result.marqueeFormat, region); - // Screenshot - processMedia(result, media_list, ssConfig.media_screenshot, - result.screenshotUrl, result.screenshotFormat, region); - // Video - processMedia(result, media_list, ssConfig.media_video, - result.videoUrl, result.videoFormat, region); + // 3D box. + processMedia(result, media_list, ssConfig.media_3dbox, result.box3DUrl, + result.box3DFormat, region); + // Cover. + processMedia(result, media_list, ssConfig.media_cover, result.coverUrl, + result.coverFormat, region); + // Marquee (wheel). + processMedia(result, media_list, ssConfig.media_marquee, result.marqueeUrl, + result.marqueeFormat, region); + // Screenshot. + processMedia(result, media_list, ssConfig.media_screenshot, result.screenshotUrl, + result.screenshotFormat, region); + // Video. + processMedia(result, media_list, ssConfig.media_video, result.videoUrl, + result.videoFormat, region); } result.mediaURLFetch = COMPLETED; out_results.push_back(result); @@ -446,13 +457,12 @@ void ScreenScraperRequest::processGame(const pugi::xml_document& xmldoc, } } -void ScreenScraperRequest::processMedia( - ScraperSearchResult& result, - const pugi::xml_node& media_list, - std::string mediaType, - std::string& fileURL, - std::string& fileFormat, - std::string region) +void ScreenScraperRequest::processMedia(ScraperSearchResult& result, + const pugi::xml_node& media_list, + std::string mediaType, + std::string& fileURL, + std::string& fileFormat, + std::string region) { pugi::xml_node art = pugi::xml_node(nullptr); @@ -460,51 +470,52 @@ void ScreenScraperRequest::processMedia( // We need to do this because any child of 'medias' has the form // // and we need to find the right media for the region. - pugi::xpath_node_set results = media_list.select_nodes((static_cast - ("media[@type='") + mediaType + "']").c_str()); + pugi::xpath_node_set results = media_list.select_nodes( + (static_cast("media[@type='") + mediaType + "']").c_str()); - if (results.size()) { - // Videos don't have any region attributes, so just take the first entry - // (which should be the only entry as well). - if (mediaType == "video" || mediaType == "video-normalized") { - art = results.first().node(); - } - else { - // Region fallback: WOR(LD), US, CUS(TOM?), JP, EU. - for (auto _region : std::vector{ - region, "wor", "us", "cus", "jp", "eu" }) { - if (art) + if (results.size()) { + // Videos don't have any region attributes, so just take the first entry + // (which should be the only entry as well). + if (mediaType == "video" || mediaType == "video-normalized") { + art = results.first().node(); + } + else { + // Region fallback: WOR(LD), US, CUS(TOM?), JP, EU. + for (auto _region : + std::vector { region, "wor", "us", "cus", "jp", "eu" }) { + if (art) + break; + + for (auto node : results) { + if (node.node().attribute("region").value() == _region) { + art = node.node(); break; - - for (auto node : results) { - if (node.node().attribute("region").value() == _region) { - art = node.node(); - break; - } } } } } + } - if (art) { - // Sending a 'softname' containing space will make the media URLs returned - // by the API also contain the space. Escape any spaces in the URL here. - fileURL = Utils::String::replace(art.text().get(), " ", "%20"); + if (art) { + // Sending a 'softname' containing space will make the media URLs returned + // by the API also contain the space. Escape any spaces in the URL here. + fileURL = Utils::String::replace(art.text().get(), " ", "%20"); - // Get the media type returned by ScreenScraper. - std::string media_type = art.attribute("format").value(); - if (!media_type.empty()) - fileFormat = "." + media_type; - } - else { - LOG(LogDebug) << "ScreenScraperRequest::processMedia(): " - "Failed to find media XML node with name '" << mediaType << "'"; - } + // Get the media type returned by ScreenScraper. + std::string media_type = art.attribute("format").value(); + if (!media_type.empty()) + fileFormat = "." + media_type; + } + else { + LOG(LogDebug) << "ScreenScraperRequest::processMedia(): " + "Failed to find media XML node with name '" + << mediaType << "'"; + } } // Currently not used in this module. void ScreenScraperRequest::processList(const pugi::xml_document& xmldoc, - std::vector& results) + std::vector& results) { assert(mRequestQueue != nullptr); @@ -527,18 +538,18 @@ void ScreenScraperRequest::processList(const pugi::xml_document& xmldoc, std::string id = game.child("id").text().get(); std::string name = game.child("nom").text().get(); std::string platformId = game.child("systemeid").text().get(); - std::string path = ssConfig.getGameSearchUrl(name) + "&systemeid=" + - platformId + "&gameid=" + id; + std::string path = + ssConfig.getGameSearchUrl(name) + "&systemeid=" + platformId + "&gameid=" + id; - mRequestQueue->push(std::unique_ptr - (new ScreenScraperRequest(results, path))); + mRequestQueue->push( + std::unique_ptr(new ScreenScraperRequest(results, path))); game = game.next_sibling("jeu"); } } std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl( - const std::string gameName) const + const std::string gameName) const { std::string screenScraperURL; std::string searchName = gameName; @@ -546,12 +557,14 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl( // Trim leading and trailing whitespaces. searchName.erase(searchName.begin(), - std::find_if(searchName.begin(), searchName.end(), [](char c) { - return !std::isspace(static_cast(c)); - })); - searchName.erase(std::find_if(searchName.rbegin(), searchName.rend(), [](char c) { - return !std::isspace(static_cast(c)); - }).base(), searchName.end()); + std::find_if(searchName.begin(), searchName.end(), [](char c) { + return !std::isspace(static_cast(c)); + })); + searchName.erase( + std::find_if(searchName.rbegin(), searchName.rend(), + [](char c) { return !std::isspace(static_cast(c)); }) + .base(), + searchName.end()); // If only whitespaces were entered as the search string, then search using a random string // that will not return any results. This is a quick and dirty way to avoid french error @@ -578,9 +591,10 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl( // than four characters which would break the wide search. std::string trimTrailingPluses = searchName; trimTrailingPluses.erase(std::find_if(trimTrailingPluses.rbegin(), - trimTrailingPluses.rend(), [](char c) { - return c != '+'; - }).base(), trimTrailingPluses.end()); + trimTrailingPluses.rend(), + [](char c) { return c != '+'; }) + .base(), + trimTrailingPluses.end()); if (trimTrailingPluses.size() < 4) singleSearch = true; @@ -589,12 +603,12 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl( // could also lead to an error for short game names. if (!singleSearch) { std::string removeThe = - Utils::String::replace(Utils::String::toUpper(searchName), "THE ", ""); + Utils::String::replace(Utils::String::toUpper(searchName), "THE ", ""); // Any additional spaces must also be removed. removeThe.erase(removeThe.begin(), - std::find_if(removeThe.begin(), removeThe.end(), [](char c) { - return !std::isspace(static_cast(c)); - })); + std::find_if(removeThe.begin(), removeThe.end(), [](char c) { + return !std::isspace(static_cast(c)); + })); // If "the" is placed at the end of the search string, ScreenScraper also removes it. if (removeThe.size() > 4) { if (removeThe.substr(removeThe.size() - 4, 4) == " THE") @@ -605,20 +619,18 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl( } if (singleSearch) { - screenScraperURL = API_URL_BASE - + "/jeuInfos.php?devid=" + Utils::String::scramble(API_DEV_U, API_DEV_KEY) - + "&devpassword=" + Utils::String::scramble(API_DEV_P, API_DEV_KEY) - + "&softname=" + HttpReq::urlEncode(API_SOFT_NAME) - + "&output=xml" - + "&romnom=" + HttpReq::urlEncode(searchName); + screenScraperURL = API_URL_BASE + "/jeuInfos.php?devid=" + + Utils::String::scramble(API_DEV_U, API_DEV_KEY) + + "&devpassword=" + Utils::String::scramble(API_DEV_P, API_DEV_KEY) + + "&softname=" + HttpReq::urlEncode(API_SOFT_NAME) + "&output=xml" + + "&romnom=" + HttpReq::urlEncode(searchName); } else { - screenScraperURL = API_URL_BASE - + "/jeuRecherche.php?devid=" + Utils::String::scramble(API_DEV_U, API_DEV_KEY) - + "&devpassword=" + Utils::String::scramble(API_DEV_P, API_DEV_KEY) - + "&softname=" + HttpReq::urlEncode(API_SOFT_NAME) - + "&output=xml" - + "&recherche=" + HttpReq::urlEncode(searchName); + screenScraperURL = API_URL_BASE + "/jeuRecherche.php?devid=" + + Utils::String::scramble(API_DEV_U, API_DEV_KEY) + + "&devpassword=" + Utils::String::scramble(API_DEV_P, API_DEV_KEY) + + "&softname=" + HttpReq::urlEncode(API_SOFT_NAME) + "&output=xml" + + "&recherche=" + HttpReq::urlEncode(searchName); } // Username / password, if this has been setup and activated. @@ -626,8 +638,8 @@ std::string ScreenScraperRequest::ScreenScraperConfig::getGameSearchUrl( std::string username = Settings::getInstance()->getString("ScraperUsernameScreenScraper"); std::string password = Settings::getInstance()->getString("ScraperPasswordScreenScraper"); if (!username.empty() && !password.empty()) - screenScraperURL += "&ssid=" + HttpReq::urlEncode(username) + "&sspassword=" + - HttpReq::urlEncode(password); + screenScraperURL += "&ssid=" + HttpReq::urlEncode(username) + + "&sspassword=" + HttpReq::urlEncode(password); } return screenScraperURL; diff --git a/es-app/src/scrapers/ScreenScraper.h b/es-app/src/scrapers/ScreenScraper.h index 921fab82f..97295587b 100644 --- a/es-app/src/scrapers/ScreenScraper.h +++ b/es-app/src/scrapers/ScreenScraper.h @@ -10,49 +10,54 @@ #ifndef ES_APP_SCRAPERS_SCREEN_SCRAPER_H #define ES_APP_SCRAPERS_SCREEN_SCRAPER_H -#include "scrapers/Scraper.h" #include "EmulationStation.h" +#include "scrapers/Scraper.h" -namespace pugi { class xml_document; } +namespace pugi +{ + class xml_document; +} -void screenscraper_generate_scraper_requests( - const ScraperSearchParams& params, - std::queue>& requests, - std::vector& results); +void screenscraper_generate_scraper_requests(const ScraperSearchParams& params, + std::queue>& requests, + std::vector& results); class ScreenScraperRequest : public ScraperHttpRequest { public: // ctor for a GetGameList request. ScreenScraperRequest(std::queue>& requestsWrite, - std::vector& resultsWrite, - const std::string& url) : ScraperHttpRequest(resultsWrite, url), - mRequestQueue(&requestsWrite) {} + std::vector& resultsWrite, + const std::string& url) + : ScraperHttpRequest(resultsWrite, url) + , mRequestQueue(&requestsWrite) + { + } // ctor for a GetGame request. - ScreenScraperRequest(std::vector& resultsWrite, - const std::string& url) : ScraperHttpRequest(resultsWrite, url), - mRequestQueue(nullptr) {} + ScreenScraperRequest(std::vector& resultsWrite, const std::string& url) + : ScraperHttpRequest(resultsWrite, url) + , mRequestQueue(nullptr) + { + } // Settings for the scraper. static const struct ScreenScraperConfig { std::string getGameSearchUrl(const std::string gameName) const; // Access to the API. - const std::string API_DEV_U = - { 15, 21, 39, 22, 42, 40 }; - const std::string API_DEV_P = - { 32, 70, 46, 54, 12, 5, 13, 120, 50, 66, 25 }; - const std::string API_DEV_KEY = - { 67, 112, 72, 120, 121, 77, 119, 74, 84, 56, 75, 122, 78, 98, 69, 86, 56, 120, 120, 49 }; + const std::string API_DEV_U = { 15, 21, 39, 22, 42, 40 }; + const std::string API_DEV_P = { 32, 70, 46, 54, 12, 5, 13, 120, 50, 66, 25 }; + const std::string API_DEV_KEY = { 67, 112, 72, 120, 121, 77, 119, 74, 84, 56, + 75, 122, 78, 98, 69, 86, 56, 120, 120, 49 }; const std::string API_URL_BASE = "https://www.screenscraper.fr/api2"; - const std::string API_SOFT_NAME = "EmulationStation-DE " + - static_cast(PROGRAM_VERSION_STRING); + const std::string API_SOFT_NAME = + "EmulationStation-DE " + static_cast(PROGRAM_VERSION_STRING); // Which type of image artwork we need. Possible values (not a comprehensive list): // - ss: in-game screenshot // - box-3D: 3D boxart - // - box-2D: 2D boxart (default) + // - box-2D: 2D boxart // - screenmarque : marquee // - sstitle: in-game start screenshot // - steamgrid: Steam artwork @@ -75,27 +80,27 @@ public: // Which Region to use when selecting the artwork. // Applies to: artwork, name of the game, date of release. - // This is read from es_settings.xml, setting 'ScraperRegion'. + // This is read from es_settings.xml, setting "ScraperRegion". // Which Language to use when selecting the textual information. // Applies to: description, genre. - // This is read from es_settings.xml, setting 'ScraperLanguage'. + // This is read from es_settings.xml, setting "ScraperLanguage". - ScreenScraperConfig() {}; + ScreenScraperConfig() {} } configuration; protected: void process(const std::unique_ptr& req, - std::vector& results) override; + std::vector& results) override; void processList(const pugi::xml_document& xmldoc, std::vector& results); void processGame(const pugi::xml_document& xmldoc, std::vector& results); void processMedia(ScraperSearchResult& result, - const pugi::xml_node& media_list, - std::string mediaType, - std::string& fileURL, - std::string& fileFormat, - std::string region); + const pugi::xml_node& media_list, + std::string mediaType, + std::string& fileURL, + std::string& fileFormat, + std::string region); bool isGameRequest() { return !mRequestQueue; } std::queue>* mRequestQueue; diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index 0b9a692a3..1e78c6777 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -8,15 +8,15 @@ #include "views/SystemView.h" -#include "animations/LambdaAnimation.h" -#include "guis/GuiMsgBox.h" -#include "views/UIModeController.h" -#include "views/ViewController.h" #include "Log.h" #include "Settings.h" #include "Sound.h" #include "SystemData.h" #include "Window.h" +#include "animations/LambdaAnimation.h" +#include "guis/GuiMsgBox.h" +#include "views/UIModeController.h" +#include "views/ViewController.h" #if defined(_WIN64) #include @@ -26,14 +26,12 @@ const int logoBuffersLeft[] = { -5, -2, -1 }; const int logoBuffersRight[] = { 1, 2, 5 }; -SystemView::SystemView( - Window* window) - : IList - (window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP), - mPreviousScrollVelocity(0), - mUpdatedGameCount(false), - mViewNeedsReload(true), - mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER) +SystemView::SystemView(Window* window) + : IList(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP) + , mPreviousScrollVelocity(0) + , mUpdatedGameCount(false) + , mViewNeedsReload(true) + , mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER) { mCamOffset = 0; mExtrasCamOffset = 0; @@ -58,8 +56,8 @@ void SystemView::populate() { mEntries.clear(); - for (auto it = SystemData::sSystemVector.cbegin(); - it != SystemData::sSystemVector.cend(); it++) { + for (auto it = SystemData::sSystemVector.cbegin(); // Line break. + it != SystemData::sSystemVector.cend(); it++) { const std::shared_ptr& theme = (*it)->getTheme(); if (mViewNeedsReload) @@ -74,11 +72,11 @@ void SystemView::populate() const ThemeData::ThemeElement* logoElem = theme->getElement("system", "logo", "image"); if (logoElem) { std::string path = logoElem->get("path"); - std::string defaultPath = logoElem->has("default") ? - logoElem->get("default") : ""; + std::string defaultPath = + logoElem->has("default") ? logoElem->get("default") : ""; if ((!path.empty() && ResourceManager::getInstance()->fileExists(path)) || - (!defaultPath.empty() && - ResourceManager::getInstance()->fileExists(defaultPath))) { + (!defaultPath.empty() && + ResourceManager::getInstance()->fileExists(defaultPath))) { ImageComponent* logo = new ImageComponent(mWindow, false, false); logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale); logo->applyTheme(theme, "system", "logo", ThemeFlags::PATH | ThemeFlags::COLOR); @@ -88,16 +86,14 @@ void SystemView::populate() } if (!e.data.logo) { // No logo in theme; use text. - TextComponent* text = new TextComponent( - mWindow, - (*it)->getName(), - Font::get(FONT_SIZE_LARGE), - 0x000000FF, - ALIGN_CENTER); + TextComponent* text = + new TextComponent(mWindow, (*it)->getName(), Font::get(FONT_SIZE_LARGE), + 0x000000FF, ALIGN_CENTER); text->setSize(mCarousel.logoSize * mCarousel.logoScale); text->applyTheme((*it)->getTheme(), "system", "logoText", - ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR | - ThemeFlags::FORCE_UPPERCASE | ThemeFlags::LINE_SPACING | ThemeFlags::TEXT); + ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR | + ThemeFlags::FORCE_UPPERCASE | ThemeFlags::LINE_SPACING | + ThemeFlags::TEXT); e.data.logo = std::shared_ptr(text); if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) { @@ -134,10 +130,9 @@ void SystemView::populate() e.data.backgroundExtras = ThemeData::makeExtras((*it)->getTheme(), "system", mWindow); // Sort the extras by z-index. - std::stable_sort(e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(), - [](GuiComponent* a, GuiComponent* b) { - return b->getZIndex() > a->getZIndex(); - }); + std::stable_sort( + e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(), + [](GuiComponent* a, GuiComponent* b) { return b->getZIndex() > a->getZIndex(); }); this->add(e); } @@ -146,9 +141,10 @@ void SystemView::populate() // Something is wrong, there is not a single system to show, check if UI mode is not full. if (!UIModeController::getInstance()->isUIModeFull()) { Settings::getInstance()->setString("UIMode", "full"); - mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - "The selected UI mode has nothing to show,\n returning to UI mode \"Full\"", - "OK", nullptr)); + mWindow->pushGui(new GuiMsgBox( + mWindow, getHelpStyle(), + "The selected UI mode has nothing to show,\n returning to UI mode \"Full\"", "OK", + nullptr)); } } } @@ -162,14 +158,14 @@ void SystemView::updateGameCount() ss << "CONFIGURATION"; else if (getSelected()->isCollection() && (getSelected()->getName() == "favorites")) ss << gameCount.first << " GAME" << (gameCount.first == 1 ? " " : "S"); - // The 'recent' gamelist has probably been trimmed after sorting, so we'll cap it at + // The "recent" gamelist has probably been trimmed after sorting, so we'll cap it at // its maximum limit of 50 games. else if (getSelected()->isCollection() && (getSelected()->getName() == "recent")) - ss << (gameCount.first > 50 ? 50 : gameCount.first) << " GAME" << - (gameCount.first == 1 ? " " : "S"); + ss << (gameCount.first > 50 ? 50 : gameCount.first) << " GAME" + << (gameCount.first == 1 ? " " : "S"); else - ss << gameCount.first << " GAME" << (gameCount.first == 1 ? " " : "S ") << "(" << - gameCount.second << " FAVORITE" << (gameCount.second == 1 ? ")" : "S)"); + ss << gameCount.first << " GAME" << (gameCount.first == 1 ? " " : "S ") << "(" + << gameCount.second << " FAVORITE" << (gameCount.second == 1 ? ")" : "S)"); mSystemInfo.setText(ss.str()); } @@ -190,40 +186,40 @@ bool SystemView::input(InputConfig* config, Input input) if (input.value != 0) { if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r && - SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { + SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { LOG(LogDebug) << "SystemView::input(): Reloading all"; ViewController::get()->reloadAll(); return true; } switch (mCarousel.type) { - case VERTICAL: - case VERTICAL_WHEEL: - if (config->isMappedLike("up", input)) { - ViewController::get()->cancelViewTransitions(); - listInput(-1); - return true; - } - if (config->isMappedLike("down", input)) { - ViewController::get()->cancelViewTransitions(); - listInput(1); - return true; - } - break; - case HORIZONTAL: - case HORIZONTAL_WHEEL: - default: - if (config->isMappedLike("left", input)) { - ViewController::get()->cancelViewTransitions(); - listInput(-1); - return true; - } - if (config->isMappedLike("right", input)) { - ViewController::get()->cancelViewTransitions(); - listInput(1); - return true; - } - break; + case VERTICAL: + case VERTICAL_WHEEL: + if (config->isMappedLike("up", input)) { + ViewController::get()->cancelViewTransitions(); + listInput(-1); + return true; + } + if (config->isMappedLike("down", input)) { + ViewController::get()->cancelViewTransitions(); + listInput(1); + return true; + } + break; + case HORIZONTAL: + case HORIZONTAL_WHEEL: + default: + if (config->isMappedLike("left", input)) { + ViewController::get()->cancelViewTransitions(); + listInput(-1); + return true; + } + if (config->isMappedLike("right", input)) { + ViewController::get()->cancelViewTransitions(); + listInput(1); + return true; + } + break; } if (config->isMappedTo("a", input)) { @@ -233,17 +229,16 @@ bool SystemView::input(InputConfig* config, Input input) return true; } if (Settings::getInstance()->getBool("RandomAddButton") && - (config->isMappedTo("leftthumbstickclick", input) || - config->isMappedTo("rightthumbstickclick", input))) { + (config->isMappedTo("leftthumbstickclick", input) || + config->isMappedTo("rightthumbstickclick", input))) { // Get a random system and jump to it. NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND); setCursor(SystemData::getRandomSystem(getSelected())); return true; } - if (!UIModeController::getInstance()->isUIModeKid() && - config->isMappedTo("back", input) && - Settings::getInstance()->getBool("ScreensaverControls")) { + if (!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("back", input) && + Settings::getInstance()->getBool("ScreensaverControls")) { if (!mWindow->isScreensaverActive()) { ViewController::get()->stopScrolling(); ViewController::get()->cancelViewTransitions(); @@ -254,10 +249,8 @@ bool SystemView::input(InputConfig* config, Input input) } } else { - if (config->isMappedLike("left", input) || - config->isMappedLike("right", input) || - config->isMappedLike("up", input) || - config->isMappedLike("down", input)) + if (config->isMappedLike("left", input) || config->isMappedLike("right", input) || + config->isMappedLike("up", input) || config->isMappedLike("down", input)) listInput(0); } @@ -302,8 +295,7 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) std::string transition_style = Settings::getInstance()->getString("TransitionStyle"); // To prevent ugly jumps with two systems when quickly repeating the same direction. - if (mPreviousScrollVelocity != 0 && posMax == 2 && - mScrollVelocity == mPreviousScrollVelocity ) { + if (mPreviousScrollVelocity != 0 && posMax == 2 && mScrollVelocity == mPreviousScrollVelocity) { if (fabs(endPos - startPos) < 0.5 || fabs(endPos - startPos) > 1.5) { (mScrollVelocity < 0) ? endPos -= 1 : endPos += 1; (mCursor == 0) ? mCursor = 1 : mCursor = 0; @@ -321,77 +313,84 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) if (transition_style == "fade") { float startExtrasFade = mExtrasFadeOpacity; anim = new LambdaAnimation( - [this, startExtrasFade, startPos, endPos, posMax](float t) { - t -= 1; - float f = Math::lerp(startPos, endPos, t*t*t + 1); - if (f < 0) - f += posMax; - if (f >= posMax) - f -= posMax; + [this, startExtrasFade, startPos, endPos, posMax](float t) { + t -= 1; + float f = Math::lerp(startPos, endPos, t * t * t + 1); + if (f < 0) + f += posMax; + if (f >= posMax) + f -= posMax; - this->mCamOffset = f; + this->mCamOffset = f; - t += 1; - if (t < 0.3f) - this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.2f + startExtrasFade); - else if (t < 0.7f) - this->mExtrasFadeOpacity = 1.0f; - else - this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.6f) / 0.3f); + t += 1; + if (t < 0.3f) + this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.2f + startExtrasFade); + else if (t < 0.7f) + this->mExtrasFadeOpacity = 1.0f; + else + this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.6f) / 0.3f); - if (t > 0.5f) - this->mExtrasCamOffset = endPos; + if (t > 0.5f) + this->mExtrasCamOffset = endPos; - // Update the game count when the entire animation has been completed. - if (mExtrasFadeOpacity == 1.0) - updateGameCount(); - }, 500); + // Update the game count when the entire animation has been completed. + if (mExtrasFadeOpacity == 1.0f) + updateGameCount(); + }, + 500); } else if (transition_style == "slide") { mUpdatedGameCount = false; anim = new LambdaAnimation( - [this, startPos, endPos, posMax](float t) { - t -= 1; - float f = Math::lerp(startPos, endPos, t*t*t + 1); - if (f < 0) - f += posMax; - if (f >= posMax) - f -= posMax; + [this, startPos, endPos, posMax](float t) { + t -= 1; + float f = Math::lerp(startPos, endPos, t * t * t + 1); + if (f < 0) + f += posMax; + if (f >= posMax) + f -= posMax; - this->mCamOffset = f; - this->mExtrasCamOffset = f; + this->mCamOffset = f; + this->mExtrasCamOffset = f; - // Hack to make the game count being updated in the middle of the animation. - bool update = false; - if (endPos == -1 && fabs(fabs(posMax) - fabs(mCamOffset)) > 0.5 && !mUpdatedGameCount) - update = true; - else if (endPos > posMax && fabs(endPos - posMax - fabs(mCamOffset)) < - 0.5 && !mUpdatedGameCount) - update = true; - else if (fabs(fabs(endPos) - fabs(mCamOffset)) < 0.5 && !mUpdatedGameCount) - update = true; + // Hack to make the game count being updated in the middle of the animation. + bool update = false; + if (endPos == -1.0f && fabs(fabs(posMax) - fabs(mCamOffset)) > 0.5f && + !mUpdatedGameCount) { + update = true; + } + else if (endPos > posMax && fabs(endPos - posMax - fabs(mCamOffset)) < 0.5f && + !mUpdatedGameCount) { + update = true; + } + else if (fabs(fabs(endPos) - fabs(mCamOffset)) < 0.5f && !mUpdatedGameCount) { + update = true; + } - if (update) { - mUpdatedGameCount = true; - updateGameCount(); - } - }, 500); + if (update) { + mUpdatedGameCount = true; + updateGameCount(); + } + }, + 500); } else { // Instant. updateGameCount(); anim = new LambdaAnimation( - [this, startPos, endPos, posMax](float t) { - t -= 1; - float f = Math::lerp(startPos, endPos, t*t*t + 1); - if (f < 0) - f += posMax; - if (f >= posMax) - f -= posMax; + [this, startPos, endPos, posMax](float t) { + t -= 1; + float f = Math::lerp(startPos, endPos, t * t * t + 1); + if (f < 0) + f += posMax; + if (f >= posMax) + f -= posMax; - this->mCamOffset = f; - this->mExtrasCamOffset = endPos; - }, 500); + this->mCamOffset = f; + this->mExtrasCamOffset = endPos; + }, + 500); } setAnimation(anim, 0, nullptr, false, 0); @@ -400,7 +399,7 @@ void SystemView::onCursorChanged(const CursorState& /*state*/) void SystemView::render(const Transform4x4f& parentTrans) { if (size() == 0) - return; // Nothing to render. + return; // Nothing to render. Transform4x4f trans = getTransform() * parentTrans; @@ -430,7 +429,7 @@ std::vector SystemView::getHelpPrompts() prompts.push_back(HelpPrompt("thumbstickclick", "random")); if (!UIModeController::getInstance()->isUIModeKid() && - Settings::getInstance()->getBool("ScreensaverControls")) + Settings::getInstance()->getBool("ScreensaverControls")) prompts.push_back(HelpPrompt("back", "toggle screensaver")); return prompts; @@ -443,7 +442,7 @@ HelpStyle SystemView::getHelpStyle() return style; } -void SystemView::onThemeChanged(const std::shared_ptr& /*theme*/) +void SystemView::onThemeChanged(const std::shared_ptr& /*theme*/) { LOG(LogDebug) << "SystemView::onThemeChanged()"; mViewNeedsReload = true; @@ -451,7 +450,7 @@ void SystemView::onThemeChanged(const std::shared_ptr& /*theme*/) } // Get the ThemeElements that make up the SystemView. -void SystemView::getViewElements(const std::shared_ptr& theme) +void SystemView::getViewElements(const std::shared_ptr& theme) { LOG(LogDebug) << "SystemView::getViewElements()"; @@ -460,13 +459,14 @@ void SystemView::getViewElements(const std::shared_ptr& theme) if (!theme->hasView("system")) return; - const ThemeData::ThemeElement* carouselElem = theme-> - getElement("system", "systemcarousel", "carousel"); + const ThemeData::ThemeElement* carouselElem = + theme->getElement("system", "systemcarousel", "carousel"); + if (carouselElem) getCarouselFromTheme(carouselElem); - const ThemeData::ThemeElement* sysInfoElem = theme-> - getElement("system", "systemInfo", "text"); + const ThemeData::ThemeElement* sysInfoElem = theme->getElement("system", "systemInfo", "text"); + if (sysInfoElem) mSystemInfo.applyTheme(theme, "system", "systemInfo", ThemeFlags::ALL); @@ -479,71 +479,79 @@ void SystemView::renderCarousel(const Transform4x4f& trans) // Background box behind logos. Transform4x4f carouselTrans = trans; carouselTrans.translate(Vector3f(mCarousel.pos.x(), mCarousel.pos.y(), 0.0)); - carouselTrans.translate(Vector3f(mCarousel.origin.x() * mCarousel.size.x() * -1, - mCarousel.origin.y() * mCarousel.size.y() * -1, 0.0f)); + carouselTrans.translate(Vector3f(mCarousel.origin.x() * mCarousel.size.x() * -1.0f, + mCarousel.origin.y() * mCarousel.size.y() * -1.0f, 0.0f)); Vector2f clipPos(carouselTrans.translation().x(), carouselTrans.translation().y()); - Renderer::pushClipRect(Vector2i(static_cast(clipPos.x()), static_cast(clipPos.y())), - Vector2i(static_cast(mCarousel.size.x()), static_cast(mCarousel.size.y()))); + Renderer::pushClipRect( + Vector2i(static_cast(clipPos.x()), static_cast(clipPos.y())), + Vector2i(static_cast(mCarousel.size.x()), static_cast(mCarousel.size.y()))); Renderer::setMatrix(carouselTrans); - Renderer::drawRect(0.0f, 0.0f, mCarousel.size.x(), mCarousel.size.y(), - mCarousel.color, mCarousel.colorEnd, mCarousel.colorGradientHorizontal); + Renderer::drawRect(0.0f, 0.0f, mCarousel.size.x(), mCarousel.size.y(), mCarousel.color, + mCarousel.colorEnd, mCarousel.colorGradientHorizontal); // Draw logos. // Note: logoSpacing will also include the size of the logo itself. - Vector2f logoSpacing(0.0, 0.0); - float xOff = 0.0; - float yOff = 0.0; + Vector2f logoSpacing(0.0f, 0.0f); + float xOff = 0.0f; + float yOff = 0.0f; switch (mCarousel.type) { - case VERTICAL_WHEEL: - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - - (mCamOffset * logoSpacing[1]); + case VERTICAL_WHEEL: { + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f - + (mCamOffset * logoSpacing[1]); if (mCarousel.logoAlignment == ALIGN_LEFT) - xOff = mCarousel.logoSize.x() / 10.f; + xOff = mCarousel.logoSize.x() / 10.0f; else if (mCarousel.logoAlignment == ALIGN_RIGHT) xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); else - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f; + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f; break; - case VERTICAL: - logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() * - mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.y(); - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - - (mCamOffset * logoSpacing[1]); - + } + case VERTICAL: { + logoSpacing[1] = + ((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) / + (mCarousel.maxLogoCount)) + + mCarousel.logoSize.y(); + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f - + (mCamOffset * logoSpacing[1]); if (mCarousel.logoAlignment == ALIGN_LEFT) - xOff = mCarousel.logoSize.x() / 10.f; + xOff = mCarousel.logoSize.x() / 10.0f; else if (mCarousel.logoAlignment == ALIGN_RIGHT) xOff = mCarousel.size.x() - (mCarousel.logoSize.x() * 1.1f); else - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2; + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f; break; - case HORIZONTAL_WHEEL: - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2 - - (mCamOffset * logoSpacing[1]); + } + case HORIZONTAL_WHEEL: { + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f - + (mCamOffset * logoSpacing[1]); if (mCarousel.logoAlignment == ALIGN_TOP) - yOff = mCarousel.logoSize.y() / 10; + yOff = mCarousel.logoSize.y() / 10.0f; else if (mCarousel.logoAlignment == ALIGN_BOTTOM) yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); else - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2; + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f; break; - case HORIZONTAL: - default: - logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() * - mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.x(); - xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f - - (mCamOffset * logoSpacing[0]); - + } + case HORIZONTAL: { + } + default: { + logoSpacing[0] = + ((mCarousel.size.x() - (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) / + (mCarousel.maxLogoCount)) + + mCarousel.logoSize.x(); + xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.0f - + (mCamOffset * logoSpacing[0]); if (mCarousel.logoAlignment == ALIGN_TOP) - yOff = mCarousel.logoSize.y() / 10.f; + yOff = mCarousel.logoSize.y() / 10.0f; else if (mCarousel.logoAlignment == ALIGN_BOTTOM) yOff = mCarousel.size.y() - (mCarousel.logoSize.y() * 1.1f); else - yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f; + yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.0f; break; + } } int center = static_cast(mCamOffset); @@ -558,9 +566,10 @@ void SystemView::renderCarousel(const Transform4x4f& trans) bufferRight = 0; } - for (int i = center - logoCount / 2 + bufferLeft; - i <= center + logoCount / 2 + bufferRight; i++) { + for (int i = center - logoCount / 2 + bufferLeft; // Line break. + i <= center + logoCount / 2 + bufferRight; i++) { int index = i; + while (index < 0) index += static_cast(mEntries.size()); while (index >= static_cast(mEntries.size())) @@ -575,11 +584,11 @@ void SystemView::renderCarousel(const Transform4x4f& trans) scale = std::min(mCarousel.logoScale, std::max(1.0f, scale)); scale /= mCarousel.logoScale; - int opacity = static_cast(std::round(0x80 + ((0xFF - 0x80) * - (1.0f - fabs(distance))))); + int opacity = + static_cast(std::round(0x80 + ((0xFF - 0x80) * (1.0f - fabs(distance))))); opacity = std::max(static_cast(0x80), opacity); - const std::shared_ptr &comp = mEntries.at(index).data.logo; + const std::shared_ptr& comp = mEntries.at(index).data.logo; if (mCarousel.type == VERTICAL_WHEEL || mCarousel.type == HORIZONTAL_WHEEL) { comp->setRotationDegrees(mCarousel.logoRotation * distance); comp->setRotationOrigin(mCarousel.logoRotationOrigin); @@ -599,11 +608,11 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp // Adding texture loading buffers depending on scrolling speed and status. int bufferIndex = getScrollingVelocity() + 1; - Renderer::pushClipRect(Vector2i::Zero(), Vector2i(static_cast(mSize.x()), - static_cast(mSize.y()))); + Renderer::pushClipRect(Vector2i::Zero(), + Vector2i(static_cast(mSize.x()), static_cast(mSize.y()))); - for (int i = extrasCenter + logoBuffersLeft[bufferIndex]; i <= extrasCenter + - logoBuffersRight[bufferIndex]; i++) { + for (int i = extrasCenter + logoBuffersLeft[bufferIndex]; + i <= extrasCenter + logoBuffersRight[bufferIndex]; i++) { int index = i; while (index < 0) index += static_cast(mEntries.size()); @@ -611,20 +620,20 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp index -= static_cast(mEntries.size()); // Only render selected system when not showing. - if (mShowing || index == mCursor) - { + if (mShowing || index == mCursor) { Transform4x4f extrasTrans = trans; if (mCarousel.type == HORIZONTAL || mCarousel.type == HORIZONTAL_WHEEL) extrasTrans.translate(Vector3f((i - mExtrasCamOffset) * mSize.x(), 0, 0)); else extrasTrans.translate(Vector3f(0, (i - mExtrasCamOffset) * mSize.y(), 0)); - Renderer::pushClipRect(Vector2i(static_cast(extrasTrans.translation()[0]), - static_cast(extrasTrans.translation()[1])), - Vector2i(static_cast(mSize.x()), static_cast(mSize.y()))); + Renderer::pushClipRect( + Vector2i(static_cast(extrasTrans.translation()[0]), + static_cast(extrasTrans.translation()[1])), + Vector2i(static_cast(mSize.x()), static_cast(mSize.y()))); SystemViewData data = mEntries.at(index).data; for (unsigned int j = 0; j < data.backgroundExtras.size(); j++) { - GuiComponent *extra = data.backgroundExtras[j]; + GuiComponent* extra = data.backgroundExtras[j]; if (extra->getZIndex() >= lower && extra->getZIndex() < upper) extra->render(extrasTrans); } @@ -637,13 +646,13 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp void SystemView::renderFade(const Transform4x4f& trans) { - unsigned int fadeColor = 0x00000000 | static_cast(mExtrasFadeOpacity * 255); + unsigned int fadeColor = 0x00000000 | static_cast(mExtrasFadeOpacity * 255.0f); Renderer::setMatrix(trans); Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), fadeColor, fadeColor); } // Populate the system carousel with the legacy values. -void SystemView::getDefaultElements(void) +void SystemView::getDefaultElements(void) { // Carousel. mCarousel.type = HORIZONTAL; @@ -658,13 +667,13 @@ void SystemView::getDefaultElements(void) mCarousel.colorEnd = 0xFFFFFFD8; mCarousel.colorGradientHorizontal = true; mCarousel.logoScale = 1.2f; - mCarousel.logoRotation = 7.5; - mCarousel.logoRotationOrigin.x() = -5; - mCarousel.logoRotationOrigin.y() = 0.5; + mCarousel.logoRotation = 7.5f; + mCarousel.logoRotationOrigin.x() = -5.0f; + mCarousel.logoRotationOrigin.y() = 0.5f; mCarousel.logoSize.x() = 0.25f * mSize.x(); mCarousel.logoSize.y() = 0.155f * mSize.y(); mCarousel.maxLogoCount = 3; - mCarousel.zIndex = 40; + mCarousel.zIndex = 40.0f; // System info bar. mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight() * 2.2f); @@ -702,7 +711,8 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem) if (elem->has("colorEnd")) mCarousel.colorEnd = elem->get("colorEnd"); if (elem->has("gradientType")) - mCarousel.colorGradientHorizontal = !(elem->get("gradientType").compare("horizontal")); + mCarousel.colorGradientHorizontal = + !(elem->get("gradientType").compare("horizontal")); if (elem->has("logoScale")) mCarousel.logoScale = elem->get("logoScale"); if (elem->has("logoSize")) @@ -727,14 +737,4 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem) else mCarousel.logoAlignment = ALIGN_CENTER; } -} - -void SystemView::onShow() -{ - mShowing = true; -} - -void SystemView::onHide() -{ - mShowing = false; -} +} \ No newline at end of file diff --git a/es-app/src/views/SystemView.h b/es-app/src/views/SystemView.h index edfc1ab1c..8d30c42c4 100644 --- a/es-app/src/views/SystemView.h +++ b/es-app/src/views/SystemView.h @@ -9,11 +9,11 @@ #ifndef ES_APP_VIEWS_SYSTEM_VIEW_H #define ES_APP_VIEWS_SYSTEM_VIEW_H +#include "GuiComponent.h" +#include "Sound.h" #include "components/IList.h" #include "components/TextComponent.h" #include "resources/Font.h" -#include "GuiComponent.h" -#include "Sound.h" #include @@ -55,8 +55,8 @@ public: SystemView(Window* window); ~SystemView(); - virtual void onShow() override; - virtual void onHide() override; + virtual void onShow() override { mShowing = true; } + virtual void onHide() override { mShowing = false; } void goToSystem(SystemData* system, bool animate); @@ -69,12 +69,14 @@ public: std::vector getHelpPrompts() override; virtual HelpStyle getHelpStyle() override; - CarouselType getCarouselType() { return mCarousel.type; }; + CarouselType getCarouselType() { return mCarousel.type; } protected: void onCursorChanged(const CursorState& state) override; - virtual void onScroll() override { - NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND); } + virtual void onScroll() override + { + NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND); + } private: void populate(); diff --git a/es-app/src/views/UIModeController.cpp b/es-app/src/views/UIModeController.cpp index 1736769c9..8c96bb349 100644 --- a/es-app/src/views/UIModeController.cpp +++ b/es-app/src/views/UIModeController.cpp @@ -9,12 +9,12 @@ #include "UIModeController.h" -#include "utils/StringUtil.h" -#include "views/ViewController.h" #include "FileFilterIndex.h" #include "Log.h" #include "SystemData.h" #include "Window.h" +#include "utils/StringUtil.h" +#include "views/ViewController.h" UIModeController* UIModeController::sInstance = nullptr; @@ -34,7 +34,8 @@ void UIModeController::deinit() } } -UIModeController::UIModeController() : mPassKeyCounter(0) +UIModeController::UIModeController() + : mPassKeyCounter(0) { mPassKeySequence = Settings::getInstance()->getString("UIMode_passkey"); mCurrentUIMode = Settings::getInstance()->getString("UIMode"); @@ -47,13 +48,12 @@ void UIModeController::monitorUIMode() if (uimode != mCurrentUIMode && !ViewController::get()->isCameraMoving()) { mCurrentUIMode = uimode; // Reset filters and sort gamelists (which will update the game counter). - for (auto it = SystemData::sSystemVector.cbegin(); it != - SystemData::sSystemVector.cend(); it++) { + for (auto it = SystemData::sSystemVector.cbegin(); // Line break. + it != SystemData::sSystemVector.cend(); it++) { (*it)->sortSystem(true); (*it)->getIndex()->resetFilters(); if ((*it)->getThemeFolder() == "custom-collections") { - for (FileData* customSystem : - (*it)->getRootFolder()->getChildrenListToDisplay()) + for (FileData* customSystem : (*it)->getRootFolder()->getChildrenListToDisplay()) customSystem->getSystem()->getIndex()->resetFilters(); } } @@ -75,6 +75,7 @@ bool UIModeController::listen(InputConfig* config, Input input) unlockUIMode(); return true; } + return false; } @@ -101,21 +102,21 @@ void UIModeController::unlockUIMode() bool UIModeController::isUIModeFull() { - return ((mCurrentUIMode == "full" || (isUIModeKid() && - Settings::getInstance()->getBool("EnableMenuKidMode"))) - && !Settings::getInstance()->getBool("ForceKiosk")); + return ((mCurrentUIMode == "full" || + (isUIModeKid() && Settings::getInstance()->getBool("EnableMenuKidMode"))) && + !Settings::getInstance()->getBool("ForceKiosk")); } bool UIModeController::isUIModeKid() { return (Settings::getInstance()->getBool("ForceKid") || - ((mCurrentUIMode == "kid") && !Settings::getInstance()->getBool("ForceKiosk"))); + ((mCurrentUIMode == "kid") && !Settings::getInstance()->getBool("ForceKiosk"))); } bool UIModeController::isUIModeKiosk() { return (Settings::getInstance()->getBool("ForceKiosk") || - ((mCurrentUIMode == "kiosk") && !Settings::getInstance()->getBool("ForceKid"))); + ((mCurrentUIMode == "kiosk") && !Settings::getInstance()->getBool("ForceKid"))); } std::string UIModeController::getFormattedPassKeyStr() @@ -124,7 +125,7 @@ std::string UIModeController::getFormattedPassKeyStr() std::string out = ""; for (auto c : mPassKeySequence) { - out += (out == "") ? "" : " , "; // Add commas between the entries. + out += (out == "") ? "" : " , "; // Add commas between the entries. std::string controllerType = Settings::getInstance()->getString("InputControllerType"); std::string symbolA; @@ -139,19 +140,19 @@ std::string UIModeController::getFormattedPassKeyStr() symbolY = "X"; } else if (controllerType == "ps4" || controllerType == "ps5") { +#if defined(_MSC_VER) // MSVC compiler. // These symbols are far from perfect but you can at least understand what // they are supposed to depict. - #if defined(_MSC_VER) // MSVC compiler. symbolA = Utils::String::wideStringToString(L"\uF00D"); // Cross. symbolB = Utils::String::wideStringToString(L"\uF111"); // Circle symbolX = Utils::String::wideStringToString(L"\uF04D"); // Square. symbolY = Utils::String::wideStringToString(L"\uF0D8"); // Triangle. - #else +#else symbolA = "\uF00D"; // Cross. symbolB = "\uF111"; // Circle symbolX = "\uF04D"; // Square. symbolY = "\uF0D8"; // Triangle. - #endif +#endif } else { // Xbox controller. @@ -193,8 +194,8 @@ std::string UIModeController::getFormattedPassKeyStr() bool UIModeController::isValidInput(InputConfig* config, Input input) { - if ((config->getMappedTo(input).size() == 0) || // Not a mapped input, so ignore it. - (!input.value)) // Not a key-down event. + if ((config->getMappedTo(input).size() == 0) || // Not a mapped input, so ignore it. + (!input.value)) // Not a key-down event. return false; else return true; diff --git a/es-app/src/views/UIModeController.h b/es-app/src/views/UIModeController.h index 9c6a4f3a2..f16c4257d 100644 --- a/es-app/src/views/UIModeController.h +++ b/es-app/src/views/UIModeController.h @@ -39,7 +39,7 @@ public: bool isUIModeKid(); bool isUIModeKiosk(); - void setCurrentUIMode(const std::string& mode) { mCurrentUIMode = mode; }; + void setCurrentUIMode(const std::string& mode) { mCurrentUIMode = mode; } private: UIModeController(); @@ -58,8 +58,9 @@ private: int mPassKeyCounter; // These are Xbox button names, so they may be different in pracise on non-Xbox controllers. - const std::vector mInputVals = - { "up", "down", "left", "right", "a", "b", "x", "y" }; + const std::vector mInputVals = { + "up", "down", "left", "right", "a", "b", "x", "y" + }; }; #endif // ES_APP_VIEWS_UI_MODE_CONTROLLER_H diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index b4a29a32d..03865faaf 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -12,17 +12,6 @@ #include "views/ViewController.h" -#include "animations/Animation.h" -#include "animations/LambdaAnimation.h" -#include "animations/MoveCameraAnimation.h" -#include "guis/GuiInfoPopup.h" -#include "guis/GuiMenu.h" -#include "views/gamelist/DetailedGameListView.h" -#include "views/gamelist/GridGameListView.h" -#include "views/gamelist/IGameListView.h" -#include "views/gamelist/VideoGameListView.h" -#include "views/SystemView.h" -#include "views/UIModeController.h" #include "AudioManager.h" #include "FileFilterIndex.h" #include "InputManager.h" @@ -32,8 +21,20 @@ #include "SystemData.h" #include "SystemView.h" #include "Window.h" +#include "animations/Animation.h" +#include "animations/LambdaAnimation.h" +#include "animations/MoveCameraAnimation.h" +#include "guis/GuiInfoPopup.h" +#include "guis/GuiMenu.h" +#include "views/SystemView.h" +#include "views/UIModeController.h" +#include "views/gamelist/DetailedGameListView.h" +#include "views/gamelist/GridGameListView.h" +#include "views/gamelist/IGameListView.h" +#include "views/gamelist/VideoGameListView.h" ViewController* ViewController::sInstance = nullptr; + #if defined(_MSC_VER) // MSVC compiler. const std::string ViewController::FAVORITE_CHAR = Utils::String::wideStringToString(L"\uF005"); const std::string ViewController::FOLDER_CHAR = Utils::String::wideStringToString(L"\uF07C"); @@ -60,21 +61,20 @@ void ViewController::init(Window* window) sInstance = new ViewController(window); } -ViewController::ViewController( - Window* window) - : GuiComponent(window), - mCurrentView(nullptr), - mPreviousView(nullptr), - mSkipView(nullptr), - mCamera(Transform4x4f::Identity()), - mSystemViewTransition(false), - mWrappedViews(false), - mFadeOpacity(0), - mCancelledTransition(false), - mLockInput(false), - mNextSystem(false), - mGameToLaunch(nullptr), - mNoGamesMessageBox(nullptr) +ViewController::ViewController(Window* window) + : GuiComponent(window) + , mCurrentView(nullptr) + , mPreviousView(nullptr) + , mSkipView(nullptr) + , mCamera(Transform4x4f::Identity()) + , mSystemViewTransition(false) + , mWrappedViews(false) + , mFadeOpacity(0) + , mCancelledTransition(false) + , mLockInput(false) + , mNextSystem(false) + , mGameToLaunch(nullptr) + , mNoGamesMessageBox(nullptr) { mState.viewing = NOTHING; mState.viewstyle = AUTOMATIC; @@ -89,108 +89,106 @@ ViewController::~ViewController() void ViewController::invalidSystemsFileDialog() { - std::string errorMessage = - "COULDN'T PARSE THE SYSTEMS CONFIGURATION FILE.\n" - "IF YOU HAVE A CUSTOMIZED es_systems.xml FILE, THEN\n" - "SOMETHING IS LIKELY WRONG WITH YOUR XML SYNTAX.\n" - "IF YOU DON'T HAVE A CUSTOM SYSTEMS FILE, THEN THE\n" - "EMULATIONSTATION INSTALLATION IS BROKEN. SEE THE\n" - "APPLICATION LOG FILE es_log.txt FOR ADDITIONAL INFO."; + std::string errorMessage = "COULDN'T PARSE THE SYSTEMS CONFIGURATION FILE.\n" + "IF YOU HAVE A CUSTOMIZED es_systems.xml FILE, THEN\n" + "SOMETHING IS LIKELY WRONG WITH YOUR XML SYNTAX.\n" + "IF YOU DON'T HAVE A CUSTOM SYSTEMS FILE, THEN THE\n" + "EMULATIONSTATION INSTALLATION IS BROKEN. SEE THE\n" + "APPLICATION LOG FILE es_log.txt FOR ADDITIONAL INFO."; - mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), - errorMessage.c_str(), - "QUIT", [] { - SDL_Event quit; - quit.type = SDL_QUIT; - SDL_PushEvent(&quit); - }, "", nullptr, "", nullptr, true)); + mWindow->pushGui(new GuiMsgBox( + mWindow, HelpStyle(), errorMessage.c_str(), "QUIT", + [] { + SDL_Event quit; + quit.type = SDL_QUIT; + SDL_PushEvent(&quit); + }, + "", nullptr, "", nullptr, true)); } void ViewController::noGamesDialog() { - mNoGamesErrorMessage = - "NO GAME FILES WERE FOUND. EITHER PLACE YOUR GAMES IN\n" - "THE CURRENTLY CONFIGURED ROM DIRECTORY OR CHANGE\n" - "ITS PATH USING THE BUTTON BELOW. OPTIONALLY THE ROM\n" - "DIRECTORY STRUCTURE CAN BE GENERATED WHICH WILL\n" - "CREATE A TEXT FILE FOR EACH SYSTEM PROVIDING SOME\n" - "INFORMATION SUCH AS THE SUPPORTED FILE EXTENSIONS.\n" - "THIS IS THE CURRENTLY CONFIGURED ROM DIRECTORY:\n"; + mNoGamesErrorMessage = "NO GAME FILES WERE FOUND. EITHER PLACE YOUR GAMES IN\n" + "THE CURRENTLY CONFIGURED ROM DIRECTORY OR CHANGE\n" + "ITS PATH USING THE BUTTON BELOW. OPTIONALLY THE ROM\n" + "DIRECTORY STRUCTURE CAN BE GENERATED WHICH WILL\n" + "CREATE A TEXT FILE FOR EACH SYSTEM PROVIDING SOME\n" + "INFORMATION SUCH AS THE SUPPORTED FILE EXTENSIONS.\n" + "THIS IS THE CURRENTLY CONFIGURED ROM DIRECTORY:\n"; - #if defined(_WIN64) +#if defined(_WIN64) mRomDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\"); - #else +#else mRomDirectory = FileData::getROMDirectory(); - #endif +#endif - mNoGamesMessageBox = new GuiMsgBox(mWindow, HelpStyle(), mNoGamesErrorMessage + mRomDirectory, - "CHANGE ROM DIRECTORY", [this] { - std::string currentROMDirectory; - #if defined(_WIN64) - currentROMDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\"); - #else - currentROMDirectory = FileData::getROMDirectory(); - #endif + mNoGamesMessageBox = new GuiMsgBox( + mWindow, HelpStyle(), mNoGamesErrorMessage + mRomDirectory, "CHANGE ROM DIRECTORY", + [this] { + std::string currentROMDirectory; +#if defined(_WIN64) + currentROMDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\"); +#else + currentROMDirectory = FileData::getROMDirectory(); +#endif - mWindow->pushGui(new GuiComplexTextEditPopup( - mWindow, - HelpStyle(), - "ENTER ROM DIRECTORY PATH", - "Currently configured path:", - currentROMDirectory, - currentROMDirectory, + mWindow->pushGui(new GuiComplexTextEditPopup( + mWindow, HelpStyle(), "ENTER ROM DIRECTORY PATH", + "Currently configured path:", currentROMDirectory, currentROMDirectory, [this](const std::string& newROMDirectory) { Settings::getInstance()->setString("ROMDirectory", newROMDirectory); Settings::getInstance()->saveFile(); - #if defined(_WIN64) +#if defined(_WIN64) mRomDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\"); - #else +#else mRomDirectory = FileData::getROMDirectory(); - #endif +#endif mNoGamesMessageBox->changeText(mNoGamesErrorMessage + mRomDirectory); mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), - "ROM DIRECTORY SETTING SAVED, RESTART\n" - "THE APPLICATION TO RESCAN THE SYSTEMS", - "OK", nullptr, "", nullptr, "", nullptr, true)); + "ROM DIRECTORY SETTING SAVED, RESTART\n" + "THE APPLICATION TO RESCAN THE SYSTEMS", + "OK", nullptr, "", nullptr, "", nullptr, true)); }, - false, - "SAVE", - "SAVE CHANGES?", - "LOAD CURRENT", - "LOAD CURRENTLY CONFIGURED VALUE", - "CLEAR", - "CLEAR (LEAVE BLANK TO RESET TO DEFAULT DIRECTORY)", - false)); - }, - "CREATE DIRECTORIES", [this] { - mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), + false, "SAVE", "SAVE CHANGES?", "LOAD CURRENT", "LOAD CURRENTLY CONFIGURED VALUE", + "CLEAR", "CLEAR (LEAVE BLANK TO RESET TO DEFAULT DIRECTORY)", false)); + }, + "CREATE DIRECTORIES", + [this] { + mWindow->pushGui(new GuiMsgBox( + mWindow, HelpStyle(), "THIS WILL CREATE DIRECTORIES FOR ALL THE\n" "GAME SYSTEMS DEFINED IN es_systems.xml\n\n" "THIS MAY CREATE A LOT OF FOLDERS SO IT'S\n" "ADVICED TO REMOVE THE ONES YOU DON'T NEED\n\n" "PROCEED?", - "YES", [this] { - if (!SystemData::createSystemDirectories()) { - mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), - "THE SYSTEM DIRECTORIES WERE SUCCESSFULLY\n" - "GENERATED, EXIT THE APPLICATION AND PLACE\n" - "YOUR GAMES IN THE NEWLY CREATED FOLDERS", "OK", nullptr, - "", nullptr, "", nullptr, true)); - } - else { - mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), - "ERROR CREATING THE SYSTEM DIRECTORIES,\n" - "PERMISSION PROBLEMS OR DISK FULL?\n\n" - "SEE THE LOG FILE FOR MORE DETAILS", "OK", nullptr, - "", nullptr, "", nullptr, true)); - } - }, "NO", nullptr, "", nullptr, true)); - }, - "QUIT", [] { - SDL_Event quit; - quit.type = SDL_QUIT; - SDL_PushEvent(&quit); - }, true, false); + "YES", + [this] { + if (!SystemData::createSystemDirectories()) { + mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), + "THE SYSTEM DIRECTORIES WERE SUCCESSFULLY\n" + "GENERATED, EXIT THE APPLICATION AND PLACE\n" + "YOUR GAMES IN THE NEWLY CREATED FOLDERS", + "OK", nullptr, "", nullptr, "", nullptr, + true)); + } + else { + mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), + "ERROR CREATING THE SYSTEM DIRECTORIES,\n" + "PERMISSION PROBLEMS OR DISK FULL?\n\n" + "SEE THE LOG FILE FOR MORE DETAILS", + "OK", nullptr, "", nullptr, "", nullptr, + true)); + } + }, + "NO", nullptr, "", nullptr, true)); + }, + "QUIT", + [] { + SDL_Event quit; + quit.type = SDL_QUIT; + SDL_PushEvent(&quit); + }, + true, false); mWindow->pushGui(mNoGamesMessageBox); } @@ -205,8 +203,8 @@ void ViewController::goToStart() // If a specific system is requested, go directly to its game list. auto requestedSystem = Settings::getInstance()->getString("StartupSystem"); if ("" != requestedSystem && "retropie" != requestedSystem) { - for (auto it = SystemData::sSystemVector.cbegin(); - it != SystemData::sSystemVector.cend(); it++) { + for (auto it = SystemData::sSystemVector.cbegin(); // Line break. + it != SystemData::sSystemVector.cend(); it++) { if ((*it)->getName() == requestedSystem) { goToGameList(*it); return; @@ -237,7 +235,7 @@ bool ViewController::isCameraMoving() { if (mCurrentView) { if (mCamera.r3().x() - -mCurrentView->getPosition().x() != 0 || - mCamera.r3().y() - -mCurrentView->getPosition().y() != 0) + mCamera.r3().y() - -mCurrentView->getPosition().y() != 0) return true; } return false; @@ -322,7 +320,7 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition) auto systemList = getSystemListView(); systemList->setPosition(getSystemId(system) * static_cast(Renderer::getScreenWidth()), - systemList->getPosition().y()); + systemList->getPosition().y()); systemList->goToSystem(system, false); mCurrentView = systemList; @@ -333,7 +331,7 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition) mCamera.translation() = -mCurrentView->getPosition(); if (Settings::getInstance()->getString("TransitionStyle") == "slide") { if (getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL || - getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL) + getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL) mCamera.translation().y() += Renderer::getScreenHeight(); else mCamera.translation().x() -= Renderer::getScreenWidth(); @@ -341,7 +339,7 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition) } else if (Settings::getInstance()->getString("TransitionStyle") == "fade") { if (getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL || - getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL) + getSystemListView()->getCarouselType() == CarouselType::HORIZONTAL_WHEEL) mCamera.translation().y() += Renderer::getScreenHeight(); else mCamera.translation().x() += Renderer::getScreenWidth(); @@ -396,8 +394,9 @@ void ViewController::goToGameList(SystemData* system) restoreViewPosition(); if (mPreviousView && Settings::getInstance()->getString("TransitionStyle") == "fade" && - isAnimationPlaying(0)) + isAnimationPlaying(0)) { mPreviousView->onHide(); + } if (mPreviousView) { mSkipView = mPreviousView; @@ -449,7 +448,7 @@ void ViewController::goToGameList(SystemData* system) int sysId = getSystemId(system); sysList->setPosition(sysId * static_cast(Renderer::getScreenWidth()), - sysList->getPosition().y()); + sysList->getPosition().y()); offsetX = sysList->getPosition().x() - offsetX; mCamera.translation().x() -= offsetX; } @@ -464,7 +463,7 @@ void ViewController::goToGameList(SystemData* system) float offsetX = getGameListView(system)->getPosition().x(); // This is needed to move the camera in the correct direction if there are only two systems. if (SystemData::sSystemVector.size() == 2 && mNextSystem) - offsetX -= Renderer::getScreenWidth(); + offsetX -= Renderer::getScreenWidth(); else offsetX += Renderer::getScreenWidth(); currentPosition.x() = offsetX; @@ -541,11 +540,13 @@ void ViewController::playViewTransition(bool instant) std::string transition_style = Settings::getInstance()->getString("TransitionStyle"); if (instant || transition_style == "instant") { - setAnimation(new LambdaAnimation([this, target](float /*t*/) { - this->mCamera.translation() = -target; - if (mPreviousView) - mPreviousView->onHide(); - }, 1)); + setAnimation(new LambdaAnimation( + [this, target](float /*t*/) { + this->mCamera.translation() = -target; + if (mPreviousView) + mPreviousView->onHide(); + }, + 1)); updateHelpPrompts(); } else if (transition_style == "fade") { @@ -558,7 +559,7 @@ void ViewController::playViewTransition(bool instant) // Without this, a (much shorter) fade transition would still play as // finishedCallback is calling this function. if (!mCancelledTransition) - mFadeOpacity = Math::lerp(0, 1, t); + mFadeOpacity = Math::lerp(0.0f, 1.0f, t); }; auto fadeCallback = [this]() { @@ -569,12 +570,12 @@ void ViewController::playViewTransition(bool instant) const static int FADE_DURATION = 120; // Fade in/out time. const static int FADE_WAIT = 200; // Time to wait between in/out. setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), 0, - [this, fadeFunc, fadeCallback, target] { - this->mCamera.translation() = -target; - updateHelpPrompts(); - setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), - FADE_WAIT, fadeCallback, true); - }); + [this, fadeFunc, fadeCallback, target] { + this->mCamera.translation() = -target; + updateHelpPrompts(); + setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), FADE_WAIT, + fadeCallback, true); + }); // Fast-forward animation if we're partway faded. if (target == -mCamera.translation()) { @@ -616,7 +617,7 @@ bool ViewController::runInBackground(SystemData* system) // with the game. In that situation ES-DE would wait until the whole Steam application was // shut down before it would resume. I.e. it would not be enough to just stop the game. if (system->hasPlatformId(PlatformIds::VALVE_STEAM) || - Settings::getInstance()->getBool("RunInBackground")) + Settings::getInstance()->getBool("RunInBackground")) return true; else return false; @@ -646,8 +647,9 @@ void ViewController::launch(FileData* game) if (durationString == "disabled") { // If the game launch screen has been set as disabled, show a simple info popup // notification instead. - GuiInfoPopup* s = new GuiInfoPopup(mWindow, "LAUNCHING GAME '" + - Utils::String::toUpper(game->metadata.get("name") + "'"), 10000); + GuiInfoPopup* s = new GuiInfoPopup( + mWindow, "LAUNCHING GAME '" + Utils::String::toUpper(game->metadata.get("name") + "'"), + 10000); mWindow->setInfoPopup(s); duration = 1700; } @@ -670,16 +672,14 @@ void ViewController::launch(FileData* game) // This is just a dummy animation in order for the launch screen or notification popup // to be displayed briefly, and for the navigation sound playing to be able to complete. // During this time period, all user input is blocked. - setAnimation(new LambdaAnimation([](float t){}, duration), 0, [this, game] { + setAnimation(new LambdaAnimation([](float t) {}, duration), 0, [this, game] { game->launchGame(mWindow); // If the launch screen is disabled then this will do nothing. mWindow->closeLaunchScreen(); onFileChanged(game, true); // This is a workaround so that any keys or button presses used for exiting the emulator // are not captured upon returning. - setAnimation(new LambdaAnimation([](float t){}, 1), 0, [this] { - mLockInput = false; - }); + setAnimation(new LambdaAnimation([](float t) {}, 1), 0, [this] { mLockInput = false; }); }); } @@ -733,38 +733,41 @@ std::shared_ptr ViewController::getGameListView(SystemData* syste } // Create the view. - switch (selectedViewStyle) - { - case VIDEO: + switch (selectedViewStyle) { + case VIDEO: { view = std::shared_ptr( - new VideoGameListView(mWindow, system->getRootFolder())); + new VideoGameListView(mWindow, system->getRootFolder())); mState.viewstyle = VIDEO; break; - case DETAILED: + } + case DETAILED: { view = std::shared_ptr( - new DetailedGameListView(mWindow, system->getRootFolder())); + new DetailedGameListView(mWindow, system->getRootFolder())); mState.viewstyle = DETAILED; break; - case GRID: + } + case GRID: { view = std::shared_ptr( - new GridGameListView(mWindow, system->getRootFolder())); + new GridGameListView(mWindow, system->getRootFolder())); mState.viewstyle = GRID; break; - case BASIC: - default: + } + case BASIC: { + } + default: { view = std::shared_ptr( - new BasicGameListView(mWindow, system->getRootFolder())); + new BasicGameListView(mWindow, system->getRootFolder())); mState.viewstyle = BASIC; break; + } } view->setTheme(system->getTheme()); std::vector& sysVec = SystemData::sSystemVector; - int id = static_cast( - std::find(sysVec.cbegin(), sysVec.cend(), system) - sysVec.cbegin()); + int id = static_cast(std::find(sysVec.cbegin(), sysVec.cend(), system) - sysVec.cbegin()); view->setPosition(id * static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight() * 2)); + static_cast(Renderer::getScreenHeight() * 2)); addChild(view.get()); @@ -799,8 +802,8 @@ bool ViewController::input(InputConfig* config, Input input) // Open the main menu. if (!(UIModeController::getInstance()->isUIModeKid() && - !Settings::getInstance()->getBool("EnableMenuKidMode")) && - config->isMappedTo("start", input) && input.value != 0) { + !Settings::getInstance()->getBool("EnableMenuKidMode")) && + config->isMappedTo("start", input) && input.value != 0) { // If we don't stop the scrolling here, it will continue to // run after closing the menu. if (mSystemListView->isScrolling()) @@ -854,7 +857,7 @@ void ViewController::render(const Transform4x4f& parentTrans) // Camera position, position + size. Vector3f viewStart = transInverse.translation(); Vector3f viewEnd = transInverse * Vector3f(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight(), 0)); + static_cast(Renderer::getScreenHeight(), 0)); // Keep track of UI mode changes. UIModeController::getInstance()->monitorUIMode(); @@ -870,11 +873,11 @@ void ViewController::render(const Transform4x4f& parentTrans) if (it->second == mCurrentView || (it->second == mPreviousView && isCameraMoving())) { // Clipping. Vector3f guiStart = it->second->getPosition(); - Vector3f guiEnd = it->second->getPosition() + Vector3f(it->second->getSize().x(), - it->second->getSize().y(), 0); + Vector3f guiEnd = it->second->getPosition() + + Vector3f(it->second->getSize().x(), it->second->getSize().y(), 0); if (guiEnd.x() >= viewStart.x() && guiEnd.y() >= viewStart.y() && - guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y()) + guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y()) it->second->render(trans); } } @@ -887,7 +890,7 @@ void ViewController::render(const Transform4x4f& parentTrans) unsigned int fadeColor = 0x00000000 | static_cast(mFadeOpacity * 255); Renderer::setMatrix(parentTrans); Renderer::drawRect(0.0f, 0.0f, static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight()), fadeColor, fadeColor); + static_cast(Renderer::getScreenHeight()), fadeColor, fadeColor); } } @@ -895,13 +898,14 @@ void ViewController::preload() { unsigned int systemCount = static_cast(SystemData::sSystemVector.size()); - for (auto it = SystemData::sSystemVector.cbegin(); - it != SystemData::sSystemVector.cend(); it ++) { + for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); + it++) { if (Settings::getInstance()->getBool("SplashScreen") && - Settings::getInstance()->getBool("SplashScreenProgress")) { - mWindow->renderLoadingScreen("Loading '" + (*it)->getFullName() + "' (" + - std::to_string(std::distance(SystemData::sSystemVector.cbegin(), it)+1) + - "/" + std::to_string(systemCount) + ")"); + Settings::getInstance()->getBool("SplashScreenProgress")) { + mWindow->renderLoadingScreen( + "Loading '" + (*it)->getFullName() + "' (" + + std::to_string(std::distance(SystemData::sSystemVector.cbegin(), it) + 1) + "/" + + std::to_string(systemCount) + ")"); } (*it)->getIndex()->resetFilters(); getGameListView(*it); @@ -1028,7 +1032,7 @@ std::vector ViewController::getHelpPrompts() prompts = mCurrentView->getHelpPrompts(); if (!(UIModeController::getInstance()->isUIModeKid() && - !Settings::getInstance()->getBool("EnableMenuKidMode"))) + !Settings::getInstance()->getBool("EnableMenuKidMode"))) prompts.push_back(HelpPrompt("start", "menu")); return prompts; } diff --git a/es-app/src/views/ViewController.h b/es-app/src/views/ViewController.h index ec3e2ade3..17d5f78ee 100644 --- a/es-app/src/views/ViewController.h +++ b/es-app/src/views/ViewController.h @@ -13,11 +13,11 @@ #ifndef ES_APP_VIEWS_VIEW_CONTROLLER_H #define ES_APP_VIEWS_VIEW_CONTROLLER_H +#include "FileData.h" +#include "GuiComponent.h" #include "guis/GuiComplexTextEditPopup.h" #include "guis/GuiMsgBox.h" #include "renderers/Renderer.h" -#include "FileData.h" -#include "GuiComponent.h" #include @@ -46,8 +46,10 @@ public: // If a basic view detected a metadata change, it can request to recreate // the current gamelist view (as it may change to be detailed). void reloadGameListView(IGameListView* gamelist, bool reloadTheme = false); - inline void reloadGameListView(SystemData* system, bool reloadTheme = false) - { reloadGameListView(getGameListView(system).get(), reloadTheme); } + void reloadGameListView(SystemData* system, bool reloadTheme = false) + { + reloadGameListView(getGameListView(system).get(), reloadTheme); + } // Reload everything with a theme. // Used when the "ThemeSet" setting changes. void reloadAll(); @@ -67,22 +69,26 @@ public: void stopScrolling(); void onFileChanged(FileData* file, bool reloadGameList); - void triggerGameLaunch(FileData* game) { mGameToLaunch = game; mLockInput = true; }; - bool getGameLaunchTriggered() { return (mGameToLaunch != nullptr); }; + void triggerGameLaunch(FileData* game) + { + mGameToLaunch = game; + mLockInput = true; + }; + bool getGameLaunchTriggered() { return (mGameToLaunch != nullptr); } bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; void render(const Transform4x4f& parentTrans) override; enum ViewMode { - NOTHING, + NOTHING, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). START_SCREEN, SYSTEM_SELECT, GAME_LIST }; enum GameListViewStyle { - AUTOMATIC, + AUTOMATIC, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). BASIC, DETAILED, GRID, @@ -93,18 +99,18 @@ public: ViewMode viewing; GameListViewStyle viewstyle; - inline SystemData* getSystem() const + SystemData* getSystem() const { assert(viewing == GAME_LIST || viewing == SYSTEM_SELECT); return system; } - private: - friend ViewController; - SystemData* system; + private: + friend ViewController; + SystemData* system; }; - inline const State& getState() const { return mState; } + const State& getState() const { return mState; } virtual std::vector getHelpPrompts() override; virtual HelpStyle getHelpStyle() override; @@ -143,6 +149,9 @@ private: std::map> mGameListViews; std::shared_ptr mSystemListView; + FileData* mGameToLaunch; + State mState; + Transform4x4f mCamera; bool mSystemViewTransition; bool mWrappedViews; @@ -151,9 +160,6 @@ private: bool mCancelledTransition; // Needed only for the Fade transition style. bool mLockInput; bool mNextSystem; - FileData* mGameToLaunch; - - State mState; }; #endif // ES_APP_VIEWS_VIEW_CONTROLLER_H diff --git a/es-app/src/views/gamelist/BasicGameListView.cpp b/es-app/src/views/gamelist/BasicGameListView.cpp index cf950e338..85c5fe64f 100644 --- a/es-app/src/views/gamelist/BasicGameListView.cpp +++ b/es-app/src/views/gamelist/BasicGameListView.cpp @@ -8,15 +8,16 @@ #include "views/gamelist/BasicGameListView.h" -#include "utils/FileSystemUtil.h" -#include "views/UIModeController.h" -#include "views/ViewController.h" #include "CollectionSystemsManager.h" #include "Settings.h" #include "SystemData.h" +#include "utils/FileSystemUtil.h" +#include "views/UIModeController.h" +#include "views/ViewController.h" BasicGameListView::BasicGameListView(Window* window, FileData* root) - : ISimpleGameListView(window, root), mList(window) + : ISimpleGameListView(window, root) + , mList(window) { mList.setSize(mSize.x(), mSize.y() * 0.8f); mList.setPosition(0, mSize.y() * 0.2f); @@ -89,16 +90,16 @@ void BasicGameListView::populateList(const std::vector& files, FileDa } if ((*it)->getFavorite() && favoriteStar && - mRoot->getSystem()->getName() != "favorites") { + mRoot->getSystem()->getName() != "favorites") { if (Settings::getInstance()->getBool("SpecialCharsASCII")) - mList.add(inCollectionPrefix + "* " + - (*it)->getName(), *it, ((*it)->getType() == FOLDER)); + mList.add(inCollectionPrefix + "* " + (*it)->getName(), *it, + ((*it)->getType() == FOLDER)); else mList.add(inCollectionPrefix + ViewController::FAVORITE_CHAR + " " + - (*it)->getName(), *it, ((*it)->getType() == FOLDER)); + (*it)->getName(), + *it, ((*it)->getType() == FOLDER)); } - else if ((*it)->getType() == FOLDER && - mRoot->getSystem()->getName() != "collections") { + else if ((*it)->getType() == FOLDER && mRoot->getSystem()->getName() != "collections") { if (Settings::getInstance()->getBool("SpecialCharsASCII")) mList.add("# " + (*it)->getName(), *it, true); else @@ -117,11 +118,6 @@ void BasicGameListView::populateList(const std::vector& files, FileDa generateFirstLetterIndex(files); } -FileData* BasicGameListView::getCursor() -{ - return mList.getSelected(); -} - void BasicGameListView::setCursor(FileData* cursor) { if (!mList.setCursor(cursor) && (!cursor->isPlaceHolder())) { @@ -133,6 +129,7 @@ void BasicGameListView::setCursor(FileData* cursor) if (mCursorStack.empty() || mCursorStack.top() != cursor->getParent()) { std::stack tmp; FileData* ptr = cursor->getParent(); + while (ptr && ptr != mRoot) { tmp.push(ptr); ptr = ptr->getParent(); @@ -148,31 +145,6 @@ void BasicGameListView::setCursor(FileData* cursor) } } -FileData* BasicGameListView::getNextEntry() -{ - return mList.getNext(); -} - -FileData* BasicGameListView::getPreviousEntry() -{ - return mList.getPrevious(); -} - -FileData* BasicGameListView::getFirstEntry() -{ - return mList.getFirst(); -} - -FileData* BasicGameListView::getLastEntry() -{ - return mList.getLast(); -} - -FileData* BasicGameListView::getFirstGameEntry() -{ - return mFirstGameEntry; -} - void BasicGameListView::addPlaceholder(FileData* firstEntry) { // Empty list, add a placeholder. @@ -186,18 +158,9 @@ void BasicGameListView::addPlaceholder(FileData* firstEntry) mList.add(placeholder->getName(), placeholder, (placeholder->getType() == PLACEHOLDER)); } -std::string BasicGameListView::getQuickSystemSelectRightButton() -{ - return "right"; -} - -std::string BasicGameListView::getQuickSystemSelectLeftButton() -{ - return "left"; -} - void BasicGameListView::launch(FileData* game) { + // This triggers ViewController to launch the game. ViewController::get()->triggerGameLaunch(game); } @@ -235,7 +198,7 @@ void BasicGameListView::remove(FileData* game, bool deleteFile) if (deleteFile) { parent->sort(parent->getSortTypeFromString(parent->getSortTypeString()), - Settings::getInstance()->getBool("FavoritesFirst")); + Settings::getInstance()->getBool("FavoritesFirst")); onFileChanged(parent, false); } } @@ -251,8 +214,8 @@ void BasicGameListView::removeMedia(FileData* game) // If there are no media files left in the directory after the deletion, then remove // the directory too. Remove any empty parent directories as well. - auto removeEmptyDirFunc = [] - (std::string systemMediaDir, std::string mediaType, std::string path) { + auto removeEmptyDirFunc = [](std::string systemMediaDir, std::string mediaType, + std::string path) { std::string parentPath = Utils::FileSystem::getParent(path); while (parentPath != systemMediaDir + "/" + mediaType) { if (Utils::FileSystem::getDirContent(parentPath).size() == 0) { @@ -321,13 +284,13 @@ std::vector BasicGameListView::getHelpPrompts() std::vector prompts; if (Settings::getInstance()->getBool("QuickSystemSelect") && - SystemData::sSystemVector.size() > 1) + SystemData::sSystemVector.size() > 1) prompts.push_back(HelpPrompt("left/right", "system")); prompts.push_back(HelpPrompt("up/down", "choose")); if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() && - ViewController::get()->getState().viewing == ViewController::GAME_LIST) + ViewController::get()->getState().viewing == ViewController::GAME_LIST) prompts.push_back(HelpPrompt("a", "enter")); else prompts.push_back(HelpPrompt("a", "launch")); @@ -341,24 +304,24 @@ std::vector BasicGameListView::getHelpPrompts() prompts.push_back(HelpPrompt("thumbstickclick", "random")); if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && - !CollectionSystemsManager::get()->isEditing() && mCursorStack.empty() && - ViewController::get()->getState().viewing == ViewController::GAME_LIST && - ViewController::get()->getState().viewstyle != ViewController::BASIC) { + !CollectionSystemsManager::get()->isEditing() && mCursorStack.empty() && + ViewController::get()->getState().viewing == ViewController::GAME_LIST && + ViewController::get()->getState().viewstyle != ViewController::BASIC) { prompts.push_back(HelpPrompt("y", "jump to game")); } else if (mRoot->getSystem()->isGameSystem() && - (mRoot->getSystem()->getThemeFolder() != "custom-collections" || - !mCursorStack.empty()) && - !UIModeController::getInstance()->isUIModeKid() && - !UIModeController::getInstance()->isUIModeKiosk() && - (Settings::getInstance()->getBool("FavoritesAddButton") || - CollectionSystemsManager::get()->isEditing())) { + (mRoot->getSystem()->getThemeFolder() != "custom-collections" || + !mCursorStack.empty()) && + !UIModeController::getInstance()->isUIModeKid() && + !UIModeController::getInstance()->isUIModeKiosk() && + (Settings::getInstance()->getBool("FavoritesAddButton") || + CollectionSystemsManager::get()->isEditing())) { std::string prompt = CollectionSystemsManager::get()->getEditingCollection(); prompts.push_back(HelpPrompt("y", prompt)); } else if (mRoot->getSystem()->isGameSystem() && - mRoot->getSystem()->getThemeFolder() == "custom-collections" && - CollectionSystemsManager::get()->isEditing()) { + mRoot->getSystem()->getThemeFolder() == "custom-collections" && + CollectionSystemsManager::get()->isEditing()) { std::string prompt = CollectionSystemsManager::get()->getEditingCollection(); prompts.push_back(HelpPrompt("y", prompt)); } diff --git a/es-app/src/views/gamelist/BasicGameListView.h b/es-app/src/views/gamelist/BasicGameListView.h index 414bbf8c8..c204433bc 100644 --- a/es-app/src/views/gamelist/BasicGameListView.h +++ b/es-app/src/views/gamelist/BasicGameListView.h @@ -21,30 +21,33 @@ public: virtual void onFileChanged(FileData* file, bool reloadGameList) override; virtual void onThemeChanged(const std::shared_ptr& theme) override; - - virtual FileData* getCursor() override; virtual void setCursor(FileData* cursor) override; - virtual FileData* getNextEntry() override; - virtual FileData* getPreviousEntry() override; - virtual FileData* getFirstEntry() override; - virtual FileData* getLastEntry() override; - virtual FileData* getFirstGameEntry() override; + + virtual FileData* getCursor() override { return mList.getSelected(); } + virtual FileData* getNextEntry() override { return mList.getNext(); } + virtual FileData* getPreviousEntry() override { return mList.getPrevious(); } + virtual FileData* getFirstEntry() override { return mList.getFirst(); } + virtual FileData* getLastEntry() override { return mList.getLast(); } + virtual FileData* getFirstGameEntry() override { return mFirstGameEntry; } virtual std::string getName() const override { return "basic"; } virtual std::vector getHelpPrompts() override; - virtual void launch(FileData* game) override; - virtual bool isListScrolling() override { return mList.isScrolling(); }; - virtual void stopListScrolling() override { mList.stopScrolling(); }; + virtual bool isListScrolling() override { return mList.isScrolling(); } + virtual void stopListScrolling() override { mList.stopScrolling(); } virtual const std::vector& getFirstLetterIndex() override - { return mFirstLetterIndex; }; + { + return mFirstLetterIndex; + } + virtual void addPlaceholder(FileData* firstEntry = nullptr) override; + virtual void launch(FileData* game) override; protected: - virtual std::string getQuickSystemSelectRightButton() override; - virtual std::string getQuickSystemSelectLeftButton() override; + virtual std::string getQuickSystemSelectRightButton() override { return "right"; } + virtual std::string getQuickSystemSelectLeftButton() override { return "left"; } virtual void populateList(const std::vector& files, FileData* firstEntry) override; virtual void remove(FileData* game, bool deleteFile) override; virtual void removeMedia(FileData* game) override; diff --git a/es-app/src/views/gamelist/DetailedGameListView.cpp b/es-app/src/views/gamelist/DetailedGameListView.cpp index 6094e0e31..7d4ea3baa 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.cpp +++ b/es-app/src/views/gamelist/DetailedGameListView.cpp @@ -8,45 +8,40 @@ #include "views/gamelist/DetailedGameListView.h" -#include "animations/LambdaAnimation.h" -#include "views/ViewController.h" #include "CollectionSystemsManager.h" #include "SystemData.h" +#include "animations/LambdaAnimation.h" +#include "views/ViewController.h" #define FADE_IN_START_OPACITY 0.5f #define FADE_IN_TIME 650 -DetailedGameListView::DetailedGameListView( - Window* window, - FileData* root) - : BasicGameListView(window, root), - mDescContainer(window), - mDescription(window), - mGamelistInfo(window), - - mThumbnail(window), - mMarquee(window), - mImage(window), - - mLblRating(window), - mLblReleaseDate(window), - mLblDeveloper(window), - mLblPublisher(window), - mLblGenre(window), - mLblPlayers(window), - mLblLastPlayed(window), - mLblPlayCount(window), - - mRating(window), - mReleaseDate(window), - mDeveloper(window), - mPublisher(window), - mGenre(window), - mPlayers(window), - mLastPlayed(window), - mPlayCount(window), - mName(window), - mLastUpdated(nullptr) +DetailedGameListView::DetailedGameListView(Window* window, FileData* root) + : BasicGameListView(window, root) + , mDescContainer(window) + , mDescription(window) + , mGamelistInfo(window) + , mThumbnail(window) + , mMarquee(window) + , mImage(window) + , mLblRating(window) + , mLblReleaseDate(window) + , mLblDeveloper(window) + , mLblPublisher(window) + , mLblGenre(window) + , mLblPlayers(window) + , mLblLastPlayed(window) + , mLblPlayCount(window) + , mRating(window) + , mReleaseDate(window) + , mDeveloper(window) + , mPublisher(window) + , mGenre(window) + , mPlayers(window) + , mLastPlayed(window) + , mPlayCount(window) + , mName(window) + , mLastUpdated(nullptr) { const float padding = 0.01f; @@ -114,8 +109,8 @@ DetailedGameListView::DetailedGameListView( addChild(&mName); mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); - mDescContainer.setSize(mSize.x() * (0.50f - 2 * padding), mSize.y() - - mDescContainer.getPosition().y()); + mDescContainer.setSize(mSize.x() * (0.50f - 2.0f * padding), + mSize.y() - mDescContainer.getPosition().y()); mDescContainer.setAutoScroll(true); mDescContainer.setDefaultZIndex(40); addChild(&mDescContainer); @@ -140,20 +135,20 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr& them using namespace ThemeFlags; mThumbnail.applyTheme(theme, getName(), "md_thumbnail", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); + POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); mMarquee.applyTheme(theme, getName(), "md_marquee", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); + POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); mImage.applyTheme(theme, getName(), "md_image", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); + POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); mName.applyTheme(theme, getName(), "md_name", ALL); initMDLabels(); std::vector labels = getMDLabels(); assert(labels.size() == 8); - std::vector lblElements = { - "md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher", - "md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount" - }; + std::vector lblElements = { "md_lbl_rating", "md_lbl_releasedate", + "md_lbl_developer", "md_lbl_publisher", + "md_lbl_genre", "md_lbl_players", + "md_lbl_lastplayed", "md_lbl_playcount" }; for (unsigned int i = 0; i < labels.size(); i++) labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); @@ -161,19 +156,19 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr& them initMDValues(); std::vector values = getMDValues(); assert(values.size() == 8); - std::vector valElements = { - "md_rating", "md_releasedate", "md_developer", "md_publisher", - "md_genre", "md_players", "md_lastplayed", "md_playcount" - }; + std::vector valElements = { "md_rating", "md_releasedate", "md_developer", + "md_publisher", "md_genre", "md_players", + "md_lastplayed", "md_playcount" }; for (unsigned int i = 0; i < values.size(); i++) values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT); mDescContainer.applyTheme(theme, getName(), "md_description", - POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); + POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); mDescription.setSize(mDescContainer.getSize().x(), 0); - mDescription.applyTheme(theme, getName(), "md_description", - ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); + mDescription.applyTheme( + theme, getName(), "md_description", + ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT); // If there is no position defined in the theme for gamelistInfo, then hide it. @@ -205,7 +200,7 @@ void DetailedGameListView::initMDLabels() } else { // Work from the last component. - GuiComponent* lc = components[i-1]; + GuiComponent* lc = components[i - 1]; pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); } @@ -232,13 +227,13 @@ void DetailedGameListView::initMDValues() float bottom = 0.0f; - const float colSize = (mSize.x() * 0.48f) / 2; + const float colSize = (mSize.x() * 0.48f) / 2.0f; for (unsigned int i = 0; i < labels.size(); i++) { - const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2; + const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2.0f; values[i]->setPosition(labels[i]->getPosition() + - Vector3f(labels[i]->getSize().x(), heightDiff, 0)); + Vector3f(labels[i]->getSize().x(), heightDiff, 0)); values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y()); - values[i]->setDefaultZIndex(40); + values[i]->setDefaultZIndex(40.0f); float testBot = values[i]->getPosition().y() + values[i]->getSize().y(); @@ -247,8 +242,8 @@ void DetailedGameListView::initMDValues() } mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f); - mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - - mDescContainer.getPosition().y()); + mDescContainer.setSize(mDescContainer.getSize().x(), + mSize.y() - mDescContainer.getPosition().y()); } void DetailedGameListView::updateInfoPanel() @@ -267,7 +262,7 @@ void DetailedGameListView::updateInfoPanel() if (file) { // Always hide the metadata fields if browsing grouped custom collections. if (file->getSystem()->isCustomCollection() && - file->getPath() == file->getSystem()->getName()) + file->getPath() == file->getSystem()->getName()) hideMetaDataFields = true; else hideMetaDataFields = (file->metadata.get("hidemetadata") == "true"); @@ -283,9 +278,9 @@ void DetailedGameListView::updateInfoPanel() // or if we're in the grouped custom collection view. if (mList.isScrolling()) if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") || - (mLastUpdated->getSystem()->isCustomCollection() && - mLastUpdated->getPath() == mLastUpdated->getSystem()->getName())) - hideMetaDataFields = true; + (mLastUpdated->getSystem()->isCustomCollection() && + mLastUpdated->getPath() == mLastUpdated->getSystem()->getName())) + hideMetaDataFields = true; if (hideMetaDataFields) { mLblRating.setVisible(false); @@ -333,9 +328,9 @@ void DetailedGameListView::updateInfoPanel() // which will generate a description of three random games and return a pointer to // the first of these so that we can display its game media. if (file->getSystem()->isCustomCollection() && - file->getPath() == file->getSystem()->getName()) { - mRandomGame = CollectionSystemsManager::get()-> - updateCollectionFolderMetadata(file->getSystem()); + file->getPath() == file->getSystem()->getName()) { + mRandomGame = + CollectionSystemsManager::get()->updateCollectionFolderMetadata(file->getSystem()); if (mRandomGame) { mThumbnail.setImage(mRandomGame->getThumbnailPath()); mMarquee.setImage(mRandomGame->getMarqueePath()); @@ -366,21 +361,21 @@ void DetailedGameListView::updateInfoPanel() if (mIsFiltered) { if (mFilteredGameCountAll == mFilteredGameCount) gamelistInfoString += ViewController::FILTER_CHAR + " " + - std::to_string(mFilteredGameCount) + " / " + - std::to_string(mGameCount); + std::to_string(mFilteredGameCount) + " / " + + std::to_string(mGameCount); else gamelistInfoString += ViewController::FILTER_CHAR + " " + - std::to_string(mFilteredGameCount) + " + " + - std::to_string(mFilteredGameCountAll - mFilteredGameCount) + " / " + - std::to_string(mGameCount); + std::to_string(mFilteredGameCount) + " + " + + std::to_string(mFilteredGameCountAll - mFilteredGameCount) + + " / " + std::to_string(mGameCount); } else { - gamelistInfoString += ViewController::CONTROLLER_CHAR + " " + - std::to_string(mGameCount); + gamelistInfoString += + ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount); if (!(file->getSystem()->isCollection() && - file->getSystem()->getFullName() == "favorites")) - gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " - + std::to_string(mFavoritesGameCount); + file->getSystem()->getFullName() == "favorites")) + gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " + + std::to_string(mFavoritesGameCount); } if (mIsFolder && infoAlign != ALIGN_RIGHT) @@ -390,9 +385,9 @@ void DetailedGameListView::updateInfoPanel() // Fade in the game image. auto func = [this](float t) { - mImage.setOpacity(static_cast(Math::lerp( - static_cast(FADE_IN_START_OPACITY), 1.0f, t) * 255)); - }; + mImage.setOpacity(static_cast( + Math::lerp(static_cast(FADE_IN_START_OPACITY), 1.0f, t) * 255)); + }; mImage.setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false); mDescription.setText(file->metadata.get("desc")); diff --git a/es-app/src/views/gamelist/GridGameListView.cpp b/es-app/src/views/gamelist/GridGameListView.cpp index fd7a26140..c30339ea0 100644 --- a/es-app/src/views/gamelist/GridGameListView.cpp +++ b/es-app/src/views/gamelist/GridGameListView.cpp @@ -8,48 +8,42 @@ #include "views/gamelist/GridGameListView.h" -#include "animations/LambdaAnimation.h" -#include "views/UIModeController.h" -#include "views/ViewController.h" #include "CollectionSystemsManager.h" #include "Settings.h" #include "Sound.h" #include "SystemData.h" +#include "animations/LambdaAnimation.h" +#include "views/UIModeController.h" +#include "views/ViewController.h" #define FADE_IN_START_OPACITY 0.5f #define FADE_IN_TIME 650 -GridGameListView::GridGameListView( - Window* window, - FileData* root) - : ISimpleGameListView(window, root), - - mGrid(window), - mMarquee(window), - mImage(window), - - mDescContainer(window), - mDescription(window), - mGamelistInfo(window), - - mLblRating(window), - mLblReleaseDate(window), - mLblDeveloper(window), - mLblPublisher(window), - mLblGenre(window), - mLblPlayers(window), - mLblLastPlayed(window), - mLblPlayCount(window), - - mRating(window), - mReleaseDate(window), - mDeveloper(window), - mPublisher(window), - mGenre(window), - mPlayers(window), - mLastPlayed(window), - mPlayCount(window), - mName(window) +GridGameListView::GridGameListView(Window* window, FileData* root) + : ISimpleGameListView(window, root) + , mGrid(window) + , mMarquee(window) + , mImage(window) + , mDescContainer(window) + , mDescription(window) + , mGamelistInfo(window) + , mLblRating(window) + , mLblReleaseDate(window) + , mLblDeveloper(window) + , mLblPublisher(window) + , mLblGenre(window) + , mLblPlayers(window) + , mLblLastPlayed(window) + , mLblPlayCount(window) + , mRating(window) + , mReleaseDate(window) + , mDeveloper(window) + , mPublisher(window) + , mGenre(window) + , mPlayers(window) + , mLastPlayed(window) + , mPlayCount(window) + , mName(window) { const float padding = 0.01f; @@ -95,8 +89,8 @@ GridGameListView::GridGameListView( addChild(&mName); mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); - mDescContainer.setSize(mSize.x() * (0.50f - 2 * padding), mSize.y() - - mDescContainer.getPosition().y()); + mDescContainer.setSize(mSize.x() * (0.50f - 2.0f * padding), + mSize.y() - mDescContainer.getPosition().y()); mDescContainer.setAutoScroll(true); mDescContainer.setDefaultZIndex(40); addChild(&mDescContainer); @@ -107,7 +101,7 @@ GridGameListView::GridGameListView( mMarquee.setOrigin(0.5f, 0.5f); mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f); - mMarquee.setMaxSize(mSize.x() * (0.5f - 2 * padding), mSize.y() * 0.18f); + mMarquee.setMaxSize(mSize.x() * (0.5f - 2.0f * padding), mSize.y() * 0.18f); mMarquee.setDefaultZIndex(35); mMarquee.setVisible(false); addChild(&mMarquee); @@ -130,10 +124,6 @@ GridGameListView::GridGameListView( updateInfoPanel(); } -GridGameListView::~GridGameListView() -{ -} - void GridGameListView::onFileChanged(FileData* file, bool reloadGameList) { if (reloadGameList) { @@ -145,11 +135,6 @@ void GridGameListView::onFileChanged(FileData* file, bool reloadGameList) ISimpleGameListView::onFileChanged(file, reloadGameList); } -FileData* GridGameListView::getCursor() -{ - return mGrid.getSelected(); -} - void GridGameListView::setCursor(FileData* cursor) { if (!mGrid.setCursor(cursor) && (!cursor->isPlaceHolder())) { @@ -176,47 +161,11 @@ void GridGameListView::setCursor(FileData* cursor) } } -FileData* GridGameListView::getNextEntry() -{ - return mGrid.getNext();; -} - -FileData* GridGameListView::getPreviousEntry() -{ - return mGrid.getPrevious(); -} - -FileData* GridGameListView::getFirstEntry() -{ - return mGrid.getFirst();; -} - -FileData* GridGameListView::getLastEntry() -{ - return mGrid.getLast(); -} - -FileData* GridGameListView::getFirstGameEntry() -{ - return firstGameEntry; -} - -std::string GridGameListView::getQuickSystemSelectRightButton() -{ - return "rightshoulder"; -} - -std::string GridGameListView::getQuickSystemSelectLeftButton() -{ - return "leftshoulder"; -} - bool GridGameListView::input(InputConfig* config, Input input) { - if (input.value == 0 && (config->isMappedLike("left", input) || - config->isMappedLike("right", input) || - (config->isMappedLike("up", input)) || - (config->isMappedLike("down", input)) )) + if (input.value == 0 && + (config->isMappedLike("left", input) || config->isMappedLike("right", input) || + (config->isMappedLike("up", input)) || (config->isMappedLike("down", input)))) NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND); if (input.value != 0 && config->isMappedLike("righttrigger", input)) { @@ -288,17 +237,17 @@ void GridGameListView::onThemeChanged(const std::shared_ptr& theme) mGrid.applyTheme(theme, getName(), "gamegrid", ALL); mName.applyTheme(theme, getName(), "md_name", ALL); mMarquee.applyTheme(theme, getName(), "md_marquee", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); + POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); mImage.applyTheme(theme, getName(), "md_image", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); + POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); initMDLabels(); std::vector labels = getMDLabels(); assert(labels.size() == 8); - std::vector lblElements = { - "md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher", - "md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount" - }; + std::vector lblElements = { "md_lbl_rating", "md_lbl_releasedate", + "md_lbl_developer", "md_lbl_publisher", + "md_lbl_genre", "md_lbl_players", + "md_lbl_lastplayed", "md_lbl_playcount" }; for (unsigned int i = 0; i < labels.size(); i++) labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); @@ -306,19 +255,19 @@ void GridGameListView::onThemeChanged(const std::shared_ptr& theme) initMDValues(); std::vector values = getMDValues(); assert(values.size() == 8); - std::vector valElements = { - "md_rating", "md_releasedate", "md_developer", "md_publisher", - "md_genre", "md_players", "md_lastplayed", "md_playcount" - }; + std::vector valElements = { "md_rating", "md_releasedate", "md_developer", + "md_publisher", "md_genre", "md_players", + "md_lastplayed", "md_playcount" }; for (unsigned int i = 0; i < values.size(); i++) values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT); mDescContainer.applyTheme(theme, getName(), "md_description", - POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); + POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); mDescription.setSize(mDescContainer.getSize().x(), 0); - mDescription.applyTheme(theme, getName(), "md_description", - ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); + mDescription.applyTheme( + theme, getName(), "md_description", + ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); // Repopulate list in case a new theme is displaying a different image. // Preserve selection. @@ -336,6 +285,12 @@ void GridGameListView::onThemeChanged(const std::shared_ptr& theme) sortChildren(); } +void GridGameListView::onShow() +{ + GuiComponent::onShow(); + updateInfoPanel(); +} + void GridGameListView::initMDLabels() { std::vector components = getMDLabels(); @@ -356,7 +311,7 @@ void GridGameListView::initMDLabels() } else { // Work from the last component. - GuiComponent* lc = components[i-1]; + GuiComponent* lc = components[i - 1]; pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); } @@ -383,11 +338,11 @@ void GridGameListView::initMDValues() float bottom = 0.0f; - const float colSize = (mSize.x() * 0.48f) / 2; + const float colSize = (mSize.x() * 0.48f) / 2.0f; for (unsigned int i = 0; i < labels.size(); i++) { - const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2; + const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2.0f; values[i]->setPosition(labels[i]->getPosition() + - Vector3f(labels[i]->getSize().x(), heightDiff, 0)); + Vector3f(labels[i]->getSize().x(), heightDiff, 0)); values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y()); values[i]->setDefaultZIndex(40); @@ -397,8 +352,8 @@ void GridGameListView::initMDValues() } mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f); - mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - - mDescContainer.getPosition().y()); + mDescContainer.setSize(mDescContainer.getSize().x(), + mSize.y() - mDescContainer.getPosition().y()); } void GridGameListView::updateInfoPanel() @@ -465,21 +420,22 @@ void GridGameListView::updateInfoPanel() if (mIsFiltered) { if (mFilteredGameCountAll == mFilteredGameCount) - gamelistInfoString += ViewController::FILTER_CHAR + " " - + std::to_string(mFilteredGameCount) + " / " + std::to_string(mGameCount); + gamelistInfoString += ViewController::FILTER_CHAR + " " + + std::to_string(mFilteredGameCount) + " / " + + std::to_string(mGameCount); else gamelistInfoString += ViewController::FILTER_CHAR + " " + - std::to_string(mFilteredGameCount) + " + " + - std::to_string(mFilteredGameCountAll - mFilteredGameCount) + " / " + - std::to_string(mGameCount); + std::to_string(mFilteredGameCount) + " + " + + std::to_string(mFilteredGameCountAll - mFilteredGameCount) + + " / " + std::to_string(mGameCount); } else { - gamelistInfoString += ViewController::CONTROLLER_CHAR + " " + - std::to_string(mGameCount); + gamelistInfoString += + ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount); if (!(file->getSystem()->isCollection() && - file->getSystem()->getFullName() == "favorites")) + file->getSystem()->getFullName() == "favorites")) gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " + - std::to_string(mFavoritesGameCount); + std::to_string(mFavoritesGameCount); } if (mIsFolder && infoAlign != ALIGN_RIGHT) @@ -489,9 +445,9 @@ void GridGameListView::updateInfoPanel() // Fade in the game image. auto func = [this](float t) { - mImage.setOpacity(static_cast(Math::lerp( - static_cast(FADE_IN_START_OPACITY), 1.0f, t) * 255)); - }; + mImage.setOpacity(static_cast( + Math::lerp(static_cast(FADE_IN_START_OPACITY), 1.0f, t) * 255)); + }; mImage.setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false); mDescription.setText(file->metadata.get("desc")); @@ -535,10 +491,10 @@ void GridGameListView::updateInfoPanel() // An animation is playing, then animate if reverse != fadingOut. // An animation is not playing, then animate if opacity != our target opacity. if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || - (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) { + (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) { auto func = [comp](float t) { - // TEMPORARY - This does not seem to work, needs to be reviewed later. - // comp->setOpacity(static_cast(Math::lerp(0.0f, 1.0f, t) * 255)); + // TEMPORARY - This does not seem to work, needs to be reviewed later. + // comp->setOpacity(static_cast(Math::lerp(0.0f, 1.0f, t) * 255)); }; comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); } @@ -560,6 +516,7 @@ void GridGameListView::addPlaceholder(FileData* firstEntry) void GridGameListView::launch(FileData* game) { + // This triggers ViewController to launch the game. ViewController::get()->triggerGameLaunch(game); } @@ -605,8 +562,8 @@ void GridGameListView::removeMedia(FileData* game) // If there are no media files left in the directory after the deletion, then remove // the directory too. Remove any empty parent directories as well. - auto removeEmptyDirFunc = [] - (std::string systemMediaDir, std::string mediaType, std::string path) { + auto removeEmptyDirFunc = [](std::string systemMediaDir, std::string mediaType, + std::string path) { std::string parentPath = Utils::FileSystem::getParent(path); while (parentPath != systemMediaDir + "/" + mediaType) { if (Utils::FileSystem::getDirContent(parentPath).size() == 0) { @@ -707,7 +664,7 @@ std::vector GridGameListView::getHelpPrompts() prompts.push_back(HelpPrompt("up/down/left/right", "choose")); if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() && - ViewController::get()->getState().viewing == ViewController::GAME_LIST) + ViewController::get()->getState().viewing == ViewController::GAME_LIST) prompts.push_back(HelpPrompt("a", "enter")); else prompts.push_back(HelpPrompt("a", "launch")); @@ -715,32 +672,30 @@ std::vector GridGameListView::getHelpPrompts() prompts.push_back(HelpPrompt("b", "back")); if (mRoot->getSystem()->isGameSystem() && - mRoot->getSystem()->getThemeFolder() != "custom-collections") + mRoot->getSystem()->getThemeFolder() != "custom-collections") prompts.push_back(HelpPrompt("x", "view media")); if (mRoot->getSystem()->isGameSystem() && !mCursorStack.empty() && - mRoot->getSystem()->getThemeFolder() == "custom-collections") + mRoot->getSystem()->getThemeFolder() == "custom-collections") prompts.push_back(HelpPrompt("x", "view media")); if (!UIModeController::getInstance()->isUIModeKid()) prompts.push_back(HelpPrompt("back", "options")); - if (mRoot->getSystem()->isGameSystem() && - Settings::getInstance()->getBool("RandomAddButton")) + if (mRoot->getSystem()->isGameSystem() && Settings::getInstance()->getBool("RandomAddButton")) prompts.push_back(HelpPrompt("thumbstickclick", "random")); if (mRoot->getSystem()->isGameSystem() && - (mRoot->getSystem()->getThemeFolder() != "custom-collections" || - !mCursorStack.empty()) && - !UIModeController::getInstance()->isUIModeKid() && - !UIModeController::getInstance()->isUIModeKiosk() && - (Settings::getInstance()->getBool("FavoritesAddButton") || - CollectionSystemsManager::get()->isEditing())) { + (mRoot->getSystem()->getThemeFolder() != "custom-collections" || !mCursorStack.empty()) && + !UIModeController::getInstance()->isUIModeKid() && + !UIModeController::getInstance()->isUIModeKiosk() && + (Settings::getInstance()->getBool("FavoritesAddButton") || + CollectionSystemsManager::get()->isEditing())) { std::string prompt = CollectionSystemsManager::get()->getEditingCollection(); prompts.push_back(HelpPrompt("y", prompt)); } else if (mRoot->getSystem()->isGameSystem() && - mRoot->getSystem()->getThemeFolder() == "custom-collections" && - CollectionSystemsManager::get()->isEditing()) { + mRoot->getSystem()->getThemeFolder() == "custom-collections" && + CollectionSystemsManager::get()->isEditing()) { std::string prompt = CollectionSystemsManager::get()->getEditingCollection(); prompts.push_back(HelpPrompt("y", prompt)); } @@ -749,11 +704,6 @@ std::vector GridGameListView::getHelpPrompts() void GridGameListView::update(int deltaTime) { + // Update. ISimpleGameListView::update(deltaTime); } - -void GridGameListView::onShow() -{ - GuiComponent::onShow(); - updateInfoPanel(); -} diff --git a/es-app/src/views/gamelist/GridGameListView.h b/es-app/src/views/gamelist/GridGameListView.h index 7a9963983..f7a46ea6a 100644 --- a/es-app/src/views/gamelist/GridGameListView.h +++ b/es-app/src/views/gamelist/GridGameListView.h @@ -20,48 +20,50 @@ class GridGameListView : public ISimpleGameListView { public: GridGameListView(Window* window, FileData* root); - virtual ~GridGameListView(); + virtual ~GridGameListView() {} // Called when a FileData* is added, has its metadata changed, or is removed. virtual void onFileChanged(FileData* file, bool reloadGameList) override; virtual void onShow() override; - virtual void onThemeChanged(const std::shared_ptr& theme) override; - - virtual FileData* getCursor() override; virtual void setCursor(FileData* cursor) override; - virtual FileData* getNextEntry() override; - virtual FileData* getPreviousEntry() override; - virtual FileData* getFirstEntry() override; - virtual FileData* getLastEntry() override; - virtual FileData* getFirstGameEntry() override; - virtual bool input(InputConfig* config, Input input) override; + virtual FileData* getCursor() override { return mGrid.getSelected(); } + virtual FileData* getNextEntry() override { return mGrid.getNext(); } + virtual FileData* getPreviousEntry() override { return mGrid.getPrevious(); } + virtual FileData* getFirstEntry() override { return mGrid.getFirst(); } + virtual FileData* getLastEntry() override { return mGrid.getLast(); } + virtual FileData* getFirstGameEntry() override { return firstGameEntry; } virtual std::string getName() const override { return "grid"; } + virtual bool input(InputConfig* config, Input input) override; + virtual std::vector getHelpPrompts() override; virtual void launch(FileData* game) override; - virtual bool isListScrolling() override { return mGrid.isScrolling(); }; + virtual bool isListScrolling() override { return mGrid.isScrolling(); } virtual void stopListScrolling() override { mGrid.stopAllAnimations(); mGrid.stopScrolling(); - }; + } virtual const std::vector& getFirstLetterIndex() override - { return mFirstLetterIndex; }; + { + return mFirstLetterIndex; + } + virtual void addPlaceholder(FileData* firstEntry = nullptr) override; protected: - virtual void update(int deltaTime) override; - virtual std::string getQuickSystemSelectRightButton() override; - virtual std::string getQuickSystemSelectLeftButton() override; + virtual std::string getQuickSystemSelectRightButton() override { return "rightshoulder"; } + virtual std::string getQuickSystemSelectLeftButton() override { return "leftshoulder"; } virtual void populateList(const std::vector& files, FileData* firstEntry) override; virtual void remove(FileData* game, bool deleteFile) override; virtual void removeMedia(FileData* game) override; + virtual void update(int deltaTime) override; ImageGridComponent mGrid; // Points to the first game in the list, i.e. the first entry which is of the type 'GAME'. diff --git a/es-app/src/views/gamelist/IGameListView.cpp b/es-app/src/views/gamelist/IGameListView.cpp index f41cb5e53..64df0456f 100644 --- a/es-app/src/views/gamelist/IGameListView.cpp +++ b/es-app/src/views/gamelist/IGameListView.cpp @@ -8,18 +8,32 @@ #include "views/gamelist/IGameListView.h" -#include "guis/GuiGamelistOptions.h" -#include "views/UIModeController.h" -#include "views/ViewController.h" #include "AudioManager.h" #include "Sound.h" #include "Window.h" +#include "guis/GuiGamelistOptions.h" +#include "views/UIModeController.h" +#include "views/ViewController.h" + +IGameListView::IGameListView(Window* window, FileData* root) + : GuiComponent(window) + , mRoot(root) +{ + setSize(static_cast(Renderer::getScreenWidth()), + static_cast(Renderer::getScreenHeight())); +} + +void IGameListView::setTheme(const std::shared_ptr& theme) +{ + mTheme = theme; + onThemeChanged(theme); +} bool IGameListView::input(InputConfig* config, Input input) { // Select button opens GuiGamelistOptions. - if (!UIModeController::getInstance()->isUIModeKid() && - config->isMappedTo("back", input) && input.value) { + if (!UIModeController::getInstance()->isUIModeKid() && // Line break. + config->isMappedTo("back", input) && input.value) { ViewController::get()->cancelViewTransitions(); stopListScrolling(); mWindow->pushGui(new GuiGamelistOptions(mWindow, this->mRoot->getSystem())); @@ -28,9 +42,9 @@ bool IGameListView::input(InputConfig* config, Input input) // Ctrl-R reloads the view when debugging. else if (Settings::getInstance()->getBool("Debug") && - config->getDeviceId() == DEVICE_KEYBOARD && - (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL)) && - input.id == SDLK_r && input.value != 0) { + config->getDeviceId() == DEVICE_KEYBOARD && + (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL)) && input.id == SDLK_r && + input.value != 0) { LOG(LogDebug) << "IGameListView::input(): Reloading view"; ViewController::get()->reloadGameListView(this, true); return true; @@ -39,12 +53,6 @@ bool IGameListView::input(InputConfig* config, Input input) return GuiComponent::input(config, input); } -void IGameListView::setTheme(const std::shared_ptr& theme) -{ - mTheme = theme; - onThemeChanged(theme); -} - HelpStyle IGameListView::getHelpStyle() { HelpStyle style; @@ -60,9 +68,9 @@ void IGameListView::render(const Transform4x4f& parentTrans) float scaleY = trans.r1().y(); Vector2i pos(static_cast(std::round(trans.translation()[0])), - static_cast(std::round(trans.translation()[1]))); + static_cast(std::round(trans.translation()[1]))); Vector2i size(static_cast(std::round(mSize.x() * scaleX)), - static_cast(std::round(mSize.y() * scaleY))); + static_cast(std::round(mSize.y() * scaleY))); Renderer::pushClipRect(pos, size); renderChildren(trans); diff --git a/es-app/src/views/gamelist/IGameListView.h b/es-app/src/views/gamelist/IGameListView.h index 9ebd7075a..b1c39ac5e 100644 --- a/es-app/src/views/gamelist/IGameListView.h +++ b/es-app/src/views/gamelist/IGameListView.h @@ -9,9 +9,9 @@ #ifndef ES_APP_VIEWS_GAME_LIST_IGAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_IGAME_LIST_VIEW_H -#include "renderers/Renderer.h" #include "FileData.h" #include "GuiComponent.h" +#include "renderers/Renderer.h" class ThemeData; class Window; @@ -20,12 +20,7 @@ class Window; class IGameListView : public GuiComponent { public: - IGameListView(Window* window, FileData* root) : GuiComponent(window), mRoot(root) - { - setSize(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); - } - + IGameListView(Window* window, FileData* root); virtual ~IGameListView() {} // Called when a FileData* is added, has its metadata changed, or is removed. @@ -35,7 +30,7 @@ public: virtual void onThemeChanged(const std::shared_ptr& theme) = 0; void setTheme(const std::shared_ptr& theme); - inline const std::shared_ptr& getTheme() const { return mTheme; } + const std::shared_ptr& getTheme() const { return mTheme; } virtual FileData* getCursor() = 0; virtual void setCursor(FileData*) = 0; diff --git a/es-app/src/views/gamelist/ISimpleGameListView.cpp b/es-app/src/views/gamelist/ISimpleGameListView.cpp index d41be85c4..db12519c3 100644 --- a/es-app/src/views/gamelist/ISimpleGameListView.cpp +++ b/es-app/src/views/gamelist/ISimpleGameListView.cpp @@ -8,26 +8,24 @@ #include "views/gamelist/ISimpleGameListView.h" -#include "guis/GuiInfoPopup.h" -#include "utils/StringUtil.h" -#include "views/UIModeController.h" -#include "views/ViewController.h" #include "CollectionSystemsManager.h" #include "FileFilterIndex.h" #include "Settings.h" #include "Sound.h" #include "SystemData.h" +#include "guis/GuiInfoPopup.h" +#include "utils/StringUtil.h" +#include "views/UIModeController.h" +#include "views/ViewController.h" #include "Log.h" -ISimpleGameListView::ISimpleGameListView( - Window* window, - FileData* root) - : IGameListView(window, root), - mHeaderText(window), - mHeaderImage(window), - mBackground(window), - mRandomGame(nullptr) +ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root) + : IGameListView(window, root) + , mHeaderText(window) + , mHeaderImage(window) + , mBackground(window) + , mRandomGame(nullptr) { mHeaderText.setText("Logo Text"); mHeaderText.setSize(mSize.x(), 0); @@ -124,10 +122,10 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) std::vector listEntries = cursor->getChildrenListToDisplay(); // Check if there is an entry in the cursor stack history matching any entry // in the currect folder. If so, select that entry. - for (auto it = mCursorStackHistory.begin(); - it != mCursorStackHistory.end(); it++) { + for (auto it = mCursorStackHistory.begin(); // Line break. + it != mCursorStackHistory.end(); it++) { if (std::find(listEntries.begin(), listEntries.end(), *it) != - listEntries.end()) { + listEntries.end()) { newCursor = *it; mCursorStackHistory.erase(it); break; @@ -155,7 +153,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) mCursorStackHistory.push_back(getCursor()); NavigationSounds::getInstance()->playThemeNavigationSound(BACKSOUND); populateList(mCursorStack.top()->getParent()->getChildrenListToDisplay(), - mCursorStack.top()->getParent()); + mCursorStack.top()->getParent()); setCursor(mCursorStack.top()); if (mCursorStack.size() > 0) mCursorStack.pop(); @@ -169,9 +167,9 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) stopListScrolling(); SystemData* systemToView = getCursor()->getSystem(); if (systemToView->isCustomCollection() && - systemToView->getRootFolder()->getParent()) + systemToView->getRootFolder()->getParent()) ViewController::get()->goToSystemView( - systemToView->getRootFolder()->getParent()->getSystem(), true); + systemToView->getRootFolder()->getParent()->getSystem(), true); else ViewController::get()->goToSystemView(systemToView, true); } @@ -184,9 +182,9 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) return true; } else if (config->isMappedTo("x", input) && - mRoot->getSystem()->getThemeFolder() == "custom-collections" && - mCursorStack.empty() && ViewController::get()->getState().viewing == - ViewController::GAME_LIST) { + mRoot->getSystem()->getThemeFolder() == "custom-collections" && + mCursorStack.empty() && + ViewController::get()->getState().viewing == ViewController::GAME_LIST) { NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND); // Jump to the randomly selected game. if (mRandomGame) { @@ -206,7 +204,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) } else if (config->isMappedLike(getQuickSystemSelectRightButton(), input)) { if (Settings::getInstance()->getBool("QuickSystemSelect") && - SystemData::sSystemVector.size() > 1) { + SystemData::sSystemVector.size() > 1) { onPauseVideo(); onFocusLost(); stopListScrolling(); @@ -216,7 +214,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) } else if (config->isMappedLike(getQuickSystemSelectLeftButton(), input)) { if (Settings::getInstance()->getBool("QuickSystemSelect") && - SystemData::sSystemVector.size() > 1) { + SystemData::sSystemVector.size() > 1) { onPauseVideo(); onFocusLost(); stopListScrolling(); @@ -225,8 +223,8 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) } } else if (Settings::getInstance()->getBool("RandomAddButton") && - (config->isMappedTo("leftthumbstickclick", input) || - config->isMappedTo("rightthumbstickclick", input))) { + (config->isMappedTo("leftthumbstickclick", input) || + config->isMappedTo("rightthumbstickclick", input))) { if (mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER) { stopListScrolling(); // Jump to a random game. @@ -238,21 +236,19 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) } } else if (config->isMappedTo("y", input) && - mRoot->getSystem()->getThemeFolder() == "custom-collections" && - !CollectionSystemsManager::get()->isEditing() && - mCursorStack.empty() && ViewController::get()->getState().viewing == - ViewController::GAME_LIST) { + mRoot->getSystem()->getThemeFolder() == "custom-collections" && + !CollectionSystemsManager::get()->isEditing() && mCursorStack.empty() && + ViewController::get()->getState().viewing == ViewController::GAME_LIST) { // Jump to the randomly selected game. if (mRandomGame) { NavigationSounds::getInstance()->playThemeNavigationSound(SELECTSOUND); // If there is already an mCursorStackHistory entry for the collection, then // remove it so we don't get multiple entries. std::vector listEntries = - mRandomGame->getSystem()->getRootFolder()->getChildrenListToDisplay(); - for (auto it = mCursorStackHistory.begin(); - it != mCursorStackHistory.end(); it++) { + mRandomGame->getSystem()->getRootFolder()->getChildrenListToDisplay(); + for (auto it = mCursorStackHistory.begin(); it != mCursorStackHistory.end(); it++) { if (std::find(listEntries.begin(), listEntries.end(), *it) != - listEntries.end()) { + listEntries.end()) { mCursorStackHistory.erase(it); break; } @@ -265,34 +261,33 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) } } else if (config->isMappedTo("y", input) && - !Settings::getInstance()->getBool("FavoritesAddButton") && - !CollectionSystemsManager::get()->isEditing()) { + !Settings::getInstance()->getBool("FavoritesAddButton") && + !CollectionSystemsManager::get()->isEditing()) { return true; } else if (config->isMappedTo("y", input) && - !UIModeController::getInstance()->isUIModeKid() && - !UIModeController::getInstance()->isUIModeKiosk()) { + !UIModeController::getInstance()->isUIModeKid() && + !UIModeController::getInstance()->isUIModeKiosk()) { // Notify the user if attempting to add a custom collection to a custom collection. if (CollectionSystemsManager::get()->isEditing() && - mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER && - getCursor()->getParent()->getPath() == "collections") { + mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER && + getCursor()->getParent()->getPath() == "collections") { NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND); GuiInfoPopup* s; - s = new GuiInfoPopup(mWindow, - "CAN'T ADD CUSTOM COLLECTIONS TO CUSTOM COLLECTIONS", 4000); + s = new GuiInfoPopup(mWindow, "CAN'T ADD CUSTOM COLLECTIONS TO CUSTOM COLLECTIONS", + 4000); mWindow->setInfoPopup(s); } // Notify the user if attempting to add a placeholder to a custom collection. if (CollectionSystemsManager::get()->isEditing() && - mRoot->getSystem()->isGameSystem() && getCursor()->getType() == PLACEHOLDER) { + mRoot->getSystem()->isGameSystem() && getCursor()->getType() == PLACEHOLDER) { NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND); GuiInfoPopup* s; - s = new GuiInfoPopup(mWindow, - "CAN'T ADD PLACEHOLDERS TO CUSTOM COLLECTIONS", 4000); + s = new GuiInfoPopup(mWindow, "CAN'T ADD PLACEHOLDERS TO CUSTOM COLLECTIONS", 4000); mWindow->setInfoPopup(s); } else if (mRoot->getSystem()->isGameSystem() && getCursor()->getType() != PLACEHOLDER && - getCursor()->getParent()->getPath() != "collections") { + getCursor()->getParent()->getPath() != "collections") { if (getCursor()->getType() == GAME || getCursor()->getType() == FOLDER) NavigationSounds::getInstance()->playThemeNavigationSound(FAVORITESOUND); // When marking or unmarking a game as favorite, don't jump to the new position @@ -312,13 +307,14 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) foldersOnTop = !getCursor()->getParent()->getOnlyFoldersFlag(); if (mRoot->getSystem()->isCustomCollection() || - mRoot->getSystem()->getThemeFolder() == "custom-collections") + mRoot->getSystem()->getThemeFolder() == "custom-collections") favoritesSorting = Settings::getInstance()->getBool("FavFirstCustom"); else favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst"); - if (favoritesSorting && static_cast( - mRoot->getSystem()->getName()) != "recent" && !isEditing) { + if (favoritesSorting && + static_cast(mRoot->getSystem()->getName()) != "recent" && + !isEditing) { FileData* entryToSelect; // Add favorite flag. if (!getCursor()->getFavorite()) { @@ -331,7 +327,7 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) entryToSelect = getNextEntry(); } else if (getCursor() == getLastEntry() && - getPreviousEntry()->getFavorite()) { + getPreviousEntry()->getFavorite()) { entryToSelect = getLastEntry(); selectLastEntry = true; } @@ -342,14 +338,14 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) // If we mark the second entry as favorite and the first entry is not a // favorite, then select this entry if they are of the same type. else if (getPreviousEntry() == getFirstEntry() && - getCursor()->getType() == getPreviousEntry()->getType()) { + getCursor()->getType() == getPreviousEntry()->getType()) { entryToSelect = getPreviousEntry(); } // For all other scenarios try to select the next entry, and if it doesn't // exist, select the previous entry. else { - entryToSelect = getCursor() != getNextEntry() ? - getNextEntry() : getPreviousEntry(); + entryToSelect = + getCursor() != getNextEntry() ? getNextEntry() : getPreviousEntry(); } } // Remove favorite flag. @@ -365,9 +361,10 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) // If we are on the favorite marking boundary, select the previous entry, // unless folders are sorted on top and the previous entry is a folder. else if (foldersOnTop && - getCursor()->getFavorite() != getNextEntry()->getFavorite()) { + getCursor()->getFavorite() != getNextEntry()->getFavorite()) { entryToSelect = getPreviousEntry()->getType() == FOLDER ? - getCursor() : getPreviousEntry(); + getCursor() : + getPreviousEntry(); } // If we are on the favorite marking boundary, select the previous entry. else if (getCursor()->getFavorite() != getNextEntry()->getFavorite()) { @@ -376,17 +373,17 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) // For all other scenarios try to select the next entry, and if it doesn't // exist, select the previous entry. else { - entryToSelect = getCursor() != getNextEntry() ? - getNextEntry() : getPreviousEntry(); + entryToSelect = + getCursor() != getNextEntry() ? getNextEntry() : getPreviousEntry(); } // If we removed the last favorite marking, set the flag to jump to the // first list entry after the sorting has been performed. if (foldersOnTop && getCursor() == getFirstGameEntry() && - !getNextEntry()->getFavorite()) + !getNextEntry()->getFavorite()) removedLastFavorite = true; else if (getCursor() == getFirstEntry() && !getNextEntry()->getFavorite()) - removedLastFavorite = true; + removedLastFavorite = true; } setCursor(entryToSelect); @@ -399,22 +396,30 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) if (entryToUpdate->getType() == FOLDER) { GuiInfoPopup* s; if (isEditing) { - s = new GuiInfoPopup(mWindow, - "CAN'T ADD FOLDERS TO CUSTOM COLLECTIONS", 4000); + s = new GuiInfoPopup(mWindow, "CAN'T ADD FOLDERS TO CUSTOM COLLECTIONS", + 4000); } else { MetaDataList* md = &entryToUpdate->getSourceFileData()->metadata; if (md->get("favorite") == "false") { md->set("favorite", "true"); - s = new GuiInfoPopup(mWindow, "MARKED FOLDER '" + + s = new GuiInfoPopup( + mWindow, + "MARKED FOLDER '" + Utils::String::toUpper(Utils::String::removeParenthesis( - entryToUpdate->getName())) + "' AS FAVORITE", 4000); + entryToUpdate->getName())) + + "' AS FAVORITE", + 4000); } else { md->set("favorite", "false"); - s = new GuiInfoPopup(mWindow, "REMOVED FAVORITE MARKING FOR FOLDER '" + + s = new GuiInfoPopup( + mWindow, + "REMOVED FAVORITE MARKING FOR FOLDER '" + Utils::String::toUpper(Utils::String::removeParenthesis( - entryToUpdate->getName())) + "'", 4000); + entryToUpdate->getName())) + + "'", + 4000); } } @@ -422,8 +427,8 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) entryToUpdate->getSourceFileData()->getSystem()->onMetaDataSavePoint(); getCursor()->getParent()->sort( - mRoot->getSortTypeFromString(mRoot->getSortTypeString()), - Settings::getInstance()->getBool("FavoritesFirst")); + mRoot->getSortTypeFromString(mRoot->getSortTypeString()), + Settings::getInstance()->getBool("FavoritesFirst")); ViewController::get()->onFileChanged(getCursor(), false); @@ -431,16 +436,19 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) // was unmarked. We couldn't do this earlier as we didn't have the list // sorted yet. if (removedLastFavorite) { - ViewController::get()->getGameListView(entryToUpdate-> - getSystem())->setCursor(ViewController::get()-> - getGameListView(entryToUpdate->getSystem())->getFirstEntry()); + ViewController::get() + ->getGameListView(entryToUpdate->getSystem()) + ->setCursor(ViewController::get() + ->getGameListView(entryToUpdate->getSystem()) + ->getFirstEntry()); } return true; } else if (isEditing && entryToUpdate->metadata.get("nogamecount") == "true") { GuiInfoPopup* s = new GuiInfoPopup(mWindow, - "CAN'T ADD ENTRIES THAT ARE NOT COUNTED " - "AS GAMES TO CUSTOM COLLECTIONS", 4000); + "CAN'T ADD ENTRIES THAT ARE NOT COUNTED " + "AS GAMES TO CUSTOM COLLECTIONS", + 4000); mWindow->setInfoPopup(s); } else if (CollectionSystemsManager::get()->toggleGameInCollection(entryToUpdate)) { @@ -450,13 +458,15 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) IGameListView* view = ViewController::get()->getGameListView(system).get(); // Jump to the first entry in the gamelist if the last favorite was unmarked. if (foldersOnTop && removedLastFavorite && - !entryToUpdate->getSystem()->isCustomCollection()) { - ViewController::get()->getGameListView(entryToUpdate->getSystem())-> - setCursor(ViewController::get()->getGameListView(entryToUpdate-> - getSystem())->getFirstGameEntry()); + !entryToUpdate->getSystem()->isCustomCollection()) { + ViewController::get() + ->getGameListView(entryToUpdate->getSystem()) + ->setCursor(ViewController::get() + ->getGameListView(entryToUpdate->getSystem()) + ->getFirstGameEntry()); } else if (removedLastFavorite && - !entryToUpdate->getSystem()->isCustomCollection()) { + !entryToUpdate->getSystem()->isCustomCollection()) { view->setCursor(view->getFirstEntry()); } else if (selectLastEntry) { @@ -467,10 +477,9 @@ bool ISimpleGameListView::input(InputConfig* config, Input input) // onFileChanged() which will trigger populateList(). if (isEditing) { for (auto it = SystemData::sSystemVector.begin(); - it != SystemData::sSystemVector.end(); it++) { + it != SystemData::sSystemVector.end(); it++) { ViewController::get()->getGameListView((*it))->onFileChanged( - ViewController::get()->getGameListView((*it))-> - getCursor(), false); + ViewController::get()->getGameListView((*it))->getCursor(), false); } } return true; @@ -511,13 +520,13 @@ void ISimpleGameListView::generateGamelistInfo(FileData* cursor, FileData* first if (idx->isFiltered()) { mIsFiltered = true; - mFilteredGameCount = static_cast(rootFolder-> - getFilesRecursive(GAME, true, false).size()); + mFilteredGameCount = + static_cast(rootFolder->getFilesRecursive(GAME, true, false).size()); // Also count the games that are set to not be counted as games, as the filter may // apply to such entries as well and this will be indicated with a separate '+ XX' // in the GamelistInfo field. - mFilteredGameCountAll = static_cast(rootFolder-> - getFilesRecursive(GAME, true, true).size()); + mFilteredGameCountAll = + static_cast(rootFolder->getFilesRecursive(GAME, true, true).size()); } if (firstEntry->getParent() && firstEntry->getParent()->getType() == FOLDER) @@ -541,7 +550,7 @@ void ISimpleGameListView::generateFirstLetterIndex(const std::vector& else favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst"); - bool foldersOnTop = Settings::getInstance()->getBool("FoldersOnTop"); + bool foldersOnTop = Settings::getInstance()->getBool("FoldersOnTop"); // Find out if there are only favorites and/or only folders in the list. for (auto it = files.begin(); it != files.end(); it++) { @@ -553,16 +562,20 @@ void ISimpleGameListView::generateFirstLetterIndex(const std::vector& // Build the index. for (auto it = files.begin(); it != files.end(); it++) { - if ((*it)->getType() == FOLDER && (*it)->getFavorite() && - favoritesSorting && !onlyFavorites) + if ((*it)->getType() == FOLDER && (*it)->getFavorite() && favoritesSorting && + !onlyFavorites) { hasFavorites = true; - else if ((*it)->getType() == FOLDER && foldersOnTop && !onlyFolders) + } + else if ((*it)->getType() == FOLDER && foldersOnTop && !onlyFolders) { hasFolders = true; - else if ((*it)->getType() == GAME && (*it)->getFavorite() && - favoritesSorting && !onlyFavorites) + } + else if ((*it)->getType() == GAME && (*it)->getFavorite() && favoritesSorting && + !onlyFavorites) { hasFavorites = true; - else + } + else { mFirstLetterIndex.push_back(Utils::String::getFirstCharacter((*it)->getSortName())); + } } // Sort and make each entry unique. diff --git a/es-app/src/views/gamelist/ISimpleGameListView.h b/es-app/src/views/gamelist/ISimpleGameListView.h index 4e9ad130a..cecb95fc9 100644 --- a/es-app/src/views/gamelist/ISimpleGameListView.h +++ b/es-app/src/views/gamelist/ISimpleGameListView.h @@ -9,9 +9,9 @@ #ifndef ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H -#include "views/gamelist/IGameListView.h" #include "components/ImageComponent.h" #include "components/TextComponent.h" +#include "views/gamelist/IGameListView.h" #include @@ -39,9 +39,13 @@ public: // These functions are used to retain the folder cursor history, for instance // during a view reload. The calling function stores the history temporarily. void copyCursorHistory(std::vector& cursorHistory) override - { cursorHistory = mCursorStackHistory; }; + { + cursorHistory = mCursorStackHistory; + }; void populateCursorHistory(std::vector& cursorHistory) override - { mCursorStackHistory = cursorHistory; }; + { + mCursorStackHistory = cursorHistory; + }; protected: virtual std::string getQuickSystemSelectRightButton() = 0; diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index fa4435c63..1431bd785 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -16,67 +16,62 @@ #if defined(BUILD_VLC_PLAYER) #include "components/VideoVlcComponent.h" #endif -#include "utils/FileSystemUtil.h" -#include "views/ViewController.h" #include "AudioManager.h" #include "CollectionSystemsManager.h" #include "SystemData.h" +#include "utils/FileSystemUtil.h" +#include "views/ViewController.h" #define FADE_IN_START_OPACITY 0.5f #define FADE_IN_TIME 650 -VideoGameListView::VideoGameListView( - Window* window, - FileData* root) - : BasicGameListView(window, root), - mDescContainer(window), - mDescription(window), - mGamelistInfo(window), - - mThumbnail(window), - mMarquee(window), - mImage(window), - mVideo(nullptr), - mVideoPlaying(false), - - mLblRating(window), - mLblReleaseDate(window), - mLblDeveloper(window), - mLblPublisher(window), - mLblGenre(window), - mLblPlayers(window), - mLblLastPlayed(window), - mLblPlayCount(window), - - mRating(window), - mReleaseDate(window), - mDeveloper(window), - mPublisher(window), - mGenre(window), - mPlayers(window), - mLastPlayed(window), - mPlayCount(window), - mName(window), - mLastUpdated(nullptr) +VideoGameListView::VideoGameListView(Window* window, FileData* root) + : BasicGameListView(window, root) + , mDescContainer(window) + , mDescription(window) + , mGamelistInfo(window) + , mThumbnail(window) + , mMarquee(window) + , mImage(window) + , mVideo(nullptr) + , mVideoPlaying(false) + , mLblRating(window) + , mLblReleaseDate(window) + , mLblDeveloper(window) + , mLblPublisher(window) + , mLblGenre(window) + , mLblPlayers(window) + , mLblLastPlayed(window) + , mLblPlayCount(window) + , mRating(window) + , mReleaseDate(window) + , mDeveloper(window) + , mPublisher(window) + , mGenre(window) + , mPlayers(window) + , mLastPlayed(window) + , mPlayCount(window) + , mName(window) + , mLastUpdated(nullptr) { const float padding = 0.01f; - // Create the correct type of video window. - #if defined(_RPI_) +// Create the correct type of video window. +#if defined(_RPI_) if (Settings::getInstance()->getBool("VideoOmxPlayer")) mVideo = new VideoOmxComponent(window); else if (Settings::getInstance()->getString("VideoPlayer") == "vlc") mVideo = new VideoVlcComponent(window); else mVideo = new VideoFFmpegComponent(window); - #elif defined(BUILD_VLC_PLAYER) +#elif defined(BUILD_VLC_PLAYER) if (Settings::getInstance()->getString("VideoPlayer") == "vlc") mVideo = new VideoVlcComponent(window); else mVideo = new VideoFFmpegComponent(window); - #else +#else mVideo = new VideoFFmpegComponent(window); - #endif +#endif mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y()); mList.setSize(mSize.x() * (0.50f - padding), mList.getSize().y()); @@ -87,21 +82,21 @@ VideoGameListView::VideoGameListView( mThumbnail.setOrigin(0.5f, 0.5f); mThumbnail.setPosition(2.0f, 2.0f); mThumbnail.setVisible(false); - mThumbnail.setMaxSize(mSize.x() * (0.25f - 2 * padding), mSize.y() * 0.10f); + mThumbnail.setMaxSize(mSize.x() * (0.25f - 2.0f * padding), mSize.y() * 0.10f); mThumbnail.setDefaultZIndex(35); addChild(&mThumbnail); // Marquee. mMarquee.setOrigin(0.5f, 0.5f); mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f); - mMarquee.setMaxSize(mSize.x() * (0.5f - 2 * padding), mSize.y() * 0.18f); + mMarquee.setMaxSize(mSize.x() * (0.5f - 2.0f * padding), mSize.y() * 0.18f); mMarquee.setDefaultZIndex(35); addChild(&mMarquee); // Video. mVideo->setOrigin(0.5f, 0.5f); mVideo->setPosition(mSize.x() * 0.25f, mSize.y() * 0.4f); - mVideo->setSize(mSize.x() * (0.5f - 2 * padding), mSize.y() * 0.4f); + mVideo->setSize(mSize.x() * (0.5f - 2.0f * padding), mSize.y() * 0.4f); mVideo->setDefaultZIndex(30); addChild(mVideo); @@ -140,8 +135,8 @@ VideoGameListView::VideoGameListView( addChild(&mName); mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); - mDescContainer.setSize(mSize.x() * (0.50f - 2 * padding), mSize.y() - - mDescContainer.getPosition().y()); + mDescContainer.setSize(mSize.x() * (0.50f - 2.0f * padding), + mSize.y() - mDescContainer.getPosition().y()); mDescContainer.setAutoScroll(true); mDescContainer.setDefaultZIndex(40); addChild(&mDescContainer); @@ -160,10 +155,7 @@ VideoGameListView::VideoGameListView( initMDValues(); } -VideoGameListView::~VideoGameListView() -{ - delete mVideo; -} +VideoGameListView::~VideoGameListView() { delete mVideo; } void VideoGameListView::onThemeChanged(const std::shared_ptr& theme) { @@ -171,22 +163,23 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr& theme) using namespace ThemeFlags; mThumbnail.applyTheme(theme, getName(), "md_thumbnail", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); + POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); mMarquee.applyTheme(theme, getName(), "md_marquee", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); + POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); mImage.applyTheme(theme, getName(), "md_image", - POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); + POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); mVideo->applyTheme(theme, getName(), "md_video", - POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX | ROTATION | VISIBLE); + POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX | ROTATION | + VISIBLE); mName.applyTheme(theme, getName(), "md_name", ALL); initMDLabels(); std::vector labels = getMDLabels(); assert(labels.size() == 8); - std::vector lblElements = { - "md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher", - "md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount" - }; + std::vector lblElements = { "md_lbl_rating", "md_lbl_releasedate", + "md_lbl_developer", "md_lbl_publisher", + "md_lbl_genre", "md_lbl_players", + "md_lbl_lastplayed", "md_lbl_playcount" }; for (unsigned int i = 0; i < labels.size(); i++) labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); @@ -194,19 +187,19 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr& theme) initMDValues(); std::vector values = getMDValues(); assert(values.size() == 8); - std::vector valElements = { - "md_rating", "md_releasedate", "md_developer", "md_publisher", - "md_genre", "md_players", "md_lastplayed", "md_playcount" - }; + std::vector valElements = { "md_rating", "md_releasedate", "md_developer", + "md_publisher", "md_genre", "md_players", + "md_lastplayed", "md_playcount" }; for (unsigned int i = 0; i < values.size(); i++) values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT); mDescContainer.applyTheme(theme, getName(), "md_description", - POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); + POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); mDescription.setSize(mDescContainer.getSize().x(), 0); - mDescription.applyTheme(theme, getName(), "md_description", - ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); + mDescription.applyTheme( + theme, getName(), "md_description", + ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); mGamelistInfo.applyTheme(theme, getName(), "gamelistInfo", ALL ^ ThemeFlags::TEXT); // If there is no position defined in the theme for gamelistInfo, then hide it. @@ -238,7 +231,7 @@ void VideoGameListView::initMDLabels() } else { // Work from the last component. - GuiComponent* lc = components[i-1]; + GuiComponent* lc = components[i - 1]; pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); } @@ -265,11 +258,11 @@ void VideoGameListView::initMDValues() float bottom = 0.0f; - const float colSize = (mSize.x() * 0.48f) / 2; + const float colSize = (mSize.x() * 0.48f) / 2.0f; for (unsigned int i = 0; i < labels.size(); i++) { - const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2; + const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2.0f; values[i]->setPosition(labels[i]->getPosition() + - Vector3f(labels[i]->getSize().x(),heightDiff, 0)); + Vector3f(labels[i]->getSize().x(), heightDiff, 0)); values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y()); values[i]->setDefaultZIndex(40); @@ -280,8 +273,8 @@ void VideoGameListView::initMDValues() } mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f); - mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - - mDescContainer.getPosition().y()); + mDescContainer.setSize(mDescContainer.getSize().x(), + mSize.y() - mDescContainer.getPosition().y()); } void VideoGameListView::updateInfoPanel() @@ -300,7 +293,7 @@ void VideoGameListView::updateInfoPanel() if (file) { // Always hide the metadata fields if browsing grouped custom collections. if (file->getSystem()->isCustomCollection() && - file->getPath() == file->getSystem()->getName()) + file->getPath() == file->getSystem()->getName()) hideMetaDataFields = true; else hideMetaDataFields = (file->metadata.get("hidemetadata") == "true"); @@ -316,9 +309,9 @@ void VideoGameListView::updateInfoPanel() // or if we're in the grouped custom collection view. if (mList.isScrolling()) if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") || - (mLastUpdated->getSystem()->isCustomCollection() && - mLastUpdated->getPath() == mLastUpdated->getSystem()->getName())) - hideMetaDataFields = true; + (mLastUpdated->getSystem()->isCustomCollection() && + mLastUpdated->getPath() == mLastUpdated->getSystem()->getName())) + hideMetaDataFields = true; if (hideMetaDataFields) { mLblRating.setVisible(false); @@ -367,9 +360,9 @@ void VideoGameListView::updateInfoPanel() // which will generate a description of three random games and return a pointer to // the first of these so that we can display its game media. if (file->getSystem()->isCustomCollection() && - file->getPath() == file->getSystem()->getName()) { - mRandomGame = CollectionSystemsManager::get()-> - updateCollectionFolderMetadata(file->getSystem()); + file->getPath() == file->getSystem()->getName()) { + mRandomGame = + CollectionSystemsManager::get()->updateCollectionFolderMetadata(file->getSystem()); if (mRandomGame) { mThumbnail.setImage(mRandomGame->getThumbnailPath()); mMarquee.setImage(mRandomGame->getMarqueePath()); @@ -398,7 +391,6 @@ void VideoGameListView::updateInfoPanel() mVideo->setImage(file->getImagePath()); mVideo->onHide(); - if (!mVideo->setVideo(file->getVideoPath())) mVideo->setDefaultVideo(); } @@ -418,21 +410,21 @@ void VideoGameListView::updateInfoPanel() if (mIsFiltered) { if (mFilteredGameCountAll == mFilteredGameCount) gamelistInfoString += ViewController::FILTER_CHAR + " " + - std::to_string(mFilteredGameCount) + " / " + - std::to_string(mGameCount); + std::to_string(mFilteredGameCount) + " / " + + std::to_string(mGameCount); else gamelistInfoString += ViewController::FILTER_CHAR + " " + - std::to_string(mFilteredGameCount) + " + " + - std::to_string(mFilteredGameCountAll - mFilteredGameCount) + " / " + - std::to_string(mGameCount); + std::to_string(mFilteredGameCount) + " + " + + std::to_string(mFilteredGameCountAll - mFilteredGameCount) + + " / " + std::to_string(mGameCount); } else { - gamelistInfoString += ViewController::CONTROLLER_CHAR + " " + - std::to_string(mGameCount); + gamelistInfoString += + ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount); if (!(file->getSystem()->isCollection() && - file->getSystem()->getFullName() == "favorites")) - gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " - + std::to_string(mFavoritesGameCount); + file->getSystem()->getFullName() == "favorites")) + gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " + + std::to_string(mFavoritesGameCount); } if (mIsFolder && infoAlign != ALIGN_RIGHT) @@ -442,9 +434,9 @@ void VideoGameListView::updateInfoPanel() // Fade in the game image. auto func = [this](float t) { - mVideo->setOpacity(static_cast(Math::lerp( - static_cast(FADE_IN_START_OPACITY), 1.0f, t) * 255)); - }; + mVideo->setOpacity(static_cast( + Math::lerp(static_cast(FADE_IN_START_OPACITY), 1.0f, t) * 255)); + }; mVideo->setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false); mDescription.setText(file->metadata.get("desc")); @@ -498,10 +490,7 @@ void VideoGameListView::updateInfoPanel() } } -void VideoGameListView::launch(FileData* game) -{ - ViewController::get()->triggerGameLaunch(game); -} +void VideoGameListView::launch(FileData* game) { ViewController::get()->triggerGameLaunch(game); } std::vector VideoGameListView::getMDLabels() { From 23fdc00044f99c77943427e491f92148477de4db Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 7 Jul 2021 20:31:46 +0200 Subject: [PATCH 38/76] Formatted the es-core source tree using clang-format. --- es-core/src/AsyncHandle.h | 25 +- es-core/src/AudioManager.cpp | 68 +- es-core/src/CECInput.cpp | 40 +- es-core/src/CECInput.h | 7 +- es-core/src/GuiComponent.cpp | 262 ++------ es-core/src/GuiComponent.h | 159 +++-- es-core/src/HelpStyle.cpp | 6 +- es-core/src/HttpReq.cpp | 40 +- es-core/src/HttpReq.h | 10 +- es-core/src/ImageIO.cpp | 11 +- es-core/src/ImageIO.h | 4 +- es-core/src/InputConfig.cpp | 36 +- es-core/src/InputConfig.h | 40 +- es-core/src/InputManager.cpp | 185 +++--- es-core/src/InputManager.h | 2 +- es-core/src/Log.cpp | 37 +- es-core/src/Log.h | 32 +- es-core/src/MameNames.cpp | 67 +- es-core/src/MameNames.h | 14 +- es-core/src/Platform.cpp | 108 ++-- es-core/src/Platform.h | 4 +- es-core/src/Scripting.cpp | 22 +- es-core/src/Scripting.h | 5 +- es-core/src/Settings.cpp | 166 ++--- es-core/src/Sound.cpp | 59 +- es-core/src/Sound.h | 13 +- es-core/src/ThemeData.cpp | 590 +++++++++--------- es-core/src/ThemeData.h | 47 +- es-core/src/Window.cpp | 252 ++++---- es-core/src/Window.h | 13 +- es-core/src/animations/Animation.h | 2 +- .../src/animations/AnimationController.cpp | 19 +- es-core/src/animations/AnimationController.h | 18 +- es-core/src/animations/LambdaAnimation.h | 11 +- .../src/components/AnimatedImageComponent.cpp | 11 +- es-core/src/components/BusyComponent.cpp | 28 +- es-core/src/components/BusyComponent.h | 2 +- es-core/src/components/ButtonComponent.cpp | 48 +- es-core/src/components/ButtonComponent.h | 14 +- es-core/src/components/ComponentGrid.cpp | 41 +- es-core/src/components/ComponentGrid.h | 63 +- es-core/src/components/ComponentList.cpp | 55 +- es-core/src/components/ComponentList.h | 45 +- es-core/src/components/DateTimeComponent.cpp | 49 +- es-core/src/components/DateTimeComponent.h | 25 +- .../src/components/DateTimeEditComponent.cpp | 118 ++-- .../src/components/DateTimeEditComponent.h | 29 +- es-core/src/components/GridTileComponent.cpp | 82 +-- es-core/src/components/GridTileComponent.h | 14 +- es-core/src/components/HelpComponent.cpp | 25 +- es-core/src/components/IList.h | 83 ++- es-core/src/components/ImageComponent.cpp | 116 ++-- es-core/src/components/ImageComponent.h | 31 +- es-core/src/components/ImageGridComponent.h | 160 ++--- es-core/src/components/MenuComponent.cpp | 66 +- es-core/src/components/MenuComponent.h | 48 +- es-core/src/components/NinePatchComponent.cpp | 58 +- es-core/src/components/NinePatchComponent.h | 25 +- es-core/src/components/OptionListComponent.h | 86 ++- es-core/src/components/RatingComponent.cpp | 49 +- es-core/src/components/RatingComponent.h | 19 +- .../src/components/ScrollableContainer.cpp | 51 +- es-core/src/components/ScrollableContainer.h | 10 +- es-core/src/components/SliderComponent.cpp | 56 +- es-core/src/components/SliderComponent.h | 10 +- es-core/src/components/SwitchComponent.cpp | 56 +- es-core/src/components/SwitchComponent.h | 22 +- es-core/src/components/TextComponent.cpp | 198 +++--- es-core/src/components/TextComponent.h | 46 +- es-core/src/components/TextEditComponent.cpp | 143 ++--- es-core/src/components/TextEditComponent.h | 11 +- es-core/src/components/TextListComponent.h | 171 +++-- es-core/src/components/VideoComponent.cpp | 109 ++-- es-core/src/components/VideoComponent.h | 36 +- .../src/components/VideoFFmpegComponent.cpp | 389 ++++++------ es-core/src/components/VideoFFmpegComponent.h | 7 +- es-core/src/components/VideoOmxComponent.cpp | 91 +-- es-core/src/components/VideoVlcComponent.cpp | 143 +++-- es-core/src/components/VideoVlcComponent.h | 2 +- es-core/src/guis/GuiComplexTextEditPopup.cpp | 116 ++-- es-core/src/guis/GuiComplexTextEditPopup.h | 35 +- es-core/src/guis/GuiDetectDevice.cpp | 86 +-- es-core/src/guis/GuiDetectDevice.h | 8 +- es-core/src/guis/GuiInputConfig.cpp | 219 +++---- es-core/src/guis/GuiInputConfig.h | 8 +- es-core/src/guis/GuiMsgBox.cpp | 75 +-- es-core/src/guis/GuiMsgBox.h | 27 +- es-core/src/guis/GuiTextEditPopup.cpp | 75 ++- es-core/src/guis/GuiTextEditPopup.h | 21 +- es-core/src/math/Misc.cpp | 26 +- es-core/src/math/Misc.h | 26 +- es-core/src/math/Transform4x4f.cpp | 2 + es-core/src/math/Transform4x4f.h | 57 +- es-core/src/math/Vector2f.h | 60 +- es-core/src/math/Vector2i.h | 38 +- es-core/src/math/Vector3f.h | 88 ++- es-core/src/math/Vector4f.h | 123 ++-- es-core/src/renderers/Renderer.cpp | 242 +++---- es-core/src/renderers/Renderer.h | 179 +++--- es-core/src/renderers/Renderer_GL21.cpp | 201 +++--- es-core/src/renderers/Renderer_GLES10.cpp | 123 ++-- es-core/src/renderers/Shader_GL21.cpp | 60 +- es-core/src/renderers/Shader_GL21.h | 4 +- es-core/src/resources/Font.cpp | 244 ++++---- es-core/src/resources/Font.h | 78 ++- es-core/src/resources/ResourceManager.cpp | 37 +- es-core/src/resources/ResourceManager.h | 2 +- es-core/src/resources/TextureData.cpp | 59 +- es-core/src/resources/TextureData.h | 2 +- es-core/src/resources/TextureDataManager.cpp | 27 +- es-core/src/resources/TextureDataManager.h | 14 +- es-core/src/resources/TextureResource.cpp | 46 +- es-core/src/resources/TextureResource.h | 17 +- es-core/src/utils/CImgUtil.cpp | 52 +- es-core/src/utils/CImgUtil.h | 17 +- es-core/src/utils/FileSystemUtil.cpp | 288 ++++----- es-core/src/utils/FileSystemUtil.h | 22 +- es-core/src/utils/StringUtil.cpp | 484 ++++++++------ es-core/src/utils/StringUtil.h | 17 +- es-core/src/utils/TimeUtil.cpp | 219 +++---- es-core/src/utils/TimeUtil.h | 13 +- 121 files changed, 4362 insertions(+), 4490 deletions(-) diff --git a/es-core/src/AsyncHandle.h b/es-core/src/AsyncHandle.h index 4ea5a5482..5630890c0 100644 --- a/es-core/src/AsyncHandle.h +++ b/es-core/src/AsyncHandle.h @@ -12,7 +12,7 @@ #include enum AsyncHandleStatus { - ASYNC_IN_PROGRESS, + ASYNC_IN_PROGRESS, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). ASYNC_ERROR, ASYNC_DONE }; @@ -21,17 +21,24 @@ enum AsyncHandleStatus { class AsyncHandle { public: - AsyncHandle() : mStatus(ASYNC_IN_PROGRESS) {}; - virtual ~AsyncHandle() {}; + AsyncHandle() + : mStatus(ASYNC_IN_PROGRESS) + { + } + virtual ~AsyncHandle() {} virtual void update() = 0; // Update and return the latest status. - inline AsyncHandleStatus status() { update(); return mStatus; } + AsyncHandleStatus status() + { + update(); + return mStatus; + } // User-friendly string of our current status. // Will return error message if status() == SEARCH_ERROR. - inline std::string getStatusString() + std::string getStatusString() { switch (mStatus) { case ASYNC_IN_PROGRESS: @@ -46,8 +53,12 @@ public: } protected: - inline void setStatus(AsyncHandleStatus status) { mStatus = status; } - inline void setError(const std::string& error) { setStatus(ASYNC_ERROR); mError = error; } + void setStatus(AsyncHandleStatus status) { mStatus = status; } + void setError(const std::string& error) + { + setStatus(ASYNC_ERROR); + mError = error; + } std::string mError; AsyncHandleStatus mStatus; diff --git a/es-core/src/AudioManager.cpp b/es-core/src/AudioManager.cpp index 081589364..8ca75fa41 100644 --- a/es-core/src/AudioManager.cpp +++ b/es-core/src/AudioManager.cpp @@ -19,17 +19,20 @@ std::vector> AudioManager::sSoundVector; SDL_AudioDeviceID AudioManager::sAudioDevice = 0; SDL_AudioSpec AudioManager::sAudioFormat; SDL_AudioStream* AudioManager::sConversionStream; + bool AudioManager::sMuteStream = false; bool AudioManager::sHasAudioDevice = true; bool AudioManager::mIsClearingStream = false; AudioManager::AudioManager() { + // Init on construction. init(); } AudioManager::~AudioManager() { + // Deinit on destruction. deinit(); } @@ -74,7 +77,7 @@ void AudioManager::init() } sAudioDevice = SDL_OpenAudioDevice(0, 0, &sRequestedAudioFormat, &sAudioFormat, - SDL_AUDIO_ALLOW_ANY_CHANGE); + SDL_AUDIO_ALLOW_ANY_CHANGE); if (sAudioDevice == 0) { LOG(LogError) << "Unable to open audio device: " << SDL_GetError(); @@ -82,29 +85,30 @@ void AudioManager::init() } if (sAudioFormat.freq != sRequestedAudioFormat.freq) { - LOG(LogDebug) << "AudioManager::init(): Requested sample rate " << - std::to_string(sRequestedAudioFormat.freq) << " could not be " - "set, obtained " << std::to_string(sAudioFormat.freq); + LOG(LogDebug) << "AudioManager::init(): Requested sample rate " + << std::to_string(sRequestedAudioFormat.freq) + << " could not be set, obtained " << std::to_string(sAudioFormat.freq); } if (sAudioFormat.format != sRequestedAudioFormat.format) { - LOG(LogDebug) << "AudioManager::init(): Requested format " << - std::to_string(sRequestedAudioFormat.format) << " could not be " - "set, obtained " << std::to_string(sAudioFormat.format); + LOG(LogDebug) << "AudioManager::init(): Requested format " + << std::to_string(sRequestedAudioFormat.format) + << " could not be set, obtained " << std::to_string(sAudioFormat.format); } if (sAudioFormat.channels != sRequestedAudioFormat.channels) { - LOG(LogDebug) << "AudioManager::init(): Requested channel count " << - std::to_string(sRequestedAudioFormat.channels) << " could not be " - "set, obtained " << std::to_string(sAudioFormat.channels); + LOG(LogDebug) << "AudioManager::init(): Requested channel count " + << std::to_string(sRequestedAudioFormat.channels) + << " could not be set, obtained " << std::to_string(sAudioFormat.channels); } - #if defined(_WIN64) || defined(__APPLE__) +#if defined(_WIN64) || defined(__APPLE__) // Beats me why the buffer size is not divided by the channel count on some operating systems. if (sAudioFormat.samples != sRequestedAudioFormat.samples) { - #else +#else if (sAudioFormat.samples != sRequestedAudioFormat.samples / sRequestedAudioFormat.channels) { - #endif - LOG(LogDebug) << "AudioManager::init(): Requested sample buffer size " << - std::to_string(sRequestedAudioFormat.samples / sRequestedAudioFormat.channels) << - " could not be set, obtained " << std::to_string(sAudioFormat.samples); +#endif + LOG(LogDebug) << "AudioManager::init(): Requested sample buffer size " + << std::to_string(sRequestedAudioFormat.samples / + sRequestedAudioFormat.channels) + << " could not be set, obtained " << std::to_string(sAudioFormat.samples); } // Just in case someone changed the es_settings.xml file manually to invalid values. @@ -126,7 +130,7 @@ void AudioManager::deinit() // user on some operating systems such as macOS, and it's annoying to have a crash at the // end of debugging session. So we'll simply disable the function until it has been properly // fixed in the SDL library. -// SDL_FreeAudioStream(sConversionStream); + // SDL_FreeAudioStream(sConversionStream); SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); @@ -153,9 +157,9 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len) restLength = len; } // Mix sample into stream. - SDL_MixAudioFormat(stream, &(sound->getData()[sound->getPosition()]), - sAudioFormat.format, restLength, static_cast(Settings::getInstance()-> - getInt("SoundVolumeNavigation") * 1.28f)); + SDL_MixAudioFormat( + stream, &(sound->getData()[sound->getPosition()]), sAudioFormat.format, restLength, + static_cast(Settings::getInstance()->getInt("SoundVolumeNavigation") * 1.28f)); if (sound->getPosition() + restLength < sound->getLength()) { // Sample hasn't ended yet. stillPlaying = true; @@ -191,8 +195,8 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len) std::vector converted(chunkLength); - int processedLength = SDL_AudioStreamGet(sConversionStream, - static_cast(&converted.at(0)), chunkLength); + int processedLength = + SDL_AudioStreamGet(sConversionStream, static_cast(&converted.at(0)), chunkLength); if (processedLength < 0) { LOG(LogError) << "AudioManager::mixAudio(): Couldn't convert sound chunk:"; @@ -201,9 +205,9 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len) } // Enable only when needed, as this generates a lot of debug output. -// LOG(LogDebug) << "AudioManager::mixAudio(): chunkLength " -// "/ processedLength / streamLength: " << chunkLength << " / " << -// " / " << processedLength << " / " << streamLength; + // LOG(LogDebug) << "AudioManager::mixAudio(): chunkLength " + // "/ processedLength / streamLength: " << chunkLength << " / " << + // " / " << processedLength << " / " << streamLength; // This mute flag is used to make sure that the audio buffer already sent to the // stream is not played when the video player has been stopped. Otherwise there would @@ -213,8 +217,9 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len) SDL_MixAudioFormat(stream, &converted.at(0), sAudioFormat.format, processedLength, 0); } else { - SDL_MixAudioFormat(stream, &converted.at(0), sAudioFormat.format, processedLength, - static_cast(Settings::getInstance()->getInt("SoundVolumeVideos") * 1.28f)); + SDL_MixAudioFormat( + stream, &converted.at(0), sAudioFormat.format, processedLength, + static_cast(Settings::getInstance()->getInt("SoundVolumeVideos") * 1.28f)); } // If nothing is playing, pause the device until there is more audio to output. @@ -224,6 +229,7 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len) void AudioManager::registerSound(std::shared_ptr& sound) { + // Add sound to sound vector. sSoundVector.push_back(sound); } @@ -267,7 +273,7 @@ void AudioManager::setupAudioStream(int sampleRate) // Used for streaming audio from videos. sConversionStream = SDL_NewAudioStream(AUDIO_F32, 2, sampleRate, sAudioFormat.format, - sAudioFormat.channels, sAudioFormat.freq); + sAudioFormat.channels, sAudioFormat.freq); if (sConversionStream == nullptr) { LOG(LogError) << "Failed to create audio conversion stream:"; LOG(LogError) << SDL_GetError(); @@ -298,7 +304,7 @@ void AudioManager::clearStream() // The SDL_AudioStreamClear() function is unstable and causes random crashes, so // we have to implement a workaround instead where SDL_AudioStreamGet() is used // to empty the stream. -// SDL_AudioStreamClear(sConversionStream); + // SDL_AudioStreamClear(sConversionStream); mIsClearingStream = true; @@ -307,8 +313,8 @@ void AudioManager::clearStream() while ((streamSize = SDL_AudioStreamAvailable(sConversionStream)) > 0) { std::vector readBuffer(length); - int processedLength = SDL_AudioStreamGet(sConversionStream, - static_cast(&readBuffer.at(0)), length); + int processedLength = + SDL_AudioStreamGet(sConversionStream, static_cast(&readBuffer.at(0)), length); if (processedLength <= 0) { break; } diff --git a/es-core/src/CECInput.cpp b/es-core/src/CECInput.cpp index 1750a37fb..dfd8caa49 100644 --- a/es-core/src/CECInput.cpp +++ b/es-core/src/CECInput.cpp @@ -33,11 +33,12 @@ extern int SDL_USER_CECBUTTONUP; CECInput* CECInput::sInstance = nullptr; #if defined(HAVE_LIBCEC) -static void onAlert(void* /*cbParam*/, const CEC::libcec_alert type, - const CEC::libcec_parameter param) +static void onAlert(void* /*cbParam*/, + const CEC::libcec_alert type, + const CEC::libcec_parameter param) { - LOG(LogDebug) << "CECInput::onAlert type: " << CECInput::getAlertTypeString(type) << - " parameter: " << reinterpret_cast(param.paramData); + LOG(LogDebug) << "CECInput::onAlert type: " << CECInput::getAlertTypeString(type) + << " parameter: " << reinterpret_cast(param.paramData); } static void onCommand(void* /*cbParam*/, const CEC::cec_command* command) @@ -50,7 +51,7 @@ static void onKeyPress(void* /*cbParam*/, const CEC::cec_keypress* key) LOG(LogDebug) << "CECInput::onKeyPress keycode: " << CECInput::getKeyCodeString(key->keycode); SDL_Event event; - event.type = (key->duration > 0) ? SDL_USER_CECBUTTONUP : SDL_USER_CECBUTTONDOWN; + event.type = (key->duration > 0) ? SDL_USER_CECBUTTONUP : SDL_USER_CECBUTTONDOWN; event.user.code = key->keycode; SDL_PushEvent(&event); } @@ -93,14 +94,15 @@ void CECInput::deinit() } } -CECInput::CECInput() : mlibCEC(nullptr) +CECInput::CECInput() + : mlibCEC(nullptr) { - #if defined(HAVE_LIBCEC) - #if defined(_RPI_) +#if defined(HAVE_LIBCEC) +#if defined(_RPI_) // Restart vchi tv and CEC in case we just came back from another app using CEC (like Kodi). vchi_tv_and_cec_deinit(); vchi_tv_and_cec_init(); - #endif // _RPI_ +#endif // _RPI_ CEC::ICECCallbacks callbacks; CEC::libcec_configuration config; @@ -136,8 +138,8 @@ CECInput::CECInput() : mlibCEC(nullptr) } for (int i = 0; i < numAdapters; i++) - LOG(LogDebug) << "CEC adapter: " << i << " path: " << adapters[i].strComPath << - " name: " << adapters[i].strComName; + LOG(LogDebug) << "CEC adapter: " << i << " path: " << adapters[i].strComPath + << " name: " << adapters[i].strComName; if (!mlibCEC->Open(adapters[0].strComName)) { LOG(LogError) << "CECInput::mAdapter->Open failed"; @@ -145,28 +147,29 @@ CECInput::CECInput() : mlibCEC(nullptr) mlibCEC = nullptr; return; } - #endif // HAVE_LIBCEC +#endif // HAVE_LIBCEC } CECInput::~CECInput() { - #if defined(HAVE_LIBCEC) +#if defined(HAVE_LIBCEC) if (mlibCEC) { mlibCEC->Close(); UnloadLibCec(mlibCEC); mlibCEC = nullptr; } - #if defined(_RPI_) +#if defined(_RPI_) // Deinit vchi tv and CEC in case we are going to launch another app using CEC (like Kodi). vchi_tv_and_cec_deinit(); - #endif // _RPI_ - #endif // HAVE_LIBCEC +#endif // _RPI_ +#endif // HAVE_LIBCEC } std::string CECInput::getAlertTypeString(const unsigned int _type) { + // clang-format off switch (_type) { #if defined(HAVE_LIBCEC) case CEC::CEC_ALERT_SERVICE_DEVICE: { return "Service-Device"; } break; @@ -180,10 +183,12 @@ std::string CECInput::getAlertTypeString(const unsigned int _type) #endif // HAVE_LIBCEC default: { return "Unknown"; } break; } + // clang-format on } std::string CECInput::getOpCodeString(const unsigned int _opCode) { + // clang-format off switch (_opCode) { #if defined(HAVE_LIBCEC) case CEC::CEC_OPCODE_ACTIVE_SOURCE: { return "Active-Source"; } break; @@ -261,10 +266,12 @@ std::string CECInput::getOpCodeString(const unsigned int _opCode) #endif // HAVE_LIBCEC default: { return "Unknown"; } break; } + // clang-format on } std::string CECInput::getKeyCodeString(const unsigned int _keyCode) { + // clang-format off switch (_keyCode) { #if defined(HAVE_LIBCEC) case CEC::CEC_USER_CONTROL_CODE_SELECT: { return "Select"; } break; @@ -358,5 +365,6 @@ std::string CECInput::getKeyCodeString(const unsigned int _keyCode) case 0: #endif // HAVE_LIBCEC default: { return "Unknown"; } break; + // clang-format off } } \ No newline at end of file diff --git a/es-core/src/CECInput.h b/es-core/src/CECInput.h index 4172f911b..216e849e0 100644 --- a/es-core/src/CECInput.h +++ b/es-core/src/CECInput.h @@ -11,7 +11,10 @@ #include -namespace CEC { class ICECAdapter; } +namespace CEC +{ + class ICECAdapter; +} class CECInput { @@ -23,7 +26,7 @@ public: static std::string getKeyCodeString(const unsigned int _keyCode); private: - CECInput(); + CECInput(); ~CECInput(); static CECInput* sInstance; diff --git a/es-core/src/GuiComponent.cpp b/es-core/src/GuiComponent.cpp index 66f5e1ea6..d3cf8e09e 100644 --- a/es-core/src/GuiComponent.cpp +++ b/es-core/src/GuiComponent.cpp @@ -8,31 +8,30 @@ #include "GuiComponent.h" -#include "animations/Animation.h" -#include "animations/AnimationController.h" -#include "renderers/Renderer.h" #include "Log.h" #include "ThemeData.h" #include "Window.h" +#include "animations/Animation.h" +#include "renderers/Renderer.h" #include GuiComponent::GuiComponent(Window* window) - : mWindow(window), - mParent(nullptr), - mColor(0), - mColorShift(0), - mColorShiftEnd(0), - mOpacity(255), - mSaturation(1.0), - mPosition(Vector3f::Zero()), - mOrigin(Vector2f::Zero()), - mRotationOrigin(0.5, 0.5), - mSize(Vector2f::Zero()), - mTransform(Transform4x4f::Identity()), - mIsProcessing(false), - mVisible(true), - mEnabled(true) + : mWindow(window) + , mParent(nullptr) + , mColor(0) + , mColorShift(0) + , mColorShiftEnd(0) + , mOpacity(255) + , mSaturation(1.0f) + , mPosition(Vector3f::Zero()) + , mOrigin(Vector2f::Zero()) + , mRotationOrigin(0.5f, 0.5f) + , mSize(Vector2f::Zero()) + , mTransform(Transform4x4f::Identity()) + , mIsProcessing(false) + , mVisible(true) + , mEnabled(true) { for (unsigned char i = 0; i < MAX_ANIMATIONS; i++) mAnimationMap[i] = nullptr; @@ -94,105 +93,30 @@ void GuiComponent::renderChildren(const Transform4x4f& transform) const getChild(i)->render(transform); } -Vector3f GuiComponent::getPosition() const -{ - return mPosition; -} - void GuiComponent::setPosition(float x, float y, float z) { mPosition = Vector3f(x, y, z); onPositionChanged(); } -Vector2f GuiComponent::getOrigin() const -{ - return mOrigin; -} - void GuiComponent::setOrigin(float x, float y) { mOrigin = Vector2f(x, y); onOriginChanged(); } -Vector2f GuiComponent::getRotationOrigin() const -{ - return mRotationOrigin; -} - -void GuiComponent::setRotationOrigin(float x, float y) -{ - mRotationOrigin = Vector2f(x, y); -} - -Vector2f GuiComponent::getSize() const -{ - return mSize; -} - void GuiComponent::setSize(float w, float h) { mSize = Vector2f(w, h); onSizeChanged(); } -float GuiComponent::getRotation() const -{ - return mRotation; -} - -void GuiComponent::setRotation(float rotation) -{ - mRotation = rotation; -} - -float GuiComponent::getScale() const -{ - return mScale; -} - -void GuiComponent::setScale(float scale) -{ - mScale = scale; -} - -float GuiComponent::getZIndex() const -{ - return mZIndex; -} - -void GuiComponent::setZIndex(float z) -{ - mZIndex = z; -} - -float GuiComponent::getDefaultZIndex() const -{ - return mDefaultZIndex; -} - -void GuiComponent::setDefaultZIndex(float z) -{ - mDefaultZIndex = z; -} - -bool GuiComponent::isVisible() const -{ - return mVisible; -} -void GuiComponent::setVisible(bool visible) -{ - mVisible = visible; -} - Vector2f GuiComponent::getCenter() const { - return Vector2f(mPosition.x() - (getSize().x() * mOrigin.x()) + getSize().x() / 2, - mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2); + return Vector2f(mPosition.x() - (getSize().x() * mOrigin.x()) + getSize().x() / 2.0f, + mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2.0f); } -// Children stuff. void GuiComponent::addChild(GuiComponent* cmp) { mChildren.push_back(cmp); @@ -222,11 +146,6 @@ void GuiComponent::removeChild(GuiComponent* cmp) } } -void GuiComponent::clearChildren() -{ - mChildren.clear(); -} - void GuiComponent::sortChildren() { std::stable_sort(mChildren.begin(), mChildren.end(), [](GuiComponent* a, GuiComponent* b) { @@ -234,15 +153,10 @@ void GuiComponent::sortChildren() }); } -unsigned int GuiComponent::getChildCount() const -{ - return static_cast(mChildren.size()); -} - int GuiComponent::getChildIndex() const { std::vector::iterator it = - std::find(getParent()->mChildren.begin(), getParent()->mChildren.end(), this); + std::find(getParent()->mChildren.begin(), getParent()->mChildren.end(), this); if (it != getParent()->mChildren.end()) return static_cast(std::distance(getParent()->mChildren.begin(), it)); @@ -250,26 +164,6 @@ int GuiComponent::getChildIndex() const return -1; } -GuiComponent* GuiComponent::getChild(unsigned int i) const -{ - return mChildren.at(i); -} - -void GuiComponent::setParent(GuiComponent* parent) -{ - mParent = parent; -} - -GuiComponent* GuiComponent::getParent() const -{ - return mParent; -} - -unsigned char GuiComponent::getOpacity() const -{ - return mOpacity; -} - void GuiComponent::setOpacity(unsigned char opacity) { mOpacity = opacity; @@ -277,92 +171,47 @@ void GuiComponent::setOpacity(unsigned char opacity) (*it)->setOpacity(opacity); } -unsigned int GuiComponent::getColor() const -{ - return mColor; -} - -unsigned int GuiComponent::getColorShift() const -{ - return mColorShift; -} - -void GuiComponent::setColor(unsigned int color) -{ - mColor = color; - mColorOpacity = mColor & 0x000000FF; -} - -float GuiComponent::getSaturation() const -{ - return static_cast(mColor); -} - -void GuiComponent::setSaturation(float saturation) -{ - mSaturation = saturation; -} - -void GuiComponent::setColorShift(unsigned int color) -{ - mColorShift = color; - mColorShiftEnd = color; -} - const Transform4x4f& GuiComponent::getTransform() { mTransform = Transform4x4f::Identity(); mTransform.translate(mPosition); - if (mScale != 1.0) + + if (mScale != 1.0f) mTransform.scale(mScale); - if (mRotation != 0.0) { + + if (mRotation != 0.0f) { // Calculate offset as difference between origin and rotation origin. Vector2f rotationSize = getRotationSize(); float xOff = (mOrigin.x() - mRotationOrigin.x()) * rotationSize.x(); float yOff = (mOrigin.y() - mRotationOrigin.y()) * rotationSize.y(); // Transform to offset point. - if (xOff != 0.0 || yOff != 0.0) - mTransform.translate(Vector3f(xOff * -1, yOff * -1, 0.0f)); + if (xOff != 0.0f || yOff != 0.0f) + mTransform.translate(Vector3f(xOff * -1.0f, yOff * -1.0f, 0.0f)); // Apply rotation transform. mTransform.rotateZ(mRotation); // Transform back to original point. - if (xOff != 0.0 || yOff != 0.0) + if (xOff != 0.0f || yOff != 0.0f) mTransform.translate(Vector3f(xOff, yOff, 0.0f)); } - mTransform.translate(Vector3f(mOrigin.x() * mSize.x() * -1, - mOrigin.y() * mSize.y() * -1, 0.0f)); + mTransform.translate( + Vector3f(mOrigin.x() * mSize.x() * -1.0f, mOrigin.y() * mSize.y() * -1.0f, 0.0f)); return mTransform; } -std::string GuiComponent::getValue() const -{ - return ""; -} - -void GuiComponent::setValue(const std::string& /*value*/) -{ -} - -std::string GuiComponent::getHiddenValue() const -{ - return ""; -} - -void GuiComponent::setHiddenValue(const std::string& /*value*/) -{ -} - void GuiComponent::textInput(const std::string& text) { for (auto iter = mChildren.cbegin(); iter != mChildren.cend(); iter++) (*iter)->textInput(text); } -void GuiComponent::setAnimation(Animation* anim, int delay, - std::function finishedCallback, bool reverse, unsigned char slot) +void GuiComponent::setAnimation(Animation* anim, + int delay, + std::function finishedCallback, + bool reverse, + unsigned char slot) { assert(slot < MAX_ANIMATIONS); @@ -447,29 +296,14 @@ void GuiComponent::cancelAllAnimations() cancelAnimation(i); } -bool GuiComponent::isAnimationPlaying(unsigned char slot) const -{ - return mAnimationMap[slot] != nullptr; -} - -bool GuiComponent::isAnimationReversed(unsigned char slot) const -{ - assert(mAnimationMap[slot] != nullptr); - return mAnimationMap[slot]->isReversed(); -} - -int GuiComponent::getAnimationTime(unsigned char slot) const -{ - assert(mAnimationMap[slot] != nullptr); - return mAnimationMap[slot]->getTime(); -} - void GuiComponent::applyTheme(const std::shared_ptr& theme, - const std::string& view, const std::string& element, unsigned int properties) + const std::string& view, + const std::string& element, + unsigned int properties) { - Vector2f scale = getParent() ? getParent()->getSize() - : Vector2f(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + Vector2f scale = getParent() ? getParent()->getSize() : + Vector2f(static_cast(Renderer::getScreenWidth()), + static_cast(Renderer::getScreenHeight())); const ThemeData::ThemeElement* elem = theme->getElement(view, element, ""); if (!elem) @@ -484,9 +318,9 @@ void GuiComponent::applyTheme(const std::shared_ptr& theme, if (properties & ThemeFlags::SIZE && elem->has("size")) setSize(elem->get("size") * scale); - // Position + size also implies origin - if ((properties & ORIGIN || (properties & POSITION && - properties & ThemeFlags::SIZE)) && elem->has("origin")) { + // Position + size also implies origin. + if ((properties & ORIGIN || (properties & POSITION && properties & ThemeFlags::SIZE)) && + elem->has("origin")) { setOrigin(elem->get("origin")); } @@ -521,16 +355,6 @@ void GuiComponent::updateHelpPrompts() mWindow->setHelpPrompts(prompts, getHelpStyle()); } -HelpStyle GuiComponent::getHelpStyle() -{ - return HelpStyle(); -} - -bool GuiComponent::isProcessing() const -{ - return mIsProcessing; -} - void GuiComponent::onShow() { for (unsigned int i = 0; i < getChildCount(); i++) diff --git a/es-core/src/GuiComponent.h b/es-core/src/GuiComponent.h index 3a7c41b7e..83901040f 100644 --- a/es-core/src/GuiComponent.h +++ b/es-core/src/GuiComponent.h @@ -9,11 +9,12 @@ #ifndef ES_CORE_GUI_COMPONENT_H #define ES_CORE_GUI_COMPONENT_H -#include "math/Misc.h" -#include "math/Transform4x4f.h" #include "HelpPrompt.h" #include "HelpStyle.h" #include "InputConfig.h" +#include "animations/AnimationController.h" +#include "math/Misc.h" +#include "math/Transform4x4f.h" #include #include @@ -65,72 +66,84 @@ public: // 4. Tell your children to render, based on your component's transform - renderChildren(t). virtual void render(const Transform4x4f& parentTrans); - Vector3f getPosition() const; - inline void setPosition(const Vector3f& offset) - { setPosition(offset.x(), offset.y(), offset.z()); } + Vector3f getPosition() const { return mPosition; } + void setPosition(const Vector3f& offset) { setPosition(offset.x(), offset.y(), offset.z()); } void setPosition(float x, float y, float z = 0.0f); - virtual void onPositionChanged() {}; + virtual void onPositionChanged() {} + Vector2f getOrigin() const { return mOrigin; } // Sets the origin as a percentage of this image. // (e.g. (0, 0) is top left, (0.5, 0.5) is the center.) - Vector2f getOrigin() const; void setOrigin(float originX, float originY); - inline void setOrigin(Vector2f origin) { setOrigin(origin.x(), origin.y()); } - virtual void onOriginChanged() {}; + void setOrigin(Vector2f origin) { setOrigin(origin.x(), origin.y()); } + virtual void onOriginChanged() {} + Vector2f getRotationOrigin() const { return mRotationOrigin; } // Sets the rotation origin as a percentage of this image. // (e.g. (0, 0) is top left, (0.5, 0.5) is the center.) - Vector2f getRotationOrigin() const; - void setRotationOrigin(float originX, float originY); - inline void setRotationOrigin(Vector2f origin) - { setRotationOrigin(origin.x(), origin.y()); } + void setRotationOrigin(float originX, float originY) + { + mRotationOrigin = Vector2f(originX, originY); + } + void setRotationOrigin(Vector2f origin) { setRotationOrigin(origin.x(), origin.y()); } - virtual Vector2f getSize() const; - inline void setSize(const Vector2f& size) { setSize(size.x(), size.y()); } + virtual Vector2f getSize() const { return mSize; } + void setSize(const Vector2f& size) { setSize(size.x(), size.y()); } void setSize(float w, float h); - virtual void setResize(float width, float height) {}; - virtual void onSizeChanged() {}; + virtual void setResize(float width, float height) {} + virtual void onSizeChanged() {} - virtual Vector2f getRotationSize() const { return getSize(); }; + virtual Vector2f getRotationSize() const { return getSize(); } + float getRotation() const { return mRotation; } + void setRotation(float rotation) { mRotation = rotation; } + void setRotationDegrees(float rotation) + { + setRotation(static_cast(ES_DEG_TO_RAD(rotation))); + } - float getRotation() const; - void setRotation(float rotation); - inline void setRotationDegrees(float rotation) { - setRotation(static_cast(ES_DEG_TO_RAD(rotation))); } + float getScale() const { return mScale; } + void setScale(float scale) { mScale = scale; } - float getScale() const; - void setScale(float scale); + float getZIndex() const { return mZIndex; } + void setZIndex(float zIndex) { mZIndex = zIndex; } - float getZIndex() const; - void setZIndex(float zIndex); + float getDefaultZIndex() const { return mDefaultZIndex; } + void setDefaultZIndex(float zIndex) { mDefaultZIndex = zIndex; } - float getDefaultZIndex() const; - void setDefaultZIndex(float zIndex); - - bool isVisible() const; - void setVisible(bool visible); + bool isVisible() const { return mVisible; } + void setVisible(bool visible) { mVisible = visible; } // Returns the center point of the image (takes origin into account). Vector2f getCenter() const; - void setParent(GuiComponent* parent); - GuiComponent* getParent() const; + void setParent(GuiComponent* parent) { mParent = parent; } + GuiComponent* getParent() const { return mParent; } void addChild(GuiComponent* cmp); void removeChild(GuiComponent* cmp); - void clearChildren(); + void clearChildren() { mChildren.clear(); } void sortChildren(); - unsigned int getChildCount() const; + unsigned int getChildCount() const { return static_cast(mChildren.size()); } int getChildIndex() const; - GuiComponent* getChild(unsigned int i) const; + GuiComponent* getChild(unsigned int i) const { return mChildren.at(i); } // Animation will be automatically deleted when it completes or is stopped. - bool isAnimationPlaying(unsigned char slot) const; - bool isAnimationReversed(unsigned char slot) const; - int getAnimationTime(unsigned char slot) const; - void setAnimation(Animation* animation, int delay = 0, - std::function finishedCallback = nullptr, - bool reverse = false, unsigned char slot = 0); + bool isAnimationPlaying(unsigned char slot) const { return mAnimationMap[slot] != nullptr; } + bool isAnimationReversed(unsigned char slot) const + { + assert(mAnimationMap[slot] != nullptr); + return mAnimationMap[slot]->isReversed(); + } + int getAnimationTime(unsigned char slot) const + { + assert(mAnimationMap[slot] != nullptr); + return mAnimationMap[slot]->getTime(); + } + void setAnimation(Animation* animation, + int delay = 0, + std::function finishedCallback = nullptr, + bool reverse = false, + unsigned char slot = 0); bool stopAnimation(unsigned char slot); // Like stopAnimation, but doesn't call finishedCallback - only removes the animation, leaving // things in their current state. Returns true if successful (an animation was in this slot). @@ -143,45 +156,53 @@ public: void stopAllAnimations(); void cancelAllAnimations(); - virtual bool isListScrolling() { return false; }; - virtual void stopListScrolling() {}; - virtual unsigned char getOpacity() const; + virtual bool isListScrolling() { return false; } + virtual void stopListScrolling() {} + virtual unsigned char getOpacity() const { return mOpacity; } virtual void setOpacity(unsigned char opacity); - virtual unsigned int getColor() const; - virtual unsigned int getColorShift() const; - virtual void setColor(unsigned int color); - virtual float getSaturation() const; - virtual void setSaturation(float saturation); - virtual void setColorShift(unsigned int color); - virtual void setOriginalColor(unsigned int color) { mColorOriginalValue = color; }; - virtual void setChangedColor(unsigned int color) { mColorChangedValue = color; }; + virtual unsigned int getColor() const { return mColor; } + virtual unsigned int getColorShift() const { return mColorShift; } + virtual void setColor(unsigned int color) + { + mColor = color; + mColorOpacity = mColor & 0x000000FF; + } + virtual float getSaturation() const { return static_cast(mColor); } + virtual void setSaturation(float saturation) { mSaturation = saturation; } + virtual void setColorShift(unsigned int color) + { + mColorShift = color; + mColorShiftEnd = color; + } + virtual void setOriginalColor(unsigned int color) { mColorOriginalValue = color; } + virtual void setChangedColor(unsigned int color) { mColorChangedValue = color; } // These functions are used to enable and disable options in menus, i.e. switches and similar. - virtual bool getEnabled() { return mEnabled; }; - virtual void setEnabled(bool state) { mEnabled = state; }; + virtual bool getEnabled() { return mEnabled; } + virtual void setEnabled(bool state) { mEnabled = state; } - virtual std::shared_ptr getFont() const { return nullptr; }; + virtual std::shared_ptr getFont() const { return nullptr; } const Transform4x4f& getTransform(); - virtual std::string getValue() const; - virtual void setValue(const std::string& value); + virtual std::string getValue() const { return ""; } + virtual void setValue(const std::string& value) {} - virtual std::string getHiddenValue() const; - virtual void setHiddenValue(const std::string& value); + virtual std::string getHiddenValue() const { return ""; } + virtual void setHiddenValue(const std::string& value) {} // Used to set the parameters for ScrollableContainer. - virtual void setScrollParameters(float, float, int) {}; + virtual void setScrollParameters(float, float, int) {} - virtual void onFocusGained() {}; - virtual void onFocusLost() {}; + virtual void onFocusGained() {} + virtual void onFocusLost() {} virtual void onShow(); virtual void onHide(); virtual void onStopVideo(); virtual void onPauseVideo(); virtual void onUnpauseVideo(); - virtual bool isVideoPaused() { return false; }; + virtual bool isVideoPaused() { return false; } virtual void onScreensaverActivate(); virtual void onScreensaverDeactivate(); @@ -192,18 +213,20 @@ public: // Default implementation just handles and tags as normalized float pairs. // You probably want to keep this behavior for any derived classes as well as add your own. virtual void applyTheme(const std::shared_ptr& theme, - const std::string& view, const std::string& element, unsigned int properties); + const std::string& view, + const std::string& element, + unsigned int properties); // Returns a list of help prompts. - virtual std::vector getHelpPrompts() { return std::vector(); }; + virtual std::vector getHelpPrompts() { return std::vector(); } // Called whenever help prompts change. void updateHelpPrompts(); - virtual HelpStyle getHelpStyle(); + virtual HelpStyle getHelpStyle() { return HelpStyle(); } // Returns true if the component is busy doing background processing (e.g. HTTP downloads). - bool isProcessing() const; + bool isProcessing() const { return mIsProcessing; } const static unsigned char MAX_ANIMATIONS = 4; diff --git a/es-core/src/HelpStyle.cpp b/es-core/src/HelpStyle.cpp index 2e7f7ea1f..b57d55e73 100644 --- a/es-core/src/HelpStyle.cpp +++ b/es-core/src/HelpStyle.cpp @@ -31,9 +31,9 @@ void HelpStyle::applyTheme(const std::shared_ptr& theme, const std::s return; if (elem->has("pos")) - position = elem->get("pos") * - Vector2f(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + position = + elem->get("pos") * Vector2f(static_cast(Renderer::getScreenWidth()), + static_cast(Renderer::getScreenHeight())); if (elem->has("origin")) origin = elem->get("origin"); diff --git a/es-core/src/HttpReq.cpp b/es-core/src/HttpReq.cpp index 09fc67565..4888d346d 100644 --- a/es-core/src/HttpReq.cpp +++ b/es-core/src/HttpReq.cpp @@ -10,21 +10,21 @@ #include "HttpReq.h" +#include "Log.h" #include "resources/ResourceManager.h" #include "utils/FileSystemUtil.h" -#include "Log.h" #include CURLM* HttpReq::s_multi_handle; std::map HttpReq::s_requests; -std::string HttpReq::urlEncode(const std::string &s) +std::string HttpReq::urlEncode(const std::string& s) { const std::string unreserved = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"; + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"; - std::string escaped=""; + std::string escaped = ""; for (size_t i = 0; i < s.length(); i++) { if (unreserved.find_first_of(s[i]) != std::string::npos) { escaped.push_back(s[i]); @@ -43,11 +43,13 @@ bool HttpReq::isUrl(const std::string& str) { // The worst guess. return (!str.empty() && !Utils::FileSystem::exists(str) && - (str.find("http://") != std::string::npos || str.find("https://") != - std::string::npos || str.find("www.") != std::string::npos)); + (str.find("http://") != std::string::npos || + str.find("https://") != std::string::npos || str.find("www.") != std::string::npos)); } -HttpReq::HttpReq(const std::string& url) : mStatus(REQ_IN_PROGRESS), mHandle(nullptr) +HttpReq::HttpReq(const std::string& url) + : mStatus(REQ_IN_PROGRESS) + , mHandle(nullptr) { // The multi-handle is cleaned up via a call from GuiScraperSearch after the scraping // has been completed for a game, meaning the handle is valid for all cURL requests @@ -57,14 +59,16 @@ HttpReq::HttpReq(const std::string& url) : mStatus(REQ_IN_PROGRESS), mHandle(nul mHandle = curl_easy_init(); +#if defined(_WIN64) // On Windows, use the bundled cURL TLS/SSL certificates (which actually come from the // Mozilla project). There is a possibility to use the OS provided Schannel certificates // but I haven't been able to get this to work and it also seems to be problematic on // older Windows versions. - #if defined(_WIN64) - curl_easy_setopt(mHandle, CURLOPT_CAINFO, ResourceManager::getInstance()-> - getResourcePath(":/certificates/curl-ca-bundle.crt").c_str()); - #endif + curl_easy_setopt(mHandle, CURLOPT_CAINFO, + ResourceManager::getInstance() + ->getResourcePath(":/certificates/curl-ca-bundle.crt") + .c_str()); +#endif if (mHandle == nullptr) { mStatus = REQ_IO_ERROR; @@ -140,8 +144,8 @@ HttpReq::~HttpReq() CURLMcode merr = curl_multi_remove_handle(s_multi_handle, mHandle); if (merr != CURLM_OK) { - LOG(LogError) << "Error removing curl_easy handle from curl_multi: " << - curl_multi_strerror(merr); + LOG(LogError) << "Error removing curl_easy handle from curl_multi: " + << curl_multi_strerror(merr); } curl_easy_cleanup(mHandle); @@ -194,16 +198,6 @@ std::string HttpReq::getContent() const return mContent.str(); } -void HttpReq::onError(const std::string& msg) -{ - mErrorMsg = msg; -} - -std::string HttpReq::getErrorMsg() -{ - return mErrorMsg; -} - // Used as a curl callback. // size = size of an element, nmemb = number of elements. // Return value is number of elements successfully read. diff --git a/es-core/src/HttpReq.h b/es-core/src/HttpReq.h index 3f294dbae..28a5ff228 100644 --- a/es-core/src/HttpReq.h +++ b/es-core/src/HttpReq.h @@ -43,6 +43,7 @@ public: ~HttpReq(); enum Status { + // clang-format off REQ_IN_PROGRESS, // Request is in progress. REQ_SUCCESS, // Request completed successfully, get it with getContent(). REQ_IO_ERROR, // Some error happened, get it with getErrorMsg(). @@ -50,13 +51,14 @@ public: REQ_BAD_STATUS_CODE, // Some invalid HTTP response status code happened (non-200). REQ_INVALID_RESPONSE, // The HTTP response was invalid. REQ_UNDEFINED_ERROR + // clang-format on }; Status status(); // Process any received data and return the status afterwards. - std::string getErrorMsg(); + std::string getErrorMsg() { return mErrorMsg; } std::string getContent() const; // mStatus must be REQ_SUCCESS. - static std::string urlEncode(const std::string &s); + static std::string urlEncode(const std::string& s); static bool isUrl(const std::string& s); static void cleanupCurlMulti() @@ -65,11 +67,11 @@ public: curl_multi_cleanup(s_multi_handle); s_multi_handle = nullptr; } - }; + } private: static size_t write_content(void* buff, size_t size, size_t nmemb, void* req_ptr); - void onError(const std::string& msg); + void onError(const std::string& msg) { mErrorMsg = msg; } // God dammit libcurl why can't you have some way to check the status of an // individual handle why do I have to handle ALL messages at once. diff --git a/es-core/src/ImageIO.cpp b/es-core/src/ImageIO.cpp index 54710c98c..6793fa79c 100644 --- a/es-core/src/ImageIO.cpp +++ b/es-core/src/ImageIO.cpp @@ -14,7 +14,9 @@ #include std::vector ImageIO::loadFromMemoryRGBA32(const unsigned char* data, - const size_t size, size_t& width, size_t& height) + const size_t size, + size_t& width, + size_t& height) { std::vector rawData; width = 0; @@ -44,8 +46,7 @@ std::vector ImageIO::loadFromMemoryRGBA32(const unsigned char* da // This is necessary, because width*height*bpp might not be == pitch. unsigned char* tempData = new unsigned char[width * height * 4]; for (size_t i = 0; i < height; i++) { - const BYTE* scanLine = - FreeImage_GetScanLine(fiBitmap, static_cast(i)); + const BYTE* scanLine = FreeImage_GetScanLine(fiBitmap, static_cast(i)); memcpy(tempData + (i * width * 4), scanLine, width * 4); } // Convert from BGRA to RGBA. @@ -69,8 +70,8 @@ std::vector ImageIO::loadFromMemoryRGBA32(const unsigned char* da } } else { - LOG(LogError) << "Couldn't load image, file is missing or the file type is " << - (format == FIF_UNKNOWN ? "unknown" : "unsupported"); + LOG(LogError) << "Couldn't load image, file is missing or the file type is " + << (format == FIF_UNKNOWN ? "unknown" : "unsupported"); } // Free fiMemory again. FreeImage_CloseMemory(fiMemory); diff --git a/es-core/src/ImageIO.h b/es-core/src/ImageIO.h index 60c83b03b..3fd62e267 100644 --- a/es-core/src/ImageIO.h +++ b/es-core/src/ImageIO.h @@ -16,7 +16,9 @@ class ImageIO { public: static std::vector loadFromMemoryRGBA32(const unsigned char* data, - const size_t size, size_t& width, size_t& height); + const size_t size, + size_t& width, + size_t& height); static void flipPixelsVert(unsigned char* imagePx, const size_t& width, const size_t& height); }; diff --git a/es-core/src/InputConfig.cpp b/es-core/src/InputConfig.cpp index 8980ba58e..2a68c3a8a 100644 --- a/es-core/src/InputConfig.cpp +++ b/es-core/src/InputConfig.cpp @@ -12,13 +12,10 @@ #include -InputConfig::InputConfig( - int deviceId, - const std::string& deviceName, - const std::string& deviceGUID) - : mDeviceId(deviceId), - mDeviceName(deviceName), - mDeviceGUID(deviceGUID) +InputConfig::InputConfig(int deviceId, const std::string& deviceName, const std::string& deviceGUID) + : mDeviceId(deviceId) + , mDeviceName(deviceName) + , mDeviceGUID(deviceGUID) { } @@ -59,16 +56,6 @@ std::string InputConfig::toLower(std::string str) return str; } -void InputConfig::clear() -{ - mNameMap.clear(); -} - -bool InputConfig::isConfigured() -{ - return mNameMap.size() > 0; -} - void InputConfig::mapInput(const std::string& name, Input input) { mNameMap[toLower(name)] = input; @@ -100,19 +87,19 @@ bool InputConfig::isMappedLike(const std::string& name, Input input) { if (name == "left") { return isMappedTo("left", input) || isMappedTo("leftthumbstickleft", input) || - isMappedTo("rightthumbstickleft", input); + isMappedTo("rightthumbstickleft", input); } else if (name == "right") { return isMappedTo("right", input) || isMappedTo("leftthumbstickright", input) || - isMappedTo("rightthumbstickright", input); + isMappedTo("rightthumbstickright", input); } else if (name == "up") { return isMappedTo("up", input) || isMappedTo("leftthumbstickup", input) || - isMappedTo("rightthumbstickup", input); + isMappedTo("rightthumbstickup", input); } else if (name == "down") { return isMappedTo("down", input) || isMappedTo("leftthumbstickdown", input) || - isMappedTo("rightthumbstickdown", input); + isMappedTo("rightthumbstickdown", input); } else if (name == "leftshoulder") { return isMappedTo("leftshoulder", input) || isMappedTo("pageup", input); @@ -182,8 +169,8 @@ void InputConfig::loadFromXML(pugi::xml_node& node) InputType typeEnum = stringToInputType(type); if (typeEnum == TYPE_COUNT) { - LOG(LogError) << "InputConfig load error - input of type \"" << type << - "\" is invalid! Skipping input \"" << name << "\".\n"; + LOG(LogError) << "InputConfig load error - input of type \"" << type + << "\" is invalid! Skipping input \"" << name << "\".\n"; continue; } @@ -191,8 +178,7 @@ void InputConfig::loadFromXML(pugi::xml_node& node) int value = input.attribute("value").as_int(); if (value == 0) { - LOG(LogWarning) << "InputConfig value is 0 for " << - type << " " << id << "!\n"; + LOG(LogWarning) << "InputConfig value is 0 for " << type << " " << id << "!\n"; } mNameMap[toLower(name)] = Input(mDeviceId, typeEnum, id, value, true); diff --git a/es-core/src/InputConfig.h b/es-core/src/InputConfig.h index 9f412c672..be7a06ee3 100644 --- a/es-core/src/InputConfig.h +++ b/es-core/src/InputConfig.h @@ -9,18 +9,18 @@ #ifndef ES_CORE_INPUT_CONFIG_H #define ES_CORE_INPUT_CONFIG_H +#include #include #include -#include #include #include #include #define DEVICE_KEYBOARD -1 -#define DEVICE_CEC -2 +#define DEVICE_CEC -2 enum InputType { - TYPE_AXIS, + TYPE_AXIS, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). TYPE_BUTTON, TYPE_KEY, TYPE_CEC_BUTTON, @@ -32,8 +32,7 @@ namespace pugi class xml_node; } -struct Input -{ +struct Input { public: int device; InputType type; @@ -50,23 +49,16 @@ public: type = TYPE_COUNT; } - Input( - int dev, - InputType t, - int i, - int val, - bool conf) - : device(dev), - type(t),id(i), - value(val), - configured(conf) + Input(int dev, InputType t, int i, int val, bool conf) + : device(dev) + , type(t) + , id(i) + , value(val) + , configured(conf) { } - std::string getCECButtonName(int keycode) - { - return CECInput::getKeyCodeString(keycode); - } + std::string getCECButtonName(int keycode) { return CECInput::getKeyCodeString(keycode); } std::string string() { @@ -113,8 +105,8 @@ public: InputType stringToInputType(const std::string& type); std::string toLower(std::string str); - void clear(); - bool isConfigured(); + void clear() { mNameMap.clear(); } + bool isConfigured() { return mNameMap.size() > 0; } void mapInput(const std::string& name, Input input); void unmapInput(const std::string& name); // Unmap all Inputs mapped to this name. @@ -134,9 +126,9 @@ public: void loadFromXML(pugi::xml_node& root); void writeToXML(pugi::xml_node& parent); - inline int getDeviceId() const { return mDeviceId; }; - inline const std::string& getDeviceName() { return mDeviceName; } - inline const std::string& getDeviceGUIDString() { return mDeviceGUID; } + int getDeviceId() const { return mDeviceId; } + const std::string& getDeviceName() { return mDeviceName; } + const std::string& getDeviceGUIDString() { return mDeviceGUID; } private: std::map mNameMap; diff --git a/es-core/src/InputManager.cpp b/es-core/src/InputManager.cpp index 92fff54e7..ad6ffb624 100644 --- a/es-core/src/InputManager.cpp +++ b/es-core/src/InputManager.cpp @@ -10,14 +10,14 @@ #include "InputManager.h" -#include "resources/ResourceManager.h" -#include "utils/FileSystemUtil.h" -#include "utils/StringUtil.h" #include "CECInput.h" #include "Log.h" #include "Platform.h" #include "Scripting.h" #include "Window.h" +#include "resources/ResourceManager.h" +#include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" #include #include @@ -30,12 +30,14 @@ int SDL_USER_CECBUTTONUP = -1; InputManager* InputManager::sInstance = nullptr; -InputManager::InputManager() : mKeyboardInputConfig(nullptr) +InputManager::InputManager() + : mKeyboardInputConfig(nullptr) { } InputManager::~InputManager() { + // Deinit when destroyed. deinit(); } @@ -66,8 +68,8 @@ void InputManager::init() mConfigFileExists = true; } - mKeyboardInputConfig = std::make_unique(DEVICE_KEYBOARD, - "Keyboard", KEYBOARD_GUID_STRING); + mKeyboardInputConfig = + std::make_unique(DEVICE_KEYBOARD, "Keyboard", KEYBOARD_GUID_STRING); bool customConfig = loadInputConfig(mKeyboardInputConfig.get()); @@ -84,18 +86,18 @@ void InputManager::init() // the bundled mapping is incorrect, or the SDL version is a bit older, it makes sense to be // able to customize this. If a controller GUID is present in the mappings file that is // already present inside SDL, the custom mapping will overwrite the bundled one. - std::string mappingsFile = Utils::FileSystem::getHomePath() + - "/.emulationstation/" + "es_controller_mappings.cfg"; + std::string mappingsFile = + Utils::FileSystem::getHomePath() + "/.emulationstation/" + "es_controller_mappings.cfg"; if (!Utils::FileSystem::exists(mappingsFile)) - mappingsFile = ResourceManager::getInstance()-> - getResourcePath(":/controllers/es_controller_mappings.cfg"); + mappingsFile = ResourceManager::getInstance()->getResourcePath( + ":/controllers/es_controller_mappings.cfg"); int controllerMappings = SDL_GameControllerAddMappingsFromFile(mappingsFile.c_str()); if (controllerMappings != -1 && controllerMappings != 0) { - LOG(LogInfo) << "Loaded " << controllerMappings << " controller " << - (controllerMappings == 1 ? "mapping" : "mappings"); + LOG(LogInfo) << "Loaded " << controllerMappings << " controller " + << (controllerMappings == 1 ? "mapping" : "mappings"); } int numJoysticks = SDL_NumJoysticks(); @@ -150,18 +152,20 @@ void InputManager::writeDeviceConfig(InputConfig* config) std::string path = getConfigPath(); LOG(LogDebug) << "InputManager::writeDeviceConfig(): " - "Saving input configuration file to \"" << path << "\""; + "Saving input configuration file to \"" + << path << "\""; pugi::xml_document doc; if (Utils::FileSystem::exists(path)) { // Merge files. - #if defined(_WIN64) + +#if defined(_WIN64) pugi::xml_parse_result result = - doc.load_file(Utils::String::stringToWideString(path).c_str()); - #else + doc.load_file(Utils::String::stringToWideString(path).c_str()); +#else pugi::xml_parse_result result = doc.load_file(path.c_str()); - #endif +#endif if (!result) { LOG(LogError) << "Couldn't parse input configuration file: " << result.description(); } @@ -172,8 +176,8 @@ void InputManager::writeDeviceConfig(InputConfig* config) // If inputAction @type=onfinish is set, let doOnFinish command take care of // creating input configuration. We just put the input configuration into a // temporary input config file. - pugi::xml_node actionnode = root.find_child_by_attribute("inputAction", - "type", "onfinish"); + pugi::xml_node actionnode = + root.find_child_by_attribute("inputAction", "type", "onfinish"); if (actionnode) { path = getTemporaryConfigPath(); doc.reset(); @@ -181,12 +185,12 @@ void InputManager::writeDeviceConfig(InputConfig* config) root.append_copy(actionnode); } else { - pugi::xml_node oldEntry = root.find_child_by_attribute("inputConfig", - "deviceGUID", config->getDeviceGUIDString().c_str()); + pugi::xml_node oldEntry = root.find_child_by_attribute( + "inputConfig", "deviceGUID", config->getDeviceGUIDString().c_str()); if (oldEntry) root.remove_child(oldEntry); oldEntry = root.find_child_by_attribute("inputConfig", "deviceName", - config->getDeviceName().c_str()); + config->getDeviceName().c_str()); if (oldEntry) root.remove_child(oldEntry); } @@ -200,11 +204,11 @@ void InputManager::writeDeviceConfig(InputConfig* config) config->writeToXML(root); - #if defined(_WIN64) +#if defined(_WIN64) doc.save_file(Utils::String::stringToWideString(path).c_str()); - #else +#else doc.save_file(path.c_str()); - #endif +#endif Scripting::fireEvent("config-changed"); Scripting::fireEvent("controls-changed"); @@ -222,12 +226,12 @@ void InputManager::doOnFinish() pugi::xml_document doc; if (Utils::FileSystem::exists(path)) { - #if defined(_WIN64) +#if defined(_WIN64) pugi::xml_parse_result result = - doc.load_file(Utils::String::stringToWideString(path).c_str()); - #else + doc.load_file(Utils::String::stringToWideString(path).c_str()); +#else pugi::xml_parse_result result = doc.load_file(path.c_str()); - #endif +#endif if (!result) { LOG(LogError) << "Couldn't parse input configuration file: " << result.description(); @@ -238,18 +242,18 @@ void InputManager::doOnFinish() root = root.find_child_by_attribute("inputAction", "type", "onfinish"); if (root) { for (pugi::xml_node command = root.child("command"); command; - command = command.next_sibling("command")) { + command = command.next_sibling("command")) { std::string tocall = command.text().get(); LOG(LogInfo) << " " << tocall; std::cout << "==============================================\n" - "input config finish command:\n"; + "input config finish command:\n"; int exitCode = runSystemCommand(tocall); std::cout << "==============================================\n"; if (exitCode != 0) { - LOG(LogWarning) << "...launch terminated with nonzero exit code " << - exitCode << "!"; + LOG(LogWarning) << "...launch terminated with nonzero exit code " + << exitCode << "!"; } } } @@ -298,11 +302,11 @@ int InputManager::getButtonCountByDevice(SDL_JoystickID id) if (id == DEVICE_KEYBOARD) return -1; else if (id == DEVICE_CEC) - #if defined(HAVE_CECLIB) +#if defined(HAVE_CECLIB) return CEC::CEC_USER_CONTROL_CODE_MAX; - #else +#else return 0; - #endif +#endif else return SDL_JoystickNumButtons(mJoysticks[id]); } @@ -343,10 +347,10 @@ bool InputManager::parseEvent(const SDL_Event& event, Window* window) switch (event.type) { case SDL_CONTROLLERAXISMOTION: { - // Whether to only accept input from the first controller. - if (Settings::getInstance()->getBool("InputOnlyFirstController")) - if (mInputConfigs.begin()->first != event.cdevice.which) - return false; + // Whether to only accept input from the first controller. + if (Settings::getInstance()->getBool("InputOnlyFirstController")) + if (mInputConfigs.begin()->first != event.cdevice.which) + return false; // This is needed for a situation which sometimes occur when a game is launched, // some axis input is generated and then the controller is disconnected before @@ -363,14 +367,17 @@ bool InputManager::parseEvent(const SDL_Event& event, Window* window) int deadzone = 0; if (event.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT || - event.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + event.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) { deadzone = DEADZONE_TRIGGERS; - else + } + else { deadzone = DEADZONE_THUMBSTICKS; + } // Check if the input value switched boundaries. - if ((abs(axisValue) > deadzone) != (abs(mPrevAxisValues[ - std::make_pair(event.caxis.which, event.caxis.axis)]) > deadzone)) { + if ((abs(axisValue) > deadzone) != + (abs(mPrevAxisValues[std::make_pair(event.caxis.which, event.caxis.axis)]) > + deadzone)) { int normValue; if (abs(axisValue) <= deadzone) { normValue = 0; @@ -382,8 +389,9 @@ bool InputManager::parseEvent(const SDL_Event& event, Window* window) normValue = -1; } - window->input(getInputConfigByDevice(event.caxis.which), Input(event.caxis.which, - TYPE_AXIS, event.caxis.axis, normValue, false)); + window->input( + getInputConfigByDevice(event.caxis.which), + Input(event.caxis.which, TYPE_AXIS, event.caxis.axis, normValue, false)); causedEvent = true; } @@ -393,26 +401,27 @@ bool InputManager::parseEvent(const SDL_Event& event, Window* window) case SDL_CONTROLLERBUTTONDOWN: { } case SDL_CONTROLLERBUTTONUP: { - // Whether to only accept input from the first controller. - if (Settings::getInstance()->getBool("InputOnlyFirstController")) - if (mInputConfigs.begin()->first != event.cdevice.which) - return false; + // Whether to only accept input from the first controller. + if (Settings::getInstance()->getBool("InputOnlyFirstController")) + if (mInputConfigs.begin()->first != event.cdevice.which) + return false; // The event filtering below is required as some controllers send button presses // starting with the state 0 when using the D-pad. I consider this invalid behaviour // and the more popular controllers such as those from Microsoft and Sony do not show // this strange behavior. - int buttonState = mPrevButtonValues[ - std::make_pair(event.cbutton.which, event.cbutton.button)]; + int buttonState = + mPrevButtonValues[std::make_pair(event.cbutton.which, event.cbutton.button)]; if ((buttonState == -1 || buttonState == 0) && event.cbutton.state == 0) return false; mPrevButtonValues[std::make_pair(event.cbutton.which, event.cbutton.button)] = - event.cbutton.state; + event.cbutton.state; - window->input(getInputConfigByDevice(event.cbutton.which), Input(event.cbutton.which, - TYPE_BUTTON, event.cbutton.button, event.cbutton.state == SDL_PRESSED, false)); + window->input(getInputConfigByDevice(event.cbutton.which), + Input(event.cbutton.which, TYPE_BUTTON, event.cbutton.button, + event.cbutton.state == SDL_PRESSED, false)); return true; } @@ -430,13 +439,13 @@ bool InputManager::parseEvent(const SDL_Event& event, Window* window) return false; } - window->input(getInputConfigByDevice(DEVICE_KEYBOARD), Input(DEVICE_KEYBOARD, - TYPE_KEY, event.key.keysym.sym, 1, false)); + window->input(getInputConfigByDevice(DEVICE_KEYBOARD), + Input(DEVICE_KEYBOARD, TYPE_KEY, event.key.keysym.sym, 1, false)); return true; } case SDL_KEYUP: { - window->input(getInputConfigByDevice(DEVICE_KEYBOARD), Input(DEVICE_KEYBOARD, - TYPE_KEY, event.key.keysym.sym, 0, false)); + window->input(getInputConfigByDevice(DEVICE_KEYBOARD), + Input(DEVICE_KEYBOARD, TYPE_KEY, event.key.keysym.sym, 0, false)); return true; } case SDL_TEXTINPUT: { @@ -454,21 +463,17 @@ bool InputManager::parseEvent(const SDL_Event& event, Window* window) } if ((event.type == static_cast(SDL_USER_CECBUTTONDOWN)) || - (event.type == static_cast(SDL_USER_CECBUTTONUP))) { - window->input(getInputConfigByDevice(DEVICE_CEC), Input(DEVICE_CEC, - TYPE_CEC_BUTTON, event.user.code, event.type == - static_cast(SDL_USER_CECBUTTONDOWN), false)); + (event.type == static_cast(SDL_USER_CECBUTTONUP))) { + window->input(getInputConfigByDevice(DEVICE_CEC), + Input(DEVICE_CEC, TYPE_CEC_BUTTON, event.user.code, + event.type == static_cast(SDL_USER_CECBUTTONDOWN), + false)); return true; } return false; } -bool InputManager::initialized() const -{ - return mKeyboardInputConfig != nullptr; -} - bool InputManager::loadInputConfig(InputConfig* config) { if (!mConfigFileExists) @@ -477,11 +482,11 @@ bool InputManager::loadInputConfig(InputConfig* config) std::string path = getConfigPath(); pugi::xml_document doc; - #if defined(_WIN64) +#if defined(_WIN64) pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str()); - #else +#else pugi::xml_parse_result res = doc.load_file(path.c_str()); - #endif +#endif if (!res) { LOG(LogError) << "Couldn't parse the input configuration file: " << res.description(); @@ -492,16 +497,16 @@ bool InputManager::loadInputConfig(InputConfig* config) if (!root) return false; - pugi::xml_node configNode = root.find_child_by_attribute("inputConfig", - "deviceGUID", config->getDeviceGUIDString().c_str()); + pugi::xml_node configNode = root.find_child_by_attribute("inputConfig", "deviceGUID", + config->getDeviceGUIDString().c_str()); // Enabling this will match an entry in es_input.xml based on the device name if there // was no GUID match. This is probably not a good idea as many controllers share the same // name even though the GUID differ and potentially the button configuration could be // different between them. Keeping the code for now though. -// if (!configNode) -// configNode = root.find_child_by_attribute("inputConfig", -// "deviceName", config->getDeviceName().c_str()); + // if (!configNode) + // configNode = root.find_child_by_attribute("inputConfig", + // "deviceName", config->getDeviceName().c_str()); // With the move to the SDL GameController API the button layout changed quite a lot, so // es_input.xml files generated using the old API will end up with a completely unusable @@ -535,11 +540,11 @@ void InputManager::loadDefaultKBConfig() cfg->mapInput("A", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_RETURN, 1, true)); cfg->mapInput("B", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_BACKSPACE, 1, true)); cfg->mapInput("X", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_DELETE, 1, true)); - #if defined(__APPLE__) +#if defined(__APPLE__) cfg->mapInput("Y", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_PRINTSCREEN, 1, true)); - #else +#else cfg->mapInput("Y", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_INSERT, 1, true)); - #endif +#endif cfg->mapInput("Start", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_ESCAPE, 1, true)); cfg->mapInput("Back", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F1, 1, true)); @@ -559,6 +564,7 @@ void InputManager::loadDefaultControllerConfig(SDL_JoystickID deviceIndex) if (cfg->isConfigured()) return; + // clang-format off cfg->mapInput("Up", Input(deviceIndex, TYPE_BUTTON, SDL_CONTROLLER_BUTTON_DPAD_UP, 1, true)); cfg->mapInput("Down", Input(deviceIndex, TYPE_BUTTON, SDL_CONTROLLER_BUTTON_DPAD_DOWN, 1, true)); cfg->mapInput("Left", Input(deviceIndex, TYPE_BUTTON, SDL_CONTROLLER_BUTTON_DPAD_LEFT, 1, true)); @@ -583,6 +589,7 @@ void InputManager::loadDefaultControllerConfig(SDL_JoystickID deviceIndex) cfg->mapInput("RightThumbstickLeft", Input(deviceIndex, TYPE_AXIS, SDL_CONTROLLER_AXIS_RIGHTX, -1, true)); cfg->mapInput("RightThumbstickRight", Input(deviceIndex, TYPE_AXIS, SDL_CONTROLLER_AXIS_RIGHTX, 1, true)); cfg->mapInput("RightThumbstickClick", Input(deviceIndex, TYPE_BUTTON, SDL_CONTROLLER_BUTTON_RIGHTSTICK, 1, true)); + // clang-format on } void InputManager::addControllerByDeviceIndex(int deviceIndex) @@ -599,21 +606,21 @@ void InputManager::addControllerByDeviceIndex(int deviceIndex) char guid[65]; SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, 65); - mInputConfigs[joyID] = std::make_unique( - joyID, SDL_GameControllerName(mControllers[joyID]), guid); + mInputConfigs[joyID] = + std::make_unique(joyID, SDL_GameControllerName(mControllers[joyID]), guid); bool customConfig = loadInputConfig(mInputConfigs[joyID].get()); if (customConfig) { - LOG(LogInfo) << "Added controller with custom configuration: \"" << - SDL_GameControllerName(mControllers[joyID]) << "\" (GUID: " << guid << - ", instance ID: " << joyID << ", device index: " << deviceIndex << ")"; + LOG(LogInfo) << "Added controller with custom configuration: \"" + << SDL_GameControllerName(mControllers[joyID]) << "\" (GUID: " << guid + << ", instance ID: " << joyID << ", device index: " << deviceIndex << ")"; } else { loadDefaultControllerConfig(joyID); - LOG(LogInfo) << "Added controller with default configuration: \"" << - SDL_GameControllerName(mControllers[joyID]) << "\" (GUID: " << guid << - ", instance ID: " << joyID << ", device index: " << deviceIndex << ")"; + LOG(LogInfo) << "Added controller with default configuration: \"" + << SDL_GameControllerName(mControllers[joyID]) << "\" (GUID: " << guid + << ", instance ID: " << joyID << ", device index: " << deviceIndex << ")"; } int numAxes = SDL_JoystickNumAxes(joy); @@ -634,8 +641,8 @@ void InputManager::removeControllerByJoystickID(SDL_JoystickID joyID) SDL_Joystick* joy = SDL_JoystickFromInstanceID(joyID); SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, 65); - LOG(LogInfo) << "Removed controller \"" << SDL_GameControllerName(mControllers[joyID]) << - "\" (GUID: " << guid << ", instance ID: " << joyID << ")"; + LOG(LogInfo) << "Removed controller \"" << SDL_GameControllerName(mControllers[joyID]) + << "\" (GUID: " << guid << ", instance ID: " << joyID << ")"; // Delete mPrevAxisValues for the device. int axisEntries = static_cast(mPrevAxisValues.size()); diff --git a/es-core/src/InputManager.h b/es-core/src/InputManager.h index b675db2e0..5bdc89ea2 100644 --- a/es-core/src/InputManager.h +++ b/es-core/src/InputManager.h @@ -55,7 +55,7 @@ public: int getNumJoysticks() { return static_cast(mJoysticks.size()); } private: - bool initialized() const; + bool initialized() const { return mKeyboardInputConfig != nullptr; } bool loadInputConfig(InputConfig* config); void loadDefaultKBConfig(); diff --git a/es-core/src/Log.cpp b/es-core/src/Log.cpp index 680070685..2530e5067 100644 --- a/es-core/src/Log.cpp +++ b/es-core/src/Log.cpp @@ -8,32 +8,16 @@ #include "Log.h" -#include "utils/FileSystemUtil.h" -#include "utils/StringUtil.h" #include "Platform.h" +#include "utils/StringUtil.h" #include -#include #include +#include LogLevel Log::reportingLevel = LogInfo; std::ofstream file; -LogLevel Log::getReportingLevel() -{ - return reportingLevel; -} - -std::string Log::getLogPath() -{ - return Utils::FileSystem::getHomePath() + "/.emulationstation/es_log.txt"; -} - -void Log::setReportingLevel(LogLevel level) -{ - reportingLevel = level; -} - void Log::init() { Utils::FileSystem::removeFile(getLogPath() + ".bak"); @@ -44,24 +28,24 @@ void Log::init() void Log::open() { - #if defined(_WIN64) +#if defined(_WIN64) file.open(Utils::String::stringToWideString(getLogPath()).c_str()); - #else +#else file.open(getLogPath().c_str()); - #endif +#endif } std::ostringstream& Log::get(LogLevel level) { time_t t = time(nullptr); struct tm tm; - #if defined(_WIN64) +#if defined(_WIN64) // Of course Windows does not follow standards and puts the parameters the other way // around compared to POSIX. localtime_s(&tm, &t); - #else +#else localtime_r(&t, &tm); - #endif +#endif os << std::put_time(&tm, "%b %d %T ") << logLevelMap[level] << ":\t"; messageLevel = level; @@ -70,6 +54,7 @@ std::ostringstream& Log::get(LogLevel level) void Log::flush() { + // This runs on application exit. file.flush(); } @@ -85,8 +70,8 @@ Log::~Log() if (!file.is_open()) { // Not open yet, print to stdout. - std::cerr << "ERROR - tried to write to log file before it was open! " - "The following won't be logged:\n"; + std::cerr << "Error: Tried to write to log file before it was open, " + "the following won't be logged:\n"; std::cerr << os.str(); return; } diff --git a/es-core/src/Log.h b/es-core/src/Log.h index d12f58934..da45efd9f 100644 --- a/es-core/src/Log.h +++ b/es-core/src/Log.h @@ -9,15 +9,19 @@ #ifndef ES_CORE_LOG_H #define ES_CORE_LOG_H +#include "utils/FileSystemUtil.h" + #include #include -#define LOG(level) \ -if (level > Log::getReportingLevel()); \ -else Log().get(level) +#define LOG(level) \ + if (level > Log::getReportingLevel()) \ + ; \ + else \ + Log().get(level) enum LogLevel { - LogError, + LogError, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). LogWarning, LogInfo, LogDebug @@ -29,10 +33,12 @@ public: ~Log(); std::ostringstream& get(LogLevel level = LogInfo); - static LogLevel getReportingLevel(); - static void setReportingLevel(LogLevel level); - - static std::string getLogPath(); + static LogLevel getReportingLevel() { return reportingLevel; } + static void setReportingLevel(LogLevel level) { reportingLevel = level; } + static std::string getLogPath() + { + return Utils::FileSystem::getHomePath() + "/.emulationstation/es_log.txt"; + } static void flush(); static void init(); @@ -43,11 +49,11 @@ protected: std::ostringstream os; private: - std::map logLevelMap { - { LogError, "Error" }, - { LogWarning, "Warn" }, - { LogInfo, "Info" }, - { LogDebug, "Debug" } + std::map logLevelMap { // Log level indicators. + { LogError, "Error" }, + { LogWarning, "Warn" }, + { LogInfo, "Info" }, + { LogDebug, "Debug" } }; static LogLevel reportingLevel; diff --git a/es-core/src/MameNames.cpp b/es-core/src/MameNames.cpp index 2d4a8c409..30b1f4748 100644 --- a/es-core/src/MameNames.cpp +++ b/es-core/src/MameNames.cpp @@ -11,10 +11,10 @@ #include "MameNames.h" +#include "Log.h" #include "resources/ResourceManager.h" #include "utils/FileSystemUtil.h" #include "utils/StringUtil.h" -#include "Log.h" #include #include @@ -53,25 +53,23 @@ MameNames::MameNames() LOG(LogInfo) << "Parsing MAME names file \"" << xmlpath << "\"..."; pugi::xml_document doc; - #if defined(_WIN64) +#if defined(_WIN64) pugi::xml_parse_result result = - doc.load_file(Utils::String::stringToWideString(xmlpath).c_str()); - #else + doc.load_file(Utils::String::stringToWideString(xmlpath).c_str()); +#else pugi::xml_parse_result result = doc.load_file(xmlpath.c_str()); - #endif +#endif if (!result) { - LOG(LogError) << "Error parsing MAME names file \"" << xmlpath << "\": " - << result.description(); + LOG(LogError) << "Error parsing MAME names file \"" << xmlpath + << "\": " << result.description(); return; } - for (pugi::xml_node gameNode = doc.child("game"); - gameNode; gameNode = gameNode.next_sibling("game")) { - NamePair namePair = { - gameNode.child("mamename").text().get(), - gameNode.child("realname").text().get() - }; + for (pugi::xml_node gameNode = doc.child("game"); gameNode; + gameNode = gameNode.next_sibling("game")) { + NamePair namePair = { gameNode.child("mamename").text().get(), + gameNode.child("realname").text().get() }; mNamePairs.push_back(namePair); } @@ -83,20 +81,20 @@ MameNames::MameNames() LOG(LogInfo) << "Parsing MAME BIOSes file \"" << xmlpath << "\"..."; - #if defined(_WIN64) +#if defined(_WIN64) result = doc.load_file(Utils::String::stringToWideString(xmlpath).c_str()); - #else +#else result = doc.load_file(xmlpath.c_str()); - #endif +#endif if (!result) { - LOG(LogError) << "Error parsing MAME BIOSes file \"" << xmlpath << "\": " - << result.description(); + LOG(LogError) << "Error parsing MAME BIOSes file \"" << xmlpath + << "\": " << result.description(); return; } - for (pugi::xml_node biosNode = doc.child("bios"); - biosNode; biosNode = biosNode.next_sibling("bios")) { + for (pugi::xml_node biosNode = doc.child("bios"); biosNode; + biosNode = biosNode.next_sibling("bios")) { std::string bios = biosNode.text().get(); mMameBioses.push_back(bios); } @@ -109,29 +107,25 @@ MameNames::MameNames() LOG(LogInfo) << "Parsing MAME devices file \"" << xmlpath << "\"..."; - #if defined(_WIN64) +#if defined(_WIN64) result = doc.load_file(Utils::String::stringToWideString(xmlpath).c_str()); - #else +#else result = doc.load_file(xmlpath.c_str()); - #endif +#endif if (!result) { - LOG(LogError) << "Error parsing MAME devices file \"" << xmlpath << "\": " - << result.description(); + LOG(LogError) << "Error parsing MAME devices file \"" << xmlpath + << "\": " << result.description(); return; } - for (pugi::xml_node deviceNode = doc.child("device"); - deviceNode; deviceNode = deviceNode.next_sibling("device")) { + for (pugi::xml_node deviceNode = doc.child("device"); deviceNode; + deviceNode = deviceNode.next_sibling("device")) { std::string device = deviceNode.text().get(); mMameDevices.push_back(device); } } -MameNames::~MameNames() -{ -} - std::string MameNames::getRealName(const std::string& _mameName) { size_t start = 0; @@ -158,17 +152,6 @@ std::string MameNames::getCleanName(const std::string& _mameName) return cleanName; } -const bool MameNames::isBios(const std::string& _biosName) -{ - return MameNames::find(mMameBioses, _biosName); -} - -const bool MameNames::isDevice(const std::string& _deviceName) -{ - return MameNames::find(mMameDevices, _deviceName); - -} - const bool MameNames::find(std::vector devices, const std::string& name) { size_t start = 0; diff --git a/es-core/src/MameNames.h b/es-core/src/MameNames.h index 9cbeee670..6ef25cf09 100644 --- a/es-core/src/MameNames.h +++ b/es-core/src/MameNames.h @@ -24,8 +24,14 @@ public: static MameNames* getInstance(); std::string getRealName(const std::string& _mameName); std::string getCleanName(const std::string& _mameName); - const bool isBios(const std::string& _biosName); - const bool isDevice(const std::string& _deviceName); + const bool isBios(const std::string& _biosName) + { + return MameNames::find(mMameBioses, _biosName); + } + const bool isDevice(const std::string& _deviceName) + { + return MameNames::find(mMameDevices, _deviceName); + } private: struct NamePair { @@ -35,8 +41,8 @@ private: typedef std::vector namePairVector; - MameNames(); - ~MameNames(); + MameNames(); + ~MameNames() {} static MameNames* sInstance; diff --git a/es-core/src/Platform.cpp b/es-core/src/Platform.cpp index 4e509cf1e..c90b4940f 100644 --- a/es-core/src/Platform.cpp +++ b/es-core/src/Platform.cpp @@ -8,12 +8,12 @@ #include "Platform.h" -#include "renderers/Renderer.h" -#include "utils/StringUtil.h" #include "AudioManager.h" #include "Log.h" #include "MameNames.h" #include "Settings.h" +#include "renderers/Renderer.h" +#include "utils/StringUtil.h" #include @@ -52,36 +52,36 @@ int runPoweroffCommand() int runSystemCommand(const std::string& cmd_utf8) { - #if defined(_WIN64) +#if defined(_WIN64) // On Windows we use _wsystem to support non-ASCII paths // which requires converting from UTF-8 to a wstring. std::wstring wchar_str = Utils::String::stringToWideString(cmd_utf8); return _wsystem(wchar_str.c_str()); - #else +#else return system(cmd_utf8.c_str()); - #endif +#endif } int runSystemCommand(const std::wstring& cmd_utf16) { - #if defined(_WIN64) +#if defined(_WIN64) return _wsystem(cmd_utf16.c_str()); - #else +#else return 0; - #endif +#endif } int launchGameUnix(const std::string& cmd_utf8, bool runInBackground) { - #if defined(__unix__) || defined (__APPLE__) +#if defined(__unix__) || defined(__APPLE__) std::string command = std::string(cmd_utf8) + " 2>&1 &"; // Launching games while keeping ES-DE running in the background is very crude as for // instance no output from the command is captured and no real error handling is // implemented. It should therefore only be used when absolutely necessary. if (runInBackground) { - LOG(LogDebug) << "Platform::launchGameUnix(): Launching game while keeping ES-DE " - "running in the background, no command output will be written to the log file"; + LOG(LogDebug) << "Platform::launchGameUnix(): Launching game while keeping ES-DE running " + "in the background, no command output will be written to the log file"; return system(command.c_str()); } @@ -106,12 +106,11 @@ int launchGameUnix(const std::string& cmd_utf8, bool runInBackground) // Remove any trailing newline from the command output. if (commandOutput.size()) { if (commandOutput.back() == '\n') - commandOutput.pop_back(); + commandOutput.pop_back(); } if (returnValue) { - LOG(LogError) << "launchGameUnix - return value " << - std::to_string(returnValue) + ":"; + LOG(LogError) << "launchGameUnix - return value " << std::to_string(returnValue) + ":"; if (commandOutput.size()) LOG(LogError) << commandOutput; else @@ -124,14 +123,14 @@ int launchGameUnix(const std::string& cmd_utf8, bool runInBackground) return returnValue; - #else // __unix__ +#else // __unix__ return 0; - #endif +#endif } int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground) { - #if defined(_WIN64) +#if defined(_WIN64) STARTUPINFOW si {}; PROCESS_INFORMATION pi; @@ -139,17 +138,19 @@ int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground) bool processReturnValue = true; DWORD errorCode = 0; + // clang-format off processReturnValue = CreateProcessW( - nullptr, // No application name (use command line). - const_cast(cmd_utf16.c_str()), // Command line. - nullptr, // Process attributes. - nullptr, // Thread attributes. - FALSE, // Handles inheritance. - 0, // Creation flags. - nullptr, // Use parent's environment block. - nullptr, // Use parent's starting directory. - &si, // Pointer to the STARTUPINFOW structure. - &pi); // Pointer to the PROCESS_INFORMATION structure. + nullptr, // No application name (use command line). + const_cast(cmd_utf16.c_str()), // Command line. + nullptr, // Process attributes. + nullptr, // Thread attributes. + FALSE, // Handles inheritance. + 0, // Creation flags. + nullptr, // Use parent's environment block. + nullptr, // Use parent's starting directory. + &si, // Pointer to the STARTUPINFOW structure. + &pi); // Pointer to the PROCESS_INFORMATION structure. + // clang-format on if (!runInBackground) { if (Settings::getInstance()->getBool("LaunchWorkaround")) { @@ -169,9 +170,9 @@ int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground) if (!processReturnValue) { LPWSTR pBuffer = nullptr; - FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, - nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&pBuffer), 0, nullptr); + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, + GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&pBuffer), 0, nullptr); errorCode = GetLastError(); @@ -186,8 +187,8 @@ int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground) } } - LOG(LogError) << "launchGameWindows - system error code " << - errorCode << ": " << errorMessage; + LOG(LogError) << "launchGameWindows - system error code " << errorCode << ": " + << errorMessage; } // Close process and thread handles. @@ -196,40 +197,40 @@ int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground) return errorCode; - #else // _WIN64 +#else // _WIN64 return 0; - #endif +#endif } unsigned int getTaskbarState() { - #if defined(_WIN64) +#if defined(_WIN64) APPBARDATA barData; barData.cbSize = sizeof(APPBARDATA); return static_cast(SHAppBarMessage(ABM_GETSTATE, &barData)); - #else +#else return 0; - #endif +#endif } void hideTaskbar() { - #if defined(_WIN64) +#if defined(_WIN64) APPBARDATA barData; barData.cbSize = sizeof(APPBARDATA); barData.lParam = ABS_AUTOHIDE; SHAppBarMessage(ABM_SETSTATE, &barData); - #endif +#endif } void revertTaskbarState(unsigned int& state) { - #if defined(_WIN64) +#if defined(_WIN64) APPBARDATA barData; barData.cbSize = sizeof(APPBARDATA); barData.lParam = state; SHAppBarMessage(ABM_SETSTATE, &barData); - #endif +#endif } QuitMode quitMode = QuitMode::QUIT; @@ -264,7 +265,7 @@ void touch(const std::string& filename) if (fp != nullptr) fclose(fp); #else - int fd = open(filename.c_str(), O_CREAT|O_WRONLY, 0644); + int fd = open(filename.c_str(), O_CREAT | O_WRONLY, 0644); if (fd >= 0) close(fd); #endif @@ -273,15 +274,18 @@ void touch(const std::string& filename) void processQuitMode() { switch (quitMode) { - case QuitMode::REBOOT: - LOG(LogInfo) << "Rebooting system"; - runRebootCommand(); - break; - case QuitMode::POWEROFF: - LOG(LogInfo) << "Powering off system"; - runPoweroffCommand(); - break; - default: - break; + case QuitMode::REBOOT: { + LOG(LogInfo) << "Rebooting system"; + runRebootCommand(); + break; + } + case QuitMode::POWEROFF: { + LOG(LogInfo) << "Powering off system"; + runPoweroffCommand(); + break; + } + default: { + break; + } } } diff --git a/es-core/src/Platform.h b/es-core/src/Platform.h index 580016e24..aca84afc7 100644 --- a/es-core/src/Platform.h +++ b/es-core/src/Platform.h @@ -12,11 +12,12 @@ #include #if defined(_WIN64) +#include #include #endif enum QuitMode { - QUIT = 0, + QUIT = 0, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). REBOOT = 1, POWEROFF = 2 }; @@ -35,6 +36,7 @@ void revertTaskbarState(unsigned int& state); // Clean, normal shutdown. int quitES(QuitMode mode = QuitMode::QUIT); + // Immediately shut down the application as cleanly as possible. void emergencyShutdown(); void processQuitMode(); diff --git a/es-core/src/Scripting.cpp b/es-core/src/Scripting.cpp index bdb3c8165..5e3cd926f 100644 --- a/es-core/src/Scripting.cpp +++ b/es-core/src/Scripting.cpp @@ -14,20 +14,20 @@ #include "Scripting.h" -#include "utils/FileSystemUtil.h" #include "Log.h" #include "Platform.h" #include "Settings.h" +#include "utils/FileSystemUtil.h" namespace Scripting { - void fireEvent(const std::string& eventName, const std::string& arg1, const std::string& arg2) - { + void fireEvent(const std::string& eventName, const std::string& arg1, const std::string& arg2) + { if (!Settings::getInstance()->getBool("CustomEventScripts")) return; - LOG(LogDebug) << "Scripting::fireEvent(): " << eventName << " \"" << arg1 << - "\" \"" << arg2 << "\""; + LOG(LogDebug) << "Scripting::fireEvent(): " << eventName << " \"" << arg1 << "\" \"" << arg2 + << "\""; std::list scriptDirList; std::string scriptDir; @@ -38,10 +38,10 @@ namespace Scripting scriptDirList.push_back(scriptDir); for (std::list::const_iterator dirIt = scriptDirList.cbegin(); - dirIt != scriptDirList.cend(); dirIt++) { + dirIt != scriptDirList.cend(); dirIt++) { std::list scripts = Utils::FileSystem::getDirContent(*dirIt); - for (std::list::const_iterator it = scripts.cbegin(); - it != scripts.cend(); it++) { + for (std::list::const_iterator it = scripts.cbegin(); // Line break. + it != scripts.cend(); it++) { std::string arg1Quotation; std::string arg2Quotation; // Add quotation marks around the arguments as long as these are not already @@ -51,10 +51,10 @@ namespace Scripting if (arg2.front() != '\"') arg2Quotation = "\""; std::string script = *it + " " + arg1Quotation + arg1 + arg1Quotation + " " + - arg2Quotation + arg2 + arg2Quotation; + arg2Quotation + arg2 + arg2Quotation; LOG(LogDebug) << "Executing: " << script; runSystemCommand(script); } } - } -} + } +} // namespace Scripting diff --git a/es-core/src/Scripting.h b/es-core/src/Scripting.h index 294ddf227..9df4ab399 100644 --- a/es-core/src/Scripting.h +++ b/es-core/src/Scripting.h @@ -20,7 +20,8 @@ namespace Scripting { void fireEvent(const std::string& eventName, - const std::string& arg1="", const std::string& arg2=""); + const std::string& arg1 = "", + const std::string& arg2 = ""); } -#endif //ES_CORE_SCRIPTING_H +#endif // ES_CORE_SCRIPTING_H diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index 4625f1c05..70964667c 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -9,11 +9,11 @@ #include "Settings.h" -#include "utils/FileSystemUtil.h" -#include "utils/StringUtil.h" #include "Log.h" #include "Platform.h" #include "Scripting.h" +#include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" #include #include @@ -25,7 +25,9 @@ Settings* Settings::sInstance = nullptr; // the in-program settings menu. Most can be set using command-line arguments, // but some are debug flags that are either hardcoded or set by internal debug // functions. -std::vector settings_dont_save { +std::vector settingsSkipSaving +{ + // clang-format off // These options can be set using command-line arguments: "WindowWidth", // Set via --resolution [width] [height] "WindowHeight", // set via --resolution [width] [height] @@ -55,6 +57,7 @@ std::vector settings_dont_save { "DebugImage", "SplashScreenProgress", "ScraperFilter" + // clang-format on }; Settings::Settings() @@ -174,9 +177,8 @@ void Settings::setDefaults() mBoolMap["ScreensaverSlideshowScanlines"] = { true, true }; mBoolMap["ScreensaverSlideshowCustomImages"] = { false, false }; mBoolMap["ScreensaverSlideshowRecurse"] = { false, false }; - mStringMap["ScreensaverSlideshowImageDir"] = { - "~/.emulationstation/slideshow/custom_images", - "~/.emulationstation/slideshow/custom_images" }; + mStringMap["ScreensaverSlideshowImageDir"] = { "~/.emulationstation/slideshow/custom_images", + "~/.emulationstation/slideshow/custom_images" }; // UI settings -> screensaver settings -> video screensaver settings. mIntMap["ScreensaverSwapVideoTimeout"] = { 0, 0 }; @@ -185,6 +187,7 @@ void Settings::setDefaults() mBoolMap["ScreensaverVideoScanlines"] = { true, true }; mBoolMap["ScreensaverVideoBlur"] = { false, false }; +#if defined(_RPI_) // Sound settings. // The ALSA Audio Card and Audio Device selection code is disabled at the moment. // As PulseAudio controls the sound devices for the desktop environment, it doesn't @@ -193,7 +196,6 @@ void Settings::setDefaults() // settings could be added later on, if needed. // The code is still active for Raspberry Pi though as I'm not sure if this is // useful for that device. - #if defined(_RPI_) mStringMap["AudioCard"] = { "default", "default" }; // Audio out device for volume control. //#endif @@ -201,9 +203,8 @@ void Settings::setDefaults() mStringMap["AudioDevice"] = { "PCM", "PCM" }; // Audio out device for Video playback using OMX player. mStringMap["OMXAudioDev"] = { "both", "both" }; - //#else - // mStringMap["AudioDevice"] = { "Master", "Master" }; - #endif +#endif + mIntMap["SoundVolumeNavigation"] = { 80, 80 }; mIntMap["SoundVolumeVideos"] = { 100, 100 }; mBoolMap["GamelistVideoAudio"] = { true, true }; @@ -223,32 +224,32 @@ void Settings::setDefaults() mBoolMap["UseCustomCollectionsSystem"] = { true, true }; mBoolMap["CollectionShowSystemInfo"] = { true, true }; - // Other settings. - #if defined(_RPI_) +// Other settings. +#if defined(_RPI_) mIntMap["MaxVRAM"] = { 80, 80 }; - #else +#else mIntMap["MaxVRAM"] = { 256, 256 }; - #endif +#endif mIntMap["DisplayIndex"] = { 1, 1 }; - #if defined (__unix__) +#if defined(__unix__) mStringMap["FullscreenMode"] = { "normal", "normal" }; - #endif - #if defined(BUILD_VLC_PLAYER) +#endif +#if defined(BUILD_VLC_PLAYER) mStringMap["VideoPlayer"] = { "ffmpeg", "ffmpeg" }; - #endif - #if defined(_RPI_) +#endif +#if defined(_RPI_) mBoolMap["VideoOmxPlayer"] = { false, false }; // We're defaulting to OMX Player for full screen video on the Pi. mBoolMap["ScreensaverOmxPlayer"] = { true, true }; - #endif +#endif mStringMap["SaveGamelistsMode"] = { "always", "always" }; - #if defined(_WIN64) +#if defined(_WIN64) mBoolMap["HideTaskbar"] = { false, false }; - #endif +#endif mBoolMap["RunInBackground"] = { false, false }; - #if defined(_WIN64) +#if defined(_WIN64) mBoolMap["LaunchWorkaround"] = { true, true }; - #endif +#endif mStringMap["MediaDirectory"] = { "", "" }; mBoolMap["VideoUpscaleFrameRate"] = { false, false }; mBoolMap["LaunchCommandOverride"] = { true, true }; @@ -256,15 +257,15 @@ void Settings::setDefaults() mBoolMap["ShowHiddenGames"] = { true, true }; mBoolMap["CustomEventScripts"] = { false, false }; mBoolMap["ParseGamelistOnly"] = { false, false }; - #if defined(__unix__) +#if defined(__unix__) mBoolMap["DisableComposition"] = { true, true }; - #endif +#endif mBoolMap["DisplayGPUStatistics"] = { false, false }; - // macOS requires root privileges to reboot and power off so it doesn't make much - // sense to enable this setting and menu entry for that operating system. - #if !defined(__APPLE__) +// macOS requires root privileges to reboot and power off so it doesn't make much +// sense to enable this setting and menu entry for that operating system. +#if !defined(__APPLE__) mBoolMap["ShowQuitMenu"] = { false, false }; - #endif +#endif // // Settings configured via command-line arguments. @@ -278,18 +279,18 @@ void Settings::setDefaults() mBoolMap["IgnoreGamelist"] = { false, false }; mBoolMap["SplashScreen"] = { true, true }; mBoolMap["VSync"] = { true, true }; - #if !defined(_WIN64) +#if !defined(_WIN64) mBoolMap["Windowed"] = { false, false }; - #endif - mIntMap["WindowWidth"] = { 0, 0 }; - mIntMap["WindowHeight"] = { 0, 0 }; - mIntMap["ScreenWidth"] = { 0, 0 }; +#endif + mIntMap["WindowWidth"] = { 0, 0 }; + mIntMap["WindowHeight"] = { 0, 0 }; + mIntMap["ScreenWidth"] = { 0, 0 }; // Undocumented options. - mIntMap["ScreenHeight"] = { 0, 0 }; + mIntMap["ScreenHeight"] = { 0, 0 }; mIntMap["ScreenOffsetX"] = { 0, 0 }; mIntMap["ScreenOffsetY"] = { 0, 0 }; - mIntMap["ScreenRotate"] = { 0, 0 }; + mIntMap["ScreenRotate"] = { 0, 0 }; // // Settings that can be changed in es_settings.xml @@ -317,9 +318,10 @@ void saveMap(pugi::xml_document& doc, std::map& map, const std::string& ty { for (auto iter = map.cbegin(); iter != map.cend(); iter++) { // Key is on the "don't save" list, so don't save it. - if (std::find(settings_dont_save.cbegin(), settings_dont_save.cend(), - iter->first) != settings_dont_save.cend()) + if (std::find(settingsSkipSaving.cbegin(), settingsSkipSaving.cend(), iter->first) != + settingsSkipSaving.cend()) { continue; + } pugi::xml_node node = doc.append_child(type.c_str()); node.append_attribute("name").set_value(iter->first.c_str()); @@ -330,8 +332,8 @@ void saveMap(pugi::xml_document& doc, std::map& map, const std::string& ty void Settings::saveFile() { LOG(LogDebug) << "Settings::saveFile(): Saving settings to es_settings.xml"; - const std::string path = Utils::FileSystem::getHomePath() + - "/.emulationstation/es_settings.xml"; + const std::string path = + Utils::FileSystem::getHomePath() + "/.emulationstation/es_settings.xml"; pugi::xml_document doc; @@ -345,11 +347,11 @@ void Settings::saveFile() node.append_attribute("value").set_value(iter->second.second.c_str()); } - #if defined(_WIN64) +#if defined(_WIN64) doc.save_file(Utils::String::stringToWideString(path).c_str()); - #else +#else doc.save_file(path.c_str()); - #endif +#endif Scripting::fireEvent("config-changed"); Scripting::fireEvent("settings-changed"); @@ -358,11 +360,11 @@ void Settings::saveFile() void Settings::loadFile() { // Prior to ES-DE v1.1, the configuration file had the .cfg suffix instead of .xml - const std::string legacyConfigFile = Utils::FileSystem::getHomePath() + - "/.emulationstation/es_settings.cfg"; + const std::string legacyConfigFile = + Utils::FileSystem::getHomePath() + "/.emulationstation/es_settings.cfg"; - const std::string configFile = Utils::FileSystem::getHomePath() + - "/.emulationstation/es_settings.xml"; + const std::string configFile = + Utils::FileSystem::getHomePath() + "/.emulationstation/es_settings.xml"; if (Utils::FileSystem::exists(legacyConfigFile) && !Utils::FileSystem::exists(configFile)) Utils::FileSystem::copyFile(legacyConfigFile, configFile, false); @@ -371,12 +373,12 @@ void Settings::loadFile() return; pugi::xml_document doc; - #if defined(_WIN64) +#if defined(_WIN64) pugi::xml_parse_result result = - doc.load_file(Utils::String::stringToWideString(configFile).c_str()); - #else + doc.load_file(Utils::String::stringToWideString(configFile).c_str()); +#else pugi::xml_parse_result result = doc.load_file(configFile.c_str()); - #endif +#endif if (!result) { LOG(LogError) << "Could not parse the es_settings.xml file\n " << result.description(); return; @@ -392,37 +394,37 @@ void Settings::loadFile() setString(node.attribute("name").as_string(), node.attribute("value").as_string()); } -// Print a warning message if the setting we're trying to get doesn't already exist in -// the map. Then return the value in the map. -#define SETTINGS_GETSET(type, mapName, getFunction, getDefaultFunction, setFunction) \ -type Settings::getFunction(const std::string& name) \ -{ \ - if (mapName.find(name) == mapName.cend()) { \ - LOG(LogError) << "Tried to use unset setting " << name; \ - } \ - return mapName[name].second; \ -} \ -type Settings::getDefaultFunction(const std::string& name) \ -{ \ - if (mapName.find(name) == mapName.cend()) { \ - LOG(LogError) << "Tried to use unset setting " << name; \ - } \ - return mapName[name].first; \ -} \ -bool Settings::setFunction(const std::string& name, type value) \ -{ \ - if (mapName.count(name) == 0 || mapName[name].second != value) { \ - mapName[name].second = value; \ -\ - if (std::find(settings_dont_save.cbegin(), settings_dont_save.cend(), name) \ - == settings_dont_save.cend()) \ - mWasChanged = true; \ -\ - return true; \ - } \ - return false; \ -} +// Macro to create the get and set functions for the various data types +#define SETTINGS_GETSET(type, mapName, getFunction, getDefaultFunction, setFunction) \ + type Settings::getFunction(const std::string& name) \ + { \ + if (mapName.find(name) == mapName.cend()) { \ + LOG(LogError) << "Tried to use unset setting " << name; \ + } \ + return mapName[name].second; \ + } \ + type Settings::getDefaultFunction(const std::string& name) \ + { \ + if (mapName.find(name) == mapName.cend()) { \ + LOG(LogError) << "Tried to use unset setting " << name; \ + } \ + return mapName[name].first; \ + } \ + bool Settings::setFunction(const std::string& name, type value) \ + { \ + if (mapName.count(name) == 0 || mapName[name].second != value) { \ + mapName[name].second = value; \ + \ + if (std::find(settingsSkipSaving.cbegin(), settingsSkipSaving.cend(), name) == \ + settingsSkipSaving.cend()) \ + mWasChanged = true; \ + \ + return true; \ + } \ + return false; \ + } +// Parameters for the macro defined above. SETTINGS_GETSET(bool, mBoolMap, getBool, getDefaultBool, setBool); SETTINGS_GETSET(int, mIntMap, getInt, getDefaultInt, setInt); SETTINGS_GETSET(float, mFloatMap, getFloat, getDefaultFloat, setFloat); diff --git a/es-core/src/Sound.cpp b/es-core/src/Sound.cpp index ab1dc64e5..496d513e3 100644 --- a/es-core/src/Sound.cpp +++ b/es-core/src/Sound.cpp @@ -9,11 +9,11 @@ #include "Sound.h" -#include "resources/ResourceManager.h" #include "AudioManager.h" #include "Log.h" #include "Settings.h" #include "ThemeData.h" +#include "resources/ResourceManager.h" NavigationSounds* NavigationSounds::sInstance = nullptr; @@ -32,11 +32,12 @@ std::shared_ptr Sound::get(const std::string& path) } std::shared_ptr Sound::getFromTheme(const std::shared_ptr& theme, - const std::string& view, const std::string& element) + const std::string& view, + const std::string& element) { if (!theme) { - LOG(LogDebug) << "Sound::getFromTheme(): Using fallback sound file for \"" - << element << "\""; + LOG(LogDebug) << "Sound::getFromTheme(): Using fallback sound file for \"" << element + << "\""; return get(ResourceManager::getInstance()->getResourcePath(":/sounds/" + element + ".wav")); } @@ -44,7 +45,7 @@ std::shared_ptr Sound::getFromTheme(const std::shared_ptr& the const ThemeData::ThemeElement* elem = theme->getElement(view, element, "sound"); if (!elem || !elem->has("path")) { - LOG(LogDebug) << "Sound::getFromTheme(): " << "Tag not found, using fallback sound file"; + LOG(LogDebug) << "Sound::getFromTheme(): Tag not found, using fallback sound file"; return get(ResourceManager::getInstance()->getResourcePath(":/sounds/" + element + ".wav")); } @@ -52,20 +53,15 @@ std::shared_ptr Sound::getFromTheme(const std::shared_ptr& the return get(elem->get("path")); } -Sound::Sound( - const std::string& path) - : mSampleData(nullptr), - mSamplePos(0), - mSampleLength(0), - playing(false) +Sound::Sound(const std::string& path) + : mSampleData(nullptr) + , mSamplePos(0) + , mSampleLength(0) + , playing(false) { loadFile(path); } -Sound::~Sound() -{ -} - void Sound::loadFile(const std::string& path) { mPath = path; @@ -91,9 +87,9 @@ void Sound::init() } // Convert sound file to the format required by ES-DE. - SDL_AudioStream *conversionStream = SDL_NewAudioStream(wave.format, wave.channels, wave.freq, - AudioManager::sAudioFormat.format, AudioManager::sAudioFormat.channels, - AudioManager::sAudioFormat.freq); + SDL_AudioStream* conversionStream = + SDL_NewAudioStream(wave.format, wave.channels, wave.freq, AudioManager::sAudioFormat.format, + AudioManager::sAudioFormat.channels, AudioManager::sAudioFormat.freq); if (conversionStream == nullptr) { LOG(LogError) << "Failed to create sample conversion stream:"; @@ -169,11 +165,6 @@ void Sound::play() AudioManager::getInstance()->play(); } -bool Sound::isPlaying() const -{ - return playing; -} - void Sound::stop() { // Flag our sample as not playing and rewind its position. @@ -183,16 +174,6 @@ void Sound::stop() SDL_UnlockAudioDevice(AudioManager::sAudioDevice); } -const Uint8* Sound::getData() const -{ - return mSampleData; -} - -Uint32 Sound::getPosition() const -{ - return mSamplePos; -} - void Sound::setPosition(Uint32 newPosition) { mSamplePos = newPosition; @@ -203,11 +184,6 @@ void Sound::setPosition(Uint32 newPosition) } } -Uint32 Sound::getLength() const -{ - return mSampleLength; -} - NavigationSounds* NavigationSounds::getInstance() { if (sInstance == nullptr) @@ -234,11 +210,12 @@ void NavigationSounds::loadThemeNavigationSounds(const std::shared_ptr get(const std::string& path); static std::shared_ptr getFromTheme(const std::shared_ptr& theme, - const std::string& view, const std::string& elem); + const std::string& view, + const std::string& elem); private: Sound(const std::string& path = ""); diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index 5d19b8908..72dbe06b1 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -10,190 +10,190 @@ #include "ThemeData.h" +#include "Log.h" +#include "Platform.h" +#include "Settings.h" #include "components/ImageComponent.h" #include "components/TextComponent.h" #include "utils/FileSystemUtil.h" #include "utils/StringUtil.h" -#include "Log.h" -#include "Platform.h" -#include "Settings.h" #include #include -std::vector ThemeData::sSupportedViews { - { "all" }, { "system" }, { "basic" }, { "detailed" }, { "grid" }, { "video" } }; +std::vector ThemeData::sSupportedViews { { "all" }, { "system" }, { "basic" }, + { "detailed" }, { "grid" }, { "video" } }; std::vector ThemeData::sSupportedFeatures { - { "navigationsounds" }, { "video" }, { "carousel" }, { "z-index" }, { "visible" } }; - -std::map> ThemeData::sElementMap { - { "image", { - { "pos", NORMALIZED_PAIR }, - { "size", NORMALIZED_PAIR }, - { "maxSize", NORMALIZED_PAIR }, - { "origin", NORMALIZED_PAIR }, - { "rotation", FLOAT }, - { "rotationOrigin", NORMALIZED_PAIR }, - { "path", PATH }, - { "default", PATH }, - { "tile", BOOLEAN }, - { "color", COLOR }, - { "colorEnd", COLOR }, - { "gradientType", STRING }, - { "visible", BOOLEAN }, - { "zIndex", FLOAT } } }, - { "imagegrid", { - { "pos", NORMALIZED_PAIR }, - { "size", NORMALIZED_PAIR }, - { "margin", NORMALIZED_PAIR }, - { "padding", NORMALIZED_RECT }, - { "autoLayout", NORMALIZED_PAIR }, - { "autoLayoutSelectedZoom", FLOAT }, - { "gameImage", PATH }, - { "folderImage", PATH }, - { "imageSource", STRING }, - { "scrollDirection", STRING }, - { "centerSelection", BOOLEAN }, - { "scrollLoop", BOOLEAN }, - { "animate", BOOLEAN }, - { "zIndex", FLOAT } } }, - { "gridtile", { - { "size", NORMALIZED_PAIR }, - { "padding", NORMALIZED_PAIR }, - { "imageColor", COLOR }, - { "backgroundImage", PATH }, - { "backgroundCornerSize", NORMALIZED_PAIR }, - { "backgroundColor", COLOR }, - { "backgroundCenterColor", COLOR }, - { "backgroundEdgeColor", COLOR } } }, - { "text", { - { "pos", NORMALIZED_PAIR }, - { "size", NORMALIZED_PAIR }, - { "origin", NORMALIZED_PAIR }, - { "rotation", FLOAT }, - { "rotationOrigin", NORMALIZED_PAIR }, - { "text", STRING }, - { "backgroundColor", COLOR }, - { "fontPath", PATH }, - { "fontSize", FLOAT }, - { "color", COLOR }, - { "alignment", STRING }, - { "forceUppercase", BOOLEAN }, - { "lineSpacing", FLOAT }, - { "value", STRING }, - { "visible", BOOLEAN }, - { "zIndex", FLOAT } } }, - { "textlist", { - { "pos", NORMALIZED_PAIR }, - { "size", NORMALIZED_PAIR }, - { "origin", NORMALIZED_PAIR }, - { "selectorHeight", FLOAT }, - { "selectorOffsetY", FLOAT }, - { "selectorColor", COLOR }, - { "selectorColorEnd", COLOR }, - { "selectorGradientType", STRING }, - { "selectorImagePath", PATH }, - { "selectorImageTile", BOOLEAN }, - { "selectedColor", COLOR }, - { "primaryColor", COLOR }, - { "secondaryColor", COLOR }, - { "fontPath", PATH }, - { "fontSize", FLOAT }, - { "scrollSound", PATH }, // Need to keep this for backward compatibility with old themes. - { "alignment", STRING }, - { "horizontalMargin", FLOAT }, - { "forceUppercase", BOOLEAN }, - { "lineSpacing", FLOAT }, - { "zIndex", FLOAT } } }, - { "container", { - { "pos", NORMALIZED_PAIR }, - { "size", NORMALIZED_PAIR }, - { "origin", NORMALIZED_PAIR }, - { "visible", BOOLEAN }, - { "zIndex", FLOAT } } }, - { "ninepatch", { - { "pos", NORMALIZED_PAIR }, - { "size", NORMALIZED_PAIR }, - { "path", PATH }, - { "visible", BOOLEAN }, - { "zIndex", FLOAT } } }, - { "datetime", { - { "pos", NORMALIZED_PAIR }, - { "size", NORMALIZED_PAIR }, - { "origin", NORMALIZED_PAIR }, - { "rotation", FLOAT }, - { "rotationOrigin", NORMALIZED_PAIR }, - { "backgroundColor", COLOR }, - { "fontPath", PATH }, - { "fontSize", FLOAT }, - { "color", COLOR }, - { "alignment", STRING }, - { "forceUppercase", BOOLEAN }, - { "lineSpacing", FLOAT }, - { "value", STRING }, - { "format", STRING }, - { "displayRelative", BOOLEAN }, - { "visible", BOOLEAN }, - { "zIndex", FLOAT } } }, - { "rating", { - { "pos", NORMALIZED_PAIR }, - { "size", NORMALIZED_PAIR }, - { "origin", NORMALIZED_PAIR }, - { "rotation", FLOAT }, - { "rotationOrigin", NORMALIZED_PAIR }, - { "color", COLOR }, - { "filledPath", PATH }, - { "unfilledPath", PATH }, - { "visible", BOOLEAN }, - { "zIndex", FLOAT } } }, - { "sound", { - { "path", PATH } } }, - { "helpsystem", { - { "pos", NORMALIZED_PAIR }, - { "origin", NORMALIZED_PAIR }, - { "textColor", COLOR }, - { "iconColor", COLOR }, - { "fontPath", PATH }, - { "fontSize", FLOAT } } }, - { "navigationsounds", { - { "systembrowseSound", PATH }, - { "quicksysselectSound", PATH }, - { "selectSound", PATH }, - { "backSound", PATH }, - { "scrollSound", PATH }, - { "favoriteSound", PATH }, - { "launchSound", PATH } } }, - { "video", { - { "pos", NORMALIZED_PAIR }, - { "size", NORMALIZED_PAIR }, - { "maxSize", NORMALIZED_PAIR }, - { "origin", NORMALIZED_PAIR }, - { "rotation", FLOAT }, - { "rotationOrigin", NORMALIZED_PAIR }, - { "default", PATH }, - { "delay", FLOAT }, - { "visible", BOOLEAN }, - { "zIndex", FLOAT }, - { "showSnapshotNoVideo", BOOLEAN }, - { "showSnapshotDelay", BOOLEAN } } }, - { "carousel", { - { "type", STRING }, - { "size", NORMALIZED_PAIR }, - { "pos", NORMALIZED_PAIR }, - { "origin", NORMALIZED_PAIR }, - { "color", COLOR }, - { "colorEnd", COLOR }, - { "gradientType", STRING }, - { "logoScale", FLOAT }, - { "logoRotation", FLOAT }, - { "logoRotationOrigin", NORMALIZED_PAIR }, - { "logoSize", NORMALIZED_PAIR }, - { "logoAlignment", STRING }, - { "maxLogoCount", FLOAT }, - { "zIndex", FLOAT } } } + { "navigationsounds" }, { "video" }, { "carousel" }, { "z-index" }, { "visible" } }; +std::map> + ThemeData::sElementMap { + { "image", + { { "pos", NORMALIZED_PAIR }, + { "size", NORMALIZED_PAIR }, + { "maxSize", NORMALIZED_PAIR }, + { "origin", NORMALIZED_PAIR }, + { "rotation", FLOAT }, + { "rotationOrigin", NORMALIZED_PAIR }, + { "path", PATH }, + { "default", PATH }, + { "tile", BOOLEAN }, + { "color", COLOR }, + { "colorEnd", COLOR }, + { "gradientType", STRING }, + { "visible", BOOLEAN }, + { "zIndex", FLOAT } } }, + { "imagegrid", + { { "pos", NORMALIZED_PAIR }, + { "size", NORMALIZED_PAIR }, + { "margin", NORMALIZED_PAIR }, + { "padding", NORMALIZED_RECT }, + { "autoLayout", NORMALIZED_PAIR }, + { "autoLayoutSelectedZoom", FLOAT }, + { "gameImage", PATH }, + { "folderImage", PATH }, + { "imageSource", STRING }, + { "scrollDirection", STRING }, + { "centerSelection", BOOLEAN }, + { "scrollLoop", BOOLEAN }, + { "animate", BOOLEAN }, + { "zIndex", FLOAT } } }, + { "gridtile", + { { "size", NORMALIZED_PAIR }, + { "padding", NORMALIZED_PAIR }, + { "imageColor", COLOR }, + { "backgroundImage", PATH }, + { "backgroundCornerSize", NORMALIZED_PAIR }, + { "backgroundColor", COLOR }, + { "backgroundCenterColor", COLOR }, + { "backgroundEdgeColor", COLOR } } }, + { "text", + { { "pos", NORMALIZED_PAIR }, + { "size", NORMALIZED_PAIR }, + { "origin", NORMALIZED_PAIR }, + { "rotation", FLOAT }, + { "rotationOrigin", NORMALIZED_PAIR }, + { "text", STRING }, + { "backgroundColor", COLOR }, + { "fontPath", PATH }, + { "fontSize", FLOAT }, + { "color", COLOR }, + { "alignment", STRING }, + { "forceUppercase", BOOLEAN }, + { "lineSpacing", FLOAT }, + { "value", STRING }, + { "visible", BOOLEAN }, + { "zIndex", FLOAT } } }, + { "textlist", + { { "pos", NORMALIZED_PAIR }, + { "size", NORMALIZED_PAIR }, + { "origin", NORMALIZED_PAIR }, + { "selectorHeight", FLOAT }, + { "selectorOffsetY", FLOAT }, + { "selectorColor", COLOR }, + { "selectorColorEnd", COLOR }, + { "selectorGradientType", STRING }, + { "selectorImagePath", PATH }, + { "selectorImageTile", BOOLEAN }, + { "selectedColor", COLOR }, + { "primaryColor", COLOR }, + { "secondaryColor", COLOR }, + { "fontPath", PATH }, + { "fontSize", FLOAT }, + { "scrollSound", PATH }, // For backward compatibility with old themes. + { "alignment", STRING }, + { "horizontalMargin", FLOAT }, + { "forceUppercase", BOOLEAN }, + { "lineSpacing", FLOAT }, + { "zIndex", FLOAT } } }, + { "container", + { { "pos", NORMALIZED_PAIR }, + { "size", NORMALIZED_PAIR }, + { "origin", NORMALIZED_PAIR }, + { "visible", BOOLEAN }, + { "zIndex", FLOAT } } }, + { "ninepatch", + { { "pos", NORMALIZED_PAIR }, + { "size", NORMALIZED_PAIR }, + { "path", PATH }, + { "visible", BOOLEAN }, + { "zIndex", FLOAT } } }, + { "datetime", + { { "pos", NORMALIZED_PAIR }, + { "size", NORMALIZED_PAIR }, + { "origin", NORMALIZED_PAIR }, + { "rotation", FLOAT }, + { "rotationOrigin", NORMALIZED_PAIR }, + { "backgroundColor", COLOR }, + { "fontPath", PATH }, + { "fontSize", FLOAT }, + { "color", COLOR }, + { "alignment", STRING }, + { "forceUppercase", BOOLEAN }, + { "lineSpacing", FLOAT }, + { "value", STRING }, + { "format", STRING }, + { "displayRelative", BOOLEAN }, + { "visible", BOOLEAN }, + { "zIndex", FLOAT } } }, + { "rating", + { { "pos", NORMALIZED_PAIR }, + { "size", NORMALIZED_PAIR }, + { "origin", NORMALIZED_PAIR }, + { "rotation", FLOAT }, + { "rotationOrigin", NORMALIZED_PAIR }, + { "color", COLOR }, + { "filledPath", PATH }, + { "unfilledPath", PATH }, + { "visible", BOOLEAN }, + { "zIndex", FLOAT } } }, + { "sound", { { "path", PATH } } }, + { "helpsystem", + { { "pos", NORMALIZED_PAIR }, + { "origin", NORMALIZED_PAIR }, + { "textColor", COLOR }, + { "iconColor", COLOR }, + { "fontPath", PATH }, + { "fontSize", FLOAT } } }, + { "navigationsounds", + { { "systembrowseSound", PATH }, + { "quicksysselectSound", PATH }, + { "selectSound", PATH }, + { "backSound", PATH }, + { "scrollSound", PATH }, + { "favoriteSound", PATH }, + { "launchSound", PATH } } }, + { "video", + { { "pos", NORMALIZED_PAIR }, + { "size", NORMALIZED_PAIR }, + { "maxSize", NORMALIZED_PAIR }, + { "origin", NORMALIZED_PAIR }, + { "rotation", FLOAT }, + { "rotationOrigin", NORMALIZED_PAIR }, + { "default", PATH }, + { "delay", FLOAT }, + { "visible", BOOLEAN }, + { "zIndex", FLOAT }, + { "showSnapshotNoVideo", BOOLEAN }, + { "showSnapshotDelay", BOOLEAN } } }, + { "carousel", + { { "type", STRING }, + { "size", NORMALIZED_PAIR }, + { "pos", NORMALIZED_PAIR }, + { "origin", NORMALIZED_PAIR }, + { "color", COLOR }, + { "colorEnd", COLOR }, + { "gradientType", STRING }, + { "logoScale", FLOAT }, + { "logoRotation", FLOAT }, + { "logoRotationOrigin", NORMALIZED_PAIR }, + { "logoSize", NORMALIZED_PAIR }, + { "logoAlignment", STRING }, + { "maxLogoCount", FLOAT }, + { "zIndex", FLOAT } } } + }; + #define MINIMUM_THEME_FORMAT_VERSION 3 #define CURRENT_THEME_FORMAT_VERSION 6 @@ -241,6 +241,7 @@ std::string resolvePlaceholders(const std::string& in) ThemeData::ThemeData() { + // The version will be loaded from the theme file. mVersion = 0; } @@ -261,11 +262,11 @@ void ThemeData::loadFile(std::map sysDataMap, const st mVariables.insert(sysDataMap.cbegin(), sysDataMap.cend()); pugi::xml_document doc; - #if defined(_WIN64) +#if defined(_WIN64) pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str()); - #else +#else pugi::xml_parse_result res = doc.load_file(path.c_str()); - #endif +#endif if (!res) throw error << "XML parsing error: \n " << res.description(); @@ -276,13 +277,13 @@ void ThemeData::loadFile(std::map sysDataMap, const st // parse version mVersion = root.child("formatVersion").text().as_float(-404); if (mVersion == -404) - throw error << " tag missing!\n It's either out of date or you need " - "to add " << CURRENT_THEME_FORMAT_VERSION << - " inside your tag."; + throw error << " tag missing\n " + "It's either out of date or you need to add " + << CURRENT_THEME_FORMAT_VERSION << " inside your tag."; if (mVersion < MINIMUM_THEME_FORMAT_VERSION) - throw error << "Theme uses format version " << mVersion << - ". Minimum supported version is " << MINIMUM_THEME_FORMAT_VERSION << "."; + throw error << "Theme uses format version " << mVersion << ". Minimum supported version is " + << MINIMUM_THEME_FORMAT_VERSION << "."; parseVariables(root); parseIncludes(root); @@ -299,19 +300,18 @@ void ThemeData::parseIncludes(const pugi::xml_node& root) std::string relPath = resolvePlaceholders(node.text().as_string()); std::string path = Utils::FileSystem::resolveRelativePath(relPath, mPaths.back(), true); if (!ResourceManager::getInstance()->fileExists(path)) - throw error << " -> \"" << relPath << - "\" not found (resolved to \"" << path << "\")"; + throw error << " -> \"" << relPath << "\" not found (resolved to \"" << path << "\")"; error << " -> \"" << relPath << "\""; mPaths.push_back(path); pugi::xml_document includeDoc; - #if defined(_WIN64) +#if defined(_WIN64) pugi::xml_parse_result result = - includeDoc.load_file(Utils::String::stringToWideString(path).c_str()); - #else + includeDoc.load_file(Utils::String::stringToWideString(path).c_str()); +#else pugi::xml_parse_result result = includeDoc.load_file(path.c_str()); - #endif +#endif if (!result) throw error << ": Error parsing file: " << result.description(); @@ -339,9 +339,10 @@ void ThemeData::parseFeatures(const pugi::xml_node& root) const std::string supportedAttr = node.attribute("supported").as_string(); - if (std::find(sSupportedFeatures.cbegin(), sSupportedFeatures.cend(), - supportedAttr) != sSupportedFeatures.cend()) + if (std::find(sSupportedFeatures.cbegin(), sSupportedFeatures.cend(), supportedAttr) != + sSupportedFeatures.cend()) { parseViews(node); + } } } @@ -384,10 +385,11 @@ void ThemeData::parseViews(const pugi::xml_node& root) prevOff = nameAttr.find_first_not_of(delim, off); off = nameAttr.find_first_of(delim, prevOff); - if (std::find(sSupportedViews.cbegin(), sSupportedViews.cend(), - viewKey) != sSupportedViews.cend()) { - ThemeView& view = mViews.insert(std::pair(viewKey, ThemeView())).first->second; + if (std::find(sSupportedViews.cbegin(), sSupportedViews.cend(), viewKey) != + sSupportedViews.cend()) { + ThemeView& view = + mViews.insert(std::pair(viewKey, ThemeView())) + .first->second; parseView(node, view); } } @@ -416,19 +418,21 @@ void ThemeData::parseView(const pugi::xml_node& root, ThemeView& view) prevOff = nameAttr.find_first_not_of(delim, off); off = nameAttr.find_first_of(delim, prevOff); - parseElement(node, elemTypeIt->second, - view.elements.insert(std::pair(elemKey, ThemeElement())).first->second); + parseElement( + node, elemTypeIt->second, + view.elements.insert(std::pair(elemKey, ThemeElement())) + .first->second); - if (std::find(view.orderedKeys.cbegin(), view.orderedKeys.cend(), - elemKey) == view.orderedKeys.cend()) + if (std::find(view.orderedKeys.cbegin(), view.orderedKeys.cend(), elemKey) == + view.orderedKeys.cend()) view.orderedKeys.push_back(elemKey); } } } void ThemeData::parseElement(const pugi::xml_node& root, - const std::map& typeMap, ThemeElement& element) + const std::map& typeMap, + ThemeElement& element) { ThemeException error; error.setFiles(mPaths); @@ -439,85 +443,88 @@ void ThemeData::parseElement(const pugi::xml_node& root, for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { auto typeIt = typeMap.find(node.name()); if (typeIt == typeMap.cend()) - throw error << ": Unknown property type \"" << node.name() << - "\" (for element of type " << root.name() << ")"; + throw error << ": Unknown property type \"" << node.name() << "\" (for element of type " + << root.name() << ")"; std::string str = resolvePlaceholders(node.text().as_string()); switch (typeIt->second) { - case NORMALIZED_RECT: { - Vector4f val; + case NORMALIZED_RECT: { + Vector4f val; - auto splits = Utils::String::delimitedStringToVector(str, " "); - if (splits.size() == 2) { - val = Vector4f(static_cast(atof(splits.at(0).c_str())), - static_cast(atof(splits.at(1).c_str())), - static_cast(atof(splits.at(0).c_str())), - static_cast(atof(splits.at(1).c_str()))); + auto splits = Utils::String::delimitedStringToVector(str, " "); + if (splits.size() == 2) { + val = Vector4f(static_cast(atof(splits.at(0).c_str())), + static_cast(atof(splits.at(1).c_str())), + static_cast(atof(splits.at(0).c_str())), + static_cast(atof(splits.at(1).c_str()))); + } + else if (splits.size() == 4) { + val = Vector4f(static_cast(atof(splits.at(0).c_str())), + static_cast(atof(splits.at(1).c_str())), + static_cast(atof(splits.at(2).c_str())), + static_cast(atof(splits.at(3).c_str()))); + } + + element.properties[node.name()] = val; + break; } - else if (splits.size() == 4) { - val = Vector4f(static_cast(atof(splits.at(0).c_str())), - static_cast(atof(splits.at(1).c_str())), - static_cast(atof(splits.at(2).c_str())), - static_cast(atof(splits.at(3).c_str()))); + case NORMALIZED_PAIR: { + size_t divider = str.find(' '); + if (divider == std::string::npos) + throw error << "invalid normalized pair (property \"" << node.name() + << "\", value \"" << str.c_str() << "\")"; + + std::string first = str.substr(0, divider); + std::string second = str.substr(divider, std::string::npos); + + Vector2f val(static_cast(atof(first.c_str())), + static_cast(atof(second.c_str()))); + + element.properties[node.name()] = val; + break; } - - element.properties[node.name()] = val; - break; - } - case NORMALIZED_PAIR: { - size_t divider = str.find(' '); - if (divider == std::string::npos) - throw error << "invalid normalized pair (property \"" << node.name() << - "\", value \"" << str.c_str() << "\")"; - - std::string first = str.substr(0, divider); - std::string second = str.substr(divider, std::string::npos); - - Vector2f val(static_cast(atof(first.c_str())), - static_cast(atof(second.c_str()))); - - element.properties[node.name()] = val; - break; - } - case STRING: { - element.properties[node.name()] = str; - break; - } - case PATH: { - std::string path = Utils::FileSystem::resolveRelativePath(str, mPaths.back(), true); - if (!ResourceManager::getInstance()->fileExists(path)) { - std::stringstream ss; - // "From theme yadda yadda, included file yadda yadda. - LOG(LogWarning) << error.msg << ":"; - LOG(LogWarning) << "Could not find file \"" << node.text().get() << "\" " << - ((node.text().get() != path) ? "which resolves to \"" + path + "\"" : ""); + case STRING: { + element.properties[node.name()] = str; + break; } - element.properties[node.name()] = path; - break; - } - case COLOR: { - element.properties[node.name()] = getHexColor(str); - break; - } - case FLOAT: { - float floatVal = static_cast(strtod(str.c_str(), 0)); - element.properties[node.name()] = floatVal; - break; - } - case BOOLEAN: { - // Only look at first char. - char first = str[0]; - // 1*, t* (true), T* (True), y* (yes), Y* (YES) - bool boolVal = (first == '1' || first == 't' || first == 'T' || - first == 'y' || first == 'Y'); + case PATH: { + std::string path = Utils::FileSystem::resolveRelativePath(str, mPaths.back(), true); + if (!ResourceManager::getInstance()->fileExists(path)) { + std::stringstream ss; + // "From theme yadda yadda, included file yadda yadda. + LOG(LogWarning) << error.msg << ":"; + LOG(LogWarning) + << "Could not find file \"" << node.text().get() << "\" " + << ((node.text().get() != path) ? "which resolves to \"" + path + "\"" : + ""); + } + element.properties[node.name()] = path; + break; + } + case COLOR: { + element.properties[node.name()] = getHexColor(str); + break; + } + case FLOAT: { + float floatVal = static_cast(strtod(str.c_str(), 0)); + element.properties[node.name()] = floatVal; + break; + } + case BOOLEAN: { + // Only look at first char. + char first = str[0]; + // 1*, t* (true), T* (True), y* (yes), Y* (YES) + bool boolVal = + (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); - element.properties[node.name()] = boolVal; - break; - } - default: - throw error << "Unknown ElementPropertyType for \"" << - root.attribute("name").as_string() << "\", property " << node.name(); + element.properties[node.name()] = boolVal; + break; + } + default: { + throw error << "Unknown ElementPropertyType for \"" + << root.attribute("name").as_string() << "\", property " << node.name(); + } } } } @@ -529,7 +536,8 @@ bool ThemeData::hasView(const std::string& view) } const ThemeData::ThemeElement* ThemeData::getElement(const std::string& view, - const std::string& element, const std::string& expectedType) const + const std::string& element, + const std::string& expectedType) const { auto viewIt = mViews.find(view); if (viewIt == mViews.cend()) @@ -540,9 +548,9 @@ const ThemeData::ThemeElement* ThemeData::getElement(const std::string& view, return nullptr; if (elemIt->second.type != expectedType && !expectedType.empty()) { - LOG(LogWarning) << " requested mismatched theme type for [" << - view << "." << element << "] - expected \"" - << expectedType << "\", got \"" << elemIt->second.type << "\""; + LOG(LogWarning) << " requested mismatched theme type for [" << view << "." << element + << "] - expected \"" << expectedType << "\", got \"" << elemIt->second.type + << "\""; return nullptr; } @@ -555,14 +563,14 @@ const std::shared_ptr& ThemeData::getDefault() if (theme == nullptr) { theme = std::shared_ptr(new ThemeData()); - const std::string path = Utils::FileSystem::getHomePath() + - "/.emulationstation/es_theme_default.xml"; + const std::string path = + Utils::FileSystem::getHomePath() + "/.emulationstation/es_theme_default.xml"; if (Utils::FileSystem::exists(path)) { try { std::map emptyMap; theme->loadFile(emptyMap, path); } - catch(ThemeException& e) { + catch (ThemeException& e) { LOG(LogError) << e.what(); theme = std::shared_ptr(new ThemeData()); // Reset to empty. } @@ -573,7 +581,8 @@ const std::shared_ptr& ThemeData::getDefault() } std::vector ThemeData::makeExtras(const std::shared_ptr& theme, - const std::string& view, Window* window) + const std::string& view, + Window* window) { std::vector comps; @@ -581,8 +590,8 @@ std::vector ThemeData::makeExtras(const std::shared_ptrmViews.cend()) return comps; - for (auto it = viewIt->second.orderedKeys.cbegin(); - it != viewIt->second.orderedKeys.cend(); it++) { + for (auto it = viewIt->second.orderedKeys.cbegin(); // Line break. + it != viewIt->second.orderedKeys.cend(); it++) { ThemeElement& elem = viewIt->second.elements.at(*it); if (elem.extra) { GuiComponent* comp = nullptr; @@ -608,19 +617,20 @@ std::map ThemeData::getThemeSets() std::map sets; // Check for themes first under the home directory, then under the data installation - // directory (Unix only) and last under the ES executable directory. - #if defined(__unix__) || defined(__APPLE__) + // directory (Unix only) and last under the ES-DE binary directory. + +#if defined(__unix__) || defined(__APPLE__) static const size_t pathCount = 3; - #else +#else static const size_t pathCount = 2; - #endif +#endif std::string paths[pathCount] = { Utils::FileSystem::getExePath() + "/themes", - #if defined(__APPLE__) +#if defined(__APPLE__) Utils::FileSystem::getExePath() + "/../Resources/themes", - #elif defined(__unix__) +#elif defined(__unix__) Utils::FileSystem::getProgramDataPath() + "/themes", - #endif +#endif Utils::FileSystem::getHomePath() + "/.emulationstation/themes" }; @@ -631,9 +641,9 @@ std::map ThemeData::getThemeSets() Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(paths[i]); for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); - it != dirContent.cend(); it++) { + it != dirContent.cend(); it++) { if (Utils::FileSystem::isDirectory(*it)) { - ThemeSet set = {*it}; + ThemeSet set = { *it }; sets[set.getName()] = set; } } @@ -650,9 +660,9 @@ std::string ThemeData::getThemeFromCurrentSet(const std::string& system) return ""; std::map::const_iterator set = - themeSets.find(Settings::getInstance()->getString("ThemeSet")); + themeSets.find(Settings::getInstance()->getString("ThemeSet")); if (set == themeSets.cend()) { - // Currently selected theme set is missing, so just pick the first available set. + // Currently configured theme set is missing, so just pick the first available set. set = themeSets.cbegin(); Settings::getInstance()->setString("ThemeSet", set->first); } diff --git a/es-core/src/ThemeData.h b/es-core/src/ThemeData.h index d2855298f..d3e008b64 100644 --- a/es-core/src/ThemeData.h +++ b/es-core/src/ThemeData.h @@ -21,10 +21,12 @@ #include #include -namespace pugi { class xml_node; } +namespace pugi +{ + class xml_node; +} -template -class TextListComponent; +template class TextListComponent; class GuiComponent; class ImageComponent; @@ -63,10 +65,9 @@ public: virtual const char* what() const throw() { return msg.c_str(); } - template - friend ThemeException& operator<<(ThemeException& e, T msg); + template friend ThemeException& operator<<(ThemeException& e, T msg); - inline void setFiles(const std::deque& deque) + void setFiles(const std::deque& deque) { *this << "From theme \"" << deque.front() << "\""; for (auto it = deque.cbegin() + 1; it != deque.cend(); it++) @@ -74,8 +75,7 @@ public: } }; -template -ThemeException& operator<<(ThemeException& e, T appendMsg) +template ThemeException& operator<<(ThemeException& e, T appendMsg) { std::stringstream ss; ss << e.msg << appendMsg; @@ -83,13 +83,14 @@ ThemeException& operator<<(ThemeException& e, T appendMsg) return e; } -struct ThemeSet -{ +struct ThemeSet { std::string path; - inline std::string getName() const { return Utils::FileSystem::getStem(path); } - inline std::string getThemePath(const std::string& system) const - { return path + "/" + system + "/theme.xml"; } + std::string getName() const { return Utils::FileSystem::getStem(path); } + std::string getThemePath(const std::string& system) const + { + return path + "/" + system + "/theme.xml"; + } }; class ThemeData @@ -124,8 +125,7 @@ public: std::map properties; - template - const T get(const std::string& prop) const + template const T get(const std::string& prop) const { if (std::is_same::value) return *(const T*)&properties.at(prop).v; @@ -142,8 +142,10 @@ public: return T(); } - inline bool has(const std::string& prop) const - { return (properties.find(prop) != properties.cend()); } + bool has(const std::string& prop) const + { + return (properties.find(prop) != properties.cend()); + } }; private: @@ -173,11 +175,13 @@ public: bool hasView(const std::string& view); // If expectedType is an empty string, will do no type checking. - const ThemeElement* getElement(const std::string& view, const std::string& element, - const std::string& expectedType) const; + const ThemeElement* getElement(const std::string& view, + const std::string& element, + const std::string& expectedType) const; static std::vector makeExtras(const std::shared_ptr& theme, - const std::string& view, Window* window); + const std::string& view, + Window* window); static const std::shared_ptr& getDefault(); @@ -198,7 +202,8 @@ private: void parseViews(const pugi::xml_node& themeRoot); void parseView(const pugi::xml_node& viewNode, ThemeView& view); void parseElement(const pugi::xml_node& elementNode, - const std::map& typeMap, ThemeElement& element); + const std::map& typeMap, + ThemeElement& element); std::map mViews; }; diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index ae84f2889..e1820ecc6 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -14,40 +14,41 @@ #if defined(BUILD_VLC_PLAYER) #include "components/VideoVlcComponent.h" #endif -#include "resources/Font.h" #include "AudioManager.h" #include "InputManager.h" #include "Log.h" -#include "Scripting.h" #include "Sound.h" +#include "resources/Font.h" #include #include +#define CLOCK_BACKGROUND_CREATION false + Window::Window() - : mScreensaver(nullptr), - mMediaViewer(nullptr), - mLaunchScreen(nullptr), - mInfoPopup(nullptr), - mNormalizeNextUpdate(false), - mFrameTimeElapsed(0), - mFrameCountElapsed(0), - mAverageDeltaTime(10), - mAllowSleep(true), - mSleeping(false), - mTimeSinceLastInput(0), - mRenderScreensaver(false), - mRenderMediaViewer(false), - mRenderLaunchScreen(false), - mGameLaunchedState(false), - mAllowTextScrolling(true), - mCachedBackground(false), - mInvalidatedCachedBackground(false), - mVideoPlayerCount(0), - mTopOpacity(0), - mTopScale(0.5), - mListScrollOpacity(0), - mChangedThemeSet(false) + : mScreensaver(nullptr) + , mMediaViewer(nullptr) + , mLaunchScreen(nullptr) + , mInfoPopup(nullptr) + , mNormalizeNextUpdate(false) + , mFrameTimeElapsed(0) + , mFrameCountElapsed(0) + , mAverageDeltaTime(10) + , mAllowSleep(true) + , mSleeping(false) + , mTimeSinceLastInput(0) + , mRenderScreensaver(false) + , mRenderMediaViewer(false) + , mRenderLaunchScreen(false) + , mGameLaunchedState(false) + , mAllowTextScrolling(true) + , mCachedBackground(false) + , mInvalidatedCachedBackground(false) + , mVideoPlayerCount(0) + , mTopOpacity(0) + , mTopScale(0.5) + , mListScrollOpacity(0) + , mChangedThemeSet(false) { mHelp = new HelpComponent(this); mBackgroundOverlay = new ImageComponent(this); @@ -122,7 +123,7 @@ bool Window::init() mBackgroundOverlay->setImage(":/graphics/screen_gradient.png"); mBackgroundOverlay->setResize(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); mListScrollFont = Font::get(FONT_SIZE_LARGE); @@ -141,9 +142,9 @@ void Window::deinit() InputManager::getInstance()->deinit(); ResourceManager::getInstance()->unloadAll(); - #if defined(BUILD_VLC_PLAYER) +#if defined(BUILD_VLC_PLAYER) VideoVlcComponent::deinit(); - #endif +#endif Renderer::deinit(); } @@ -154,8 +155,9 @@ void Window::input(InputConfig* config, Input input) // The DebugSkipInputLogging option has to be set manually in es_settings.xml as // it does not have any settings menu entry. if (Settings::getInstance()->getBool("Debug") && - !Settings::getInstance()->getBool("DebugSkipInputLogging")) + !Settings::getInstance()->getBool("DebugSkipInputLogging")) { logInput(config, input); + } if (mMediaViewer && mRenderMediaViewer) { if (config->isMappedLike("right", input) && input.value != 0) @@ -177,17 +179,17 @@ void Window::input(InputConfig* config, Input input) if (mScreensaver) { if (mScreensaver->isScreensaverActive() && - Settings::getInstance()->getBool("ScreensaverControls") && - ((Settings::getInstance()->getString("ScreensaverType") == "video") || - (Settings::getInstance()->getString("ScreensaverType") == "slideshow"))) { + Settings::getInstance()->getBool("ScreensaverControls") && + ((Settings::getInstance()->getString("ScreensaverType") == "video") || + (Settings::getInstance()->getString("ScreensaverType") == "slideshow"))) { bool customImageSlideshow = false; if (Settings::getInstance()->getString("ScreensaverType") == "slideshow" && - Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages")) + Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages")) customImageSlideshow = true; if ((customImageSlideshow || mScreensaver->getCurrentGame() != nullptr) && - (config->isMappedTo("a", input) || config->isMappedTo("y", input) || - config->isMappedLike("left", input) || config->isMappedLike("right", input))) { + (config->isMappedTo("a", input) || config->isMappedTo("y", input) || + config->isMappedLike("left", input) || config->isMappedLike("right", input))) { // Left or right browses to the next video or image. if (config->isMappedLike("left", input) || config->isMappedLike("right", input)) { if (input.value != 0) { @@ -230,22 +232,22 @@ void Window::input(InputConfig* config, Input input) } if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_g && - SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { + SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { // Toggle debug grid with Ctrl-G. Settings::getInstance()->setBool("DebugGrid", - !Settings::getInstance()->getBool("DebugGrid")); + !Settings::getInstance()->getBool("DebugGrid")); } else if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_t && - SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { + SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { // Toggle TextComponent debug view with Ctrl-T. Settings::getInstance()->setBool("DebugText", - !Settings::getInstance()->getBool("DebugText")); + !Settings::getInstance()->getBool("DebugText")); } else if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_i && - SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { + SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { // Toggle ImageComponent debug view with Ctrl-I. Settings::getInstance()->setBool("DebugImage", - !Settings::getInstance()->getBool("DebugImage")); + !Settings::getInstance()->getBool("DebugImage")); } else { if (peekGui()) @@ -270,8 +272,8 @@ void Window::logInput(InputConfig* config, Input input) mapname += ", "; } - LOG(LogDebug) << "Window::logInput(" << config->getDeviceName() << "): " << - input.string() << ", isMappedTo=" << mapname << "value=" << input.value; + LOG(LogDebug) << "Window::logInput(" << config->getDeviceName() << "): " << input.string() + << ", isMappedTo=" << mapname << "value=" << input.value; } void Window::update(int deltaTime) @@ -292,12 +294,13 @@ void Window::update(int deltaTime) std::stringstream ss; // FPS. - ss << std::fixed << std::setprecision(1) << - (1000.0f * static_cast(mFrameCountElapsed) / - static_cast(mFrameTimeElapsed)) << " FPS ("; - ss << std::fixed << std::setprecision(2) << - (static_cast(mFrameTimeElapsed) / - static_cast(mFrameCountElapsed)) << " ms)"; + ss << std::fixed << std::setprecision(1) + << (1000.0f * static_cast(mFrameCountElapsed) / + static_cast(mFrameTimeElapsed)) + << " FPS ("; + ss << std::fixed << std::setprecision(2) + << (static_cast(mFrameTimeElapsed) / static_cast(mFrameCountElapsed)) + << " ms)"; // The following calculations are not accurate, and the font calculation is completely // broken. For now, still report the figures as it's somehow useful to locate memory @@ -307,12 +310,12 @@ void Window::update(int deltaTime) float textureTotalUsageMiB = TextureResource::getTotalTextureSize() / 1024.0f / 1024.0f; float fontVramUsageMiB = Font::getTotalMemUsage() / 1024.0f / 1024.0f; - ss << "\nFont VRAM: " << fontVramUsageMiB << " MiB\nTexture VRAM: " << - textureVramUsageMiB << " MiB\nMax Texture VRAM: " << - textureTotalUsageMiB << " MiB"; - mFrameDataText = std::unique_ptr - (mDefaultFonts.at(0)->buildTextCache(ss.str(), Renderer::getScreenWidth() * - 0.02f, Renderer::getScreenHeight() * 0.02f, 0xFF00FFFF, 1.3f)); + ss << "\nFont VRAM: " << fontVramUsageMiB + << " MiB\nTexture VRAM: " << textureVramUsageMiB + << " MiB\nMax Texture VRAM: " << textureTotalUsageMiB << " MiB"; + mFrameDataText = std::unique_ptr(mDefaultFonts.at(0)->buildTextCache( + ss.str(), Renderer::getScreenWidth() * 0.02f, Renderer::getScreenHeight() * 0.02f, + 0xFF00FFFF, 1.3f)); } mFrameTimeElapsed = 0; @@ -368,27 +371,29 @@ void Window::render() else if (mRenderScreensaver && mScreensaver->isFallbackScreensaver()) renderBottom = true; else if (mRenderScreensaver && - Settings::getInstance()->getString("ScreensaverType") == "video") + Settings::getInstance()->getString("ScreensaverType") == "video") renderBottom = false; else if (mRenderScreensaver && - Settings::getInstance()->getString("ScreensaverType") == "slideshow") + Settings::getInstance()->getString("ScreensaverType") == "slideshow") renderBottom = false; if (renderBottom) bottom->render(transform); if (bottom != top || mRenderLaunchScreen) { - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) if (!mCachedBackground) { // Generate a cache texture of the shaded background when opening the menu, which // will remain valid until the menu is closed. This is way faster than having to // render the shaders for every frame. -// const auto backgroundStartTime = std::chrono::system_clock::now(); +#if (CLOCK_BACKGROUND_CREATION) + const auto backgroundStartTime = std::chrono::system_clock::now(); +#endif std::shared_ptr mPostprocessedBackground; mPostprocessedBackground = TextureResource::get(""); - unsigned char* processedTexture = new unsigned char[Renderer::getScreenWidth() * - Renderer::getScreenHeight() * 4]; + unsigned char* processedTexture = + new unsigned char[Renderer::getScreenWidth() * Renderer::getScreenHeight() * 4]; // Defocus the background using multiple passes of gaussian blur, with the number // of iterations relative to the screen resolution. @@ -396,7 +401,7 @@ void Window::render() if (Settings::getInstance()->getBool("MenuBlurBackground")) { float heightModifier = Renderer::getScreenHeightModifier(); - + // clang-format off if (heightModifier < 1) backgroundParameters.blurPasses = 2; // Below 1080 else if (heightModifier >= 4) @@ -411,23 +416,25 @@ void Window::render() backgroundParameters.blurPasses = 3; // 1440 else if (heightModifier >= 1) backgroundParameters.blurPasses = 2; // 1080 + // clang-format on // Also dim the background slightly. backgroundParameters.fragmentDimValue = 0.60f; Renderer::shaderPostprocessing(Renderer::SHADER_BLUR_HORIZONTAL | - Renderer::SHADER_BLUR_VERTICAL | Renderer::SHADER_DIM, - backgroundParameters, processedTexture); + Renderer::SHADER_BLUR_VERTICAL | + Renderer::SHADER_DIM, + backgroundParameters, processedTexture); } else { // Dim the background slightly. backgroundParameters.fragmentDimValue = 0.60f; - Renderer::shaderPostprocessing( - Renderer::SHADER_DIM, backgroundParameters, processedTexture); + Renderer::shaderPostprocessing(Renderer::SHADER_DIM, backgroundParameters, + processedTexture); } - mPostprocessedBackground->initFromPixels(processedTexture, - Renderer::getScreenWidth(), Renderer::getScreenHeight()); + mPostprocessedBackground->initFromPixels( + processedTexture, Renderer::getScreenWidth(), Renderer::getScreenHeight()); mBackgroundOverlay->setImage(mPostprocessedBackground); @@ -444,10 +451,14 @@ void Window::render() delete[] processedTexture; mCachedBackground = true; -// const auto backgroundEndTime = std::chrono::system_clock::now(); -// LOG(LogDebug) << "Window::render(): Time to create cached background: " << -// std::chrono::duration_cast -// (backgroundEndTime - backgroundStartTime).count() << " ms"; +#if (CLOCK_BACKGROUND_CREATION) + const auto backgroundEndTime = std::chrono::system_clock::now(); + LOG(LogDebug) << "Window::render(): Time to create cached background: " + << std::chrono::duration_cast( + backgroundEndTime - backgroundStartTime) + .count() + << " ms"; +#endif } // Fade in the cached background if the menu opening effect has been set to scale-up. if (Settings::getInstance()->getString("MenuOpeningEffect") == "scale-up") { @@ -455,7 +466,7 @@ void Window::render() if (mBackgroundOverlayOpacity < 255) mBackgroundOverlayOpacity = Math::clamp(mBackgroundOverlayOpacity + 30, 0, 255); } - #endif +#endif // USE_OPENGL_21 mBackgroundOverlay->render(transform); @@ -464,8 +475,8 @@ void Window::render() if (mTopScale < 1.0f) { mTopScale = Math::clamp(mTopScale + 0.07f, 0.0f, 1.0f); Vector2f topCenter = top->getCenter(); - top->setOrigin({0.5, 0.5}); - top->setPosition({topCenter.x(), topCenter.y(), 0}); + top->setOrigin({ 0.5f, 0.5f }); + top->setPosition({ topCenter.x(), topCenter.y(), 0.0f }); top->setScale(mTopScale); } } @@ -476,7 +487,7 @@ void Window::render() else { mCachedBackground = false; mTopOpacity = 0; - mTopScale = 0.5; + mTopScale = 0.5f; } } @@ -484,15 +495,15 @@ void Window::render() if (mListScrollOpacity != 0) { Renderer::setMatrix(Transform4x4f::Identity()); Renderer::drawRect(0.0f, 0.0f, static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight()), - 0x00000000 | mListScrollOpacity, 0x00000000 | mListScrollOpacity); + static_cast(Renderer::getScreenHeight()), + 0x00000000 | mListScrollOpacity, 0x00000000 | mListScrollOpacity); Vector2f offset = mListScrollFont->sizeText(mListScrollText); offset[0] = (Renderer::getScreenWidth() - offset.x()) * 0.5f; offset[1] = (Renderer::getScreenHeight() - offset.y()) * 0.5f; - TextCache* cache = mListScrollFont->buildTextCache(mListScrollText, - offset.x(), offset.y(), 0xFFFFFF00 | mListScrollOpacity); + TextCache* cache = mListScrollFont->buildTextCache(mListScrollText, offset.x(), offset.y(), + 0xFFFFFF00 | mListScrollOpacity); mListScrollFont->renderTextCache(cache); delete cache; } @@ -501,7 +512,7 @@ void Window::render() mHelp->render(transform); unsigned int screensaverTimer = - static_cast(Settings::getInstance()->getInt("ScreensaverTimer")); + static_cast(Settings::getInstance()->getInt("ScreensaverTimer")); if (mTimeSinceLastInput >= screensaverTimer && screensaverTimer != 0) { // If the media viewer is running or if a menu is open, reset the screensaver timer so // that the screensaver won't start. @@ -549,17 +560,17 @@ void Window::renderLoadingScreen(std::string text) Transform4x4f trans = Transform4x4f::Identity(); Renderer::setMatrix(trans); Renderer::drawRect(0.0f, 0.0f, static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); + static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); ImageComponent splash(this, true); splash.setResize(Renderer::getScreenWidth() * 0.6f, 0.0f); splash.setImage(":/graphics/splash.svg"); - splash.setPosition((Renderer::getScreenWidth() - splash.getSize().x()) / 2, - (Renderer::getScreenHeight() - splash.getSize().y()) / 2 * 0.6f); + splash.setPosition((Renderer::getScreenWidth() - splash.getSize().x()) / 2.0f, + (Renderer::getScreenHeight() - splash.getSize().y()) / 2.0f * 0.6f); splash.render(trans); auto& font = mDefaultFonts.at(1); - TextCache* cache = font->buildTextCache(text, 0, 0, 0x656565FF); + TextCache* cache = font->buildTextCache(text, 0.0f, 0.0f, 0x656565FF); float x = std::round((Renderer::getScreenWidth() - cache->metrics.size.x()) / 2.0f); float y = std::round(Renderer::getScreenHeight() * 0.835f); @@ -602,20 +613,17 @@ void Window::setHelpPrompts(const std::vector& prompts, const HelpSt // Can we combine? (dpad only). if ((it->first == "up/down" && - addPrompts.at(mappedTo->second).first != "left/right") || - (it->first == "left/right" && - addPrompts.at(mappedTo->second).first != "up/down")) { - // Yes! + addPrompts.at(mappedTo->second).first != "left/right") || + (it->first == "left/right" && + addPrompts.at(mappedTo->second).first != "up/down")) { + // Yes. addPrompts.at(mappedTo->second).first = "up/down/left/right"; - // Don't need to add this to addPrompts since we just merged. } else { - // No, we can't combine! addPrompts.push_back(*it); } } else { - // No, it hasn't! mappedToSeenMap.emplace(it->second, static_cast(addPrompts.size())); addPrompts.push_back(*it); } @@ -624,29 +632,31 @@ void Window::setHelpPrompts(const std::vector& prompts, const HelpSt // Sort prompts so it goes [dpad_all] [dpad_u/d] [dpad_l/r] [a/b/x/y/l/r] [start/back]. std::sort(addPrompts.begin(), addPrompts.end(), - [](const HelpPrompt& a, const HelpPrompt& b) -> bool { + [](const HelpPrompt& a, const HelpPrompt& b) -> bool { + static const std::vector map = { "up/down/left/right", + "up/down", + "left/right", + "a", + "b", + "x", + "y", + "l", + "r", + "start", + "back" }; + int i = 0; + int aVal = 0; + int bVal = 0; + while (i < map.size()) { + if (a.first == map[i]) + aVal = i; + if (b.first == map[i]) + bVal = i; + i++; + } - static const std::vector map = { - "up/down/left/right", - "up/down", - "left/right", - "a", "b", "x", "y", "l", "r", - "start", "back" - }; - - int i = 0; - int aVal = 0; - int bVal = 0; - while (i < map.size()) { - if (a.first == map[i]) - aVal = i; - if (b.first == map[i]) - bVal = i; - i++; - } - - return aVal > bVal; - }); + return aVal > bVal; + }); mHelp->setPrompts(addPrompts); } @@ -789,18 +799,8 @@ void Window::invalidateCachedBackground() mInvalidatedCachedBackground = true; } -void Window::onSleep() -{ - Scripting::fireEvent("sleep"); -} - -void Window::onWake() -{ - Scripting::fireEvent("wake"); -} - bool Window::isProcessing() { - return count_if (mGuiStack.cbegin(), mGuiStack.cend(), [](GuiComponent* c) { - return c->isProcessing(); }) > 0; + return count_if(mGuiStack.cbegin(), mGuiStack.cend(), + [](GuiComponent* c) { return c->isProcessing(); }) > 0; } diff --git a/es-core/src/Window.h b/es-core/src/Window.h index 634c59c3e..5201b2902 100644 --- a/es-core/src/Window.h +++ b/es-core/src/Window.h @@ -10,10 +10,11 @@ #ifndef ES_CORE_WINDOW_H #define ES_CORE_WINDOW_H -#include "resources/TextureResource.h" #include "HelpPrompt.h" #include "InputConfig.h" +#include "Scripting.h" #include "Settings.h" +#include "resources/TextureResource.h" #include #include @@ -78,7 +79,7 @@ public: public: virtual void render(const Transform4x4f& parentTrans) = 0; virtual void stop() = 0; - virtual ~InfoPopup() {}; + virtual ~InfoPopup() {} }; Window(); @@ -87,7 +88,7 @@ public: void pushGui(GuiComponent* gui); void removeGui(GuiComponent* gui); GuiComponent* peekGui(); - inline int getGuiStackSize() { return static_cast(mGuiStack.size()); } + int getGuiStackSize() { return static_cast(mGuiStack.size()); } bool init(); void deinit(); @@ -102,7 +103,7 @@ public: bool getAllowSleep() { return mAllowSleep; } void setAllowSleep(bool sleep) { mAllowSleep = sleep; } - inline bool isSleeping() const { return mSleeping; } + bool isSleeping() const { return mSleeping; } void renderLoadingScreen(std::string text); // The list scroll overlay is triggered from IList when the highest scrolling tier is reached. @@ -148,8 +149,8 @@ public: bool getChangedThemeSet() { return mChangedThemeSet; } private: - void onSleep(); - void onWake(); + void onSleep() { Scripting::fireEvent("sleep"); } + void onWake() { Scripting::fireEvent("wake"); } // Returns true if at least one component on the stack is processing. bool isProcessing(); diff --git a/es-core/src/animations/Animation.h b/es-core/src/animations/Animation.h index b2eaf28d0..62c5a15f8 100644 --- a/es-core/src/animations/Animation.h +++ b/es-core/src/animations/Animation.h @@ -12,7 +12,7 @@ class Animation { public: - virtual ~Animation() {}; + virtual ~Animation() {} virtual int getDuration() const = 0; virtual void apply(float t) = 0; }; diff --git a/es-core/src/animations/AnimationController.cpp b/es-core/src/animations/AnimationController.cpp index 7f39ce946..e351b87ae 100644 --- a/es-core/src/animations/AnimationController.cpp +++ b/es-core/src/animations/AnimationController.cpp @@ -10,16 +10,15 @@ #include "animations/Animation.h" -AnimationController::AnimationController( - Animation* anim, - int delay, - std::function finishedCallback, - bool reverse) - : mAnimation(anim), - mFinishedCallback(finishedCallback), - mReverse(reverse), - mTime(-delay), - mDelay(delay) +AnimationController::AnimationController(Animation* anim, + int delay, + std::function finishedCallback, + bool reverse) + : mAnimation(anim) + , mFinishedCallback(finishedCallback) + , mReverse(reverse) + , mTime(-delay) + , mDelay(delay) { } diff --git a/es-core/src/animations/AnimationController.h b/es-core/src/animations/AnimationController.h index 786d44503..a44d23072 100644 --- a/es-core/src/animations/AnimationController.h +++ b/es-core/src/animations/AnimationController.h @@ -17,20 +17,22 @@ class AnimationController { public: // Takes ownership of anim (will delete in destructor). - AnimationController(Animation* anim, int delay = 0, - std::function finishedCallback = nullptr, bool reverse = false); + AnimationController(Animation* anim, + int delay = 0, + std::function finishedCallback = nullptr, + bool reverse = false); virtual ~AnimationController(); // Returns true if the animation is complete. bool update(int deltaTime); - inline bool isReversed() const { return mReverse; } - inline int getTime() const { return mTime; } - inline int getDelay() const { return mDelay; } - inline const std::function& getFinishedCallback() const { return mFinishedCallback; } - inline Animation* getAnimation() const { return mAnimation; } + bool isReversed() const { return mReverse; } + int getTime() const { return mTime; } + int getDelay() const { return mDelay; } + const std::function& getFinishedCallback() const { return mFinishedCallback; } + Animation* getAnimation() const { return mAnimation; } - inline void removeFinishedCallback() { mFinishedCallback = nullptr; } + void removeFinishedCallback() { mFinishedCallback = nullptr; } private: Animation* mAnimation; diff --git a/es-core/src/animations/LambdaAnimation.h b/es-core/src/animations/LambdaAnimation.h index 82055a492..974d3d5c4 100644 --- a/es-core/src/animations/LambdaAnimation.h +++ b/es-core/src/animations/LambdaAnimation.h @@ -19,16 +19,15 @@ class LambdaAnimation : public Animation { public: LambdaAnimation(const std::function& func, int duration) - : mFunction(func), mDuration(duration) {} + : mFunction(func) + , mDuration(duration) + { + } virtual ~LambdaAnimation() = default; int getDuration() const override { return mDuration; } - - void apply(float t) override - { - mFunction(t); - } + void apply(float t) override { mFunction(t); } private: std::function mFunction; diff --git a/es-core/src/components/AnimatedImageComponent.cpp b/es-core/src/components/AnimatedImageComponent.cpp index 9f655aa61..64cbc2b81 100644 --- a/es-core/src/components/AnimatedImageComponent.cpp +++ b/es-core/src/components/AnimatedImageComponent.cpp @@ -8,12 +8,13 @@ #include "components/AnimatedImageComponent.h" +#include "Log.h" #include "components/ImageComponent.h" #include "resources/ResourceManager.h" -#include "Log.h" AnimatedImageComponent::AnimatedImageComponent(Window* window) - : GuiComponent(window), mEnabled(false) + : GuiComponent(window) + , mEnabled(false) { } @@ -25,9 +26,9 @@ void AnimatedImageComponent::load(const AnimationDef* def) for (size_t i = 0; i < def->frameCount; i++) { if (def->frames[i].path != "" && - !ResourceManager::getInstance()->fileExists(def->frames[i].path)) { - LOG(LogError) << "Missing animation frame " << i << - " (\"" << def->frames[i].path << "\")"; + !ResourceManager::getInstance()->fileExists(def->frames[i].path)) { + LOG(LogError) << "Missing animation frame " << i << " (\"" << def->frames[i].path + << "\")"; continue; } diff --git a/es-core/src/components/BusyComponent.cpp b/es-core/src/components/BusyComponent.cpp index cd924cbe4..c66a5ae99 100644 --- a/es-core/src/components/BusyComponent.cpp +++ b/es-core/src/components/BusyComponent.cpp @@ -14,21 +14,23 @@ // Animation definition. AnimationFrame BUSY_ANIMATION_FRAMES[] = { - {":/graphics/busy_0.svg", 300}, - {":/graphics/busy_1.svg", 300}, - {":/graphics/busy_2.svg", 300}, - {":/graphics/busy_3.svg", 300}, + { ":/graphics/busy_0.svg", 300 }, + { ":/graphics/busy_1.svg", 300 }, + { ":/graphics/busy_2.svg", 300 }, + { ":/graphics/busy_3.svg", 300 }, }; const AnimationDef BUSY_ANIMATION_DEF = { BUSY_ANIMATION_FRAMES, 4, true }; -BusyComponent::BusyComponent(Window* window): GuiComponent(window), - mBackground(window, ":/graphics/frame.png"), mGrid(window, Vector2i(5, 3)) +BusyComponent::BusyComponent(Window* window) + : GuiComponent(window) + , mBackground(window, ":/graphics/frame.png") + , mGrid(window, Vector2i(5, 3)) { mAnimation = std::make_shared(mWindow); mAnimation->load(&BUSY_ANIMATION_DEF); - mText = std::make_shared(mWindow, "WORKING...", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + mText = std::make_shared(mWindow, "WORKING...", Font::get(FONT_SIZE_MEDIUM), + 0x777777FF); // Col 0 = animation, col 1 = spacer, col 2 = text. mGrid.setEntry(mAnimation, Vector2i(1, 1), false, true); @@ -57,13 +59,13 @@ void BusyComponent::onSizeChanged() mGrid.setRowHeightPerc(1, textHeight / mSize.y()); mBackground.setCornerSize({ 16.0f * Renderer::getScreenWidthModifier(), - 16.0f * Renderer::getScreenHeightModifier() }); - mBackground.fitTo(Vector2f(mGrid.getColWidth(1) + mGrid.getColWidth(2) + - mGrid.getColWidth(3), textHeight + (2 * Renderer::getScreenHeightModifier())), - mAnimation->getPosition(), Vector2f(0, 0)); + 16.0f * Renderer::getScreenHeightModifier() }); + mBackground.fitTo(Vector2f(mGrid.getColWidth(1) + mGrid.getColWidth(2) + mGrid.getColWidth(3), + textHeight + (2.0f * Renderer::getScreenHeightModifier())), + mAnimation->getPosition(), Vector2f(0, 0)); } void BusyComponent::reset() { - //mAnimation->reset(); + // mAnimation->reset(); } diff --git a/es-core/src/components/BusyComponent.h b/es-core/src/components/BusyComponent.h index 0dd634e4c..e3c361905 100644 --- a/es-core/src/components/BusyComponent.h +++ b/es-core/src/components/BusyComponent.h @@ -9,9 +9,9 @@ #ifndef ES_CORE_COMPONENTS_BUSY_COMPONENT_H #define ES_CORE_COMPONENTS_BUSY_COMPONENT_H +#include "GuiComponent.h" #include "components/ComponentGrid.h" #include "components/NinePatchComponent.h" -#include "GuiComponent.h" class AnimatedImageComponent; class TextComponent; diff --git a/es-core/src/components/ButtonComponent.cpp b/es-core/src/components/ButtonComponent.cpp index caa82ad4b..06d7032d4 100644 --- a/es-core/src/components/ButtonComponent.cpp +++ b/es-core/src/components/ButtonComponent.cpp @@ -8,20 +8,21 @@ #include "components/ButtonComponent.h" +#include "Settings.h" #include "resources/Font.h" #include "utils/StringUtil.h" -#include "Settings.h" -ButtonComponent::ButtonComponent( - Window* window, const std::string& text, - const std::string& helpText, - const std::function& func) - : GuiComponent(window), - mBox(window, ":/graphics/button.svg"), - mFont(Font::get(FONT_SIZE_MEDIUM)), - mFocused(false), - mEnabled(true), - mTextColorFocused(0xFFFFFFFF), mTextColorUnfocused(0x777777FF) +ButtonComponent::ButtonComponent(Window* window, + const std::string& text, + const std::string& helpText, + const std::function& func) + : GuiComponent(window) + , mBox(window, ":/graphics/button.svg") + , mFont(Font::get(FONT_SIZE_MEDIUM)) + , mFocused(false) + , mEnabled(true) + , mTextColorFocused(0xFFFFFFFF) + , mTextColorUnfocused(0x777777FF) { setPressedFunc(func); setText(text, helpText); @@ -30,12 +31,8 @@ ButtonComponent::ButtonComponent( void ButtonComponent::onSizeChanged() { - mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); -} - -void ButtonComponent::setPressedFunc(std::function f) -{ - mPressedFunc = f; + // Fit to mBox. + mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); } bool ButtonComponent::input(InputConfig* config, Input input) @@ -56,9 +53,10 @@ void ButtonComponent::setText(const std::string& text, const std::string& helpTe mTextCache = std::unique_ptr(mFont->buildTextCache(mText, 0, 0, getCurTextColor())); - float minWidth = mFont->sizeText("DELETE").x() + (12 * Renderer::getScreenWidthModifier()); - setSize(std::max(mTextCache->metrics.size.x() + (12 * Renderer::getScreenWidthModifier()), - minWidth), mTextCache->metrics.size.y()); + float minWidth = mFont->sizeText("DELETE").x() + (12.0f * Renderer::getScreenWidthModifier()); + setSize(std::max(mTextCache->metrics.size.x() + (12.0f * Renderer::getScreenWidthModifier()), + minWidth), + mTextCache->metrics.size.y()); updateHelpPrompts(); } @@ -103,14 +101,14 @@ void ButtonComponent::render(const Transform4x4f& parentTrans) if (mTextCache) { Vector3f centerOffset((mSize.x() - mTextCache->metrics.size.x()) / 2.0f, - (mSize.y() - mTextCache->metrics.size.y()) / 2.0f, 0); + (mSize.y() - mTextCache->metrics.size.y()) / 2.0f, 0); trans = trans.translate(centerOffset); if (Settings::getInstance()->getBool("DebugText")) { - Renderer::drawRect(centerOffset.x(), 0.0f, mTextCache->metrics.size.x(), - mSize.y(), 0x00000033, 0x00000033); - Renderer::drawRect(mBox.getPosition().x(), 0.0f, mBox.getSize().x(), - mSize.y(), 0x0000FF33, 0x0000FF33); + Renderer::drawRect(centerOffset.x(), 0.0f, mTextCache->metrics.size.x(), mSize.y(), + 0x00000033, 0x00000033); + Renderer::drawRect(mBox.getPosition().x(), 0.0f, mBox.getSize().x(), mSize.y(), + 0x0000FF33, 0x0000FF33); } Renderer::setMatrix(trans); diff --git a/es-core/src/components/ButtonComponent.h b/es-core/src/components/ButtonComponent.h index ae38916a5..fd2c59171 100644 --- a/es-core/src/components/ButtonComponent.h +++ b/es-core/src/components/ButtonComponent.h @@ -9,18 +9,20 @@ #ifndef ES_CORE_COMPONENTS_BUTTON_COMPONENT_H #define ES_CORE_COMPONENTS_BUTTON_COMPONENT_H -#include "components/NinePatchComponent.h" #include "GuiComponent.h" +#include "components/NinePatchComponent.h" class TextCache; class ButtonComponent : public GuiComponent { public: - ButtonComponent(Window* window, const std::string& text = "", - const std::string& helpText = "", const std::function& func = nullptr); + ButtonComponent(Window* window, + const std::string& text = "", + const std::string& helpText = "", + const std::function& func = nullptr); - void setPressedFunc(std::function f); + void setPressedFunc(std::function f) { mPressedFunc = f; } void setEnabled(bool state) override; bool input(InputConfig* config, Input input) override; @@ -28,8 +30,8 @@ public: void setText(const std::string& text, const std::string& helpText); - inline const std::string& getText() const { return mText; }; - inline const std::function& getPressedFunc() const { return mPressedFunc; }; + const std::string& getText() const { return mText; } + const std::function& getPressedFunc() const { return mPressedFunc; } void onSizeChanged() override; void onFocusGained() override; diff --git a/es-core/src/components/ComponentGrid.cpp b/es-core/src/components/ComponentGrid.cpp index 183975e73..d8bf872d3 100644 --- a/es-core/src/components/ComponentGrid.cpp +++ b/es-core/src/components/ComponentGrid.cpp @@ -12,12 +12,10 @@ using namespace GridFlags; -ComponentGrid::ComponentGrid( - Window* window, - const Vector2i& gridDimensions) - : GuiComponent(window), - mGridSize(gridDimensions), - mCursor(0, 0) +ComponentGrid::ComponentGrid(Window* window, const Vector2i& gridDimensions) + : GuiComponent(window) + , mGridSize(gridDimensions) + , mCursor(0, 0) { assert(gridDimensions.x() > 0 && gridDimensions.y() > 0); @@ -25,6 +23,7 @@ ComponentGrid::ComponentGrid( mColWidths = new float[gridDimensions.x()]; mRowHeights = new float[gridDimensions.y()]; + for (int x = 0; x < gridDimensions.x(); x++) mColWidths[x] = 0; for (int y = 0; y < gridDimensions.y(); y++) @@ -90,14 +89,13 @@ void ComponentGrid::setRowHeightPerc(int row, float height, bool update) onSizeChanged(); } -void ComponentGrid::setEntry( - const std::shared_ptr& comp, - const Vector2i& pos, - bool canFocus, - bool resize, - const Vector2i& size, - unsigned int border, - GridFlags::UpdateType updateType) +void ComponentGrid::setEntry(const std::shared_ptr& comp, + const Vector2i& pos, + bool canFocus, + bool resize, + const Vector2i& size, + unsigned int border, + GridFlags::UpdateType updateType) { assert(pos.x() >= 0 && pos.x() < mGridSize.x() && pos.y() >= 0 && pos.y() < mGridSize.y()); assert(comp != nullptr); @@ -294,14 +292,14 @@ bool ComponentGrid::moveCursor(Vector2i dir) Vector2i searchAxis(dir.x() == 0, dir.y() == 0); while (mCursor.x() >= 0 && mCursor.y() >= 0 && mCursor.x() < mGridSize.x() && - mCursor.y() < mGridSize.y()) { + mCursor.y() < mGridSize.y()) { mCursor = mCursor + dir; Vector2i curDirPos = mCursor; const GridEntry* cursorEntry; // Spread out on search axis+ - while (mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y() - && mCursor.x() >= 0 && mCursor.y() >= 0) { + while (mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y() && mCursor.x() >= 0 && + mCursor.y() >= 0) { cursorEntry = getCellAt(mCursor); if (cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) { onCursorMoved(origCursor, mCursor); @@ -312,8 +310,8 @@ bool ComponentGrid::moveCursor(Vector2i dir) // Now again on search axis- mCursor = curDirPos; - while (mCursor.x() >= 0 && mCursor.y() >= 0 - && mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()) { + while (mCursor.x() >= 0 && mCursor.y() >= 0 && mCursor.x() < mGridSize.x() && + mCursor.y() < mGridSize.y()) { cursorEntry = getCellAt(mCursor); if (cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) { @@ -356,8 +354,9 @@ void ComponentGrid::update(int deltaTime) const GridEntry* cursorEntry = getCellAt(mCursor); for (auto it = mCells.cbegin(); it != mCells.cend(); it++) { if (it->updateType == UPDATE_ALWAYS || - (it->updateType == UPDATE_WHEN_SELECTED && cursorEntry == &(*it))) + (it->updateType == UPDATE_WHEN_SELECTED && cursorEntry == &(*it))) { it->component->update(deltaTime); + } } } @@ -371,7 +370,7 @@ void ComponentGrid::render(const Transform4x4f& parentTrans) for (size_t i = 0; i < mSeparators.size(); i++) { Renderer::setMatrix(trans); Renderer::drawRect(mSeparators[i][0], mSeparators[i][1], mSeparators[i][2], - mSeparators[i][3], 0xC6C7C6FF, 0xC6C7C6FF); + mSeparators[i][3], 0xC6C7C6FF, 0xC6C7C6FF); } } diff --git a/es-core/src/components/ComponentGrid.h b/es-core/src/components/ComponentGrid.h index 8b98e4950..9acd927e0 100644 --- a/es-core/src/components/ComponentGrid.h +++ b/es-core/src/components/ComponentGrid.h @@ -9,14 +9,14 @@ #ifndef ES_CORE_COMPONENTS_COMPONENT_GRID_H #define ES_CORE_COMPONENTS_COMPONENT_GRID_H +#include "GuiComponent.h" #include "math/Vector2i.h" #include "renderers/Renderer.h" -#include "GuiComponent.h" namespace GridFlags { enum UpdateType { - UPDATE_ALWAYS, + UPDATE_ALWAYS, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). UPDATE_WHEN_SELECTED, UPDATE_NEVER }; @@ -28,7 +28,7 @@ namespace GridFlags BORDER_LEFT = 4, BORDER_RIGHT = 8 }; -}; +}; // namespace GridFlags // Provides basic layout of components in an X*Y grid. class ComponentGrid : public GuiComponent @@ -39,14 +39,13 @@ public: bool removeEntry(const std::shared_ptr& comp); - void setEntry( - const std::shared_ptr& comp, - const Vector2i& pos, - bool canFocus, - bool resize = true, - const Vector2i& size = Vector2i(1, 1), - unsigned int border = GridFlags::BORDER_NONE, - GridFlags::UpdateType updateType = GridFlags::UPDATE_ALWAYS); + void setEntry(const std::shared_ptr& comp, + const Vector2i& pos, + bool canFocus, + bool resize = true, + const Vector2i& size = Vector2i(1, 1), + unsigned int border = GridFlags::BORDER_NONE, + GridFlags::UpdateType updateType = GridFlags::UPDATE_ALWAYS); void textInput(const std::string& text) override; bool input(InputConfig* config, Input input) override; @@ -69,7 +68,7 @@ public: bool moveCursor(Vector2i dir); void setCursorTo(const std::shared_ptr& comp); - inline std::shared_ptr getSelectedComponent() + std::shared_ptr getSelectedComponent() { const GridEntry* e = getCellAt(mCursor); if (e) @@ -95,28 +94,24 @@ private: GridFlags::UpdateType updateType; unsigned int border; - GridEntry( - const Vector2i& p = Vector2i::Zero(), - const Vector2i& d = Vector2i::Zero(), - const std::shared_ptr& cmp = nullptr, - bool f = false, - bool r = true, - GridFlags::UpdateType u = GridFlags::UPDATE_ALWAYS, - unsigned int b = - GridFlags::BORDER_NONE) - : pos(p), - dim(d), - component(cmp), - canFocus(f), - resize(r), - updateType(u), - border(b) - {}; - - operator bool() const + GridEntry(const Vector2i& p = Vector2i::Zero(), + const Vector2i& d = Vector2i::Zero(), + const std::shared_ptr& cmp = nullptr, + bool f = false, + bool r = true, + GridFlags::UpdateType u = GridFlags::UPDATE_ALWAYS, + unsigned int b = GridFlags::BORDER_NONE) + : pos(p) + , dim(d) + , component(cmp) + , canFocus(f) + , resize(r) + , updateType(u) + , border(b) { - return component != nullptr; } + + operator bool() const { return component != nullptr; } }; // Update position and size. @@ -125,9 +120,7 @@ private: void onCursorMoved(Vector2i from, Vector2i to); const GridEntry* getCellAt(int x, int y) const; - - inline const GridEntry* getCellAt(const Vector2i& pos) const - { return getCellAt(pos.x(), pos.y()); } + const GridEntry* getCellAt(const Vector2i& pos) const { return getCellAt(pos.x(), pos.y()); } std::vector> mSeparators; Vector2i mGridSize; diff --git a/es-core/src/components/ComponentList.cpp b/es-core/src/components/ComponentList.cpp index 6edff9740..9e1edfab3 100644 --- a/es-core/src/components/ComponentList.cpp +++ b/es-core/src/components/ComponentList.cpp @@ -10,14 +10,14 @@ #define TOTAL_HORIZONTAL_PADDING_PX 20.0f -ComponentList::ComponentList(Window* window) : IList(window, LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP) +ComponentList::ComponentList(Window* window) + : IList(window, LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP) { // Adjust the padding relative to the aspect ratio and screen resolution to make it look // coherent regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference. float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); - mHorizontalPadding = TOTAL_HORIZONTAL_PADDING_PX * aspectValue * - Renderer::getScreenWidthModifier(); + mHorizontalPadding = + TOTAL_HORIZONTAL_PADDING_PX * aspectValue * Renderer::getScreenWidthModifier(); mSelectorBarOffset = 0.0f; mCameraOffset = 0.0f; @@ -34,7 +34,7 @@ void ComponentList::addRow(const ComponentListRow& row, bool setCursorHere) this->add(e); for (auto it = mEntries.back().data.elements.cbegin(); - it != mEntries.back().data.elements.cend(); it++) + it != mEntries.back().data.elements.cend(); it++) addChild(it->component.get()); updateElementSize(mEntries.back().data); @@ -56,16 +56,6 @@ void ComponentList::onSizeChanged() updateCameraOffset(); } -void ComponentList::onFocusLost() -{ - mFocused = false; -} - -void ComponentList::onFocusGained() -{ - mFocused = true; -} - bool ComponentList::input(InputConfig* config, Input input) { if (size() == 0) @@ -122,7 +112,7 @@ void ComponentList::update(int deltaTime) if (size()) { // Update our currently selected row. for (auto it = mEntries.at(mCursor).data.elements.cbegin(); - it != mEntries.at(mCursor).data.elements.cend(); it++) + it != mEntries.at(mCursor).data.elements.cend(); it++) it->component->update(deltaTime); } } @@ -156,8 +146,8 @@ void ComponentList::updateCameraOffset() // Move the camera to scroll. const float totalHeight = getTotalRowHeight(); if (totalHeight > mSize.y()) { - float target = mSelectorBarOffset + - getRowHeight(mEntries.at(mCursor).data) / 2.0f - (mSize.y() / 2.0f); + float target = mSelectorBarOffset + getRowHeight(mEntries.at(mCursor).data) / 2.0f - + (mSize.y() / 2.0f); // Clamp the camera to prevent a fraction of a row from being displayed. mCameraOffset = 0.0f; @@ -187,9 +177,10 @@ void ComponentList::render(const Transform4x4f& parentTrans) // Clip everything to be inside our bounds. Vector3f dim(mSize.x(), mSize.y(), 0.0f); dim = trans * dim - trans.translation(); - Renderer::pushClipRect(Vector2i(static_cast(std::round(trans.translation().x())), - static_cast(std::round(trans.translation().y()))), Vector2i(static_cast( - std::round(dim.x())), static_cast(std::round(dim.y())))); + Renderer::pushClipRect( + Vector2i(static_cast(std::round(trans.translation().x())), + static_cast(std::round(trans.translation().y()))), + Vector2i(static_cast(std::round(dim.x())), static_cast(std::round(dim.y())))); // Scroll the camera. trans.translate(Vector3f(0.0f, -std::round(mCameraOffset), 0.0f)); @@ -205,7 +196,7 @@ void ComponentList::render(const Transform4x4f& parentTrans) // For the row where the cursor is at, we want to remove any hue from the // font or image before inverting, as it would otherwise lead to an ugly // inverted color (e.g. red inverting to a green hue). - if (i == mCursor && it->component->getValue() != "" ) { + if (i == mCursor && it->component->getValue() != "") { // Check if we're dealing with text or an image component. bool isTextComponent = true; unsigned int origColor = it->component->getColor(); @@ -260,13 +251,13 @@ void ComponentList::render(const Transform4x4f& parentTrans) const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data); if (opacity == 1) { - Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), - selectedRowHeight, 0xFFFFFFFF, 0xFFFFFFFF, false, opacity, trans, - Renderer::Blend::ONE_MINUS_DST_COLOR, Renderer::Blend::ZERO); + Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), selectedRowHeight, 0xFFFFFFFF, + 0xFFFFFFFF, false, opacity, trans, + Renderer::Blend::ONE_MINUS_DST_COLOR, Renderer::Blend::ZERO); - Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), - selectedRowHeight, 0x777777FF, 0x777777FF, false, opacity, trans, - Renderer::Blend::ONE, Renderer::Blend::ONE); + Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), selectedRowHeight, 0x777777FF, + 0x777777FF, false, opacity, trans, Renderer::Blend::ONE, + Renderer::Blend::ONE); } for (auto it = drawAfterCursor.cbegin(); it != drawAfterCursor.cend(); it++) @@ -281,12 +272,12 @@ void ComponentList::render(const Transform4x4f& parentTrans) float y = 0; for (unsigned int i = 0; i < mEntries.size(); i++) { Renderer::drawRect(0.0f, y, mSize.x(), 1.0f * Renderer::getScreenHeightModifier(), - 0xC6C7C6FF, 0xC6C7C6FF, false, opacity, trans); + 0xC6C7C6FF, 0xC6C7C6FF, false, opacity, trans); y += getRowHeight(mEntries.at(i).data); } - Renderer::drawRect(0.0f, y, mSize.x(), 1.0f * Renderer::getScreenHeightModifier(), - 0xC6C7C6FF, 0xC6C7C6FF, false, opacity, trans); + Renderer::drawRect(0.0f, y, mSize.x(), 1.0f * Renderer::getScreenHeightModifier(), 0xC6C7C6FF, + 0xC6C7C6FF, false, opacity, trans); Renderer::popClipRect(); } @@ -362,7 +353,7 @@ std::vector ComponentList::getHelpPrompts() return std::vector(); std::vector prompts = - mEntries.at(mCursor).data.elements.back().component->getHelpPrompts(); + mEntries.at(mCursor).data.elements.back().component->getHelpPrompts(); if (size() > 1) { bool addMovePrompt = true; diff --git a/es-core/src/components/ComponentList.h b/es-core/src/components/ComponentList.h index e4b6e4b31..309e2007c 100644 --- a/es-core/src/components/ComponentList.h +++ b/es-core/src/components/ComponentList.h @@ -12,21 +12,21 @@ #include "IList.h" struct ComponentListElement { - ComponentListElement( - const std::shared_ptr& cmp = nullptr, - bool resize_w = true, - bool inv = true) - : component(cmp), - resize_width(resize_w), - invert_when_selected(inv) {}; + ComponentListElement(const std::shared_ptr& cmp = nullptr, + bool resize_w = true, + bool inv = true) + : component(cmp) + , resize_width(resize_w) + , invert_when_selected(inv) + { + } std::shared_ptr component; bool resize_width; bool invert_when_selected; }; -struct ComponentListRow -{ +struct ComponentListRow { std::vector elements; // The input handler is called when the user enters any input while this row is @@ -36,14 +36,15 @@ struct ComponentListRow // to forward the input to the rightmost element in the currently selected row. std::function input_handler; - inline void addElement(const std::shared_ptr& component, - bool resize_width, bool invert_when_selected = true) + void addElement(const std::shared_ptr& component, + bool resize_width, + bool invert_when_selected = true) { elements.push_back(ComponentListElement(component, resize_width, invert_when_selected)); } // Utility function for making an input handler for "when the users presses A on this, do func". - inline void makeAcceptInputHandler(const std::function& func) + void makeAcceptInputHandler(const std::function& func) { input_handler = [func](InputConfig* config, Input input) -> bool { if (config->isMappedTo("a", input) && input.value != 0) { @@ -69,19 +70,23 @@ public: virtual std::vector getHelpPrompts() override; void onSizeChanged() override; - void onFocusGained() override; - void onFocusLost() override; + void onFocusGained() override { mFocused = true; } + void onFocusLost() override { mFocused = false; } bool moveCursor(int amt); - inline int getCursorId() const { return mCursor; } + int getCursorId() const { return mCursor; } float getTotalRowHeight() const; - inline float getRowHeight(int row) const { return getRowHeight(mEntries.at(row).data); } + float getRowHeight(int row) const { return getRowHeight(mEntries.at(row).data); } - inline void setCursorChangedCallback(const std::function& callback) - { mCursorChangedCallback = callback; }; - inline const std::function& getCursorChangedCallback() const - { return mCursorChangedCallback; }; + void setCursorChangedCallback(const std::function& callback) + { + mCursorChangedCallback = callback; + } + const std::function& getCursorChangedCallback() const + { + return mCursorChangedCallback; + } protected: void onCursorChanged(const CursorState& state) override; diff --git a/es-core/src/components/DateTimeComponent.cpp b/es-core/src/components/DateTimeComponent.cpp index 8d1913fd3..c1e670fce 100644 --- a/es-core/src/components/DateTimeComponent.cpp +++ b/es-core/src/components/DateTimeComponent.cpp @@ -10,28 +10,28 @@ #include "components/DateTimeComponent.h" -#include "utils/StringUtil.h" #include "Log.h" #include "Settings.h" +#include "utils/StringUtil.h" DateTimeComponent::DateTimeComponent(Window* window) - : TextComponent(window), mDisplayRelative(false) + : TextComponent(window) + , mDisplayRelative(false) { // ISO 8601 date format. setFormat("%Y-%m-%d"); } -DateTimeComponent::DateTimeComponent( - Window* window, - const std::string& text, - const std::shared_ptr& font, - unsigned int color, - Alignment align, - Vector3f pos, - Vector2f size, - unsigned int bgcolor) - : TextComponent(window, text, font, color, align, pos, size, bgcolor), - mDisplayRelative(false) +DateTimeComponent::DateTimeComponent(Window* window, + const std::string& text, + const std::shared_ptr& font, + unsigned int color, + Alignment align, + Vector3f pos, + Vector2f size, + unsigned int bgcolor) + : TextComponent(window, text, font, color, align, pos, size, bgcolor) + , mDisplayRelative(false) { // ISO 8601 date format. setFormat("%Y-%m-%d"); @@ -45,6 +45,7 @@ void DateTimeComponent::setValue(const std::string& val) std::string DateTimeComponent::getValue() const { + // Return time value as a string. return mTime; } @@ -79,17 +80,17 @@ std::string DateTimeComponent::getDisplayString() const std::string buf; if (dur.getDays() > 0) - buf = std::to_string(dur.getDays()) + " day" + - (dur.getDays() > 1 ? "s" : "") + " ago"; + buf = std::to_string(dur.getDays()) + " day" + // Line break. + (dur.getDays() > 1 ? "s" : "") + " ago"; else if (dur.getHours() > 0) - buf = std::to_string(dur.getHours()) + " hour" + - (dur.getHours() > 1 ? "s" : "") + " ago"; + buf = std::to_string(dur.getHours()) + " hour" + // Line break. + (dur.getHours() > 1 ? "s" : "") + " ago"; else if (dur.getMinutes() > 0) - buf = std::to_string(dur.getMinutes()) + " minute" + - (dur.getMinutes() > 1 ? "s" : "") + " ago"; + buf = std::to_string(dur.getMinutes()) + " minute" + // Line break. + (dur.getMinutes() > 1 ? "s" : "") + " ago"; else - buf = std::to_string(dur.getSeconds()) + " second" + - (dur.getSeconds() > 1 || dur.getSeconds() == 0 ? "s" : "") + " ago"; + buf = std::to_string(dur.getSeconds()) + " second" + // Line break. + (dur.getSeconds() > 1 || dur.getSeconds() == 0 ? "s" : "") + " ago"; return std::string(buf); } @@ -106,7 +107,9 @@ void DateTimeComponent::render(const Transform4x4f& parentTrans) } void DateTimeComponent::applyTheme(const std::shared_ptr& theme, - const std::string& view, const std::string& element, unsigned int properties) + const std::string& view, + const std::string& element, + unsigned int properties) { GuiComponent::applyTheme(theme, view, element, properties); @@ -140,7 +143,7 @@ void DateTimeComponent::applyTheme(const std::shared_ptr& theme, else if (str == "right") setHorizontalAlignment(ALIGN_RIGHT); else - LOG(LogError) << "Unknown text alignment string: " << str; + LOG(LogError) << "Unknown text alignment string: " << str; } if (properties & FORCE_UPPERCASE && elem->has("forceUppercase")) diff --git a/es-core/src/components/DateTimeComponent.h b/es-core/src/components/DateTimeComponent.h index 8d48f97fd..444484851 100644 --- a/es-core/src/components/DateTimeComponent.h +++ b/es-core/src/components/DateTimeComponent.h @@ -11,8 +11,8 @@ #ifndef ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H #define ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H -#include "utils/TimeUtil.h" #include "TextComponent.h" +#include "utils/TimeUtil.h" class ThemeData; @@ -21,15 +21,14 @@ class DateTimeComponent : public TextComponent { public: DateTimeComponent(Window* window); - DateTimeComponent( - Window* window, - const std::string& text, - const std::shared_ptr& font, - unsigned int color = 0x000000FF, - Alignment align = ALIGN_LEFT, - Vector3f pos = Vector3f::Zero(), - Vector2f size = Vector2f::Zero(), - unsigned int bgcolor = 0x00000000); + DateTimeComponent(Window* window, + const std::string& text, + const std::shared_ptr& font, + unsigned int color = 0x000000FF, + Alignment align = ALIGN_LEFT, + Vector3f pos = Vector3f::Zero(), + Vector2f size = Vector2f::Zero(), + unsigned int bgcolor = 0x00000000); void render(const Transform4x4f& parentTrans) override; @@ -39,8 +38,10 @@ public: void setFormat(const std::string& format); void setDisplayRelative(bool displayRelative); - virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) override; + virtual void applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) override; protected: void onTextChanged() override; diff --git a/es-core/src/components/DateTimeEditComponent.cpp b/es-core/src/components/DateTimeEditComponent.cpp index 119d256ee..ef91ed5f4 100644 --- a/es-core/src/components/DateTimeEditComponent.cpp +++ b/es-core/src/components/DateTimeEditComponent.cpp @@ -8,24 +8,21 @@ #include "components/DateTimeEditComponent.h" +#include "Settings.h" #include "resources/Font.h" #include "utils/StringUtil.h" -#include "Settings.h" -DateTimeEditComponent::DateTimeEditComponent( - Window* window, - bool alignRight, - DisplayMode dispMode) - : GuiComponent(window), - mEditing(false), - mEditIndex(0), - mDisplayMode(dispMode), - mRelativeUpdateAccumulator(0), - mColor(0x777777FF), - mFont(Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT)), - mUppercase(false), - mAutoSize(true), - mAlignRight(alignRight) +DateTimeEditComponent::DateTimeEditComponent(Window* window, bool alignRight, DisplayMode dispMode) + : GuiComponent(window) + , mEditing(false) + , mEditIndex(0) + , mDisplayMode(dispMode) + , mRelativeUpdateAccumulator(0) + , mColor(0x777777FF) + , mFont(Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT)) + , mUppercase(false) + , mAutoSize(true) + , mAlignRight(alignRight) { updateTextCache(); } @@ -68,8 +65,9 @@ bool DateTimeEditComponent::input(InputConfig* config, Input input) if (mEditing) { if (config->isMappedLike("lefttrigger", input) || - config->isMappedLike("righttrigger", input)) + config->isMappedLike("righttrigger", input)) { return true; + } if (config->isMappedTo("b", input)) { mEditing = false; @@ -101,23 +99,21 @@ bool DateTimeEditComponent::input(InputConfig* config, Input input) new_tm.tm_mon = 0; else if (new_tm.tm_mon < 0) new_tm.tm_mon = 11; - } else if (mEditIndex == 2) { const int days_in_month = - Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1); + Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1); new_tm.tm_mday += incDir; if (new_tm.tm_mday > days_in_month) new_tm.tm_mday = 1; else if (new_tm.tm_mday < 1) new_tm.tm_mday = days_in_month; - } // Validate day. const int days_in_month = - Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1); + Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1); if (new_tm.tm_mday > days_in_month) new_tm.tm_mday = days_in_month; @@ -185,11 +181,11 @@ void DateTimeEditComponent::render(const Transform4x4f& parentTrans) if (Settings::getInstance()->getBool("DebugText")) { Renderer::setMatrix(trans); if (mTextCache->metrics.size.x() > 0) { - Renderer::drawRect(0.0f, 0.0f - off.y(), - mSize.x() - off.x(), mSize.y(), 0x0000FF33, 0x0000FF33); + Renderer::drawRect(0.0f, 0.0f - off.y(), mSize.x() - off.x(), mSize.y(), 0x0000FF33, + 0x0000FF33); } Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(), - mTextCache->metrics.size.y(), 0x00000033, 0x00000033); + mTextCache->metrics.size.y(), 0x00000033, 0x00000033); } mTextCache->setColor((mColor & 0xFFFFFF00) | getOpacity()); @@ -198,8 +194,8 @@ void DateTimeEditComponent::render(const Transform4x4f& parentTrans) if (mEditing) { if (mEditIndex >= 0 && static_cast(mEditIndex) < mCursorBoxes.size()) Renderer::drawRect(mCursorBoxes[mEditIndex][0], mCursorBoxes[mEditIndex][1], - mCursorBoxes[mEditIndex][2], mCursorBoxes[mEditIndex][3], - 0x00000022, 0x00000022); + mCursorBoxes[mEditIndex][2], mCursorBoxes[mEditIndex][3], + 0x00000022, 0x00000022); } } } @@ -211,41 +207,24 @@ void DateTimeEditComponent::setValue(const std::string& val) updateTextCache(); } -std::string DateTimeEditComponent::getValue() const -{ - return mTime; -} - -DateTimeEditComponent::DisplayMode DateTimeEditComponent::getCurrentDisplayMode() const -{ -// if (mEditing) { -// if (mDisplayMode == DISP_RELATIVE_TO_NOW) { -// // TODO: if time component == 00:00:00, return DISP_DATE, else return DISP_DATE_TIME. -// return DISP_DATE; -// } -// } - - return mDisplayMode; -} - std::string DateTimeEditComponent::getDisplayString(DisplayMode mode) const { // ISO 8601 date format. std::string fmt; switch (mode) { - case DISP_DATE: { - if (mTime.getTime() == 0) - return "unknown"; - fmt = "%Y-%m-%d"; - break; - } - case DISP_DATE_TIME: { - if (mTime.getTime() == 0) - return "unknown"; - fmt = "%Y-%m-%d %H:%M:%S"; - break; - } - case DISP_RELATIVE_TO_NOW: { + case DISP_DATE: { + if (mTime.getTime() == 0) + return "unknown"; + fmt = "%Y-%m-%d"; + break; + } + case DISP_DATE_TIME: { + if (mTime.getTime() == 0) + return "unknown"; + fmt = "%Y-%m-%d %H:%M:%S"; + break; + } + case DISP_RELATIVE_TO_NOW: { // Relative time. if (mTime.getTime() == 0) return "never"; @@ -256,21 +235,22 @@ std::string DateTimeEditComponent::getDisplayString(DisplayMode mode) const std::string buf; if (dur.getDays() > 0) - buf = std::to_string(dur.getDays()) + " day" + - (dur.getDays() > 1 ? "s" : "") + " ago"; + buf = std::to_string(dur.getDays()) + // Line break. + " day" + (dur.getDays() > 1 ? "s" : "") + " ago"; else if (dur.getHours() > 0) - buf = std::to_string(dur.getHours()) + " hour" + - (dur.getHours() > 1 ? "s" : "") + " ago"; + buf = std::to_string(dur.getHours()) + // Line break. + " hour" + (dur.getHours() > 1 ? "s" : "") + " ago"; else if (dur.getMinutes() > 0) - buf = std::to_string(dur.getMinutes()) + " minute" + - (dur.getMinutes() > 1 ? "s" : "") + " ago"; + buf = std::to_string(dur.getMinutes()) + // Line break. + " minute" + (dur.getMinutes() > 1 ? "s" : "") + " ago"; else - buf = std::to_string(dur.getSeconds()) + " second" + - (dur.getSeconds() > 1 || dur.getSeconds() == 0 ? "s" : "") + " ago"; + buf = std::to_string(dur.getSeconds()) + // Line break. + " second" + (dur.getSeconds() > 1 || dur.getSeconds() == 0 ? "s" : "") + + " ago"; return std::string(buf); + break; } - break; } return Utils::Time::timeToString(mTime, fmt); @@ -296,16 +276,16 @@ void DateTimeEditComponent::updateTextCache() dispString = ""; } else { - dispString = mUppercase ? Utils::String::toUpper(getDisplayString(mode)) : - getDisplayString(mode); + dispString = + mUppercase ? Utils::String::toUpper(getDisplayString(mode)) : getDisplayString(mode); } std::shared_ptr font = getFont(); mTextCache = std::unique_ptr(font->buildTextCache(dispString, 0, 0, mColor)); if (mAutoSize) { mSize = mTextCache->metrics.size; - mAutoSize = false; + if (getParent()) getParent()->onSizeChanged(); } @@ -367,7 +347,9 @@ void DateTimeEditComponent::setUppercase(bool uppercase) } void DateTimeEditComponent::applyTheme(const std::shared_ptr& theme, - const std::string& view, const std::string& element, unsigned int properties) + const std::string& view, + const std::string& element, + unsigned int properties) { const ThemeData::ThemeElement* elem = theme->getElement(view, element, "datetime"); diff --git a/es-core/src/components/DateTimeEditComponent.h b/es-core/src/components/DateTimeEditComponent.h index cc79053b7..3273fe51b 100644 --- a/es-core/src/components/DateTimeEditComponent.h +++ b/es-core/src/components/DateTimeEditComponent.h @@ -9,8 +9,8 @@ #ifndef ES_CORE_COMPONENTS_DATE_TIME_EDIT_COMPONENT_H #define ES_CORE_COMPONENTS_DATE_TIME_EDIT_COMPONENT_H -#include "utils/TimeUtil.h" #include "GuiComponent.h" +#include "utils/TimeUtil.h" class TextCache; @@ -18,21 +18,22 @@ class TextCache; class DateTimeEditComponent : public GuiComponent { public: - enum DisplayMode{ - DISP_DATE, + enum DisplayMode { + DISP_DATE, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). DISP_DATE_TIME, DISP_RELATIVE_TO_NOW }; - DateTimeEditComponent(Window* window, bool alignRight = false, - DisplayMode dispMode = DISP_DATE); + DateTimeEditComponent(Window* window, + bool alignRight = false, + DisplayMode dispMode = DISP_DATE); void setValue(const std::string& val) override; - std::string getValue() const override; + std::string getValue() const override { return mTime; } bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; - unsigned int getColor() const override { return mColor; }; + unsigned int getColor() const override { return mColor; } void render(const Transform4x4f& parentTrans) override; void onSizeChanged() override; @@ -45,19 +46,21 @@ public: // The initial value is DISP_DATE. void setDisplayMode(DisplayMode mode); - // Text color. + // Text color. void setColor(unsigned int color) override; // Font to use. Default is Font::get(FONT_SIZE_MEDIUM). - void setOriginalColor(unsigned int color) override { mColorOriginalValue = color; }; - void setChangedColor(unsigned int color) override { mColorChangedValue = color; }; + void setOriginalColor(unsigned int color) override { mColorOriginalValue = color; } + void setChangedColor(unsigned int color) override { mColorChangedValue = color; } void setFont(std::shared_ptr font); // Force text to be uppercase when in DISP_RELATIVE_TO_NOW mode. void setUppercase(bool uppercase); - virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) override; + virtual void applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) override; virtual std::vector getHelpPrompts() override; @@ -65,7 +68,7 @@ private: std::shared_ptr getFont() const override; std::string getDisplayString(DisplayMode mode) const; - DisplayMode getCurrentDisplayMode() const; + DisplayMode getCurrentDisplayMode() const { return mDisplayMode; } void updateTextCache(); diff --git a/es-core/src/components/GridTileComponent.cpp b/es-core/src/components/GridTileComponent.cpp index 4fa6a829a..af5a87136 100644 --- a/es-core/src/components/GridTileComponent.cpp +++ b/es-core/src/components/GridTileComponent.cpp @@ -8,16 +8,17 @@ #include "GridTileComponent.h" +#include "ThemeData.h" #include "animations/LambdaAnimation.h" #include "resources/TextureResource.h" -#include "ThemeData.h" -GridTileComponent::GridTileComponent(Window* window) : - GuiComponent(window), mBackground(window, ":/graphics/frame.png") +GridTileComponent::GridTileComponent(Window* window) + : GuiComponent(window) + , mBackground(window, ":/graphics/frame.png") { mDefaultProperties.mSize = getDefaultTileSize(); mDefaultProperties.mPadding = Vector2f(16.0f * Renderer::getScreenWidthModifier(), - 16.0f * Renderer::getScreenHeightModifier()); + 16.0f * Renderer::getScreenHeightModifier()); mDefaultProperties.mImageColor = 0xAAAAAABB; // Attempting to use frame.svg instead causes quite severe performance problems. mDefaultProperties.mBackgroundImage = ":/graphics/frame.png"; @@ -76,7 +77,7 @@ void GridTileComponent::update(int deltaTime) void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTileProperties* properties) { Vector2f screen = Vector2f(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); if (elem->has("size")) properties->mSize = elem->get("size") * screen; @@ -106,10 +107,12 @@ void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTilePropert } void GridTileComponent::applyTheme(const std::shared_ptr& theme, - const std::string& view, const std::string& /*element*/, unsigned int /*properties*/) + const std::string& view, + const std::string& /*element*/, + unsigned int /*properties*/) { Vector2f screen = Vector2f(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); // Apply theme to the default gridtile. const ThemeData::ThemeElement* elem = theme->getElement(view, "default", "gridtile"); @@ -133,26 +136,23 @@ void GridTileComponent::applyTheme(const std::shared_ptr& theme, Vector2f GridTileComponent::getDefaultTileSize() { Vector2f screen = Vector2f(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); return screen * 0.22f; } Vector2f GridTileComponent::getSelectedTileSize() const { + // Return the tile size. return mDefaultProperties.mSize * 1.2f; } bool GridTileComponent::isSelected() const { + // Return whether the tile is selected. return mSelected; } -void GridTileComponent::reset() -{ - setImage(""); -} - void GridTileComponent::setImage(const std::string& path) { mImage->setImage(path); @@ -169,8 +169,10 @@ void GridTileComponent::setImage(const std::shared_ptr& texture resize(); } -void GridTileComponent::setSelected( - bool selected, bool allowAnimation, Vector3f* pPosition, bool force) +void GridTileComponent::setSelected(bool selected, + bool allowAnimation, + Vector3f* pPosition, + bool force) { if (mSelected == selected && !force) return; @@ -191,15 +193,18 @@ void GridTileComponent::setSelected( auto func = [this](float t) { t -= 1; // Cubic ease out. - float pct = Math::lerp(0, 1, t*t*t + 1); + float pct = Math::lerp(0, 1, t * t * t + 1); this->setSelectedZoom(pct); }; cancelAnimation(3); - setAnimation(new LambdaAnimation(func, 250), 0, [this] { - this->setSelectedZoom(1); - mAnimPosition = Vector3f(0, 0, 0); - }, false, 3); + setAnimation( + new LambdaAnimation(func, 250), 0, + [this] { + this->setSelectedZoom(1); + mAnimPosition = Vector3f(0, 0, 0); + }, + false, 3); } } else { @@ -213,15 +218,14 @@ void GridTileComponent::setSelected( this->setSelectedZoom(1); auto func = [this](float t) { - t -= 1; // Cubic ease out. - float pct = Math::lerp(0, 1, t*t*t + 1); + t -= 1.0f; // Cubic ease out. + float pct = Math::lerp(0, 1, t * t * t + 1.0f); this->setSelectedZoom(1.0f - pct); }; cancelAnimation(3); - setAnimation(new LambdaAnimation(func, 250), 0, [this] { - this->setSelectedZoom(0); - }, false, 3); + setAnimation( + new LambdaAnimation(func, 250), 0, [this] { this->setSelectedZoom(0); }, false, 3); } } } @@ -235,10 +239,7 @@ void GridTileComponent::setSelectedZoom(float percent) resize(); } -void GridTileComponent::setVisible(bool visible) -{ - mVisible = visible; -} +void GridTileComponent::setVisible(bool visible) { mVisible = visible; } void GridTileComponent::resize() { @@ -278,30 +279,31 @@ void GridTileComponent::calcCurrentProperties() if (mSelectedZoomPercent != 0.0f && mSelectedZoomPercent != 1.0f) { if (mDefaultProperties.mSize != mSelectedProperties.mSize) mCurrentProperties.mSize = mDefaultProperties.mSize * zoomPercentInverse + - mSelectedProperties.mSize * mSelectedZoomPercent; + mSelectedProperties.mSize * mSelectedZoomPercent; if (mDefaultProperties.mPadding != mSelectedProperties.mPadding) mCurrentProperties.mPadding = mDefaultProperties.mPadding * zoomPercentInverse + - mSelectedProperties.mPadding * mSelectedZoomPercent; + mSelectedProperties.mPadding * mSelectedZoomPercent; if (mDefaultProperties.mImageColor != mSelectedProperties.mImageColor) - mCurrentProperties.mImageColor = mixColors(mDefaultProperties.mImageColor, - mSelectedProperties.mImageColor, mSelectedZoomPercent); + mCurrentProperties.mImageColor = + mixColors(mDefaultProperties.mImageColor, mSelectedProperties.mImageColor, + mSelectedZoomPercent); if (mDefaultProperties.mBackgroundCornerSize != mSelectedProperties.mBackgroundCornerSize) - mCurrentProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize * - zoomPercentInverse + mSelectedProperties.mBackgroundCornerSize * - mSelectedZoomPercent; + mCurrentProperties.mBackgroundCornerSize = + mDefaultProperties.mBackgroundCornerSize * zoomPercentInverse + + mSelectedProperties.mBackgroundCornerSize * mSelectedZoomPercent; if (mDefaultProperties.mBackgroundCenterColor != mSelectedProperties.mBackgroundCenterColor) mCurrentProperties.mBackgroundCenterColor = - mixColors(mDefaultProperties.mBackgroundCenterColor, - mSelectedProperties.mBackgroundCenterColor, mSelectedZoomPercent); + mixColors(mDefaultProperties.mBackgroundCenterColor, + mSelectedProperties.mBackgroundCenterColor, mSelectedZoomPercent); if (mDefaultProperties.mBackgroundEdgeColor != mSelectedProperties.mBackgroundEdgeColor) mCurrentProperties.mBackgroundEdgeColor = - mixColors(mDefaultProperties.mBackgroundEdgeColor, - mSelectedProperties.mBackgroundEdgeColor, mSelectedZoomPercent); + mixColors(mDefaultProperties.mBackgroundEdgeColor, + mSelectedProperties.mBackgroundEdgeColor, mSelectedZoomPercent); } } diff --git a/es-core/src/components/GridTileComponent.h b/es-core/src/components/GridTileComponent.h index 2936645d5..6c0d600ee 100644 --- a/es-core/src/components/GridTileComponent.h +++ b/es-core/src/components/GridTileComponent.h @@ -28,8 +28,10 @@ public: GridTileComponent(Window* window); void render(const Transform4x4f& parentTrans) override; - virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) override; + virtual void applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) override; // Made this a static function because the ImageGridComponent needs to know the default tile // max size to calculate the grid dimension before it instantiates the GridTileComponents. @@ -37,12 +39,14 @@ public: Vector2f getSelectedTileSize() const; bool isSelected() const; - void reset(); + void reset() { setImage(""); } void setImage(const std::string& path); void setImage(const std::shared_ptr& texture); - void setSelected(bool selected, bool allowAnimation = true, - Vector3f* pPosition = nullptr, bool force=false); + void setSelected(bool selected, + bool allowAnimation = true, + Vector3f* pPosition = nullptr, + bool force = false); void setVisible(bool visible); void forceSize(Vector2f size, float selectedZoom); diff --git a/es-core/src/components/HelpComponent.cpp b/es-core/src/components/HelpComponent.cpp index b784e7326..dd2c526a3 100644 --- a/es-core/src/components/HelpComponent.cpp +++ b/es-core/src/components/HelpComponent.cpp @@ -8,20 +8,21 @@ #include "components/HelpComponent.h" +#include "Log.h" +#include "Settings.h" #include "components/ComponentGrid.h" #include "components/ImageComponent.h" #include "components/TextComponent.h" #include "resources/TextureResource.h" #include "utils/StringUtil.h" -#include "Log.h" -#include "Settings.h" #define ICON_TEXT_SPACING 8 // Space between [icon] and [text] (px). #define ENTRY_SPACING 16 // Space between [text] and next [icon] (px). static std::map sIconPathMap {}; -HelpComponent::HelpComponent(Window* window) : GuiComponent(window) +HelpComponent::HelpComponent(Window* window) + : GuiComponent(window) { assignIcons(); } @@ -113,7 +114,7 @@ void HelpComponent::updateGrid() std::shared_ptr& font = mStyle.font; mGrid = std::make_shared(mWindow, - Vector2i(static_cast(mPrompts.size()) * 4, 1)); + Vector2i(static_cast(mPrompts.size()) * 4, 1)); // [icon] [spacer1] [text] [spacer2] @@ -130,12 +131,12 @@ void HelpComponent::updateGrid() icon->setResize(0, height); icons.push_back(icon); - auto lbl = std::make_shared(mWindow, - Utils::String::toUpper(it->second), font, mStyle.textColor); + auto lbl = std::make_shared(mWindow, Utils::String::toUpper(it->second), + font, mStyle.textColor); labels.push_back(lbl); width += icon->getSize().x() + lbl->getSize().x() + - ((ICON_TEXT_SPACING + ENTRY_SPACING) * Renderer::getScreenWidthModifier()); + ((ICON_TEXT_SPACING + ENTRY_SPACING) * Renderer::getScreenWidthModifier()); } mGrid->setSize(width, height); @@ -143,8 +144,8 @@ void HelpComponent::updateGrid() for (unsigned int i = 0; i < icons.size(); i++) { const int col = i * 4; mGrid->setColWidthPerc(col, icons.at(i)->getSize().x() / width); - mGrid->setColWidthPerc(col + 1, (ICON_TEXT_SPACING * - Renderer::getScreenWidthModifier()) / width); + mGrid->setColWidthPerc(col + 1, + (ICON_TEXT_SPACING * Renderer::getScreenWidthModifier()) / width); mGrid->setColWidthPerc(col + 2, labels.at(i)->getSize().x() / width); mGrid->setEntry(icons.at(i), Vector2i(col, 0), false, false); @@ -167,13 +168,13 @@ std::shared_ptr HelpComponent::getIconTexture(const char* name) return nullptr; } if (!ResourceManager::getInstance()->fileExists(pathLookup->second)) { - LOG(LogError) << "Couldn't load help icon \"" << name << - "\" as the file \"" << pathLookup->second << "\" is missing"; + LOG(LogError) << "Couldn't load help icon \"" << name << "\" as the file \"" + << pathLookup->second << "\" is missing"; return nullptr; } std::shared_ptr tex = - TextureResource::get(pathLookup->second, false, false, false); + TextureResource::get(pathLookup->second, false, false, false); mIconCache[std::string(name)] = tex; return tex; } diff --git a/es-core/src/components/IList.h b/es-core/src/components/IList.h index fc37bbb63..1c907a1b3 100644 --- a/es-core/src/components/IList.h +++ b/es-core/src/components/IList.h @@ -9,17 +9,17 @@ #ifndef ES_CORE_COMPONENTS_ILIST_H #define ES_CORE_COMPONENTS_ILIST_H +#include "Window.h" #include "components/ImageComponent.h" #include "utils/StringUtil.h" -#include "Window.h" enum CursorState { - CURSOR_STOPPED, + CURSOR_STOPPED, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). CURSOR_SCROLLING }; enum ListLoopType { - LIST_ALWAYS_LOOP, + LIST_ALWAYS_LOOP, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). LIST_PAUSE_AT_END, LIST_NEVER_LOOP }; @@ -35,10 +35,11 @@ struct ScrollTierList { }; // Default scroll tiers. +// clang-format off const ScrollTier QUICK_SCROLL_TIERS[] = { - {500, 500}, - {1200, 114}, - {0, 16} + { 500, 500 }, + { 1200, 114 }, + { 0, 16 } }; const ScrollTierList LIST_SCROLL_STYLE_QUICK = { 3, @@ -46,17 +47,17 @@ const ScrollTierList LIST_SCROLL_STYLE_QUICK = { }; const ScrollTier SLOW_SCROLL_TIERS[] = { - {500, 500}, - {0, 200} + { 500, 500 }, + { 0, 200 } }; const ScrollTierList LIST_SCROLL_STYLE_SLOW = { 2, SLOW_SCROLL_TIERS }; +// clang-format on -template -class IList : public GuiComponent +template class IList : public GuiComponent { public: struct Entry { @@ -82,14 +83,13 @@ protected: Window* mWindow; public: - IList( - Window* window, - const ScrollTierList& tierList = LIST_SCROLL_STYLE_QUICK, - const ListLoopType& loopType = LIST_PAUSE_AT_END) - : GuiComponent(window), - mTierList(tierList), - mLoopType(loopType), - mWindow(window) + IList(Window* window, + const ScrollTierList& tierList = LIST_SCROLL_STYLE_QUICK, + const ListLoopType& loopType = LIST_PAUSE_AT_END) + : GuiComponent(window) + , mTierList(tierList) + , mLoopType(loopType) + , mWindow(window) { mCursor = 0; mScrollTier = 0; @@ -101,15 +101,9 @@ public: mTitleOverlayColor = 0xFFFFFF00; } - bool isScrolling() const - { - return (mScrollVelocity != 0 && mScrollTier > 0); - } + bool isScrolling() const { return (mScrollVelocity != 0 && mScrollTier > 0); } - int getScrollingVelocity() - { - return mScrollVelocity; - } + int getScrollingVelocity() { return mScrollVelocity; } void stopScrolling() { @@ -128,43 +122,43 @@ public: onCursorChanged(CURSOR_STOPPED); } - inline const std::string& getSelectedName() + const std::string& getSelectedName() { assert(size() > 0); return mEntries.at(mCursor).name; } - inline const UserData& getSelected() const + const UserData& getSelected() const { assert(size() > 0); return mEntries.at(mCursor).object; } - inline const UserData& getNext() const + const UserData& getNext() const { // If there is a next entry, then return it, otherwise return the current entry. if (mCursor + 1 < mEntries.size()) - return mEntries.at(mCursor+1).object; + return mEntries.at(mCursor + 1).object; else return mEntries.at(mCursor).object; } - inline const UserData& getPrevious() const + const UserData& getPrevious() const { // If there is a previous entry, then return it, otherwise return the current entry. if (mCursor != 0) - return mEntries.at(mCursor-1).object; + return mEntries.at(mCursor - 1).object; else return mEntries.at(mCursor).object; } - inline const UserData& getFirst() const + const UserData& getFirst() const { assert(size() > 0); return mEntries.front().object; } - inline const UserData& getLast() const + const UserData& getLast() const { assert(size() > 0); return mEntries.back().object; @@ -192,10 +186,7 @@ public: } // Entry management. - void add(const Entry& e) - { - mEntries.push_back(e); - } + void add(const Entry& e) { mEntries.push_back(e); } bool remove(const UserData& obj) { @@ -209,10 +200,7 @@ public: return false; } - inline int size() const - { - return static_cast(mEntries.size()); - } + int size() const { return static_cast(mEntries.size()); } protected: void remove(typename std::vector::const_iterator& it) @@ -241,7 +229,6 @@ protected: return true; } - bool listInput(int velocity) // A velocity of 0 = stop scrolling. { mScrollVelocity = velocity; @@ -284,8 +271,8 @@ protected: } // Should we go to the next scrolling tier? - while (mScrollTier < mTierList.count - 1 && mScrollTierAccumulator >= - mTierList.tiers[mScrollTier].length) { + while (mScrollTier < mTierList.count - 1 && + mScrollTierAccumulator >= mTierList.tiers[mScrollTier].length) { mScrollTierAccumulator -= mTierList.tiers[mScrollTier].length; mScrollTier++; } @@ -314,11 +301,11 @@ protected: favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst"); if (favoritesSorting && getSelected()->getFavorite()) { - #if defined(_MSC_VER) // MSVC compiler. +#if defined(_MSC_VER) // MSVC compiler. titleIndex = Utils::String::wideStringToString(L"\uF005"); - #else +#else titleIndex = "\uF005"; - #endif +#endif } else { titleIndex = getSelected()->getName(); diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index b53059b27..253d155b5 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -8,11 +8,11 @@ #include "components/ImageComponent.h" -#include "resources/TextureResource.h" -#include "utils/CImgUtil.h" #include "Log.h" #include "Settings.h" #include "ThemeData.h" +#include "resources/TextureResource.h" +#include "utils/CImgUtil.h" Vector2i ImageComponent::getTextureSize() const { @@ -27,34 +27,27 @@ Vector2f ImageComponent::getSize() const return GuiComponent::getSize() * (mBottomRightCrop - mTopLeftCrop); } -ImageComponent::ImageComponent( - Window* window, - bool forceLoad, - bool dynamic) - : GuiComponent(window), - mTargetIsMax(false), - mTargetIsMin(false), - mFlipX(false), - mFlipY(false), - mTargetSize(0, 0), - mColorShift(0xFFFFFFFF), - mColorShiftEnd(0xFFFFFFFF), - mColorGradientHorizontal(true), - mForceLoad(forceLoad), - mDynamic(dynamic), - mFadeOpacity(0), - mFading(false), - mRotateByTargetSize(false), - mTopLeftCrop(0.0f, 0.0f), - mBottomRightCrop(1.0f, 1.0f) +ImageComponent::ImageComponent(Window* window, bool forceLoad, bool dynamic) + : GuiComponent(window) + , mTargetIsMax(false) + , mTargetIsMin(false) + , mFlipX(false) + , mFlipY(false) + , mTargetSize(0, 0) + , mColorShift(0xFFFFFFFF) + , mColorShiftEnd(0xFFFFFFFF) + , mColorGradientHorizontal(true) + , mForceLoad(forceLoad) + , mDynamic(dynamic) + , mFadeOpacity(0) + , mFading(false) + , mRotateByTargetSize(false) + , mTopLeftCrop(0.0f, 0.0f) + , mBottomRightCrop(1.0f, 1.0f) { updateColors(); } -ImageComponent::~ImageComponent() -{ -} - void ImageComponent::resize() { if (!mTexture) @@ -91,8 +84,8 @@ void ImageComponent::resize() mSize[1] = floorf(mSize[1] * resizeScale.y()); // For SVG rasterization, always calculate width from rounded height (see comment // above). We need to make sure we're not creating an image larger than max size. - mSize[0] = std::min((mSize[1] / textureSize.y()) * textureSize.x(), - mTargetSize.x()); + mSize[0] = + std::min((mSize[1] / textureSize.y()) * textureSize.x(), mTargetSize.x()); } } else if (mTargetIsMin) { @@ -118,7 +111,6 @@ void ImageComponent::resize() // above). We need to make sure we're not creating an image smaller than min size. mSize[1] = std::max(floorf(mSize[1]), mTargetSize.y()); mSize[0] = std::max((mSize[1] / textureSize.y()) * textureSize.x(), mTargetSize.x()); - } else { // If both components are set, we just stretch. @@ -147,16 +139,6 @@ void ImageComponent::resize() onSizeChanged(); } -void ImageComponent::onSizeChanged() -{ - updateVertices(); -} - -void ImageComponent::setDefaultImage(std::string path) -{ - mDefaultPath = path; -} - void ImageComponent::setImage(std::string path, bool tile) { // Always load bundled graphic resources statically, unless mForceLoad has been set. @@ -218,16 +200,6 @@ void ImageComponent::setMinSize(float width, float height) resize(); } -Vector2f ImageComponent::getRotationSize() const -{ - return mRotateByTargetSize ? mTargetSize : mSize; -} - -void ImageComponent::setRotateByTargetSize(bool rotate) -{ - mRotateByTargetSize = rotate; -} - void ImageComponent::cropLeft(float percent) { assert(percent >= 0.0f && percent <= 1.0f); @@ -262,6 +234,7 @@ void ImageComponent::crop(float left, float top, float right, float bot) void ImageComponent::uncrop() { + // Remove any applied crop. crop(0, 0, 0, 0); } @@ -375,10 +348,12 @@ void ImageComponent::updateVertices() const float px = mTexture->isTiled() ? mSize.x() / getTextureSize().x() : 1.0f; const float py = mTexture->isTiled() ? mSize.y() / getTextureSize().y() : 1.0f; + // clang-format off mVertices[0] = { { topLeft.x(), topLeft.y() }, { mTopLeftCrop.x(), py - mTopLeftCrop.y() }, 0 }; mVertices[1] = { { topLeft.x(), bottomRight.y() }, { mTopLeftCrop.x(), 1.0f - mBottomRightCrop.y() }, 0 }; mVertices[2] = { { bottomRight.x(), topLeft.y() }, { mBottomRightCrop.x() * px, py - mTopLeftCrop.y() }, 0 }; mVertices[3] = { { bottomRight.x(), bottomRight.y() }, { mBottomRightCrop.x() * px, 1.0f - mBottomRightCrop.y() }, 0 }; + // clang-format on updateColors(); @@ -400,10 +375,11 @@ void ImageComponent::updateVertices() void ImageComponent::updateColors() { const float opacity = (mOpacity * (mFading ? mFadeOpacity / 255.0f : 1.0f)) / 255.0f; - const unsigned int color = Renderer::convertRGBAToABGR((mColorShift & 0xFFFFFF00) | - static_cast((mColorShift & 0xFF) * opacity)); - const unsigned int colorEnd = Renderer::convertRGBAToABGR((mColorShiftEnd & 0xFFFFFF00) | - static_cast((mColorShiftEnd & 0xFF) * opacity)); + const unsigned int color = Renderer::convertRGBAToABGR( + (mColorShift & 0xFFFFFF00) | static_cast((mColorShift & 0xFF) * opacity)); + const unsigned int colorEnd = + Renderer::convertRGBAToABGR((mColorShiftEnd & 0xFFFFFF00) | + static_cast((mColorShiftEnd & 0xFF) * opacity)); mVertices[0].col = color; mVertices[1].col = mColorGradientHorizontal ? colorEnd : color; @@ -423,7 +399,7 @@ void ImageComponent::render(const Transform4x4f& parentTrans) if (Settings::getInstance()->getBool("DebugImage")) { Vector2f targetSizePos = (mTargetSize - mSize) * mOrigin * -1; Renderer::drawRect(targetSizePos.x(), targetSizePos.y(), mTargetSize.x(), - mTargetSize.y(), 0xFF000033, 0xFF000033); + mTargetSize.y(), 0xFF000033, 0xFF000033); Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0xFF000033, 0xFF000033); } // An image with zero size would normally indicate a corrupt image file. @@ -433,12 +409,12 @@ void ImageComponent::render(const Transform4x4f& parentTrans) // texture is bound in this case but we want to handle a fade so it doesn't just // 'jump' in when it finally loads. fadeIn(mTexture->bind()); - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) if (mSaturation < 1.0) { mVertices[0].shaders = Renderer::SHADER_DESATURATE; mVertices[0].saturation = mSaturation; } - #endif +#endif Renderer::drawTriangleStrips(&mVertices[0], 4, trans); } else { @@ -448,8 +424,8 @@ void ImageComponent::render(const Transform4x4f& parentTrans) else { std::string textureFilePath = mTexture->getTextureFilePath(); if (textureFilePath != "") { - LOG(LogError) << "Image texture for file \"" << textureFilePath << - "\" has zero size"; + LOG(LogError) << "Image texture for file \"" << textureFilePath + << "\" has zero size"; } else { LOG(LogError) << "Image texture has zero size"; @@ -492,26 +468,24 @@ void ImageComponent::fadeIn(bool textureLoaded) } } -bool ImageComponent::hasImage() -{ - return (bool)mTexture; -} - -void ImageComponent::applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) +void ImageComponent::applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) { using namespace ThemeFlags; - GuiComponent::applyTheme(theme, view, element, (properties ^ ThemeFlags::SIZE) | - ((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0)); + GuiComponent::applyTheme(theme, view, element, + (properties ^ ThemeFlags::SIZE) | + ((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0)); const ThemeData::ThemeElement* elem = theme->getElement(view, element, "image"); if (!elem) return; Vector2f scale = getParent() ? getParent()->getSize() : - Vector2f(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + Vector2f(static_cast(Renderer::getScreenWidth()), + static_cast(Renderer::getScreenHeight())); if (properties & ThemeFlags::SIZE) { if (elem->has("size")) @@ -536,8 +510,8 @@ void ImageComponent::applyTheme(const std::shared_ptr& theme, const s if (elem->has("colorEnd")) setColorShiftEnd(elem->get("colorEnd")); if (elem->has("gradientType")) - setColorGradientHorizontal(!(elem-> - get("gradientType").compare("horizontal"))); + setColorGradientHorizontal( + !(elem->get("gradientType").compare("horizontal"))); } } diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index eb58a3e3c..68da21126 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -9,9 +9,9 @@ #ifndef ES_CORE_COMPONENTS_IMAGE_COMPONENT_H #define ES_CORE_COMPONENTS_IMAGE_COMPONENT_H +#include "GuiComponent.h" #include "math/Vector2i.h" #include "renderers/Renderer.h" -#include "GuiComponent.h" class TextureResource; @@ -19,9 +19,9 @@ class ImageComponent : public GuiComponent { public: ImageComponent(Window* window, bool forceLoad = false, bool dynamic = true); - virtual ~ImageComponent(); + virtual ~ImageComponent() {} - void setDefaultImage(std::string path); + void setDefaultImage(std::string path) { mDefaultPath = path; } // Loads the image at the given filepath. Will tile if tile is true (retrieves texture // as tiling, creates vertices accordingly). @@ -31,7 +31,7 @@ public: // Use an already existing texture. void setImage(const std::shared_ptr& texture); - void onSizeChanged() override; + void onSizeChanged() override { updateVertices(); } // Resize the image to fit this size. If one axis is zero, scale that axis to maintain // aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are @@ -39,18 +39,18 @@ public: // Can be set before or after an image is loaded. // setMaxSize() and setResize() are mutually exclusive. void setResize(float width, float height) override; - inline void setResize(const Vector2f& size) { setResize(size.x(), size.y()); } + void setResize(const Vector2f& size) { setResize(size.x(), size.y()); } // Resize the image to be as large as possible but fit within a box of this size. // Can be set before or after an image is loaded. // Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive. void setMaxSize(float width, float height); - inline void setMaxSize(const Vector2f& size) { setMaxSize(size.x(), size.y()); } + void setMaxSize(const Vector2f& size) { setMaxSize(size.x(), size.y()); } void setMinSize(float width, float height); - inline void setMinSize(const Vector2f& size) { setMinSize(size.x(), size.y()); } + void setMinSize(const Vector2f& size) { setMinSize(size.x(), size.y()); } - Vector2f getRotationSize() const override; + Vector2f getRotationSize() const override { return mRotateByTargetSize ? mTargetSize : mSize; } // Applied AFTER image positioning and sizing. // cropTop(0.2) will crop 20% of the top of the image. @@ -70,7 +70,7 @@ public: void setColorShiftEnd(unsigned int color); void setColorGradientHorizontal(bool horizontal); - unsigned int getColorShift() const override { return mColorShift; }; + unsigned int getColorShift() const override { return mColorShift; } void setOpacity(unsigned char opacity) override; void setSaturation(float saturation) override; @@ -79,7 +79,7 @@ public: void setFlipY(bool flip); // Mirror on the Y axis. // Flag indicating if rotation should be based on target size vs. actual size. - void setRotateByTargetSize(bool rotate); + void setRotateByTargetSize(bool rotate) { mRotateByTargetSize = rotate; } // Returns the size of the current texture, or (0, 0) if none is loaded. // May be different than drawn size (use getSize() for that). @@ -87,17 +87,18 @@ public: Vector2f getSize() const override; - bool hasImage(); + bool hasImage() { return static_cast(mTexture); } + std::shared_ptr getTexture() { return mTexture; } void render(const Transform4x4f& parentTrans) override; - virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) override; + virtual void applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) override; virtual std::vector getHelpPrompts() override; - std::shared_ptr getTexture() { return mTexture; }; - private: Vector2f mTargetSize; diff --git a/es-core/src/components/ImageGridComponent.h b/es-core/src/components/ImageGridComponent.h index b2d09913b..b3ac4c393 100644 --- a/es-core/src/components/ImageGridComponent.h +++ b/es-core/src/components/ImageGridComponent.h @@ -9,21 +9,21 @@ #ifndef ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H #define ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H +#include "GridTileComponent.h" +#include "Log.h" #include "animations/LambdaAnimation.h" #include "components/IList.h" #include "resources/TextureResource.h" -#include "GridTileComponent.h" -#include "Log.h" #define EXTRAITEMS 2 enum ScrollDirection { - SCROLL_VERTICALLY, + SCROLL_VERTICALLY, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). SCROLL_HORIZONTALLY }; enum ImageSource { - THUMBNAIL, + THUMBNAIL, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). IMAGE, MIXIMAGE, SCREENSHOT, @@ -36,8 +36,7 @@ struct ImageGridData { std::string texturePath; }; -template -class ImageGridComponent : public IList +template class ImageGridComponent : public IList { protected: using IList::mEntries; @@ -49,9 +48,6 @@ protected: using IList::mSize; using IList::mCursor; using IList::mWindow; - // The following change is required for compilation with Clang. - // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2070 - // using IList::Entry; using IList::IList; public: @@ -66,14 +62,18 @@ public: bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; void render(const Transform4x4f& parentTrans) override; - virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) override; + virtual void applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) override; void onSizeChanged() override; - inline void setCursorChangedCallback(const std::function& func) - { mCursorChangedCallback = func; } + void setCursorChangedCallback(const std::function& func) + { + mCursorChangedCallback = func; + } - ImageSource getImageSource() { return mImageSource; }; + ImageSource getImageSource() { return mImageSource; } protected: virtual void onCursorChanged(const CursorState& state) override; @@ -81,13 +81,14 @@ protected: private: // Tiles. void buildTiles(); - void updateTiles(bool ascending = true, bool allowAnimation = true, - bool updateSelectedState = true); + void updateTiles(bool ascending = true, + bool allowAnimation = true, + bool updateSelectedState = true); void updateTileAtPos(int tilePos, int imgPos, bool allowAnimation, bool updateSelectedState); void calcGridDimension(); bool isScrollLoop(); - bool isVertical() { return mScrollDirection == SCROLL_VERTICALLY; }; + bool isVertical() { return mScrollDirection == SCROLL_VERTICALLY; } // Images and entries. bool mEntriesDirty; @@ -121,11 +122,12 @@ private: std::function mCursorChangedCallback; }; -template -ImageGridComponent::ImageGridComponent(Window* window) : IList(window) +template +ImageGridComponent::ImageGridComponent(Window* window) + : IList(window) { Vector2f screen = Vector2f(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); mCamera = 0.0; mCameraDirection = 1.0; @@ -152,7 +154,7 @@ ImageGridComponent::ImageGridComponent(Window* window) : IList +template void ImageGridComponent::add(const std::string& name, const std::string& imagePath, const T& obj) { typename IList::Entry entry; @@ -164,8 +166,7 @@ void ImageGridComponent::add(const std::string& name, const std::string& imag mEntriesDirty = true; } -template -bool ImageGridComponent::input(InputConfig* config, Input input) +template bool ImageGridComponent::input(InputConfig* config, Input input) { if (input.value != 0) { int idx = isVertical() ? 0 : 1; @@ -190,15 +191,15 @@ bool ImageGridComponent::input(InputConfig* config, Input input) } else { if (config->isMappedLike("up", input) || config->isMappedLike("down", input) || - config->isMappedLike("left", input) || config->isMappedLike("right", input)) + config->isMappedLike("left", input) || config->isMappedLike("right", input)) { stopScrolling(); + } } return GuiComponent::input(config, input); } -template -void ImageGridComponent::update(int deltaTime) +template void ImageGridComponent::update(int deltaTime) { GuiComponent::update(deltaTime); listUpdate(deltaTime); @@ -207,16 +208,15 @@ void ImageGridComponent::update(int deltaTime) (*it)->update(deltaTime); } -template -void ImageGridComponent::render(const Transform4x4f& parentTrans) +template void ImageGridComponent::render(const Transform4x4f& parentTrans) { Transform4x4f trans = getTransform() * parentTrans; Transform4x4f tileTrans = trans; - float offsetX = isVertical() ? 0.0f : mCamera * mCameraDirection * - (mTileSize.x() + mMargin.x()); - float offsetY = isVertical() ? mCamera * mCameraDirection * - (mTileSize.y() + mMargin.y()) : 0.0f; + float offsetX = + isVertical() ? 0.0f : mCamera * mCameraDirection * (mTileSize.x() + mMargin.x()); + float offsetY = + isVertical() ? mCamera * mCameraDirection * (mTileSize.y() + mMargin.y()) : 0.0f; tileTrans.translate(Vector3f(offsetX, offsetY, 0.0)); @@ -230,9 +230,9 @@ void ImageGridComponent::render(const Transform4x4f& parentTrans) float scaleY = trans.r1().y(); Vector2i pos(static_cast(std::round(trans.translation()[0])), - static_cast(std::round(trans.translation()[1]))); + static_cast(std::round(trans.translation()[1]))); Vector2i size(static_cast(std::round(mSize.x() * scaleX)), - static_cast(std::round(mSize.y() * scaleY))); + static_cast(std::round(mSize.y() * scaleY))); Renderer::pushClipRect(pos, size); @@ -258,9 +258,11 @@ void ImageGridComponent::render(const Transform4x4f& parentTrans) GuiComponent::renderChildren(trans); } -template +template void ImageGridComponent::applyTheme(const std::shared_ptr& theme, - const std::string& view, const std::string& element, unsigned int properties) + const std::string& view, + const std::string& element, + unsigned int properties) { // Apply theme to GuiComponent but not the size property, which will be applied // at the end of this function. @@ -270,7 +272,7 @@ void ImageGridComponent::applyTheme(const std::shared_ptr& theme, mTheme = theme; Vector2f screen = Vector2f(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + static_cast(Renderer::getScreenHeight())); const ThemeData::ThemeElement* elem = theme->getElement(view, element, "imagegrid"); if (elem) { @@ -279,7 +281,7 @@ void ImageGridComponent::applyTheme(const std::shared_ptr& theme, if (elem->has("padding")) mPadding = elem->get("padding") * - Vector4f(screen.x(), screen.y(), screen.x(), screen.y()); + Vector4f(screen.x(), screen.y(), screen.x(), screen.y()); if (elem->has("autoLayout")) mAutoLayout = elem->get("autoLayout"); @@ -309,8 +311,8 @@ void ImageGridComponent::applyTheme(const std::shared_ptr& theme, } if (elem->has("scrollDirection")) - mScrollDirection = (ScrollDirection)(elem-> - get("scrollDirection") == "horizontal"); + mScrollDirection = + (ScrollDirection)(elem->get("scrollDirection") == "horizontal"); if (elem->has("centerSelection")) { mCenterSelection = (elem->get("centerSelection")); @@ -367,9 +369,8 @@ void ImageGridComponent::applyTheme(const std::shared_ptr& theme, // grid dimension, and then (re)build the tiles. elem = theme->getElement(view, "default", "gridtile"); - mTileSize = elem && elem->has("size") ? - elem->get("size") * screen : - GridTileComponent::getDefaultTileSize(); + mTileSize = elem && elem->has("size") ? elem->get("size") * screen : + GridTileComponent::getDefaultTileSize(); // Apply size property which will trigger a call to onSizeChanged() which will build the tiles. GuiComponent::applyTheme(theme, view, element, ThemeFlags::SIZE); @@ -379,15 +380,13 @@ void ImageGridComponent::applyTheme(const std::shared_ptr& theme, buildTiles(); } -template -void ImageGridComponent::onSizeChanged() +template void ImageGridComponent::onSizeChanged() { buildTiles(); updateTiles(); } -template -void ImageGridComponent::onCursorChanged(const CursorState& state) +template void ImageGridComponent::onCursorChanged(const CursorState& state) { if (mLastCursor == mCursor) { if (state == CURSOR_STOPPED && mCursorChangedCallback) @@ -470,7 +469,7 @@ void ImageGridComponent::onCursorChanged(const CursorState& state) mStartPosition = lastScroll * dimOpposite; } else if ((maxCentralCol != centralCol && col == firstVisibleCol + maxCentralCol) || - col == firstVisibleCol + centralCol) { + col == firstVisibleCol + centralCol) { if (col == firstVisibleCol + maxCentralCol) mStartPosition = (col - maxCentralCol) * dimOpposite; else @@ -507,23 +506,23 @@ void ImageGridComponent::onCursorChanged(const CursorState& state) if (!moveCamera) return; - t -= 1; // Cubic ease out. - float pct = Math::lerp(0, 1, t*t*t + 1); + t -= 1.0f; // Cubic ease out. + float pct = Math::lerp(0, 1.0f, t * t * t + 1.0f); t = startPos * (1.0f - pct) + endPos * pct; mCamera = t; }; reinterpret_cast(this)->setAnimation( - new LambdaAnimation(func, 250), 0, [this, direction] { - mCamera = 0; - updateTiles(direction, false); - }, false, 2); + new LambdaAnimation(func, 250), 0, + [this, direction] { + mCamera = 0; + updateTiles(direction, false); + }, + false, 2); } - // Create and position tiles (mTiles). -template -void ImageGridComponent::buildTiles() +template void ImageGridComponent::buildTiles() { mStartPosition = 0; mTiles.clear(); @@ -531,18 +530,20 @@ void ImageGridComponent::buildTiles() calcGridDimension(); if (mCenterSelection) { - int dimScrollable = (isVertical() ? mGridDimension.y() : - mGridDimension.x()) - 2 * EXTRAITEMS; + int dimScrollable = + (isVertical() ? mGridDimension.y() : mGridDimension.x()) - 2 * EXTRAITEMS; mStartPosition -= static_cast(floorf(dimScrollable / 2.0f)); } Vector2f tileDistance = mTileSize + mMargin; if (mAutoLayout.x() != 0.0f && mAutoLayout.y() != 0.0f) { - auto x = (mSize.x() - (mMargin.x() * (mAutoLayout.x() - 1.0f)) - mPadding.x() - - mPadding.z()) / static_cast(mAutoLayout.x()); - auto y = (mSize.y() - (mMargin.y() * (mAutoLayout.y() - 1.0f)) - mPadding.y() - - mPadding.w()) / static_cast(mAutoLayout.y()); + auto x = + (mSize.x() - (mMargin.x() * (mAutoLayout.x() - 1.0f)) - mPadding.x() - mPadding.z()) / + static_cast(mAutoLayout.x()); + auto y = + (mSize.y() - (mMargin.y() * (mAutoLayout.y() - 1.0f)) - mPadding.y() - mPadding.w()) / + static_cast(mAutoLayout.y()); mTileSize = Vector2f(x, y); tileDistance = mTileSize + mMargin; @@ -566,8 +567,8 @@ void ImageGridComponent::buildTiles() X = vert ? x : y - EXTRAITEMS; Y = vert ? y - EXTRAITEMS : x; - tile->setPosition(X * tileDistance.x() + startPosition.x(), Y * - tileDistance.y() + startPosition.y()); + tile->setPosition(X * tileDistance.x() + startPosition.x(), + Y * tileDistance.y() + startPosition.y()); tile->setOrigin(0.5f, 0.5f); tile->setImage(""); @@ -582,9 +583,10 @@ void ImageGridComponent::buildTiles() } } -template -void ImageGridComponent::updateTiles(bool ascending, bool allowAnimation, - bool updateSelectedState) +template +void ImageGridComponent::updateTiles(bool ascending, + bool allowAnimation, + bool updateSelectedState) { if (!mTiles.size()) return; @@ -631,9 +633,11 @@ void ImageGridComponent::updateTiles(bool ascending, bool allowAnimation, mLastCursor = mCursor; } -template -void ImageGridComponent::updateTileAtPos(int tilePos, int imgPos, - bool allowAnimation, bool updateSelectedState) +template +void ImageGridComponent::updateTileAtPos(int tilePos, + int imgPos, + bool allowAnimation, + bool updateSelectedState) { std::shared_ptr tile = mTiles.at(tilePos); @@ -647,7 +651,7 @@ void ImageGridComponent::updateTileAtPos(int tilePos, int imgPos, // If we have more tiles than we can display on screen, hide them. // Same for tiles out of the buffer. if (imgPos < 0 || imgPos >= size() || tilePos < 0 || - tilePos >= static_cast(mTiles.size())) { + tilePos >= static_cast(mTiles.size())) { if (updateSelectedState) tile->setSelected(false, allowAnimation); tile->reset(); @@ -680,14 +684,12 @@ void ImageGridComponent::updateTileAtPos(int tilePos, int imgPos, tile->setSelected(imgPos == mCursor, allowAnimation); } } - } } // Calculate how many tiles of size mTileSize we can fit in a grid of size mSize using // a margin size of mMargin. -template -void ImageGridComponent::calcGridDimension() +template void ImageGridComponent::calcGridDimension() { // grid_size = columns * tile_size + (columns - 1) * margin // <=> columns = (grid_size + margin) / (tile_size + margin) @@ -700,7 +702,7 @@ void ImageGridComponent::calcGridDimension() // Ceil y dim so we can display partial last row. mGridDimension = Vector2i(static_cast(gridDimension.x()), - static_cast(ceilf(gridDimension.y()))); + static_cast(ceilf(gridDimension.y()))); // Grid dimension validation. if (mGridDimension.x() < 1) { @@ -717,13 +719,13 @@ void ImageGridComponent::calcGridDimension() mGridDimension.x() += 2 * EXTRAITEMS; } -template -bool ImageGridComponent::isScrollLoop() { +template bool ImageGridComponent::isScrollLoop() +{ if (!mScrollLoop) return false; if (isVertical()) return (mGridDimension.x() * (mGridDimension.y() - 2 * EXTRAITEMS)) <= mEntries.size(); return (mGridDimension.y() * (mGridDimension.x() - 2 * EXTRAITEMS)) <= mEntries.size(); -}; +} #endif // ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H diff --git a/es-core/src/components/MenuComponent.cpp b/es-core/src/components/MenuComponent.cpp index 6360285f9..ce7258599 100644 --- a/es-core/src/components/MenuComponent.cpp +++ b/es-core/src/components/MenuComponent.cpp @@ -8,22 +8,21 @@ #include "components/MenuComponent.h" -#include "components/ButtonComponent.h" #include "Settings.h" +#include "components/ButtonComponent.h" #define BUTTON_GRID_VERT_PADDING 32.0f #define BUTTON_GRID_HORIZ_PADDING 10.0f #define TITLE_HEIGHT (mTitle->getFont()->getLetterHeight() + TITLE_VERT_PADDING) -MenuComponent::MenuComponent( - Window* window, - std::string title, - const std::shared_ptr& titleFont) - : GuiComponent(window), - mBackground(window), - mGrid(window, Vector2i(1, 3)), - mNeedsSaving(false) +MenuComponent::MenuComponent(Window* window, + std::string title, + const std::shared_ptr& titleFont) + : GuiComponent(window) + , mBackground(window) + , mGrid(window, Vector2i(1, 3)) + , mNeedsSaving(false) { addChild(&mBackground); addChild(&mGrid); @@ -49,6 +48,7 @@ MenuComponent::MenuComponent( MenuComponent::~MenuComponent() { + // Save when destroyed. save(); } @@ -75,15 +75,15 @@ void MenuComponent::setTitle(std::string title, const std::shared_ptr& fon float MenuComponent::getButtonGridHeight() const { return (mButtonGrid ? mButtonGrid->getSize().y() : - Font::get(FONT_SIZE_MEDIUM)->getHeight() + - (BUTTON_GRID_VERT_PADDING * Renderer::getScreenHeightModifier())); + Font::get(FONT_SIZE_MEDIUM)->getHeight() + + (BUTTON_GRID_VERT_PADDING * Renderer::getScreenHeightModifier())); } void MenuComponent::updateSize() { const float maxHeight = Renderer::getScreenHeight() * 0.80f; float height = TITLE_HEIGHT + mList->getTotalRowHeight() + getButtonGridHeight() + - (2.0f * Renderer::getScreenHeightModifier()); + (2.0f * Renderer::getScreenHeightModifier()); if (height > maxHeight) { height = TITLE_HEIGHT + getButtonGridHeight(); int i = 0; @@ -98,14 +98,15 @@ void MenuComponent::updateSize() } } - float width = static_cast(std::min(static_cast(Renderer::getScreenHeight() * - 1.05f), static_cast(Renderer::getScreenWidth() * 0.90f))); + float width = + static_cast(std::min(static_cast(Renderer::getScreenHeight() * 1.05f), + static_cast(Renderer::getScreenWidth() * 0.90f))); setSize(width, height); } void MenuComponent::onSizeChanged() { - mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); // Update grid row/column sizes. mGrid.setRowHeightPerc(0, TITLE_HEIGHT / mSize.y()); @@ -115,10 +116,11 @@ void MenuComponent::onSizeChanged() } void MenuComponent::addButton(const std::string& name, - const std::string& helpText, const std::function& callback) + const std::string& helpText, + const std::function& callback) { - mButtons.push_back(std::make_shared - (mWindow,Utils::String::toUpper(name), helpText, callback)); + mButtons.push_back(std::make_shared(mWindow, Utils::String::toUpper(name), + helpText, callback)); updateGrid(); updateSize(); } @@ -136,30 +138,28 @@ void MenuComponent::updateGrid() } } -std::vector MenuComponent::getHelpPrompts() +std::shared_ptr makeButtonGrid( + Window* window, const std::vector>& buttons) { - return mGrid.getHelpPrompts(); -} - -std::shared_ptr makeButtonGrid(Window* window, - const std::vector>& buttons) -{ - std::shared_ptr buttonGrid = std::make_shared - (window, Vector2i(static_cast(buttons.size()), 2)); + std::shared_ptr buttonGrid = + std::make_shared(window, Vector2i(static_cast(buttons.size()), 2)); // Initialize to padding. - float buttonGridWidth = BUTTON_GRID_HORIZ_PADDING * - Renderer::getScreenWidthModifier() * buttons.size(); + float buttonGridWidth = + BUTTON_GRID_HORIZ_PADDING * Renderer::getScreenWidthModifier() * buttons.size(); for (int i = 0; i < static_cast(buttons.size()); i++) { buttonGrid->setEntry(buttons.at(i), Vector2i(i, 0), true, false); buttonGridWidth += buttons.at(i)->getSize().x(); } for (unsigned int i = 0; i < buttons.size(); i++) - buttonGrid->setColWidthPerc(i, (buttons.at(i)->getSize().x() + - BUTTON_GRID_HORIZ_PADDING * Renderer::getScreenWidthModifier()) / buttonGridWidth); + buttonGrid->setColWidthPerc( + i, (buttons.at(i)->getSize().x() + + BUTTON_GRID_HORIZ_PADDING * Renderer::getScreenWidthModifier()) / + buttonGridWidth); - buttonGrid->setSize(buttonGridWidth, buttons.at(0)->getSize().y() + - (BUTTON_GRID_VERT_PADDING * Renderer::getScreenHeightModifier()) + 2); + buttonGrid->setSize(buttonGridWidth, + buttons.at(0)->getSize().y() + + (BUTTON_GRID_VERT_PADDING * Renderer::getScreenHeightModifier()) + 2); // Spacer row to deal with dropshadow to make buttons look centered. buttonGrid->setRowHeightPerc(1, 2 / buttonGrid->getSize().y()); diff --git a/es-core/src/components/MenuComponent.h b/es-core/src/components/MenuComponent.h index e73ae37c5..b0d8e927f 100644 --- a/es-core/src/components/MenuComponent.h +++ b/es-core/src/components/MenuComponent.h @@ -17,20 +17,21 @@ #include +#define TITLE_VERT_PADDING (Renderer::getScreenHeight() * 0.0637f) + class ButtonComponent; class ImageComponent; -std::shared_ptr makeButtonGrid(Window* window, - const std::vector>& buttons); +std::shared_ptr makeButtonGrid( + Window* window, const std::vector>& buttons); std::shared_ptr makeArrow(Window* window); -#define TITLE_VERT_PADDING (Renderer::getScreenHeight() * 0.0637f) - class MenuComponent : public GuiComponent { public: - MenuComponent(Window* window, std::string title, - const std::shared_ptr& titleFont = Font::get(FONT_SIZE_LARGE)); + MenuComponent(Window* window, + std::string title, + const std::shared_ptr& titleFont = Font::get(FONT_SIZE_LARGE)); virtual ~MenuComponent(); void save(); @@ -38,31 +39,42 @@ public: void setNeedsSaving() { mNeedsSaving = true; } - inline void addRow(const ComponentListRow& row, bool setCursorHere = false) - { mList->addRow(row, setCursorHere); updateSize(); } + void addRow(const ComponentListRow& row, bool setCursorHere = false) + { + mList->addRow(row, setCursorHere); + updateSize(); + } - inline void addWithLabel(const std::string& label, const std::shared_ptr& comp, - bool setCursorHere = false, bool invert_when_selected = true) + void addWithLabel(const std::string& label, + const std::shared_ptr& comp, + bool setCursorHere = false, + bool invert_when_selected = true) { ComponentListRow row; - row.addElement(std::make_shared(mWindow, - Utils::String::toUpper(label), Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(std::make_shared(mWindow, Utils::String::toUpper(label), + Font::get(FONT_SIZE_MEDIUM), 0x777777FF), + true); row.addElement(comp, false, invert_when_selected); addRow(row, setCursorHere); } - inline void addSaveFunc(const std::function& func) { mSaveFuncs.push_back(func); } + void addSaveFunc(const std::function& func) { mSaveFuncs.push_back(func); } - void addButton(const std::string& label, const std::string& helpText, - const std::function& callback); + void addButton(const std::string& label, + const std::string& helpText, + const std::function& callback); void setTitle(std::string title, const std::shared_ptr& font); void setCursorToFirstListEntry() { mList->moveCursor(-mList->getCursorId()); } - inline void setCursorToList() { mGrid.setCursorTo(mList); } - inline void setCursorToButtons() { assert(mButtonGrid); mGrid.setCursorTo(mButtonGrid); } + void setCursorToList() { mGrid.setCursorTo(mList); } + void setCursorToButtons() + { + assert(mButtonGrid); + mGrid.setCursorTo(mButtonGrid); + } - virtual std::vector getHelpPrompts() override; + virtual std::vector getHelpPrompts() override { return mGrid.getHelpPrompts(); } private: void updateSize(); diff --git a/es-core/src/components/NinePatchComponent.cpp b/es-core/src/components/NinePatchComponent.cpp index f8df08ef3..f14915086 100644 --- a/es-core/src/components/NinePatchComponent.cpp +++ b/es-core/src/components/NinePatchComponent.cpp @@ -8,21 +8,20 @@ #include "components/NinePatchComponent.h" -#include "resources/TextureResource.h" #include "Log.h" #include "ThemeData.h" +#include "resources/TextureResource.h" -NinePatchComponent::NinePatchComponent( - Window* window, - const std::string& path, - unsigned int edgeColor, - unsigned int centerColor) - : GuiComponent(window), - mCornerSize(16.0f, 16.0f), - mEdgeColor(edgeColor), - mCenterColor(centerColor), - mPath(path), - mVertices(nullptr) +NinePatchComponent::NinePatchComponent(Window* window, + const std::string& path, + unsigned int edgeColor, + unsigned int centerColor) + : GuiComponent(window) + , mCornerSize(16.0f, 16.0f) + , mEdgeColor(edgeColor) + , mCenterColor(centerColor) + , mPath(path) + , mVertices(nullptr) { if (!mPath.empty()) buildVertices(); @@ -42,8 +41,8 @@ void NinePatchComponent::updateColors() for (int i = 0; i < 6 * 9; i++) mVertices[i].col = edgeColor; - for (int i = 6*4; i < 6; i++) - mVertices[(6*4)+i].col = centerColor; + for (int i = 6 * 4; i < 6; i++) + mVertices[(6 * 4) + i].col = centerColor; } void NinePatchComponent::buildVertices() @@ -72,19 +71,21 @@ void NinePatchComponent::buildVertices() mVertices = new Renderer::Vertex[6 * 9]; texSize = Vector2f(static_cast(mTexture->getSize().x()), - static_cast(mTexture->getSize().y())); + static_cast(mTexture->getSize().y())); - const float imgSizeX[3] = { mCornerSize.x(), mSize.x() - mCornerSize.x() * 2, mCornerSize.x()}; - const float imgSizeY[3] = { mCornerSize.y(), mSize.y() - mCornerSize.y() * 2, mCornerSize.y()}; - const float imgPosX[3] = { 0, imgSizeX[0], imgSizeX[0] + imgSizeX[1]}; - const float imgPosY[3] = { 0, imgSizeY[0], imgSizeY[0] + imgSizeY[1]}; + // clang-format off + const float imgSizeX[3] = { mCornerSize.x(), mSize.x() - mCornerSize.x() * 2.0f, mCornerSize.x() }; + const float imgSizeY[3] = { mCornerSize.y(), mSize.y() - mCornerSize.y() * 2.0f, mCornerSize.y() }; + const float imgPosX[3] = { 0, imgSizeX[0], imgSizeX[0] + imgSizeX[1] }; + const float imgPosY[3] = { 0, imgSizeY[0], imgSizeY[0] + imgSizeY[1] }; // The "1 +" in posY and "-" in sizeY is to deal with texture coordinates having a bottom // left corner origin vs. verticies having a top left origin. - const float texSizeX[3] = { mCornerSize.x() / texSize.x(), (texSize.x() - mCornerSize.x() * 2) / texSize.x(), mCornerSize.x() / texSize.x() }; - const float texSizeY[3] = { -mCornerSize.y() / texSize.y(), -(texSize.y() - mCornerSize.y() * 2) / texSize.y(), -mCornerSize.y() / texSize.y() }; - const float texPosX[3] = { 0, texSizeX[0], texSizeX[0] + texSizeX[1] }; - const float texPosY[3] = { 1, 1 + texSizeY[0], 1 + texSizeY[0] + texSizeY[1] }; + const float texSizeX[3] = { mCornerSize.x() / texSize.x(), (texSize.x() - mCornerSize.x() * 2.0f) / texSize.x(), mCornerSize.x() / texSize.x() }; + const float texSizeY[3] = { -mCornerSize.y() / texSize.y(), -(texSize.y() - mCornerSize.y() * 2.0f) / texSize.y(), -mCornerSize.y() / texSize.y() }; + const float texPosX[3] = { 0.0f, texSizeX[0], texSizeX[0] + texSizeX[1] }; + const float texPosY[3] = { 1.0f, 1.0f + texSizeY[0], 1.0f + texSizeY[0] + texSizeY[1] }; + // clang-format on int v = 0; @@ -96,10 +97,12 @@ void NinePatchComponent::buildVertices() const Vector2f texPos = Vector2f(texPosX[sliceX], texPosY[sliceY]); const Vector2f texSize = Vector2f(texSizeX[sliceX], texSizeY[sliceY]); + // clang-format off mVertices[v + 1] = { { imgPos.x() , imgPos.y() }, { texPos.x(), texPos.y() }, 0 }; mVertices[v + 2] = { { imgPos.x() , imgPos.y() + imgSize.y() }, { texPos.x(), texPos.y() + texSize.y() }, 0 }; mVertices[v + 3] = { { imgPos.x() + imgSize.x(), imgPos.y() }, { texPos.x() + texSize.x(), texPos.y() }, 0 }; mVertices[v + 4] = { { imgPos.x() + imgSize.x(), imgPos.y() + imgSize.y() }, { texPos.x() + texSize.x(), texPos.y() + texSize.y() }, 0 }; + // clang-format on // Round vertices. for (int i = 1; i < 5; i++) @@ -141,10 +144,7 @@ void NinePatchComponent::render(const Transform4x4f& parentTrans) renderChildren(trans); } -void NinePatchComponent::onSizeChanged() -{ - buildVertices(); -} +void NinePatchComponent::onSizeChanged() { buildVertices(); } void NinePatchComponent::fitTo(Vector2f size, Vector3f position, Vector2f padding) { @@ -176,7 +176,9 @@ void NinePatchComponent::setCenterColor(unsigned int centerColor) } void NinePatchComponent::applyTheme(const std::shared_ptr& theme, - const std::string& view, const std::string& element, unsigned int properties) + const std::string& view, + const std::string& element, + unsigned int properties) { GuiComponent::applyTheme(theme, view, element, properties); diff --git a/es-core/src/components/NinePatchComponent.h b/es-core/src/components/NinePatchComponent.h index c7ff33145..9c0191ded 100644 --- a/es-core/src/components/NinePatchComponent.h +++ b/es-core/src/components/NinePatchComponent.h @@ -9,8 +9,8 @@ #ifndef ES_CORE_COMPONENTS_NINE_PATCH_COMPONENT_H #define ES_CORE_COMPONENTS_NINE_PATCH_COMPONENT_H -#include "renderers/Renderer.h" #include "GuiComponent.h" +#include "renderers/Renderer.h" class TextureResource; @@ -29,16 +29,19 @@ class TextureResource; class NinePatchComponent : public GuiComponent { public: - NinePatchComponent(Window* window, const std::string& path = "", - unsigned int edgeColor = 0xFFFFFFFF, unsigned int centerColor = 0xFFFFFFFF); + NinePatchComponent(Window* window, + const std::string& path = "", + unsigned int edgeColor = 0xFFFFFFFF, + unsigned int centerColor = 0xFFFFFFFF); virtual ~NinePatchComponent(); void render(const Transform4x4f& parentTrans) override; void onSizeChanged() override; - void fitTo(Vector2f size, Vector3f position = Vector3f::Zero(), - Vector2f padding = Vector2f::Zero()); + void fitTo(Vector2f size, + Vector3f position = Vector3f::Zero(), + Vector2f padding = Vector2f::Zero()); void setImagePath(const std::string& path); // Apply a color shift to the "edge" parts of the ninepatch. @@ -46,15 +49,17 @@ public: // Apply a color shift to the "center" part of the ninepatch. void setCenterColor(unsigned int centerColor); - virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) override; + virtual void applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) override; - inline const Vector2f& getCornerSize() const { return mCornerSize; }; - inline void setCornerSize(const Vector2f& size) + const Vector2f& getCornerSize() const { return mCornerSize; } + void setCornerSize(const Vector2f& size) { mCornerSize = size; buildVertices(); - }; + } private: void buildVertices(); diff --git a/es-core/src/components/OptionListComponent.h b/es-core/src/components/OptionListComponent.h index c608a6e55..223ec141f 100644 --- a/es-core/src/components/OptionListComponent.h +++ b/es-core/src/components/OptionListComponent.h @@ -21,28 +21,20 @@ // Used to display a list of options. // Can select one or multiple options. - -// if !multiSelect -// * <- curEntry -> -// Always -// * press a -> open full list. - -template -class OptionListComponent : public GuiComponent +template class OptionListComponent : public GuiComponent { public: - OptionListComponent( - Window* window, - const HelpStyle& helpstyle, - const std::string& name, - bool multiSelect = false) - : GuiComponent(window), - mHelpStyle(helpstyle), - mMultiSelect(multiSelect), - mName(name), - mText(window), - mLeftArrow(window), - mRightArrow(window) + OptionListComponent(Window* window, + const HelpStyle& helpstyle, + const std::string& name, + bool multiSelect = false) + : GuiComponent(window) + , mHelpStyle(helpstyle) + , mMultiSelect(multiSelect) + , mName(name) + , mText(window) + , mLeftArrow(window) + , mRightArrow(window) { auto font = Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT); mText.setFont(font); @@ -79,15 +71,15 @@ public: LOG(LogWarning) << "OptionListComponent too narrow"; } - mText.setSize(mSize.x() - mLeftArrow.getSize().x() - - mRightArrow.getSize().x(), mText.getFont()->getHeight()); + mText.setSize(mSize.x() - mLeftArrow.getSize().x() - mRightArrow.getSize().x(), + mText.getFont()->getHeight()); // Position. mLeftArrow.setPosition(0, (mSize.y() - mLeftArrow.getSize().y()) / 2); mText.setPosition(mLeftArrow.getPosition().x() + mLeftArrow.getSize().x(), - (mSize.y() - mText.getSize().y()) / 2); + (mSize.y() - mText.getSize().y()) / 2); mRightArrow.setPosition(mText.getPosition().x() + mText.getSize().x(), - (mSize.y() - mRightArrow.getSize().y()) / 2); + (mSize.y() - mRightArrow.getSize().y()) / 2); } bool input(InputConfig* config, Input input) override @@ -115,7 +107,6 @@ public: mEntries.at(next).selected = true; onSelectedChanged(); return true; - } else if (config->isMappedLike("right", input)) { // Ignore input if the component has been disabled. @@ -205,7 +196,7 @@ public: void sortEntriesByName() { std::sort(std::begin(mEntries), std::end(mEntries), - [](OptionListData a, OptionListData b) { return a.name < b.name; }); + [](OptionListData a, OptionListData b) { return a.name < b.name; }); } unsigned int getSelectedId() @@ -217,11 +208,11 @@ public: } LOG(LogWarning) << "OptionListComponent::getSelectedId() - " - "no selected element found, defaulting to 0"; + "no selected element found, defaulting to 0"; return 0; } - HelpStyle getHelpStyle() override { return mHelpStyle; }; + HelpStyle getHelpStyle() override { return mHelpStyle; } private: struct OptionListData { @@ -232,10 +223,7 @@ private: HelpStyle mHelpStyle; - void open() - { - mWindow->pushGui(new OptionListPopup(mWindow, getHelpStyle(), this, mName)); - } + void open() { mWindow->pushGui(new OptionListPopup(mWindow, getHelpStyle(), this, mName)); } void onSelectedChanged() { @@ -246,7 +234,8 @@ private: mText.setText(ss.str()); mText.setSize(0, mText.getSize().y()); setSize(mText.getSize().x() + mRightArrow.getSize().x() + - 24 * Renderer::getScreenWidthModifier(), mText.getSize().y()); + 24 * Renderer::getScreenWidthModifier(), + mText.getSize().y()); if (mParent) // Hack since there's no "on child size changed" callback. mParent->onSizeChanged(); } @@ -257,8 +246,9 @@ private: mText.setText(Utils::String::toUpper(it->name)); mText.setSize(0, mText.getSize().y()); setSize(mText.getSize().x() + mLeftArrow.getSize().x() + - mRightArrow.getSize().x() + - 24 * Renderer::getScreenWidthModifier(), mText.getSize().y()); + mRightArrow.getSize().x() + + 24.0f * Renderer::getScreenWidthModifier(), + mText.getSize().y()); if (mParent) // Hack since there's no "on child size changed" callback. mParent->onSizeChanged(); break; @@ -290,15 +280,14 @@ private: class OptionListPopup : public GuiComponent { public: - OptionListPopup( - Window* window, - const HelpStyle& helpstyle, - OptionListComponent* parent, - const std::string& title) - : GuiComponent(window), - mHelpStyle(helpstyle), - mMenu(window, title.c_str()), - mParent(parent) + OptionListPopup(Window* window, + const HelpStyle& helpstyle, + OptionListComponent* parent, + const std::string& title) + : GuiComponent(window) + , mHelpStyle(helpstyle) + , mMenu(window, title.c_str()) + , mParent(parent) { auto font = Font::get(FONT_SIZE_MEDIUM); ComponentListRow row; @@ -308,8 +297,9 @@ private: for (auto it = mParent->mEntries.begin(); it != mParent->mEntries.end(); it++) { row.elements.clear(); - row.addElement(std::make_shared - (mWindow, Utils::String::toUpper(it->name), font, 0x777777FF), true); + row.addElement(std::make_shared( + mWindow, Utils::String::toUpper(it->name), font, 0x777777FF), + true); OptionListData& e = *it; @@ -367,7 +357,7 @@ private: } mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2.0f, - Renderer::getScreenHeight() * 0.13f); + Renderer::getScreenHeight() * 0.13f); addChild(&mMenu); } @@ -389,7 +379,7 @@ private: return prompts; } - HelpStyle getHelpStyle() override { return mHelpStyle; }; + HelpStyle getHelpStyle() override { return mHelpStyle; } private: MenuComponent mMenu; diff --git a/es-core/src/components/RatingComponent.cpp b/es-core/src/components/RatingComponent.cpp index 5710f9404..a2f1c8aba 100644 --- a/es-core/src/components/RatingComponent.cpp +++ b/es-core/src/components/RatingComponent.cpp @@ -9,25 +9,23 @@ #include "components/RatingComponent.h" -#include "resources/TextureResource.h" #include "Settings.h" #include "ThemeData.h" +#include "resources/TextureResource.h" -RatingComponent::RatingComponent( - Window* window, - bool colorizeChanges) - : GuiComponent(window), - mColorShift(DEFAULT_COLORSHIFT), - mColorShiftEnd(DEFAULT_COLORSHIFT), - mUnfilledColor(DEFAULT_COLORSHIFT), - mColorizeChanges(colorizeChanges), - mColorOriginalValue(DEFAULT_COLORSHIFT), - mColorChangedValue(DEFAULT_COLORSHIFT) +RatingComponent::RatingComponent(Window* window, bool colorizeChanges) + : GuiComponent(window) + , mColorShift(DEFAULT_COLORSHIFT) + , mColorShiftEnd(DEFAULT_COLORSHIFT) + , mUnfilledColor(DEFAULT_COLORSHIFT) + , mColorizeChanges(colorizeChanges) + , mColorOriginalValue(DEFAULT_COLORSHIFT) + , mColorChangedValue(DEFAULT_COLORSHIFT) { mFilledTexture = TextureResource::get(":/graphics/star_filled.svg", true); mUnfilledTexture = TextureResource::get(":/graphics/star_unfilled.svg", true); mValue = 0.5f; - mSize = Vector2f(64 * NUM_RATING_STARS, 64); + mSize = Vector2f(64.0f * NUM_RATING_STARS, 64.0f); updateVertices(); updateColors(); } @@ -39,13 +37,13 @@ void RatingComponent::setValue(const std::string& value) } else { // Round up to the closest .1 value, i.e. to the closest half-icon. - mValue = ceilf(stof(value) / 0.1f) / 10; - mOriginalValue = static_cast(mValue * 10); + mValue = ceilf(stof(value) / 0.1f) / 10.0f; + mOriginalValue = static_cast(mValue * 10.0f); // If the argument to colorize the rating icons has been passed, set the // color shift accordingly. if (mColorizeChanges) { - if (static_cast(mValue * 10) == mOriginalValue) + if (static_cast(mValue * 10.0f) == mOriginalValue) setColorShift(mColorOriginalValue); else setColorShift(mColorChangedValue); @@ -122,6 +120,7 @@ void RatingComponent::updateVertices() const float fw = getSize().y() * numStars; const unsigned int color = Renderer::convertRGBAToABGR(mColorShift); + // clang-format off mVertices[0] = { { 0.0f, 0.0f }, { 0.0f, 1.0f }, color }; mVertices[1] = { { 0.0f, h }, { 0.0f, 0.0f }, color }; mVertices[2] = { { w, 0.0f }, { mValue * numStars, 1.0f }, color }; @@ -131,13 +130,7 @@ void RatingComponent::updateVertices() mVertices[5] = { { 0.0f, h }, { 0.0f, 0.0f }, color }; mVertices[6] = { { fw, 0.0f }, { numStars, 1.0f }, color }; mVertices[7] = { { fw, h }, { numStars, 0.0f }, color }; - - -// Disabled this code as it caused subtle but strange rendering errors -// where the icons changed size slightly when changing rating scores. -// // Round vertices. -// for (int i = 0; i < 8; i++) -// mVertices[i].pos.round(); + // clang-format on } void RatingComponent::updateColors() @@ -159,8 +152,8 @@ void RatingComponent::render(const Transform4x4f& parentTrans) if (mOpacity > 0) { if (Settings::getInstance()->getBool("DebugImage")) { - Renderer::drawRect(0.0f, 0.0f, mSize.y() * NUM_RATING_STARS, - mSize.y(), 0xFF000033, 0xFF000033); + Renderer::drawRect(0.0f, 0.0f, mSize.y() * NUM_RATING_STARS, mSize.y(), 0xFF000033, + 0xFF000033); } if (mUnfilledTexture->bind()) { @@ -189,14 +182,14 @@ void RatingComponent::render(const Transform4x4f& parentTrans) bool RatingComponent::input(InputConfig* config, Input input) { if (config->isMappedTo("a", input) && input.value != 0) { - mValue += (1.f/2) / NUM_RATING_STARS; + mValue += (1.0f / 2.0f) / NUM_RATING_STARS; if (mValue > 1.05f) mValue = 0.0f; // If the argument to colorize the rating icons has been passed, // set the color shift accordingly. if (mColorizeChanges) { - if (static_cast(mValue * 10) == mOriginalValue) + if (static_cast(mValue * 10.0f) == mOriginalValue) setColorShift(mColorOriginalValue); else setColorShift(mColorChangedValue); @@ -208,7 +201,9 @@ bool RatingComponent::input(InputConfig* config, Input input) } void RatingComponent::applyTheme(const std::shared_ptr& theme, - const std::string& view, const std::string& element, unsigned int properties) + const std::string& view, + const std::string& element, + unsigned int properties) { using namespace ThemeFlags; diff --git a/es-core/src/components/RatingComponent.h b/es-core/src/components/RatingComponent.h index 747c6a310..dfb2be65c 100644 --- a/es-core/src/components/RatingComponent.h +++ b/es-core/src/components/RatingComponent.h @@ -10,18 +10,13 @@ #ifndef ES_APP_COMPONENTS_RATING_COMPONENT_H #define ES_APP_COMPONENTS_RATING_COMPONENT_H -#include "renderers/Renderer.h" #include "GuiComponent.h" +#include "renderers/Renderer.h" class TextureResource; #define NUM_RATING_STARS 5 -// Used to visually display/edit some sort of "score" - e.g. 5/10, 3/5, etc. -// setSize(x, y) works a little differently than you might expect: -// * (0, y != 0) - x will be automatically calculated (5*y). -// * (x != 0, 0) - y will be automatically calculated (x/5). -// * (x != 0, y != 0) - you better be sure x = y*5. class RatingComponent : public GuiComponent { public: @@ -40,13 +35,15 @@ public: // Multiply all pixels in the image by this color when rendering. void setColorShift(unsigned int color) override; - unsigned int getColorShift() const override { return mColorShift; }; + unsigned int getColorShift() const override { return mColorShift; } - void setOriginalColor(unsigned int color) override { mColorOriginalValue = color; }; - void setChangedColor(unsigned int color) override { mColorChangedValue = color; }; + void setOriginalColor(unsigned int color) override { mColorOriginalValue = color; } + void setChangedColor(unsigned int color) override { mColorChangedValue = color; } - virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) override; + virtual void applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) override; virtual std::vector getHelpPrompts() override; diff --git a/es-core/src/components/ScrollableContainer.cpp b/es-core/src/components/ScrollableContainer.cpp index f2d12ff54..d025ae89f 100644 --- a/es-core/src/components/ScrollableContainer.cpp +++ b/es-core/src/components/ScrollableContainer.cpp @@ -9,22 +9,21 @@ #include "components/ScrollableContainer.h" +#include "Window.h" #include "animations/LambdaAnimation.h" #include "math/Vector2i.h" #include "renderers/Renderer.h" #include "resources/Font.h" -#include "Window.h" -ScrollableContainer::ScrollableContainer( - Window* window) - : GuiComponent(window), - mAutoScrollDelay(0), - mAutoScrollSpeed(0), - mAutoScrollAccumulator(0), - mScrollPos(0, 0), - mScrollDir(0, 0), - mAutoScrollResetAccumulator(0), - mFontSize(0.0f) +ScrollableContainer::ScrollableContainer(Window* window) + : GuiComponent(window) + , mAutoScrollDelay(0) + , mAutoScrollSpeed(0) + , mAutoScrollAccumulator(0) + , mScrollPos(0, 0) + , mScrollDir(0, 0) + , mAutoScrollResetAccumulator(0) + , mFontSize(0.0f) { // Set the modifier to get equivalent scrolling speed regardless of screen resolution. mResolutionModifier = Renderer::getScreenHeightModifier(); @@ -33,7 +32,8 @@ ScrollableContainer::ScrollableContainer( // For narrower aspect ratios than 16:9 there is a need to set an additional compensation // to get somehow consistent scrolling speeds. float aspectCompensation = static_cast(Renderer::getScreenHeight()) / - static_cast(Renderer::getScreenWidth()) - 0.5625f; + static_cast(Renderer::getScreenWidth()) - + 0.5625f; if (aspectCompensation > 0) mResolutionModifier += aspectCompensation * 4.0f; @@ -42,24 +42,14 @@ ScrollableContainer::ScrollableContainer( mAutoScrollSpeedConstant = AUTO_SCROLL_SPEED; } -Vector2f ScrollableContainer::getScrollPos() const -{ - return mScrollPos; -} - -void ScrollableContainer::setScrollPos(const Vector2f& pos) -{ - mScrollPos = pos; -} - void ScrollableContainer::setAutoScroll(bool autoScroll) { if (autoScroll) { mScrollDir = Vector2f(0, 1); mAutoScrollDelay = static_cast(mAutoScrollDelayConstant); mAutoScrollSpeed = mAutoScrollSpeedConstant; - mAutoScrollSpeed = static_cast(static_cast(mAutoScrollSpeedConstant) / - mResolutionModifier); + mAutoScrollSpeed = + static_cast(static_cast(mAutoScrollSpeedConstant) / mResolutionModifier); reset(); } else { @@ -71,7 +61,8 @@ void ScrollableContainer::setAutoScroll(bool autoScroll) } void ScrollableContainer::setScrollParameters(float autoScrollDelayConstant, - float autoScrollResetDelayConstant, int autoScrollSpeedConstant) + float autoScrollResetDelayConstant, + int autoScrollSpeedConstant) { mAutoScrollResetDelayConstant = autoScrollResetDelayConstant; mAutoScrollDelayConstant = autoScrollDelayConstant; @@ -90,7 +81,7 @@ void ScrollableContainer::update(int deltaTime) { // Don't scroll if the media viewer or screensaver is active or if text scrolling is disabled; if (mWindow->isMediaViewerActive() || mWindow->isScreensaverActive() || - !mWindow->getAllowTextScrolling()) { + !mWindow->getAllowTextScrolling()) { if (mScrollPos != 0 && !mWindow->isLaunchScreenDisplayed()) reset(); return; @@ -105,8 +96,8 @@ void ScrollableContainer::update(int deltaTime) // Also adjust the scrolling speed based on the size of the font. float fontSizeModifier = mSmallFontSize / mFontSize; - adjustedAutoScrollSpeed = static_cast(adjustedAutoScrollSpeed * - fontSizeModifier * fontSizeModifier); + adjustedAutoScrollSpeed = + static_cast(adjustedAutoScrollSpeed * fontSizeModifier * fontSizeModifier); if (adjustedAutoScrollSpeed < 0) adjustedAutoScrollSpeed = 1; @@ -165,11 +156,11 @@ void ScrollableContainer::render(const Transform4x4f& parentTrans) Transform4x4f trans = parentTrans * getTransform(); Vector2i clipPos(static_cast(trans.translation().x()), - static_cast(trans.translation().y())); + static_cast(trans.translation().y())); Vector3f dimScaled = trans * Vector3f(mSize.x(), mSize.y(), 0); Vector2i clipDim(static_cast((dimScaled.x()) - trans.translation().x()), - static_cast((dimScaled.y()) - trans.translation().y())); + static_cast((dimScaled.y()) - trans.translation().y())); Renderer::pushClipRect(clipPos, clipDim); diff --git a/es-core/src/components/ScrollableContainer.h b/es-core/src/components/ScrollableContainer.h index 293d4d598..96d8b17f5 100644 --- a/es-core/src/components/ScrollableContainer.h +++ b/es-core/src/components/ScrollableContainer.h @@ -24,11 +24,13 @@ class ScrollableContainer : public GuiComponent public: ScrollableContainer(Window* window); - Vector2f getScrollPos() const; - void setScrollPos(const Vector2f& pos); + Vector2f getScrollPos() const { return mScrollPos; } + void setScrollPos(const Vector2f& pos) { mScrollPos = pos; } + void setAutoScroll(bool autoScroll); - void setScrollParameters(float autoScrollDelayConstant, float autoScrollResetDelayConstant, - int autoScrollSpeedConstant) override; + void setScrollParameters(float autoScrollDelayConstant, + float autoScrollResetDelayConstant, + int autoScrollSpeedConstant) override; void reset(); void update(int deltaTime) override; diff --git a/es-core/src/components/SliderComponent.cpp b/es-core/src/components/SliderComponent.cpp index 7749fdcc8..247b9fc1e 100644 --- a/es-core/src/components/SliderComponent.cpp +++ b/es-core/src/components/SliderComponent.cpp @@ -14,23 +14,19 @@ #define MOVE_REPEAT_RATE 40 SliderComponent::SliderComponent( - Window* window, - float min, - float max, - float increment, - const std::string& suffix) - : GuiComponent(window), - mMin(min), - mMax(max), - mSingleIncrement(increment), - mMoveRate(0), - mKnob(window), - mSuffix(suffix) + Window* window, float min, float max, float increment, const std::string& suffix) + : GuiComponent(window) + , mMin(min) + , mMax(max) + , mSingleIncrement(increment) + , mMoveRate(0) + , mKnob(window) + , mSuffix(suffix) { assert((min - max) != 0); // Some sane default value. - mValue = (max + min) / 2; + mValue = (max + min) / 2.0f; mKnob.setOrigin(0.5f, 0.5f); mKnob.setImage(":/graphics/slider_knob.svg"); @@ -87,14 +83,15 @@ void SliderComponent::render(const Transform4x4f& parentTrans) if (mValueCache) mFont->renderTextCache(mValueCache.get()); - float width = mSize.x() - mKnob.getSize().x() - - (mValueCache ? mValueCache->metrics.size.x() + - (4 * Renderer::getScreenWidthModifier()) : 0); + float width = + mSize.x() - mKnob.getSize().x() - + (mValueCache ? mValueCache->metrics.size.x() + (4.0f * Renderer::getScreenWidthModifier()) : + 0); // Render line. - const float lineWidth = 2 * Renderer::getScreenHeightModifier();; - Renderer::drawRect(mKnob.getSize().x() / 2, mSize.y() / 2 - - lineWidth / 2, width, lineWidth, 0x777777FF, 0x777777FF); + const float lineWidth = 2.0f * Renderer::getScreenHeightModifier(); + Renderer::drawRect(mKnob.getSize().x() / 2.0f, mSize.y() / 2.0f - lineWidth / 2.0f, width, + lineWidth, 0x777777FF, 0x777777FF); // Render knob. mKnob.render(trans); @@ -113,10 +110,7 @@ void SliderComponent::setValue(float value) onValueChanged(); } -float SliderComponent::getValue() -{ - return mValue; -} +float SliderComponent::getValue() { return mValue; } void SliderComponent::onSizeChanged() { @@ -146,18 +140,20 @@ void SliderComponent::onValueChanged() const std::string max = ss.str(); Vector2f textSize = mFont->sizeText(max); - mValueCache = std::shared_ptr(mFont->buildTextCache(val, mSize.x() - - textSize.x(), (mSize.y() - textSize.y()) / 2, 0x777777FF)); + mValueCache = std::shared_ptr(mFont->buildTextCache( + val, mSize.x() - textSize.x(), (mSize.y() - textSize.y()) / 2.0f, 0x777777FF)); mValueCache->metrics.size[0] = textSize.x(); // Fudge the width. } // Update knob position/size. mKnob.setResize(0, mSize.y() * 0.7f); - float lineLength = mSize.x() - mKnob.getSize().x() - - (mValueCache ? mValueCache->metrics.size.x() + - (4 * Renderer::getScreenWidthModifier()) : 0); - mKnob.setPosition(((mValue - mMin / 2) / mMax) * lineLength + - mKnob.getSize().x() / 2, mSize.y() / 2); + float lineLength = + mSize.x() - mKnob.getSize().x() - + (mValueCache ? mValueCache->metrics.size.x() + (4.0f * Renderer::getScreenWidthModifier()) : + 0); + + mKnob.setPosition(((mValue - mMin / 2.0f) / mMax) * lineLength + mKnob.getSize().x() / 2.0f, + mSize.y() / 2.0f); } std::vector SliderComponent::getHelpPrompts() diff --git a/es-core/src/components/SliderComponent.h b/es-core/src/components/SliderComponent.h index e10c3d913..3b9f7ec86 100644 --- a/es-core/src/components/SliderComponent.h +++ b/es-core/src/components/SliderComponent.h @@ -9,8 +9,8 @@ #ifndef ES_CORE_COMPONENTS_SLIDER_COMPONENT_H #define ES_CORE_COMPONENTS_SLIDER_COMPONENT_H -#include "components/ImageComponent.h" #include "GuiComponent.h" +#include "components/ImageComponent.h" class Font; class TextCache; @@ -20,13 +20,9 @@ class SliderComponent : public GuiComponent { public: // Minimum value (far left of the slider), maximum value (far right of the slider), - // increment size (how much just pressing L/R moves by), unit to display (optional). + // increment size (how much pressing L/R moves by), unit to display (optional). SliderComponent( - Window* window, - float min, - float max, - float increment, - const std::string& suffix = ""); + Window* window, float min, float max, float increment, const std::string& suffix = ""); void setValue(float val); float getValue(); diff --git a/es-core/src/components/SwitchComponent.cpp b/es-core/src/components/SwitchComponent.cpp index 961a0d9cf..18d1010a4 100644 --- a/es-core/src/components/SwitchComponent.cpp +++ b/es-core/src/components/SwitchComponent.cpp @@ -10,31 +10,19 @@ #include "resources/Font.h" -SwitchComponent::SwitchComponent( - Window* window, - bool state) - : GuiComponent(window), - mImage(window), - mState(state), - mOriginalValue(state), - mColorOriginalValue(DEFAULT_COLORSHIFT), - mColorChangedValue(DEFAULT_COLORSHIFT) +SwitchComponent::SwitchComponent(Window* window, bool state) + : GuiComponent(window) + , mImage(window) + , mState(state) + , mOriginalValue(state) + , mColorOriginalValue(DEFAULT_COLORSHIFT) + , mColorChangedValue(DEFAULT_COLORSHIFT) { mImage.setImage(":/graphics/off.svg"); mImage.setResize(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()); mSize = mImage.getSize(); } -void SwitchComponent::onSizeChanged() -{ - mImage.setSize(mSize); -} - -void SwitchComponent::setResize(float width, float height) -{ - mImage.setResize(width, height); -} - bool SwitchComponent::input(InputConfig* config, Input input) { if (config->isMappedTo("a", input) && input.value) { @@ -61,21 +49,13 @@ void SwitchComponent::render(const Transform4x4f& parentTrans) renderChildren(trans); } -bool SwitchComponent::getState() const -{ - return mState; -} - void SwitchComponent::setState(bool state) { mState = state; onStateChanged(); } -std::string SwitchComponent::getValue() const -{ - return mState ? "true" : "false"; -} +std::string SwitchComponent::getValue() const { return mState ? "true" : "false"; } void SwitchComponent::setValue(const std::string& statestring) { @@ -88,26 +68,6 @@ void SwitchComponent::setValue(const std::string& statestring) onStateChanged(); } -unsigned char SwitchComponent::getOpacity() const -{ - return mImage.getOpacity(); -} - -void SwitchComponent::setOpacity(unsigned char opacity) -{ - mImage.setOpacity(opacity); -} - -void SwitchComponent::setColorShift(unsigned int color) -{ - mImage.setColorShift(color); -} - -unsigned int SwitchComponent::getColorShift() const -{ - return mImage.getColorShift(); -} - void SwitchComponent::onStateChanged() { mImage.setImage(mState ? ":/graphics/on.svg" : ":/graphics/off.svg"); diff --git a/es-core/src/components/SwitchComponent.h b/es-core/src/components/SwitchComponent.h index 94f672ead..cc7cadf9d 100644 --- a/es-core/src/components/SwitchComponent.h +++ b/es-core/src/components/SwitchComponent.h @@ -9,8 +9,8 @@ #ifndef ES_CORE_COMPONENTS_SWITCH_COMPONENT_H #define ES_CORE_COMPONENTS_SWITCH_COMPONENT_H -#include "components/ImageComponent.h" #include "GuiComponent.h" +#include "components/ImageComponent.h" // A simple "on/off" switch. class SwitchComponent : public GuiComponent @@ -20,25 +20,25 @@ public: bool input(InputConfig* config, Input input) override; void render(const Transform4x4f& parentTrans) override; - void onSizeChanged() override; + void onSizeChanged() override { mImage.setSize(mSize); } - void setResize(float width, float height) override; + void setResize(float width, float height) override { mImage.setResize(width, height); } - bool getState() const; + bool getState() const { return mState; } void setState(bool state); std::string getValue() const override; void setValue(const std::string& statestring) override; - void setOriginalColor(unsigned int color) override { mColorOriginalValue = color; }; - void setChangedColor(unsigned int color) override { mColorChangedValue = color; }; - void setCallback(const std::function& callbackFunc) { mToggleCallback = callbackFunc; }; + void setOriginalColor(unsigned int color) override { mColorOriginalValue = color; } + void setChangedColor(unsigned int color) override { mColorChangedValue = color; } + void setCallback(const std::function& callbackFunc) { mToggleCallback = callbackFunc; } - unsigned char getOpacity() const override; - void setOpacity(unsigned char opacity) override; + unsigned char getOpacity() const override { return mImage.getOpacity(); } + void setOpacity(unsigned char opacity) override { mImage.setOpacity(opacity); } // Multiply all pixels in the image by this color when rendering. - void setColorShift(unsigned int color) override; + void setColorShift(unsigned int color) override { mImage.setColorShift(color); } - unsigned int getColorShift() const override; + unsigned int getColorShift() const override { return mImage.getColorShift(); } virtual std::vector getHelpPrompts() override; diff --git a/es-core/src/components/TextComponent.cpp b/es-core/src/components/TextComponent.cpp index 02ebc635f..332cc56c4 100644 --- a/es-core/src/components/TextComponent.cpp +++ b/es-core/src/components/TextComponent.cpp @@ -8,49 +8,47 @@ #include "components/TextComponent.h" -#include "utils/StringUtil.h" #include "Log.h" #include "Settings.h" +#include "utils/StringUtil.h" -TextComponent::TextComponent( - Window* window) - : GuiComponent(window), - mFont(Font::get(FONT_SIZE_MEDIUM)), - mUppercase(false), - mColor(0x000000FF), - mAutoCalcExtent(true, true), - mHorizontalAlignment(ALIGN_LEFT), - mVerticalAlignment(ALIGN_CENTER), - mLineSpacing(1.5f), - mNoTopMargin(false), - mBgColor(0), - mMargin(0.0f), - mRenderBackground(false) +TextComponent::TextComponent(Window* window) + : GuiComponent(window) + , mFont(Font::get(FONT_SIZE_MEDIUM)) + , mUppercase(false) + , mColor(0x000000FF) + , mAutoCalcExtent(true, true) + , mHorizontalAlignment(ALIGN_LEFT) + , mVerticalAlignment(ALIGN_CENTER) + , mLineSpacing(1.5f) + , mNoTopMargin(false) + , mBgColor(0) + , mMargin(0.0f) + , mRenderBackground(false) { } -TextComponent::TextComponent( - Window* window, - const std::string& text, - const std::shared_ptr& font, - unsigned int color, - Alignment align, - Vector3f pos, - Vector2f size, - unsigned int bgcolor, - float margin) - : GuiComponent(window), - mFont(nullptr), - mUppercase(false), - mColor(0x000000FF), - mAutoCalcExtent(true, true), - mHorizontalAlignment(align), - mVerticalAlignment(ALIGN_CENTER), - mLineSpacing(1.5f), - mNoTopMargin(false), - mBgColor(0), - mMargin(margin), - mRenderBackground(false) +TextComponent::TextComponent(Window* window, + const std::string& text, + const std::shared_ptr& font, + unsigned int color, + Alignment align, + Vector3f pos, + Vector2f size, + unsigned int bgcolor, + float margin) + : GuiComponent(window) + , mFont(nullptr) + , mUppercase(false) + , mColor(0x000000FF) + , mAutoCalcExtent(true, true) + , mHorizontalAlignment(align) + , mVerticalAlignment(ALIGN_CENTER) + , mLineSpacing(1.5f) + , mNoTopMargin(false) + , mBgColor(0) + , mMargin(margin) + , mRenderBackground(false) { setFont(font); setColor(color); @@ -87,45 +85,30 @@ void TextComponent::setBackgroundColor(unsigned int color) mBgColorOpacity = mBgColor & 0x000000FF; } -void TextComponent::setRenderBackground(bool render) -{ - mRenderBackground = render; -} - // Scale the opacity. void TextComponent::setOpacity(unsigned char opacity) { - // This function is mostly called to do fading in-out of the Text component element. - // Therefore, we assume here that opacity is a fractional value (expressed as an int 0-255), - // of the opacity originally set with setColor() or setBackgroundColor(). - unsigned char o = static_cast(static_cast(opacity) / 255.f * - static_cast(mColorOpacity)); + // This function is mostly called to do fade in and fade out of the text component element. + // Therefore we assume here that opacity is a fractional value (expressed as an unsigned + // char 0 - 255) of the opacity originally set with setColor() or setBackgroundColor(). + unsigned char o = static_cast(static_cast(opacity) / 255.0f * + static_cast(mColorOpacity)); mColor = (mColor & 0xFFFFFF00) | static_cast(o); - unsigned char bgo = static_cast(static_cast(opacity) / 255.f * - static_cast(mBgColorOpacity)); + unsigned char bgo = static_cast(static_cast(opacity) / 255.0f * + static_cast(mBgColorOpacity)); mBgColor = (mBgColor & 0xFFFFFF00) | static_cast(bgo); onColorChanged(); GuiComponent::setOpacity(opacity); } -unsigned char TextComponent::getOpacity() const -{ - return mColor & 0x000000FF; -} - void TextComponent::setText(const std::string& text) { mText = text; onTextChanged(); } -void TextComponent::setHiddenText(const std::string& text) -{ - mHiddenText = text; -} - void TextComponent::setUppercase(bool uppercase) { mUppercase = uppercase; @@ -148,17 +131,21 @@ void TextComponent::render(const Transform4x4f& parentTrans) const Vector2f& textSize = mTextCache->metrics.size; float yOff = 0; switch (mVerticalAlignment) { - case ALIGN_TOP: + case ALIGN_TOP: { yOff = 0; break; - case ALIGN_BOTTOM: + } + case ALIGN_BOTTOM: { yOff = (getSize().y() - textSize.y()); break; - case ALIGN_CENTER: + } + case ALIGN_CENTER: { yOff = (getSize().y() - textSize.y()) / 2.0f; break; - default: + } + default: { break; + } } Vector3f off(0, yOff, 0); @@ -174,22 +161,26 @@ void TextComponent::render(const Transform4x4f& parentTrans) // Draw the text area, where the text actually is located. if (Settings::getInstance()->getBool("DebugText")) { switch (mHorizontalAlignment) { - case ALIGN_LEFT: - Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(), - mTextCache->metrics.size.y(), 0x00000033, 0x00000033); - break; - case ALIGN_CENTER: - Renderer::drawRect((mSize.x() - mTextCache->metrics.size.x()) / 2.0f, 0.0f, - mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), - 0x00000033, 0x00000033); - break; - case ALIGN_RIGHT: - Renderer::drawRect(mSize.x() - mTextCache->metrics.size.x(), 0.0f, - mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), - 0x00000033, 0x00000033); - break; - default: - break; + case ALIGN_LEFT: { + Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(), + mTextCache->metrics.size.y(), 0x00000033, 0x00000033); + break; + } + case ALIGN_CENTER: { + Renderer::drawRect((mSize.x() - mTextCache->metrics.size.x()) / 2.0f, 0.0f, + mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), + 0x00000033, 0x00000033); + break; + } + case ALIGN_RIGHT: { + Renderer::drawRect(mSize.x() - mTextCache->metrics.size.x(), 0.0f, + mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), + 0x00000033, 0x00000033); + break; + } + default: { + break; + } } } mFont->renderTextCache(mTextCache.get()); @@ -203,8 +194,10 @@ void TextComponent::calculateExtent() } else { if (mAutoCalcExtent.y()) - mSize[1] = mFont->sizeWrappedText(mUppercase ? Utils::String::toUpper(mText) - : mText, getSize().x(), mLineSpacing).y(); + mSize[1] = mFont + ->sizeWrappedText(mUppercase ? Utils::String::toUpper(mText) : mText, + getSize().x(), mLineSpacing) + .y(); } } @@ -246,14 +239,14 @@ void TextComponent::onTextChanged() text.append(abbrev); - mTextCache = std::shared_ptr(f->buildTextCache(text, Vector2f(0, 0), - (mColor >> 8 << 8) | mOpacity, mSize.x(), mHorizontalAlignment, - mLineSpacing, mNoTopMargin)); + mTextCache = std::shared_ptr( + f->buildTextCache(text, Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(), + mHorizontalAlignment, mLineSpacing, mNoTopMargin)); } else { - mTextCache = std::shared_ptr(f->buildTextCache(f->wrapText( - text, mSize.x()), Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(), - mHorizontalAlignment, mLineSpacing, mNoTopMargin)); + mTextCache = std::shared_ptr(f->buildTextCache( + f->wrapText(text, mSize.x()), Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(), + mHorizontalAlignment, mLineSpacing, mNoTopMargin)); } } @@ -269,10 +262,7 @@ void TextComponent::setHorizontalAlignment(Alignment align) onTextChanged(); } -void TextComponent::setVerticalAlignment(Alignment align) -{ - mVerticalAlignment = align; -} +void TextComponent::setVerticalAlignment(Alignment align) { mVerticalAlignment = align; } void TextComponent::setLineSpacing(float spacing) { @@ -286,28 +276,10 @@ void TextComponent::setNoTopMargin(bool margin) onTextChanged(); } -std::string TextComponent::getValue() const -{ - return mText; -} - -void TextComponent::setValue(const std::string& value) -{ - setText(value); -} - -std::string TextComponent::getHiddenValue() const -{ - return mHiddenText; -} - -void TextComponent::setHiddenValue(const std::string& value) -{ - setHiddenText(value); -} - -void TextComponent::applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) +void TextComponent::applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) { GuiComponent::applyTheme(theme, view, element, properties); diff --git a/es-core/src/components/TextComponent.h b/es-core/src/components/TextComponent.h index 30070ffb4..52a65d373 100644 --- a/es-core/src/components/TextComponent.h +++ b/es-core/src/components/TextComponent.h @@ -9,8 +9,8 @@ #ifndef ES_CORE_COMPONENTS_TEXT_COMPONENT_H #define ES_CORE_COMPONENTS_TEXT_COMPONENT_H -#include "resources/Font.h" #include "GuiComponent.h" +#include "resources/Font.h" class ThemeData; @@ -25,46 +25,47 @@ class TextComponent : public GuiComponent { public: TextComponent(Window* window); - TextComponent( - Window* window, - const std::string& text, - const std::shared_ptr& font, - unsigned int color = 0x000000FF, - Alignment align = ALIGN_LEFT, - Vector3f pos = Vector3f::Zero(), - Vector2f size = Vector2f::Zero(), - unsigned int bgcolor = 0x00000000, - float margin = 0.0f); + TextComponent(Window* window, + const std::string& text, + const std::shared_ptr& font, + unsigned int color = 0x000000FF, + Alignment align = ALIGN_LEFT, + Vector3f pos = Vector3f::Zero(), + Vector2f size = Vector2f::Zero(), + unsigned int bgcolor = 0x00000000, + float margin = 0.0f); void setFont(const std::shared_ptr& font); void setUppercase(bool uppercase); void onSizeChanged() override; void setText(const std::string& text); - void setHiddenText(const std::string& text); + void setHiddenText(const std::string& text) { mHiddenText = text; } void setColor(unsigned int color) override; void setHorizontalAlignment(Alignment align); void setVerticalAlignment(Alignment align); void setLineSpacing(float spacing); void setNoTopMargin(bool margin); void setBackgroundColor(unsigned int color); - void setRenderBackground(bool render); + void setRenderBackground(bool render) { mRenderBackground = render; } void render(const Transform4x4f& parentTrans) override; - std::string getValue() const override; - void setValue(const std::string& value) override; + std::string getValue() const override { return mText; } + void setValue(const std::string& value) override { setText(value); } - std::string getHiddenValue() const override; - void setHiddenValue(const std::string& value) override; + std::string getHiddenValue() const override { return mHiddenText; } + void setHiddenValue(const std::string& value) override { setHiddenText(value); } - unsigned char getOpacity() const override; + unsigned char getOpacity() const override { return mColor & 0x000000FF; } void setOpacity(unsigned char opacity) override; - virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) override; + virtual void applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) override; - unsigned int getColor() const override { return mColor; }; - inline std::shared_ptr getFont() const override { return mFont; } + unsigned int getColor() const override { return mColor; } + std::shared_ptr getFont() const override { return mFont; } Alignment getHorizontalAlignment() { return mHorizontalAlignment; } Alignment getVerticalAlignment() { return mVerticalAlignment; } @@ -77,7 +78,6 @@ protected: private: void calculateExtent(); - void onColorChanged(); unsigned int mColor; diff --git a/es-core/src/components/TextEditComponent.cpp b/es-core/src/components/TextEditComponent.cpp index 23aeab722..de9bd028d 100644 --- a/es-core/src/components/TextEditComponent.cpp +++ b/es-core/src/components/TextEditComponent.cpp @@ -8,24 +8,23 @@ #include "components/TextEditComponent.h" -#include "resources/Font.h" #include "utils/StringUtil.h" -#define TEXT_PADDING_HORIZ 10 -#define TEXT_PADDING_VERT 2 +#define TEXT_PADDING_HORIZ 10.0f +#define TEXT_PADDING_VERT 2.0f #define CURSOR_REPEAT_START_DELAY 500 #define CURSOR_REPEAT_SPEED 28 // Lower is faster. -TextEditComponent::TextEditComponent( - Window* window) - : GuiComponent(window), - mBox(window, ":/graphics/textinput.svg"), - mFocused(false), - mScrollOffset(0.0f, 0.0f), - mCursor(0), mEditing(false), - mFont(Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT)), - mCursorRepeatDir(0) +TextEditComponent::TextEditComponent(Window* window) + : GuiComponent(window) + , mBox(window, ":/graphics/textinput.svg") + , mFocused(false) + , mScrollOffset(0.0f, 0.0f) + , mCursor(0) + , mEditing(false) + , mFont(Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT)) + , mCursorRepeatDir(0) { addChild(&mBox); onFocusLost(); @@ -47,8 +46,9 @@ void TextEditComponent::onFocusLost() void TextEditComponent::onSizeChanged() { - mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-34 + mResolutionAdjustment, -32 - - (TEXT_PADDING_VERT * Renderer::getScreenHeightModifier()))); + mBox.fitTo(mSize, Vector3f::Zero(), + Vector2f(-34 + mResolutionAdjustment, + -32 - (TEXT_PADDING_VERT * Renderer::getScreenHeightModifier()))); onTextChanged(); // Wrap point probably changed. } @@ -59,11 +59,6 @@ void TextEditComponent::setValue(const std::string& val) onTextChanged(); } -std::string TextEditComponent::getValue() const -{ - return mText; -} - void TextEditComponent::textInput(const std::string& text) { if (mEditing) { @@ -103,33 +98,35 @@ void TextEditComponent::stopEditing() bool TextEditComponent::input(InputConfig* config, Input input) { - bool const cursor_left = (config->getDeviceId() != DEVICE_KEYBOARD && - config->isMappedLike("left", input)) || (config->getDeviceId() == DEVICE_KEYBOARD && - input.id == SDLK_LEFT); - bool const cursor_right = (config->getDeviceId() != DEVICE_KEYBOARD && - config->isMappedLike("right", input)) || (config->getDeviceId() == DEVICE_KEYBOARD && - input.id == SDLK_RIGHT); - bool const cursor_up = (config->getDeviceId() != DEVICE_KEYBOARD && - config->isMappedLike("up", input)) || (config->getDeviceId() == DEVICE_KEYBOARD && - input.id == SDLK_UP); - bool const cursor_down = (config->getDeviceId() != DEVICE_KEYBOARD && - config->isMappedLike("down", input)) || (config->getDeviceId() == DEVICE_KEYBOARD && - input.id == SDLK_DOWN); + bool const cursor_left = + (config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("left", input)) || + (config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_LEFT); + bool const cursor_right = + (config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("right", input)) || + (config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_RIGHT); + bool const cursor_up = + (config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("up", input)) || + (config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_UP); + bool const cursor_down = + (config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("down", input)) || + (config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_DOWN); bool const shoulder_left = (config->isMappedLike("leftshoulder", input)); bool const shoulder_right = (config->isMappedLike("rightshoulder", input)); bool const trigger_left = (config->isMappedLike("lefttrigger", input)); bool const trigger_right = (config->isMappedLike("righttrigger", input)); if (input.value == 0) { - if (cursor_left || cursor_right || cursor_up || cursor_down || - shoulder_left || shoulder_right | trigger_left || trigger_right) + if (cursor_left || cursor_right || cursor_up || cursor_down || shoulder_left || + shoulder_right | trigger_left || trigger_right) { mCursorRepeatDir = 0; + } return false; } - if ((config->isMappedTo("a", input) || (config->getDeviceId() == DEVICE_KEYBOARD && - input.id == SDLK_RETURN)) && mFocused && !mEditing) { + if ((config->isMappedTo("a", input) || + (config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_RETURN)) && + mFocused && !mEditing) { startEditing(); return true; } @@ -146,8 +143,8 @@ bool TextEditComponent::input(InputConfig* config, Input input) // Done editing (accept changes). if ((config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_ESCAPE) || - (config->getDeviceId() != DEVICE_KEYBOARD && (config->isMappedTo("a", input) || - config->isMappedTo("b", input)))) { + (config->getDeviceId() != DEVICE_KEYBOARD && + (config->isMappedTo("a", input) || config->isMappedTo("b", input)))) { mTextOrig = mText; stopEditing(); return true; @@ -181,21 +178,22 @@ bool TextEditComponent::input(InputConfig* config, Input input) } else if (config->getDeviceId() == DEVICE_KEYBOARD) { switch (input.id) { - case SDLK_HOME: - setCursor(0); - break; - - case SDLK_END: - setCursor(std::string::npos); - break; - - case SDLK_DELETE: - if (mCursor < mText.length()) { - // Fake as Backspace one char to the right. - moveCursor(1); - textInput("\b"); + case SDLK_HOME: { + setCursor(0); + break; + } + case SDLK_END: { + setCursor(std::string::npos); + break; + } + case SDLK_DELETE: { + if (mCursor < mText.length()) { + // Fake as Backspace one char to the right. + moveCursor(1); + textInput("\b"); + } + break; } - break; } } // Consume all input when editing text. @@ -241,10 +239,10 @@ void TextEditComponent::setCursor(size_t pos) void TextEditComponent::onTextChanged() { - std::string wrappedText = (isMultiline() ? - mFont->wrapText(mText, getTextAreaSize().x()) : mText); - mTextCache = std::unique_ptr - (mFont->buildTextCache(wrappedText, 0, 0, 0x77777700 | getOpacity())); + std::string wrappedText = + (isMultiline() ? mFont->wrapText(mText, getTextAreaSize().x()) : mText); + mTextCache = std::unique_ptr( + mFont->buildTextCache(wrappedText, 0, 0, 0x77777700 | getOpacity())); if (mCursor > static_cast(mText.length())) mCursor = static_cast(mText.length()); @@ -253,13 +251,14 @@ void TextEditComponent::onTextChanged() void TextEditComponent::onCursorChanged() { if (isMultiline()) { - Vector2f textSize = mFont-> - getWrappedTextCursorOffset(mText, getTextAreaSize().x(), mCursor); + Vector2f textSize = + mFont->getWrappedTextCursorOffset(mText, getTextAreaSize().x(), mCursor); - if (mScrollOffset.y() + getTextAreaSize().y() < textSize.y() + - mFont->getHeight()) // Need to scroll down? + // Need to scroll down? + if (mScrollOffset.y() + getTextAreaSize().y() < textSize.y() + mFont->getHeight()) mScrollOffset[1] = textSize.y() - getTextAreaSize().y() + mFont->getHeight(); - else if (mScrollOffset.y() > textSize.y()) // Need to scroll up? + // Need to scroll up? + else if (mScrollOffset.y() > textSize.y()) mScrollOffset[1] = textSize.y(); } else { @@ -282,11 +281,11 @@ void TextEditComponent::render(const Transform4x4f& parentTrans) trans.translation() += Vector3f(getTextAreaPos().x(), getTextAreaPos().y(), 0); Vector2i clipPos(static_cast(trans.translation().x()), - static_cast(trans.translation().y())); + static_cast(trans.translation().y())); // Use "text area" size for clipping. Vector3f dimScaled = trans * Vector3f(getTextAreaSize().x(), getTextAreaSize().y(), 0); Vector2i clipDim(static_cast((dimScaled.x()) - trans.translation().x()), - static_cast((dimScaled.y()) - trans.translation().y())); + static_cast((dimScaled.y()) - trans.translation().y())); Renderer::pushClipRect(clipPos, clipDim); trans.translate(Vector3f(-mScrollOffset.x(), -mScrollOffset.y(), 0)); @@ -310,28 +309,24 @@ void TextEditComponent::render(const Transform4x4f& parentTrans) } float cursorHeight = mFont->getHeight() * 0.8f; - Renderer::drawRect(cursorPos.x(), cursorPos.y() + (mFont->getHeight() - cursorHeight) / 2, - 2.0f * Renderer::getScreenWidthModifier(), cursorHeight, 0x000000FF, 0x000000FF); + Renderer::drawRect( + cursorPos.x(), cursorPos.y() + (mFont->getHeight() - cursorHeight) / 2.0f, + 2.0f * Renderer::getScreenWidthModifier(), cursorHeight, 0x000000FF, 0x000000FF); } } -bool TextEditComponent::isMultiline() -{ - return (getSize().y() > mFont->getHeight() * 1.25f); -} - Vector2f TextEditComponent::getTextAreaPos() const { - return Vector2f((-mResolutionAdjustment + - (TEXT_PADDING_HORIZ * Renderer::getScreenWidthModifier())) / 2.0f, - (TEXT_PADDING_VERT * Renderer::getScreenHeightModifier()) / 2.0f); + return Vector2f( + (-mResolutionAdjustment + (TEXT_PADDING_HORIZ * Renderer::getScreenWidthModifier())) / 2.0f, + (TEXT_PADDING_VERT * Renderer::getScreenHeightModifier()) / 2.0f); } Vector2f TextEditComponent::getTextAreaSize() const { return Vector2f(mSize.x() + mResolutionAdjustment - - (TEXT_PADDING_HORIZ * Renderer::getScreenWidthModifier()), mSize.y() - - (TEXT_PADDING_VERT * Renderer::getScreenHeightModifier())); + (TEXT_PADDING_HORIZ * Renderer::getScreenWidthModifier()), + mSize.y() - (TEXT_PADDING_VERT * Renderer::getScreenHeightModifier())); } std::vector TextEditComponent::getHelpPrompts() diff --git a/es-core/src/components/TextEditComponent.h b/es-core/src/components/TextEditComponent.h index 1a97a7bbe..9365912a3 100644 --- a/es-core/src/components/TextEditComponent.h +++ b/es-core/src/components/TextEditComponent.h @@ -9,8 +9,9 @@ #ifndef ES_CORE_COMPONENTS_TEXT_EDIT_COMPONENT_H #define ES_CORE_COMPONENTS_TEXT_EDIT_COMPONENT_H -#include "components/NinePatchComponent.h" #include "GuiComponent.h" +#include "components/NinePatchComponent.h" +#include "resources/Font.h" class Font; class TextCache; @@ -32,13 +33,13 @@ public: void onSizeChanged() override; void setValue(const std::string& val) override; - std::string getValue() const override; + std::string getValue() const override { return mText; } void startEditing(); void stopEditing(); - inline bool isEditing() const { return mEditing; }; - inline std::shared_ptr getFont() const override{ return mFont; } + bool isEditing() const { return mEditing; } + std::shared_ptr getFont() const override { return mFont; } void setCursor(size_t pos); @@ -51,7 +52,7 @@ private: void updateCursorRepeat(int deltaTime); void moveCursor(int amt); - bool isMultiline(); + bool isMultiline() { return (getSize().y() > mFont->getHeight() * 1.25f); } Vector2f getTextAreaPos() const; Vector2f getTextAreaSize() const; diff --git a/es-core/src/components/TextListComponent.h b/es-core/src/components/TextListComponent.h index 0eec010a6..f38318a18 100644 --- a/es-core/src/components/TextListComponent.h +++ b/es-core/src/components/TextListComponent.h @@ -9,12 +9,12 @@ #ifndef ES_APP_COMPONENTS_TEXT_LIST_COMPONENT_H #define ES_APP_COMPONENTS_TEXT_LIST_COMPONENT_H +#include "Log.h" +#include "Sound.h" #include "components/IList.h" #include "math/Misc.h" #include "resources/Font.h" #include "utils/StringUtil.h" -#include "Log.h" -#include "Sound.h" #include @@ -26,8 +26,7 @@ struct TextListData { }; // A graphical list. Supports multiple colors for rows and scrolling. -template -class TextListComponent : public IList +template class TextListComponent : public IList { protected: using IList::mEntries; @@ -38,9 +37,6 @@ protected: using IList::mSize; using IList::mCursor; using IList::mWindow; - // The following change is required for compilation with Clang. - // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2070 - // using IList::Entry; using IList::IList; public: @@ -53,45 +49,51 @@ public: bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; void render(const Transform4x4f& parentTrans) override; - void applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) override; + void applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) override; void add(const std::string& name, const T& obj, unsigned int colorId); enum Alignment { - ALIGN_LEFT, + ALIGN_LEFT, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). ALIGN_CENTER, ALIGN_RIGHT }; - inline void setAlignment(Alignment align) { mAlignment = align; } + void setAlignment(Alignment align) { mAlignment = align; } - inline void setCursorChangedCallback(const std::function& func) - { mCursorChangedCallback = func; } + void setCursorChangedCallback(const std::function& func) + { + mCursorChangedCallback = func; + } - inline void setFont(const std::shared_ptr& font) + void setFont(const std::shared_ptr& font) { mFont = font; for (auto it = mEntries.begin(); it != mEntries.end(); it++) it->data.textCache.reset(); } - inline void setUppercase(bool /*uppercase*/) + void setUppercase(bool /*uppercase*/) { mUppercase = true; for (auto it = mEntries.begin(); it != mEntries.end(); it++) it->data.textCache.reset(); } - inline void setSelectorHeight(float selectorScale) { mSelectorHeight = selectorScale; } - inline void setSelectorOffsetY(float selectorOffsetY) { mSelectorOffsetY = selectorOffsetY; } - inline void setSelectorColor(unsigned int color) { mSelectorColor = color; } - inline void setSelectorColorEnd(unsigned int color) { mSelectorColorEnd = color; } - inline void setSelectorColorGradientHorizontal(bool horizontal) - { mSelectorColorGradientHorizontal = horizontal; } - inline void setSelectedColor(unsigned int color) { mSelectedColor = color; } - inline void setColor(unsigned int id, unsigned int color) { mColors[id] = color; } - inline void setLineSpacing(float lineSpacing) { mLineSpacing = lineSpacing; } + void setSelectorHeight(float selectorScale) { mSelectorHeight = selectorScale; } + void setSelectorOffsetY(float selectorOffsetY) { mSelectorOffsetY = selectorOffsetY; } + void setSelectorColor(unsigned int color) { mSelectorColor = color; } + void setSelectorColorEnd(unsigned int color) { mSelectorColorEnd = color; } + void setSelectorColorGradientHorizontal(bool horizontal) + { + mSelectorColorGradientHorizontal = horizontal; + } + void setSelectedColor(unsigned int color) { mSelectedColor = color; } + void setColor(unsigned int id, unsigned int color) { mColors[id] = color; } + void setLineSpacing(float lineSpacing) { mLineSpacing = lineSpacing; } protected: virtual void onScroll() override @@ -128,8 +130,9 @@ private: }; template -TextListComponent::TextListComponent(Window* window) : - IList(window), mSelectorImage(window) +TextListComponent::TextListComponent(Window* window) + : IList(window) + , mSelectorImage(window) { mMarqueeOffset = 0; mMarqueeOffset2 = 0; @@ -152,8 +155,7 @@ TextListComponent::TextListComponent(Window* window) : mColors[1] = 0x00FF00FF; } -template -void TextListComponent::render(const Transform4x4f& parentTrans) +template void TextListComponent::render(const Transform4x4f& parentTrans) { if (size() == 0) return; @@ -164,14 +166,14 @@ void TextListComponent::render(const Transform4x4f& parentTrans) int startEntry = 0; float y = 0; - const float entrySize = std::max(font->getHeight(1.0), - static_cast(font->getSize())) * mLineSpacing; + const float entrySize = + std::max(font->getHeight(1.0), static_cast(font->getSize())) * mLineSpacing; // Number of entries that can fit on the screen simultaneously. int screenCount = static_cast(mSize.y() / entrySize + 0.5f); if (size() >= screenCount) { - startEntry = mCursor - screenCount/2; + startEntry = mCursor - screenCount / 2; if (startEntry < 0) startEntry = 0; if (startEntry >= size() - screenCount) @@ -185,36 +187,31 @@ void TextListComponent::render(const Transform4x4f& parentTrans) // Draw selector bar. if (startEntry < listCutoff) { if (mSelectorImage.hasImage()) { - mSelectorImage.setPosition(0.0f, (mCursor - startEntry) * - entrySize + mSelectorOffsetY, 0.0f); + mSelectorImage.setPosition(0.0f, (mCursor - startEntry) * entrySize + mSelectorOffsetY, + 0.0f); mSelectorImage.render(trans); } else { Renderer::setMatrix(trans); - Renderer::drawRect( - 0.0f, - (mCursor - startEntry) * entrySize + mSelectorOffsetY, - mSize.x(), - mSelectorHeight, - mSelectorColor, - mSelectorColorEnd, - mSelectorColorGradientHorizontal); + Renderer::drawRect(0.0f, (mCursor - startEntry) * entrySize + mSelectorOffsetY, + mSize.x(), mSelectorHeight, mSelectorColor, mSelectorColorEnd, + mSelectorColorGradientHorizontal); } } if (Settings::getInstance()->getBool("DebugText")) { - Renderer::drawRect(mHorizontalMargin, 0.0f, mSize.x() - mHorizontalMargin * 2.0f, - mSize.y(), 0x00000033, 0x00000033); + Renderer::drawRect(mHorizontalMargin, 0.0f, mSize.x() - mHorizontalMargin * 2.0f, mSize.y(), + 0x00000033, 0x00000033); Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0x0000FF33, 0x0000FF33); } // Clip to inside margins. Vector3f dim(mSize.x(), mSize.y(), 0.0f); dim = trans * dim - trans.translation(); - Renderer::pushClipRect(Vector2i(static_cast(trans.translation().x() + - mHorizontalMargin), static_cast(trans.translation().y())), - Vector2i(static_cast(dim.x() - mHorizontalMargin * 2.0f), - static_cast(dim.y()))); + Renderer::pushClipRect( + Vector2i(static_cast(trans.translation().x() + mHorizontalMargin), + static_cast(trans.translation().y())), + Vector2i(static_cast(dim.x() - mHorizontalMargin * 2.0f), static_cast(dim.y()))); for (int i = startEntry; i < listCutoff; i++) { typename IList::Entry& entry = mEntries.at(static_cast(i)); @@ -226,9 +223,8 @@ void TextListComponent::render(const Transform4x4f& parentTrans) color = mColors[entry.data.colorId]; if (!entry.data.textCache) - entry.data.textCache = std::unique_ptr( - font->buildTextCache(mUppercase ? - Utils::String::toUpper(entry.name) : entry.name, 0, 0, 0x000000FF)); + entry.data.textCache = std::unique_ptr(font->buildTextCache( + mUppercase ? Utils::String::toUpper(entry.name) : entry.name, 0, 0, 0x000000FF)); // If a game is marked as hidden, lower the text opacity a lot. // If a game is marked to not be counted, lower the opacity a moderate amount. @@ -242,21 +238,21 @@ void TextListComponent::render(const Transform4x4f& parentTrans) Vector3f offset(0.0f, y, 0.0f); switch (mAlignment) { - case ALIGN_LEFT: - offset[0] = mHorizontalMargin; - break; - case ALIGN_CENTER: - offset[0] = static_cast((mSize.x() - - entry.data.textCache->metrics.size.x()) / 2.0f); - if (offset[0] < mHorizontalMargin) + case ALIGN_LEFT: offset[0] = mHorizontalMargin; - break; - case ALIGN_RIGHT: - offset[0] = (mSize.x() - entry.data.textCache->metrics.size.x()); - offset[0] -= mHorizontalMargin; - if (offset[0] < mHorizontalMargin) - offset[0] = mHorizontalMargin; - break; + break; + case ALIGN_CENTER: + offset[0] = + static_cast((mSize.x() - entry.data.textCache->metrics.size.x()) / 2.0f); + if (offset[0] < mHorizontalMargin) + offset[0] = mHorizontalMargin; + break; + case ALIGN_RIGHT: + offset[0] = (mSize.x() - entry.data.textCache->metrics.size.x()); + offset[0] -= mHorizontalMargin; + if (offset[0] < mHorizontalMargin) + offset[0] = mHorizontalMargin; + break; } // Render text. @@ -291,8 +287,7 @@ void TextListComponent::render(const Transform4x4f& parentTrans) GuiComponent::renderChildren(trans); } -template -bool TextListComponent::input(InputConfig* config, Input input) +template bool TextListComponent::input(InputConfig* config, Input input) { if (size() > 0) { if (input.value != 0) { @@ -324,12 +319,11 @@ bool TextListComponent::input(InputConfig* config, Input input) } } else { - if (config->isMappedLike("down", input) || - config->isMappedLike("up", input) || - config->isMappedLike("rightshoulder", input) || - config->isMappedLike("leftshoulder", input) || - config->isMappedLike("lefttrigger", input) || - config->isMappedLike("righttrigger", input)) + if (config->isMappedLike("down", input) || config->isMappedLike("up", input) || + config->isMappedLike("rightshoulder", input) || + config->isMappedLike("leftshoulder", input) || + config->isMappedLike("lefttrigger", input) || + config->isMappedLike("righttrigger", input)) stopScrolling(); } } @@ -337,8 +331,7 @@ bool TextListComponent::input(InputConfig* config, Input input) return GuiComponent::input(config, input); } -template -void TextListComponent::update(int deltaTime) +template void TextListComponent::update(int deltaTime) { listUpdate(deltaTime); @@ -351,8 +344,10 @@ void TextListComponent::update(int deltaTime) mMarqueeOffset2 = 0; // If we're not scrolling and this object's text exceeds our size, then marquee it. - const float textLength = mFont->sizeText(Utils::String::toUpper( - mEntries.at(static_cast(mCursor)).name)).x(); + const float textLength = mFont + ->sizeText(Utils::String::toUpper( + mEntries.at(static_cast(mCursor)).name)) + .x(); const float limit = mSize.x() - mHorizontalMargin * 2.0f; if (textLength > limit) { @@ -371,7 +366,8 @@ void TextListComponent::update(int deltaTime) mMarqueeTime -= maxTime; mMarqueeOffset = static_cast(Math::Scroll::loop(delay, scrollTime + returnTime, - static_cast(mMarqueeTime), scrollLength + returnLength)); + static_cast(mMarqueeTime), + scrollLength + returnLength)); if (mMarqueeOffset > (scrollLength - (limit - returnLength))) mMarqueeOffset2 = static_cast(mMarqueeOffset - (scrollLength + returnLength)); @@ -394,8 +390,7 @@ void TextListComponent::add(const std::string& name, const T& obj, unsigned i static_cast*>(this)->add(entry); } -template -void TextListComponent::onCursorChanged(const CursorState& state) +template void TextListComponent::onCursorChanged(const CursorState& state) { mMarqueeOffset = 0; mMarqueeOffset2 = 0; @@ -407,7 +402,9 @@ void TextListComponent::onCursorChanged(const CursorState& state) template void TextListComponent::applyTheme(const std::shared_ptr& theme, - const std::string& view, const std::string& element, unsigned int properties) + const std::string& view, + const std::string& element, + unsigned int properties) { GuiComponent::applyTheme(theme, view, element, properties); @@ -424,8 +421,8 @@ void TextListComponent::applyTheme(const std::shared_ptr& theme, if (elem->has("selectorColorEnd")) setSelectorColorEnd(elem->get("selectorColorEnd")); if (elem->has("selectorGradientType")) - setSelectorColorGradientHorizontal(!(elem->get - ("selectorGradientType").compare("horizontal"))); + setSelectorColorGradientHorizontal( + !(elem->get("selectorGradientType").compare("horizontal"))); if (elem->has("selectedColor")) setSelectedColor(elem->get("selectedColor")); if (elem->has("primaryColor")) @@ -435,8 +432,8 @@ void TextListComponent::applyTheme(const std::shared_ptr& theme, } setFont(Font::getFromTheme(elem, properties, mFont)); - const float selectorHeight = std::max(mFont->getHeight(1.0), - static_cast(mFont->getSize())) * mLineSpacing; + const float selectorHeight = + std::max(mFont->getHeight(1.0), static_cast(mFont->getSize())) * mLineSpacing; setSelectorHeight(selectorHeight); if (properties & ALIGNMENT) { @@ -453,8 +450,8 @@ void TextListComponent::applyTheme(const std::shared_ptr& theme, } if (elem->has("horizontalMargin")) { mHorizontalMargin = elem->get("horizontalMargin") * - (this->mParent ? this->mParent->getSize().x() : - static_cast(Renderer::getScreenWidth())); + (this->mParent ? this->mParent->getSize().x() : + static_cast(Renderer::getScreenWidth())); } } @@ -468,7 +465,7 @@ void TextListComponent::applyTheme(const std::shared_ptr& theme, setSelectorHeight(elem->get("selectorHeight") * Renderer::getScreenHeight()); if (elem->has("selectorOffsetY")) { float scale = this->mParent ? this->mParent->getSize().y() : - static_cast(Renderer::getScreenHeight()); + static_cast(Renderer::getScreenHeight()); setSelectorOffsetY(elem->get("selectorOffsetY") * scale); } else { diff --git a/es-core/src/components/VideoComponent.cpp b/es-core/src/components/VideoComponent.cpp index b7326be51..4ca19cc8e 100644 --- a/es-core/src/components/VideoComponent.cpp +++ b/es-core/src/components/VideoComponent.cpp @@ -8,39 +8,38 @@ #include "components/VideoComponent.h" -#include "resources/ResourceManager.h" -#include "utils/FileSystemUtil.h" #include "ThemeData.h" #include "Window.h" +#include "resources/ResourceManager.h" +#include "utils/FileSystemUtil.h" #include #define SCREENSAVER_FADE_IN_TIME 1100 #define MEDIA_VIEWER_FADE_IN_TIME 600 -VideoComponent::VideoComponent( - Window* window) - : GuiComponent(window), - mWindow(window), - mStaticImage(window), - mVideoHeight(0), - mVideoWidth(0), - mStartDelayed(false), - mIsPlaying(false), - mIsActuallyPlaying(false), - mPause(false), - mShowing(false), - mDisable(false), - mMediaViewerMode(false), - mScreensaverActive(false), - mScreensaverMode(false), - mGameLaunched(false), - mBlockPlayer(false), - mTargetIsMax(false), - mFadeIn(1.0), - mTargetSize(0, 0), - mVideoAreaPos(0, 0), - mVideoAreaSize(0, 0) +VideoComponent::VideoComponent(Window* window) + : GuiComponent(window) + , mWindow(window) + , mStaticImage(window) + , mVideoHeight(0) + , mVideoWidth(0) + , mStartDelayed(false) + , mIsPlaying(false) + , mIsActuallyPlaying(false) + , mPause(false) + , mShowing(false) + , mDisable(false) + , mMediaViewerMode(false) + , mScreensaverActive(false) + , mScreensaverMode(false) + , mGameLaunched(false) + , mBlockPlayer(false) + , mTargetIsMax(false) + , mFadeIn(1.0) + , mTargetSize(0, 0) + , mVideoAreaPos(0, 0) + , mVideoAreaSize(0, 0) { // Setup the default configuration. mConfig.showSnapshotDelay = false; @@ -79,11 +78,6 @@ bool VideoComponent::setVideo(std::string path) return false; } -void VideoComponent::setDefaultVideo() -{ - setVideo(mConfig.defaultVideoPath); -} - void VideoComponent::setImage(std::string path) { // Check that the image has changed. @@ -94,21 +88,6 @@ void VideoComponent::setImage(std::string path) mStaticImagePath = path; } -void VideoComponent::setScreensaverMode(bool isScreensaver) -{ - mScreensaverMode = isScreensaver; -} - -void VideoComponent::setMediaViewerMode(bool isMediaViewer) -{ - mMediaViewerMode = isMediaViewer; -} - -void VideoComponent::setOpacity(unsigned char opacity) -{ - mOpacity = opacity; -} - void VideoComponent::onShow() { mBlockPlayer = false; @@ -192,21 +171,6 @@ void VideoComponent::topWindow(bool isTop) manageState(); } -void VideoComponent::onOriginChanged() -{ - mStaticImage.setOrigin(mOrigin); -} - -void VideoComponent::onPositionChanged() -{ - mStaticImage.setPosition(mPosition); -} - -void VideoComponent::onSizeChanged() -{ - mStaticImage.onSizeChanged(); -} - void VideoComponent::render(const Transform4x4f& parentTrans) { if (!isVisible()) @@ -237,19 +201,22 @@ void VideoComponent::renderSnapshot(const Transform4x4f& parentTrans) // set to start playing the video immediately. Although this may seem a bit inconsistent it // simply looks better than leaving an empty space where the video would have been located. if (mWindow->getGuiStackSize() > 1 || (mConfig.showSnapshotNoVideo && mVideoPath.empty()) || - (mStartDelayed && mConfig.showSnapshotDelay)) { + (mStartDelayed && mConfig.showSnapshotDelay)) { mStaticImage.setOpacity(mOpacity); mStaticImage.render(parentTrans); } } -void VideoComponent::applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) +void VideoComponent::applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) { using namespace ThemeFlags; - GuiComponent::applyTheme(theme, view, element, (properties ^ ThemeFlags::SIZE) | - ((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0)); + GuiComponent::applyTheme(theme, view, element, + (properties ^ ThemeFlags::SIZE) | + ((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0)); const ThemeData::ThemeElement* elem = theme->getElement(view, element, "video"); @@ -257,8 +224,8 @@ void VideoComponent::applyTheme(const std::shared_ptr& theme, const s return; Vector2f scale = getParent() ? getParent()->getSize() : - Vector2f(static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight())); + Vector2f(static_cast(Renderer::getScreenWidth()), + static_cast(Renderer::getScreenHeight())); if (properties & ThemeFlags::SIZE) { if (elem->has("size")) { @@ -308,12 +275,12 @@ void VideoComponent::update(int deltaTime) // Fade in videos, the time period is a bit different between the screensaver, // media viewer and gamelist view. if (mScreensaverMode && mFadeIn < 1.0f) { - mFadeIn = Math::clamp(mFadeIn + (deltaTime / - static_cast(SCREENSAVER_FADE_IN_TIME)), 0.0f, 1.0f); + mFadeIn = Math::clamp(mFadeIn + (deltaTime / static_cast(SCREENSAVER_FADE_IN_TIME)), + 0.0f, 1.0f); } else if (mMediaViewerMode && mFadeIn < 1.0f) { - mFadeIn = Math::clamp(mFadeIn + (deltaTime / - static_cast(MEDIA_VIEWER_FADE_IN_TIME)), 0.0f, 1.0f); + mFadeIn = Math::clamp(mFadeIn + (deltaTime / static_cast(MEDIA_VIEWER_FADE_IN_TIME)), + 0.0f, 1.0f); } else if (mFadeIn < 1.0f) { mFadeIn = Math::clamp(mFadeIn + 0.01f, 0.0f, 1.0f); diff --git a/es-core/src/components/VideoComponent.h b/es-core/src/components/VideoComponent.h index 5905971aa..8a7653cc5 100644 --- a/es-core/src/components/VideoComponent.h +++ b/es-core/src/components/VideoComponent.h @@ -9,8 +9,8 @@ #ifndef ES_CORE_COMPONENTS_VIDEO_COMPONENT_H #define ES_CORE_COMPONENTS_VIDEO_COMPONENT_H -#include "components/ImageComponent.h" #include "GuiComponent.h" +#include "components/ImageComponent.h" #include @@ -34,15 +34,15 @@ public: // Loads the video at the given filepath. bool setVideo(std::string path); // Configures the component to show the default video. - void setDefaultVideo(); + void setDefaultVideo() { setVideo(mConfig.defaultVideoPath); } // Loads a static image that is displayed if the video cannot be played. void setImage(std::string path); // Sets whether we're in media viewer mode. - void setMediaViewerMode(bool isMediaViewer); + void setMediaViewerMode(bool isMediaViewer) { mMediaViewerMode = isMediaViewer; } // Sets whether we're in screensaver mode. - void setScreensaverMode(bool isScreensaver); + void setScreensaverMode(bool isScreensaver) { mScreensaverMode = isScreensaver; } // Set the opacity for the embedded static image. - void setOpacity(unsigned char opacity) override; + void setOpacity(unsigned char opacity) override { mOpacity = opacity; } virtual void onShow() override; virtual void onHide() override; @@ -57,15 +57,17 @@ public: virtual void topWindow(bool isTop) override; // These functions update the embedded static image. - void onOriginChanged() override; - void onPositionChanged() override; - void onSizeChanged() override; + void onOriginChanged() override { mStaticImage.setOrigin(mOrigin); } + void onPositionChanged() override { mStaticImage.setPosition(mPosition); } + void onSizeChanged() override { mStaticImage.onSizeChanged(); } void render(const Transform4x4f& parentTrans) override; void renderSnapshot(const Transform4x4f& parentTrans); - virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, - const std::string& element, unsigned int properties) override; + virtual void applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) override; virtual std::vector getHelpPrompts() override; @@ -76,24 +78,24 @@ public: // zero, no resizing. This can be set before or after a video is loaded. // setMaxSize() and setResize() are mutually exclusive. virtual void setResize(float width, float height) override = 0; - inline void setResize(const Vector2f& size) { setResize(size.x(), size.y()); } + void setResize(const Vector2f& size) { setResize(size.x(), size.y()); } // Resize the video to be as large as possible but fit within a box of this size. // This can be set before or after a video is loaded. // Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive. virtual void setMaxSize(float width, float height) = 0; - inline void setMaxSize(const Vector2f& size) { setMaxSize(size.x(), size.y()); } + void setMaxSize(const Vector2f& size) { setMaxSize(size.x(), size.y()); } private: // Start the video immediately. - virtual void startVideo() {}; + virtual void startVideo() {} // Stop the video. - virtual void stopVideo() {}; + virtual void stopVideo() {} // Pause the video when a game has been launched. - virtual void pauseVideo() {}; + virtual void pauseVideo() {} // Handle looping of the video. Must be called periodically. - virtual void handleLooping() {}; - virtual void updatePlayer() {}; + virtual void handleLooping() {} + virtual void updatePlayer() {} // Start the video after any configured delay. void startVideoWithDelay(); diff --git a/es-core/src/components/VideoFFmpegComponent.cpp b/es-core/src/components/VideoFFmpegComponent.cpp index ef64069fc..120d24c40 100644 --- a/es-core/src/components/VideoFFmpegComponent.cpp +++ b/es-core/src/components/VideoFFmpegComponent.cpp @@ -8,48 +8,44 @@ #include "components/VideoFFmpegComponent.h" -#include "resources/TextureResource.h" #include "AudioManager.h" #include "Settings.h" #include "Window.h" +#include "resources/TextureResource.h" #define DEBUG_VIDEO false -VideoFFmpegComponent::VideoFFmpegComponent( - Window* window) - : VideoComponent(window), - mFrameProcessingThread(nullptr), - mFormatContext(nullptr), - mVideoStream(nullptr), - mAudioStream(nullptr), - mVideoCodec(nullptr), - mAudioCodec(nullptr), - mVideoCodecContext(nullptr), - mAudioCodecContext(nullptr), - mVBufferSrcContext(nullptr), - mVBufferSinkContext(nullptr), - mVFilterGraph(nullptr), - mVFilterInputs(nullptr), - mVFilterOutputs(nullptr), - mABufferSrcContext(nullptr), - mABufferSinkContext(nullptr), - mAFilterGraph(nullptr), - mAFilterInputs(nullptr), - mAFilterOutputs(nullptr), - mVideoTimeBase(0.0l), - mVideoTargetQueueSize(0), - mAudioTargetQueueSize(0), - mAccumulatedTime(0), - mStartTimeAccumulation(false), - mDecodedFrame(false), - mEndOfVideo(false) +VideoFFmpegComponent::VideoFFmpegComponent(Window* window) + : VideoComponent(window) + , mFrameProcessingThread(nullptr) + , mFormatContext(nullptr) + , mVideoStream(nullptr) + , mAudioStream(nullptr) + , mVideoCodec(nullptr) + , mAudioCodec(nullptr) + , mVideoCodecContext(nullptr) + , mAudioCodecContext(nullptr) + , mVBufferSrcContext(nullptr) + , mVBufferSinkContext(nullptr) + , mVFilterGraph(nullptr) + , mVFilterInputs(nullptr) + , mVFilterOutputs(nullptr) + , mABufferSrcContext(nullptr) + , mABufferSinkContext(nullptr) + , mAFilterGraph(nullptr) + , mAFilterInputs(nullptr) + , mAFilterOutputs(nullptr) + , mVideoTimeBase(0.0l) + , mVideoTargetQueueSize(0) + , mAudioTargetQueueSize(0) + , mAccumulatedTime(0) + , mStartTimeAccumulation(false) + , mDecodedFrame(false) + , mEndOfVideo(false) { } -VideoFFmpegComponent::~VideoFFmpegComponent() -{ - stopVideo(); -} +VideoFFmpegComponent::~VideoFFmpegComponent() { stopVideo(); } void VideoFFmpegComponent::setResize(float width, float height) { @@ -96,7 +92,6 @@ void VideoFFmpegComponent::resize() mSize[1] = std::round(mSize[1]); mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x(); - } else { // If both components are set, we just stretch. @@ -127,8 +122,8 @@ void VideoFFmpegComponent::render(const Transform4x4f& parentTrans) unsigned int color; if (mDecodedFrame && mFadeIn < 1) { const unsigned int fadeIn = static_cast(mFadeIn * 255.0f); - color = Renderer::convertRGBAToABGR((fadeIn << 24) | - (fadeIn << 16) | (fadeIn << 8) | 255); + color = + Renderer::convertRGBAToABGR((fadeIn << 24) | (fadeIn << 16) | (fadeIn << 8) | 255); } else { color = 0xFFFFFFFF; @@ -139,13 +134,16 @@ void VideoFFmpegComponent::render(const Transform4x4f& parentTrans) // Render the black rectangle behind the video. if (mVideoRectangleCoords.size() == 4) { Renderer::drawRect(mVideoRectangleCoords[0], mVideoRectangleCoords[1], - mVideoRectangleCoords[2], mVideoRectangleCoords[3], 0x000000FF, 0x000000FF); + mVideoRectangleCoords[2], mVideoRectangleCoords[3], // Line break. + 0x000000FF, 0x000000FF); } + // clang-format off vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 0.0f }, color }; vertices[1] = { { 0.0f , mSize.y() }, { 0.0f, 1.0f }, color }; vertices[2] = { { mSize.x(), 0.0f }, { 1.0f, 0.0f }, color }; vertices[3] = { { mSize.x(), mSize.y() }, { 1.0f, 1.0f }, color }; + // clang-format on // Round vertices. for (int i = 0; i < 4; i++) @@ -167,8 +165,8 @@ void VideoFFmpegComponent::render(const Transform4x4f& parentTrans) int pictureHeight = 0; if (pictureSize > 0) { - tempPictureRGBA.insert(tempPictureRGBA.begin(), - mOutputPicture.pictureRGBA.begin(), mOutputPicture.pictureRGBA.end()); + tempPictureRGBA.insert(tempPictureRGBA.begin(), mOutputPicture.pictureRGBA.begin(), + mOutputPicture.pictureRGBA.end()); pictureWidth = mOutputPicture.width; pictureHeight = mOutputPicture.height; @@ -188,14 +186,14 @@ void VideoFFmpegComponent::render(const Transform4x4f& parentTrans) mTexture->bind(); - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) // Render scanlines if this option is enabled. However, if this is the media viewer // or the video screensaver, then skip this as the scanline rendering is then handled // in those modules as a postprocessing step. if ((!mScreensaverMode && !mMediaViewerMode) && - Settings::getInstance()->getBool("GamelistVideoScanlines")) + Settings::getInstance()->getBool("GamelistVideoScanlines")) vertices[0].shaders = Renderer::SHADER_SCANLINES; - #endif +#endif // Render it. Renderer::setMatrix(trans); @@ -215,16 +213,17 @@ void VideoFFmpegComponent::updatePlayer() mAudioMutex.lock(); if (mOutputAudio.size()) { AudioManager::getInstance()->processStream(&mOutputAudio.at(0), - static_cast(mOutputAudio.size())); + static_cast(mOutputAudio.size())); mOutputAudio.clear(); } mAudioMutex.unlock(); if (mIsActuallyPlaying && mStartTimeAccumulation) { - mAccumulatedTime += static_cast( - std::chrono::duration_cast - (std::chrono::high_resolution_clock::now() - - mTimeReference).count()) / 1000000000.0l; + mAccumulatedTime += + static_cast(std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - mTimeReference) + .count()) / + 1000000000.0l; } mTimeReference = std::chrono::high_resolution_clock::now(); @@ -232,7 +231,7 @@ void VideoFFmpegComponent::updatePlayer() if (!mFrameProcessingThread) { AudioManager::getInstance()->unmuteStream(); mFrameProcessingThread = - std::make_unique(&VideoFFmpegComponent::frameProcessing, this); + std::make_unique(&VideoFFmpegComponent::frameProcessing, this); } } @@ -291,7 +290,7 @@ bool VideoFFmpegComponent::setupVideoFilters() if (!(mVFilterGraph = avfilter_graph_alloc())) { LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): " - "Couldn't allocate filter graph"; + "Couldn't allocate filter graph"; return false; } @@ -302,14 +301,14 @@ bool VideoFFmpegComponent::setupVideoFilters() const AVFilter* bufferSrc = avfilter_get_by_name("buffer"); if (!bufferSrc) { LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): " - "Couldn't find \"buffer\" filter"; + "Couldn't find \"buffer\" filter"; return false; } const AVFilter* bufferSink = avfilter_get_by_name("buffersink"); if (!bufferSink) { LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): " - "Couldn't find \"buffersink\" filter"; + "Couldn't find \"buffersink\" filter"; return false; } @@ -322,41 +321,30 @@ bool VideoFFmpegComponent::setupVideoFilters() width += 16 - modulo; std::string filterArguments = - "width=" + std::to_string(width) + ":" + - "height=" + std::to_string(height) + - ":pix_fmt=" + av_get_pix_fmt_name(mVideoCodecContext->pix_fmt) + - ":time_base=" + std::to_string(mVideoStream->time_base.num) + "/" + - std::to_string(mVideoStream->time_base.den) + - ":sar=" + std::to_string(mVideoCodecContext->sample_aspect_ratio.num) + "/" + - std::to_string(mVideoCodecContext->sample_aspect_ratio.den); + "width=" + std::to_string(width) + ":" + "height=" + std::to_string(height) + + ":pix_fmt=" + av_get_pix_fmt_name(mVideoCodecContext->pix_fmt) + + ":time_base=" + std::to_string(mVideoStream->time_base.num) + "/" + + std::to_string(mVideoStream->time_base.den) + + ":sar=" + std::to_string(mVideoCodecContext->sample_aspect_ratio.num) + "/" + + std::to_string(mVideoCodecContext->sample_aspect_ratio.den); - returnValue = avfilter_graph_create_filter( - &mVBufferSrcContext, - bufferSrc, - "in", - filterArguments.c_str(), - nullptr, - mVFilterGraph); + returnValue = avfilter_graph_create_filter(&mVBufferSrcContext, bufferSrc, "in", + filterArguments.c_str(), nullptr, mVFilterGraph); if (returnValue < 0) { LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): " - "Couldn't create filter instance for buffer source: " << - av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); + "Couldn't create filter instance for buffer source: " + << av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); return false; } - returnValue = avfilter_graph_create_filter( - &mVBufferSinkContext, - bufferSink, - "out", - nullptr, - nullptr, - mVFilterGraph); + returnValue = avfilter_graph_create_filter(&mVBufferSinkContext, bufferSink, "out", nullptr, + nullptr, mVFilterGraph); if (returnValue < 0) { LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): " - "Couldn't create filter instance for buffer sink: " << - av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); + "Couldn't create filter instance for buffer sink: " + << av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); return false; } @@ -376,34 +364,32 @@ bool VideoFFmpegComponent::setupVideoFilters() // Whether to upscale the frame rate to 60 FPS. if (Settings::getInstance()->getBool("VideoUpscaleFrameRate")) { if (modulo > 0) - filterDescription = - "scale=width=" + std::to_string(width) + - ":height=" + std::to_string(height) + - ",fps=fps=60,"; + filterDescription = "scale=width=" + std::to_string(width) + + ":height=" + std::to_string(height) + ",fps=fps=60,"; else filterDescription = "fps=fps=60,"; // The "framerate" filter is a more advanced way to upscale the frame rate using // interpolation. However I have not been able to get this to work with slice // threading so the performance is poor. As such it's disabled for now. -// if (modulo > 0) -// filterDescription = -// "scale=width=" + std::to_string(width) + -// ":height=" + std::to_string(height) + -// ",framerate=fps=60,"; -// else -// filterDescription = "framerate=fps=60,"; + // if (modulo > 0) + // filterDescription = + // "scale=width=" + std::to_string(width) + + // ":height=" + std::to_string(height) + + // ",framerate=fps=60,"; + // else + // filterDescription = "framerate=fps=60,"; } filterDescription += "format=pix_fmts=" + std::string(av_get_pix_fmt_name(outPixFormats[0])); returnValue = avfilter_graph_parse_ptr(mVFilterGraph, filterDescription.c_str(), - &mVFilterInputs, &mVFilterOutputs, nullptr); + &mVFilterInputs, &mVFilterOutputs, nullptr); if (returnValue < 0) { LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): " - "Couldn't add graph filter: " << - av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); + "Couldn't add graph filter: " + << av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); return false; } @@ -411,8 +397,8 @@ bool VideoFFmpegComponent::setupVideoFilters() if (returnValue < 0) { LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): " - "Couldn't configure graph: " << - av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); + "Couldn't configure graph: " + << av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); return false; } @@ -431,7 +417,7 @@ bool VideoFFmpegComponent::setupAudioFilters() if (!(mAFilterGraph = avfilter_graph_alloc())) { LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): " - "Couldn't allocate filter graph"; + "Couldn't allocate filter graph"; return false; } @@ -442,55 +428,45 @@ bool VideoFFmpegComponent::setupAudioFilters() const AVFilter* bufferSrc = avfilter_get_by_name("abuffer"); if (!bufferSrc) { LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): " - "Couldn't find \"abuffer\" filter"; + "Couldn't find \"abuffer\" filter"; return false; } const AVFilter* bufferSink = avfilter_get_by_name("abuffersink"); if (!bufferSink) { LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): " - "Couldn't find \"abuffersink\" filter"; + "Couldn't find \"abuffersink\" filter"; return false; } char channelLayout[512]; - av_get_channel_layout_string(channelLayout, sizeof(channelLayout), - mAudioCodecContext->channels, mAudioCodecContext->channel_layout); + av_get_channel_layout_string(channelLayout, sizeof(channelLayout), mAudioCodecContext->channels, + mAudioCodecContext->channel_layout); std::string filterArguments = - "time_base=" + std::to_string(mAudioStream->time_base.num) + "/" + - std::to_string(mAudioStream->time_base.den) + - ":sample_rate=" + std::to_string(mAudioCodecContext->sample_rate) + - ":sample_fmt=" + av_get_sample_fmt_name(mAudioCodecContext->sample_fmt) + - ":channel_layout=" + channelLayout; + "time_base=" + std::to_string(mAudioStream->time_base.num) + "/" + + std::to_string(mAudioStream->time_base.den) + + ":sample_rate=" + std::to_string(mAudioCodecContext->sample_rate) + + ":sample_fmt=" + av_get_sample_fmt_name(mAudioCodecContext->sample_fmt) + + ":channel_layout=" + channelLayout; - returnValue = avfilter_graph_create_filter( - &mABufferSrcContext, - bufferSrc, - "in", - filterArguments.c_str(), - nullptr, - mAFilterGraph); + returnValue = avfilter_graph_create_filter(&mABufferSrcContext, bufferSrc, "in", + filterArguments.c_str(), nullptr, mAFilterGraph); if (returnValue < 0) { LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): " - "Couldn't create filter instance for buffer source: " << - av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); + "Couldn't create filter instance for buffer source: " + << av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); return false; } - returnValue = avfilter_graph_create_filter( - &mABufferSinkContext, - bufferSink, - "out", - nullptr, - nullptr, - mAFilterGraph); + returnValue = avfilter_graph_create_filter(&mABufferSinkContext, bufferSink, "out", nullptr, + nullptr, mAFilterGraph); if (returnValue < 0) { LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): " - "Couldn't create filter instance for buffer sink: " << - av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); + "Couldn't create filter instance for buffer sink: " + << av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); return false; } @@ -506,18 +482,18 @@ bool VideoFFmpegComponent::setupAudioFilters() mAFilterOutputs->next = nullptr; std::string filterDescription = - "aresample=" + std::to_string(outSampleRates[0]) + "," + - "aformat=sample_fmts=" + av_get_sample_fmt_name(outSampleFormats[0]) + - ":channel_layouts=stereo," - "asetnsamples=n=1024:p=0"; + "aresample=" + std::to_string(outSampleRates[0]) + "," + + "aformat=sample_fmts=" + av_get_sample_fmt_name(outSampleFormats[0]) + + ":channel_layouts=stereo," + "asetnsamples=n=1024:p=0"; returnValue = avfilter_graph_parse_ptr(mAFilterGraph, filterDescription.c_str(), - &mAFilterInputs, &mAFilterOutputs, nullptr); + &mAFilterInputs, &mAFilterOutputs, nullptr); if (returnValue < 0) { LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): " - "Couldn't add graph filter: " << - av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); + "Couldn't add graph filter: " + << av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); return false; } @@ -525,8 +501,8 @@ bool VideoFFmpegComponent::setupAudioFilters() if (returnValue < 0) { LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): " - "Couldn't configure graph: " << - av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); + "Couldn't configure graph: " + << av_make_error_string(errorMessage, sizeof(errorMessage), returnValue); return false; } @@ -544,20 +520,20 @@ void VideoFFmpegComponent::readFrames() return; if (mVideoCodecContext && mFormatContext) { - if (mVideoFrameQueue.size() < mVideoTargetQueueSize || (mAudioStreamIndex >= 0 && - mAudioFrameQueue.size() < mAudioTargetQueueSize)) { + if (mVideoFrameQueue.size() < mVideoTargetQueueSize || + (mAudioStreamIndex >= 0 && mAudioFrameQueue.size() < mAudioTargetQueueSize)) { while ((readFrameReturn = av_read_frame(mFormatContext, mPacket)) >= 0) { if (mPacket->stream_index == mVideoStreamIndex) { if (!avcodec_send_packet(mVideoCodecContext, mPacket) && - !avcodec_receive_frame(mVideoCodecContext, mVideoFrame)) { + !avcodec_receive_frame(mVideoCodecContext, mVideoFrame)) { // We have a video frame that needs conversion to RGBA format. - int returnValue = av_buffersrc_add_frame_flags(mVBufferSrcContext, - mVideoFrame, AV_BUFFERSRC_FLAG_KEEP_REF); + int returnValue = av_buffersrc_add_frame_flags( + mVBufferSrcContext, mVideoFrame, AV_BUFFERSRC_FLAG_KEEP_REF); if (returnValue < 0) { LOG(LogError) << "VideoFFmpegComponent::readFrames(): " - "Couldn't add video frame to buffer source"; + "Couldn't add video frame to buffer source"; } av_packet_unref(mPacket); @@ -566,15 +542,15 @@ void VideoFFmpegComponent::readFrames() } else if (mPacket->stream_index == mAudioStreamIndex) { if (!avcodec_send_packet(mAudioCodecContext, mPacket) && - !avcodec_receive_frame(mAudioCodecContext, mAudioFrame)) { + !avcodec_receive_frame(mAudioCodecContext, mAudioFrame)) { // We have an audio frame that needs conversion and resampling. - int returnValue = av_buffersrc_add_frame_flags(mABufferSrcContext, - mAudioFrame, AV_BUFFERSRC_FLAG_KEEP_REF); + int returnValue = av_buffersrc_add_frame_flags( + mABufferSrcContext, mAudioFrame, AV_BUFFERSRC_FLAG_KEEP_REF); if (returnValue < 0) { LOG(LogError) << "VideoFFmpegComponent::readFrames(): " - "Couldn't add audio frame to buffer source"; + "Couldn't add audio frame to buffer source"; } av_packet_unref(mPacket); @@ -606,21 +582,20 @@ void VideoFFmpegComponent::getProcessedFrames() // (picture) should be displayed. The packet DTS value is used for the basis of // the calculation as per the recommendation in the FFmpeg documentation for // the av_read_frame function. - double pts = static_cast(mVideoFrameResampled->pkt_dts) * - av_q2d(mVideoStream->time_base); + double pts = + static_cast(mVideoFrameResampled->pkt_dts) * av_q2d(mVideoStream->time_base); // Needs to be adjusted if changing the rate? double frameDuration = static_cast(mVideoFrameResampled->pkt_duration) * - av_q2d(mVideoStream->time_base); + av_q2d(mVideoStream->time_base); currFrame.pts = pts; currFrame.frameDuration = frameDuration; int bufferSize = mVideoFrameResampled->width * mVideoFrameResampled->height * 4; - currFrame.frameRGBA.insert(currFrame.frameRGBA.begin(), - &mVideoFrameResampled->data[0][0], - &mVideoFrameResampled->data[0][bufferSize]); + currFrame.frameRGBA.insert(currFrame.frameRGBA.begin(), &mVideoFrameResampled->data[0][0], + &mVideoFrameResampled->data[0][bufferSize]); mVideoFrameQueue.push(currFrame); av_frame_unref(mVideoFrameResampled); @@ -629,8 +604,8 @@ void VideoFFmpegComponent::getProcessedFrames() // Audio frames. // When resampling, we may not always get a frame returned from the sink as there may not // have been enough data available to the filter. - while (mAudioCodecContext && av_buffersink_get_frame(mABufferSinkContext, - mAudioFrameResampled) >= 0) { + while (mAudioCodecContext && + av_buffersink_get_frame(mABufferSinkContext, mAudioFrameResampled) >= 0) { AudioFrame currFrame; AVRational timeBase; @@ -644,11 +619,11 @@ void VideoFFmpegComponent::getProcessedFrames() currFrame.pts = pts; int bufferSize = mAudioFrameResampled->nb_samples * mAudioFrameResampled->channels * - av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT); + av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT); currFrame.resampledData.insert(currFrame.resampledData.begin(), - &mAudioFrameResampled->data[0][0], - &mAudioFrameResampled->data[0][bufferSize]); + &mAudioFrameResampled->data[0][0], + &mAudioFrameResampled->data[0][bufferSize]); mAudioFrameQueue.push(currFrame); av_frame_unref(mAudioFrameResampled); @@ -674,22 +649,21 @@ void VideoFFmpegComponent::outputFrames() if (mAudioFrameQueue.front().pts < mAccumulatedTime + AUDIO_BUFFER) { // Enable only when needed, as this generates a lot of debug output. if (DEBUG_VIDEO) { - LOG(LogDebug) << "Processing audio frame with PTS: " << - mAudioFrameQueue.front().pts; - LOG(LogDebug) << "Total audio frames processed / audio frame queue size: " << - mAudioFrameCount << " / " << std::to_string(mAudioFrameQueue.size()); + LOG(LogDebug) << "Processing audio frame with PTS: " + << mAudioFrameQueue.front().pts; + LOG(LogDebug) << "Total audio frames processed / audio frame queue size: " + << mAudioFrameCount << " / " + << std::to_string(mAudioFrameQueue.size()); } bool outputSound = false; if ((!mScreensaverMode && !mMediaViewerMode) && - Settings::getInstance()->getBool("GamelistVideoAudio")) + Settings::getInstance()->getBool("GamelistVideoAudio")) outputSound = true; - else if (mScreensaverMode && Settings::getInstance()-> - getBool("ScreensaverVideoAudio")) + else if (mScreensaverMode && Settings::getInstance()->getBool("ScreensaverVideoAudio")) outputSound = true; - else if (mMediaViewerMode && Settings::getInstance()-> - getBool("MediaViewerVideoAudio")) + else if (mMediaViewerMode && Settings::getInstance()->getBool("MediaViewerVideoAudio")) outputSound = true; if (outputSound) { @@ -697,8 +671,8 @@ void VideoFFmpegComponent::outputFrames() mAudioMutex.lock(); mOutputAudio.insert(mOutputAudio.end(), - mAudioFrameQueue.front().resampledData.begin(), - mAudioFrameQueue.front().resampledData.end()); + mAudioFrameQueue.front().resampledData.begin(), + mAudioFrameQueue.front().resampledData.end()); mAudioMutex.unlock(); } @@ -717,10 +691,11 @@ void VideoFFmpegComponent::outputFrames() if (mVideoFrameQueue.front().pts < mAccumulatedTime) { // Enable only when needed, as this generates a lot of debug output. if (DEBUG_VIDEO) { - LOG(LogDebug) << "Processing video frame with PTS: " << - mVideoFrameQueue.front().pts; - LOG(LogDebug) << "Total video frames processed / video frame queue size: " << - mVideoFrameCount << " / " << std::to_string(mVideoFrameQueue.size()); + LOG(LogDebug) << "Processing video frame with PTS: " + << mVideoFrameQueue.front().pts; + LOG(LogDebug) << "Total video frames processed / video frame queue size: " + << mVideoFrameCount << " / " + << std::to_string(mVideoFrameQueue.size()); } mPictureMutex.lock(); @@ -734,7 +709,7 @@ void VideoFFmpegComponent::outputFrames() // rates close to, or at, the rendering frame rate, for example 59.94 and 60 FPS. if (mDecodedFrame && !mOutputPicture.hasBeenRendered) { double timeDifference = mAccumulatedTime - mVideoFrameQueue.front().pts - - mVideoFrameQueue.front().frameDuration * 2.0l; + mVideoFrameQueue.front().frameDuration * 2.0l; if (timeDifference < mVideoFrameQueue.front().frameDuration) { mPictureMutex.unlock(); break; @@ -743,8 +718,8 @@ void VideoFFmpegComponent::outputFrames() mOutputPicture.pictureRGBA.clear(); mOutputPicture.pictureRGBA.insert(mOutputPicture.pictureRGBA.begin(), - mVideoFrameQueue.front().frameRGBA.begin(), - mVideoFrameQueue.front().frameRGBA.end()); + mVideoFrameQueue.front().frameRGBA.begin(), + mVideoFrameQueue.front().frameRGBA.end()); mOutputPicture.width = mVideoFrameQueue.front().width; mOutputPicture.height = mVideoFrameQueue.front().height; @@ -802,10 +777,10 @@ void VideoFFmpegComponent::calculateBlackRectangle() rectHeight = mSize.y(); } // Populate the rectangle coordinates to be used in render(). - mVideoRectangleCoords.push_back(std::round(mVideoAreaPos.x() - - rectWidth * mOrigin.x())); - mVideoRectangleCoords.push_back(std::round(mVideoAreaPos.y() - - rectHeight * mOrigin.y())); + mVideoRectangleCoords.push_back( + std::round(mVideoAreaPos.x() - rectWidth * mOrigin.x())); + mVideoRectangleCoords.push_back( + std::round(mVideoAreaPos.y() - rectHeight * mOrigin.y())); mVideoRectangleCoords.push_back(std::round(rectWidth)); mVideoRectangleCoords.push_back(std::round(rectHeight)); } @@ -852,14 +827,16 @@ void VideoFFmpegComponent::startVideo() // File operations and basic setup. if (avformat_open_input(&mFormatContext, filePath.c_str(), nullptr, nullptr)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't open video file \"" << - mVideoPath << "\""; + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't open video file \"" + << mVideoPath << "\""; return; } if (avformat_find_stream_info(mFormatContext, nullptr)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't read stream information" - "from video file \"" << mVideoPath << "\""; + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't read stream information from video file \"" + << mVideoPath << "\""; return; } @@ -868,12 +845,13 @@ void VideoFFmpegComponent::startVideo() // Video stream setup. - mVideoStreamIndex = av_find_best_stream( - mFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0); + mVideoStreamIndex = + av_find_best_stream(mFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0); if (mVideoStreamIndex < 0) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't retrieve video stream " - "for file \"" << mVideoPath << "\""; + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't retrieve video stream for file \"" + << mVideoPath << "\""; return; } @@ -884,16 +862,18 @@ void VideoFFmpegComponent::startVideo() mVideoCodec = const_cast(avcodec_find_decoder(mVideoStream->codecpar->codec_id)); if (!mVideoCodec) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't find a suitable video " - "codec for file \"" << mVideoPath << "\""; + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't find a suitable video codec for file \"" + << mVideoPath << "\""; return; } mVideoCodecContext = avcodec_alloc_context3(mVideoCodec); if (!mVideoCodec) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't allocate video " - "codec context for file \"" << mVideoPath << "\""; + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't allocate video codec context for file \"" + << mVideoPath << "\""; return; } @@ -901,35 +881,38 @@ void VideoFFmpegComponent::startVideo() mVideoCodecContext->flags |= AV_CODEC_FLAG_TRUNCATED; if (avcodec_parameters_to_context(mVideoCodecContext, mVideoStream->codecpar)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't fill the video " - "codec context parameters for file \"" << mVideoPath << "\""; + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't fill the video codec context parameters for file \"" + << mVideoPath << "\""; return; } if (avcodec_open2(mVideoCodecContext, mVideoCodec, nullptr)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't initialize the " - "video codec context for file \"" << mVideoPath << "\""; + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't initialize the video codec context for file \"" + << mVideoPath << "\""; return; } // Audio stream setup, optional as some videos may not have any audio tracks. - mAudioStreamIndex = av_find_best_stream( - mFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); + mAudioStreamIndex = + av_find_best_stream(mFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); if (mAudioStreamIndex < 0) { - LOG(LogDebug) << "VideoFFmpegComponent::startVideo(): Couldn't retrieve audio stream " - "for file \"" << mVideoPath << "\""; + LOG(LogDebug) << "VideoFFmpegComponent::startVideo(): " + "Couldn't retrieve audio stream for file \"" + << mVideoPath << "\""; } if (mAudioStreamIndex >= 0) { mAudioStream = mFormatContext->streams[mAudioStreamIndex]; - mAudioCodec = const_cast( - avcodec_find_decoder(mAudioStream->codecpar->codec_id)); + mAudioCodec = + const_cast(avcodec_find_decoder(mAudioStream->codecpar->codec_id)); if (!mAudioCodec) { - LOG(LogError) << "Couldn't find a suitable audio codec for file \"" << - mVideoPath << "\""; + LOG(LogError) << "Couldn't find a suitable audio codec for file \"" << mVideoPath + << "\""; return; } @@ -943,14 +926,16 @@ void VideoFFmpegComponent::startVideo() mAudioCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; if (avcodec_parameters_to_context(mAudioCodecContext, mAudioStream->codecpar)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't fill the audio " - "codec context parameters for file \"" << mVideoPath << "\""; + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't fill the audio codec context parameters for file \"" + << mVideoPath << "\""; return; } if (avcodec_open2(mAudioCodecContext, mAudioCodec, nullptr)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't initialize the " - "audio codec context for file \"" << mVideoPath << "\""; + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't initialize the audio codec context for file \"" + << mVideoPath << "\""; return; } } @@ -959,7 +944,7 @@ void VideoFFmpegComponent::startVideo() // Set some reasonable target queue sizes (buffers). mVideoTargetQueueSize = static_cast(av_q2d(mVideoStream->avg_frame_rate) / 2.0l); - if (mAudioStreamIndex >=0) + if (mAudioStreamIndex >= 0) mAudioTargetQueueSize = mAudioStream->codecpar->channels * 15; else mAudioTargetQueueSize = 30; @@ -1035,7 +1020,7 @@ void VideoFFmpegComponent::handleLooping() // If the screensaver video swap time is set to 0, it means we should // skip to the next game when the video has finished playing. if (mScreensaverMode && - Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") == 0) { + Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") == 0) { mWindow->screensaverTriggerNextGame(); } else { diff --git a/es-core/src/components/VideoFFmpegComponent.h b/es-core/src/components/VideoFFmpegComponent.h index 619bb7f03..d7a134be8 100644 --- a/es-core/src/components/VideoFFmpegComponent.h +++ b/es-core/src/components/VideoFFmpegComponent.h @@ -14,8 +14,7 @@ #include "VideoComponent.h" -extern "C" -{ +extern "C" { #include #include #include @@ -89,8 +88,8 @@ private: AVFormatContext* mFormatContext; AVStream* mVideoStream; AVStream* mAudioStream; - AVCodec *mVideoCodec; - AVCodec *mAudioCodec; + AVCodec* mVideoCodec; + AVCodec* mAudioCodec; AVCodecContext* mVideoCodecContext; AVCodecContext* mAudioCodecContext; int mVideoStreamIndex; diff --git a/es-core/src/components/VideoOmxComponent.cpp b/es-core/src/components/VideoOmxComponent.cpp index f5dc43d06..4d537f244 100644 --- a/es-core/src/components/VideoOmxComponent.cpp +++ b/es-core/src/components/VideoOmxComponent.cpp @@ -9,10 +9,10 @@ #if defined(_RPI_) #include "components/VideoOmxComponent.h" -#include "renderers/Renderer.h" -#include "utils/StringUtil.h" #include "AudioManager.h" #include "Settings.h" +#include "renderers/Renderer.h" +#include "utils/StringUtil.h" #include #include @@ -21,18 +21,19 @@ class VolumeControl { public: - static std::shared_ptr & getInstance(); + static std::shared_ptr& getInstance(); int getVolume() const; }; -VideoOmxComponent::VideoOmxComponent(Window* window) : - VideoComponent(window), - mPlayerPid(-1) +VideoOmxComponent::VideoOmxComponent(Window* window) + : VideoComponent(window) + , mPlayerPid(-1) { } VideoOmxComponent::~VideoOmxComponent() { + // Stop video when destroyed. stopVideo(); } @@ -79,8 +80,8 @@ void VideoOmxComponent::startVideo() mPlayingVideoPath = mVideoPath; // Disable AudioManager so video can play, in case we're requesting ALSA. - if (Utils::String::startsWith(Settings::getInstance()-> - getString("OMXAudioDev").c_str(), "alsa")) + if (Utils::String::startsWith(Settings::getInstance()->getString("OMXAudioDev").c_str(), + "alsa")) AudioManager::getInstance()->deinit(); // Start the player process. @@ -112,66 +113,84 @@ void VideoOmxComponent::startVideo() const int x2 = static_cast(x1 + mSize.x()); const int y2 = static_cast(y1 + mSize.y()); sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2); - } - break; + } break; case 1: { - const int x1 = static_cast(Renderer::getWindowWidth() - - Renderer::getScreenOffsetY() - y - mSize.y()); + const int x1 = + static_cast(Renderer::getWindowWidth() - + Renderer::getScreenOffsetY() - y - mSize.y()); const int y1 = static_cast(Renderer::getScreenOffsetX() + x); const int x2 = static_cast(x1 + mSize.y()); const int y2 = static_cast(y1 + mSize.x()); sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2); - } - break; + } break; case 2: { - const int x1 = static_cast(Renderer::getWindowWidth() - - Renderer::getScreenOffsetX() - x - mSize.x()); - const int y1 = static_cast(Renderer::getWindowHeight() - - Renderer::getScreenOffsetY() - y - mSize.y()); + const int x1 = + static_cast(Renderer::getWindowWidth() - + Renderer::getScreenOffsetX() - x - mSize.x()); + const int y1 = + static_cast(Renderer::getWindowHeight() - + Renderer::getScreenOffsetY() - y - mSize.y()); const int x2 = static_cast(x1 + mSize.x()); const int y2 = static_cast(y1 + mSize.y()); sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2); - } - break; + } break; case 3: { const int x1 = static_cast(Renderer::getScreenOffsetY() + y); - const int y1 = static_cast(Renderer::getWindowHeight() - - Renderer::getScreenOffsetX() - x - mSize.x()); + const int y1 = + static_cast(Renderer::getWindowHeight() - + Renderer::getScreenOffsetX() - x - mSize.x()); const int x2 = static_cast(x1 + mSize.y()); const int y2 = static_cast(y1 + mSize.x()); sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2); - } - break; + } break; } // Rotate the video. switch (Renderer::getScreenRotate()) { - case 0: { sprintf(buf2, "%d", static_cast(0)); } break; - case 1: { sprintf(buf2, "%d", static_cast(90)); } break; - case 2: { sprintf(buf2, "%d", static_cast(180)); } break; - case 3: { sprintf(buf2, "%d", static_cast(270)); } break; + case 0: { + sprintf(buf2, "%d", static_cast(0)); + } break; + case 1: { + sprintf(buf2, "%d", static_cast(90)); + } break; + case 2: { + sprintf(buf2, "%d", static_cast(180)); + } break; + case 3: { + sprintf(buf2, "%d", static_cast(270)); + } break; } // We need to specify the layer of 10000 or above to ensure the video is // displayed on top of our SDL display. - const char* argv[] = { "", "--layer", "10010", "--loop", "--no-osd", - "--aspect-mode", "letterbox", "--vol", "0", "-o", "both", - "--win", buf1, "--orientation", buf2, "", "", "", "", "", "", - "", "", "", "", "", NULL }; + const char* argv[] = { "", "--layer", + "10010", "--loop", + "--no-osd", "--aspect-mode", + "letterbox", "--vol", + "0", "-o", + "both", "--win", + buf1, "--orientation", + buf2, "", + "", "", + "", "", + "", "", + "", "", + "", "", + NULL }; // Check if we want to mute the audio. if ((!Settings::getInstance()->getBool("GamelistVideoAudio") || - static_cast(VolumeControl::getInstance()->getVolume()) == 0) || - (!Settings::getInstance()->getBool("ScreensaverVideoAudio") && - mScreensaverMode)) { + static_cast(VolumeControl::getInstance()->getVolume()) == 0) || + (!Settings::getInstance()->getBool("ScreensaverVideoAudio") && + mScreensaverMode)) { argv[8] = "-1000000"; } else { float percentVolume = - static_cast(VolumeControl::getInstance()->getVolume()); + static_cast(VolumeControl::getInstance()->getVolume()); int OMXVolume = static_cast((percentVolume - 98) * 105); argv[8] = std::to_string(OMXVolume).c_str(); } diff --git a/es-core/src/components/VideoVlcComponent.cpp b/es-core/src/components/VideoVlcComponent.cpp index 077f6bcea..60dd46b83 100644 --- a/es-core/src/components/VideoVlcComponent.cpp +++ b/es-core/src/components/VideoVlcComponent.cpp @@ -10,12 +10,12 @@ #include "components/VideoVlcComponent.h" -#include "renderers/Renderer.h" -#include "resources/TextureResource.h" -#include "utils/StringUtil.h" #include "AudioManager.h" #include "Settings.h" #include "Window.h" +#include "renderers/Renderer.h" +#include "resources/TextureResource.h" +#include "utils/StringUtil.h" #if defined(__APPLE__) #include "utils/FileSystemUtil.h" @@ -26,19 +26,18 @@ #include #if defined(_WIN64) -#include #include +#include #endif libvlc_instance_t* VideoVlcComponent::mVLC = nullptr; -VideoVlcComponent::VideoVlcComponent( - Window* window) - : VideoComponent(window), - mMediaPlayer(nullptr), - mMedia(nullptr), - mContext({}), - mHasSetAudioVolume(false) +VideoVlcComponent::VideoVlcComponent(Window* window) + : VideoComponent(window) + , mMediaPlayer(nullptr) + , mMedia(nullptr) + , mContext({}) + , mHasSetAudioVolume(false) { // Get an empty texture for rendering the video. mTexture = TextureResource::get(""); @@ -86,7 +85,7 @@ void VideoVlcComponent::setupVLC() if (!mVLC) { const char* args[] = { "--quiet" }; - #if defined(__APPLE__) +#if defined(__APPLE__) // It's required to set the VLC_PLUGIN_PATH variable on macOS, or the libVLC // initialization will fail (with no error message). std::string vlcPluginPath = Utils::FileSystem::getExePath() + "/plugins"; @@ -94,7 +93,7 @@ void VideoVlcComponent::setupVLC() setenv("VLC_PLUGIN_PATH", vlcPluginPath.c_str(), 1); else setenv("VLC_PLUGIN_PATH", "/Applications/VLC.app/Contents/MacOS/plugins/", 1); - #endif +#endif mVLC = libvlc_new(1, args); } @@ -105,7 +104,8 @@ void VideoVlcComponent::setupContext() if (!mContext.valid) { // Create an RGBA surface to render the video into. mContext.surface = SDL_CreateRGBSurface(SDL_SWSURFACE, static_cast(mVideoWidth), - static_cast(mVideoHeight), 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); + static_cast(mVideoHeight), 32, 0xff000000, + 0x00ff0000, 0x0000ff00, 0x000000ff); mContext.mutex = SDL_CreateMutex(); mContext.valid = true; resize(); @@ -148,7 +148,6 @@ void VideoVlcComponent::resize() mSize[1] = std::round(mSize[1]); mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x(); - } else { // If both components are set, we just stretch. @@ -196,8 +195,8 @@ void VideoVlcComponent::render(const Transform4x4f& parentTrans) unsigned int color; if (mFadeIn < 1) { const unsigned int fadeIn = static_cast(mFadeIn * 255.0f); - color = Renderer::convertRGBAToABGR((fadeIn << 24) | - (fadeIn << 16) | (fadeIn << 8) | 255); + color = + Renderer::convertRGBAToABGR((fadeIn << 24) | (fadeIn << 16) | (fadeIn << 8) | 255); } else { color = 0xFFFFFFFF; @@ -209,13 +208,16 @@ void VideoVlcComponent::render(const Transform4x4f& parentTrans) // Render the black rectangle behind the video. if (mVideoRectangleCoords.size() == 4) { Renderer::drawRect(mVideoRectangleCoords[0], mVideoRectangleCoords[1], - mVideoRectangleCoords[2], mVideoRectangleCoords[3], 0x000000FF, 0x000000FF); + mVideoRectangleCoords[2], mVideoRectangleCoords[3], // Line break. + 0x000000FF, 0x000000FF); } + // clang-format off vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 0.0f }, color }; vertices[1] = { { 0.0f , mSize.y() }, { 0.0f, 1.0f }, color }; vertices[2] = { { mSize.x(), 0.0f }, { 1.0f, 0.0f }, color }; vertices[3] = { { mSize.x(), mSize.y() }, { 1.0f, 1.0f }, color }; + // clang-format on // Round vertices. for (int i = 0; i < 4; i++) @@ -223,17 +225,18 @@ void VideoVlcComponent::render(const Transform4x4f& parentTrans) // Build a texture for the video frame. mTexture->initFromPixels(reinterpret_cast(mContext.surface->pixels), - mContext.surface->w, mContext.surface->h); + mContext.surface->w, mContext.surface->h); mTexture->bind(); - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) // Render scanlines if this option is enabled. However, if this is the media viewer // or the video screensaver, then skip this as the scanline rendering is then handled // in those modules as a postprocessing step. if ((!mScreensaverMode && !mMediaViewerMode) && - Settings::getInstance()->getBool("GamelistVideoScanlines")) + Settings::getInstance()->getBool("GamelistVideoScanlines")) { vertices[0].shaders = Renderer::SHADER_SCANLINES; - #endif + } +#endif // Render it. Renderer::setMatrix(trans); @@ -283,10 +286,10 @@ void VideoVlcComponent::calculateBlackRectangle() rectHeight = mSize.y(); } // Populate the rectangle coordinates to be used in render(). - mVideoRectangleCoords.push_back(std::round(mVideoAreaPos.x() - - rectWidth * mOrigin.x())); - mVideoRectangleCoords.push_back(std::round(mVideoAreaPos.y() - - rectHeight * mOrigin.y())); + mVideoRectangleCoords.push_back( + std::round(mVideoAreaPos.x() - rectWidth * mOrigin.x())); + mVideoRectangleCoords.push_back( + std::round(mVideoAreaPos.y() - rectHeight * mOrigin.y())); mVideoRectangleCoords.push_back(std::round(rectWidth)); mVideoRectangleCoords.push_back(std::round(rectHeight)); } @@ -310,20 +313,18 @@ void VideoVlcComponent::setAudioVolume() bool outputSound = false; if ((!mScreensaverMode && !mMediaViewerMode) && - Settings::getInstance()->getBool("GamelistVideoAudio")) + Settings::getInstance()->getBool("GamelistVideoAudio")) outputSound = true; - else if (mScreensaverMode && Settings::getInstance()-> - getBool("ScreensaverVideoAudio")) + else if (mScreensaverMode && Settings::getInstance()->getBool("ScreensaverVideoAudio")) outputSound = true; - else if (mMediaViewerMode && Settings::getInstance()-> - getBool("MediaViewerVideoAudio")) + else if (mMediaViewerMode && Settings::getInstance()->getBool("MediaViewerVideoAudio")) outputSound = true; if (outputSound) { if (libvlc_audio_get_mute(mMediaPlayer) == 1) libvlc_audio_set_mute(mMediaPlayer, 0); libvlc_audio_set_volume(mMediaPlayer, - Settings::getInstance()->getInt("SoundVolumeVideos")); + Settings::getInstance()->getInt("SoundVolumeVideos")); } else { libvlc_audio_set_volume(mMediaPlayer, 0); @@ -339,11 +340,11 @@ void VideoVlcComponent::startVideo() mVideoWidth = 0; mVideoHeight = 0; - #if defined(_WIN64) +#if defined(_WIN64) std::string path(Utils::String::replace(mVideoPath, "/", "\\")); - #else +#else std::string path(mVideoPath); - #endif +#endif // Make sure we have a video path. if (mVLC && (path.size() > 0)) { // Set the video that we are going to be playing so we don't attempt to restart it. @@ -356,8 +357,8 @@ void VideoVlcComponent::startVideo() int parseResult; // Asynchronous media parsing. - libvlc_event_attach(libvlc_media_event_manager(mMedia), - libvlc_MediaParsedChanged, VlcMediaParseCallback, 0); + libvlc_event_attach(libvlc_media_event_manager(mMedia), libvlc_MediaParsedChanged, + VlcMediaParseCallback, 0); parseResult = libvlc_media_parse_with_options(mMedia, libvlc_media_parse_local, -1); if (!parseResult) { @@ -382,8 +383,8 @@ void VideoVlcComponent::startVideo() } libvlc_media_tracks_release(tracks, track_count); libvlc_media_parse_stop(mMedia); - libvlc_event_detach(libvlc_media_event_manager(mMedia), - libvlc_MediaParsedChanged, VlcMediaParseCallback, 0); + libvlc_event_detach(libvlc_media_event_manager(mMedia), libvlc_MediaParsedChanged, + VlcMediaParseCallback, 0); // Make sure we found a valid video track. if ((mVideoWidth > 0) && (mVideoHeight > 0)) { @@ -395,32 +396,36 @@ void VideoVlcComponent::startVideo() // The code below enables the libVLC audio output to be processed inside ES-DE. // Unfortunately this causes excessive stuttering for some reason that I still // don't understand, so at the moment this code is disabled. -// auto audioFormatCallback = [](void **data, char *format, -// unsigned *rate, unsigned *channels) -> int { -// format = const_cast("F32L"); -// *rate = 48000; -// *channels = 2; -// return 0; -// }; -// -// libvlc_audio_set_format_callbacks(mMediaPlayer, -// audioFormatCallback, nullptr); -// -// auto audioPlayCallback = [](void* data, const void* samples, -// unsigned count, int64_t pts) { -// AudioManager::getInstance()->processStream(samples, count); -// }; -// -// libvlc_audio_set_callbacks(mMediaPlayer, audioPlayCallback, -// nullptr, nullptr, nullptr, nullptr, this); + // auto audioFormatCallback = [](void **data, char *format, + // unsigned *rate, unsigned *channels) -> int { + // format = const_cast("F32L"); + // *rate = 48000; + // *channels = 2; + // return 0; + // }; + // + // libvlc_audio_set_format_callbacks(mMediaPlayer, + // audioFormatCallback, nullptr); + // + // auto audioPlayCallback = [](void* data, const void* + // samples, + // unsigned count, int64_t pts) { + // AudioManager::getInstance()->processStream(samples, + // count); + // }; + // + // libvlc_audio_set_callbacks(mMediaPlayer, + // audioPlayCallback, + // nullptr, nullptr, nullptr, nullptr, this); libvlc_video_set_format(mMediaPlayer, "RGBA", static_cast(mVideoWidth), - static_cast(mVideoHeight), static_cast(mVideoWidth * 4)); + static_cast(mVideoHeight), + static_cast(mVideoWidth * 4)); // Lock video memory as a preparation for rendering a frame. auto videoLockCallback = [](void* data, void** p_pixels) -> void* { struct VideoContext* videoContext = - reinterpret_cast(data); + reinterpret_cast(data); SDL_LockMutex(videoContext->mutex); SDL_LockSurface(videoContext->surface); *p_pixels = videoContext->surface->pixels; @@ -428,15 +433,15 @@ void VideoVlcComponent::startVideo() }; // Unlock the video memory after rendering a frame. - auto videoUnlockCallback = [](void* data, void*, void *const*) { + auto videoUnlockCallback = [](void* data, void*, void* const*) { struct VideoContext* videoContext = - reinterpret_cast(data); + reinterpret_cast(data); SDL_UnlockSurface(videoContext->surface); SDL_UnlockMutex(videoContext->mutex); }; - libvlc_video_set_callbacks(mMediaPlayer, videoLockCallback, - videoUnlockCallback, nullptr, reinterpret_cast(&mContext)); + libvlc_video_set_callbacks(mMediaPlayer, videoLockCallback, videoUnlockCallback, + nullptr, reinterpret_cast(&mContext)); libvlc_media_player_play(mMediaPlayer); @@ -517,7 +522,7 @@ void VideoVlcComponent::handleLooping() // If the screensaver video swap time is set to 0, it means we should // skip to the next game when the video has finished playing. if (mScreensaverMode && - Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") == 0) { + Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") == 0) { mWindow->screensaverTriggerNextGame(); } else { @@ -527,13 +532,13 @@ void VideoVlcComponent::handleLooping() bool outputSound = false; if ((!mScreensaverMode && !mMediaViewerMode) && - Settings::getInstance()->getBool("GamelistVideoAudio")) + Settings::getInstance()->getBool("GamelistVideoAudio")) outputSound = true; - else if (mScreensaverMode && Settings::getInstance()-> - getBool("ScreensaverVideoAudio")) + else if (mScreensaverMode && + Settings::getInstance()->getBool("ScreensaverVideoAudio")) outputSound = true; - else if (mMediaViewerMode && Settings::getInstance()-> - getBool("MediaViewerVideoAudio")) + else if (mMediaViewerMode && + Settings::getInstance()->getBool("MediaViewerVideoAudio")) outputSound = true; if (!outputSound) diff --git a/es-core/src/components/VideoVlcComponent.h b/es-core/src/components/VideoVlcComponent.h index c4b8f15cd..880852aa8 100644 --- a/es-core/src/components/VideoVlcComponent.h +++ b/es-core/src/components/VideoVlcComponent.h @@ -66,7 +66,7 @@ private: // Handle looping the video. Must be called periodically. virtual void handleLooping() override; - static void VlcMediaParseCallback(const libvlc_event_t *event, void *user_data) {}; + static void VlcMediaParseCallback(const libvlc_event_t* event, void* user_data) {} static VideoVlcComponent* sInstance; static libvlc_instance_t* mVLC; diff --git a/es-core/src/guis/GuiComplexTextEditPopup.cpp b/es-core/src/guis/GuiComplexTextEditPopup.cpp index 20d77fba7..d649976ba 100644 --- a/es-core/src/guis/GuiComplexTextEditPopup.cpp +++ b/es-core/src/guis/GuiComplexTextEditPopup.cpp @@ -10,68 +10,68 @@ #include "guis/GuiComplexTextEditPopup.h" +#include "Window.h" #include "components/ButtonComponent.h" #include "components/MenuComponent.h" #include "components/TextEditComponent.h" #include "guis/GuiMsgBox.h" -#include "Window.h" GuiComplexTextEditPopup::GuiComplexTextEditPopup( - Window* window, - const HelpStyle& helpstyle, - const std::string& title, - const std::string& infoString1, - const std::string& infoString2, - const std::string& initValue, - const std::function& okCallback, - bool multiLine, - const std::string& acceptBtnText, - const std::string& saveConfirmationText, - const std::string& loadBtnText, - const std::string& loadBtnHelpText, - const std::string& clearBtnText, - const std::string& clearBtnHelpText, - bool hideCancelButton) - : GuiComponent(window), - mHelpStyle(helpstyle), - mBackground(window, ":/graphics/frame.svg"), - mGrid(window, Vector2i(1, 5)), - mMultiLine(multiLine), - mInitValue(initValue), - mOkCallback(okCallback), - mSaveConfirmationText(saveConfirmationText), - mHideCancelButton(hideCancelButton) + Window* window, + const HelpStyle& helpstyle, + const std::string& title, + const std::string& infoString1, + const std::string& infoString2, + const std::string& initValue, + const std::function& okCallback, + bool multiLine, + const std::string& acceptBtnText, + const std::string& saveConfirmationText, + const std::string& loadBtnText, + const std::string& loadBtnHelpText, + const std::string& clearBtnText, + const std::string& clearBtnHelpText, + bool hideCancelButton) + : GuiComponent(window) + , mHelpStyle(helpstyle) + , mBackground(window, ":/graphics/frame.svg") + , mGrid(window, Vector2i(1, 5)) + , mMultiLine(multiLine) + , mInitValue(initValue) + , mOkCallback(okCallback) + , mSaveConfirmationText(saveConfirmationText) + , mHideCancelButton(hideCancelButton) { addChild(&mBackground); addChild(&mGrid); mTitle = std::make_shared(mWindow, Utils::String::toUpper(title), - Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER); - mInfoString1 = std::make_shared(mWindow, infoString1, - Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER); - mInfoString2 = std::make_shared(mWindow, infoString2, - Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER); + Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER); + mInfoString1 = std::make_shared(mWindow, infoString1, Font::get(FONT_SIZE_SMALL), + 0x555555FF, ALIGN_CENTER); + mInfoString2 = std::make_shared(mWindow, infoString2, Font::get(FONT_SIZE_SMALL), + 0x555555FF, ALIGN_CENTER); mText = std::make_shared(mWindow); mText->setValue(initValue); std::vector> buttons; buttons.push_back(std::make_shared(mWindow, acceptBtnText, acceptBtnText, - [this, okCallback] { - okCallback(mText->getValue()); - delete this; - })); + [this, okCallback] { + okCallback(mText->getValue()); + delete this; + })); buttons.push_back(std::make_shared(mWindow, loadBtnText, loadBtnHelpText, - [this, infoString2] { - mText->setValue(infoString2); - mText->setCursor(0); - mText->setCursor(infoString2.size()); - })); + [this, infoString2] { + mText->setValue(infoString2); + mText->setCursor(0); + mText->setCursor(infoString2.size()); + })); buttons.push_back(std::make_shared(mWindow, clearBtnText, clearBtnHelpText, - [this] { mText->setValue(""); })); + [this] { mText->setValue(""); })); if (!mHideCancelButton) buttons.push_back(std::make_shared(mWindow, "CANCEL", "discard changes", - [this] { delete this; })); + [this] { delete this; })); mButtonGrid = makeButtonGrid(mWindow, buttons); @@ -79,14 +79,14 @@ GuiComplexTextEditPopup::GuiComplexTextEditPopup( mGrid.setEntry(mInfoString1, Vector2i(0, 1), false, true); mGrid.setEntry(mInfoString2, Vector2i(0, 2), false, false); mGrid.setEntry(mText, Vector2i(0, 3), true, false, Vector2i(1, 1), - GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); mGrid.setEntry(mButtonGrid, Vector2i(0, 4), true, false); mGrid.setRowHeightPerc(1, 0.15f, true); float textHeight = mText->getFont()->getHeight(); if (multiLine) - textHeight *= 6; + textHeight *= 6.0f; // Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent // regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference. @@ -97,18 +97,17 @@ GuiComplexTextEditPopup::GuiComplexTextEditPopup( mText->setSize(0, textHeight); mInfoString2->setSize(infoWidth, mInfoString2->getFont()->getHeight()); - setSize(windowWidth, mTitle->getFont()->getHeight() + textHeight + - mButtonGrid->getSize().y() + mButtonGrid->getSize().y() * 1.85f); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, - (Renderer::getScreenHeight() - mSize.y()) / 2); + setSize(windowWidth, mTitle->getFont()->getHeight() + textHeight + mButtonGrid->getSize().y() + + mButtonGrid->getSize().y() * 1.85f); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, + (Renderer::getScreenHeight() - mSize.y()) / 2.0f); mText->startEditing(); } void GuiComplexTextEditPopup::onSizeChanged() { - mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); - - mText->setSize(mSize.x() - 40, mText->getSize().y()); + mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); + mText->setSize(mSize.x() - 40.0f, mText->getSize().y()); // Update grid. mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y()); @@ -122,13 +121,22 @@ bool GuiComplexTextEditPopup::input(InputConfig* config, Input input) return true; if (!mHideCancelButton) { - // Pressing back when not text editing closes us. + // Pressing back when not text editing closes us. if (config->isMappedTo("b", input) && input.value) { if (mText->getValue() != mInitValue) { // Changes were made, ask if the user wants to save them. - mWindow->pushGui(new GuiMsgBox(mWindow, mHelpStyle, mSaveConfirmationText, "YES", - [this] { this->mOkCallback(mText->getValue()); delete this; return true; }, - "NO", [this] { delete this; return false; })); + mWindow->pushGui(new GuiMsgBox( + mWindow, mHelpStyle, mSaveConfirmationText, "YES", + [this] { + this->mOkCallback(mText->getValue()); + delete this; + return true; + }, + "NO", + [this] { + delete this; + return false; + })); } else { delete this; diff --git a/es-core/src/guis/GuiComplexTextEditPopup.h b/es-core/src/guis/GuiComplexTextEditPopup.h index 7725f2db3..a1ffc8e41 100644 --- a/es-core/src/guis/GuiComplexTextEditPopup.h +++ b/es-core/src/guis/GuiComplexTextEditPopup.h @@ -11,9 +11,9 @@ #ifndef ES_CORE_GUIS_GUI_COMPLEX_TEXT_EDIT_POPUP_H #define ES_CORE_GUIS_GUI_COMPLEX_TEXT_EDIT_POPUP_H +#include "GuiComponent.h" #include "components/ComponentGrid.h" #include "components/NinePatchComponent.h" -#include "GuiComponent.h" class TextComponent; class TextEditComponent; @@ -21,28 +21,27 @@ class TextEditComponent; class GuiComplexTextEditPopup : public GuiComponent { public: - GuiComplexTextEditPopup( - Window* window, - const HelpStyle& helpstyle, - const std::string& title, - const std::string& infoString1, - const std::string& infoString2, - const std::string& initValue, - const std::function& okCallback, - bool multiLine, - const std::string& acceptBtnText = "OK", - const std::string& saveConfirmationText = "SAVE CHANGES?", - const std::string& loadBtnText = "LOAD", - const std::string& loadBtnHelpText = "load default", - const std::string& clearBtnText = "CLEAR", - const std::string& clearBtnHelpText = "clear", - bool hideCancelButton = false); + GuiComplexTextEditPopup(Window* window, + const HelpStyle& helpstyle, + const std::string& title, + const std::string& infoString1, + const std::string& infoString2, + const std::string& initValue, + const std::function& okCallback, + bool multiLine, + const std::string& acceptBtnText = "OK", + const std::string& saveConfirmationText = "SAVE CHANGES?", + const std::string& loadBtnText = "LOAD", + const std::string& loadBtnHelpText = "load default", + const std::string& clearBtnText = "CLEAR", + const std::string& clearBtnHelpText = "clear", + bool hideCancelButton = false); bool input(InputConfig* config, Input input) override; void onSizeChanged() override; std::vector getHelpPrompts() override; - HelpStyle getHelpStyle() override { return mHelpStyle; }; + HelpStyle getHelpStyle() override { return mHelpStyle; } private: NinePatchComponent mBackground; diff --git a/es-core/src/guis/GuiDetectDevice.cpp b/es-core/src/guis/GuiDetectDevice.cpp index 06495d460..2cf8d33ab 100644 --- a/es-core/src/guis/GuiDetectDevice.cpp +++ b/es-core/src/guis/GuiDetectDevice.cpp @@ -8,25 +8,24 @@ #include "guis/GuiDetectDevice.h" +#include "InputManager.h" +#include "Window.h" #include "components/TextComponent.h" #include "guis/GuiInputConfig.h" #include "utils/FileSystemUtil.h" #include "utils/StringUtil.h" -#include "InputManager.h" -#include "Window.h" #define HOLD_TIME 1000 -GuiDetectDevice::GuiDetectDevice( - Window* window, - bool firstRun, - bool forcedConfig, - const std::function& doneCallback) - : GuiComponent(window), - mFirstRun(firstRun), - mForcedConfig(forcedConfig), - mBackground(window, ":/graphics/frame.svg"), - mGrid(window, Vector2i(1, 5)) +GuiDetectDevice::GuiDetectDevice(Window* window, + bool firstRun, + bool forcedConfig, + const std::function& doneCallback) + : GuiComponent(window) + , mFirstRun(firstRun) + , mForcedConfig(forcedConfig) + , mBackground(window, ":/graphics/frame.svg") + , mGrid(window, Vector2i(1, 5)) { mHoldingConfig = nullptr; mHoldTime = 0; @@ -36,8 +35,9 @@ GuiDetectDevice::GuiDetectDevice( addChild(&mGrid); // Title. - mTitle = std::make_shared(mWindow, firstRun ? "WELCOME" : - "CONFIGURE INPUT DEVICE", Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); + mTitle = + std::make_shared(mWindow, firstRun ? "WELCOME" : "CONFIGURE INPUT DEVICE", + Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(1, 1), GridFlags::BORDER_BOTTOM); // Device info. @@ -52,33 +52,33 @@ GuiDetectDevice::GuiDetectDevice( if (numDevices > 1 && Settings::getInstance()->getBool("InputOnlyFirstController")) deviceInfo << " (ONLY ACCEPTING INPUT FROM FIRST CONTROLLER)"; - mDeviceInfo = std::make_shared(mWindow, deviceInfo.str(), - Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER); + mDeviceInfo = std::make_shared( + mWindow, deviceInfo.str(), Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER); mGrid.setEntry(mDeviceInfo, Vector2i(0, 1), false, true); // Message. if (numDevices > 0) { - mMsg1 = std::make_shared(mWindow, - "HOLD A BUTTON ON YOUR GAMEPAD OR KEYBOARD TO CONFIGURE IT", - Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER); + mMsg1 = std::make_shared( + mWindow, "HOLD A BUTTON ON YOUR GAMEPAD OR KEYBOARD TO CONFIGURE IT", + Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER); } else { - mMsg1 = std::make_shared(mWindow, - "HOLD A BUTTON ON YOUR KEYBOARD TO CONFIGURE IT", - Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER); + mMsg1 = std::make_shared( + mWindow, "HOLD A BUTTON ON YOUR KEYBOARD TO CONFIGURE IT", Font::get(FONT_SIZE_SMALL), + 0x777777FF, ALIGN_CENTER); } mGrid.setEntry(mMsg1, Vector2i(0, 2), false, true); - const std::string msg2str = firstRun ? - "PRESS ESC TO SKIP (OR F4 TO QUIT AT ANY TIME)" : "PRESS ESC TO CANCEL"; - mMsg2 = std::make_shared(mWindow, msg2str, - Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER); + const std::string msg2str = + firstRun ? "PRESS ESC TO SKIP (OR F4 TO QUIT AT ANY TIME)" : "PRESS ESC TO CANCEL"; + mMsg2 = std::make_shared(mWindow, msg2str, Font::get(FONT_SIZE_SMALL), + 0x777777FF, ALIGN_CENTER); mGrid.setEntry(mMsg2, Vector2i(0, 3), false, true); // Currently held device. - mDeviceHeld = std::make_shared(mWindow, "", - Font::get(FONT_SIZE_MEDIUM), 0xFFFFFFFF, ALIGN_CENTER); + mDeviceHeld = std::make_shared(mWindow, "", Font::get(FONT_SIZE_MEDIUM), + 0xFFFFFFFF, ALIGN_CENTER); mGrid.setEntry(mDeviceHeld, Vector2i(0, 4), false, true); // Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent @@ -87,27 +87,27 @@ GuiDetectDevice::GuiDetectDevice( float width = Math::clamp(0.60f * aspectValue, 0.50f, 0.80f) * Renderer::getScreenWidth(); setSize(width, Renderer::getScreenHeight() * 0.5f); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, - (Renderer::getScreenHeight() - mSize.y()) / 2); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, + (Renderer::getScreenHeight() - mSize.y()) / 2.0f); } void GuiDetectDevice::onSizeChanged() { - mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); // Grid. mGrid.setSize(mSize); mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y()); - //mGrid.setRowHeightPerc(1, mDeviceInfo->getFont()->getHeight() / mSize.y()); + // mGrid.setRowHeightPerc(1, mDeviceInfo->getFont()->getHeight() / mSize.y()); mGrid.setRowHeightPerc(2, mMsg1->getFont()->getHeight() / mSize.y()); mGrid.setRowHeightPerc(3, mMsg2->getFont()->getHeight() / mSize.y()); - //mGrid.setRowHeightPerc(4, mDeviceHeld->getFont()->getHeight() / mSize.y()); + // mGrid.setRowHeightPerc(4, mDeviceHeld->getFont()->getHeight() / mSize.y()); } bool GuiDetectDevice::input(InputConfig* config, Input input) { - if (!mFirstRun && input.device == DEVICE_KEYBOARD && input.type == TYPE_KEY && - input.value && input.id == SDLK_ESCAPE) { + if (!mFirstRun && input.device == DEVICE_KEYBOARD && input.type == TYPE_KEY && input.value && + input.id == SDLK_ESCAPE) { // Cancel the configuration. delete this; // Delete GUI element. return true; @@ -115,15 +115,15 @@ bool GuiDetectDevice::input(InputConfig* config, Input input) // First run, but the user chooses to skip the configuration. This will default to the // built-in keyboard mappings. else if (mFirstRun && input.device == DEVICE_KEYBOARD && input.type == TYPE_KEY && - input.value && input.id == SDLK_ESCAPE) { - if (mDoneCallback) - mDoneCallback(); - delete this; // Delete GUI element. - return true; + input.value && input.id == SDLK_ESCAPE) { + if (mDoneCallback) + mDoneCallback(); + delete this; // Delete GUI element. + return true; } if (input.type == TYPE_BUTTON || input.type == TYPE_AXIS || input.type == TYPE_KEY || - input.type == TYPE_CEC_BUTTON) { + input.type == TYPE_CEC_BUTTON) { if (input.value && mHoldingConfig == nullptr) { // Started holding. mHoldingConfig = config; @@ -146,8 +146,8 @@ void GuiDetectDevice::update(int deltaTime) // configuration unless the flag to force the configuration was passed on the // command line. if (!mForcedConfig && mFirstRun && - Utils::FileSystem::exists(InputManager::getConfigPath()) && - InputManager::getInstance()->getNumConfiguredDevices() > 0) { + Utils::FileSystem::exists(InputManager::getConfigPath()) && + InputManager::getInstance()->getNumConfiguredDevices() > 0) { if (mDoneCallback) mDoneCallback(); delete this; // Delete GUI element. diff --git a/es-core/src/guis/GuiDetectDevice.h b/es-core/src/guis/GuiDetectDevice.h index d482491d4..a9a6da560 100644 --- a/es-core/src/guis/GuiDetectDevice.h +++ b/es-core/src/guis/GuiDetectDevice.h @@ -9,17 +9,19 @@ #ifndef ES_CORE_GUIS_GUI_DETECT_DEVICE_H #define ES_CORE_GUIS_GUI_DETECT_DEVICE_H +#include "GuiComponent.h" #include "components/ComponentGrid.h" #include "components/NinePatchComponent.h" -#include "GuiComponent.h" class TextComponent; class GuiDetectDevice : public GuiComponent { public: - GuiDetectDevice(Window* window, bool firstRun, bool forcedConfig, - const std::function& doneCallback); + GuiDetectDevice(Window* window, + bool firstRun, + bool forcedConfig, + const std::function& doneCallback); bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; diff --git a/es-core/src/guis/GuiInputConfig.cpp b/es-core/src/guis/GuiInputConfig.cpp index 1c97db6ab..c3c76e692 100644 --- a/es-core/src/guis/GuiInputConfig.cpp +++ b/es-core/src/guis/GuiInputConfig.cpp @@ -8,12 +8,12 @@ #include "guis/GuiInputConfig.h" -#include "components/ButtonComponent.h" -#include "components/MenuComponent.h" -#include "guis/GuiMsgBox.h" #include "InputManager.h" #include "Log.h" #include "Window.h" +#include "components/ButtonComponent.h" +#include "components/MenuComponent.h" +#include "guis/GuiMsgBox.h" #define HOLD_TO_SKIP_MS 1000 @@ -27,23 +27,22 @@ struct InputConfigStructure { static const int inputCount = 24; static InputConfigStructure sGuiInputConfigList[inputCount]; -GuiInputConfig::GuiInputConfig( - Window* window, - InputConfig* target, - bool reconfigureAll, - const std::function& okCallback) - : GuiComponent(window), - mBackground(window, ":/graphics/frame.svg"), - mGrid(window, Vector2i(1, 7)), - mTargetConfig(target), - mHoldingInput(false) +GuiInputConfig::GuiInputConfig(Window* window, + InputConfig* target, + bool reconfigureAll, + const std::function& okCallback) + : GuiComponent(window) + , mBackground(window, ":/graphics/frame.svg") + , mGrid(window, Vector2i(1, 7)) + , mTargetConfig(target) + , mHoldingInput(false) { // Populate the configuration list with the text and icons applicable to the // configured controller type. populateConfigList(); - LOG(LogInfo) << "Configuring device " << target->getDeviceId() << " (" << - target->getDeviceName() << ")."; + LOG(LogInfo) << "Configuring device " << target->getDeviceId() << " (" + << target->getDeviceName() << ")."; if (reconfigureAll) target->clear(); @@ -57,8 +56,8 @@ GuiInputConfig::GuiInputConfig( // 0 is a spacer row. mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 0), false); - mTitle = std::make_shared(mWindow, "CONFIGURING", - Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); + mTitle = std::make_shared(mWindow, "CONFIGURING", Font::get(FONT_SIZE_LARGE), + 0x555555FF, ALIGN_CENTER); mGrid.setEntry(mTitle, Vector2i(0, 1), false, true); std::stringstream ss; @@ -67,13 +66,15 @@ GuiInputConfig::GuiInputConfig( else if (target->getDeviceId() == DEVICE_CEC) ss << "CEC"; else - ss << "GAMEPAD " << (target->getDeviceId() + 1) << " (" << target->getDeviceName() << ")"; - mSubtitle1 = std::make_shared(mWindow, Utils::String::toUpper(ss.str()), - Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER); + ss << "GAMEPAD " << (target->getDeviceId() + 1) << " (" << target->getDeviceName() << ")"; + mSubtitle1 = + std::make_shared(mWindow, Utils::String::toUpper(ss.str()), + Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER); mGrid.setEntry(mSubtitle1, Vector2i(0, 2), false, true); - mSubtitle2 = std::make_shared(mWindow, "HOLD ANY BUTTON 1 SECOND TO SKIP", - Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER); + mSubtitle2 = + std::make_shared(mWindow, "HOLD ANY BUTTON 1 SECOND TO SKIP", + Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER); // The opacity will be set to visible for any row that is skippable. mSubtitle2->setOpacity(0); @@ -98,12 +99,13 @@ GuiInputConfig::GuiInputConfig( spacer->setSize(16, 0); row.addElement(spacer, false); - auto text = std::make_shared(mWindow, - sGuiInputConfigList[i].dispName, Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + auto text = std::make_shared(mWindow, sGuiInputConfigList[i].dispName, + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); row.addElement(text, true); auto mapping = std::make_shared(mWindow, "-NOT DEFINED-", - Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT), 0x999999FF, ALIGN_RIGHT); + Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT), + 0x999999FF, ALIGN_RIGHT); setNotDefined(mapping); // Overrides the text and color set above. row.addElement(mapping, true); mMappings.push_back(mapping); @@ -142,9 +144,10 @@ GuiInputConfig::GuiInputConfig( else { // Button released. Make sure we were holding something and we let go of // what we were previously holding. - if (!mHoldingInput || mHeldInput.device != input.device || mHeldInput.id != - input.id || mHeldInput.type != input.type) + if (!mHoldingInput || mHeldInput.device != input.device || + mHeldInput.id != input.id || mHeldInput.type != input.type) { return true; + } mHoldingInput = false; @@ -177,8 +180,8 @@ GuiInputConfig::GuiInputConfig( delete this; }; - buttons.push_back(std::make_shared - (mWindow, "OK", "ok", [this, okFunction] { okFunction(); })); + buttons.push_back(std::make_shared(mWindow, "OK", "ok", + [this, okFunction] { okFunction(); })); mButtonGrid = makeButtonGrid(mWindow, buttons); mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false); @@ -190,122 +193,76 @@ GuiInputConfig::GuiInputConfig( setSize(width, Renderer::getScreenHeight() * 0.75f); setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, - (Renderer::getScreenHeight() - mSize.y()) / 2.0f); + (Renderer::getScreenHeight() - mSize.y()) / 2.0f); } void GuiInputConfig::populateConfigList() { std::string controllerType = Settings::getInstance()->getString("InputControllerType"); - sGuiInputConfigList[0] = - { "Up", false, "D-PAD UP", ":/help/dpad_up.svg" }; - sGuiInputConfigList[1] = - { "Down", false, "D-PAD DOWN", ":/help/dpad_down.svg" }; - sGuiInputConfigList[2] = - { "Left", false, "D-PAD LEFT", ":/help/dpad_left.svg" }; - sGuiInputConfigList[3] = - { "Right", false, "D-PAD RIGHT", ":/help/dpad_right.svg" }; + // clang-format off + sGuiInputConfigList[0] = { "Up", false, "D-PAD UP", ":/help/dpad_up.svg" }; + sGuiInputConfigList[1] = { "Down", false, "D-PAD DOWN", ":/help/dpad_down.svg" }; + sGuiInputConfigList[2] = { "Left", false, "D-PAD LEFT", ":/help/dpad_left.svg" }; + sGuiInputConfigList[3] = { "Right", false, "D-PAD RIGHT", ":/help/dpad_right.svg" }; if (controllerType == "snes") { - sGuiInputConfigList[4] = - { "Back", false, "SELECT", ":/help/button_back_SNES.svg" }; - sGuiInputConfigList[5] = - { "Start", false, "START", ":/help/button_start_SNES.svg" }; - sGuiInputConfigList[6] = - { "A", false, "B", ":/help/mbuttons_a_SNES.svg" }; - sGuiInputConfigList[7] = - { "B", false, "A", ":/help/mbuttons_b_SNES.svg" }; - sGuiInputConfigList[8] = - { "X", true, "Y", ":/help/mbuttons_x_SNES.svg" }; - sGuiInputConfigList[9] = - { "Y", true, "X", ":/help/mbuttons_y_SNES.svg" }; + sGuiInputConfigList[4] = { "Back", false, "SELECT", ":/help/button_back_SNES.svg" }; + sGuiInputConfigList[5] = { "Start", false, "START", ":/help/button_start_SNES.svg" }; + sGuiInputConfigList[6] = { "A", false, "B", ":/help/mbuttons_a_SNES.svg" }; + sGuiInputConfigList[7] = { "B", false, "A", ":/help/mbuttons_b_SNES.svg" }; + sGuiInputConfigList[8] = { "X", true, "Y", ":/help/mbuttons_x_SNES.svg" }; + sGuiInputConfigList[9] = { "Y", true, "X", ":/help/mbuttons_y_SNES.svg" }; } else if (controllerType == "ps4") { - sGuiInputConfigList[4] = - { "Back", false, "SHARE", ":/help/button_back_PS4.svg" }; - sGuiInputConfigList[5] = - { "Start", false, "OPTIONS", ":/help/button_start_PS4.svg" }; - sGuiInputConfigList[6] = - { "A", false, "CROSS", ":/help/mbuttons_a_PS.svg" }; - sGuiInputConfigList[7] = - { "B", false, "CIRCLE", ":/help/mbuttons_b_PS.svg" }; - sGuiInputConfigList[8] = - { "X", true, "SQUARE", ":/help/mbuttons_x_PS.svg" }; - sGuiInputConfigList[9] = - { "Y", true, "TRIANGLE", ":/help/mbuttons_y_PS.svg" }; + sGuiInputConfigList[4] = { "Back", false, "SHARE", ":/help/button_back_PS4.svg" }; + sGuiInputConfigList[5] = { "Start", false, "OPTIONS", ":/help/button_start_PS4.svg" }; + sGuiInputConfigList[6] = { "A", false, "CROSS", ":/help/mbuttons_a_PS.svg" }; + sGuiInputConfigList[7] = { "B", false, "CIRCLE", ":/help/mbuttons_b_PS.svg" }; + sGuiInputConfigList[8] = { "X", true, "SQUARE", ":/help/mbuttons_x_PS.svg" }; + sGuiInputConfigList[9] = { "Y", true, "TRIANGLE", ":/help/mbuttons_y_PS.svg" }; } else if (controllerType == "ps5") { - sGuiInputConfigList[4] = - { "Back", false, "CREATE", ":/help/button_back_PS5.svg" }; - sGuiInputConfigList[5] = - { "Start", false, "OPTIONS", ":/help/button_start_PS5.svg" }; - sGuiInputConfigList[6] = - { "A", false, "CROSS", ":/help/mbuttons_a_PS.svg" }; - sGuiInputConfigList[7] = - { "B", false, "CIRCLE", ":/help/mbuttons_b_PS.svg" }; - sGuiInputConfigList[8] = - { "X", true, "SQUARE", ":/help/mbuttons_x_PS.svg" }; - sGuiInputConfigList[9] = - { "Y", true, "TRIANGLE", ":/help/mbuttons_y_PS.svg" }; + sGuiInputConfigList[4] = { "Back", false, "CREATE", ":/help/button_back_PS5.svg" }; + sGuiInputConfigList[5] = { "Start", false, "OPTIONS", ":/help/button_start_PS5.svg" }; + sGuiInputConfigList[6] = { "A", false, "CROSS", ":/help/mbuttons_a_PS.svg" }; + sGuiInputConfigList[7] = { "B", false, "CIRCLE", ":/help/mbuttons_b_PS.svg" }; + sGuiInputConfigList[8] = { "X", true, "SQUARE", ":/help/mbuttons_x_PS.svg" }; + sGuiInputConfigList[9] = { "Y", true, "TRIANGLE", ":/help/mbuttons_y_PS.svg" }; } else if (controllerType == "xbox360") { - sGuiInputConfigList[4] = - { "Back", false, "BACK", ":/help/button_back_XBOX360.svg" }; - sGuiInputConfigList[5] = - { "Start", false, "START", ":/help/button_start_XBOX360.svg" }; - sGuiInputConfigList[6] = - { "A", false, "A", ":/help/mbuttons_a_XBOX.svg" }; - sGuiInputConfigList[7] = - { "B", false, "B", ":/help/mbuttons_b_XBOX.svg" }; - sGuiInputConfigList[8] = - { "X", true, "X", ":/help/mbuttons_x_XBOX.svg" }; - sGuiInputConfigList[9] = - { "Y", true, "Y", ":/help/mbuttons_y_XBOX.svg" }; + sGuiInputConfigList[4] = { "Back", false, "BACK", ":/help/button_back_XBOX360.svg" }; + sGuiInputConfigList[5] = { "Start", false, "START", ":/help/button_start_XBOX360.svg" }; + sGuiInputConfigList[6] = { "A", false, "A", ":/help/mbuttons_a_XBOX.svg" }; + sGuiInputConfigList[7] = { "B", false, "B", ":/help/mbuttons_b_XBOX.svg" }; + sGuiInputConfigList[8] = { "X", true, "X", ":/help/mbuttons_x_XBOX.svg" }; + sGuiInputConfigList[9] = { "Y", true, "Y", ":/help/mbuttons_y_XBOX.svg" }; } else { // Xbox One and later. - sGuiInputConfigList[4] = - { "Back", false, "VIEW", ":/help/button_back_XBOX.svg" }; - sGuiInputConfigList[5] = - { "Start", false, "MENU", ":/help/button_start_XBOX.svg" }; - sGuiInputConfigList[6] = - { "A", false, "A", ":/help/mbuttons_a_XBOX.svg" }; - sGuiInputConfigList[7] = - { "B", false, "B", ":/help/mbuttons_b_XBOX.svg" }; - sGuiInputConfigList[8] = - { "X", true, "X", ":/help/mbuttons_x_XBOX.svg" }; - sGuiInputConfigList[9] = - { "Y", true, "Y", ":/help/mbuttons_y_XBOX.svg" }; + sGuiInputConfigList[4] = { "Back", false, "VIEW", ":/help/button_back_XBOX.svg" }; + sGuiInputConfigList[5] = { "Start", false, "MENU", ":/help/button_start_XBOX.svg" }; + sGuiInputConfigList[6] = { "A", false, "A", ":/help/mbuttons_a_XBOX.svg" }; + sGuiInputConfigList[7] = { "B", false, "B", ":/help/mbuttons_b_XBOX.svg" }; + sGuiInputConfigList[8] = { "X", true, "X", ":/help/mbuttons_x_XBOX.svg" }; + sGuiInputConfigList[9] = { "Y", true, "Y", ":/help/mbuttons_y_XBOX.svg" }; } - sGuiInputConfigList[10] = - { "LeftShoulder", true, "LEFT SHOULDER", ":/help/button_l.svg" }; - sGuiInputConfigList[11] = - { "RightShoulder", true, "RIGHT SHOULDER", ":/help/button_r.svg" }; - sGuiInputConfigList[12] = - { "LeftTrigger", true, "LEFT TRIGGER", ":/help/button_lt.svg" }; - sGuiInputConfigList[13] = - { "RightTrigger", true, "RIGHT TRIGGER", ":/help/button_rt.svg" }; - sGuiInputConfigList[14] = - { "LeftThumbstickUp", true, "LEFT THUMBSTICK UP", ":/help/thumbstick_up.svg" }; - sGuiInputConfigList[15] = - { "LeftThumbstickDown", true, "LEFT THUMBSTICK DOWN", ":/help/thumbstick_down.svg" }; - sGuiInputConfigList[16] = - { "LeftThumbstickLeft", true, "LEFT THUMBSTICK LEFT", ":/help/thumbstick_left.svg" }; - sGuiInputConfigList[17] = - { "LeftThumbstickRight", true, "LEFT THUMBSTICK RIGHT", ":/help/thumbstick_right.svg" }; - sGuiInputConfigList[18] = - { "LeftThumbstickClick", true, "LEFT THUMBSTICK CLICK", ":/help/thumbstick_click.svg" }; - sGuiInputConfigList[19] = - { "RightThumbstickUp", true, "RIGHT THUMBSTICK UP", ":/help/thumbstick_up.svg" }; - sGuiInputConfigList[20] = - { "RightThumbstickDown", true, "RIGHT THUMBSTICK DOWN", ":/help/thumbstick_down.svg" }; - sGuiInputConfigList[21] = - { "RightThumbstickLeft", true, "RIGHT THUMBSTICK LEFT", ":/help/thumbstick_left.svg" }; - sGuiInputConfigList[22] = - { "RightThumbstickRight", true, "RIGHT THUMBSTICK RIGHT", ":/help/thumbstick_right.svg" }; - sGuiInputConfigList[23] = - { "RightThumbstickClick", true, "RIGHT THUMBSTICK CLICK", ":/help/thumbstick_click.svg" }; + sGuiInputConfigList[10] = { "LeftShoulder", true, "LEFT SHOULDER", ":/help/button_l.svg" }; + sGuiInputConfigList[11] = { "RightShoulder", true, "RIGHT SHOULDER", ":/help/button_r.svg" }; + sGuiInputConfigList[12] = { "LeftTrigger", true, "LEFT TRIGGER", ":/help/button_lt.svg" }; + sGuiInputConfigList[13] = { "RightTrigger", true, "RIGHT TRIGGER", ":/help/button_rt.svg" }; + sGuiInputConfigList[14] = { "LeftThumbstickUp", true, "LEFT THUMBSTICK UP", ":/help/thumbstick_up.svg" }; + sGuiInputConfigList[15] = { "LeftThumbstickDown", true, "LEFT THUMBSTICK DOWN", ":/help/thumbstick_down.svg" }; + sGuiInputConfigList[16] = { "LeftThumbstickLeft", true, "LEFT THUMBSTICK LEFT", ":/help/thumbstick_left.svg" }; + sGuiInputConfigList[17] = { "LeftThumbstickRight", true, "LEFT THUMBSTICK RIGHT", ":/help/thumbstick_right.svg" }; + sGuiInputConfigList[18] = { "LeftThumbstickClick", true, "LEFT THUMBSTICK CLICK", ":/help/thumbstick_click.svg" }; + sGuiInputConfigList[19] = { "RightThumbstickUp", true, "RIGHT THUMBSTICK UP", ":/help/thumbstick_up.svg" }; + sGuiInputConfigList[20] = { "RightThumbstickDown", true, "RIGHT THUMBSTICK DOWN", ":/help/thumbstick_down.svg" }; + sGuiInputConfigList[21] = { "RightThumbstickLeft", true, "RIGHT THUMBSTICK LEFT", ":/help/thumbstick_left.svg" }; + sGuiInputConfigList[22] = { "RightThumbstickRight", true, "RIGHT THUMBSTICK RIGHT", ":/help/thumbstick_right.svg" }; + sGuiInputConfigList[23] = { "RightThumbstickClick", true, "RIGHT THUMBSTICK CLICK", ":/help/thumbstick_click.svg" }; + // clang-format on } void GuiInputConfig::update(int deltaTime) @@ -336,7 +293,7 @@ void GuiInputConfig::update(int deltaTime) void GuiInputConfig::onSizeChanged() { - mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); // Update grid. mGrid.setSize(mSize); @@ -400,8 +357,8 @@ bool GuiInputConfig::assign(Input input, int inputId) // If this input is mapped to something other than "nothing" or the current row, // generate an error. (If it's the same as what it was before, allow it.) if (mTargetConfig->getMappedTo(input).size() > 0 && - !mTargetConfig->isMappedTo(sGuiInputConfigList[inputId].name, input) && - sGuiInputConfigList[inputId].name != "HotKeyEnable") { + !mTargetConfig->isMappedTo(sGuiInputConfigList[inputId].name, input) && + sGuiInputConfigList[inputId].name != "HotKeyEnable") { error(mMappings.at(inputId), "Already mapped!"); return false; } @@ -411,8 +368,8 @@ bool GuiInputConfig::assign(Input input, int inputId) input.configured = true; mTargetConfig->mapInput(sGuiInputConfigList[inputId].name, input); - LOG(LogInfo) << "Mapping [" << input.string() << "] to [" << - sGuiInputConfigList[inputId].name << "]"; + LOG(LogInfo) << "Mapping [" << input.string() << "] to [" << sGuiInputConfigList[inputId].name + << "]"; return true; } diff --git a/es-core/src/guis/GuiInputConfig.h b/es-core/src/guis/GuiInputConfig.h index 97c31b31e..317c2d3c5 100644 --- a/es-core/src/guis/GuiInputConfig.h +++ b/es-core/src/guis/GuiInputConfig.h @@ -9,9 +9,9 @@ #ifndef ES_CORE_GUIS_GUI_INPUT_CONFIG_H #define ES_CORE_GUIS_GUI_INPUT_CONFIG_H +#include "GuiComponent.h" #include "components/ComponentGrid.h" #include "components/NinePatchComponent.h" -#include "GuiComponent.h" class ComponentList; class TextComponent; @@ -19,8 +19,10 @@ class TextComponent; class GuiInputConfig : public GuiComponent { public: - GuiInputConfig(Window* window, InputConfig* target, bool reconfigureAll, - const std::function& okCallback); + GuiInputConfig(Window* window, + InputConfig* target, + bool reconfigureAll, + const std::function& okCallback); void populateConfigList(); diff --git a/es-core/src/guis/GuiMsgBox.cpp b/es-core/src/guis/GuiMsgBox.cpp index 16b87ef5b..9006fec4e 100644 --- a/es-core/src/guis/GuiMsgBox.cpp +++ b/es-core/src/guis/GuiMsgBox.cpp @@ -14,50 +14,55 @@ #define HORIZONTAL_PADDING_PX 20 -GuiMsgBox::GuiMsgBox(Window* window, const HelpStyle& helpstyle, const std::string& text, - const std::string& name1, const std::function& func1, - const std::string& name2, const std::function& func2, - const std::string& name3, const std::function& func3, - bool disableBackButton, - bool deleteOnButtonPress) - : GuiComponent(window), - mHelpStyle(helpstyle), - mBackground(window, ":/graphics/frame.svg"), - mGrid(window, Vector2i(1, 2)), - mDisableBackButton(disableBackButton), - mDeleteOnButtonPress(deleteOnButtonPress) +GuiMsgBox::GuiMsgBox(Window* window, + const HelpStyle& helpstyle, + const std::string& text, + const std::string& name1, + const std::function& func1, + const std::string& name2, + const std::function& func2, + const std::string& name3, + const std::function& func3, + bool disableBackButton, + bool deleteOnButtonPress) + : GuiComponent(window) + , mHelpStyle(helpstyle) + , mBackground(window, ":/graphics/frame.svg") + , mGrid(window, Vector2i(1, 2)) + , mDisableBackButton(disableBackButton) + , mDeleteOnButtonPress(deleteOnButtonPress) { // Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent // regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference. float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); - float width = floorf(Math::clamp(0.60f * aspectValue, 0.60f, 0.80f) * - Renderer::getScreenWidth()); - float minWidth = floorf(Math::clamp(0.30f * aspectValue, 0.10f, 0.50f) * - Renderer::getScreenWidth()); + float width = + floorf(Math::clamp(0.60f * aspectValue, 0.60f, 0.80f) * Renderer::getScreenWidth()); + float minWidth = + floorf(Math::clamp(0.30f * aspectValue, 0.10f, 0.50f) * Renderer::getScreenWidth()); - mMsg = std::make_shared(mWindow, text, Font::get(FONT_SIZE_MEDIUM), - 0x777777FF, ALIGN_CENTER); + mMsg = std::make_shared(mWindow, text, Font::get(FONT_SIZE_MEDIUM), 0x777777FF, + ALIGN_CENTER); mGrid.setEntry(mMsg, Vector2i(0, 0), false, false); // Create the buttons. - mButtons.push_back(std::make_shared - (mWindow, name1, name1, std::bind(&GuiMsgBox::deleteMeAndCall, this, func1))); + mButtons.push_back(std::make_shared( + mWindow, name1, name1, std::bind(&GuiMsgBox::deleteMeAndCall, this, func1))); if (!name2.empty()) - mButtons.push_back(std::make_shared - (mWindow, name2, name2, std::bind(&GuiMsgBox::deleteMeAndCall, this, func2))); + mButtons.push_back(std::make_shared( + mWindow, name2, name2, std::bind(&GuiMsgBox::deleteMeAndCall, this, func2))); if (!name3.empty()) - mButtons.push_back(std::make_shared - (mWindow, name3, name3, std::bind(&GuiMsgBox::deleteMeAndCall, this, func3))); + mButtons.push_back(std::make_shared( + mWindow, name3, name3, std::bind(&GuiMsgBox::deleteMeAndCall, this, func3))); - // Set accelerator automatically (button to press when "b" is pressed). + // Set accelerator automatically (button to press when "B" is pressed). if (mButtons.size() == 1) { mAcceleratorFunc = mButtons.front()->getPressedFunc(); } else { for (auto it = mButtons.cbegin(); it != mButtons.cend(); it++) { if (Utils::String::toUpper((*it)->getText()) == "OK" || - Utils::String::toUpper((*it)->getText()) == "NO") { + Utils::String::toUpper((*it)->getText()) == "NO") { mAcceleratorFunc = (*it)->getPressedFunc(); break; } @@ -80,14 +85,14 @@ GuiMsgBox::GuiMsgBox(Window* window, const HelpStyle& helpstyle, const std::stri // Now that we know width, we can find height. mMsg->setSize(width, 0); // mMsg->getSize.y() now returns the proper length. - const float msgHeight = std::max(Font::get(FONT_SIZE_LARGE)->getHeight(), - mMsg->getSize().y() * 1.225f); + const float msgHeight = + std::max(Font::get(FONT_SIZE_LARGE)->getHeight(), mMsg->getSize().y() * 1.225f); setSize(width + HORIZONTAL_PADDING_PX * 2 * Renderer::getScreenWidthModifier(), msgHeight + mButtonGrid->getSize().y()); // Center for good measure. setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, - (Renderer::getScreenHeight() - mSize.y()) / 2.0f); + (Renderer::getScreenHeight() - mSize.y()) / 2.0f); addChild(&mBackground); addChild(&mGrid); @@ -103,8 +108,8 @@ void GuiMsgBox::changeText(const std::string& newText) // reference. float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); - float width = floorf(Math::clamp(0.60f * aspectValue, 0.60f, 0.80f) * - Renderer::getScreenWidth()); + float width = + floorf(Math::clamp(0.60f * aspectValue, 0.60f, 0.80f) * Renderer::getScreenWidth()); float minWidth = Renderer::getScreenWidth() * 0.3f; // Decide final width. @@ -119,8 +124,8 @@ void GuiMsgBox::changeText(const std::string& newText) // Now that we know width, we can find height. mMsg->setSize(width, 0); // mMsg->getSize.y() now returns the proper length. - const float msgHeight = std::max(Font::get(FONT_SIZE_LARGE)->getHeight(), - mMsg->getSize().y() * 1.225f); + const float msgHeight = + std::max(Font::get(FONT_SIZE_LARGE)->getHeight(), mMsg->getSize().y() * 1.225f); setSize(width + HORIZONTAL_PADDING_PX * 2 * Renderer::getScreenWidthModifier(), msgHeight + mButtonGrid->getSize().y()); } @@ -152,10 +157,10 @@ void GuiMsgBox::onSizeChanged() // Update messagebox size. mMsg->setSize(mSize.x() - HORIZONTAL_PADDING_PX * 2 * Renderer::getScreenWidthModifier(), - mGrid.getRowHeight(0)); + mGrid.getRowHeight(0)); mGrid.onSizeChanged(); - mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); } void GuiMsgBox::deleteMeAndCall(const std::function& func) diff --git a/es-core/src/guis/GuiMsgBox.h b/es-core/src/guis/GuiMsgBox.h index 9997c4d8b..edb04b697 100644 --- a/es-core/src/guis/GuiMsgBox.h +++ b/es-core/src/guis/GuiMsgBox.h @@ -10,9 +10,9 @@ #ifndef ES_CORE_GUIS_GUI_MSG_BOX_H #define ES_CORE_GUIS_GUI_MSG_BOX_H +#include "GuiComponent.h" #include "components/ComponentGrid.h" #include "components/NinePatchComponent.h" -#include "GuiComponent.h" class ButtonComponent; class TextComponent; @@ -20,18 +20,17 @@ class TextComponent; class GuiMsgBox : public GuiComponent { public: - GuiMsgBox( - Window* window, - const HelpStyle& helpstyle, - const std::string& text, - const std::string& name1 = "OK", - const std::function& func1 = nullptr, - const std::string& name2 = "", - const std::function& func2 = nullptr, - const std::string& name3 = "", - const std::function& func3 = nullptr, - bool disableBackButton = false, - bool deleteOnButtonPress = true); + GuiMsgBox(Window* window, + const HelpStyle& helpstyle, + const std::string& text, + const std::string& name1 = "OK", + const std::function& func1 = nullptr, + const std::string& name2 = "", + const std::function& func2 = nullptr, + const std::string& name3 = "", + const std::function& func3 = nullptr, + bool disableBackButton = false, + bool deleteOnButtonPress = true); void changeText(const std::string& newText); @@ -39,7 +38,7 @@ public: void onSizeChanged() override; std::vector getHelpPrompts() override; - HelpStyle getHelpStyle() override { return mHelpStyle; }; + HelpStyle getHelpStyle() override { return mHelpStyle; } private: void deleteMeAndCall(const std::function& func); diff --git a/es-core/src/guis/GuiTextEditPopup.cpp b/es-core/src/guis/GuiTextEditPopup.cpp index 1bdb06961..27fe9eb1d 100644 --- a/es-core/src/guis/GuiTextEditPopup.cpp +++ b/es-core/src/guis/GuiTextEditPopup.cpp @@ -8,58 +8,60 @@ #include "guis/GuiTextEditPopup.h" +#include "Window.h" #include "components/ButtonComponent.h" #include "components/MenuComponent.h" #include "components/TextEditComponent.h" #include "guis/GuiMsgBox.h" -#include "Window.h" -GuiTextEditPopup::GuiTextEditPopup( - Window* window, - const HelpStyle& helpstyle, - const std::string& title, - const std::string& initValue, - const std::function& okCallback, - bool multiLine, - const std::string& acceptBtnText, - const std::string& saveConfirmationText) - : GuiComponent(window), - mHelpStyle(helpstyle), - mBackground(window, ":/graphics/frame.svg"), - mGrid(window, Vector2i(1, 3)), - mMultiLine(multiLine), - mInitValue(initValue), - mOkCallback(okCallback), - mSaveConfirmationText(saveConfirmationText) +GuiTextEditPopup::GuiTextEditPopup(Window* window, + const HelpStyle& helpstyle, + const std::string& title, + const std::string& initValue, + const std::function& okCallback, + bool multiLine, + const std::string& acceptBtnText, + const std::string& saveConfirmationText) + : GuiComponent(window) + , mHelpStyle(helpstyle) + , mBackground(window, ":/graphics/frame.svg") + , mGrid(window, Vector2i(1, 3)) + , mMultiLine(multiLine) + , mInitValue(initValue) + , mOkCallback(okCallback) + , mSaveConfirmationText(saveConfirmationText) { addChild(&mBackground); addChild(&mGrid); mTitle = std::make_shared(mWindow, Utils::String::toUpper(title), - Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER); + Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER); mText = std::make_shared(mWindow); mText->setValue(initValue); std::vector> buttons; buttons.push_back(std::make_shared(mWindow, acceptBtnText, acceptBtnText, - [this, okCallback] { okCallback(mText->getValue()); delete this; })); + [this, okCallback] { + okCallback(mText->getValue()); + delete this; + })); buttons.push_back(std::make_shared(mWindow, "CLEAR", "clear", - [this] { mText->setValue(""); })); + [this] { mText->setValue(""); })); buttons.push_back(std::make_shared(mWindow, "CANCEL", "discard changes", - [this] { delete this; })); + [this] { delete this; })); mButtonGrid = makeButtonGrid(mWindow, buttons); mGrid.setEntry(mTitle, Vector2i(0, 0), false, true); mGrid.setEntry(mText, Vector2i(0, 1), true, false, Vector2i(1, 1), - GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); mGrid.setEntry(mButtonGrid, Vector2i(0, 2), true, false); float textHeight = mText->getFont()->getHeight(); if (multiLine) - textHeight *= 6; + textHeight *= 6.0f; mText->setSize(0, textHeight); // Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent @@ -67,16 +69,16 @@ GuiTextEditPopup::GuiTextEditPopup( float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); float width = Math::clamp(0.50f * aspectValue, 0.40f, 0.70f) * Renderer::getScreenWidth(); - setSize(width, mTitle->getFont()->getHeight() + - textHeight + mButtonGrid->getSize().y() + mButtonGrid->getSize().y() / 2); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - - mSize.y()) / 2); + setSize(width, mTitle->getFont()->getHeight() + textHeight + mButtonGrid->getSize().y() + + mButtonGrid->getSize().y() / 2.0f); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f, + (Renderer::getScreenHeight() - mSize.y()) / 2.0f); mText->startEditing(); } void GuiTextEditPopup::onSizeChanged() { - mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f)); mText->setSize(mSize.x() - 40, mText->getSize().y()); @@ -95,9 +97,18 @@ bool GuiTextEditPopup::input(InputConfig* config, Input input) if (config->isMappedTo("b", input) && input.value) { if (mText->getValue() != mInitValue) { // Changes were made, ask if the user wants to save them. - mWindow->pushGui(new GuiMsgBox(mWindow, mHelpStyle, mSaveConfirmationText, "YES", - [this] { this->mOkCallback(mText->getValue()); delete this; return true; }, - "NO", [this] { delete this; return false; })); + mWindow->pushGui(new GuiMsgBox( + mWindow, mHelpStyle, mSaveConfirmationText, "YES", + [this] { + this->mOkCallback(mText->getValue()); + delete this; + return true; + }, + "NO", + [this] { + delete this; + return false; + })); } else { delete this; diff --git a/es-core/src/guis/GuiTextEditPopup.h b/es-core/src/guis/GuiTextEditPopup.h index c5bd1eb1a..fe07b53a4 100644 --- a/es-core/src/guis/GuiTextEditPopup.h +++ b/es-core/src/guis/GuiTextEditPopup.h @@ -9,9 +9,9 @@ #ifndef ES_CORE_GUIS_GUI_TEXT_EDIT_POPUP_H #define ES_CORE_GUIS_GUI_TEXT_EDIT_POPUP_H +#include "GuiComponent.h" #include "components/ComponentGrid.h" #include "components/NinePatchComponent.h" -#include "GuiComponent.h" class TextComponent; class TextEditComponent; @@ -19,21 +19,20 @@ class TextEditComponent; class GuiTextEditPopup : public GuiComponent { public: - GuiTextEditPopup( - Window* window, - const HelpStyle& helpstyle, - const std::string& title, - const std::string& initValue, - const std::function& okCallback, - bool multiLine, - const std::string& acceptBtnText = "OK", - const std::string& saveConfirmationText = "SAVE CHANGES?"); + GuiTextEditPopup(Window* window, + const HelpStyle& helpstyle, + const std::string& title, + const std::string& initValue, + const std::function& okCallback, + bool multiLine, + const std::string& acceptBtnText = "OK", + const std::string& saveConfirmationText = "SAVE CHANGES?"); bool input(InputConfig* config, Input input) override; void onSizeChanged() override; std::vector getHelpPrompts() override; - HelpStyle getHelpStyle() override { return mHelpStyle; }; + HelpStyle getHelpStyle() override { return mHelpStyle; } private: NinePatchComponent mBackground; diff --git a/es-core/src/math/Misc.cpp b/es-core/src/math/Misc.cpp index a8b216e98..6e14e0e10 100644 --- a/es-core/src/math/Misc.cpp +++ b/es-core/src/math/Misc.cpp @@ -17,20 +17,22 @@ namespace Math float smoothStep(const float _left, const float _right, const float _x) { - const float x = clamp((_x - _left)/(_right - _left), 0.0f, 1.0f); + const float x = clamp((_x - _left) / (_right - _left), 0.0f, 1.0f); return x * x * (3 - (2 * x)); } float smootherStep(const float _left, const float _right, const float _x) { - const float x = clamp((_x - _left)/(_right - _left), 0.0f, 1.0f); + const float x = clamp((_x - _left) / (_right - _left), 0.0f, 1.0f); return x * x * x * (x * ((x * 6) - 15) + 10); } namespace Scroll { - float bounce(const float _delayTime, const float _scrollTime, - const float _currentTime, const float _scrollLength) + float bounce(const float _delayTime, + const float _scrollTime, + const float _currentTime, + const float _scrollLength) { if (_currentTime < _delayTime) { // Wait. @@ -47,16 +49,18 @@ namespace Math } else if (_currentTime < (_delayTime + _scrollTime + _delayTime + _scrollTime)) { // Lerp back from scrollLength to 0. - const float fraction = (_currentTime - _delayTime - _scrollTime - - _delayTime) / _scrollTime; + const float fraction = + (_currentTime - _delayTime - _scrollTime - _delayTime) / _scrollTime; return lerp(_scrollLength, 0.0f, smootherStep(0, 1, fraction)); } // And back to waiting. return 0; } - float loop(const float _delayTime, const float _scrollTime, - const float _currentTime, const float _scrollLength) + float loop(const float _delayTime, + const float _scrollTime, + const float _currentTime, + const float _scrollLength) { if (_currentTime < _delayTime) { // Wait. @@ -72,5 +76,7 @@ namespace Math return 0; } // Math::Scroll::loop - } // Math::Scroll:: -} // Math:: + + } // namespace Scroll + +} // namespace Math diff --git a/es-core/src/math/Misc.h b/es-core/src/math/Misc.h index dfb07acb8..e3e85ba91 100644 --- a/es-core/src/math/Misc.h +++ b/es-core/src/math/Misc.h @@ -11,15 +11,14 @@ #include -#define ES_PI (3.1415926535897932384626433832795028841971693993751058209749445923) -#define ES_RAD_TO_DEG(_x) ((_x) * (180.0 / ES_PI)) -#define ES_DEG_TO_RAD(_x) ((_x) * (ES_PI / 180.0)) +#define ES_PI (3.1415926535897932384626433832795028841971693993751058209749445923) +#define ES_RAD_TO_DEG(_x) ((_x) * (180.0 / ES_PI)) +#define ES_DEG_TO_RAD(_x) ((_x) * (ES_PI / 180.0)) namespace Math { // When moving to the C++20 standard these functions are no longer required. - template - T const& clamp(const T& _num, const T& _min, const T& _max) + template T const& clamp(const T& _num, const T& _min, const T& _max) { return std::max(std::min(_num, _max), _min); } @@ -30,11 +29,16 @@ namespace Math namespace Scroll { - float bounce(const float _delayTime, const float _scrollTime, - const float _currentTime, const float _scrollLength); - float loop(const float _delayTime, const float _scrollTime, - const float _currentTime, const float _scrollLength); - } -} + float bounce(const float _delayTime, + const float _scrollTime, + const float _currentTime, + const float _scrollLength); + float loop(const float _delayTime, + const float _scrollTime, + const float _currentTime, + const float _scrollLength); + } // namespace Scroll + +} // namespace Math #endif // ES_CORE_MATH_MISC_H diff --git a/es-core/src/math/Transform4x4f.cpp b/es-core/src/math/Transform4x4f.cpp index 071bc9b25..5d565a79d 100644 --- a/es-core/src/math/Transform4x4f.cpp +++ b/es-core/src/math/Transform4x4f.cpp @@ -10,6 +10,7 @@ #include +// clang-format off const Transform4x4f Transform4x4f::operator*(const Transform4x4f& _other) const { const float* tm = reinterpret_cast(this); @@ -321,3 +322,4 @@ Transform4x4f& Transform4x4f::round() return *this; } +// clang-format on diff --git a/es-core/src/math/Transform4x4f.h b/es-core/src/math/Transform4x4f.h index dd49bdc37..c9f07da13 100644 --- a/es-core/src/math/Transform4x4f.h +++ b/es-core/src/math/Transform4x4f.h @@ -9,44 +9,43 @@ #ifndef ES_CORE_MATH_TRANSFORM4X4F_H #define ES_CORE_MATH_TRANSFORM4X4F_H -#include "math/Vector4f.h" #include "math/Vector3f.h" +#include "math/Vector4f.h" class Transform4x4f { public: Transform4x4f() {} - Transform4x4f( - const Vector4f& _r0, - const Vector4f& _r1, - const Vector4f& _r2, - const Vector4f& _r3) - : mR0(_r0), - mR1(_r1), - mR2(_r2), - mR3(_r3) {} + Transform4x4f(const Vector4f& _r0, + const Vector4f& _r1, + const Vector4f& _r2, + const Vector4f& _r3) + : mR0(_r0) + , mR1(_r1) + , mR2(_r2) + , mR3(_r3) + { + } const Transform4x4f operator*(const Transform4x4f& _other) const; const Vector3f operator*(const Vector3f& _other) const; Transform4x4f& operator*=(const Transform4x4f& _other) - { *this = *this * _other; return *this; } + { + *this = *this * _other; + return *this; + } - inline Vector4f& r0() { return mR0; } - inline Vector4f& r1() { return mR1; } - inline Vector4f& r2() { return mR2; } - inline Vector4f& r3() { return mR3; } - inline const Vector4f& r0() const { return mR0; } - inline const Vector4f& r1() const { return mR1; } - inline const Vector4f& r2() const { return mR2; } - inline const Vector4f& r3() const { return mR3; } + Vector4f& r0() { return mR0; } + Vector4f& r1() { return mR1; } + Vector4f& r2() { return mR2; } + Vector4f& r3() { return mR3; } + const Vector4f& r0() const { return mR0; } + const Vector4f& r1() const { return mR1; } + const Vector4f& r2() const { return mR2; } + const Vector4f& r3() const { return mR3; } Transform4x4f& orthoProjection( - float _left, - float _right, - float _bottom, - float _top, - float _near, - float _far); + float _left, float _right, float _bottom, float _top, float _near, float _far); Transform4x4f& invert(const Transform4x4f& _other); Transform4x4f& scale(const Vector3f& _scale); Transform4x4f& rotate(const float _angle, const Vector3f& _axis); @@ -56,11 +55,13 @@ public: Transform4x4f& translate(const Vector3f& _translation); Transform4x4f& round(); - inline Vector3f& translation() { return mR3.v3(); } - inline const Vector3f& translation() const { return mR3.v3(); } + Vector3f& translation() { return mR3.v3(); } + const Vector3f& translation() const { return mR3.v3(); } static const Transform4x4f Identity() - { return { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; } + { + return { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; + } protected: Vector4f mR0; diff --git a/es-core/src/math/Vector2f.h b/es-core/src/math/Vector2f.h index 0acf407b1..193776e68 100644 --- a/es-core/src/math/Vector2f.h +++ b/es-core/src/math/Vector2f.h @@ -20,26 +20,41 @@ class Vector2f { public: Vector2f() {} - Vector2f(const float _f) : mX(_f), mY(_f) {} - Vector2f(const float _x, const float _y) : mX(_x), mY(_y) {} - explicit Vector2f(const Vector3f& _v) : mX((reinterpret_cast(_v)).mX), - mY((reinterpret_cast(_v)).mY) {} - explicit Vector2f(const Vector4f& _v) : mX((reinterpret_cast(_v)).mX), - mY((reinterpret_cast(_v)).mY) {} + Vector2f(const float _f) + : mX(_f) + , mY(_f) + { + } + Vector2f(const float _x, const float _y) + : mX(_x) + , mY(_y) + { + } + explicit Vector2f(const Vector3f& _v) + : mX((reinterpret_cast(_v)).mX) + , mY((reinterpret_cast(_v)).mY) + { + } + explicit Vector2f(const Vector4f& _v) + : mX((reinterpret_cast(_v)).mX) + , mY((reinterpret_cast(_v)).mY) + { + } + // clang-format off const bool operator==(const Vector2f& _other) const - { return ((mX == _other.mX) && (mY == _other.mY)); } + { return ((mX == _other.mX) && (mY == _other.mY)); } const bool operator!=(const Vector2f& _other) const - { return ((mX != _other.mX) || (mY != _other.mY)); } + { return ((mX != _other.mX) || (mY != _other.mY)); } const Vector2f operator+(const Vector2f& _other) const - { return { mX + _other.mX, mY + _other.mY }; } + { return { mX + _other.mX, mY + _other.mY }; } const Vector2f operator-(const Vector2f& _other) const - { return { mX - _other.mX, mY - _other.mY }; } + { return { mX - _other.mX, mY - _other.mY }; } const Vector2f operator*(const Vector2f& _other) const - { return { mX * _other.mX, mY * _other.mY }; } + { return { mX * _other.mX, mY * _other.mY }; } const Vector2f operator/(const Vector2f& _other) const - { return { mX / _other.mX, mY / _other.mY }; } + { return { mX / _other.mX, mY / _other.mY }; } const Vector2f operator+(const float& _other) const { return { mX + _other, mY + _other }; } const Vector2f operator-(const float& _other) const { return { mX - _other, mY - _other }; } @@ -59,21 +74,22 @@ public: Vector2f& operator/=(const float& _other) { *this = *this / _other; return *this; } float& operator[](const int _index) - { assert(_index < 2 && "index out of range"); return (&mX)[_index]; } + { assert(_index < 2 && "index out of range"); return (&mX)[_index]; } const float& operator[](const int _index) const - { assert(_index < 2 && "index out of range"); return (&mX)[_index]; } + { assert(_index < 2 && "index out of range"); return (&mX)[_index]; } + // clang-format on - inline float& x() { return mX; } - inline float& y() { return mY; } - inline const float& x() const { return mX; } - inline const float& y() const { return mY; } + float& x() { return mX; } + float& y() { return mY; } + const float& x() const { return mX; } + const float& y() const { return mY; } Vector2f& round(); - Vector2f& lerp (const Vector2f& _start, const Vector2f& _end, const float _fraction); + Vector2f& lerp(const Vector2f& _start, const Vector2f& _end, const float _fraction); - static const Vector2f Zero() { return { 0, 0 }; } - static const Vector2f UnitX() { return { 1, 0 }; } - static const Vector2f UnitY() { return { 0, 1 }; } + static const Vector2f Zero() { return { 0.0f, 0.0f }; } + static const Vector2f UnitX() { return { 1.0f, 0.0f }; } + static const Vector2f UnitY() { return { 0.0f, 1.0f }; } private: float mX; diff --git a/es-core/src/math/Vector2i.h b/es-core/src/math/Vector2i.h index 55c47d0e7..1924852d6 100644 --- a/es-core/src/math/Vector2i.h +++ b/es-core/src/math/Vector2i.h @@ -15,22 +15,31 @@ class Vector2i { public: Vector2i() {} - Vector2i(const int _i) : mX(_i), mY(_i) {} - Vector2i(const int _x, const int _y) : mX(_x), mY(_y) {} + Vector2i(const int _i) + : mX(_i) + , mY(_i) + { + } + Vector2i(const int _x, const int _y) + : mX(_x) + , mY(_y) + { + } + // clang-format off const bool operator==(const Vector2i& _other) const - { return ((mX == _other.mX) && (mY == _other.mY)); } + { return ((mX == _other.mX) && (mY == _other.mY)); } const bool operator!=(const Vector2i& _other) const - { return ((mX != _other.mX) || (mY != _other.mY)); } + { return ((mX != _other.mX) || (mY != _other.mY)); } const Vector2i operator+(const Vector2i& _other) const - { return { mX + _other.mX, mY + _other.mY }; } + { return { mX + _other.mX, mY + _other.mY }; } const Vector2i operator-(const Vector2i& _other) const - { return { mX - _other.mX, mY - _other.mY }; } + { return { mX - _other.mX, mY - _other.mY }; } const Vector2i operator*(const Vector2i& _other) const - { return { mX * _other.mX, mY * _other.mY }; } + { return { mX * _other.mX, mY * _other.mY }; } const Vector2i operator/(const Vector2i& _other) const - { return { mX / _other.mX, mY / _other.mY }; } + { return { mX / _other.mX, mY / _other.mY }; } const Vector2i operator+(const int& _other) const { return { mX + _other, mY + _other }; } const Vector2i operator-(const int& _other) const { return { mX - _other, mY - _other }; } @@ -50,14 +59,15 @@ public: Vector2i& operator/=(const int& _other) { *this = *this / _other; return *this; } int& operator[](const int _index) - { assert(_index < 2 && "index out of range"); return (&mX)[_index]; } + { assert(_index < 2 && "index out of range"); return (&mX)[_index]; } const int& operator[](const int _index) const - { assert(_index < 2 && "index out of range"); return (&mX)[_index]; } + { assert(_index < 2 && "index out of range"); return (&mX)[_index]; } + // clang-format on - inline int& x() { return mX; } - inline int& y() { return mY; } - inline const int& x() const { return mX; } - inline const int& y() const { return mY; } + int& x() { return mX; } + int& y() { return mY; } + const int& x() const { return mX; } + const int& y() const { return mY; } static const Vector2i Zero() { return { 0, 0 }; } static const Vector2i UnitX() { return { 1, 0 }; } diff --git a/es-core/src/math/Vector3f.h b/es-core/src/math/Vector3f.h index c14829b69..73a05f71d 100644 --- a/es-core/src/math/Vector3f.h +++ b/es-core/src/math/Vector3f.h @@ -20,39 +20,60 @@ class Vector3f { public: Vector3f() {} - Vector3f(const float _f) : mX(_f), mY(_f), mZ(_f) {} - Vector3f(const float _x, const float _y, const float _z) : mX(_x), mY(_y), mZ(_z) {} - explicit Vector3f(const Vector2f& _v) : mX((reinterpret_cast(_v)).mX), - mY((reinterpret_cast(_v)).mY), mZ(0) {} + Vector3f(const float _f) + : mX(_f) + , mY(_f) + , mZ(_f) + { + } + Vector3f(const float _x, const float _y, const float _z) + : mX(_x) + , mY(_y) + , mZ(_z) + { + } + explicit Vector3f(const Vector2f& _v) + : mX((reinterpret_cast(_v)).mX) + , mY((reinterpret_cast(_v)).mY) + , mZ(0) + { + } explicit Vector3f(const Vector2f& _v, const float _z) - : mX((reinterpret_cast(_v)).mX), - mY((reinterpret_cast(_v)).mY), mZ(_z) {} - explicit Vector3f(const Vector4f& _v) : mX((reinterpret_cast(_v)).mX), - mY((reinterpret_cast(_v)).mY), - mZ((reinterpret_cast(_v)).mZ) {} + : mX((reinterpret_cast(_v)).mX) + , mY((reinterpret_cast(_v)).mY) + , mZ(_z) + { + } + explicit Vector3f(const Vector4f& _v) + : mX((reinterpret_cast(_v)).mX) + , mY((reinterpret_cast(_v)).mY) + , mZ((reinterpret_cast(_v)).mZ) + { + } + // clang-format off const bool operator==(const Vector3f& _other) const - { return ((mX == _other.mX) && (mY == _other.mY) && (mZ == _other.mZ)); } + { return ((mX == _other.mX) && (mY == _other.mY) && (mZ == _other.mZ)); } const bool operator!=(const Vector3f& _other) const - { return ((mX != _other.mX) || (mY != _other.mY) || (mZ != _other.mZ)); } + { return ((mX != _other.mX) || (mY != _other.mY) || (mZ != _other.mZ)); } const Vector3f operator+(const Vector3f& _other) const - { return { mX + _other.mX, mY + _other.mY, mZ + _other.mZ }; } + { return { mX + _other.mX, mY + _other.mY, mZ + _other.mZ }; } const Vector3f operator-(const Vector3f& _other) const - { return { mX - _other.mX, mY - _other.mY, mZ - _other.mZ }; } + { return { mX - _other.mX, mY - _other.mY, mZ - _other.mZ }; } const Vector3f operator*(const Vector3f& _other) const - { return { mX * _other.mX, mY * _other.mY, mZ * _other.mZ }; } + { return { mX * _other.mX, mY * _other.mY, mZ * _other.mZ }; } const Vector3f operator/(const Vector3f& _other) const - { return { mX / _other.mX, mY / _other.mY, mZ / _other.mZ }; } + { return { mX / _other.mX, mY / _other.mY, mZ / _other.mZ }; } const Vector3f operator+(const float& _other) const - { return { mX + _other, mY + _other, mZ + _other }; } + { return { mX + _other, mY + _other, mZ + _other }; } const Vector3f operator-(const float& _other) const - { return { mX - _other, mY - _other, mZ - _other }; } + { return { mX - _other, mY - _other, mZ - _other }; } const Vector3f operator*(const float& _other) const - { return { mX * _other, mY * _other, mZ * _other }; } + { return { mX * _other, mY * _other, mZ * _other }; } const Vector3f operator/(const float& _other) const - { return { mX / _other, mY / _other, mZ / _other }; } + { return { mX / _other, mY / _other, mZ / _other }; } const Vector3f operator-() const { return { -mX , -mY, -mZ }; } @@ -67,27 +88,28 @@ public: Vector3f& operator/=(const float& _other) { *this = *this / _other; return *this; } float& operator[](const int _index) - { assert(_index < 3 && "index out of range"); return (&mX)[_index]; } + { assert(_index < 3 && "index out of range"); return (&mX)[_index]; } const float& operator[](const int _index) const - { assert(_index < 3 && "index out of range"); return (&mX)[_index]; } + { assert(_index < 3 && "index out of range"); return (&mX)[_index]; } + // clang-format on - inline float& x() { return mX; } - inline float& y() { return mY; } - inline float& z() { return mZ; } - inline const float& x() const { return mX; } - inline const float& y() const { return mY; } - inline const float& z() const { return mZ; } + float& x() { return mX; } + float& y() { return mY; } + float& z() { return mZ; } + const float& x() const { return mX; } + const float& y() const { return mY; } + const float& z() const { return mZ; } - inline Vector2f& v2() { return *reinterpret_cast(this); } - inline const Vector2f& v2() const { return *reinterpret_cast(this); } + Vector2f& v2() { return *reinterpret_cast(this); } + const Vector2f& v2() const { return *reinterpret_cast(this); } Vector3f& round(); Vector3f& lerp(const Vector3f& _start, const Vector3f& _end, const float _fraction); - static const Vector3f Zero() { return { 0, 0, 0 }; } - static const Vector3f UnitX() { return { 1, 0, 0 }; } - static const Vector3f UnitY() { return { 0, 1, 0 }; } - static const Vector3f UnitZ() { return { 0, 0, 1 }; } + static const Vector3f Zero() { return { 0.0f, 0.0f, 0.0f }; } + static const Vector3f UnitX() { return { 1.0f, 0.0f, 0.0f }; } + static const Vector3f UnitY() { return { 0.0f, 1.0f, 0.0f }; } + static const Vector3f UnitZ() { return { 0.0f, 0.0f, 1.0f }; } private: float mX; diff --git a/es-core/src/math/Vector4f.h b/es-core/src/math/Vector4f.h index 22439d386..000684ada 100644 --- a/es-core/src/math/Vector4f.h +++ b/es-core/src/math/Vector4f.h @@ -20,51 +20,81 @@ class Vector4f { public: Vector4f() {} - Vector4f(const float _f) : mX(_f), mY(_f), mZ(_f), mW(_f) {} + Vector4f(const float _f) + : mX(_f) + , mY(_f) + , mZ(_f) + , mW(_f) + { + } Vector4f(const float _x, const float _y, const float _z, const float _w) - : mX(_x), mY(_y), mZ(_z), mW(_w) {} + : mX(_x) + , mY(_y) + , mZ(_z) + , mW(_w) + { + } explicit Vector4f(const Vector2f& _v) - : mX((reinterpret_cast(_v)).mX), - mY((reinterpret_cast(_v)).mY), mZ(0), mW(0) {} + : mX((reinterpret_cast(_v)).mX) + , mY((reinterpret_cast(_v)).mY) + , mZ(0) + , mW(0) + { + } explicit Vector4f(const Vector2f& _v, const float _z) - : mX((reinterpret_cast(_v)).mX), - mY((reinterpret_cast(_v)).mY), mZ(_z), mW(0) {} + : mX((reinterpret_cast(_v)).mX) + , mY((reinterpret_cast(_v)).mY) + , mZ(_z) + , mW(0) + { + } explicit Vector4f(const Vector2f& _v, const float _z, const float _w) - : mX((reinterpret_cast(_v)).mX), - mY((reinterpret_cast(_v)).mY), mZ(_z), mW(_w) {} + : mX((reinterpret_cast(_v)).mX) + , mY((reinterpret_cast(_v)).mY) + , mZ(_z) + , mW(_w) + { + } explicit Vector4f(const Vector3f& _v) - : mX((reinterpret_cast(_v)).mX), - mY((reinterpret_cast(_v)).mY), - mZ((reinterpret_cast(_v)).mZ), mW(0) {} + : mX((reinterpret_cast(_v)).mX) + , mY((reinterpret_cast(_v)).mY) + , mZ((reinterpret_cast(_v)).mZ) + , mW(0) + { + } explicit Vector4f(const Vector3f& _v, const float _w) - : mX((reinterpret_cast(_v)).mX), - mY((reinterpret_cast(_v)).mY), - mZ((reinterpret_cast(_v)).mZ), mW(_w) {} + : mX((reinterpret_cast(_v)).mX) + , mY((reinterpret_cast(_v)).mY) + , mZ((reinterpret_cast(_v)).mZ) + , mW(_w) + { + } + // clang-format off const bool operator==(const Vector4f& _other) const - { return ((mX == _other.mX) && (mY == _other.mY) && - (mZ == _other.mZ) && (mW == _other.mW)); } + { return ((mX == _other.mX) && (mY == _other.mY) && + (mZ == _other.mZ) && (mW == _other.mW)); } const bool operator!=(const Vector4f& _other) const - { return ((mX != _other.mX) || (mY != _other.mY) || - (mZ != _other.mZ) || (mW != _other.mW)); } + { return ((mX != _other.mX) || (mY != _other.mY) || + (mZ != _other.mZ) || (mW != _other.mW)); } const Vector4f operator+(const Vector4f& _other) const - { return { mX + _other.mX, mY + _other.mY, mZ + _other.mZ, mW + _other.mW }; } + { return { mX + _other.mX, mY + _other.mY, mZ + _other.mZ, mW + _other.mW }; } const Vector4f operator-(const Vector4f& _other) const - { return { mX - _other.mX, mY - _other.mY, mZ - _other.mZ, mW - _other.mW }; } + { return { mX - _other.mX, mY - _other.mY, mZ - _other.mZ, mW - _other.mW }; } const Vector4f operator*(const Vector4f& _other) const - { return { mX * _other.mX, mY * _other.mY, mZ * _other.mZ, mW * _other.mW }; } + { return { mX * _other.mX, mY * _other.mY, mZ * _other.mZ, mW * _other.mW }; } const Vector4f operator/(const Vector4f& _other) const - { return { mX / _other.mX, mY / _other.mY, mZ / _other.mZ, mW / _other.mW }; } + { return { mX / _other.mX, mY / _other.mY, mZ / _other.mZ, mW / _other.mW }; } const Vector4f operator+(const float& _other) const - { return { mX + _other, mY + _other, mZ + _other, mW + _other }; } + { return { mX + _other, mY + _other, mZ + _other, mW + _other }; } const Vector4f operator-(const float& _other) const - { return { mX - _other, mY - _other, mZ - _other, mW - _other }; } + { return { mX - _other, mY - _other, mZ - _other, mW - _other }; } const Vector4f operator*(const float& _other) const - { return { mX * _other, mY * _other, mZ * _other, mW * _other }; } + { return { mX * _other, mY * _other, mZ * _other, mW * _other }; } const Vector4f operator/(const float& _other) const - { return { mX / _other, mY / _other, mZ / _other, mW / _other }; } + { return { mX / _other, mY / _other, mZ / _other, mW / _other }; } const Vector4f operator-() const { return {-mX , -mY, -mZ, -mW }; } @@ -79,33 +109,34 @@ public: Vector4f& operator/=(const float& _other) { *this = *this / _other; return *this; } float& operator[](const int _index) - { assert(_index < 4 && "index out of range"); return (&mX)[_index]; } + { assert(_index < 4 && "index out of range"); return (&mX)[_index]; } const float& operator[](const int _index) const - { assert(_index < 4 && "index out of range"); return (&mX)[_index]; } + { assert(_index < 4 && "index out of range"); return (&mX)[_index]; } + // clang-format on - inline float& x() { return mX; } - inline float& y() { return mY; } - inline float& z() { return mZ; } - inline float& w() { return mW; } - inline const float& x() const { return mX; } - inline const float& y() const { return mY; } - inline const float& z() const { return mZ; } - inline const float& w() const { return mW; } + float& x() { return mX; } + float& y() { return mY; } + float& z() { return mZ; } + float& w() { return mW; } + const float& x() const { return mX; } + const float& y() const { return mY; } + const float& z() const { return mZ; } + const float& w() const { return mW; } - inline Vector2f& v2() { return *reinterpret_cast(this); } - inline const Vector2f& v2() const { return *reinterpret_cast(this); } + Vector2f& v2() { return *reinterpret_cast(this); } + const Vector2f& v2() const { return *reinterpret_cast(this); } - inline Vector3f& v3() { return *reinterpret_cast(this); } - inline const Vector3f& v3() const { return *reinterpret_cast(this); } + Vector3f& v3() { return *reinterpret_cast(this); } + const Vector3f& v3() const { return *reinterpret_cast(this); } Vector4f& round(); - Vector4f& lerp (const Vector4f& _start, const Vector4f& _end, const float _fraction); + Vector4f& lerp(const Vector4f& _start, const Vector4f& _end, const float _fraction); - static const Vector4f Zero() { return { 0, 0, 0, 0 }; } - static const Vector4f UnitX() { return { 1, 0, 0, 0 }; } - static const Vector4f UnitY() { return { 0, 1, 0, 0 }; } - static const Vector4f UnitZ() { return { 0, 0, 1, 0 }; } - static const Vector4f UnitW() { return { 0, 0, 0, 1 }; } + static const Vector4f Zero() { return { 0.0f, 0.0f, 0.0f, 0.0f }; } + static const Vector4f UnitX() { return { 1.0f, 0.0f, 0.0f, 0.0f }; } + static const Vector4f UnitY() { return { 0.0f, 1.0f, 0.0f, 0.0f }; } + static const Vector4f UnitZ() { return { 0.0f, 0.0f, 1.0f, 0.0f }; } + static const Vector4f UnitW() { return { 0.0f, 0.0f, 0.0f, 1.0f }; } private: float mX; diff --git a/es-core/src/renderers/Renderer.cpp b/es-core/src/renderers/Renderer.cpp index e80b01516..7b96c6d56 100644 --- a/es-core/src/renderers/Renderer.cpp +++ b/es-core/src/renderers/Renderer.cpp @@ -8,18 +8,18 @@ #include "renderers/Renderer.h" -#include "math/Transform4x4f.h" -#include "math/Vector2i.h" -#include "resources/ResourceManager.h" #include "ImageIO.h" #include "Log.h" #include "Settings.h" #include "Shader_GL21.h" +#include "math/Transform4x4f.h" +#include "math/Vector2i.h" +#include "resources/ResourceManager.h" #include #include -#if defined (_WIN64) +#if defined(_WIN64) #include #endif @@ -44,31 +44,31 @@ namespace Renderer { size_t width = 0; size_t height = 0; - ResourceData resData = ResourceManager::getInstance()-> - getFileData(":/graphics/window_icon_256.png"); + ResourceData resData = + ResourceManager::getInstance()->getFileData(":/graphics/window_icon_256.png"); std::vector rawData = - ImageIO::loadFromMemoryRGBA32(resData.ptr.get(), resData.length, width, height); + ImageIO::loadFromMemoryRGBA32(resData.ptr.get(), resData.length, width, height); if (!rawData.empty()) { ImageIO::flipPixelsVert(rawData.data(), width, height); - #if SDL_BYTEORDER == SDL_BIG_ENDIAN +#if SDL_BYTEORDER == SDL_BIG_ENDIAN unsigned int rmask = 0xFF000000; unsigned int gmask = 0x00FF0000; unsigned int bmask = 0x0000FF00; unsigned int amask = 0x000000FF; - #else +#else unsigned int rmask = 0x000000FF; unsigned int gmask = 0x0000FF00; unsigned int bmask = 0x00FF0000; unsigned int amask = 0xFF000000; - #endif +#endif // Try creating SDL surface from logo data. - SDL_Surface* logoSurface = SDL_CreateRGBSurfaceFrom( - static_cast(rawData.data()), - static_cast(width), static_cast(height), 32, - static_cast((width * 4)), rmask, gmask, bmask, amask); + SDL_Surface* logoSurface = + SDL_CreateRGBSurfaceFrom(static_cast(rawData.data()), + static_cast(width), static_cast(height), 32, + static_cast((width * 4)), rmask, gmask, bmask, amask); if (logoSurface != nullptr) { SDL_SetWindowIcon(sdlWindow, logoSurface); @@ -100,8 +100,8 @@ namespace Renderer int availableDisplays = SDL_GetNumVideoDisplays(); if (displayIndex > availableDisplays - 1) { - LOG(LogWarning) << "Requested display " << std::to_string(displayIndex + 1) << - " does not exist, changing to display 1"; + LOG(LogWarning) << "Requested display " << std::to_string(displayIndex + 1) + << " does not exist, changing to display 1"; displayIndex = 0; } else { @@ -111,7 +111,7 @@ namespace Renderer SDL_DisplayMode displayMode; SDL_GetDesktopDisplayMode(displayIndex, &displayMode); - #if defined (_WIN64) +#if defined(_WIN64) // Tell Windows that we're DPI aware so that we can set a physical resolution and // avoid any automatic DPI scaling. SetProcessDPIAware(); @@ -122,28 +122,35 @@ namespace Renderer SDL_GetDisplayBounds(displayIndex, &displayBounds); displayMode.w = displayBounds.w; displayMode.h = displayBounds.h; - #endif +#endif windowWidth = Settings::getInstance()->getInt("WindowWidth") ? - Settings::getInstance()->getInt("WindowWidth") : displayMode.w; + Settings::getInstance()->getInt("WindowWidth") : + displayMode.w; windowHeight = Settings::getInstance()->getInt("WindowHeight") ? - Settings::getInstance()->getInt("WindowHeight") : displayMode.h; + Settings::getInstance()->getInt("WindowHeight") : + displayMode.h; screenWidth = Settings::getInstance()->getInt("ScreenWidth") ? - Settings::getInstance()->getInt("ScreenWidth") : windowWidth; + Settings::getInstance()->getInt("ScreenWidth") : + windowWidth; screenHeight = Settings::getInstance()->getInt("ScreenHeight") ? - Settings::getInstance()->getInt("ScreenHeight") : windowHeight; + Settings::getInstance()->getInt("ScreenHeight") : + windowHeight; screenOffsetX = Settings::getInstance()->getInt("ScreenOffsetX") ? - Settings::getInstance()->getInt("ScreenOffsetX") : 0; + Settings::getInstance()->getInt("ScreenOffsetX") : + 0; screenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ? - Settings::getInstance()->getInt("ScreenOffsetY") : 0; + Settings::getInstance()->getInt("ScreenOffsetY") : + 0; screenRotate = Settings::getInstance()->getInt("ScreenRotate") ? - Settings::getInstance()->getInt("ScreenRotate") : 0; + Settings::getInstance()->getInt("ScreenRotate") : + 0; // Prevent the application window from minimizing when switching windows (when launching // games or when manually switching windows using the task switcher). SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); - #if defined(__unix__) +#if defined(__unix__) // Disabling desktop composition can lead to better framerates and a more fluid user // interface, but with some drivers it can cause strange behaviours when returning to // the desktop. @@ -151,29 +158,28 @@ namespace Renderer SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "1"); else SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); - #endif +#endif - #if defined(__APPLE__) || defined(__unix__) +#if defined(__APPLE__) || defined(__unix__) bool userResolution = false; // Check if the user has changed the resolution from the command line. if (windowWidth != displayMode.w || windowHeight != displayMode.h) userResolution = true; - // Not sure if this could be a useful setting for some users. -// SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "0"); - #endif - - setupWindow(); + // Not sure if this could be a useful setting for some users. + // SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "0"); +#endif unsigned int windowFlags; + setupWindow(); - #if defined(_WIN64) +#if defined(_WIN64) // For Windows, always set the mode to windowed, as full screen mode seems to // behave quite erratic. There may be a proper fix for this, but for now windowed // mode seems to behave well and it's almost completely seamless, especially with // a hidden taskbar. As well, setting SDL_WINDOW_BORDERLESS introduces issues too // so unfortunately this needs to be avoided. windowFlags = getWindowFlags(); - #elif defined(__APPLE__) +#elif defined(__APPLE__) // This seems to be the only full window mode that somehow works on macOS as a real // fullscreen mode will do lots of weird stuff like preventing window switching // or refusing to let emulators run at all. SDL_WINDOW_FULLSCREEN_DESKTOP almost @@ -190,7 +196,7 @@ namespace Renderer // If the user has changed the resolution from the command line, then add a // border to the window. windowFlags = SDL_WINDOW_ALLOW_HIGHDPI | getWindowFlags(); - #else +#else if (Settings::getInstance()->getBool("Windowed")) { windowFlags = getWindowFlags(); } @@ -205,17 +211,17 @@ namespace Renderer else { windowFlags = SDL_WINDOW_FULLSCREEN | getWindowFlags(); } - #endif +#endif - if ((sdlWindow = SDL_CreateWindow("EmulationStation", - SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), - SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), - windowWidth, windowHeight, windowFlags)) == nullptr) { + if ((sdlWindow = + SDL_CreateWindow("EmulationStation", SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), + SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), windowWidth, + windowHeight, windowFlags)) == nullptr) { LOG(LogError) << "Couldn't create SDL window. " << SDL_GetError(); return false; } - #if defined(__APPLE__) +#if defined(__APPLE__) // The code below is required as the high DPI scaling on macOS is very bizarre and is // measured in "points" rather than pixels (even though the naming convention sure looks // like pixels). For example there could be a 1920x1080 entry in the OS display settings @@ -231,25 +237,25 @@ namespace Renderer SDL_GL_GetDrawableSize(sdlWindow, &width, nullptr); int scaleFactor = static_cast(width / windowWidth); - LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x" << - std::to_string(displayMode.h) << " (physical resolution " << - std::to_string(displayMode.w * scaleFactor) << "x" << - std::to_string(displayMode.h * scaleFactor) << ")"; - LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x" << - std::to_string(windowHeight) << " (physical resolution " << - std::to_string(windowWidth * scaleFactor) << "x" << - std::to_string(windowHeight * scaleFactor) << ")"; + LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x" + << std::to_string(displayMode.h) << " (physical resolution " + << std::to_string(displayMode.w * scaleFactor) << "x" + << std::to_string(displayMode.h * scaleFactor) << ")"; + LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x" + << std::to_string(windowHeight) << " (physical resolution " + << std::to_string(windowWidth * scaleFactor) << "x" + << std::to_string(windowHeight * scaleFactor) << ")"; windowWidth *= scaleFactor; windowHeight *= scaleFactor; screenWidth *= scaleFactor; screenHeight *= scaleFactor; - #else - LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x" << - std::to_string(displayMode.h); - LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x" << - std::to_string(windowHeight); - #endif +#else + LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x" + << std::to_string(displayMode.h); + LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x" + << std::to_string(windowHeight); +#endif screenHeightModifier = static_cast(screenHeight) / 1080.0f; screenWidthModifier = static_cast(screenWidth) / 1920.0f; @@ -263,14 +269,14 @@ namespace Renderer setIcon(); setSwapInterval(); +#if defined(_WIN64) // It seems as if Windows needs this to avoid a brief white screen flash on startup. // Possibly this is driver-specific rather than OS-specific. There is additional code // in init() to work around the white screen flash issue on all operating systems. - #if defined(_WIN64) swapBuffers(); - #endif +#endif - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) LOG(LogInfo) << "Loading shaders..."; std::vector shaderFiles; @@ -294,19 +300,17 @@ namespace Renderer sShaderProgramVector.push_back(loadShader); } - #endif +#endif return true; } static void destroyWindow() { - #if defined(USE_OPENGL_21) - for (auto it = sShaderProgramVector.cbegin(); - it != sShaderProgramVector.cend(); it++) { +#if defined(USE_OPENGL_21) + for (auto it = sShaderProgramVector.cbegin(); it != sShaderProgramVector.cend(); it++) delete *it; - } - #endif +#endif destroyContext(); SDL_DestroyWindow(sdlWindow); @@ -332,42 +336,42 @@ namespace Renderer viewport.w = screenWidth; viewport.h = screenHeight; projection.orthoProjection(0.0f, static_cast(screenWidth), - static_cast(screenHeight), 0.0f, -1.0f, 1.0f); + static_cast(screenHeight), 0.0f, -1.0f, 1.0f); + break; } - break; case 1: { viewport.x = windowWidth - screenOffsetY - screenHeight; viewport.y = screenOffsetX; viewport.w = screenHeight; viewport.h = screenWidth; projection.orthoProjection(0.0f, static_cast(screenHeight), - static_cast(screenWidth), 0.0f, -1.0f, 1.0f); - projection.rotate(static_cast(ES_DEG_TO_RAD(90)), {0, 0, 1}); - projection.translate({0, screenHeight * -1.0f, 0}); + static_cast(screenWidth), 0.0f, -1.0f, 1.0f); + projection.rotate(static_cast(ES_DEG_TO_RAD(90)), { 0, 0, 1 }); + projection.translate({ 0, screenHeight * -1.0f, 0 }); + break; } - break; case 2: { - viewport.x = windowWidth - screenOffsetX - screenWidth; + viewport.x = windowWidth - screenOffsetX - screenWidth; viewport.y = windowHeight - screenOffsetY - screenHeight; viewport.w = screenWidth; viewport.h = screenHeight; projection.orthoProjection(0.0f, static_cast(screenWidth), - static_cast(screenHeight), 0.0f, -1.0f, 1.0f); - projection.rotate(static_cast(ES_DEG_TO_RAD(180)), {0, 0, 1}); - projection.translate({screenWidth * -1.0f, screenHeight * -1.0f, 0}); + static_cast(screenHeight), 0.0f, -1.0f, 1.0f); + projection.rotate(static_cast(ES_DEG_TO_RAD(180)), { 0, 0, 1 }); + projection.translate({ screenWidth * -1.0f, screenHeight * -1.0f, 0 }); + break; } - break; case 3: { viewport.x = screenOffsetY; viewport.y = windowHeight - screenOffsetX - screenWidth; viewport.w = screenHeight; viewport.h = screenWidth; projection.orthoProjection(0.0f, static_cast(screenHeight), - static_cast(screenWidth), 0.0f, -1.0f, 1.0f); - projection.rotate(static_cast(ES_DEG_TO_RAD(270)), {0, 0, 1}); - projection.translate({screenWidth * -1.0f, 0, 0}); + static_cast(screenWidth), 0.0f, -1.0f, 1.0f); + projection.rotate(static_cast(ES_DEG_TO_RAD(270)), { 0, 0, 1 }); + projection.translate({ screenWidth * -1.0f, 0, 0 }); + break; } - break; } mProjectionMatrix = projection; @@ -377,7 +381,7 @@ namespace Renderer // This is required to avoid a brief white screen flash during startup on some systems. Renderer::drawRect(0.0f, 0.0f, static_cast(Renderer::getScreenWidth()), - static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); + static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); swapBuffers(); return true; @@ -385,6 +389,7 @@ namespace Renderer void deinit() { + // Destroy the window. destroyWindow(); } @@ -400,32 +405,38 @@ namespace Renderer switch (screenRotate) { case 0: box = Rect(screenOffsetX + box.x, screenOffsetY + box.y, box.w, box.h); - break; + break; case 1: - box = Rect(windowWidth - screenOffsetY - box.y - box.h, - screenOffsetX + box.x, box.h, box.w); - break; + box = Rect(windowWidth - screenOffsetY - box.y - box.h, screenOffsetX + box.x, + box.h, box.w); + break; case 2: - box = Rect(windowWidth - screenOffsetX - box.x - box.w, windowHeight - - screenOffsetY - box.y - box.h, box.w, box.h); - break; + box = Rect(windowWidth - screenOffsetX - box.x - box.w, + windowHeight - screenOffsetY - box.y - box.h, box.w, box.h); + break; case 3: - box = Rect(screenOffsetY + box.y, windowHeight - - screenOffsetX - box.x - box.w, box.h, box.w); - break; + box = Rect(screenOffsetY + box.y, windowHeight - screenOffsetX - box.x - box.w, + box.h, box.w); + break; } // Make sure the box fits within clipStack.top(), and clip further accordingly. if (clipStack.size()) { const Rect& top = clipStack.top(); - if ( top.x > box.x) box.x = top.x; - if ( top.y > box.y) box.y = top.y; - if ((top.x + top.w) < (box.x + box.w)) box.w = (top.x + top.w) - box.x; - if ((top.y + top.h) < (box.y + box.h)) box.h = (top.y + top.h) - box.y; + if (top.x > box.x) + box.x = top.x; + if (top.y > box.y) + box.y = top.y; + if ((top.x + top.w) < (box.x + box.w)) + box.w = (top.x + top.w) - box.x; + if ((top.y + top.h) < (box.y + box.h)) + box.h = (top.y + top.h) - box.y; } - if (box.w < 0) box.w = 0; - if (box.h < 0) box.h = 0; + if (box.w < 0) + box.w = 0; + if (box.h < 0) + box.h = 0; clipStack.push(box); @@ -447,18 +458,17 @@ namespace Renderer setScissor(clipStack.top()); } - void drawRect( - const float _x, - const float _y, - const float _w, - const float _h, - const unsigned int _color, - const unsigned int _colorEnd, - bool horizontalGradient, - const float _opacity, - const Transform4x4f& _trans, - const Blend::Factor _srcBlendFactor, - const Blend::Factor _dstBlendFactor) + void drawRect(const float _x, + const float _y, + const float _w, + const float _h, + const unsigned int _color, + const unsigned int _colorEnd, + bool horizontalGradient, + const float _opacity, + const Transform4x4f& _trans, + const Blend::Factor _srcBlendFactor, + const Blend::Factor _dstBlendFactor) { const unsigned int color = convertRGBAToABGR(_color); const unsigned int colorEnd = convertRGBAToABGR(_colorEnd); @@ -474,10 +484,12 @@ namespace Renderer if (_hL > 0.0f && _hL < 1.0f) _hL = 1.0f; + // clang-format off vertices[0] = { { _x , _y }, { 0.0f, 0.0f }, color }; vertices[1] = { { _x , _y + _hL }, { 0.0f, 0.0f }, horizontalGradient ? colorEnd : color }; vertices[2] = { { _x + _wL, _y }, { 0.0f, 0.0f }, horizontalGradient ? color : colorEnd }; vertices[3] = { { _x + _wL, _y + _hL }, { 0.0f, 0.0f }, colorEnd }; + // clang-format on // Round vertices. for (int i = 0; i < 4; i++) @@ -524,17 +536,13 @@ namespace Renderer index++; } - if (sShaderProgramVector.size() > index-1) - return sShaderProgramVector[index-1]; + if (sShaderProgramVector.size() > index - 1) + return sShaderProgramVector[index - 1]; else return nullptr; }; - const Transform4x4f getProjectionMatrix() - { - return mProjectionMatrix; - } - + const Transform4x4f getProjectionMatrix() { return mProjectionMatrix; } SDL_Window* getSDLWindow() { return sdlWindow; } int getWindowWidth() { return windowWidth; } int getWindowHeight() { return windowHeight; } @@ -547,4 +555,4 @@ namespace Renderer float getScreenHeightModifier() { return screenHeightModifier; } float getScreenAspectRatio() { return screenAspectRatio; } -} // Renderer:: +} // namespace Renderer diff --git a/es-core/src/renderers/Renderer.h b/es-core/src/renderers/Renderer.h index 8d5f99aa4..5fec1600e 100644 --- a/es-core/src/renderers/Renderer.h +++ b/es-core/src/renderers/Renderer.h @@ -9,26 +9,27 @@ #ifndef ES_CORE_RENDERER_RENDERER_H #define ES_CORE_RENDERER_RENDERER_H -#include "math/Transform4x4f.h" -#include "math/Vector2f.h" #include "Log.h" #include "Shader_GL21.h" +#include "math/Transform4x4f.h" +#include "math/Vector2f.h" #include #include -class Transform4x4f; -class Vector2i; struct SDL_Window; +class Transform4x4f; +class Vector2i; + namespace Renderer { - const unsigned int SHADER_DESATURATE = 1; - const unsigned int SHADER_OPACITY = 2; - const unsigned int SHADER_DIM = 4; - const unsigned int SHADER_BLUR_HORIZONTAL = 8; - const unsigned int SHADER_BLUR_VERTICAL = 16; - const unsigned int SHADER_SCANLINES = 32; + const unsigned int SHADER_DESATURATE = 1; + const unsigned int SHADER_OPACITY = 2; + const unsigned int SHADER_DIM = 4; + const unsigned int SHADER_BLUR_HORIZONTAL = 8; + const unsigned int SHADER_BLUR_VERTICAL = 16; + const unsigned int SHADER_SCANLINES = 32; struct shaderParameters { std::array textureSize; @@ -39,40 +40,40 @@ namespace Renderer unsigned int blurPasses; shaderParameters() - : textureSize({0.0f, 0.0f}), - textureCoordinates({0.0f, 0.0f, 0.0f, 0.0f}), - fragmentSaturation(1.0f), - fragmentDimValue(0.4f), - fragmentOpacity(1.0f), - blurPasses(1) - {}; + : textureSize({ 0.0f, 0.0f }) + , textureCoordinates({ 0.0f, 0.0f, 0.0f, 0.0f }) + , fragmentSaturation(1.0f) + , fragmentDimValue(0.4f) + , fragmentOpacity(1.0f) + , blurPasses(1) + { + } }; static std::vector sShaderProgramVector; static GLuint shaderFBO; static Transform4x4f mProjectionMatrix; - #if !defined(NDEBUG) - #define GL_CHECK_ERROR(Function) (Function, _GLCheckError(#Function)) +#if !defined(NDEBUG) +#define GL_CHECK_ERROR(Function) (Function, _GLCheckError(#Function)) static void _GLCheckError(const std::string& _funcName) { const GLenum errorCode = glGetError(); if (errorCode != GL_NO_ERROR) { - #if defined(USE_OPENGL_21) - LOG(LogError) << "OpenGL error: " << _funcName << - " failed with error code: 0x" << std::hex << errorCode; - #else - LOG(LogError) << "OpenGLES error: " << _funcName << - " failed with error code: 0x" << std::hex << errorCode; - #endif - +#if defined(USE_OPENGL_21) + LOG(LogError) << "OpenGL error: " << _funcName << " failed with error code: 0x" + << std::hex << errorCode; +#else + LOG(LogError) << "OpenGLES error: " << _funcName << " failed with error code: 0x" + << std::hex << errorCode; +#endif } } - #else - #define GL_CHECK_ERROR(Function) (Function) - #endif +#else +#define GL_CHECK_ERROR(Function) (Function) +#endif namespace Blend { @@ -93,37 +94,33 @@ namespace Renderer namespace Texture { enum Type { - RGBA = 0, + RGBA = 0, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). ALPHA = 1 }; } struct Rect { - Rect( - const int _x, - const int _y, - const int _w, - const int _h) - : x(_x), - y(_y), - w(_w), - h(_h) {} + Rect(const int _x, const int _y, const int _w, const int _h) + : x(_x) + , y(_y) + , w(_w) + , h(_h) + { + } int x; int y; int w; int h; }; - struct Vertex - { + struct Vertex { Vertex() {} - Vertex( - const Vector2f& _pos, - const Vector2f& _tex, - const unsigned int _col) - : pos(_pos), - tex(_tex), - col(_col) { } + Vertex(const Vector2f& _pos, const Vector2f& _tex, const unsigned int _col) + : pos(_pos) + , tex(_tex) + , col(_col) + { + } Vector2f pos; Vector2f tex; unsigned int col; @@ -136,18 +133,17 @@ namespace Renderer void deinit(); void pushClipRect(const Vector2i& _pos, const Vector2i& _size); void popClipRect(); - void drawRect( - const float _x, - const float _y, - const float _w, - const float _h, - const unsigned int _color, - const unsigned int _colorEnd, - bool horizontalGradient = false, - const float _opacity = 1.0, - const Transform4x4f& _trans = Transform4x4f::Identity(), - const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA, - const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA); + void drawRect(const float _x, + const float _y, + const float _w, + const float _h, + const unsigned int _color, + const unsigned int _colorEnd, + bool horizontalGradient = false, + const float _opacity = 1.0, + const Transform4x4f& _trans = Transform4x4f::Identity(), + const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA, + const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA); SDL_Window* getSDLWindow(); int getWindowWidth(); int getWindowHeight(); @@ -166,49 +162,46 @@ namespace Renderer Shader* getShaderProgram(unsigned int shaderID); const Transform4x4f getProjectionMatrix(); void shaderPostprocessing(unsigned int shaders, - const Renderer::shaderParameters& parameters = shaderParameters(), - unsigned char* textureRGBA = nullptr); + const Renderer::shaderParameters& parameters = shaderParameters(), + unsigned char* textureRGBA = nullptr); + + static unsigned int getWindowFlags() { return SDL_WINDOW_OPENGL; } - // API specific. - unsigned int getWindowFlags(); void setupWindow(); bool createContext(); void destroyContext(); - unsigned int createTexture( - const Texture::Type _type, - const bool _linear, - const bool _repeat, - const unsigned int _width, - const unsigned int _height, - void* _data); + unsigned int createTexture(const Texture::Type _type, + const bool _linear, + const bool _repeat, + const unsigned int _width, + const unsigned int _height, + void* _data); void destroyTexture(const unsigned int _texture); - void updateTexture( - const unsigned int _texture, - const Texture::Type _type, - const unsigned int _x, - const unsigned _y, - const unsigned int _width, - const unsigned int _height, - void* _data); + void updateTexture(const unsigned int _texture, + const Texture::Type _type, + const unsigned int _x, + const unsigned _y, + const unsigned int _width, + const unsigned int _height, + void* _data); void bindTexture(const unsigned int _texture); - void drawLines( - const Vertex* _vertices, - const unsigned int _numVertices, - const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA, - const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA); - void drawTriangleStrips( - const Vertex* _vertices, - const unsigned int _numVertices, - const Transform4x4f& _trans = Transform4x4f::Identity(), - const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA, - const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA, - const shaderParameters& _parameters = shaderParameters()); + void drawLines(const Vertex* _vertices, + const unsigned int _numVertices, + const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA, + const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA); + void drawTriangleStrips(const Vertex* _vertices, + const unsigned int _numVertices, + const Transform4x4f& _trans = Transform4x4f::Identity(), + const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA, + const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA, + const shaderParameters& _parameters = shaderParameters()); void setProjection(const Transform4x4f& _projection); void setMatrix(const Transform4x4f& _matrix); void setViewport(const Rect& _viewport); void setScissor(const Rect& _scissor); void setSwapInterval(); void swapBuffers(); -} + +} // namespace Renderer #endif // ES_CORE_RENDERER_RENDERER_H diff --git a/es-core/src/renderers/Renderer_GL21.cpp b/es-core/src/renderers/Renderer_GL21.cpp index 2aab81706..7f236ec8c 100644 --- a/es-core/src/renderers/Renderer_GL21.cpp +++ b/es-core/src/renderers/Renderer_GL21.cpp @@ -8,10 +8,10 @@ #if defined(USE_OPENGL_21) -#include "math/Transform4x4f.h" -#include "renderers/Renderer.h" #include "Settings.h" #include "Shader_GL21.h" +#include "math/Transform4x4f.h" +#include "renderers/Renderer.h" namespace Renderer { @@ -20,6 +20,7 @@ namespace Renderer static GLenum convertBlendFactor(const Blend::Factor _blendFactor) { + // clang-format off switch (_blendFactor) { case Blend::ZERO: { return GL_ZERO; } break; case Blend::ONE: { return GL_ONE; } break; @@ -33,31 +34,29 @@ namespace Renderer case Blend::ONE_MINUS_DST_ALPHA: { return GL_ONE_MINUS_DST_ALPHA; } break; default: { return GL_ZERO; } } + // clang-format on } static GLenum convertTextureType(const Texture::Type _type) { + // clang-format off switch (_type) { case Texture::RGBA: { return GL_RGBA; } break; case Texture::ALPHA: { return GL_ALPHA; } break; default: { return GL_ZERO; } } - } - - unsigned int getWindowFlags() - { - return SDL_WINDOW_OPENGL; + // clang-format on } void setupWindow() { - #if defined(__APPLE__) +#if defined(__APPLE__) // This is required on macOS, as the operating system will otherwise insist on using // a newer OpenGL version which completely breaks the application. SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); - #else +#else SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - #endif +#endif SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); @@ -78,32 +77,34 @@ namespace Renderer return false; } - #if defined(_WIN64) +#if defined(_WIN64) glewInit(); - #endif +#endif SDL_GL_MakeCurrent(getSDLWindow(), sdlContext); - std::string vendor = glGetString(GL_VENDOR) ? - reinterpret_cast(glGetString(GL_VENDOR)) : ""; - std::string renderer = glGetString(GL_RENDERER) ? - reinterpret_cast(glGetString(GL_RENDERER)) : ""; - std::string version = glGetString(GL_VERSION) ? - reinterpret_cast(glGetString(GL_VERSION)) : ""; + std::string vendor = + glGetString(GL_VENDOR) ? reinterpret_cast(glGetString(GL_VENDOR)) : ""; + std::string renderer = + glGetString(GL_RENDERER) ? reinterpret_cast(glGetString(GL_RENDERER)) : ""; + std::string version = + glGetString(GL_VERSION) ? reinterpret_cast(glGetString(GL_VERSION)) : ""; std::string extensions = glGetString(GL_EXTENSIONS) ? - reinterpret_cast(glGetString(GL_EXTENSIONS)) : ""; + reinterpret_cast(glGetString(GL_EXTENSIONS)) : + ""; LOG(LogInfo) << "GL vendor: " << vendor; LOG(LogInfo) << "GL renderer: " << renderer; LOG(LogInfo) << "GL version: " << version; - #if defined(_WIN64) +#if defined(_WIN64) LOG(LogInfo) << "EmulationStation renderer: OpenGL 2.1 with GLEW"; - #else +#else LOG(LogInfo) << "EmulationStation renderer: OpenGL 2.1"; - #endif +#endif LOG(LogInfo) << "Checking available OpenGL extensions..."; std::string glExts = glGetString(GL_EXTENSIONS) ? - reinterpret_cast(glGetString(GL_EXTENSIONS)) : ""; + reinterpret_cast(glGetString(GL_EXTENSIONS)) : + ""; if (extensions.find("GL_ARB_texture_non_power_of_two") == std::string::npos) { LOG(LogError) << "GL_ARB_texture_non_power_of_two: MISSING"; missingExtension = true; @@ -137,7 +138,7 @@ namespace Renderer return false; } - uint8_t data[4] = {255, 255, 255, 255}; + uint8_t data[4] = { 255, 255, 255, 255 }; whiteTexture = createTexture(Texture::RGBA, false, true, 1, 1, data); GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); @@ -162,13 +163,12 @@ namespace Renderer sdlContext = nullptr; } - unsigned int createTexture( - const Texture::Type _type, - const bool _linear, - const bool _repeat, - const unsigned int _width, - const unsigned int _height, - void* _data) + unsigned int createTexture(const Texture::Type _type, + const bool _linear, + const bool _repeat, + const unsigned int _width, + const unsigned int _height, + void* _data) { const GLenum type = convertTextureType(_type); unsigned int texture; @@ -176,16 +176,19 @@ namespace Renderer GL_CHECK_ERROR(glGenTextures(1, &texture)); GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); - GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, _repeat ? - static_cast(GL_REPEAT) : static_cast(GL_CLAMP_TO_EDGE))); - GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, _repeat ? - static_cast(GL_REPEAT) : static_cast(GL_CLAMP_TO_EDGE))); - GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _linear ? - static_cast(GL_LINEAR) : static_cast(GL_NEAREST))); + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + _repeat ? static_cast(GL_REPEAT) : + static_cast(GL_CLAMP_TO_EDGE))); + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + _repeat ? static_cast(GL_REPEAT) : + static_cast(GL_CLAMP_TO_EDGE))); + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + _linear ? static_cast(GL_LINEAR) : + static_cast(GL_NEAREST))); GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, type, _width, _height, 0, type, - GL_UNSIGNED_BYTE, _data)); + GL_UNSIGNED_BYTE, _data)); return texture; } @@ -195,20 +198,19 @@ namespace Renderer GL_CHECK_ERROR(glDeleteTextures(1, &_texture)); } - void updateTexture( - const unsigned int _texture, - const Texture::Type _type, - const unsigned int _x, - const unsigned _y, - const unsigned int _width, - const unsigned int _height, - void* _data) + void updateTexture(const unsigned int _texture, + const Texture::Type _type, + const unsigned int _x, + const unsigned _y, + const unsigned int _width, + const unsigned int _height, + void* _data) { const GLenum type = convertTextureType(_type); GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, _texture)); - GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, _width, _height, - type, GL_UNSIGNED_BYTE, _data)); + GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, _width, _height, type, + GL_UNSIGNED_BYTE, _data)); GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, whiteTexture)); } @@ -220,29 +222,27 @@ namespace Renderer GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, _texture)); } - void drawLines( - const Vertex* _vertices, - const unsigned int _numVertices, - const Blend::Factor _srcBlendFactor, - const Blend::Factor _dstBlendFactor) + void drawLines(const Vertex* _vertices, + const unsigned int _numVertices, + const Blend::Factor _srcBlendFactor, + const Blend::Factor _dstBlendFactor) { GL_CHECK_ERROR(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].pos)); GL_CHECK_ERROR(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].tex)); - GL_CHECK_ERROR(glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof(Vertex), &_vertices[0].col)); + GL_CHECK_ERROR(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &_vertices[0].col)); - GL_CHECK_ERROR(glBlendFunc(convertBlendFactor(_srcBlendFactor), - convertBlendFactor(_dstBlendFactor))); + GL_CHECK_ERROR( + glBlendFunc(convertBlendFactor(_srcBlendFactor), convertBlendFactor(_dstBlendFactor))); GL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, _numVertices)); } - void drawTriangleStrips( - const Vertex* _vertices, - const unsigned int _numVertices, - const Transform4x4f& _trans, - const Blend::Factor _srcBlendFactor, - const Blend::Factor _dstBlendFactor, - const shaderParameters& _parameters) + void drawTriangleStrips(const Vertex* _vertices, + const unsigned int _numVertices, + const Transform4x4f& _trans, + const Blend::Factor _srcBlendFactor, + const Blend::Factor _dstBlendFactor, + const shaderParameters& _parameters) { float width = _vertices[3].pos[0]; float height = _vertices[3].pos[1]; @@ -251,17 +251,17 @@ namespace Renderer GL_CHECK_ERROR(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].tex)); GL_CHECK_ERROR(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &_vertices[0].col)); - GL_CHECK_ERROR(glBlendFunc(convertBlendFactor(_srcBlendFactor), - convertBlendFactor(_dstBlendFactor))); + GL_CHECK_ERROR( + glBlendFunc(convertBlendFactor(_srcBlendFactor), convertBlendFactor(_dstBlendFactor))); - #if defined(USE_OPENGL_21) +#if defined(USE_OPENGL_21) if (_vertices[0].shaders == 0) { GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices)); } else { // If saturation is set below the maximum (default) value, run the // desaturation shader. - if (_vertices->saturation < 1.0 || _parameters.fragmentSaturation < 1.0) { + if (_vertices->saturation < 1.0f || _parameters.fragmentSaturation < 1.0f) { Shader* runShader = getShaderProgram(SHADER_DESATURATE); // Only try to use the shader if it has been loaded properly. if (runShader) { @@ -278,9 +278,8 @@ namespace Renderer if (runShader) { runShader->activateShaders(); runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans); - _vertices->opacity < 1.0 ? - runShader->setOpacity(_vertices->opacity) : - runShader->setOpacity(_parameters.fragmentOpacity); + _vertices->opacity < 1.0f ? runShader->setOpacity(_vertices->opacity) : + runShader->setOpacity(_parameters.fragmentOpacity); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices)); runShader->deactivateShaders(); } @@ -303,7 +302,7 @@ namespace Renderer if (runShader) { runShader->activateShaders(); runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans); - runShader->setTextureSize({width, height}); + runShader->setTextureSize({ width, height }); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices)); runShader->deactivateShaders(); } @@ -314,7 +313,7 @@ namespace Renderer if (runShader) { runShader->activateShaders(); runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans); - runShader->setTextureSize({width, height}); + runShader->setTextureSize({ width, height }); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices)); runShader->deactivateShaders(); } @@ -338,20 +337,20 @@ namespace Renderer // scanlines to videos with non-standard aspect ratios. float relativeWidth = width / getScreenWidth(); float relativeAdjustment = (relativeWidth + relativeHeight) / 2.0f; - float modifier = 1.41f + relativeAdjustment / 7.0f - - (0.14f * screenHeightModifier); + float modifier = + 1.41f + relativeAdjustment / 7.0f - (0.14f * screenHeightModifier); shaderHeight = height * modifier; } if (runShader) { runShader->activateShaders(); runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans); - runShader->setTextureSize({shaderWidth, shaderHeight}); + runShader->setTextureSize({ shaderWidth, shaderHeight }); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices)); runShader->deactivateShaders(); } } } - #endif +#endif } void setProjection(const Transform4x4f& _projection) @@ -372,8 +371,8 @@ namespace Renderer void setViewport(const Rect& _viewport) { // glViewport starts at the bottom left of the window. - GL_CHECK_ERROR(glViewport( _viewport.x, getWindowHeight() - - _viewport.y - _viewport.h, _viewport.w, _viewport.h)); + GL_CHECK_ERROR(glViewport(_viewport.x, getWindowHeight() - _viewport.y - _viewport.h, + _viewport.w, _viewport.h)); } void setScissor(const Rect& _scissor) @@ -383,8 +382,8 @@ namespace Renderer } else { // glScissor starts at the bottom left of the window. - GL_CHECK_ERROR(glScissor(_scissor.x, getWindowHeight() - - _scissor.y - _scissor.h, _scissor.w, _scissor.h)); + GL_CHECK_ERROR(glScissor(_scissor.x, getWindowHeight() - _scissor.y - _scissor.h, + _scissor.w, _scissor.h)); GL_CHECK_ERROR(glEnable(GL_SCISSOR_TEST)); } } @@ -413,11 +412,12 @@ namespace Renderer GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); } - void shaderPostprocessing(unsigned int shaders, const Renderer::shaderParameters& parameters, - unsigned char* textureRGBA) + void shaderPostprocessing(unsigned int shaders, + const Renderer::shaderParameters& parameters, + unsigned char* textureRGBA) { Vertex vertices[4]; - std::vectorshaderList; + std::vector shaderList; GLuint width = getScreenWidth(); GLuint height = getScreenHeight(); float widthf = static_cast(width); @@ -425,10 +425,12 @@ namespace Renderer // Set vertex positions and texture coordinates to full screen as all // postprocessing is applied to the complete screen area. - vertices[0] = { { 0, 0 }, { 0, 1 }, 0 }; - vertices[1] = { { 0, heightf }, { 0, 0 }, 0 }; - vertices[2] = { { widthf, 0 }, { 1, 1 }, 0 }; - vertices[3] = { { widthf, heightf }, { 1, 0 }, 0}; + // clang-format off + vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 1.0f }, 0 }; + vertices[1] = { { 0.0f , heightf }, { 0.0f, 0.0f }, 0 }; + vertices[2] = { { widthf, 0.0f }, { 1.0f, 1.0f }, 0 }; + vertices[3] = { { widthf, heightf }, { 1.0f, 0.0f }, 0 }; + // clang-format on if (shaders & Renderer::SHADER_DESATURATE) shaderList.push_back(Renderer::SHADER_DESATURATE); @@ -457,27 +459,24 @@ namespace Renderer // For the blur shaders there is an optional variable to set the number of passes // to execute, which proportionally affects the blur amount. if (shaderList[i] == Renderer::SHADER_BLUR_HORIZONTAL || - shaderList[i] == Renderer::SHADER_BLUR_VERTICAL) + shaderList[i] == Renderer::SHADER_BLUR_VERTICAL) { shaderPasses = parameters.blurPasses; + } for (int p = 0; p < shaderPasses; p++) { GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO)); // Attach the texture to the shader framebuffer. - GL_CHECK_ERROR(glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - screenTexture, - 0)); + GL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, screenTexture, 0)); // Blit the screen contents to screenTexture. GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, - GL_COLOR_BUFFER_BIT, GL_NEAREST)); + GL_COLOR_BUFFER_BIT, GL_NEAREST)); // Apply/render the shaders. - drawTriangleStrips(vertices, 4, Transform4x4f::Identity(), - Blend::SRC_ALPHA, Blend::ONE_MINUS_SRC_ALPHA, parameters); + drawTriangleStrips(vertices, 4, Transform4x4f::Identity(), Blend::SRC_ALPHA, + Blend::ONE_MINUS_SRC_ALPHA, parameters); // If textureRGBA has an address, it means that the output should go to this // texture rather than to the screen. The glReadPixels() function is slow, but @@ -485,8 +484,8 @@ namespace Renderer // screen texture, it doesn't really matter. if (textureRGBA) { GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO)); - GL_CHECK_ERROR(glReadPixels(0, 0, width, height, - GL_RGBA, GL_UNSIGNED_BYTE, textureRGBA)); + GL_CHECK_ERROR( + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, textureRGBA)); GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); } else { @@ -494,7 +493,7 @@ namespace Renderer GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO)); GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, - GL_COLOR_BUFFER_BIT, GL_NEAREST)); + GL_COLOR_BUFFER_BIT, GL_NEAREST)); } } } @@ -503,6 +502,6 @@ namespace Renderer destroyTexture(screenTexture); } -} // Renderer:: +} // namespace Renderer #endif // USE_OPENGL_21 diff --git a/es-core/src/renderers/Renderer_GLES10.cpp b/es-core/src/renderers/Renderer_GLES10.cpp index e9e3a03f1..a179ca5fe 100644 --- a/es-core/src/renderers/Renderer_GLES10.cpp +++ b/es-core/src/renderers/Renderer_GLES10.cpp @@ -8,10 +8,10 @@ #if defined(USE_OPENGLES_10) -#include "renderers/Renderer.h" -#include "math/Transform4x4f.h" #include "Log.h" #include "Settings.h" +#include "math/Transform4x4f.h" +#include "renderers/Renderer.h" #include #include @@ -23,6 +23,7 @@ namespace Renderer static GLenum convertBlendFactor(const Blend::Factor _blendFactor) { + // clang-format off switch (_blendFactor) { case Blend::ZERO: { return GL_ZERO; } break; case Blend::ONE: { return GL_ONE; } break; @@ -36,20 +37,18 @@ namespace Renderer case Blend::ONE_MINUS_DST_ALPHA: { return GL_ONE_MINUS_DST_ALPHA; } break; default: { return GL_ZERO; } } + // clang-format on } static GLenum convertTextureType(const Texture::Type _type) { + // clang-format off switch (_type) { case Texture::RGBA: { return GL_RGBA; } break; case Texture::ALPHA: { return GL_ALPHA; } break; default: { return GL_ZERO; } } - } - - unsigned int getWindowFlags() - { - return SDL_WINDOW_OPENGL; + // clang-format on } void setupWindow() @@ -70,14 +69,15 @@ namespace Renderer sdlContext = SDL_GL_CreateContext(getSDLWindow()); SDL_GL_MakeCurrent(getSDLWindow(), sdlContext); - std::string vendor = glGetString(GL_VENDOR) ? - reinterpret_cast(glGetString(GL_VENDOR)) : ""; - std::string renderer = glGetString(GL_RENDERER) ? - reinterpret_cast(glGetString(GL_RENDERER)) : ""; - std::string version = glGetString(GL_VERSION) ? - reinterpret_cast(glGetString(GL_VERSION)) : ""; + std::string vendor = + glGetString(GL_VENDOR) ? reinterpret_cast(glGetString(GL_VENDOR)) : ""; + std::string renderer = + glGetString(GL_RENDERER) ? reinterpret_cast(glGetString(GL_RENDERER)) : ""; + std::string version = + glGetString(GL_VERSION) ? reinterpret_cast(glGetString(GL_VERSION)) : ""; std::string extensions = glGetString(GL_EXTENSIONS) ? - reinterpret_cast(glGetString(GL_EXTENSIONS)) : ""; + reinterpret_cast(glGetString(GL_EXTENSIONS)) : + ""; LOG(LogInfo) << "GL vendor: " << vendor; LOG(LogInfo) << "GL renderer: " << renderer; @@ -85,12 +85,13 @@ namespace Renderer LOG(LogInfo) << "EmulationStation renderer: OpenGL ES 1.0"; LOG(LogInfo) << "Checking available OpenGL ES extensions..."; std::string glExts = glGetString(GL_EXTENSIONS) ? - reinterpret_cast(glGetString(GL_EXTENSIONS)) : ""; - LOG(LogInfo) << "GL_OES_texture_npot: " << - (extensions.find("GL_OES_texture_npot") != - std::string::npos ? "OK" : "MISSING"); + reinterpret_cast(glGetString(GL_EXTENSIONS)) : + ""; + LOG(LogInfo) << "GL_OES_texture_npot: " + << (extensions.find("GL_OES_texture_npot") != std::string::npos ? "OK" : + "MISSING"); - uint8_t data[4] = {255, 255, 255, 255}; + uint8_t data[4] = { 255, 255, 255, 255 }; whiteTexture = createTexture(Texture::RGBA, false, true, 1, 1, data); GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); @@ -111,13 +112,12 @@ namespace Renderer sdlContext = nullptr; } - unsigned int createTexture( - const Texture::Type _type, - const bool _linear, - const bool _repeat, - const unsigned int _width, - const unsigned int _height, - void* _data) + unsigned int createTexture(const Texture::Type _type, + const bool _linear, + const bool _repeat, + const unsigned int _width, + const unsigned int _height, + void* _data) { const GLenum type = convertTextureType(_type); unsigned int texture; @@ -125,16 +125,16 @@ namespace Renderer GL_CHECK_ERROR(glGenTextures(1, &texture)); GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture)); - GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, _repeat ? - GL_REPEAT : GL_CLAMP_TO_EDGE)); - GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, _repeat ? - GL_REPEAT : GL_CLAMP_TO_EDGE)); - GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _linear ? - GL_LINEAR : GL_NEAREST)); + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + _repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE)); + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + _repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE)); + GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + _linear ? GL_LINEAR : GL_NEAREST)); GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, type, _width, _height, 0, type, - GL_UNSIGNED_BYTE, _data)); + GL_UNSIGNED_BYTE, _data)); return texture; } @@ -144,20 +144,19 @@ namespace Renderer GL_CHECK_ERROR(glDeleteTextures(1, &_texture)); } - void updateTexture( - const unsigned int _texture, - const Texture::Type _type, - const unsigned int _x, - const unsigned _y, - const unsigned int _width, - const unsigned int _height, - void* _data) + void updateTexture(const unsigned int _texture, + const Texture::Type _type, + const unsigned int _x, + const unsigned _y, + const unsigned int _width, + const unsigned int _height, + void* _data) { const GLenum type = convertTextureType(_type); GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, _texture)); GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, _width, _height, type, - GL_UNSIGNED_BYTE, _data)); + GL_UNSIGNED_BYTE, _data)); GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, whiteTexture)); } @@ -169,36 +168,34 @@ namespace Renderer GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, _texture)); } - void drawLines( - const Vertex* _vertices, - const unsigned int _numVertices, - const Blend::Factor _srcBlendFactor, - const Blend::Factor _dstBlendFactor) + void drawLines(const Vertex* _vertices, + const unsigned int _numVertices, + const Blend::Factor _srcBlendFactor, + const Blend::Factor _dstBlendFactor) { GL_CHECK_ERROR(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].pos)); GL_CHECK_ERROR(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].tex)); GL_CHECK_ERROR(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &_vertices[0].col)); - GL_CHECK_ERROR(glBlendFunc(convertBlendFactor(_srcBlendFactor), - convertBlendFactor(_dstBlendFactor))); + GL_CHECK_ERROR( + glBlendFunc(convertBlendFactor(_srcBlendFactor), convertBlendFactor(_dstBlendFactor))); GL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, _numVertices)); } - void drawTriangleStrips( - const Vertex* _vertices, - const unsigned int _numVertices, - const Transform4x4f& _trans, - const Blend::Factor _srcBlendFactor, - const Blend::Factor _dstBlendFactor, - const shaderParameters& _parameters) + void drawTriangleStrips(const Vertex* _vertices, + const unsigned int _numVertices, + const Transform4x4f& _trans, + const Blend::Factor _srcBlendFactor, + const Blend::Factor _dstBlendFactor, + const shaderParameters& _parameters) { GL_CHECK_ERROR(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].pos)); GL_CHECK_ERROR(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].tex)); GL_CHECK_ERROR(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &_vertices[0].col)); - GL_CHECK_ERROR(glBlendFunc(convertBlendFactor(_srcBlendFactor), - convertBlendFactor(_dstBlendFactor))); + GL_CHECK_ERROR( + glBlendFunc(convertBlendFactor(_srcBlendFactor), convertBlendFactor(_dstBlendFactor))); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices)); } @@ -221,8 +218,8 @@ namespace Renderer void setViewport(const Rect& _viewport) { // glViewport starts at the bottom left of the window. - GL_CHECK_ERROR(glViewport( _viewport.x, getWindowHeight() - - _viewport.y - _viewport.h, _viewport.w, _viewport.h)); + GL_CHECK_ERROR(glViewport(_viewport.x, getWindowHeight() - _viewport.y - _viewport.h, + _viewport.w, _viewport.h)); } void setScissor(const Rect& _scissor) @@ -232,8 +229,8 @@ namespace Renderer } else { // glScissor starts at the bottom left of the window. - GL_CHECK_ERROR(glScissor(_scissor.x, getWindowHeight() - - _scissor.y - _scissor.h, _scissor.w, _scissor.h)); + GL_CHECK_ERROR(glScissor(_scissor.x, getWindowHeight() - _scissor.y - _scissor.h, + _scissor.w, _scissor.h)); GL_CHECK_ERROR(glEnable(GL_SCISSOR_TEST)); } } @@ -262,6 +259,6 @@ namespace Renderer GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); } -} // Renderer:: +} // namespace Renderer #endif // USE_OPENGLES_10 diff --git a/es-core/src/renderers/Shader_GL21.cpp b/es-core/src/renderers/Shader_GL21.cpp index 44b467de6..52d376e53 100644 --- a/es-core/src/renderers/Shader_GL21.cpp +++ b/es-core/src/renderers/Shader_GL21.cpp @@ -10,26 +10,27 @@ #include "Shader_GL21.h" +#include "Log.h" #include "renderers/Renderer.h" #include "resources/ResourceManager.h" -#include "Log.h" namespace Renderer { Renderer::Shader::Shader() - : mProgramID(-1), - shaderMVPMatrix(-1), - shaderTextureSize(-1), - shaderTextureCoord(-1), - shaderColor(-1), - shaderSaturation(-1), - shaderOpacity(-1), - shaderDimValue(-1) + : mProgramID(-1) + , shaderMVPMatrix(-1) + , shaderTextureSize(-1) + , shaderTextureCoord(-1) + , shaderColor(-1) + , shaderSaturation(-1) + , shaderOpacity(-1) + , shaderDimValue(-1) { } Renderer::Shader::~Shader() { + // Delete the shader program when destroyed. deleteProgram(mProgramID); } @@ -52,8 +53,7 @@ namespace Renderer else if (shaderType == GL_FRAGMENT_SHADER) preprocessorDefines += "#define FRAGMENT\n"; - shaderVector.push_back(std::make_tuple( - path, preprocessorDefines + shaderCode, shaderType)); + shaderVector.push_back(std::make_tuple(path, preprocessorDefines + shaderCode, shaderType)); } bool Renderer::Shader::createProgram() @@ -67,16 +67,16 @@ namespace Renderer GLuint currentShader = glCreateShader(std::get<2>(*it)); GLchar const* shaderCodePtr = std::get<1>(*it).c_str(); - glShaderSource(currentShader, 1, - reinterpret_cast(&shaderCodePtr), nullptr); + glShaderSource(currentShader, 1, reinterpret_cast(&shaderCodePtr), + nullptr); glCompileShader(currentShader); GLint shaderCompiled; glGetShaderiv(currentShader, GL_COMPILE_STATUS, &shaderCompiled); if (shaderCompiled != GL_TRUE) { - LOG(LogError) << "OpenGL error: Unable to compile shader " << - currentShader << " (" << std::get<0>(*it) << ")."; + LOG(LogError) << "OpenGL error: Unable to compile shader " << currentShader << " (" + << std::get<0>(*it) << ")."; printShaderInfoLog(currentShader, std::get<2>(*it)); return false; } @@ -118,7 +118,7 @@ namespace Renderer { if (shaderMVPMatrix != -1) GL_CHECK_ERROR(glUniformMatrix4fv(shaderMVPMatrix, 1, GL_FALSE, - reinterpret_cast(&mvpMatrix))); + reinterpret_cast(&mvpMatrix))); } void Renderer::Shader::setTextureSize(std::array shaderVec2) @@ -130,16 +130,16 @@ namespace Renderer void Renderer::Shader::setTextureCoordinates(std::array shaderVec4) { if (shaderTextureCoord != -1) { - glVertexAttrib4f(shaderTextureCoord, shaderVec4[0], shaderVec4[1], - shaderVec4[2], shaderVec4[3]); + glVertexAttrib4f(shaderTextureCoord, shaderVec4[0], shaderVec4[1], shaderVec4[2], + shaderVec4[3]); } } void Renderer::Shader::setColor(std::array shaderVec4) { if (shaderColor != -1) - GL_CHECK_ERROR(glUniform4f(shaderColor, shaderVec4[0], - shaderVec4[1], shaderVec4[2], shaderVec4[3])); + GL_CHECK_ERROR(glUniform4f(shaderColor, shaderVec4[0], shaderVec4[1], shaderVec4[2], + shaderVec4[3])); } void Renderer::Shader::setSaturation(GLfloat saturation) @@ -162,19 +162,16 @@ namespace Renderer void Renderer::Shader::activateShaders() { + // Install the shader program. GL_CHECK_ERROR(glUseProgram(mProgramID)); } void Renderer::Shader::deactivateShaders() { + // Remove the shader program. GL_CHECK_ERROR(glUseProgram(0)); } - GLuint Renderer::Shader::getProgramID() - { - return mProgramID; - } - void Renderer::Shader::printProgramInfoLog(GLuint programID) { if (glIsProgram(programID)) { @@ -187,8 +184,8 @@ namespace Renderer glGetProgramInfoLog(programID, maxLength, &logLength, &infoLog.front()); if (logLength > 0) { - LOG(LogDebug) << "Renderer_GL21::printProgramLog():\n" << - std::string(infoLog.begin(), infoLog.end()); + LOG(LogDebug) << "Renderer_GL21::printProgramLog():\n" + << std::string(infoLog.begin(), infoLog.end()); } } else { @@ -208,9 +205,10 @@ namespace Renderer glGetShaderInfoLog(shaderID, maxLength, &logLength, &infoLog.front()); if (logLength > 0) { - LOG(LogDebug) << "Renderer_GL21::printShaderLog(): Error in " << - (shaderType == GL_VERTEX_SHADER ? "VERTEX section:\n" : - "FRAGMENT section:\n") << std::string(infoLog.begin(), infoLog.end()); + LOG(LogDebug) << "Renderer_GL21::printShaderLog(): Error in " + << (shaderType == GL_VERTEX_SHADER ? "VERTEX section:\n" : + "FRAGMENT section:\n") + << std::string(infoLog.begin(), infoLog.end()); } } else { @@ -218,6 +216,6 @@ namespace Renderer } } -} // Renderer +} // namespace Renderer #endif // USE_OPENGL_21 diff --git a/es-core/src/renderers/Shader_GL21.h b/es-core/src/renderers/Shader_GL21.h index f92e6e28b..94a78b40f 100644 --- a/es-core/src/renderers/Shader_GL21.h +++ b/es-core/src/renderers/Shader_GL21.h @@ -52,7 +52,7 @@ namespace Renderer // Sets the shader program to 0 which reverts to the fixed function pipeline. void deactivateShaders(); // Returns the program ID that was generated by glCreateProgram(). - GLuint getProgramID(); + GLuint getProgramID() { return mProgramID; } // Only used for error logging if the shaders fail to compile or link. void printProgramInfoLog(GLuint programID); void printShaderInfoLog(GLuint shaderID, GLenum shaderType); @@ -71,6 +71,6 @@ namespace Renderer GLint shaderDimValue; }; -} // Renderer +} // namespace Renderer #endif // ES_CORE_RENDERER_SHADER_GL21_H diff --git a/es-core/src/resources/Font.cpp b/es-core/src/resources/Font.cpp index 5f3f6f94b..59011ce8f 100644 --- a/es-core/src/resources/Font.cpp +++ b/es-core/src/resources/Font.cpp @@ -9,20 +9,20 @@ #include "resources/Font.h" +#include "Log.h" #include "renderers/Renderer.h" #include "utils/FileSystemUtil.h" #include "utils/StringUtil.h" -#include "Log.h" FT_Library Font::sLibrary = nullptr; -int Font::getSize() const { return mSize; } - std::map, std::weak_ptr> Font::sFontMap; -Font::FontFace::FontFace(ResourceData&& d, int size) : data(d) +Font::FontFace::FontFace(ResourceData&& d, int size) + : data(d) { - int err = FT_New_Memory_Face(sLibrary, data.ptr.get(), (FT_Long)data.length, 0, &face); + int err = + FT_New_Memory_Face(sLibrary, data.ptr.get(), static_cast(data.length), 0, &face); assert(!err); if (!err) @@ -38,6 +38,7 @@ Font::FontFace::~FontFace() void Font::initLibrary() { assert(sLibrary == nullptr); + if (FT_Init_FreeType(&sLibrary)) { sLibrary = nullptr; LOG(LogError) << "Couldn't initialize FreeType"; @@ -74,7 +75,9 @@ size_t Font::getTotalMemUsage() return total; } -Font::Font(int size, const std::string& path) : mSize(size), mPath(path) +Font::Font(int size, const std::string& path) + : mSize(size) + , mPath(path) { assert(mSize > 0); @@ -96,9 +99,8 @@ Font::~Font() auto fontEntry = sFontMap.find(std::pair(mPath, mSize)); - if (fontEntry != sFontMap.cend()) { + if (fontEntry != sFontMap.cend()) sFontMap.erase(fontEntry); - } if (sFontMap.empty() && sLibrary) { FT_Done_FreeType(sLibrary); @@ -106,21 +108,11 @@ Font::~Font() } } -void Font::reload(std::shared_ptr& /*rm*/) -{ - rebuildTextures(); -} - -void Font::unload(std::shared_ptr& /*rm*/) -{ - unloadTextures(); -} - std::shared_ptr Font::get(int size, const std::string& path) { const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(path); - std::pair def(canonicalPath.empty() ? getDefaultPath() : canonicalPath, size); + auto foundFont = sFontMap.find(def); if (foundFont != sFontMap.cend()) { if (!foundFont->second.expired()) @@ -152,6 +144,7 @@ Font::FontTexture::FontTexture(const int mSize) Font::FontTexture::~FontTexture() { + // Deinit the texture when destroyed. deinitTexture(); } @@ -162,14 +155,12 @@ bool Font::FontTexture::findEmpty(const Vector2i& size, Vector2i& cursor_out) if (writePos.x() + size.x() >= textureSize.x() && writePos.y() + rowHeight + size.y() + 1 < textureSize.y()) { - // Row full, but it should fit on the next row. - // Move cursor to next row. + // Row full, but it should fit on the next row so move the cursor there. writePos = Vector2i(0, writePos.y() + rowHeight + 1); // Leave 1px of space between glyphs. rowHeight = 0; } - if (writePos.x() + size.x() >= textureSize.x() || - writePos.y() + size.y() >= textureSize.y()) { + if (writePos.x() + size.x() >= textureSize.x() || writePos.y() + size.y() >= textureSize.y()) { // Nope, still won't fit. return false; } @@ -186,21 +177,21 @@ bool Font::FontTexture::findEmpty(const Vector2i& size, Vector2i& cursor_out) void Font::FontTexture::initTexture() { assert(textureId == 0); - textureId = Renderer::createTexture(Renderer::Texture::ALPHA, false, false, - textureSize.x(), textureSize.y(), nullptr); + textureId = Renderer::createTexture(Renderer::Texture::ALPHA, false, false, textureSize.x(), + textureSize.y(), nullptr); } void Font::FontTexture::deinitTexture() { - if (textureId != 0) - { + if (textureId != 0) { Renderer::destroyTexture(textureId); textureId = 0; } } void Font::getTextureForNewGlyph(const Vector2i& glyphSize, - FontTexture*& tex_out, Vector2i& cursor_out) + FontTexture*& tex_out, + Vector2i& cursor_out) { if (mTextures.size()) { // Check if the most recent texture has space. @@ -218,8 +209,8 @@ void Font::getTextureForNewGlyph(const Vector2i& glyphSize, bool ok = tex_out->findEmpty(glyphSize, cursor_out); if (!ok) { - LOG(LogError) << "Glyph too big to fit on a new texture (glyph size > " << - tex_out->textureSize.x() << ", " << tex_out->textureSize.y() << ")"; + LOG(LogError) << "Glyph too big to fit on a new texture (glyph size > " + << tex_out->textureSize.x() << ", " << tex_out->textureSize.y() << ")"; tex_out = nullptr; } } @@ -234,23 +225,20 @@ std::vector getFallbackFontPaths() ResourceManager::getInstance()->getResourcePath(":/fonts/Akrobat-SemiBold.ttf"); // Vera sans Unicode. - fontPaths.push_back(ResourceManager::getInstance()-> - getResourcePath(":/fonts/DejaVuSans.ttf")); + fontPaths.push_back(ResourceManager::getInstance()->getResourcePath(":/fonts/DejaVuSans.ttf")); // GNU FreeFont monospaced. - fontPaths.push_back(ResourceManager::getInstance()-> - getResourcePath(":/fonts/FreeMono.ttf")); + fontPaths.push_back(ResourceManager::getInstance()->getResourcePath(":/fonts/FreeMono.ttf")); // Various languages, such as Japanese and Chinese. - fontPaths.push_back(ResourceManager::getInstance()-> - getResourcePath(":/fonts/DroidSansFallbackFull.ttf")); + fontPaths.push_back( + ResourceManager::getInstance()->getResourcePath(":/fonts/DroidSansFallbackFull.ttf")); // Korean. - fontPaths.push_back(ResourceManager::getInstance()-> - getResourcePath(":/fonts/NanumMyeongjo.ttf")); + fontPaths.push_back( + ResourceManager::getInstance()->getResourcePath(":/fonts/NanumMyeongjo.ttf")); // Font Awesome icon glyphs, used for various special symbols like stars, folders etc. - fontPaths.push_back(ResourceManager::getInstance()-> - getResourcePath(":/fonts/fontawesome-webfont.ttf")); + fontPaths.push_back( + ResourceManager::getInstance()->getResourcePath(":/fonts/fontawesome-webfont.ttf")); // This is only needed for some really rare special characters. - fontPaths.push_back(ResourceManager::getInstance()-> - getResourcePath(":/fonts/Ubuntu-C.ttf")); + fontPaths.push_back(ResourceManager::getInstance()->getResourcePath(":/fonts/Ubuntu-C.ttf")); fontPaths.shrink_to_fit(); return fontPaths; @@ -283,10 +271,7 @@ FT_Face Font::getFaceForChar(unsigned int id) return mFaceCache.cbegin()->second->face; } -void Font::clearFaceCache() -{ - mFaceCache.clear(); -} +void Font::clearFaceCache() { mFaceCache.clear(); } Font::Glyph* Font::getGlyph(unsigned int id) { @@ -298,16 +283,16 @@ Font::Glyph* Font::getGlyph(unsigned int id) // Nope, need to make a glyph. FT_Face face = getFaceForChar(id); if (!face) { - LOG(LogError) << "Couldn't find appropriate font face for character " << - id << " for font " << mPath; + LOG(LogError) << "Couldn't find appropriate font face for character " << id << " for font " + << mPath; return nullptr; } FT_GlyphSlot g = face->glyph; if (FT_Load_Char(face, id, FT_LOAD_RENDER)) { - LOG(LogError) << "Couldn't find glyph for character " << - id << " for font " << mPath << ", size " << mSize; + LOG(LogError) << "Couldn't find glyph for character " << id << " for font " << mPath + << ", size " << mSize; return nullptr; } @@ -320,8 +305,8 @@ Font::Glyph* Font::getGlyph(unsigned int id) // getTextureForNewGlyph can fail if the glyph is bigger than the max texture // size (absurdly large font size). if (tex == nullptr) { - LOG(LogError) << "Couldn't create glyph for character " << id << " for font " << - mPath << ", size " << mSize << " (no suitable texture found)"; + LOG(LogError) << "Couldn't create glyph for character " << id << " for font " << mPath + << ", size " << mSize << " (no suitable texture found)"; return nullptr; } @@ -330,18 +315,18 @@ Font::Glyph* Font::getGlyph(unsigned int id) glyph.texture = tex; glyph.texPos = Vector2f(cursor.x() / static_cast(tex->textureSize.x()), - cursor.y() / static_cast(tex->textureSize.y())); + cursor.y() / static_cast(tex->textureSize.y())); glyph.texSize = Vector2f(glyphSize.x() / static_cast(tex->textureSize.x()), - glyphSize.y() / static_cast(tex->textureSize.y())); + glyphSize.y() / static_cast(tex->textureSize.y())); glyph.advance = Vector2f(static_cast(g->metrics.horiAdvance) / 64.0f, - static_cast(g->metrics.vertAdvance) / 64.0f); + static_cast(g->metrics.vertAdvance) / 64.0f); glyph.bearing = Vector2f(static_cast(g->metrics.horiBearingX) / 64.0f, - static_cast(g->metrics.horiBearingY) / 64.0f); + static_cast(g->metrics.horiBearingY) / 64.0f); // Upload glyph bitmap to texture. - Renderer::updateTexture(tex->textureId, Renderer::Texture::ALPHA, cursor.x(), - cursor.y(), glyphSize.x(), glyphSize.y(), g->bitmap.buffer); + Renderer::updateTexture(tex->textureId, Renderer::Texture::ALPHA, cursor.x(), cursor.y(), + glyphSize.x(), glyphSize.y(), g->bitmap.buffer); // Update max glyph height. if (glyphSize.y() > mMaxGlyphHeight) @@ -358,7 +343,7 @@ void Font::rebuildTextures() for (auto it = mTextures.begin(); it != mTextures.end(); it++) it->initTexture(); - // Reupload the texture data. + // Re-upload the texture data. for (auto it = mGlyphMap.cbegin(); it != mGlyphMap.cend(); it++) { FT_Face face = getFaceForChar(it->first); FT_GlyphSlot glyphSlot = face->glyph; @@ -370,13 +355,13 @@ void Font::rebuildTextures() // Find the position/size. Vector2i cursor(static_cast(it->second.texPos.x() * tex->textureSize.x()), - static_cast(it->second.texPos.y() * tex->textureSize.y())); + static_cast(it->second.texPos.y() * tex->textureSize.y())); Vector2i glyphSize(static_cast(it->second.texSize.x() * tex->textureSize.x()), - static_cast(it->second.texSize.y() * tex->textureSize.y())); + static_cast(it->second.texSize.y() * tex->textureSize.y())); // Upload to texture. - Renderer::updateTexture(tex->textureId, Renderer::Texture::ALPHA, - cursor.x(), cursor.y(), glyphSize.x(), glyphSize.y(), glyphSlot->bitmap.buffer); + Renderer::updateTexture(tex->textureId, Renderer::Texture::ALPHA, cursor.x(), cursor.y(), + glyphSize.x(), glyphSize.y(), glyphSlot->bitmap.buffer); } } @@ -394,7 +379,7 @@ void Font::renderTextCache(TextCache* cache) Renderer::bindTexture(*it->textureIdPtr); Renderer::drawTriangleStrips(&it->verts[0], - static_cast(it->verts.size())); + static_cast(it->verts.size())); } } @@ -442,6 +427,7 @@ std::string Font::getTextMaxWidth(std::string text, float maxWidth) float Font::getHeight(float lineSpacing) const { + // Return overall height including line spacing. return mMaxGlyphHeight * lineSpacing; } @@ -466,7 +452,7 @@ std::string Font::wrapText(std::string text, float xLen) float dotsSize = sizeText("...").x(); // While there's text or we still have text to render. - while (text.length() > 0) { + while (text.length() > 0) { space = text.find_first_of(" \t\n"); if (space == std::string::npos) space = text.length() - 1; @@ -515,8 +501,10 @@ Vector2f Font::sizeWrappedText(std::string text, float xLen, float lineSpacing) return sizeText(text, lineSpacing); } -Vector2f Font::getWrappedTextCursorOffset(std::string text, float xLen, - size_t stop, float lineSpacing) +Vector2f Font::getWrappedTextCursorOffset(std::string text, + float xLen, + size_t stop, + float lineSpacing) { std::string wrappedText = wrapText(text, xLen); @@ -557,36 +545,43 @@ Vector2f Font::getWrappedTextCursorOffset(std::string text, float xLen, // TextCache. // -float Font::getNewlineStartOffset(const std::string& text, const unsigned int& charStart, - const float& xLen, const Alignment& alignment) +float Font::getNewlineStartOffset(const std::string& text, + const unsigned int& charStart, + const float& xLen, + const Alignment& alignment) { switch (alignment) { - case ALIGN_LEFT: - return 0; - case ALIGN_CENTER: { + case ALIGN_LEFT: { + return 0; + } + case ALIGN_CENTER: { int endChar = 0; endChar = static_cast(text.find('\n', charStart)); - return (xLen - sizeText(text.substr(charStart, endChar != - std::string::npos ? endChar - charStart : endChar)).x()) / 2.0f; - } - case ALIGN_RIGHT: { + return (xLen - sizeText(text.substr(charStart, endChar != std::string::npos ? + endChar - charStart : + endChar)) + .x()) / + 2.0f; + } + case ALIGN_RIGHT: { int endChar = static_cast(text.find('\n', charStart)); - return xLen - (sizeText(text.substr(charStart, endChar != - std::string::npos ? endChar - charStart : endChar)).x()); - } - default: - return 0; + return xLen - (sizeText(text.substr(charStart, endChar != std::string::npos ? + endChar - charStart : + endChar)) + .x()); + } + default: + return 0; } } -TextCache* Font::buildTextCache( - const std::string& text, - Vector2f offset, - unsigned int color, - float xLen, - Alignment alignment, - float lineSpacing, - bool noTopMargin) +TextCache* Font::buildTextCache(const std::string& text, + Vector2f offset, + unsigned int color, + float xLen, + Alignment alignment, + float lineSpacing, + bool noTopMargin) { float x = offset[0] + (xLen != 0 ? getNewlineStartOffset(text, 0, xLen, alignment) : 0); float yTop = 0; @@ -618,9 +613,12 @@ TextCache* Font::buildTextCache( if (character == '\n') { y += getHeight(lineSpacing); - x = offset[0] + (xLen != 0 ? getNewlineStartOffset(text, - static_cast(cursor) /* cursor is already advanced */, - xLen, alignment) : 0); + x = offset[0] + (xLen != 0 ? + getNewlineStartOffset(text, + static_cast( + cursor) /* cursor is already advanced */, + xLen, alignment) : + 0); continue; } @@ -637,23 +635,22 @@ TextCache* Font::buildTextCache( const Vector2i& textureSize = glyph->texture->textureSize; const unsigned int convertedColor = Renderer::convertRGBAToABGR(color); - vertices[1] = { - { glyphStartX, y - glyph->bearing.y() }, - { glyph->texPos.x(), glyph->texPos.y() }, - convertedColor }; - vertices[2] = { - { glyphStartX, y - glyph->bearing.y() + (glyph->texSize.y() * textureSize.y()) }, - { glyph->texPos.x(), glyph->texPos.y() + glyph->texSize.y() }, - convertedColor }; - vertices[3] = { - { glyphStartX + glyph->texSize.x() * textureSize.x(), y - glyph->bearing.y() }, - { glyph->texPos.x() + glyph->texSize.x(), glyph->texPos.y() }, - convertedColor }; - vertices[4] = { - { glyphStartX + glyph->texSize.x() * textureSize.x(), y - glyph->bearing.y() + - (glyph->texSize.y() * textureSize.y()) }, - { glyph->texPos.x() + glyph->texSize.x(), glyph->texPos.y() + glyph->texSize.y() }, - convertedColor }; + vertices[1] = { { glyphStartX, y - glyph->bearing.y() }, + { glyph->texPos.x(), glyph->texPos.y() }, + convertedColor }; + vertices[2] = { { glyphStartX, + y - glyph->bearing.y() + (glyph->texSize.y() * textureSize.y()) }, + { glyph->texPos.x(), glyph->texPos.y() + glyph->texSize.y() }, + convertedColor }; + vertices[3] = { { glyphStartX + glyph->texSize.x() * textureSize.x(), + y - glyph->bearing.y() }, + { glyph->texPos.x() + glyph->texSize.x(), glyph->texPos.y() }, + convertedColor }; + vertices[4] = { { glyphStartX + glyph->texSize.x() * textureSize.x(), + y - glyph->bearing.y() + (glyph->texSize.y() * textureSize.y()) }, + { glyph->texPos.x() + glyph->texSize.x(), + glyph->texPos.y() + glyph->texSize.y() }, + convertedColor }; // Round vertices. for (int i = 1; i < 5; i++) @@ -667,7 +664,7 @@ TextCache* Font::buildTextCache( x += glyph->advance.x(); } - //TextCache::CacheMetrics metrics = { sizeText(text, lineSpacing) }; + // TextCache::CacheMetrics metrics = { sizeText(text, lineSpacing) }; TextCache* cache = new TextCache(); cache->vertexLists.resize(vertMap.size()); @@ -682,20 +679,18 @@ TextCache* Font::buildTextCache( } clearFaceCache(); - return cache; } -TextCache* Font::buildTextCache( - const std::string& text, - float offsetX, - float offsetY, - unsigned int color, - float lineSpacing, - bool noTopMargin) +TextCache* Font::buildTextCache(const std::string& text, + float offsetX, + float offsetY, + unsigned int color, + float lineSpacing, + bool noTopMargin) { - return buildTextCache(text, Vector2f(offsetX, offsetY), color, 0.0f, ALIGN_LEFT, - lineSpacing, noTopMargin); + return buildTextCache(text, Vector2f(offsetX, offsetY), color, 0.0f, ALIGN_LEFT, lineSpacing, + noTopMargin); } void TextCache::setColor(unsigned int color) @@ -708,7 +703,8 @@ void TextCache::setColor(unsigned int color) } std::shared_ptr Font::getFromTheme(const ThemeData::ThemeElement* elem, - unsigned int properties, const std::shared_ptr& orig) + unsigned int properties, + const std::shared_ptr& orig) { using namespace ThemeFlags; if (!(properties & FONT_PATH) && !(properties & FONT_SIZE)) @@ -725,8 +721,10 @@ std::shared_ptr Font::getFromTheme(const ThemeData::ThemeElement* elem, path = elem->get("fontPath"); if (!((path[0] == ':') && (path[1] == '/')) && !Utils::FileSystem::exists(path)) { - LOG(LogError) << "Font file \"" << path << "\" defined by the theme does not exist, " - "falling back to \"" << getDefaultPath() << "\""; + LOG(LogError) << "Font file \"" << path + << "\" defined by the theme does not exist, " + "falling back to \"" + << getDefaultPath() << "\""; path = getDefaultPath(); } diff --git a/es-core/src/resources/Font.h b/es-core/src/resources/Font.h index 15365cbcb..63bc3a757 100644 --- a/es-core/src/resources/Font.h +++ b/es-core/src/resources/Font.h @@ -10,11 +10,11 @@ #ifndef ES_CORE_RESOURCES_FONT_H #define ES_CORE_RESOURCES_FONT_H +#include "ThemeData.h" #include "math/Vector2f.h" #include "math/Vector2i.h" #include "renderers/Renderer.h" #include "resources/ResourceManager.h" -#include "ThemeData.h" #include #include FT_FREETYPE_H @@ -22,6 +22,7 @@ class TextCache; +// clang-format off #define FONT_SIZE_MINI (static_cast(0.030f * \ std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))) #define FONT_SIZE_SMALL (static_cast(0.035f * \ @@ -30,6 +31,7 @@ class TextCache; std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))) #define FONT_SIZE_LARGE (static_cast(0.085f * \ std::min(Renderer::getScreenHeight(), Renderer::getScreenWidth()))) +// clang-format on #define FONT_PATH_LIGHT ":/fonts/Akrobat-Regular.ttf" #define FONT_PATH_REGULAR ":/fonts/Akrobat-SemiBold.ttf" @@ -47,54 +49,58 @@ enum Alignment { class Font : public IReloadable { public: - static void initLibrary(); - - static std::shared_ptr get(int size, const std::string& path = getDefaultPath()); - virtual ~Font(); + static void initLibrary(); + static std::shared_ptr get(int size, const std::string& path = getDefaultPath()); // Returns the expected size of a string when rendered. Extra spacing is applied to the Y axis. Vector2f sizeText(std::string text, float lineSpacing = 1.5f); + // Returns the portion of a string that fits within the passed argument maxWidth. std::string getTextMaxWidth(std::string text, float maxWidth); - TextCache* buildTextCache( - const std::string& text, - float offsetX, - float offsetY, - unsigned int color, - float lineSpacing = 1.5f, - bool noTopMargin = false); - TextCache* buildTextCache( - const std::string& text, - Vector2f offset, - unsigned int color, - float xLen, - Alignment alignment = ALIGN_LEFT, - float lineSpacing = 1.5f, - bool noTopMargin = false); + + TextCache* buildTextCache(const std::string& text, + float offsetX, + float offsetY, + unsigned int color, + float lineSpacing = 1.5f, + bool noTopMargin = false); + + TextCache* buildTextCache(const std::string& text, + Vector2f offset, + unsigned int color, + float xLen, + Alignment alignment = ALIGN_LEFT, + float lineSpacing = 1.5f, + bool noTopMargin = false); + void renderTextCache(TextCache* cache); // Inserts newlines into text to make it wrap properly. std::string wrapText(std::string text, float xLen); + // Returns the expected size of a string after wrapping is applied. Vector2f sizeWrappedText(std::string text, float xLen, float lineSpacing = 1.5f); - // Returns the position of of the cursor after moving "cursor" characters. - Vector2f getWrappedTextCursorOffset(std::string text, float xLen, size_t cursor, - float lineSpacing = 1.5f); + + // Returns the position of the cursor after moving a "cursor" amount of characters. + Vector2f getWrappedTextCursorOffset(std::string text, + float xLen, + size_t cursor, + float lineSpacing = 1.5f); float getHeight(float lineSpacing = 1.5f) const; float getLetterHeight(); - void unload(std::shared_ptr& rm) override; - void reload(std::shared_ptr& rm) override; + void reload(std::shared_ptr& rm) override { rebuildTextures(); } + void unload(std::shared_ptr& rm) override { unloadTextures(); } - int getSize() const; - inline const std::string& getPath() const { return mPath; } - - inline static std::string getDefaultPath() { return FONT_PATH_REGULAR; } + int getSize() const { return mSize; } + const std::string& getPath() const { return mPath; } + static std::string getDefaultPath() { return FONT_PATH_REGULAR; } static std::shared_ptr getFromTheme(const ThemeData::ThemeElement* elem, - unsigned int properties, const std::shared_ptr& orig); + unsigned int properties, + const std::shared_ptr& orig); // Returns an approximation of VRAM used by this font's texture (in bytes). size_t getMemUsage() const; @@ -122,6 +128,7 @@ private: // Initializes the OpenGL texture according to this FontTexture's settings, // updating textureId. void initTexture(); + // Deinitializes the OpenGL texture if any exists, is automatically called // in the destructor. void deinitTexture(); @@ -140,8 +147,9 @@ private: std::vector mTextures; - void getTextureForNewGlyph(const Vector2i& glyphSize, FontTexture*& tex_out, - Vector2i& cursor_out); + void getTextureForNewGlyph(const Vector2i& glyphSize, + FontTexture*& tex_out, + Vector2i& cursor_out); std::map> mFaceCache; FT_Face getFaceForChar(unsigned int id); @@ -166,8 +174,10 @@ private: const int mSize; const std::string mPath; - float getNewlineStartOffset(const std::string& text, const unsigned int& charStart, - const float& xLen, const Alignment& alignment); + float getNewlineStartOffset(const std::string& text, + const unsigned int& charStart, + const float& xLen, + const Alignment& alignment); friend TextCache; }; diff --git a/es-core/src/resources/ResourceManager.cpp b/es-core/src/resources/ResourceManager.cpp index c75ae5787..e61d69ea9 100644 --- a/es-core/src/resources/ResourceManager.cpp +++ b/es-core/src/resources/ResourceManager.cpp @@ -9,23 +9,19 @@ #include "ResourceManager.h" -#include "utils/FileSystemUtil.h" -#include "utils/StringUtil.h" #include "Log.h" #include "Platform.h" #include "Scripting.h" +#include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" #include auto array_deleter = [](unsigned char* p) { delete[] p; }; -auto nop_deleter = [](unsigned char* /*p*/) { }; +auto nop_deleter = [](unsigned char* /*p*/) {}; std::shared_ptr ResourceManager::sInstance = nullptr; -ResourceManager::ResourceManager() -{ -} - std::shared_ptr& ResourceManager::getInstance() { if (!sInstance) @@ -34,8 +30,7 @@ std::shared_ptr& ResourceManager::getInstance() return sInstance; } -std::string ResourceManager::getResourcePath(const std::string& path, - bool terminateOnFailure) const +std::string ResourceManager::getResourcePath(const std::string& path, bool terminateOnFailure) const { // Check if this is a resource file. if ((path[0] == ':') && (path[1] == '/')) { @@ -47,17 +42,17 @@ std::string ResourceManager::getResourcePath(const std::string& path, if (Utils::FileSystem::exists(testHome)) return testHome; +#if defined(__APPLE__) // For macOS, check in the ../Resources directory relative to the executable directory. - #if defined(__APPLE__) std::string applePackagePath = - Utils::FileSystem::getExePath() + "/../Resources/resources/" + &path[2]; + Utils::FileSystem::getExePath() + "/../Resources/resources/" + &path[2]; if (Utils::FileSystem::exists(applePackagePath)) { return applePackagePath; } +#elif defined(__unix__) // Check under the data installation directory (Unix only). - #elif defined(__unix__) std::string testDataPath; testDataPath = Utils::FileSystem::getProgramDataPath() + "/resources/" + &path[2]; @@ -65,7 +60,7 @@ std::string ResourceManager::getResourcePath(const std::string& path, if (Utils::FileSystem::exists(testDataPath)) { return testDataPath; } - #endif +#endif // Check under the ES executable directory. std::string testExePath = Utils::FileSystem::getExePath() + "/resources/" + &path[2]; @@ -81,11 +76,11 @@ std::string ResourceManager::getResourcePath(const std::string& path, LOG(LogError) << "Program resource missing: " << path; LOG(LogError) << "Tried to find the resource in the following locations:"; LOG(LogError) << testHome; - #if defined(__APPLE__) +#if defined(__APPLE__) LOG(LogError) << applePackagePath; - #elif defined(__unix__) +#elif defined(__unix__) LOG(LogError) << testDataPath; - #endif +#endif LOG(LogError) << testExePath; LOG(LogError) << "Has EmulationStation been properly installed?"; Scripting::fireEvent("quit"); @@ -112,17 +107,17 @@ const ResourceData ResourceManager::getFileData(const std::string& path) const } // If the file doesn't exist, return an "empty" ResourceData. - ResourceData data = {nullptr, 0}; + ResourceData data = { nullptr, 0 }; return data; } ResourceData ResourceManager::loadFile(const std::string& path) const { - #if defined(_WIN64) +#if defined(_WIN64) std::ifstream stream(Utils::String::stringToWideString(path).c_str(), std::ios::binary); - #else +#else std::ifstream stream(path, std::ios::binary); - #endif +#endif stream.seekg(0, stream.end); size_t size = static_cast(stream.tellg()); @@ -133,7 +128,7 @@ ResourceData ResourceManager::loadFile(const std::string& path) const stream.read(reinterpret_cast(data.get()), size); stream.close(); - ResourceData ret = {data, size}; + ResourceData ret = { data, size }; return ret; } diff --git a/es-core/src/resources/ResourceManager.h b/es-core/src/resources/ResourceManager.h index 5ddfcc310..e668bff99 100644 --- a/es-core/src/resources/ResourceManager.h +++ b/es-core/src/resources/ResourceManager.h @@ -47,7 +47,7 @@ public: bool fileExists(const std::string& path) const; private: - ResourceManager(); + ResourceManager() {} static std::shared_ptr sInstance; std::list> mReloadables; diff --git a/es-core/src/resources/TextureData.cpp b/es-core/src/resources/TextureData.cpp index c43298804..58b114c4e 100644 --- a/es-core/src/resources/TextureData.cpp +++ b/es-core/src/resources/TextureData.cpp @@ -11,11 +11,11 @@ #include "resources/TextureData.h" +#include "ImageIO.h" +#include "Log.h" #include "math/Misc.h" #include "renderers/Renderer.h" #include "resources/ResourceManager.h" -#include "ImageIO.h" -#include "Log.h" #include #include @@ -23,17 +23,16 @@ #define DPI 96 -TextureData::TextureData( - bool tile) - : mTile(tile), - mTextureID(0), - mDataRGBA({}), - mScaleDuringLoad(1.0f), - mScalable(false), - mWidth(0), - mHeight(0), - mSourceWidth(0.0f), - mSourceHeight(0.0f) +TextureData::TextureData(bool tile) + : mTile(tile) + , mTextureID(0) + , mDataRGBA({}) + , mScaleDuringLoad(1.0f) + , mScalable(false) + , mWidth(0) + , mHeight(0) + , mSourceWidth(0.0f) + , mSourceHeight(0.0f) { } @@ -78,13 +77,13 @@ bool TextureData::initSVGFromMemory(const std::string& fileData) if (mWidth == 0) { // Auto scale width to keep aspect ratio. - mWidth = static_cast(std::round((static_cast(mHeight) / - svgImage->height) * svgImage->width)); + mWidth = static_cast( + std::round((static_cast(mHeight) / svgImage->height) * svgImage->width)); } else if (mHeight == 0) { // Auto scale height to keep aspect ratio. - mHeight = static_cast(std::round((static_cast(mWidth) / - svgImage->width) * svgImage->height)); + mHeight = static_cast( + std::round((static_cast(mWidth) / svgImage->width) * svgImage->height)); } std::vector tempVector; @@ -93,14 +92,15 @@ bool TextureData::initSVGFromMemory(const std::string& fileData) NSVGrasterizer* rast = nsvgCreateRasterizer(); nsvgRasterize(rast, svgImage, 0, 0, mHeight / svgImage->height, tempVector.data(), - static_cast(mWidth), static_cast(mHeight), static_cast(mWidth) * 4); + static_cast(mWidth), static_cast(mHeight), + static_cast(mWidth) * 4); // This is important in order to avoid memory leaks. nsvgDeleteRasterizer(rast); nsvgDelete(svgImage); - mDataRGBA.insert(mDataRGBA.begin(), - tempVector.data(), tempVector.data() + (mWidth * mHeight * 4)); + mDataRGBA.insert(mDataRGBA.begin(), tempVector.data(), + tempVector.data() + (mWidth * mHeight * 4)); ImageIO::flipPixelsVert(mDataRGBA.data(), mWidth, mHeight); @@ -120,12 +120,13 @@ bool TextureData::initImageFromMemory(const unsigned char* fileData, size_t leng } std::vector imageRGBA = ImageIO::loadFromMemoryRGBA32( - static_cast(fileData), length, width, height); + static_cast(fileData), length, width, height); if (imageRGBA.size() == 0) { - LOG(LogError) << "Couldn't initialize texture from memory, invalid data (" << - (mPath != "" ? "file path: \"" + mPath + "\", " : "") << "data ptr: " << - reinterpret_cast(fileData) << ", reported size: " << length << ")"; + LOG(LogError) << "Couldn't initialize texture from memory, invalid data (" + << (mPath != "" ? "file path: \"" + mPath + "\", " : "") + << "data ptr: " << reinterpret_cast(fileData) + << ", reported size: " << length << ")"; return false; } @@ -167,8 +168,8 @@ bool TextureData::load() retval = initSVGFromMemory(dataString); } else { - retval = initImageFromMemory(static_cast(data.ptr.get()), - data.length); + retval = + initImageFromMemory(static_cast(data.ptr.get()), data.length); } } return retval; @@ -196,9 +197,9 @@ bool TextureData::uploadAndBind() return false; // Upload texture. - mTextureID = Renderer::createTexture(Renderer::Texture::RGBA, true, mTile, - static_cast(mWidth), static_cast(mHeight), - mDataRGBA.data()); + mTextureID = Renderer::createTexture( + Renderer::Texture::RGBA, true, mTile, static_cast(mWidth), + static_cast(mHeight), mDataRGBA.data()); } return true; } diff --git a/es-core/src/resources/TextureData.h b/es-core/src/resources/TextureData.h index 8a65c0b18..d038f0123 100644 --- a/es-core/src/resources/TextureData.h +++ b/es-core/src/resources/TextureData.h @@ -24,7 +24,7 @@ public: // These functions populate mDataRGBA but do not upload the texture to VRAM. - //!!!! Needs to be canonical path. Caller should check for duplicates before calling this. + // Needs to be canonical path. Caller should check for duplicates before calling this. void initFromPath(const std::string& path); bool initSVGFromMemory(const std::string& fileData); bool initImageFromMemory(const unsigned char* fileData, size_t length); diff --git a/es-core/src/resources/TextureDataManager.cpp b/es-core/src/resources/TextureDataManager.cpp index 3f4e79fcf..b4a64de2a 100644 --- a/es-core/src/resources/TextureDataManager.cpp +++ b/es-core/src/resources/TextureDataManager.cpp @@ -8,20 +8,20 @@ #include "resources/TextureDataManager.h" -#include "resources/TextureData.h" -#include "resources/TextureResource.h" #include "Log.h" #include "Settings.h" +#include "resources/TextureData.h" +#include "resources/TextureResource.h" TextureDataManager::TextureDataManager() { unsigned char data[5 * 5 * 4]; mBlank = std::shared_ptr(new TextureData(false)); for (int i = 0; i < (5 * 5); i++) { - data[i*4] = (i % 2) * 255; - data[i*4+1] = (i % 2) * 255; - data[i*4+2] = (i % 2) * 255; - data[i*4+3] = 0; + data[i * 4] = (i % 2) * 255; + data[i * 4 + 1] = (i % 2) * 255; + data[i * 4 + 2] = (i % 2) * 255; + data[i * 4 + 3] = 0; } mBlank->initFromRGBA(data, 5, 5); mLoader = new TextureLoader; @@ -29,6 +29,7 @@ TextureDataManager::TextureDataManager() TextureDataManager::~TextureDataManager() { + // Delete TextureLoader object when destroyed. delete mLoader; } @@ -103,6 +104,7 @@ size_t TextureDataManager::getCommittedSize() size_t TextureDataManager::getQueueSize() { + // Return queue size. return mLoader->getQueueSize(); } @@ -116,14 +118,14 @@ void TextureDataManager::load(std::shared_ptr tex, bool block) size_t settingVRAM = static_cast(Settings::getInstance()->getInt("MaxVRAM")); if (settingVRAM < 80) { - LOG(LogWarning) << "MaxVRAM is too low at " << settingVRAM << - " MiB, setting it to the minimum allowed value of 80 MiB"; + LOG(LogWarning) << "MaxVRAM is too low at " << settingVRAM + << " MiB, setting it to the minimum allowed value of 80 MiB"; Settings::getInstance()->setInt("MaxVRAM", 80); settingVRAM = 80; } else if (settingVRAM > 1024) { - LOG(LogWarning) << "MaxVRAM is too high at " << settingVRAM << - " MiB, setting it to the maximum allowed value of 1024 MiB"; + LOG(LogWarning) << "MaxVRAM is too high at " << settingVRAM + << " MiB, setting it to the maximum allowed value of 1024 MiB"; Settings::getInstance()->setInt("MaxVRAM", 1024); settingVRAM = 1024; } @@ -133,7 +135,6 @@ void TextureDataManager::load(std::shared_ptr tex, bool block) for (auto it = mTextures.crbegin(); it != mTextures.crend(); it++) { if (size < max_texture) break; - //size -= (*it)->getVRAMUsage(); (*it)->releaseVRAM(); (*it)->releaseRAM(); // It may be already in the loader queue. In this case it wouldn't have been using @@ -141,13 +142,15 @@ void TextureDataManager::load(std::shared_ptr tex, bool block) mLoader->remove(*it); size = TextureResource::getTotalMemUsage(); } + if (!block) mLoader->load(tex); else tex->load(); } -TextureLoader::TextureLoader() : mExit(false) +TextureLoader::TextureLoader() + : mExit(false) { mThread = std::make_unique(&TextureLoader::threadProc, this); } diff --git a/es-core/src/resources/TextureDataManager.h b/es-core/src/resources/TextureDataManager.h index 84890cc41..1989e1915 100644 --- a/es-core/src/resources/TextureDataManager.h +++ b/es-core/src/resources/TextureDataManager.h @@ -35,8 +35,8 @@ private: void threadProc(); std::list> mTextureDataQ; - std::map>::const_iterator> mTextureDataLookup; + std::map>::const_iterator> + mTextureDataLookup; std::unique_ptr mThread; std::mutex mMutex; @@ -75,19 +75,19 @@ public: bool bind(const TextureResource* key); // Get the total size of all textures managed by this object, loaded and unloaded in bytes. - size_t getTotalSize(); + size_t getTotalSize(); // Get the total size of all committed textures (in VRAM) in bytes. - size_t getCommittedSize(); + size_t getCommittedSize(); // Get the total size of all load-pending textures in the queue - these will // be committed to VRAM as the queue is processed. - size_t getQueueSize(); + size_t getQueueSize(); // Load a texture, freeing resources as necessary to make space. void load(std::shared_ptr tex, bool block = false); private: std::list> mTextures; - std::map>::const_iterator> mTextureLookup; + std::map>::const_iterator> + mTextureLookup; std::shared_ptr mBlank; TextureLoader* mLoader; }; diff --git a/es-core/src/resources/TextureResource.cpp b/es-core/src/resources/TextureResource.cpp index 5aef84b3f..268a98e52 100644 --- a/es-core/src/resources/TextureResource.cpp +++ b/es-core/src/resources/TextureResource.cpp @@ -8,21 +8,20 @@ #include "resources/TextureResource.h" -#include "utils/FileSystemUtil.h" #include "resources/TextureData.h" +#include "utils/FileSystemUtil.h" TextureDataManager TextureResource::sTextureDataManager; -std::map< TextureResource::TextureKeyType, - std::weak_ptr> TextureResource::sTextureMap; +std::map> + TextureResource::sTextureMap; std::set TextureResource::sAllTextures; -TextureResource::TextureResource( - const std::string& path, - bool tile, - bool dynamic, - float scaleDuringLoad) - : mTextureData(nullptr), - mForceLoad(false) +TextureResource::TextureResource(const std::string& path, + bool tile, + bool dynamic, + float scaleDuringLoad) + : mTextureData(nullptr) + , mForceLoad(false) { // Create a texture data object for this texture. if (!path.empty()) { @@ -88,8 +87,8 @@ void TextureResource::initFromMemory(const char* data, size_t length) mTextureData->releaseRAM(); mTextureData->initImageFromMemory(reinterpret_cast(data), length); // Get the size from the texture data. - mSize = Vector2i(static_cast(mTextureData->width()), - static_cast(mTextureData->height())); + mSize = + Vector2i(static_cast(mTextureData->width()), static_cast(mTextureData->height())); mSourceSize = Vector2f(mTextureData->sourceWidth(), mTextureData->sourceHeight()); } @@ -125,11 +124,6 @@ std::string TextureResource::getTextureFilePath() return ""; } -const Vector2i TextureResource::getSize() const -{ - return mSize; -} - bool TextureResource::isTiled() const { if (mTextureData != nullptr) @@ -150,11 +144,7 @@ bool TextureResource::bind() } std::shared_ptr TextureResource::get( - const std::string& path, - bool tile, - bool forceLoad, - bool dynamic, - float scaleDuringLoad) + const std::string& path, bool tile, bool forceLoad, bool dynamic, float scaleDuringLoad) { std::shared_ptr& rm = ResourceManager::getInstance(); @@ -177,7 +167,7 @@ std::shared_ptr TextureResource::get( // Need to create it. std::shared_ptr tex; tex = std::shared_ptr( - new TextureResource(key.first, tile, dynamic, scaleDuringLoad)); + new TextureResource(key.first, tile, dynamic, scaleDuringLoad)); std::shared_ptr data = sTextureDataManager.get(tex.get()); // Is it an SVG? @@ -213,16 +203,6 @@ void TextureResource::rasterizeAt(size_t width, size_t height) data->load(); } -Vector2f TextureResource::getSourceImageSize() const -{ - return mSourceSize; -} - -bool TextureResource::isInitialized() const -{ - return true; -} - size_t TextureResource::getTotalMemUsage() { size_t total = 0; diff --git a/es-core/src/resources/TextureResource.h b/es-core/src/resources/TextureResource.h index 380be1787..ef0eae958 100644 --- a/es-core/src/resources/TextureResource.h +++ b/es-core/src/resources/TextureResource.h @@ -26,12 +26,11 @@ class TextureData; class TextureResource : public IReloadable { public: - static std::shared_ptr get( - const std::string& path, - bool tile = false, - bool forceLoad = false, - bool dynamic = true, - float scaleDuringLoad = 1.0f); + static std::shared_ptr get(const std::string& path, + bool tile = false, + bool forceLoad = false, + bool dynamic = true, + float scaleDuringLoad = 1.0f); void initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height); virtual void initFromMemory(const char* data, size_t length); static void manualUnload(std::string path, bool tile); @@ -46,14 +45,14 @@ public: // situations. An alternative is to set a scaling factor directly when loading the texture // using get(), by using the scaleDuringLoad parameter (which also works for raster graphics). void rasterizeAt(size_t width, size_t height); - Vector2f getSourceImageSize() const; + Vector2f getSourceImageSize() const { return mSourceSize; } virtual ~TextureResource(); - bool isInitialized() const; + bool isInitialized() const { return true; } bool isTiled() const; - const Vector2i getSize() const; + const Vector2i getSize() const { return mSize; } bool bind(); // Returns an approximation of total VRAM used by textures (in bytes). diff --git a/es-core/src/utils/CImgUtil.cpp b/es-core/src/utils/CImgUtil.cpp index e873a2077..4b23b9027 100644 --- a/es-core/src/utils/CImgUtil.cpp +++ b/es-core/src/utils/CImgUtil.cpp @@ -13,7 +13,7 @@ namespace Utils namespace CImg { void convertRGBAToCImg(std::vector imageRGBA, - cimg_library::CImg& image) + cimg_library::CImg& image) { // CImg does not interleave the pixels as in RGBARGBARGBA so a conversion is required. int counter = 0; @@ -29,20 +29,20 @@ namespace Utils } void convertCImgToRGBA(cimg_library::CImg image, - std::vector& imageRGBA) + std::vector& imageRGBA) { for (int r = image.height() - 1; r >= 0; r--) { for (int c = 0; c < image.width(); c++) { - imageRGBA.push_back((unsigned char)image(c,r,0,2)); - imageRGBA.push_back((unsigned char)image(c,r,0,1)); - imageRGBA.push_back((unsigned char)image(c,r,0,0)); - imageRGBA.push_back((unsigned char)image(c,r,0,3)); + imageRGBA.push_back((unsigned char)image(c, r, 0, 2)); + imageRGBA.push_back((unsigned char)image(c, r, 0, 1)); + imageRGBA.push_back((unsigned char)image(c, r, 0, 0)); + imageRGBA.push_back((unsigned char)image(c, r, 0, 3)); } } } void getTransparentPaddingCoords(cimg_library::CImg& image, - int (&imageCoords)[4]) + int (&imageCoords)[4]) { // Check that the image actually has an alpha channel. if (image.spectrum() != 4) @@ -147,8 +147,7 @@ namespace Utils } if (rowCounterTop > 0) - image.crop(0, 0, 0, 3, image.width() - 1, image.height() - 1 - - rowCounterTop, 0, 0); + image.crop(0, 0, 0, 3, image.width() - 1, image.height() - 1 - rowCounterTop, 0, 0); if (rowCounterBottom > 0) image.crop(0, rowCounterBottom, 0, 3, image.width() - 1, image.height() - 1, 0, 0); @@ -157,8 +156,8 @@ namespace Utils image.crop(columnCounterLeft, 0, 0, 3, image.width() - 1, image.height() - 1, 0, 0); if (columnCounterRight > 0) - image.crop(0, 0, 0, 3, image.width() - columnCounterRight - 1, - image.height() - 1, 0, 0); + image.crop(0, 0, 0, 3, image.width() - columnCounterRight - 1, image.height() - 1, + 0, 0); } void cropLetterboxes(cimg_library::CImg& image) @@ -190,8 +189,8 @@ namespace Utils } if (rowCounterUpper > 0) - image.crop(0, 0, 0, 3, image.width() - 1, image.height() - 1 - - rowCounterUpper, 0, 0); + image.crop(0, 0, 0, 3, image.width() - 1, image.height() - 1 - rowCounterUpper, 0, + 0); if (rowCounterLower > 0) image.crop(0, rowCounterLower, 0, 3, image.width() - 1, image.height() - 1, 0, 0); @@ -229,20 +228,23 @@ namespace Utils image.crop(columnCounterLeft, 0, 0, 3, image.width() - 1, image.height() - 1, 0, 0); if (columnCounterRight > 0) - image.crop(0, 0, 0, 3, image.width() - columnCounterRight - 1, - image.height() - 1, 0, 0); + image.crop(0, 0, 0, 3, image.width() - columnCounterRight - 1, image.height() - 1, + 0, 0); } - void addDropShadow(cimg_library::CImg& image, unsigned int shadowDistance, - float transparency, unsigned int iterations) + void addDropShadow(cimg_library::CImg& image, + unsigned int shadowDistance, + float transparency, + unsigned int iterations) { // Check that the image actually has an alpha channel. if (image.spectrum() != 4) return; - // Make the shadow image larger than the source image to leave space for the drop shadow. - cimg_library::CImg shadowImage(image.width() + shadowDistance * 3, - image.height() + shadowDistance * 3, 1, 4, 0); + // Make the shadow image larger than the source image to leave space for the + // drop shadow. + cimg_library::CImg shadowImage( + image.width() + shadowDistance * 3, image.height() + shadowDistance * 3, 1, 4, 0); // Create a mask image. cimg_library::CImg maskImage(image.width(), image.height(), 1, 4, 0); @@ -256,20 +258,20 @@ namespace Utils // Lower the transparency and apply the blur. shadowImage.get_shared_channel(3) /= transparency; shadowImage.blur_box(static_cast(shadowDistance), - static_cast(shadowDistance), 1, true, iterations); + static_cast(shadowDistance), 1, true, iterations); // Add the mask to the alpha channel of the shadow image. shadowImage.get_shared_channel(3).draw_image(0, 0, maskImage.get_shared_channels(0, 0), - maskImage.get_shared_channel(3), 1, 255); + maskImage.get_shared_channel(3), 1, 255); // Draw the source image on top of the shadow image. shadowImage.draw_image(0, 0, image.get_shared_channels(0, 2), - image.get_shared_channel(3), 1, 255); + image.get_shared_channel(3), 1, 255); // Remove the any unused space that we added to leave room for the shadow. removeTransparentPadding(shadowImage); image = shadowImage; } - } // CImg:: + } // namespace CImg -} // Utils:: +} // namespace Utils diff --git a/es-core/src/utils/CImgUtil.h b/es-core/src/utils/CImgUtil.h index e611911b4..66e177d1d 100644 --- a/es-core/src/utils/CImgUtil.h +++ b/es-core/src/utils/CImgUtil.h @@ -20,17 +20,20 @@ namespace Utils namespace CImg { void convertRGBAToCImg(std::vector imageRGBA, - cimg_library::CImg& image); + cimg_library::CImg& image); void convertCImgToRGBA(cimg_library::CImg image, - std::vector& imageRGBA); + std::vector& imageRGBA); void getTransparentPaddingCoords(cimg_library::CImg& image, - int (&imageCoords)[4]); + int (&imageCoords)[4]); void removeTransparentPadding(cimg_library::CImg& image); void cropLetterboxes(cimg_library::CImg& image); void cropPillarboxes(cimg_library::CImg& image); - void addDropShadow(cimg_library::CImg& image, unsigned int shadowDistance, - float transparency, unsigned int iterations); - } -} + void addDropShadow(cimg_library::CImg& image, + unsigned int shadowDistance, + float transparency, + unsigned int iterations); + } // namespace CImg + +} // namespace Utils #endif // ES_CORE_UTILS_CIMG_UTIL_H diff --git a/es-core/src/utils/FileSystemUtil.cpp b/es-core/src/utils/FileSystemUtil.cpp index 91b3ad710..1e6075d62 100644 --- a/es-core/src/utils/FileSystemUtil.cpp +++ b/es-core/src/utils/FileSystemUtil.cpp @@ -16,20 +16,20 @@ #include "utils/FileSystemUtil.h" -#include "utils/StringUtil.h" #include "Log.h" +#include "utils/StringUtil.h" -#include #include #include +#include #if defined(_WIN64) -#include #include +#include #if defined(_MSC_VER) // MSVC compiler. #define stat64 _stat64 -#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) -#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) +#define S_ISREG(x) (((x)&S_IFMT) == S_IFREG) +#define S_ISDIR(x) (((x)&S_IFMT) == S_IFDIR) #endif #else #include @@ -69,7 +69,7 @@ namespace Utils // Only parse the directory, if it's a directory. if (isDirectory(genericPath)) { - #if defined(_WIN64) +#if defined(_WIN64) WIN32_FIND_DATAW findData; std::wstring wildcard = Utils::String::stringToWideString(genericPath) + L"/*"; HANDLE hFind = FindFirstFileW(wildcard.c_str(), &findData); @@ -88,11 +88,10 @@ namespace Utils contentList.merge(getDirContent(fullName, true)); } } - } - while (FindNextFileW(hFind, &findData)); + } while (FindNextFileW(hFind, &findData)); FindClose(hFind); } - #else +#else DIR* dir = opendir(genericPath.c_str()); if (dir != nullptr) { @@ -114,7 +113,7 @@ namespace Utils } closedir(dir); } - #endif +#endif } contentList.sort(); return contentList; @@ -142,6 +141,7 @@ namespace Utils void setHomePath(const std::string& path) { + // Set home path. homePath = getGenericPath(path); } @@ -151,33 +151,33 @@ namespace Utils if (homePath.length()) return homePath; - #if defined(_WIN64) +#if defined(_WIN64) // On Windows we need to check HOMEDRIVE and HOMEPATH. if (!homePath.length()) { std::wstring envHomeDrive; std::wstring envHomePath; - #if defined(_MSC_VER) // MSVC compiler. +#if defined(_MSC_VER) // MSVC compiler. wchar_t* buffer; if (!_wdupenv_s(&buffer, nullptr, L"HOMEDRIVE")) envHomeDrive = buffer; if (!_wdupenv_s(&buffer, nullptr, L"HOMEPATH")) envHomePath = buffer; - #else +#else envHomeDrive = _wgetenv(L"HOMEDRIVE"); envHomePath = _wgetenv(L"HOMEPATH"); - #endif +#endif if (envHomeDrive.length() && envHomePath.length()) homePath = getGenericPath(Utils::String::wideStringToString(envHomeDrive) + - "/" + Utils::String::wideStringToString(envHomePath)); + "/" + Utils::String::wideStringToString(envHomePath)); } - #else +#else if (!homePath.length()) { std::string envHome = getenv("HOME"); if (envHome.length()) homePath = getGenericPath(envHome); } - #endif +#endif // No homepath found, fall back to current working directory. if (!homePath.length()) @@ -189,24 +189,26 @@ namespace Utils std::string getCWDPath() { // Return current working directory. - #if defined(_WIN64) + +#if defined(_WIN64) wchar_t tempWide[512]; return (_wgetcwd(tempWide, 512) ? - getGenericPath(Utils::String::wideStringToString(tempWide)) : ""); - #else + getGenericPath(Utils::String::wideStringToString(tempWide)) : + ""); +#else char temp[512]; return (getcwd(temp, 512) ? getGenericPath(temp) : ""); - #endif +#endif } std::string getPathToBinary(const std::string& executable) { - #if defined(_WIN64) +#if defined(_WIN64) return ""; - #else +#else std::string pathVariable = std::string(getenv("PATH")); std::vector pathList = - Utils::String::delimitedStringToVector(pathVariable, ":"); + Utils::String::delimitedStringToVector(pathVariable, ":"); std::string pathTest; @@ -216,21 +218,21 @@ namespace Utils return it->c_str(); } return ""; - #endif +#endif } void setExePath(const std::string& path) { constexpr int pathMax = 32767; - #if defined(_WIN64) +#if defined(_WIN64) std::wstring result(pathMax, 0); if (GetModuleFileNameW(nullptr, &result[0], pathMax) != 0) exePath = Utils::String::wideStringToString(result); - #else +#else std::string result(pathMax, 0); if (readlink("/proc/self/exe", &result[0], pathMax) != -1) exePath = result; - #endif +#endif exePath = getCanonicalPath(exePath); // Fallback to argv[0] if everything else fails. @@ -242,27 +244,28 @@ namespace Utils std::string getExePath() { + // Return executable path. return exePath; } std::string getProgramDataPath() { - #if defined(__unix__) +#if defined(__unix__) return installPrefix + "/share/emulationstation"; - #else +#else return ""; - #endif +#endif } std::string getPreferredPath(const std::string& path) { std::string preferredPath = path; size_t offset = std::string::npos; - #if defined(_WIN64) +#if defined(_WIN64) // Convert '/' to '\\' while ((offset = preferredPath.find('/')) != std::string::npos) preferredPath.replace(offset, 1, "\\"); - #endif +#endif return preferredPath; } @@ -277,15 +280,15 @@ namespace Utils // Convert '\\' to '/' while ((offset = genericPath.find('\\')) != std::string::npos) - genericPath.replace(offset, 1 ,"/"); + genericPath.replace(offset, 1, "/"); // Remove double '/' while ((offset = genericPath.find("//")) != std::string::npos) genericPath.erase(offset, 1); // Remove trailing '/' when the path is more than a simple '/' - while (genericPath.length() > 1 && ((offset = genericPath.find_last_of('/')) == - (genericPath.length() - 1))) + while (genericPath.length() > 1 && + ((offset = genericPath.find_last_of('/')) == (genericPath.length() - 1))) genericPath.erase(offset, 1); return genericPath; @@ -295,10 +298,10 @@ namespace Utils { std::string escapedPath = getGenericPath(path); - #if defined(_WIN64) +#if defined(_WIN64) // Windows escapes stuff by just putting everything in quotes. return '"' + getPreferredPath(escapedPath) + '"'; - #else +#else // Insert a backslash before most characters that would mess up a bash path. const char* invalidChars = "\\ '\"!$^&*(){}[]?;<>"; const char* invalidChar = invalidChars; @@ -318,7 +321,7 @@ namespace Utils invalidChar++; } return escapedPath; - #endif +#endif } std::string getCanonicalPath(const std::string& path) @@ -337,8 +340,8 @@ namespace Utils canonicalPath.clear(); scan = false; - for (stringList::const_iterator it = pathList.cbegin(); - it != pathList.cend(); it++) { + for (stringList::const_iterator it = pathList.cbegin(); it != pathList.cend(); + it++) { // Ignore empty. if ((*it).empty()) continue; @@ -353,13 +356,13 @@ namespace Utils continue; } - #if defined(_WIN64) +#if defined(_WIN64) // Append folder to path. canonicalPath += (canonicalPath.size() == 0) ? (*it) : ("/" + (*it)); - #else +#else // Append folder to path. canonicalPath += ("/" + (*it)); - #endif +#endif if (isSymlink(canonicalPath)) { std::string resolved = resolveSymlink(canonicalPath); @@ -389,8 +392,7 @@ namespace Utils std::string baseVar = isAbsolute(base) ? getGenericPath(base) : getAbsolutePath(base); return isAbsolute(absolutePath) ? absolutePath : - getGenericPath(baseVar + "/" + absolutePath); - + getGenericPath(baseVar + "/" + absolutePath); } std::string getParent(const std::string& path) @@ -414,7 +416,7 @@ namespace Utils // Find last '/' and return the filename. if ((offset = genericPath.find_last_of('/')) != std::string::npos) return ((genericPath[offset + 1] == 0) ? "." : - std::string(genericPath, offset + 1)); + std::string(genericPath, offset + 1)); // No '/' found, entire path is a filename. return genericPath; @@ -466,11 +468,12 @@ namespace Utils } std::string resolveRelativePath(const std::string& path, - const std::string& relativeTo, const bool allowHome) + const std::string& relativeTo, + const bool allowHome) { std::string genericPath = getGenericPath(path); - std::string relativeToVar = isDirectory(relativeTo) ? - getGenericPath(relativeTo) : getParent(relativeTo); + std::string relativeToVar = + isDirectory(relativeTo) ? getGenericPath(relativeTo) : getParent(relativeTo); // Nothing to resolve. if (!genericPath.length()) @@ -489,7 +492,8 @@ namespace Utils } std::string createRelativePath(const std::string& path, - const std::string& relativeTo, const bool allowHome) + const std::string& relativeTo, + const bool allowHome) { bool contains = false; std::string relativePath = removeCommonPath(path, relativeTo, contains); @@ -507,11 +511,12 @@ namespace Utils } std::string removeCommonPath(const std::string& path, - const std::string& commonArg, bool& contains) + const std::string& commonArg, + bool& contains) { std::string genericPath = getGenericPath(path); - std::string common = isDirectory(commonArg) ? - getGenericPath(commonArg) : getParent(commonArg); + std::string common = + isDirectory(commonArg) ? getGenericPath(commonArg) : getParent(commonArg); if (genericPath.find(common) == 0) { contains = true; @@ -527,37 +532,39 @@ namespace Utils std::string genericPath = getGenericPath(path); std::string resolved; - #if defined(_WIN64) +#if defined(_WIN64) std::wstring resolvedW; HANDLE hFile = CreateFileW(Utils::String::stringToWideString(genericPath).c_str(), - FILE_READ_ATTRIBUTES, FILE_SHARE_READ, 0, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + FILE_READ_ATTRIBUTES, FILE_SHARE_READ, 0, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, 0); if (hFile != INVALID_HANDLE_VALUE) { - resolvedW.resize(GetFinalPathNameByHandleW(hFile, nullptr, 0, - FILE_NAME_NORMALIZED) + 1); - if (GetFinalPathNameByHandleW(hFile, const_cast(resolvedW.data()), - static_cast(resolvedW.size()), FILE_NAME_NORMALIZED) > 0) { + resolvedW.resize( + GetFinalPathNameByHandleW(hFile, nullptr, 0, FILE_NAME_NORMALIZED) + 1); + if (GetFinalPathNameByHandleW(hFile, const_cast(resolvedW.data()), + static_cast(resolvedW.size()), + FILE_NAME_NORMALIZED) > 0) { resolvedW.resize(resolvedW.size() - 1); resolved = getGenericPath(Utils::String::wideStringToString(resolvedW)); } CloseHandle(hFile); } - #else +#else struct stat info; // Check if lstat succeeded. if (lstat(genericPath.c_str(), &info) == 0) { resolved.resize(info.st_size); if (readlink(genericPath.c_str(), const_cast(resolved.data()), - resolved.size()) > 0) + resolved.size()) > 0) resolved = getGenericPath(resolved); } - #endif +#endif return resolved; } bool copyFile(const std::string& sourcePath, - const std::string& destinationPath, bool overwrite) + const std::string& destinationPath, + bool overwrite) { if (!exists(sourcePath)) { LOG(LogError) << "Can't copy file, source file does not exist:"; @@ -573,34 +580,34 @@ namespace Utils if (!overwrite && exists(destinationPath)) { LOG(LogError) << "Destination file exists and the overwrite flag " - "has not been set"; + "has not been set"; return true; } - #if defined(_WIN64) - std::ifstream sourceFile( - Utils::String::stringToWideString(sourcePath).c_str(), std::ios::binary); - #else +#if defined(_WIN64) + std::ifstream sourceFile(Utils::String::stringToWideString(sourcePath).c_str(), + std::ios::binary); +#else std::ifstream sourceFile(sourcePath, std::ios::binary); - #endif +#endif if (sourceFile.fail()) { - LOG(LogError) << "Couldn't read from source file \"" << sourcePath << - "\", permission problems?"; + LOG(LogError) << "Couldn't read from source file \"" << sourcePath + << "\", permission problems?"; sourceFile.close(); return true; } - #if defined(_WIN64) - std::ofstream targetFile( - Utils::String::stringToWideString(destinationPath).c_str(), std::ios::binary); - #else +#if defined(_WIN64) + std::ofstream targetFile(Utils::String::stringToWideString(destinationPath).c_str(), + std::ios::binary); +#else std::ofstream targetFile(destinationPath, std::ios::binary); - #endif +#endif if (targetFile.fail()) { - LOG(LogError) << "Couldn't write to target file \"" << destinationPath << - "\", permission problems?"; + LOG(LogError) << "Couldn't write to target file \"" << destinationPath + << "\", permission problems?"; targetFile.close(); return true; } @@ -614,7 +621,8 @@ namespace Utils } bool renameFile(const std::string& sourcePath, - const std::string& destinationPath, bool overwrite) + const std::string& destinationPath, + bool overwrite) { // Don't print any error message for a missing source file as Log will use this // function when initializing the logging. It would always generate an error in @@ -630,17 +638,16 @@ namespace Utils } if (!overwrite && exists(destinationPath)) { - LOG(LogError) << "Destination file exists and the overwrite flag " - "has not been set"; + LOG(LogError) << "Destination file exists and the overwrite flag has not been set"; return true; } - #if defined(_WIN64) +#if defined(_WIN64) _wrename(Utils::String::stringToWideString(sourcePath).c_str(), - Utils::String::stringToWideString(destinationPath).c_str()); - #else + Utils::String::stringToWideString(destinationPath).c_str()); +#else std::rename(sourcePath.c_str(), destinationPath.c_str()); - #endif +#endif return false; } @@ -653,7 +660,7 @@ namespace Utils if (!exists(genericPath)) return true; - #if defined(_WIN64) +#if defined(_WIN64) if (_wunlink(Utils::String::stringToWideString(genericPath).c_str()) != 0) { LOG(LogError) << "Couldn't delete file, permission problems?"; LOG(LogError) << genericPath; @@ -662,7 +669,7 @@ namespace Utils else { return false; } - #else +#else if (unlink(genericPath.c_str()) != 0) { LOG(LogError) << "Couldn't delete file, permission problems?"; LOG(LogError) << genericPath; @@ -672,7 +679,7 @@ namespace Utils return false; } return (unlink(genericPath.c_str()) == 0); - #endif +#endif } bool removeDirectory(const std::string& path) @@ -687,17 +694,17 @@ namespace Utils LOG(LogError) << path; return false; } - #if defined(_WIN64) +#if defined(_WIN64) if (_wrmdir(Utils::String::stringToWideString(path).c_str()) != 0) { - #else +#else if (rmdir(path.c_str()) != 0) { - #endif +#endif LOG(LogError) << "Couldn't delete directory, permission problems?"; LOG(LogError) << path; return false; } return true; - } + } // namespace FileSystem bool createDirectory(const std::string& path) { @@ -706,13 +713,13 @@ namespace Utils if (exists(genericPath)) return true; - #if defined(_WIN64) +#if defined(_WIN64) if (_wmkdir(Utils::String::stringToWideString(genericPath).c_str()) == 0) return true; - #else +#else if (mkdir(genericPath.c_str(), 0755) == 0) return true; - #endif +#endif // Failed to create directory, try to create the parent. std::string parent = getParent(genericPath); @@ -721,33 +728,34 @@ namespace Utils if (parent != genericPath) createDirectory(parent); - // Try to create directory again now that the parent should exist. - #if defined(_WIN64) + // Try to create directory again now that the parent should exist. + +#if defined(_WIN64) return (_wmkdir(Utils::String::stringToWideString(genericPath).c_str()) == 0); - #else +#else return (mkdir(genericPath.c_str(), 0755) == 0); - #endif +#endif } bool exists(const std::string& path) { std::string genericPath = getGenericPath(path); - #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) struct stat info; return (stat(genericPath.c_str(), &info) == 0); - #elif defined(_WIN64) +#elif defined(_WIN64) struct _stat64 info; return (_wstat64(Utils::String::stringToWideString(genericPath).c_str(), &info) == 0); - #else - struct stat64 info; - return (stat64(genericPath.c_str(), &info) == 0); - #endif +#else + struct stat64 info; + return (stat64(genericPath.c_str(), &info) == 0); +#endif } bool driveExists(const std::string& path) { - #if defined(_WIN64) +#if defined(_WIN64) std::string genericPath = getGenericPath(path); // Try to add a dot or a backslash and a dot depending on how the drive // letter was defined by the user. @@ -759,39 +767,39 @@ namespace Utils struct _stat64 info; return (_wstat64(Utils::String::stringToWideString(genericPath).c_str(), &info) == 0); - #else +#else return false; - #endif +#endif } bool isAbsolute(const std::string& path) { std::string genericPath = getGenericPath(path); - #if defined(_WIN64) +#if defined(_WIN64) return ((genericPath.size() > 1) && (genericPath[1] == ':')); - #else +#else return ((genericPath.size() > 0) && (genericPath[0] == '/')); - #endif +#endif } bool isRegularFile(const std::string& path) { std::string genericPath = getGenericPath(path); - #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) struct stat info; if (stat(genericPath.c_str(), &info) != 0) return false; - #elif defined(_WIN64) +#elif defined(_WIN64) struct stat64 info; if (_wstat64(Utils::String::stringToWideString(genericPath).c_str(), &info) != 0) return false; - #else - struct stat64 info; - if (stat64(genericPath.c_str(), &info) != 0) - return false; - #endif +#else + struct stat64 info; + if (stat64(genericPath.c_str(), &info) != 0) + return false; +#endif // Check for S_IFREG attribute. return (S_ISREG(info.st_mode)); @@ -801,19 +809,19 @@ namespace Utils { std::string genericPath = getGenericPath(path); - #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) struct stat info; if (stat(genericPath.c_str(), &info) != 0) return false; - #elif defined(_WIN64) +#elif defined(_WIN64) struct stat64 info; if (_wstat64(Utils::String::stringToWideString(genericPath).c_str(), &info) != 0) return false; - #else - struct stat64 info; - if (stat64(genericPath.c_str(), &info) != 0) - return false; - #endif +#else + struct stat64 info; + if (stat64(genericPath.c_str(), &info) != 0) + return false; +#endif // Check for S_IFDIR attribute. return (S_ISDIR(info.st_mode)); @@ -823,30 +831,30 @@ namespace Utils { std::string genericPath = getGenericPath(path); - #if defined(_WIN64) +#if defined(_WIN64) // Check for symlink attribute. const DWORD Attributes = - GetFileAttributesW(Utils::String::stringToWideString(genericPath).c_str()); + GetFileAttributesW(Utils::String::stringToWideString(genericPath).c_str()); if ((Attributes != INVALID_FILE_ATTRIBUTES) && - (Attributes & FILE_ATTRIBUTE_REPARSE_POINT)) + (Attributes & FILE_ATTRIBUTE_REPARSE_POINT)) return true; - #else +#else - #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) struct stat info; if (lstat(genericPath.c_str(), &info) != 0) return false; - #else +#else struct stat64 info; if (lstat64(genericPath.c_str(), &info) != 0) return false; - #endif +#endif // Check for S_IFLNK attribute. return (S_ISLNK(info.st_mode)); - #endif +#endif // Not a symlink. return false; @@ -856,13 +864,13 @@ namespace Utils { std::string genericPath = getGenericPath(path); - #if defined(_WIN64) +#if defined(_WIN64) // Check for hidden attribute. const DWORD Attributes = - GetFileAttributesW(Utils::String::stringToWideString(genericPath).c_str()); + GetFileAttributesW(Utils::String::stringToWideString(genericPath).c_str()); if ((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_HIDDEN)) return true; - #endif +#endif // Filenames starting with . are hidden in Linux, but // we do this check for windows as well. @@ -872,6 +880,6 @@ namespace Utils return false; } - } // FileSystem:: + } // namespace FileSystem -} // Utils:: +} // namespace Utils diff --git a/es-core/src/utils/FileSystemUtil.h b/es-core/src/utils/FileSystemUtil.h index 95ef4c31b..b8f07e31f 100644 --- a/es-core/src/utils/FileSystemUtil.h +++ b/es-core/src/utils/FileSystemUtil.h @@ -34,23 +34,28 @@ namespace Utils std::string getEscapedPath(const std::string& path); std::string getCanonicalPath(const std::string& path); std::string getAbsolutePath(const std::string& path, - const std::string& base = getCWDPath()); + const std::string& base = getCWDPath()); std::string getParent(const std::string& path); std::string getFileName(const std::string& path); std::string getStem(const std::string& path); std::string getExtension(const std::string& path); std::string expandHomePath(const std::string& path); std::string resolveRelativePath(const std::string& path, - const std::string& relativeTo, const bool allowHome); + const std::string& relativeTo, + const bool allowHome); std::string createRelativePath(const std::string& path, - const std::string& relativeTo, const bool allowHome); + const std::string& relativeTo, + const bool allowHome); std::string removeCommonPath(const std::string& path, - const std::string& commonArg, bool& contains); + const std::string& commonArg, + bool& contains); std::string resolveSymlink(const std::string& path); bool copyFile(const std::string& sourcePath, - const std::string& destinationPath, bool overwrite); + const std::string& destinationPath, + bool overwrite); bool renameFile(const std::string& sourcePath, - const std::string& destinationPath, bool overwrite); + const std::string& destinationPath, + bool overwrite); bool removeFile(const std::string& path); bool removeDirectory(const std::string& path); bool createDirectory(const std::string& path); @@ -61,7 +66,8 @@ namespace Utils bool isDirectory(const std::string& path); bool isSymlink(const std::string& path); bool isHidden(const std::string& path); - } -} + } // namespace FileSystem + +} // namespace Utils #endif // ES_CORE_UTILS_FILE_SYSTEM_UTIL_H diff --git a/es-core/src/utils/StringUtil.cpp b/es-core/src/utils/StringUtil.cpp index eb9f3c2ef..552b23d7b 100644 --- a/es-core/src/utils/StringUtil.cpp +++ b/es-core/src/utils/StringUtil.cpp @@ -19,160 +19,278 @@ namespace Utils { // Unicode case conversion mapping table, based on data from this site: // https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/nls/rbagslowtoupmaptable.htm - static wchar_t unicodeLowercase[] = - { - (wchar_t)0x0061, (wchar_t)0x0062, (wchar_t)0x0063, (wchar_t)0x0064, (wchar_t)0x0065, (wchar_t)0x0066, (wchar_t)0x0067, (wchar_t)0x0068, (wchar_t)0x0069, - (wchar_t)0x006A, (wchar_t)0x006B, (wchar_t)0x006C, (wchar_t)0x006D, (wchar_t)0x006E, (wchar_t)0x006F, (wchar_t)0x0070, (wchar_t)0x0071, (wchar_t)0x0072, - (wchar_t)0x0073, (wchar_t)0x0074, (wchar_t)0x0075, (wchar_t)0x0076, (wchar_t)0x0077, (wchar_t)0x0078, (wchar_t)0x0079, (wchar_t)0x007A, (wchar_t)0x00E0, - (wchar_t)0x00E1, (wchar_t)0x00E2, (wchar_t)0x00E3, (wchar_t)0x00E4, (wchar_t)0x00E5, (wchar_t)0x00E6, (wchar_t)0x00E7, (wchar_t)0x00E8, (wchar_t)0x00E9, - (wchar_t)0x00EA, (wchar_t)0x00EB, (wchar_t)0x00EC, (wchar_t)0x00ED, (wchar_t)0x00EE, (wchar_t)0x00EF, (wchar_t)0x00F0, (wchar_t)0x00F1, (wchar_t)0x00F2, - (wchar_t)0x00F3, (wchar_t)0x00F4, (wchar_t)0x00F5, (wchar_t)0x00F6, (wchar_t)0x00F8, (wchar_t)0x00F9, (wchar_t)0x00FA, (wchar_t)0x00FB, (wchar_t)0x00FC, - (wchar_t)0x00FD, (wchar_t)0x00FE, (wchar_t)0x00FF, (wchar_t)0x0101, (wchar_t)0x0103, (wchar_t)0x0105, (wchar_t)0x0107, (wchar_t)0x0109, (wchar_t)0x010B, - (wchar_t)0x010D, (wchar_t)0x010F, (wchar_t)0x0111, (wchar_t)0x0113, (wchar_t)0x0115, (wchar_t)0x0117, (wchar_t)0x0119, (wchar_t)0x011B, (wchar_t)0x011D, - (wchar_t)0x011F, (wchar_t)0x0121, (wchar_t)0x0123, (wchar_t)0x0125, (wchar_t)0x0127, (wchar_t)0x0129, (wchar_t)0x012B, (wchar_t)0x012D, (wchar_t)0x012F, - (wchar_t)0x0131, (wchar_t)0x0133, (wchar_t)0x0135, (wchar_t)0x0137, (wchar_t)0x013A, (wchar_t)0x013C, (wchar_t)0x013E, (wchar_t)0x0140, (wchar_t)0x0142, - (wchar_t)0x0144, (wchar_t)0x0146, (wchar_t)0x0148, (wchar_t)0x014B, (wchar_t)0x014D, (wchar_t)0x014F, (wchar_t)0x0151, (wchar_t)0x0153, (wchar_t)0x0155, - (wchar_t)0x0157, (wchar_t)0x0159, (wchar_t)0x015B, (wchar_t)0x015D, (wchar_t)0x015F, (wchar_t)0x0161, (wchar_t)0x0163, (wchar_t)0x0165, (wchar_t)0x0167, - (wchar_t)0x0169, (wchar_t)0x016B, (wchar_t)0x016D, (wchar_t)0x016F, (wchar_t)0x0171, (wchar_t)0x0173, (wchar_t)0x0175, (wchar_t)0x0177, (wchar_t)0x017A, - (wchar_t)0x017C, (wchar_t)0x017E, (wchar_t)0x0183, (wchar_t)0x0185, (wchar_t)0x0188, (wchar_t)0x018C, (wchar_t)0x0192, (wchar_t)0x0199, (wchar_t)0x01A1, - (wchar_t)0x01A3, (wchar_t)0x01A5, (wchar_t)0x01A8, (wchar_t)0x01AD, (wchar_t)0x01B0, (wchar_t)0x01B4, (wchar_t)0x01B6, (wchar_t)0x01B9, (wchar_t)0x01BD, - (wchar_t)0x01C6, (wchar_t)0x01C9, (wchar_t)0x01CC, (wchar_t)0x01CE, (wchar_t)0x01D0, (wchar_t)0x01D2, (wchar_t)0x01D4, (wchar_t)0x01D6, (wchar_t)0x01D8, - (wchar_t)0x01DA, (wchar_t)0x01DC, (wchar_t)0x01DF, (wchar_t)0x01E1, (wchar_t)0x01E3, (wchar_t)0x01E5, (wchar_t)0x01E7, (wchar_t)0x01E9, (wchar_t)0x01EB, - (wchar_t)0x01ED, (wchar_t)0x01EF, (wchar_t)0x01F3, (wchar_t)0x01F5, (wchar_t)0x01FB, (wchar_t)0x01FD, (wchar_t)0x01FF, (wchar_t)0x0201, (wchar_t)0x0203, - (wchar_t)0x0205, (wchar_t)0x0207, (wchar_t)0x0209, (wchar_t)0x020B, (wchar_t)0x020D, (wchar_t)0x020F, (wchar_t)0x0211, (wchar_t)0x0213, (wchar_t)0x0215, - (wchar_t)0x0217, (wchar_t)0x0253, (wchar_t)0x0254, (wchar_t)0x0257, (wchar_t)0x0258, (wchar_t)0x0259, (wchar_t)0x025B, (wchar_t)0x0260, (wchar_t)0x0263, - (wchar_t)0x0268, (wchar_t)0x0269, (wchar_t)0x026F, (wchar_t)0x0272, (wchar_t)0x0275, (wchar_t)0x0283, (wchar_t)0x0288, (wchar_t)0x028A, (wchar_t)0x028B, - (wchar_t)0x0292, (wchar_t)0x03AC, (wchar_t)0x03AD, (wchar_t)0x03AE, (wchar_t)0x03AF, (wchar_t)0x03B1, (wchar_t)0x03B2, (wchar_t)0x03B3, (wchar_t)0x03B4, - (wchar_t)0x03B5, (wchar_t)0x03B6, (wchar_t)0x03B7, (wchar_t)0x03B8, (wchar_t)0x03B9, (wchar_t)0x03BA, (wchar_t)0x03BB, (wchar_t)0x03BC, (wchar_t)0x03BD, - (wchar_t)0x03BE, (wchar_t)0x03BF, (wchar_t)0x03C0, (wchar_t)0x03C1, (wchar_t)0x03C3, (wchar_t)0x03C4, (wchar_t)0x03C5, (wchar_t)0x03C6, (wchar_t)0x03C7, - (wchar_t)0x03C8, (wchar_t)0x03C9, (wchar_t)0x03CA, (wchar_t)0x03CB, (wchar_t)0x03CC, (wchar_t)0x03CD, (wchar_t)0x03CE, (wchar_t)0x03E3, (wchar_t)0x03E5, - (wchar_t)0x03E7, (wchar_t)0x03E9, (wchar_t)0x03EB, (wchar_t)0x03ED, (wchar_t)0x03EF, (wchar_t)0x0430, (wchar_t)0x0431, (wchar_t)0x0432, (wchar_t)0x0433, - (wchar_t)0x0434, (wchar_t)0x0435, (wchar_t)0x0436, (wchar_t)0x0437, (wchar_t)0x0438, (wchar_t)0x0439, (wchar_t)0x043A, (wchar_t)0x043B, (wchar_t)0x043C, - (wchar_t)0x043D, (wchar_t)0x043E, (wchar_t)0x043F, (wchar_t)0x0440, (wchar_t)0x0441, (wchar_t)0x0442, (wchar_t)0x0443, (wchar_t)0x0444, (wchar_t)0x0445, - (wchar_t)0x0446, (wchar_t)0x0447, (wchar_t)0x0448, (wchar_t)0x0449, (wchar_t)0x044A, (wchar_t)0x044B, (wchar_t)0x044C, (wchar_t)0x044D, (wchar_t)0x044E, - (wchar_t)0x044F, (wchar_t)0x0451, (wchar_t)0x0452, (wchar_t)0x0453, (wchar_t)0x0454, (wchar_t)0x0455, (wchar_t)0x0456, (wchar_t)0x0457, (wchar_t)0x0458, - (wchar_t)0x0459, (wchar_t)0x045A, (wchar_t)0x045B, (wchar_t)0x045C, (wchar_t)0x045E, (wchar_t)0x045F, (wchar_t)0x0461, (wchar_t)0x0463, (wchar_t)0x0465, - (wchar_t)0x0467, (wchar_t)0x0469, (wchar_t)0x046B, (wchar_t)0x046D, (wchar_t)0x046F, (wchar_t)0x0471, (wchar_t)0x0473, (wchar_t)0x0475, (wchar_t)0x0477, - (wchar_t)0x0479, (wchar_t)0x047B, (wchar_t)0x047D, (wchar_t)0x047F, (wchar_t)0x0481, (wchar_t)0x0491, (wchar_t)0x0493, (wchar_t)0x0495, (wchar_t)0x0497, - (wchar_t)0x0499, (wchar_t)0x049B, (wchar_t)0x049D, (wchar_t)0x049F, (wchar_t)0x04A1, (wchar_t)0x04A3, (wchar_t)0x04A5, (wchar_t)0x04A7, (wchar_t)0x04A9, - (wchar_t)0x04AB, (wchar_t)0x04AD, (wchar_t)0x04AF, (wchar_t)0x04B1, (wchar_t)0x04B3, (wchar_t)0x04B5, (wchar_t)0x04B7, (wchar_t)0x04B9, (wchar_t)0x04BB, - (wchar_t)0x04BD, (wchar_t)0x04BF, (wchar_t)0x04C2, (wchar_t)0x04C4, (wchar_t)0x04C8, (wchar_t)0x04CC, (wchar_t)0x04D1, (wchar_t)0x04D3, (wchar_t)0x04D5, - (wchar_t)0x04D7, (wchar_t)0x04D9, (wchar_t)0x04DB, (wchar_t)0x04DD, (wchar_t)0x04DF, (wchar_t)0x04E1, (wchar_t)0x04E3, (wchar_t)0x04E5, (wchar_t)0x04E7, - (wchar_t)0x04E9, (wchar_t)0x04EB, (wchar_t)0x04EF, (wchar_t)0x04F1, (wchar_t)0x04F3, (wchar_t)0x04F5, (wchar_t)0x04F9, (wchar_t)0x0561, (wchar_t)0x0562, - (wchar_t)0x0563, (wchar_t)0x0564, (wchar_t)0x0565, (wchar_t)0x0566, (wchar_t)0x0567, (wchar_t)0x0568, (wchar_t)0x0569, (wchar_t)0x056A, (wchar_t)0x056B, - (wchar_t)0x056C, (wchar_t)0x056D, (wchar_t)0x056E, (wchar_t)0x056F, (wchar_t)0x0570, (wchar_t)0x0571, (wchar_t)0x0572, (wchar_t)0x0573, (wchar_t)0x0574, - (wchar_t)0x0575, (wchar_t)0x0576, (wchar_t)0x0577, (wchar_t)0x0578, (wchar_t)0x0579, (wchar_t)0x057A, (wchar_t)0x057B, (wchar_t)0x057C, (wchar_t)0x057D, - (wchar_t)0x057E, (wchar_t)0x057F, (wchar_t)0x0580, (wchar_t)0x0581, (wchar_t)0x0582, (wchar_t)0x0583, (wchar_t)0x0584, (wchar_t)0x0585, (wchar_t)0x0586, - (wchar_t)0x10D0, (wchar_t)0x10D1, (wchar_t)0x10D2, (wchar_t)0x10D3, (wchar_t)0x10D4, (wchar_t)0x10D5, (wchar_t)0x10D6, (wchar_t)0x10D7, (wchar_t)0x10D8, - (wchar_t)0x10D9, (wchar_t)0x10DA, (wchar_t)0x10DB, (wchar_t)0x10DC, (wchar_t)0x10DD, (wchar_t)0x10DE, (wchar_t)0x10DF, (wchar_t)0x10E0, (wchar_t)0x10E1, - (wchar_t)0x10E2, (wchar_t)0x10E3, (wchar_t)0x10E4, (wchar_t)0x10E5, (wchar_t)0x10E6, (wchar_t)0x10E7, (wchar_t)0x10E8, (wchar_t)0x10E9, (wchar_t)0x10EA, - (wchar_t)0x10EB, (wchar_t)0x10EC, (wchar_t)0x10ED, (wchar_t)0x10EE, (wchar_t)0x10EF, (wchar_t)0x10F0, (wchar_t)0x10F1, (wchar_t)0x10F2, (wchar_t)0x10F3, - (wchar_t)0x10F4, (wchar_t)0x10F5, (wchar_t)0x1E01, (wchar_t)0x1E03, (wchar_t)0x1E05, (wchar_t)0x1E07, (wchar_t)0x1E09, (wchar_t)0x1E0B, (wchar_t)0x1E0D, - (wchar_t)0x1E0F, (wchar_t)0x1E11, (wchar_t)0x1E13, (wchar_t)0x1E15, (wchar_t)0x1E17, (wchar_t)0x1E19, (wchar_t)0x1E1B, (wchar_t)0x1E1D, (wchar_t)0x1E1F, - (wchar_t)0x1E21, (wchar_t)0x1E23, (wchar_t)0x1E25, (wchar_t)0x1E27, (wchar_t)0x1E29, (wchar_t)0x1E2B, (wchar_t)0x1E2D, (wchar_t)0x1E2F, (wchar_t)0x1E31, - (wchar_t)0x1E33, (wchar_t)0x1E35, (wchar_t)0x1E37, (wchar_t)0x1E39, (wchar_t)0x1E3B, (wchar_t)0x1E3D, (wchar_t)0x1E3F, (wchar_t)0x1E41, (wchar_t)0x1E43, - (wchar_t)0x1E45, (wchar_t)0x1E47, (wchar_t)0x1E49, (wchar_t)0x1E4B, (wchar_t)0x1E4D, (wchar_t)0x1E4F, (wchar_t)0x1E51, (wchar_t)0x1E53, (wchar_t)0x1E55, - (wchar_t)0x1E57, (wchar_t)0x1E59, (wchar_t)0x1E5B, (wchar_t)0x1E5D, (wchar_t)0x1E5F, (wchar_t)0x1E61, (wchar_t)0x1E63, (wchar_t)0x1E65, (wchar_t)0x1E67, - (wchar_t)0x1E69, (wchar_t)0x1E6B, (wchar_t)0x1E6D, (wchar_t)0x1E6F, (wchar_t)0x1E71, (wchar_t)0x1E73, (wchar_t)0x1E75, (wchar_t)0x1E77, (wchar_t)0x1E79, - (wchar_t)0x1E7B, (wchar_t)0x1E7D, (wchar_t)0x1E7F, (wchar_t)0x1E81, (wchar_t)0x1E83, (wchar_t)0x1E85, (wchar_t)0x1E87, (wchar_t)0x1E89, (wchar_t)0x1E8B, - (wchar_t)0x1E8D, (wchar_t)0x1E8F, (wchar_t)0x1E91, (wchar_t)0x1E93, (wchar_t)0x1E95, (wchar_t)0x1EA1, (wchar_t)0x1EA3, (wchar_t)0x1EA5, (wchar_t)0x1EA7, - (wchar_t)0x1EA9, (wchar_t)0x1EAB, (wchar_t)0x1EAD, (wchar_t)0x1EAF, (wchar_t)0x1EB1, (wchar_t)0x1EB3, (wchar_t)0x1EB5, (wchar_t)0x1EB7, (wchar_t)0x1EB9, - (wchar_t)0x1EBB, (wchar_t)0x1EBD, (wchar_t)0x1EBF, (wchar_t)0x1EC1, (wchar_t)0x1EC3, (wchar_t)0x1EC5, (wchar_t)0x1EC7, (wchar_t)0x1EC9, (wchar_t)0x1ECB, - (wchar_t)0x1ECD, (wchar_t)0x1ECF, (wchar_t)0x1ED1, (wchar_t)0x1ED3, (wchar_t)0x1ED5, (wchar_t)0x1ED7, (wchar_t)0x1ED9, (wchar_t)0x1EDB, (wchar_t)0x1EDD, - (wchar_t)0x1EDF, (wchar_t)0x1EE1, (wchar_t)0x1EE3, (wchar_t)0x1EE5, (wchar_t)0x1EE7, (wchar_t)0x1EE9, (wchar_t)0x1EEB, (wchar_t)0x1EED, (wchar_t)0x1EEF, - (wchar_t)0x1EF1, (wchar_t)0x1EF3, (wchar_t)0x1EF5, (wchar_t)0x1EF7, (wchar_t)0x1EF9, (wchar_t)0x1F00, (wchar_t)0x1F01, (wchar_t)0x1F02, (wchar_t)0x1F03, - (wchar_t)0x1F04, (wchar_t)0x1F05, (wchar_t)0x1F06, (wchar_t)0x1F07, (wchar_t)0x1F10, (wchar_t)0x1F11, (wchar_t)0x1F12, (wchar_t)0x1F13, (wchar_t)0x1F14, - (wchar_t)0x1F15, (wchar_t)0x1F20, (wchar_t)0x1F21, (wchar_t)0x1F22, (wchar_t)0x1F23, (wchar_t)0x1F24, (wchar_t)0x1F25, (wchar_t)0x1F26, (wchar_t)0x1F27, - (wchar_t)0x1F30, (wchar_t)0x1F31, (wchar_t)0x1F32, (wchar_t)0x1F33, (wchar_t)0x1F34, (wchar_t)0x1F35, (wchar_t)0x1F36, (wchar_t)0x1F37, (wchar_t)0x1F40, - (wchar_t)0x1F41, (wchar_t)0x1F42, (wchar_t)0x1F43, (wchar_t)0x1F44, (wchar_t)0x1F45, (wchar_t)0x1F51, (wchar_t)0x1F53, (wchar_t)0x1F55, (wchar_t)0x1F57, - (wchar_t)0x1F60, (wchar_t)0x1F61, (wchar_t)0x1F62, (wchar_t)0x1F63, (wchar_t)0x1F64, (wchar_t)0x1F65, (wchar_t)0x1F66, (wchar_t)0x1F67, (wchar_t)0x1F80, - (wchar_t)0x1F81, (wchar_t)0x1F82, (wchar_t)0x1F83, (wchar_t)0x1F84, (wchar_t)0x1F85, (wchar_t)0x1F86, (wchar_t)0x1F87, (wchar_t)0x1F90, (wchar_t)0x1F91, - (wchar_t)0x1F92, (wchar_t)0x1F93, (wchar_t)0x1F94, (wchar_t)0x1F95, (wchar_t)0x1F96, (wchar_t)0x1F97, (wchar_t)0x1FA0, (wchar_t)0x1FA1, (wchar_t)0x1FA2, - (wchar_t)0x1FA3, (wchar_t)0x1FA4, (wchar_t)0x1FA5, (wchar_t)0x1FA6, (wchar_t)0x1FA7, (wchar_t)0x1FB0, (wchar_t)0x1FB1, (wchar_t)0x1FD0, (wchar_t)0x1FD1, - (wchar_t)0x1FE0, (wchar_t)0x1FE1, (wchar_t)0x24D0, (wchar_t)0x24D1, (wchar_t)0x24D2, (wchar_t)0x24D3, (wchar_t)0x24D4, (wchar_t)0x24D5, (wchar_t)0x24D6, - (wchar_t)0x24D7, (wchar_t)0x24D8, (wchar_t)0x24D9, (wchar_t)0x24DA, (wchar_t)0x24DB, (wchar_t)0x24DC, (wchar_t)0x24DD, (wchar_t)0x24DE, (wchar_t)0x24DF, - (wchar_t)0x24E0, (wchar_t)0x24E1, (wchar_t)0x24E2, (wchar_t)0x24E3, (wchar_t)0x24E4, (wchar_t)0x24E5, (wchar_t)0x24E6, (wchar_t)0x24E7, (wchar_t)0x24E8, - (wchar_t)0x24E9, (wchar_t)0xFF41, (wchar_t)0xFF42, (wchar_t)0xFF43, (wchar_t)0xFF44, (wchar_t)0xFF45, (wchar_t)0xFF46, (wchar_t)0xFF47, (wchar_t)0xFF48, - (wchar_t)0xFF49, (wchar_t)0xFF4A, (wchar_t)0xFF4B, (wchar_t)0xFF4C, (wchar_t)0xFF4D, (wchar_t)0xFF4E, (wchar_t)0xFF4F, (wchar_t)0xFF50, (wchar_t)0xFF51, - (wchar_t)0xFF52, (wchar_t)0xFF53, (wchar_t)0xFF54, (wchar_t)0xFF55, (wchar_t)0xFF56, (wchar_t)0xFF57, (wchar_t)0xFF58, (wchar_t)0xFF59, (wchar_t)0xFF5A + static wchar_t unicodeLowercase[] = { + (wchar_t)0x0061, (wchar_t)0x0062, (wchar_t)0x0063, (wchar_t)0x0064, (wchar_t)0x0065, + (wchar_t)0x0066, (wchar_t)0x0067, (wchar_t)0x0068, (wchar_t)0x0069, (wchar_t)0x006A, + (wchar_t)0x006B, (wchar_t)0x006C, (wchar_t)0x006D, (wchar_t)0x006E, (wchar_t)0x006F, + (wchar_t)0x0070, (wchar_t)0x0071, (wchar_t)0x0072, (wchar_t)0x0073, (wchar_t)0x0074, + (wchar_t)0x0075, (wchar_t)0x0076, (wchar_t)0x0077, (wchar_t)0x0078, (wchar_t)0x0079, + (wchar_t)0x007A, (wchar_t)0x00E0, (wchar_t)0x00E1, (wchar_t)0x00E2, (wchar_t)0x00E3, + (wchar_t)0x00E4, (wchar_t)0x00E5, (wchar_t)0x00E6, (wchar_t)0x00E7, (wchar_t)0x00E8, + (wchar_t)0x00E9, (wchar_t)0x00EA, (wchar_t)0x00EB, (wchar_t)0x00EC, (wchar_t)0x00ED, + (wchar_t)0x00EE, (wchar_t)0x00EF, (wchar_t)0x00F0, (wchar_t)0x00F1, (wchar_t)0x00F2, + (wchar_t)0x00F3, (wchar_t)0x00F4, (wchar_t)0x00F5, (wchar_t)0x00F6, (wchar_t)0x00F8, + (wchar_t)0x00F9, (wchar_t)0x00FA, (wchar_t)0x00FB, (wchar_t)0x00FC, (wchar_t)0x00FD, + (wchar_t)0x00FE, (wchar_t)0x00FF, (wchar_t)0x0101, (wchar_t)0x0103, (wchar_t)0x0105, + (wchar_t)0x0107, (wchar_t)0x0109, (wchar_t)0x010B, (wchar_t)0x010D, (wchar_t)0x010F, + (wchar_t)0x0111, (wchar_t)0x0113, (wchar_t)0x0115, (wchar_t)0x0117, (wchar_t)0x0119, + (wchar_t)0x011B, (wchar_t)0x011D, (wchar_t)0x011F, (wchar_t)0x0121, (wchar_t)0x0123, + (wchar_t)0x0125, (wchar_t)0x0127, (wchar_t)0x0129, (wchar_t)0x012B, (wchar_t)0x012D, + (wchar_t)0x012F, (wchar_t)0x0131, (wchar_t)0x0133, (wchar_t)0x0135, (wchar_t)0x0137, + (wchar_t)0x013A, (wchar_t)0x013C, (wchar_t)0x013E, (wchar_t)0x0140, (wchar_t)0x0142, + (wchar_t)0x0144, (wchar_t)0x0146, (wchar_t)0x0148, (wchar_t)0x014B, (wchar_t)0x014D, + (wchar_t)0x014F, (wchar_t)0x0151, (wchar_t)0x0153, (wchar_t)0x0155, (wchar_t)0x0157, + (wchar_t)0x0159, (wchar_t)0x015B, (wchar_t)0x015D, (wchar_t)0x015F, (wchar_t)0x0161, + (wchar_t)0x0163, (wchar_t)0x0165, (wchar_t)0x0167, (wchar_t)0x0169, (wchar_t)0x016B, + (wchar_t)0x016D, (wchar_t)0x016F, (wchar_t)0x0171, (wchar_t)0x0173, (wchar_t)0x0175, + (wchar_t)0x0177, (wchar_t)0x017A, (wchar_t)0x017C, (wchar_t)0x017E, (wchar_t)0x0183, + (wchar_t)0x0185, (wchar_t)0x0188, (wchar_t)0x018C, (wchar_t)0x0192, (wchar_t)0x0199, + (wchar_t)0x01A1, (wchar_t)0x01A3, (wchar_t)0x01A5, (wchar_t)0x01A8, (wchar_t)0x01AD, + (wchar_t)0x01B0, (wchar_t)0x01B4, (wchar_t)0x01B6, (wchar_t)0x01B9, (wchar_t)0x01BD, + (wchar_t)0x01C6, (wchar_t)0x01C9, (wchar_t)0x01CC, (wchar_t)0x01CE, (wchar_t)0x01D0, + (wchar_t)0x01D2, (wchar_t)0x01D4, (wchar_t)0x01D6, (wchar_t)0x01D8, (wchar_t)0x01DA, + (wchar_t)0x01DC, (wchar_t)0x01DF, (wchar_t)0x01E1, (wchar_t)0x01E3, (wchar_t)0x01E5, + (wchar_t)0x01E7, (wchar_t)0x01E9, (wchar_t)0x01EB, (wchar_t)0x01ED, (wchar_t)0x01EF, + (wchar_t)0x01F3, (wchar_t)0x01F5, (wchar_t)0x01FB, (wchar_t)0x01FD, (wchar_t)0x01FF, + (wchar_t)0x0201, (wchar_t)0x0203, (wchar_t)0x0205, (wchar_t)0x0207, (wchar_t)0x0209, + (wchar_t)0x020B, (wchar_t)0x020D, (wchar_t)0x020F, (wchar_t)0x0211, (wchar_t)0x0213, + (wchar_t)0x0215, (wchar_t)0x0217, (wchar_t)0x0253, (wchar_t)0x0254, (wchar_t)0x0257, + (wchar_t)0x0258, (wchar_t)0x0259, (wchar_t)0x025B, (wchar_t)0x0260, (wchar_t)0x0263, + (wchar_t)0x0268, (wchar_t)0x0269, (wchar_t)0x026F, (wchar_t)0x0272, (wchar_t)0x0275, + (wchar_t)0x0283, (wchar_t)0x0288, (wchar_t)0x028A, (wchar_t)0x028B, (wchar_t)0x0292, + (wchar_t)0x03AC, (wchar_t)0x03AD, (wchar_t)0x03AE, (wchar_t)0x03AF, (wchar_t)0x03B1, + (wchar_t)0x03B2, (wchar_t)0x03B3, (wchar_t)0x03B4, (wchar_t)0x03B5, (wchar_t)0x03B6, + (wchar_t)0x03B7, (wchar_t)0x03B8, (wchar_t)0x03B9, (wchar_t)0x03BA, (wchar_t)0x03BB, + (wchar_t)0x03BC, (wchar_t)0x03BD, (wchar_t)0x03BE, (wchar_t)0x03BF, (wchar_t)0x03C0, + (wchar_t)0x03C1, (wchar_t)0x03C3, (wchar_t)0x03C4, (wchar_t)0x03C5, (wchar_t)0x03C6, + (wchar_t)0x03C7, (wchar_t)0x03C8, (wchar_t)0x03C9, (wchar_t)0x03CA, (wchar_t)0x03CB, + (wchar_t)0x03CC, (wchar_t)0x03CD, (wchar_t)0x03CE, (wchar_t)0x03E3, (wchar_t)0x03E5, + (wchar_t)0x03E7, (wchar_t)0x03E9, (wchar_t)0x03EB, (wchar_t)0x03ED, (wchar_t)0x03EF, + (wchar_t)0x0430, (wchar_t)0x0431, (wchar_t)0x0432, (wchar_t)0x0433, (wchar_t)0x0434, + (wchar_t)0x0435, (wchar_t)0x0436, (wchar_t)0x0437, (wchar_t)0x0438, (wchar_t)0x0439, + (wchar_t)0x043A, (wchar_t)0x043B, (wchar_t)0x043C, (wchar_t)0x043D, (wchar_t)0x043E, + (wchar_t)0x043F, (wchar_t)0x0440, (wchar_t)0x0441, (wchar_t)0x0442, (wchar_t)0x0443, + (wchar_t)0x0444, (wchar_t)0x0445, (wchar_t)0x0446, (wchar_t)0x0447, (wchar_t)0x0448, + (wchar_t)0x0449, (wchar_t)0x044A, (wchar_t)0x044B, (wchar_t)0x044C, (wchar_t)0x044D, + (wchar_t)0x044E, (wchar_t)0x044F, (wchar_t)0x0451, (wchar_t)0x0452, (wchar_t)0x0453, + (wchar_t)0x0454, (wchar_t)0x0455, (wchar_t)0x0456, (wchar_t)0x0457, (wchar_t)0x0458, + (wchar_t)0x0459, (wchar_t)0x045A, (wchar_t)0x045B, (wchar_t)0x045C, (wchar_t)0x045E, + (wchar_t)0x045F, (wchar_t)0x0461, (wchar_t)0x0463, (wchar_t)0x0465, (wchar_t)0x0467, + (wchar_t)0x0469, (wchar_t)0x046B, (wchar_t)0x046D, (wchar_t)0x046F, (wchar_t)0x0471, + (wchar_t)0x0473, (wchar_t)0x0475, (wchar_t)0x0477, (wchar_t)0x0479, (wchar_t)0x047B, + (wchar_t)0x047D, (wchar_t)0x047F, (wchar_t)0x0481, (wchar_t)0x0491, (wchar_t)0x0493, + (wchar_t)0x0495, (wchar_t)0x0497, (wchar_t)0x0499, (wchar_t)0x049B, (wchar_t)0x049D, + (wchar_t)0x049F, (wchar_t)0x04A1, (wchar_t)0x04A3, (wchar_t)0x04A5, (wchar_t)0x04A7, + (wchar_t)0x04A9, (wchar_t)0x04AB, (wchar_t)0x04AD, (wchar_t)0x04AF, (wchar_t)0x04B1, + (wchar_t)0x04B3, (wchar_t)0x04B5, (wchar_t)0x04B7, (wchar_t)0x04B9, (wchar_t)0x04BB, + (wchar_t)0x04BD, (wchar_t)0x04BF, (wchar_t)0x04C2, (wchar_t)0x04C4, (wchar_t)0x04C8, + (wchar_t)0x04CC, (wchar_t)0x04D1, (wchar_t)0x04D3, (wchar_t)0x04D5, (wchar_t)0x04D7, + (wchar_t)0x04D9, (wchar_t)0x04DB, (wchar_t)0x04DD, (wchar_t)0x04DF, (wchar_t)0x04E1, + (wchar_t)0x04E3, (wchar_t)0x04E5, (wchar_t)0x04E7, (wchar_t)0x04E9, (wchar_t)0x04EB, + (wchar_t)0x04EF, (wchar_t)0x04F1, (wchar_t)0x04F3, (wchar_t)0x04F5, (wchar_t)0x04F9, + (wchar_t)0x0561, (wchar_t)0x0562, (wchar_t)0x0563, (wchar_t)0x0564, (wchar_t)0x0565, + (wchar_t)0x0566, (wchar_t)0x0567, (wchar_t)0x0568, (wchar_t)0x0569, (wchar_t)0x056A, + (wchar_t)0x056B, (wchar_t)0x056C, (wchar_t)0x056D, (wchar_t)0x056E, (wchar_t)0x056F, + (wchar_t)0x0570, (wchar_t)0x0571, (wchar_t)0x0572, (wchar_t)0x0573, (wchar_t)0x0574, + (wchar_t)0x0575, (wchar_t)0x0576, (wchar_t)0x0577, (wchar_t)0x0578, (wchar_t)0x0579, + (wchar_t)0x057A, (wchar_t)0x057B, (wchar_t)0x057C, (wchar_t)0x057D, (wchar_t)0x057E, + (wchar_t)0x057F, (wchar_t)0x0580, (wchar_t)0x0581, (wchar_t)0x0582, (wchar_t)0x0583, + (wchar_t)0x0584, (wchar_t)0x0585, (wchar_t)0x0586, (wchar_t)0x10D0, (wchar_t)0x10D1, + (wchar_t)0x10D2, (wchar_t)0x10D3, (wchar_t)0x10D4, (wchar_t)0x10D5, (wchar_t)0x10D6, + (wchar_t)0x10D7, (wchar_t)0x10D8, (wchar_t)0x10D9, (wchar_t)0x10DA, (wchar_t)0x10DB, + (wchar_t)0x10DC, (wchar_t)0x10DD, (wchar_t)0x10DE, (wchar_t)0x10DF, (wchar_t)0x10E0, + (wchar_t)0x10E1, (wchar_t)0x10E2, (wchar_t)0x10E3, (wchar_t)0x10E4, (wchar_t)0x10E5, + (wchar_t)0x10E6, (wchar_t)0x10E7, (wchar_t)0x10E8, (wchar_t)0x10E9, (wchar_t)0x10EA, + (wchar_t)0x10EB, (wchar_t)0x10EC, (wchar_t)0x10ED, (wchar_t)0x10EE, (wchar_t)0x10EF, + (wchar_t)0x10F0, (wchar_t)0x10F1, (wchar_t)0x10F2, (wchar_t)0x10F3, (wchar_t)0x10F4, + (wchar_t)0x10F5, (wchar_t)0x1E01, (wchar_t)0x1E03, (wchar_t)0x1E05, (wchar_t)0x1E07, + (wchar_t)0x1E09, (wchar_t)0x1E0B, (wchar_t)0x1E0D, (wchar_t)0x1E0F, (wchar_t)0x1E11, + (wchar_t)0x1E13, (wchar_t)0x1E15, (wchar_t)0x1E17, (wchar_t)0x1E19, (wchar_t)0x1E1B, + (wchar_t)0x1E1D, (wchar_t)0x1E1F, (wchar_t)0x1E21, (wchar_t)0x1E23, (wchar_t)0x1E25, + (wchar_t)0x1E27, (wchar_t)0x1E29, (wchar_t)0x1E2B, (wchar_t)0x1E2D, (wchar_t)0x1E2F, + (wchar_t)0x1E31, (wchar_t)0x1E33, (wchar_t)0x1E35, (wchar_t)0x1E37, (wchar_t)0x1E39, + (wchar_t)0x1E3B, (wchar_t)0x1E3D, (wchar_t)0x1E3F, (wchar_t)0x1E41, (wchar_t)0x1E43, + (wchar_t)0x1E45, (wchar_t)0x1E47, (wchar_t)0x1E49, (wchar_t)0x1E4B, (wchar_t)0x1E4D, + (wchar_t)0x1E4F, (wchar_t)0x1E51, (wchar_t)0x1E53, (wchar_t)0x1E55, (wchar_t)0x1E57, + (wchar_t)0x1E59, (wchar_t)0x1E5B, (wchar_t)0x1E5D, (wchar_t)0x1E5F, (wchar_t)0x1E61, + (wchar_t)0x1E63, (wchar_t)0x1E65, (wchar_t)0x1E67, (wchar_t)0x1E69, (wchar_t)0x1E6B, + (wchar_t)0x1E6D, (wchar_t)0x1E6F, (wchar_t)0x1E71, (wchar_t)0x1E73, (wchar_t)0x1E75, + (wchar_t)0x1E77, (wchar_t)0x1E79, (wchar_t)0x1E7B, (wchar_t)0x1E7D, (wchar_t)0x1E7F, + (wchar_t)0x1E81, (wchar_t)0x1E83, (wchar_t)0x1E85, (wchar_t)0x1E87, (wchar_t)0x1E89, + (wchar_t)0x1E8B, (wchar_t)0x1E8D, (wchar_t)0x1E8F, (wchar_t)0x1E91, (wchar_t)0x1E93, + (wchar_t)0x1E95, (wchar_t)0x1EA1, (wchar_t)0x1EA3, (wchar_t)0x1EA5, (wchar_t)0x1EA7, + (wchar_t)0x1EA9, (wchar_t)0x1EAB, (wchar_t)0x1EAD, (wchar_t)0x1EAF, (wchar_t)0x1EB1, + (wchar_t)0x1EB3, (wchar_t)0x1EB5, (wchar_t)0x1EB7, (wchar_t)0x1EB9, (wchar_t)0x1EBB, + (wchar_t)0x1EBD, (wchar_t)0x1EBF, (wchar_t)0x1EC1, (wchar_t)0x1EC3, (wchar_t)0x1EC5, + (wchar_t)0x1EC7, (wchar_t)0x1EC9, (wchar_t)0x1ECB, (wchar_t)0x1ECD, (wchar_t)0x1ECF, + (wchar_t)0x1ED1, (wchar_t)0x1ED3, (wchar_t)0x1ED5, (wchar_t)0x1ED7, (wchar_t)0x1ED9, + (wchar_t)0x1EDB, (wchar_t)0x1EDD, (wchar_t)0x1EDF, (wchar_t)0x1EE1, (wchar_t)0x1EE3, + (wchar_t)0x1EE5, (wchar_t)0x1EE7, (wchar_t)0x1EE9, (wchar_t)0x1EEB, (wchar_t)0x1EED, + (wchar_t)0x1EEF, (wchar_t)0x1EF1, (wchar_t)0x1EF3, (wchar_t)0x1EF5, (wchar_t)0x1EF7, + (wchar_t)0x1EF9, (wchar_t)0x1F00, (wchar_t)0x1F01, (wchar_t)0x1F02, (wchar_t)0x1F03, + (wchar_t)0x1F04, (wchar_t)0x1F05, (wchar_t)0x1F06, (wchar_t)0x1F07, (wchar_t)0x1F10, + (wchar_t)0x1F11, (wchar_t)0x1F12, (wchar_t)0x1F13, (wchar_t)0x1F14, (wchar_t)0x1F15, + (wchar_t)0x1F20, (wchar_t)0x1F21, (wchar_t)0x1F22, (wchar_t)0x1F23, (wchar_t)0x1F24, + (wchar_t)0x1F25, (wchar_t)0x1F26, (wchar_t)0x1F27, (wchar_t)0x1F30, (wchar_t)0x1F31, + (wchar_t)0x1F32, (wchar_t)0x1F33, (wchar_t)0x1F34, (wchar_t)0x1F35, (wchar_t)0x1F36, + (wchar_t)0x1F37, (wchar_t)0x1F40, (wchar_t)0x1F41, (wchar_t)0x1F42, (wchar_t)0x1F43, + (wchar_t)0x1F44, (wchar_t)0x1F45, (wchar_t)0x1F51, (wchar_t)0x1F53, (wchar_t)0x1F55, + (wchar_t)0x1F57, (wchar_t)0x1F60, (wchar_t)0x1F61, (wchar_t)0x1F62, (wchar_t)0x1F63, + (wchar_t)0x1F64, (wchar_t)0x1F65, (wchar_t)0x1F66, (wchar_t)0x1F67, (wchar_t)0x1F80, + (wchar_t)0x1F81, (wchar_t)0x1F82, (wchar_t)0x1F83, (wchar_t)0x1F84, (wchar_t)0x1F85, + (wchar_t)0x1F86, (wchar_t)0x1F87, (wchar_t)0x1F90, (wchar_t)0x1F91, (wchar_t)0x1F92, + (wchar_t)0x1F93, (wchar_t)0x1F94, (wchar_t)0x1F95, (wchar_t)0x1F96, (wchar_t)0x1F97, + (wchar_t)0x1FA0, (wchar_t)0x1FA1, (wchar_t)0x1FA2, (wchar_t)0x1FA3, (wchar_t)0x1FA4, + (wchar_t)0x1FA5, (wchar_t)0x1FA6, (wchar_t)0x1FA7, (wchar_t)0x1FB0, (wchar_t)0x1FB1, + (wchar_t)0x1FD0, (wchar_t)0x1FD1, (wchar_t)0x1FE0, (wchar_t)0x1FE1, (wchar_t)0x24D0, + (wchar_t)0x24D1, (wchar_t)0x24D2, (wchar_t)0x24D3, (wchar_t)0x24D4, (wchar_t)0x24D5, + (wchar_t)0x24D6, (wchar_t)0x24D7, (wchar_t)0x24D8, (wchar_t)0x24D9, (wchar_t)0x24DA, + (wchar_t)0x24DB, (wchar_t)0x24DC, (wchar_t)0x24DD, (wchar_t)0x24DE, (wchar_t)0x24DF, + (wchar_t)0x24E0, (wchar_t)0x24E1, (wchar_t)0x24E2, (wchar_t)0x24E3, (wchar_t)0x24E4, + (wchar_t)0x24E5, (wchar_t)0x24E6, (wchar_t)0x24E7, (wchar_t)0x24E8, (wchar_t)0x24E9, + (wchar_t)0xFF41, (wchar_t)0xFF42, (wchar_t)0xFF43, (wchar_t)0xFF44, (wchar_t)0xFF45, + (wchar_t)0xFF46, (wchar_t)0xFF47, (wchar_t)0xFF48, (wchar_t)0xFF49, (wchar_t)0xFF4A, + (wchar_t)0xFF4B, (wchar_t)0xFF4C, (wchar_t)0xFF4D, (wchar_t)0xFF4E, (wchar_t)0xFF4F, + (wchar_t)0xFF50, (wchar_t)0xFF51, (wchar_t)0xFF52, (wchar_t)0xFF53, (wchar_t)0xFF54, + (wchar_t)0xFF55, (wchar_t)0xFF56, (wchar_t)0xFF57, (wchar_t)0xFF58, (wchar_t)0xFF59, + (wchar_t)0xFF5A }; - static wchar_t unicodeUppercase[] = - { - (wchar_t)0x0041, (wchar_t)0x0042, (wchar_t)0x0043, (wchar_t)0x0044, (wchar_t)0x0045, (wchar_t)0x0046, (wchar_t)0x0047, (wchar_t)0x0048, (wchar_t)0x0049, - (wchar_t)0x004A, (wchar_t)0x004B, (wchar_t)0x004C, (wchar_t)0x004D, (wchar_t)0x004E, (wchar_t)0x004F, (wchar_t)0x0050, (wchar_t)0x0051, (wchar_t)0x0052, - (wchar_t)0x0053, (wchar_t)0x0054, (wchar_t)0x0055, (wchar_t)0x0056, (wchar_t)0x0057, (wchar_t)0x0058, (wchar_t)0x0059, (wchar_t)0x005A, (wchar_t)0x00C0, - (wchar_t)0x00C1, (wchar_t)0x00C2, (wchar_t)0x00C3, (wchar_t)0x00C4, (wchar_t)0x00C5, (wchar_t)0x00C6, (wchar_t)0x00C7, (wchar_t)0x00C8, (wchar_t)0x00C9, - (wchar_t)0x00CA, (wchar_t)0x00CB, (wchar_t)0x00CC, (wchar_t)0x00CD, (wchar_t)0x00CE, (wchar_t)0x00CF, (wchar_t)0x00D0, (wchar_t)0x00D1, (wchar_t)0x00D2, - (wchar_t)0x00D3, (wchar_t)0x00D4, (wchar_t)0x00D5, (wchar_t)0x00D6, (wchar_t)0x00D8, (wchar_t)0x00D9, (wchar_t)0x00DA, (wchar_t)0x00DB, (wchar_t)0x00DC, - (wchar_t)0x00DD, (wchar_t)0x00DE, (wchar_t)0x0178, (wchar_t)0x0100, (wchar_t)0x0102, (wchar_t)0x0104, (wchar_t)0x0106, (wchar_t)0x0108, (wchar_t)0x010A, - (wchar_t)0x010C, (wchar_t)0x010E, (wchar_t)0x0110, (wchar_t)0x0112, (wchar_t)0x0114, (wchar_t)0x0116, (wchar_t)0x0118, (wchar_t)0x011A, (wchar_t)0x011C, - (wchar_t)0x011E, (wchar_t)0x0120, (wchar_t)0x0122, (wchar_t)0x0124, (wchar_t)0x0126, (wchar_t)0x0128, (wchar_t)0x012A, (wchar_t)0x012C, (wchar_t)0x012E, - (wchar_t)0x0049, (wchar_t)0x0132, (wchar_t)0x0134, (wchar_t)0x0136, (wchar_t)0x0139, (wchar_t)0x013B, (wchar_t)0x013D, (wchar_t)0x013F, (wchar_t)0x0141, - (wchar_t)0x0143, (wchar_t)0x0145, (wchar_t)0x0147, (wchar_t)0x014A, (wchar_t)0x014C, (wchar_t)0x014E, (wchar_t)0x0150, (wchar_t)0x0152, (wchar_t)0x0154, - (wchar_t)0x0156, (wchar_t)0x0158, (wchar_t)0x015A, (wchar_t)0x015C, (wchar_t)0x015E, (wchar_t)0x0160, (wchar_t)0x0162, (wchar_t)0x0164, (wchar_t)0x0166, - (wchar_t)0x0168, (wchar_t)0x016A, (wchar_t)0x016C, (wchar_t)0x016E, (wchar_t)0x0170, (wchar_t)0x0172, (wchar_t)0x0174, (wchar_t)0x0176, (wchar_t)0x0179, - (wchar_t)0x017B, (wchar_t)0x017D, (wchar_t)0x0182, (wchar_t)0x0184, (wchar_t)0x0187, (wchar_t)0x018B, (wchar_t)0x0191, (wchar_t)0x0198, (wchar_t)0x01A0, - (wchar_t)0x01A2, (wchar_t)0x01A4, (wchar_t)0x01A7, (wchar_t)0x01AC, (wchar_t)0x01AF, (wchar_t)0x01B3, (wchar_t)0x01B5, (wchar_t)0x01B8, (wchar_t)0x01BC, - (wchar_t)0x01C4, (wchar_t)0x01C7, (wchar_t)0x01CA, (wchar_t)0x01CD, (wchar_t)0x01CF, (wchar_t)0x01D1, (wchar_t)0x01D3, (wchar_t)0x01D5, (wchar_t)0x01D7, - (wchar_t)0x01D9, (wchar_t)0x01DB, (wchar_t)0x01DE, (wchar_t)0x01E0, (wchar_t)0x01E2, (wchar_t)0x01E4, (wchar_t)0x01E6, (wchar_t)0x01E8, (wchar_t)0x01EA, - (wchar_t)0x01EC, (wchar_t)0x01EE, (wchar_t)0x01F1, (wchar_t)0x01F4, (wchar_t)0x01FA, (wchar_t)0x01FC, (wchar_t)0x01FE, (wchar_t)0x0200, (wchar_t)0x0202, - (wchar_t)0x0204, (wchar_t)0x0206, (wchar_t)0x0208, (wchar_t)0x020A, (wchar_t)0x020C, (wchar_t)0x020E, (wchar_t)0x0210, (wchar_t)0x0212, (wchar_t)0x0214, - (wchar_t)0x0216, (wchar_t)0x0181, (wchar_t)0x0186, (wchar_t)0x018A, (wchar_t)0x018E, (wchar_t)0x018F, (wchar_t)0x0190, (wchar_t)0x0193, (wchar_t)0x0194, - (wchar_t)0x0197, (wchar_t)0x0196, (wchar_t)0x019C, (wchar_t)0x019D, (wchar_t)0x019F, (wchar_t)0x01A9, (wchar_t)0x01AE, (wchar_t)0x01B1, (wchar_t)0x01B2, - (wchar_t)0x01B7, (wchar_t)0x0386, (wchar_t)0x0388, (wchar_t)0x0389, (wchar_t)0x038A, (wchar_t)0x0391, (wchar_t)0x0392, (wchar_t)0x0393, (wchar_t)0x0394, - (wchar_t)0x0395, (wchar_t)0x0396, (wchar_t)0x0397, (wchar_t)0x0398, (wchar_t)0x0399, (wchar_t)0x039A, (wchar_t)0x039B, (wchar_t)0x039C, (wchar_t)0x039D, - (wchar_t)0x039E, (wchar_t)0x039F, (wchar_t)0x03A0, (wchar_t)0x03A1, (wchar_t)0x03A3, (wchar_t)0x03A4, (wchar_t)0x03A5, (wchar_t)0x03A6, (wchar_t)0x03A7, - (wchar_t)0x03A8, (wchar_t)0x03A9, (wchar_t)0x03AA, (wchar_t)0x03AB, (wchar_t)0x038C, (wchar_t)0x038E, (wchar_t)0x038F, (wchar_t)0x03E2, (wchar_t)0x03E4, - (wchar_t)0x03E6, (wchar_t)0x03E8, (wchar_t)0x03EA, (wchar_t)0x03EC, (wchar_t)0x03EE, (wchar_t)0x0410, (wchar_t)0x0411, (wchar_t)0x0412, (wchar_t)0x0413, - (wchar_t)0x0414, (wchar_t)0x0415, (wchar_t)0x0416, (wchar_t)0x0417, (wchar_t)0x0418, (wchar_t)0x0419, (wchar_t)0x041A, (wchar_t)0x041B, (wchar_t)0x041C, - (wchar_t)0x041D, (wchar_t)0x041E, (wchar_t)0x041F, (wchar_t)0x0420, (wchar_t)0x0421, (wchar_t)0x0422, (wchar_t)0x0423, (wchar_t)0x0424, (wchar_t)0x0425, - (wchar_t)0x0426, (wchar_t)0x0427, (wchar_t)0x0428, (wchar_t)0x0429, (wchar_t)0x042A, (wchar_t)0x042B, (wchar_t)0x042C, (wchar_t)0x042D, (wchar_t)0x042E, - (wchar_t)0x042F, (wchar_t)0x0401, (wchar_t)0x0402, (wchar_t)0x0403, (wchar_t)0x0404, (wchar_t)0x0405, (wchar_t)0x0406, (wchar_t)0x0407, (wchar_t)0x0408, - (wchar_t)0x0409, (wchar_t)0x040A, (wchar_t)0x040B, (wchar_t)0x040C, (wchar_t)0x040E, (wchar_t)0x040F, (wchar_t)0x0460, (wchar_t)0x0462, (wchar_t)0x0464, - (wchar_t)0x0466, (wchar_t)0x0468, (wchar_t)0x046A, (wchar_t)0x046C, (wchar_t)0x046E, (wchar_t)0x0470, (wchar_t)0x0472, (wchar_t)0x0474, (wchar_t)0x0476, - (wchar_t)0x0478, (wchar_t)0x047A, (wchar_t)0x047C, (wchar_t)0x047E, (wchar_t)0x0480, (wchar_t)0x0490, (wchar_t)0x0492, (wchar_t)0x0494, (wchar_t)0x0496, - (wchar_t)0x0498, (wchar_t)0x049A, (wchar_t)0x049C, (wchar_t)0x049E, (wchar_t)0x04A0, (wchar_t)0x04A2, (wchar_t)0x04A4, (wchar_t)0x04A6, (wchar_t)0x04A8, - (wchar_t)0x04AA, (wchar_t)0x04AC, (wchar_t)0x04AE, (wchar_t)0x04B0, (wchar_t)0x04B2, (wchar_t)0x04B4, (wchar_t)0x04B6, (wchar_t)0x04B8, (wchar_t)0x04BA, - (wchar_t)0x04BC, (wchar_t)0x04BE, (wchar_t)0x04C1, (wchar_t)0x04C3, (wchar_t)0x04C7, (wchar_t)0x04CB, (wchar_t)0x04D0, (wchar_t)0x04D2, (wchar_t)0x04D4, - (wchar_t)0x04D6, (wchar_t)0x04D8, (wchar_t)0x04DA, (wchar_t)0x04DC, (wchar_t)0x04DE, (wchar_t)0x04E0, (wchar_t)0x04E2, (wchar_t)0x04E4, (wchar_t)0x04E6, - (wchar_t)0x04E8, (wchar_t)0x04EA, (wchar_t)0x04EE, (wchar_t)0x04F0, (wchar_t)0x04F2, (wchar_t)0x04F4, (wchar_t)0x04F8, (wchar_t)0x0531, (wchar_t)0x0532, - (wchar_t)0x0533, (wchar_t)0x0534, (wchar_t)0x0535, (wchar_t)0x0536, (wchar_t)0x0537, (wchar_t)0x0538, (wchar_t)0x0539, (wchar_t)0x053A, (wchar_t)0x053B, - (wchar_t)0x053C, (wchar_t)0x053D, (wchar_t)0x053E, (wchar_t)0x053F, (wchar_t)0x0540, (wchar_t)0x0541, (wchar_t)0x0542, (wchar_t)0x0543, (wchar_t)0x0544, - (wchar_t)0x0545, (wchar_t)0x0546, (wchar_t)0x0547, (wchar_t)0x0548, (wchar_t)0x0549, (wchar_t)0x054A, (wchar_t)0x054B, (wchar_t)0x054C, (wchar_t)0x054D, - (wchar_t)0x054E, (wchar_t)0x054F, (wchar_t)0x0550, (wchar_t)0x0551, (wchar_t)0x0552, (wchar_t)0x0553, (wchar_t)0x0554, (wchar_t)0x0555, (wchar_t)0x0556, - (wchar_t)0x10A0, (wchar_t)0x10A1, (wchar_t)0x10A2, (wchar_t)0x10A3, (wchar_t)0x10A4, (wchar_t)0x10A5, (wchar_t)0x10A6, (wchar_t)0x10A7, (wchar_t)0x10A8, - (wchar_t)0x10A9, (wchar_t)0x10AA, (wchar_t)0x10AB, (wchar_t)0x10AC, (wchar_t)0x10AD, (wchar_t)0x10AE, (wchar_t)0x10AF, (wchar_t)0x10B0, (wchar_t)0x10B1, - (wchar_t)0x10B2, (wchar_t)0x10B3, (wchar_t)0x10B4, (wchar_t)0x10B5, (wchar_t)0x10B6, (wchar_t)0x10B7, (wchar_t)0x10B8, (wchar_t)0x10B9, (wchar_t)0x10BA, - (wchar_t)0x10BB, (wchar_t)0x10BC, (wchar_t)0x10BD, (wchar_t)0x10BE, (wchar_t)0x10BF, (wchar_t)0x10C0, (wchar_t)0x10C1, (wchar_t)0x10C2, (wchar_t)0x10C3, - (wchar_t)0x10C4, (wchar_t)0x10C5, (wchar_t)0x1E00, (wchar_t)0x1E02, (wchar_t)0x1E04, (wchar_t)0x1E06, (wchar_t)0x1E08, (wchar_t)0x1E0A, (wchar_t)0x1E0C, - (wchar_t)0x1E0E, (wchar_t)0x1E10, (wchar_t)0x1E12, (wchar_t)0x1E14, (wchar_t)0x1E16, (wchar_t)0x1E18, (wchar_t)0x1E1A, (wchar_t)0x1E1C, (wchar_t)0x1E1E, - (wchar_t)0x1E20, (wchar_t)0x1E22, (wchar_t)0x1E24, (wchar_t)0x1E26, (wchar_t)0x1E28, (wchar_t)0x1E2A, (wchar_t)0x1E2C, (wchar_t)0x1E2E, (wchar_t)0x1E30, - (wchar_t)0x1E32, (wchar_t)0x1E34, (wchar_t)0x1E36, (wchar_t)0x1E38, (wchar_t)0x1E3A, (wchar_t)0x1E3C, (wchar_t)0x1E3E, (wchar_t)0x1E40, (wchar_t)0x1E42, - (wchar_t)0x1E44, (wchar_t)0x1E46, (wchar_t)0x1E48, (wchar_t)0x1E4A, (wchar_t)0x1E4C, (wchar_t)0x1E4E, (wchar_t)0x1E50, (wchar_t)0x1E52, (wchar_t)0x1E54, - (wchar_t)0x1E56, (wchar_t)0x1E58, (wchar_t)0x1E5A, (wchar_t)0x1E5C, (wchar_t)0x1E5E, (wchar_t)0x1E60, (wchar_t)0x1E62, (wchar_t)0x1E64, (wchar_t)0x1E66, - (wchar_t)0x1E68, (wchar_t)0x1E6A, (wchar_t)0x1E6C, (wchar_t)0x1E6E, (wchar_t)0x1E70, (wchar_t)0x1E72, (wchar_t)0x1E74, (wchar_t)0x1E76, (wchar_t)0x1E78, - (wchar_t)0x1E7A, (wchar_t)0x1E7C, (wchar_t)0x1E7E, (wchar_t)0x1E80, (wchar_t)0x1E82, (wchar_t)0x1E84, (wchar_t)0x1E86, (wchar_t)0x1E88, (wchar_t)0x1E8A, - (wchar_t)0x1E8C, (wchar_t)0x1E8E, (wchar_t)0x1E90, (wchar_t)0x1E92, (wchar_t)0x1E94, (wchar_t)0x1EA0, (wchar_t)0x1EA2, (wchar_t)0x1EA4, (wchar_t)0x1EA6, - (wchar_t)0x1EA8, (wchar_t)0x1EAA, (wchar_t)0x1EAC, (wchar_t)0x1EAE, (wchar_t)0x1EB0, (wchar_t)0x1EB2, (wchar_t)0x1EB4, (wchar_t)0x1EB6, (wchar_t)0x1EB8, - (wchar_t)0x1EBA, (wchar_t)0x1EBC, (wchar_t)0x1EBE, (wchar_t)0x1EC0, (wchar_t)0x1EC2, (wchar_t)0x1EC4, (wchar_t)0x1EC6, (wchar_t)0x1EC8, (wchar_t)0x1ECA, - (wchar_t)0x1ECC, (wchar_t)0x1ECE, (wchar_t)0x1ED0, (wchar_t)0x1ED2, (wchar_t)0x1ED4, (wchar_t)0x1ED6, (wchar_t)0x1ED8, (wchar_t)0x1EDA, (wchar_t)0x1EDC, - (wchar_t)0x1EDE, (wchar_t)0x1EE0, (wchar_t)0x1EE2, (wchar_t)0x1EE4, (wchar_t)0x1EE6, (wchar_t)0x1EE8, (wchar_t)0x1EEA, (wchar_t)0x1EEC, (wchar_t)0x1EEE, - (wchar_t)0x1EF0, (wchar_t)0x1EF2, (wchar_t)0x1EF4, (wchar_t)0x1EF6, (wchar_t)0x1EF8, (wchar_t)0x1F08, (wchar_t)0x1F09, (wchar_t)0x1F0A, (wchar_t)0x1F0B, - (wchar_t)0x1F0C, (wchar_t)0x1F0D, (wchar_t)0x1F0E, (wchar_t)0x1F0F, (wchar_t)0x1F18, (wchar_t)0x1F19, (wchar_t)0x1F1A, (wchar_t)0x1F1B, (wchar_t)0x1F1C, - (wchar_t)0x1F1D, (wchar_t)0x1F28, (wchar_t)0x1F29, (wchar_t)0x1F2A, (wchar_t)0x1F2B, (wchar_t)0x1F2C, (wchar_t)0x1F2D, (wchar_t)0x1F2E, (wchar_t)0x1F2F, - (wchar_t)0x1F38, (wchar_t)0x1F39, (wchar_t)0x1F3A, (wchar_t)0x1F3B, (wchar_t)0x1F3C, (wchar_t)0x1F3D, (wchar_t)0x1F3E, (wchar_t)0x1F3F, (wchar_t)0x1F48, - (wchar_t)0x1F49, (wchar_t)0x1F4A, (wchar_t)0x1F4B, (wchar_t)0x1F4C, (wchar_t)0x1F4D, (wchar_t)0x1F59, (wchar_t)0x1F5B, (wchar_t)0x1F5D, (wchar_t)0x1F5F, - (wchar_t)0x1F68, (wchar_t)0x1F69, (wchar_t)0x1F6A, (wchar_t)0x1F6B, (wchar_t)0x1F6C, (wchar_t)0x1F6D, (wchar_t)0x1F6E, (wchar_t)0x1F6F, (wchar_t)0x1F88, - (wchar_t)0x1F89, (wchar_t)0x1F8A, (wchar_t)0x1F8B, (wchar_t)0x1F8C, (wchar_t)0x1F8D, (wchar_t)0x1F8E, (wchar_t)0x1F8F, (wchar_t)0x1F98, (wchar_t)0x1F99, - (wchar_t)0x1F9A, (wchar_t)0x1F9B, (wchar_t)0x1F9C, (wchar_t)0x1F9D, (wchar_t)0x1F9E, (wchar_t)0x1F9F, (wchar_t)0x1FA8, (wchar_t)0x1FA9, (wchar_t)0x1FAA, - (wchar_t)0x1FAB, (wchar_t)0x1FAC, (wchar_t)0x1FAD, (wchar_t)0x1FAE, (wchar_t)0x1FAF, (wchar_t)0x1FB8, (wchar_t)0x1FB9, (wchar_t)0x1FD8, (wchar_t)0x1FD9, - (wchar_t)0x1FE8, (wchar_t)0x1FE9, (wchar_t)0x24B6, (wchar_t)0x24B7, (wchar_t)0x24B8, (wchar_t)0x24B9, (wchar_t)0x24BA, (wchar_t)0x24BB, (wchar_t)0x24BC, - (wchar_t)0x24BD, (wchar_t)0x24BE, (wchar_t)0x24BF, (wchar_t)0x24C0, (wchar_t)0x24C1, (wchar_t)0x24C2, (wchar_t)0x24C3, (wchar_t)0x24C4, (wchar_t)0x24C5, - (wchar_t)0x24C6, (wchar_t)0x24C7, (wchar_t)0x24C8, (wchar_t)0x24C9, (wchar_t)0x24CA, (wchar_t)0x24CB, (wchar_t)0x24CC, (wchar_t)0x24CD, (wchar_t)0x24CE, - (wchar_t)0x24CF, (wchar_t)0xFF21, (wchar_t)0xFF22, (wchar_t)0xFF23, (wchar_t)0xFF24, (wchar_t)0xFF25, (wchar_t)0xFF26, (wchar_t)0xFF27, (wchar_t)0xFF28, - (wchar_t)0xFF29, (wchar_t)0xFF2A, (wchar_t)0xFF2B, (wchar_t)0xFF2C, (wchar_t)0xFF2D, (wchar_t)0xFF2E, (wchar_t)0xFF2F, (wchar_t)0xFF30, (wchar_t)0xFF31, - (wchar_t)0xFF32, (wchar_t)0xFF33, (wchar_t)0xFF34, (wchar_t)0xFF35, (wchar_t)0xFF36, (wchar_t)0xFF37, (wchar_t)0xFF38, (wchar_t)0xFF39, (wchar_t)0xFF3A + static wchar_t unicodeUppercase[] = { + (wchar_t)0x0041, (wchar_t)0x0042, (wchar_t)0x0043, (wchar_t)0x0044, (wchar_t)0x0045, + (wchar_t)0x0046, (wchar_t)0x0047, (wchar_t)0x0048, (wchar_t)0x0049, (wchar_t)0x004A, + (wchar_t)0x004B, (wchar_t)0x004C, (wchar_t)0x004D, (wchar_t)0x004E, (wchar_t)0x004F, + (wchar_t)0x0050, (wchar_t)0x0051, (wchar_t)0x0052, (wchar_t)0x0053, (wchar_t)0x0054, + (wchar_t)0x0055, (wchar_t)0x0056, (wchar_t)0x0057, (wchar_t)0x0058, (wchar_t)0x0059, + (wchar_t)0x005A, (wchar_t)0x00C0, (wchar_t)0x00C1, (wchar_t)0x00C2, (wchar_t)0x00C3, + (wchar_t)0x00C4, (wchar_t)0x00C5, (wchar_t)0x00C6, (wchar_t)0x00C7, (wchar_t)0x00C8, + (wchar_t)0x00C9, (wchar_t)0x00CA, (wchar_t)0x00CB, (wchar_t)0x00CC, (wchar_t)0x00CD, + (wchar_t)0x00CE, (wchar_t)0x00CF, (wchar_t)0x00D0, (wchar_t)0x00D1, (wchar_t)0x00D2, + (wchar_t)0x00D3, (wchar_t)0x00D4, (wchar_t)0x00D5, (wchar_t)0x00D6, (wchar_t)0x00D8, + (wchar_t)0x00D9, (wchar_t)0x00DA, (wchar_t)0x00DB, (wchar_t)0x00DC, (wchar_t)0x00DD, + (wchar_t)0x00DE, (wchar_t)0x0178, (wchar_t)0x0100, (wchar_t)0x0102, (wchar_t)0x0104, + (wchar_t)0x0106, (wchar_t)0x0108, (wchar_t)0x010A, (wchar_t)0x010C, (wchar_t)0x010E, + (wchar_t)0x0110, (wchar_t)0x0112, (wchar_t)0x0114, (wchar_t)0x0116, (wchar_t)0x0118, + (wchar_t)0x011A, (wchar_t)0x011C, (wchar_t)0x011E, (wchar_t)0x0120, (wchar_t)0x0122, + (wchar_t)0x0124, (wchar_t)0x0126, (wchar_t)0x0128, (wchar_t)0x012A, (wchar_t)0x012C, + (wchar_t)0x012E, (wchar_t)0x0049, (wchar_t)0x0132, (wchar_t)0x0134, (wchar_t)0x0136, + (wchar_t)0x0139, (wchar_t)0x013B, (wchar_t)0x013D, (wchar_t)0x013F, (wchar_t)0x0141, + (wchar_t)0x0143, (wchar_t)0x0145, (wchar_t)0x0147, (wchar_t)0x014A, (wchar_t)0x014C, + (wchar_t)0x014E, (wchar_t)0x0150, (wchar_t)0x0152, (wchar_t)0x0154, (wchar_t)0x0156, + (wchar_t)0x0158, (wchar_t)0x015A, (wchar_t)0x015C, (wchar_t)0x015E, (wchar_t)0x0160, + (wchar_t)0x0162, (wchar_t)0x0164, (wchar_t)0x0166, (wchar_t)0x0168, (wchar_t)0x016A, + (wchar_t)0x016C, (wchar_t)0x016E, (wchar_t)0x0170, (wchar_t)0x0172, (wchar_t)0x0174, + (wchar_t)0x0176, (wchar_t)0x0179, (wchar_t)0x017B, (wchar_t)0x017D, (wchar_t)0x0182, + (wchar_t)0x0184, (wchar_t)0x0187, (wchar_t)0x018B, (wchar_t)0x0191, (wchar_t)0x0198, + (wchar_t)0x01A0, (wchar_t)0x01A2, (wchar_t)0x01A4, (wchar_t)0x01A7, (wchar_t)0x01AC, + (wchar_t)0x01AF, (wchar_t)0x01B3, (wchar_t)0x01B5, (wchar_t)0x01B8, (wchar_t)0x01BC, + (wchar_t)0x01C4, (wchar_t)0x01C7, (wchar_t)0x01CA, (wchar_t)0x01CD, (wchar_t)0x01CF, + (wchar_t)0x01D1, (wchar_t)0x01D3, (wchar_t)0x01D5, (wchar_t)0x01D7, (wchar_t)0x01D9, + (wchar_t)0x01DB, (wchar_t)0x01DE, (wchar_t)0x01E0, (wchar_t)0x01E2, (wchar_t)0x01E4, + (wchar_t)0x01E6, (wchar_t)0x01E8, (wchar_t)0x01EA, (wchar_t)0x01EC, (wchar_t)0x01EE, + (wchar_t)0x01F1, (wchar_t)0x01F4, (wchar_t)0x01FA, (wchar_t)0x01FC, (wchar_t)0x01FE, + (wchar_t)0x0200, (wchar_t)0x0202, (wchar_t)0x0204, (wchar_t)0x0206, (wchar_t)0x0208, + (wchar_t)0x020A, (wchar_t)0x020C, (wchar_t)0x020E, (wchar_t)0x0210, (wchar_t)0x0212, + (wchar_t)0x0214, (wchar_t)0x0216, (wchar_t)0x0181, (wchar_t)0x0186, (wchar_t)0x018A, + (wchar_t)0x018E, (wchar_t)0x018F, (wchar_t)0x0190, (wchar_t)0x0193, (wchar_t)0x0194, + (wchar_t)0x0197, (wchar_t)0x0196, (wchar_t)0x019C, (wchar_t)0x019D, (wchar_t)0x019F, + (wchar_t)0x01A9, (wchar_t)0x01AE, (wchar_t)0x01B1, (wchar_t)0x01B2, (wchar_t)0x01B7, + (wchar_t)0x0386, (wchar_t)0x0388, (wchar_t)0x0389, (wchar_t)0x038A, (wchar_t)0x0391, + (wchar_t)0x0392, (wchar_t)0x0393, (wchar_t)0x0394, (wchar_t)0x0395, (wchar_t)0x0396, + (wchar_t)0x0397, (wchar_t)0x0398, (wchar_t)0x0399, (wchar_t)0x039A, (wchar_t)0x039B, + (wchar_t)0x039C, (wchar_t)0x039D, (wchar_t)0x039E, (wchar_t)0x039F, (wchar_t)0x03A0, + (wchar_t)0x03A1, (wchar_t)0x03A3, (wchar_t)0x03A4, (wchar_t)0x03A5, (wchar_t)0x03A6, + (wchar_t)0x03A7, (wchar_t)0x03A8, (wchar_t)0x03A9, (wchar_t)0x03AA, (wchar_t)0x03AB, + (wchar_t)0x038C, (wchar_t)0x038E, (wchar_t)0x038F, (wchar_t)0x03E2, (wchar_t)0x03E4, + (wchar_t)0x03E6, (wchar_t)0x03E8, (wchar_t)0x03EA, (wchar_t)0x03EC, (wchar_t)0x03EE, + (wchar_t)0x0410, (wchar_t)0x0411, (wchar_t)0x0412, (wchar_t)0x0413, (wchar_t)0x0414, + (wchar_t)0x0415, (wchar_t)0x0416, (wchar_t)0x0417, (wchar_t)0x0418, (wchar_t)0x0419, + (wchar_t)0x041A, (wchar_t)0x041B, (wchar_t)0x041C, (wchar_t)0x041D, (wchar_t)0x041E, + (wchar_t)0x041F, (wchar_t)0x0420, (wchar_t)0x0421, (wchar_t)0x0422, (wchar_t)0x0423, + (wchar_t)0x0424, (wchar_t)0x0425, (wchar_t)0x0426, (wchar_t)0x0427, (wchar_t)0x0428, + (wchar_t)0x0429, (wchar_t)0x042A, (wchar_t)0x042B, (wchar_t)0x042C, (wchar_t)0x042D, + (wchar_t)0x042E, (wchar_t)0x042F, (wchar_t)0x0401, (wchar_t)0x0402, (wchar_t)0x0403, + (wchar_t)0x0404, (wchar_t)0x0405, (wchar_t)0x0406, (wchar_t)0x0407, (wchar_t)0x0408, + (wchar_t)0x0409, (wchar_t)0x040A, (wchar_t)0x040B, (wchar_t)0x040C, (wchar_t)0x040E, + (wchar_t)0x040F, (wchar_t)0x0460, (wchar_t)0x0462, (wchar_t)0x0464, (wchar_t)0x0466, + (wchar_t)0x0468, (wchar_t)0x046A, (wchar_t)0x046C, (wchar_t)0x046E, (wchar_t)0x0470, + (wchar_t)0x0472, (wchar_t)0x0474, (wchar_t)0x0476, (wchar_t)0x0478, (wchar_t)0x047A, + (wchar_t)0x047C, (wchar_t)0x047E, (wchar_t)0x0480, (wchar_t)0x0490, (wchar_t)0x0492, + (wchar_t)0x0494, (wchar_t)0x0496, (wchar_t)0x0498, (wchar_t)0x049A, (wchar_t)0x049C, + (wchar_t)0x049E, (wchar_t)0x04A0, (wchar_t)0x04A2, (wchar_t)0x04A4, (wchar_t)0x04A6, + (wchar_t)0x04A8, (wchar_t)0x04AA, (wchar_t)0x04AC, (wchar_t)0x04AE, (wchar_t)0x04B0, + (wchar_t)0x04B2, (wchar_t)0x04B4, (wchar_t)0x04B6, (wchar_t)0x04B8, (wchar_t)0x04BA, + (wchar_t)0x04BC, (wchar_t)0x04BE, (wchar_t)0x04C1, (wchar_t)0x04C3, (wchar_t)0x04C7, + (wchar_t)0x04CB, (wchar_t)0x04D0, (wchar_t)0x04D2, (wchar_t)0x04D4, (wchar_t)0x04D6, + (wchar_t)0x04D8, (wchar_t)0x04DA, (wchar_t)0x04DC, (wchar_t)0x04DE, (wchar_t)0x04E0, + (wchar_t)0x04E2, (wchar_t)0x04E4, (wchar_t)0x04E6, (wchar_t)0x04E8, (wchar_t)0x04EA, + (wchar_t)0x04EE, (wchar_t)0x04F0, (wchar_t)0x04F2, (wchar_t)0x04F4, (wchar_t)0x04F8, + (wchar_t)0x0531, (wchar_t)0x0532, (wchar_t)0x0533, (wchar_t)0x0534, (wchar_t)0x0535, + (wchar_t)0x0536, (wchar_t)0x0537, (wchar_t)0x0538, (wchar_t)0x0539, (wchar_t)0x053A, + (wchar_t)0x053B, (wchar_t)0x053C, (wchar_t)0x053D, (wchar_t)0x053E, (wchar_t)0x053F, + (wchar_t)0x0540, (wchar_t)0x0541, (wchar_t)0x0542, (wchar_t)0x0543, (wchar_t)0x0544, + (wchar_t)0x0545, (wchar_t)0x0546, (wchar_t)0x0547, (wchar_t)0x0548, (wchar_t)0x0549, + (wchar_t)0x054A, (wchar_t)0x054B, (wchar_t)0x054C, (wchar_t)0x054D, (wchar_t)0x054E, + (wchar_t)0x054F, (wchar_t)0x0550, (wchar_t)0x0551, (wchar_t)0x0552, (wchar_t)0x0553, + (wchar_t)0x0554, (wchar_t)0x0555, (wchar_t)0x0556, (wchar_t)0x10A0, (wchar_t)0x10A1, + (wchar_t)0x10A2, (wchar_t)0x10A3, (wchar_t)0x10A4, (wchar_t)0x10A5, (wchar_t)0x10A6, + (wchar_t)0x10A7, (wchar_t)0x10A8, (wchar_t)0x10A9, (wchar_t)0x10AA, (wchar_t)0x10AB, + (wchar_t)0x10AC, (wchar_t)0x10AD, (wchar_t)0x10AE, (wchar_t)0x10AF, (wchar_t)0x10B0, + (wchar_t)0x10B1, (wchar_t)0x10B2, (wchar_t)0x10B3, (wchar_t)0x10B4, (wchar_t)0x10B5, + (wchar_t)0x10B6, (wchar_t)0x10B7, (wchar_t)0x10B8, (wchar_t)0x10B9, (wchar_t)0x10BA, + (wchar_t)0x10BB, (wchar_t)0x10BC, (wchar_t)0x10BD, (wchar_t)0x10BE, (wchar_t)0x10BF, + (wchar_t)0x10C0, (wchar_t)0x10C1, (wchar_t)0x10C2, (wchar_t)0x10C3, (wchar_t)0x10C4, + (wchar_t)0x10C5, (wchar_t)0x1E00, (wchar_t)0x1E02, (wchar_t)0x1E04, (wchar_t)0x1E06, + (wchar_t)0x1E08, (wchar_t)0x1E0A, (wchar_t)0x1E0C, (wchar_t)0x1E0E, (wchar_t)0x1E10, + (wchar_t)0x1E12, (wchar_t)0x1E14, (wchar_t)0x1E16, (wchar_t)0x1E18, (wchar_t)0x1E1A, + (wchar_t)0x1E1C, (wchar_t)0x1E1E, (wchar_t)0x1E20, (wchar_t)0x1E22, (wchar_t)0x1E24, + (wchar_t)0x1E26, (wchar_t)0x1E28, (wchar_t)0x1E2A, (wchar_t)0x1E2C, (wchar_t)0x1E2E, + (wchar_t)0x1E30, (wchar_t)0x1E32, (wchar_t)0x1E34, (wchar_t)0x1E36, (wchar_t)0x1E38, + (wchar_t)0x1E3A, (wchar_t)0x1E3C, (wchar_t)0x1E3E, (wchar_t)0x1E40, (wchar_t)0x1E42, + (wchar_t)0x1E44, (wchar_t)0x1E46, (wchar_t)0x1E48, (wchar_t)0x1E4A, (wchar_t)0x1E4C, + (wchar_t)0x1E4E, (wchar_t)0x1E50, (wchar_t)0x1E52, (wchar_t)0x1E54, (wchar_t)0x1E56, + (wchar_t)0x1E58, (wchar_t)0x1E5A, (wchar_t)0x1E5C, (wchar_t)0x1E5E, (wchar_t)0x1E60, + (wchar_t)0x1E62, (wchar_t)0x1E64, (wchar_t)0x1E66, (wchar_t)0x1E68, (wchar_t)0x1E6A, + (wchar_t)0x1E6C, (wchar_t)0x1E6E, (wchar_t)0x1E70, (wchar_t)0x1E72, (wchar_t)0x1E74, + (wchar_t)0x1E76, (wchar_t)0x1E78, (wchar_t)0x1E7A, (wchar_t)0x1E7C, (wchar_t)0x1E7E, + (wchar_t)0x1E80, (wchar_t)0x1E82, (wchar_t)0x1E84, (wchar_t)0x1E86, (wchar_t)0x1E88, + (wchar_t)0x1E8A, (wchar_t)0x1E8C, (wchar_t)0x1E8E, (wchar_t)0x1E90, (wchar_t)0x1E92, + (wchar_t)0x1E94, (wchar_t)0x1EA0, (wchar_t)0x1EA2, (wchar_t)0x1EA4, (wchar_t)0x1EA6, + (wchar_t)0x1EA8, (wchar_t)0x1EAA, (wchar_t)0x1EAC, (wchar_t)0x1EAE, (wchar_t)0x1EB0, + (wchar_t)0x1EB2, (wchar_t)0x1EB4, (wchar_t)0x1EB6, (wchar_t)0x1EB8, (wchar_t)0x1EBA, + (wchar_t)0x1EBC, (wchar_t)0x1EBE, (wchar_t)0x1EC0, (wchar_t)0x1EC2, (wchar_t)0x1EC4, + (wchar_t)0x1EC6, (wchar_t)0x1EC8, (wchar_t)0x1ECA, (wchar_t)0x1ECC, (wchar_t)0x1ECE, + (wchar_t)0x1ED0, (wchar_t)0x1ED2, (wchar_t)0x1ED4, (wchar_t)0x1ED6, (wchar_t)0x1ED8, + (wchar_t)0x1EDA, (wchar_t)0x1EDC, (wchar_t)0x1EDE, (wchar_t)0x1EE0, (wchar_t)0x1EE2, + (wchar_t)0x1EE4, (wchar_t)0x1EE6, (wchar_t)0x1EE8, (wchar_t)0x1EEA, (wchar_t)0x1EEC, + (wchar_t)0x1EEE, (wchar_t)0x1EF0, (wchar_t)0x1EF2, (wchar_t)0x1EF4, (wchar_t)0x1EF6, + (wchar_t)0x1EF8, (wchar_t)0x1F08, (wchar_t)0x1F09, (wchar_t)0x1F0A, (wchar_t)0x1F0B, + (wchar_t)0x1F0C, (wchar_t)0x1F0D, (wchar_t)0x1F0E, (wchar_t)0x1F0F, (wchar_t)0x1F18, + (wchar_t)0x1F19, (wchar_t)0x1F1A, (wchar_t)0x1F1B, (wchar_t)0x1F1C, (wchar_t)0x1F1D, + (wchar_t)0x1F28, (wchar_t)0x1F29, (wchar_t)0x1F2A, (wchar_t)0x1F2B, (wchar_t)0x1F2C, + (wchar_t)0x1F2D, (wchar_t)0x1F2E, (wchar_t)0x1F2F, (wchar_t)0x1F38, (wchar_t)0x1F39, + (wchar_t)0x1F3A, (wchar_t)0x1F3B, (wchar_t)0x1F3C, (wchar_t)0x1F3D, (wchar_t)0x1F3E, + (wchar_t)0x1F3F, (wchar_t)0x1F48, (wchar_t)0x1F49, (wchar_t)0x1F4A, (wchar_t)0x1F4B, + (wchar_t)0x1F4C, (wchar_t)0x1F4D, (wchar_t)0x1F59, (wchar_t)0x1F5B, (wchar_t)0x1F5D, + (wchar_t)0x1F5F, (wchar_t)0x1F68, (wchar_t)0x1F69, (wchar_t)0x1F6A, (wchar_t)0x1F6B, + (wchar_t)0x1F6C, (wchar_t)0x1F6D, (wchar_t)0x1F6E, (wchar_t)0x1F6F, (wchar_t)0x1F88, + (wchar_t)0x1F89, (wchar_t)0x1F8A, (wchar_t)0x1F8B, (wchar_t)0x1F8C, (wchar_t)0x1F8D, + (wchar_t)0x1F8E, (wchar_t)0x1F8F, (wchar_t)0x1F98, (wchar_t)0x1F99, (wchar_t)0x1F9A, + (wchar_t)0x1F9B, (wchar_t)0x1F9C, (wchar_t)0x1F9D, (wchar_t)0x1F9E, (wchar_t)0x1F9F, + (wchar_t)0x1FA8, (wchar_t)0x1FA9, (wchar_t)0x1FAA, (wchar_t)0x1FAB, (wchar_t)0x1FAC, + (wchar_t)0x1FAD, (wchar_t)0x1FAE, (wchar_t)0x1FAF, (wchar_t)0x1FB8, (wchar_t)0x1FB9, + (wchar_t)0x1FD8, (wchar_t)0x1FD9, (wchar_t)0x1FE8, (wchar_t)0x1FE9, (wchar_t)0x24B6, + (wchar_t)0x24B7, (wchar_t)0x24B8, (wchar_t)0x24B9, (wchar_t)0x24BA, (wchar_t)0x24BB, + (wchar_t)0x24BC, (wchar_t)0x24BD, (wchar_t)0x24BE, (wchar_t)0x24BF, (wchar_t)0x24C0, + (wchar_t)0x24C1, (wchar_t)0x24C2, (wchar_t)0x24C3, (wchar_t)0x24C4, (wchar_t)0x24C5, + (wchar_t)0x24C6, (wchar_t)0x24C7, (wchar_t)0x24C8, (wchar_t)0x24C9, (wchar_t)0x24CA, + (wchar_t)0x24CB, (wchar_t)0x24CC, (wchar_t)0x24CD, (wchar_t)0x24CE, (wchar_t)0x24CF, + (wchar_t)0xFF21, (wchar_t)0xFF22, (wchar_t)0xFF23, (wchar_t)0xFF24, (wchar_t)0xFF25, + (wchar_t)0xFF26, (wchar_t)0xFF27, (wchar_t)0xFF28, (wchar_t)0xFF29, (wchar_t)0xFF2A, + (wchar_t)0xFF2B, (wchar_t)0xFF2C, (wchar_t)0xFF2D, (wchar_t)0xFF2E, (wchar_t)0xFF2F, + (wchar_t)0xFF30, (wchar_t)0xFF31, (wchar_t)0xFF32, (wchar_t)0xFF33, (wchar_t)0xFF34, + (wchar_t)0xFF35, (wchar_t)0xFF36, (wchar_t)0xFF37, (wchar_t)0xFF38, (wchar_t)0xFF39, + (wchar_t)0xFF3A }; unsigned int chars2Unicode(const std::string& stringArg, size_t& cursor) @@ -183,27 +301,27 @@ namespace Utils // 0xxxxxxx, one byte character. if (checkCharType <= 0x7F) { // 0xxxxxxx - result = ((stringArg[cursor++])); + result = (stringArg[cursor++]); } // 11110xxx, four byte character. else if (checkCharType >= 0xF0) { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx result = (stringArg[cursor++] & 0x07) << 18; result |= (stringArg[cursor++] & 0x3F) << 12; - result |= (stringArg[cursor++] & 0x3F) << 6; + result |= (stringArg[cursor++] & 0x3F) << 6; result |= stringArg[cursor++] & 0x3F; } // 1110xxxx, three byte character. else if (checkCharType >= 0xE0) { // 1110xxxx 10xxxxxx 10xxxxxx result = (stringArg[cursor++] & 0x0F) << 12; - result |= (stringArg[cursor++] & 0x3F) << 6; + result |= (stringArg[cursor++] & 0x3F) << 6; result |= stringArg[cursor++] & 0x3F; } // 110xxxxx, two byte character. else if (checkCharType >= 0xC0) { // 110xxxxx 10xxxxxx - result = (stringArg[cursor++] & 0x1F) << 6; + result = (stringArg[cursor++] & 0x1F) << 6; result |= stringArg[cursor++] & 0x3F; } else { @@ -220,25 +338,25 @@ namespace Utils // Normal UTF-8 ASCII character. if (unicodeArg < 0x80) { - result += ((unicodeArg ) & 0xFF); + result += (unicodeArg & 0xFF); } // Two-byte character. - else if (unicodeArg < 0x800) { - result += ((unicodeArg >> 6) & 0xFF) | 0xC0; - result += ((unicodeArg ) & 0x3F) | 0x80; + else if (unicodeArg < 0x800) { + result += ((unicodeArg >> 6) & 0xFF) | 0xC0; + result += (unicodeArg & 0x3F) | 0x80; } // Three-byte character. else if (unicodeArg < 0xFFFF) { result += ((unicodeArg >> 12) & 0xFF) | 0xE0; - result += ((unicodeArg >> 6) & 0x3F) | 0x80; - result += ((unicodeArg ) & 0x3F) | 0x80; + result += ((unicodeArg >> 6) & 0x3F) | 0x80; + result += (unicodeArg & 0x3F) | 0x80; } // Four-byte character. else if (unicodeArg <= 0x1fffff) { result += ((unicodeArg >> 18) & 0xFF) | 0xF0; result += ((unicodeArg >> 12) & 0x3F) | 0x80; - result += ((unicodeArg >> 6) & 0x3F) | 0x80; - result += ((unicodeArg ) & 0x3F) | 0x80; + result += ((unicodeArg >> 6) & 0x3F) | 0x80; + result += (unicodeArg & 0x3F) | 0x80; } else { // Error, invalid character. @@ -432,15 +550,16 @@ namespace Utils return stringArg.substr(strBegin, strEnd - strBegin + 1); } - std::string replace(const std::string& stringArg, const std::string& replace, - const std::string& with) + std::string replace(const std::string& stringArg, + const std::string& replace, + const std::string& with) { std::string stringReplace = stringArg; size_t pos; while ((pos = stringReplace.find(replace)) != std::string::npos) - stringReplace = stringReplace.replace(pos, replace.length(), - with.c_str(), with.length()); + stringReplace = + stringReplace.replace(pos, replace.length(), with.c_str(), with.length()); return stringReplace; } @@ -497,7 +616,9 @@ namespace Utils } std::vector delimitedStringToVector(const std::string& stringArg, - const std::string& delimiter, bool sort, bool caseInsensitive) + const std::string& delimiter, + bool sort, + bool caseInsensitive) { std::vector vectorResult; size_t start = 0; @@ -513,8 +634,9 @@ namespace Utils if (sort) { if (caseInsensitive) std::sort(std::begin(vectorResult), std::end(vectorResult), - [](std::string a, std::string b) { - return std::toupper(a.front()) < std::toupper(b.front()); }); + [](std::string a, std::string b) { + return std::toupper(a.front()) < std::toupper(b.front()); + }); else std::sort(vectorResult.begin(), vectorResult.end()); } @@ -523,19 +645,23 @@ namespace Utils } std::string vectorToDelimitedString(std::vector vectorArg, - const std::string& delimiter, bool caseInsensitive) + const std::string& delimiter, + bool caseInsensitive) { std::string resultString; - if (caseInsensitive) + if (caseInsensitive) { std::sort(std::begin(vectorArg), std::end(vectorArg), - [](std::string a, std::string b) { - return std::toupper(a.front()) < std::toupper(b.front()); }); - else + [](std::string a, std::string b) { + return std::toupper(a.front()) < std::toupper(b.front()); + }); + } + else { std::sort(vectorArg.begin(), vectorArg.end()); + } for (std::vector::const_iterator it = vectorArg.cbegin(); - it != vectorArg.cend(); it++) + it != vectorArg.cend(); it++) resultString += (resultString.length() ? delimiter : "") + (*it); return resultString; @@ -551,6 +677,6 @@ namespace Utils return buffer; } - } // String:: + } // namespace String -} // Utils:: +} // namespace Utils diff --git a/es-core/src/utils/StringUtil.h b/es-core/src/utils/StringUtil.h index 92e39d181..98b1ecb23 100644 --- a/es-core/src/utils/StringUtil.h +++ b/es-core/src/utils/StringUtil.h @@ -27,19 +27,24 @@ namespace Utils std::string toLower(const std::string& stringArg); std::string toUpper(const std::string& stringArg); std::string trim(const std::string& stringArg); - std::string replace(const std::string& stringArg, const std::string& replace, - const std::string& with); + std::string replace(const std::string& stringArg, + const std::string& replace, + const std::string& with); std::wstring stringToWideString(const std::string& stringArg); std::string wideStringToString(const std::wstring& stringArg); bool startsWith(const std::string& stringArg, const std::string& start); bool endsWith(const std::string& stringArg, const std::string& end); std::string removeParenthesis(const std::string& stringArg); std::vector delimitedStringToVector(const std::string& stringArg, - const std::string& delimiter, bool sort = false, bool caseInsensitive = false); + const std::string& delimiter, + bool sort = false, + bool caseInsensitive = false); std::string vectorToDelimitedString(std::vector vectorArg, - const std::string& delimiter, bool caseInsensitive = false); + const std::string& delimiter, + bool caseInsensitive = false); std::string scramble(const std::string& input, const std::string& key); - } -} + } // namespace String + +} // namespace Utils #endif // ES_CORE_UTILS_STRING_UTIL_H diff --git a/es-core/src/utils/TimeUtil.cpp b/es-core/src/utils/TimeUtil.cpp index 0830615c5..6d4a46cb1 100644 --- a/es-core/src/utils/TimeUtil.cpp +++ b/es-core/src/utils/TimeUtil.cpp @@ -24,41 +24,42 @@ namespace Utils DateTime::DateTime(const time_t& time) { + // Set time. setTime(time); } DateTime::DateTime(const tm& timeStruct) { + // Set time struct. setTimeStruct(timeStruct); } DateTime::DateTime(const std::string& isoString) { + // Set ISO string. setIsoString(isoString); } - DateTime::~DateTime() - { - } - void DateTime::setTime(const time_t& time) { mTime = (time < 0) ? 0 : time; - #if defined(_WIN64) +#if defined(_WIN64) localtime_s(&mTimeStruct, &mTime); - #else +#else localtime_r(&mTime, &mTimeStruct); - #endif +#endif mIsoString = timeToString(mTime); } void DateTime::setTimeStruct(const tm& timeStruct) { + // Set time based on struct. setTime(mktime(const_cast(&timeStruct))); } void DateTime::setIsoString(const std::string& isoString) { + // Set time based on ISO string. setTime(stringToTime(isoString)); } @@ -71,10 +72,6 @@ namespace Utils mSeconds = mTotalSeconds % 60; } - Duration::~Duration() - { - } - time_t now() { time_t time; @@ -86,7 +83,7 @@ namespace Utils { const char* s = string.c_str(); const char* f = format.c_str(); - tm timeStruct = { 0, 0, 0, 1, 0, 0, 0, 0, -1 }; + tm timeStruct = { 0, 0, 0, 1, 0, 0, 0, 0, -1 }; size_t parsedChars = 0; if (string == "19700101T010000") @@ -97,71 +94,65 @@ namespace Utils f++; switch (*f++) { - // Year, including century [1970,xxxx] - case 'Y': { - if ((parsedChars + 4) <= string.length()) { - timeStruct.tm_year = (*s++ - '0') * 1000; - timeStruct.tm_year += (*s++ - '0') * 100; - timeStruct.tm_year += (*s++ - '0') * 10; - timeStruct.tm_year += (*s++ - '0'); - if (timeStruct.tm_year >= 1900) - timeStruct.tm_year -= 1900; - } - parsedChars += 4; - } - break; + // Year, including century [1970,xxxx] + case 'Y': { + if ((parsedChars + 4) <= string.length()) { + timeStruct.tm_year = (*s++ - '0') * 1000; + timeStruct.tm_year += (*s++ - '0') * 100; + timeStruct.tm_year += (*s++ - '0') * 10; + timeStruct.tm_year += (*s++ - '0'); + if (timeStruct.tm_year >= 1900) + timeStruct.tm_year -= 1900; + } + parsedChars += 4; + } break; - // Month number [01,12] - case 'm': { - if ((parsedChars + 2) <= string.length()) { - timeStruct.tm_mon = (*s++ - '0') * 10; - timeStruct.tm_mon += (*s++ - '0'); - if (timeStruct.tm_mon >= 1) - timeStruct.tm_mon -= 1; - } - parsedChars += 2; - } - break; + // Month number [01,12] + case 'm': { + if ((parsedChars + 2) <= string.length()) { + timeStruct.tm_mon = (*s++ - '0') * 10; + timeStruct.tm_mon += (*s++ - '0'); + if (timeStruct.tm_mon >= 1) + timeStruct.tm_mon -= 1; + } + parsedChars += 2; + } break; - // Day of the month [01,31] - case 'd': { - if ((parsedChars + 2) <= string.length()) { - timeStruct.tm_mday = (*s++ - '0') * 10; - timeStruct.tm_mday += (*s++ - '0'); - } - parsedChars += 2; - } - break; + // Day of the month [01,31] + case 'd': { + if ((parsedChars + 2) <= string.length()) { + timeStruct.tm_mday = (*s++ - '0') * 10; + timeStruct.tm_mday += (*s++ - '0'); + } + parsedChars += 2; + } break; - // Hour (24-hour clock) [00,23] - case 'H': { - if ((parsedChars + 2) <= string.length()) { - timeStruct.tm_hour = (*s++ - '0') * 10; - timeStruct.tm_hour += (*s++ - '0'); - } - parsedChars += 2; - } - break; + // Hour (24-hour clock) [00,23] + case 'H': { + if ((parsedChars + 2) <= string.length()) { + timeStruct.tm_hour = (*s++ - '0') * 10; + timeStruct.tm_hour += (*s++ - '0'); + } + parsedChars += 2; + } break; - // Minute [00,59] - case 'M': { - if ((parsedChars + 2) <= string.length()) { - timeStruct.tm_min = (*s++ - '0') * 10; - timeStruct.tm_min += (*s++ - '0'); - } - parsedChars += 2; - } - break; + // Minute [00,59] + case 'M': { + if ((parsedChars + 2) <= string.length()) { + timeStruct.tm_min = (*s++ - '0') * 10; + timeStruct.tm_min += (*s++ - '0'); + } + parsedChars += 2; + } break; - // Second [00,59] - case 'S': { - if ((parsedChars + 2) <= string.length()) { - timeStruct.tm_sec = (*s++ - '0') * 10; - timeStruct.tm_sec += (*s++ - '0'); - } - parsedChars += 2; - } - break; + // Second [00,59] + case 'S': { + if ((parsedChars + 2) <= string.length()) { + timeStruct.tm_sec = (*s++ - '0') * 10; + timeStruct.tm_sec += (*s++ - '0'); + } + parsedChars += 2; + } break; } } else { @@ -176,11 +167,11 @@ namespace Utils { const char* f = format.c_str(); tm timeStruct; - #if defined(_WIN64) +#if defined(_WIN64) localtime_s(&timeStruct, &time); - #else +#else localtime_r(&time, &timeStruct); - #endif +#endif char buf[256] = { '\0' }; char* s = buf; @@ -189,51 +180,45 @@ namespace Utils f++; switch (*f++) { - // Year, including century [1970,xxxx] - case 'Y': { - const int year = timeStruct.tm_year + 1900; - *s++ = static_cast((year - (year % 1000)) / 1000) + '0'; - *s++ = static_cast(((year % 1000) - (year % 100)) / 100) + '0'; - *s++ = static_cast(((year % 100) - (year % 10)) / 10) + '0'; - *s++ = static_cast(year % 10) + '0'; - } - break; + // Year, including century [1970,xxxx] + case 'Y': { + const int year = timeStruct.tm_year + 1900; + *s++ = static_cast((year - (year % 1000)) / 1000) + '0'; + *s++ = static_cast(((year % 1000) - (year % 100)) / 100) + '0'; + *s++ = static_cast(((year % 100) - (year % 10)) / 10) + '0'; + *s++ = static_cast(year % 10) + '0'; + } break; - // Month number [00,11] - case 'm': { - const int mon = timeStruct.tm_mon + 1; - *s++ = static_cast(mon / 10) + '0'; - *s++ = static_cast(mon % 10) + '0'; - } - break; + // Month number [00,11] + case 'm': { + const int mon = timeStruct.tm_mon + 1; + *s++ = static_cast(mon / 10) + '0'; + *s++ = static_cast(mon % 10) + '0'; + } break; - // Day of the month [01,31] - case 'd': { - *s++ = static_cast(timeStruct.tm_mday / 10) + '0'; - *s++ = static_cast(timeStruct.tm_mday % 10) + '0'; - } - break; + // Day of the month [01,31] + case 'd': { + *s++ = static_cast(timeStruct.tm_mday / 10) + '0'; + *s++ = static_cast(timeStruct.tm_mday % 10) + '0'; + } break; - // Hour (24-hour clock) [00,23] - case 'H': { - *s++ = static_cast(timeStruct.tm_hour / 10) + '0'; - *s++ = static_cast(timeStruct.tm_hour % 10) + '0'; - } - break; + // Hour (24-hour clock) [00,23] + case 'H': { + *s++ = static_cast(timeStruct.tm_hour / 10) + '0'; + *s++ = static_cast(timeStruct.tm_hour % 10) + '0'; + } break; - // Minute [00,59] - case 'M': { - *s++ = static_cast(timeStruct.tm_min / 10) + '0'; - *s++ = static_cast(timeStruct.tm_min % 10) + '0'; - } - break; + // Minute [00,59] + case 'M': { + *s++ = static_cast(timeStruct.tm_min / 10) + '0'; + *s++ = static_cast(timeStruct.tm_min % 10) + '0'; + } break; - // Second [00,59] - case 'S': { - *s++ = static_cast(timeStruct.tm_sec / 10) + '0'; - *s++ = static_cast(timeStruct.tm_sec % 10) + '0'; - } - break; + // Second [00,59] + case 'S': { + *s++ = static_cast(timeStruct.tm_sec / 10) + '0'; + *s++ = static_cast(timeStruct.tm_sec % 10) + '0'; + } break; } } else { @@ -260,6 +245,6 @@ namespace Utils return timeStruct.tm_yday + 1; } - } // Time:: + } // namespace Time -} // Utils:: +} // namespace Utils diff --git a/es-core/src/utils/TimeUtil.h b/es-core/src/utils/TimeUtil.h index 967eb7926..a92d4edd2 100644 --- a/es-core/src/utils/TimeUtil.h +++ b/es-core/src/utils/TimeUtil.h @@ -29,7 +29,7 @@ namespace Utils DateTime(const time_t& time); DateTime(const tm& timeStruct); DateTime(const std::string& isoString); - ~DateTime(); + ~DateTime() {} const bool operator<(const DateTime& other) const { return (mTime < other.mTime); } const bool operator<=(const DateTime& other) const { return (mTime <= other.mTime); } @@ -43,8 +43,8 @@ namespace Utils const time_t& getTime() const { return mTime; } void setTimeStruct(const tm& timeStruct); const tm& getTimeStruct() const { return mTimeStruct; } - void setIsoString (const std::string& isoString); - const std::string& getIsoString () const { return mIsoString; } + void setIsoString(const std::string& isoString); + const std::string& getIsoString() const { return mIsoString; } private: time_t mTime; @@ -56,7 +56,7 @@ namespace Utils { public: Duration(const time_t& time); - ~Duration(); + ~Duration() {} unsigned int getDays() const { return mDays; } unsigned int getHours() const { return mHours; } @@ -76,7 +76,8 @@ namespace Utils std::string timeToString(const time_t& time, const std::string& format = "%Y%m%dT%H%M%S"); int daysInMonth(const int year, const int month); int daysInYear(const int year); - } -} // Utils:: + } // namespace Time + +} // namespace Utils #endif // ES_CORE_UTILS_TIME_UTIL_H From b030ab5b8b2875ea44b6ccc14969aafca5d7cfa2 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 7 Jul 2021 20:48:38 +0200 Subject: [PATCH 39/76] (Windows) Fixed a MinGW compiler warning. --- es-core/src/Platform.h | 1 + 1 file changed, 1 insertion(+) diff --git a/es-core/src/Platform.h b/es-core/src/Platform.h index aca84afc7..c32c82c3f 100644 --- a/es-core/src/Platform.h +++ b/es-core/src/Platform.h @@ -13,6 +13,7 @@ #if defined(_WIN64) #include +// This order is required as MinGW complains if windows.h is included before winsock2.h. #include #endif From cbab11dc110801bf9c8cd9a4d09010d91c61e1b4 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 7 Jul 2021 21:02:11 +0200 Subject: [PATCH 40/76] Documentation update. --- CHANGELOG.md | 1 + CONTRIBUTING.md | 13 ++++++----- INSTALL-DEV.md | 61 ++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0add9dc41..3f0a78b2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,7 @@ Apart from this, many small improvements and bug fixes are part of the release, * Added a CMake option to control whether the VLC video player should be built, and set this to off by default * Removed the pointless APPLE_SKIP_INSTALL_LIBS CMake option * Added a clang-format style configuration file to use for automatic code formatting +* Formatted the entire codebase using clang-format * Added the NanoSVG library as a proper Git subtree * Changed the language standard from C++11 to C++14 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 16eef9413..d92a48b36 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -87,20 +87,21 @@ To see which features have been implemented in previous versions, please refer t ### Coding style -The coding style for ES-DE is mostly a combination of the Webkit and Linux kernel guidelines. The cosmetic aspect of the style is auto-formatted using clang-format, so all code changes must first have been processed using this tool. You can read in [INSTALL-DEV.md](INSTALL-DEV.md#using-clang-format-for-automatic-code-formatting) how clang-format is installed and used. +The visual appearance aspect of the coding style is automatically applied using clang-format, so to understand the exact formatting rules, refer to the .clang-format file in the root of the repository. -But of course clang-format won't change the actual code content or fix all code style choices so here are some additional key points: +Due to this approach, all code changes must be auto-formatted before they are committed. You can read in [INSTALL-DEV.md](INSTALL-DEV.md#using-clang-format-for-automatic-code-formatting) how clang-format is installed and used. + +But as clang-format won't change the actual code content or fix all code style choices, here are some additional key points: * Always write comments in C++ style, i.e. `//` instead of `/* */` * Comments should be proper sentences, starting with a capital letter and ending with a dot * As a general rule, use C++ syntax instead of C syntax, for example `static_cast(someFloatVariable)` instead of `(int)someFloatVariable` * Always declare one variable per line, never combine multiple declarations of the same type * Name member variables starting with an `m` such as `mMyMemberVariable` and name static variables starting with an `s` such as `sMyStaticVariable` -* Single-line function definitions can be placed in either the .h or .cpp files depending on the situation -* Inline functions makes perfect sense to use, but don't overdo it by using them for code that won't be called very frequently +* Short function definitions can be placed in either the .h or .cpp file depending on the situation * Avoid overoptimizations, especially if it sacrifices readability, makes the code hard to expand on or is error prone -* Try to be coherent with the existing codebase regarding names, structure etc., it should not be obvious which person wrote what parts -* For the rest, check the code and have fun! :) +* Try to be coherent with the existing codebase regarding names, structure etc., it should not be obvious what person wrote which parts +* For the rest, check the code and have fun :) ### Building and configuring diff --git a/INSTALL-DEV.md b/INSTALL-DEV.md index d2fb75280..9823d176f 100644 --- a/INSTALL-DEV.md +++ b/INSTALL-DEV.md @@ -248,26 +248,24 @@ On Linux, if you're not building a package and instead intend to install using ` Both Clang/LLVM and GCC work fine for building ES-DE. -I did some small benchmarks comparing Clang to GCC with the ES-DE codebase (as of writing it's year 2020) and it's pretty interesting. +I did some small benchmarks comparing Clang 10.0 to GCC 9.3.0 with the ES-DE v1.1 codebase on an Intel Xeon W-2245 @ 3.90GHz running Kubuntu 20.04.2 LTS and it's pretty interesting. Advantages with Clang (vs GCC): -* 10% smaller binary size for a release build -* 17% smaller binary size for a debug build -* 2% faster compile time for a release build -* 16% faster compile time for a debug build +* 8% smaller binary size for a release build +* 31% smaller binary size for a debug build +* 16% faster compile time for a release build +* 25% faster compile time for a debug build +* 13% faster application startup time for a release build * 4% faster application startup time for a debug build -Advantage with GCC (vs Clang): -* 1% faster application startup time for a release build - *Release build: Optimizations enabled, debug info disabled, binary stripped.* \ *Debug build: Optimizations disabled, debug info enabled, binary not stripped.* This Clang debug build is LLVM "native", i.e. intended to be debugged using the LLVM project debugger LLDB. The problem is that this is still not well integrated with VSCode that I use for development so I need to keep using GDB. But this is problematic as the libstd++ data required by GDB is missing in the binary, making it impossible to see the values of for instance std::string variables. -It's possible to activate the additional debug info needed by GDB by using the flag `-D_GLIBCXX_DEBUG`. I've added this to CMakeLists.txt when using Clang, but this bloats the binary and makes the code much slower. Actually, instead of a 4% faster application startup, it's now 36% slower! The same goes for the binary size, instead of 17% smaller it's now 17% larger. +It's possible to activate the additional debug info needed by GDB by using the flag `-D_GLIBCXX_DEBUG`. I've added this to CMakeLists.txt when using Clang, but this bloats the binary and makes the code much slower. Actually, instead of a 4% faster application startup, it's now 25% slower. The same goes for the binary size, instead of 31% smaller it's now 5% larger. The compilation time is still less than GCC but only by 10% instead of 25%. -I'm expecting this to be resolved in the near future though, and as I think Clang is an interesting compiler, I use it as the default when working on the project (I sometimes test with GCC to make sure that it still builds the software correctly). +I'm expecting this to be resolved in the near future though, and as I think Clang has the best error messages and comes with good tools such as the static analyzer, this is now my primary compiler. It's by the way very easy to switch between LLVM and GCC using Ubuntu, just use the `update-alternatives` command: @@ -1149,11 +1147,13 @@ So the home directory will always take precedence, and any resources or themes l ## Using clang-format for automatic code formatting -Although not completed yet as of writing this, ES-DE will have its entire codebase automatically formatted by clang-format. There is a style configuration file named .clang-format located directly at the root of the ES-DE source repository which can be used to apply the formatting anywhere in the source tree. +The entire ES-DE codebase is formatted using clang-format and all code commits must have been processed using this tool. + +There is a style configuration file named .clang-format located directly at the root of the repository which contains the formatting rules. How to install clang-format is detailed per operating system earlier in this document. -There are two ways to run this tool, from the command line or from inside an editor such as VSCode. +There are two ways to run the tool, from the command line or from inside an editor such as VSCode. To format a file from the command line, simply run: @@ -1163,7 +1163,7 @@ The -i flag will make an inplace edit of the file. But the recommended way is to run clang-format from within the editor. If using VSCode, there is an extension available named Clang-Format. After installing this, simply open a source file, right click and choose `Format Document` or use the applicable keyboard shortcut. The first time you do this, you will have to make a choice to perform the formatting using clang-format. The rest should be completely automatic. -In some instances you want to avoid getting code formatted, and you accomplish this by simply enclosing the code by the two comments "clang-format off" and "clang-format on", such as this: +In some instances you may want to avoid getting code formatted, and you can accomplish this by simply enclosing the lines with the two comments "clang-format off" and "clang-format on", such as this: ```c++ // clang-format off @@ -1177,6 +1177,41 @@ CollectionSystemDecl systemDecls[] = { // clang-format on ``` +Adding a comment (or a semicolon) on its own line will also prevent some formatting such as turning short functions and lambda expressions into single lines. For this function such a comment has been added: + +```c++ +const std::string FileData::get3DBoxPath() const +{ + // Return path to the 3D box image. + return getMediafilePath("3dboxes", "3dbox"); +} +``` + +If the comment was omitted, the function would get formatted like this: + +```c++ +const std::string FileData::get3DBoxPath() const { return getMediafilePath("3dboxes", "3dbox"); } +``` + +Adding comments (even empty ones) can also force line breaks to avoid ugly formatting such as this: + +```c++ +for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); + it++) { + (*it)->setScrapeFlag(false); +} +``` + +Adding a comment at the right place produces this much nicer formatting: +```c++ +for (auto it = SystemData::sSystemVector.cbegin(); // Line break. + it != SystemData::sSystemVector.cend(); it++) { + (*it)->setScrapeFlag(false); +} +``` + + +Of course you would like to get the code formatted according to the clang-format rules in most instances, these workaround are only meant for rare exceptions. Some compromises are necessary when auto-formatting code, at least with clang-format in its current state. ## CA certificates and MAME ROM information From 96503a23c6102e6ef021f6fb949efe2697801d12 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 7 Jul 2021 21:13:40 +0200 Subject: [PATCH 41/76] Small code formatting change. --- es-app/src/CollectionSystemsManager.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/es-app/src/CollectionSystemsManager.cpp b/es-app/src/CollectionSystemsManager.cpp index 1547c9e43..3d8950288 100644 --- a/es-app/src/CollectionSystemsManager.cpp +++ b/es-app/src/CollectionSystemsManager.cpp @@ -609,11 +609,10 @@ void CollectionSystemsManager::setEditMode(std::string collectionName, bool show mEditingCollectionSystemData = sysData; if (showPopup) { - GuiInfoPopup* s = - new GuiInfoPopup(mWindow, - "EDITING '" + Utils::String::toUpper(collectionName) + - "' COLLECTION, ADD/REMOVE GAMES WITH 'Y'", - 10000); + GuiInfoPopup* s = new GuiInfoPopup(mWindow, + "EDITING '" + Utils::String::toUpper(collectionName) + + "' COLLECTION, ADD/REMOVE GAMES WITH 'Y'", + 10000); mWindow->setInfoPopup(s); } @@ -942,8 +941,7 @@ void CollectionSystemsManager::deleteCustomCollection(std::string collectionName << configFile << "'."; GuiInfoPopup* s = new GuiInfoPopup( - mWindow, "DELETED COLLECTION '" + Utils::String::toUpper(collectionName) + "'", - 5000); + mWindow, "DELETED COLLECTION '" + Utils::String::toUpper(collectionName) + "'", 5000); mWindow->setInfoPopup(s); } else { From a62be38c8565a45b08976edc9ba1d0b353efe15e Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 7 Jul 2021 22:08:19 +0200 Subject: [PATCH 42/76] Fixed an issue where fractional rating values were always rounded up. --- es-core/src/components/RatingComponent.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/es-core/src/components/RatingComponent.cpp b/es-core/src/components/RatingComponent.cpp index a2f1c8aba..6dfc32b3f 100644 --- a/es-core/src/components/RatingComponent.cpp +++ b/es-core/src/components/RatingComponent.cpp @@ -36,8 +36,8 @@ void RatingComponent::setValue(const std::string& value) mValue = 0.0f; } else { - // Round up to the closest .1 value, i.e. to the closest half-icon. - mValue = ceilf(stof(value) / 0.1f) / 10.0f; + // Round to the closest .1 value, i.e. to the closest half-icon. + mValue = std::round(stof(value) / 0.1f) / 10.0f; mOriginalValue = static_cast(mValue * 10.0f); // If the argument to colorize the rating icons has been passed, set the From 9a53ad0463f44a944868d4d8aa7d827bd052c26d Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 8 Jul 2021 18:05:32 +0200 Subject: [PATCH 43/76] Some CMake updates to build correctly on Raspberry Pi. Also some other general CMake cleanups. --- CMakeLists.txt | 76 ++++++++++++------------------------------- es-app/CMakeLists.txt | 2 +- 2 files changed, 21 insertions(+), 57 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1da5f7a91..d9a396d70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,8 +14,6 @@ set(CMAKE_VERBOSE_MAKEFILE OFF CACHE BOOL "Show verbose compiler output" FORCE) # Package type to use for CPack on Linux. set(LINUX_CPACK_GENERATOR "DEB" CACHE STRING "CPack generator, DEB or RPM") -set(VLC_PLAYER OFF CACHE BOOL "Whether to build with the VLC video player") - # Add local find modules to the CMake path. list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake/Utils @@ -26,38 +24,24 @@ option(GLES "Set to ON if targeting Embedded OpenGL" ${GLES}) option(GL "Set to ON if targeting Desktop OpenGL" ${GL}) option(RPI "Set to ON to enable Raspberry Pi specific build" ${RPI}) option(CEC "Set to ON to enable CEC" ${CEC}) +option(VLC_PLAYER "Set to ON to build the VLC-based video player" ${VLC_PLAYER}) #--------------------------------------------------------------------------------------------------- # OpenGL setup. -if(GLES) - set(GLSystem "Embedded OpenGL" CACHE STRING "The OpenGL system to be used") -elseif(GL) - set(GLSystem "Desktop OpenGL" CACHE STRING "The OpenGL system to be used") - # Check if we're running on a Raspberry Pi. -elseif(EXISTS "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/bcm_host.h") - MESSAGE("bcm_host.h found") - set(BCMHOST found) +if(EXISTS "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/bcm_host.h") + message("-- Building on a Raspberry Pi (bcm_host.h found)") + # Setting BCMHOST seems to break OpenGL ES on the RPi 4? + #set(BCMHOST found) + # Simply set RPI instead to enable the RPi-specific parts of the code. + set(RPI ON) set(GLSystem "Embedded OpenGL" CACHE STRING "The OpenGL system to be used") - -# Check if we're running on OSMC Vero4K. -elseif(EXISTS "${CMAKE_FIND_ROOT_PATH}/opt/vero3/lib/libMali.so") - MESSAGE("libMali.so found") - set(VERO4K found) - set(GLSystem "Embedded OpenGL" CACHE STRING "The OpenGL system to be used") - -# Check if we're running on olinuxino / odroid / etc. -elseif(EXISTS "${CMAKE_FIND_ROOT_PATH}/usr/lib/libMali.so" OR - EXISTS "${CMAKE_FIND_ROOT_PATH}/usr/lib/arm-linux-gnueabihf/libMali.so" OR - EXISTS "${CMAKE_FIND_ROOT_PATH}/usr/lib/aarch64-linux-gnu/libMali.so" OR - EXISTS "${CMAKE_FIND_ROOT_PATH}/usr/lib/arm-linux-gnueabihf/mali-egl/libmali.so" OR - EXISTS "${CMAKE_FIND_ROOT_PATH}/usr/lib/arm-linux-gnueabihf/libmali.so") - MESSAGE("libMali.so found") +elseif(GLES) set(GLSystem "Embedded OpenGL" CACHE STRING "The OpenGL system to be used") else() set(GLSystem "Desktop OpenGL" CACHE STRING "The OpenGL system to be used") -endif(GLES) +endif() set_property(CACHE GLSystem PROPERTY STRINGS "Desktop OpenGL" "Embedded OpenGL") @@ -80,7 +64,7 @@ if(NOT WIN32) find_package(Pugixml REQUIRED) find_package(RapidJSON REQUIRED) find_package(SDL2 REQUIRED) - if(VLC_PLAYER OR RPI) + if(VLC_PLAYER) find_package(VLC REQUIRED) endif() endif() @@ -193,11 +177,6 @@ endif() if(DEFINED BCMHOST OR RPI) add_definitions(-D_RPI_) - add_definitions(-DBUILD_VLC_PLAYER) -endif() - -if(DEFINED VERO4K) - add_definitions(-D_VERO4K_) endif() if(DEFINED libCEC_FOUND) @@ -240,7 +219,7 @@ set(COMMON_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/external/CImg ${CMAKE_CURRENT_SOURCE_DIR}/external/nanosvg/src ${CMAKE_CURRENT_SOURCE_DIR}/es-core/src) -if(VLC_PLAYER OR RPI) +if(VLC_PLAYER) set(COMMON_INCLUDE_DIRS ${COMMON_INCLUDE_DIRS} ${VLC_INCLUDE_DIR}) endif() @@ -250,7 +229,6 @@ if(WIN32) if(NOT EXISTS ${WIN32_INCLUDE_DIR}) message(SEND_ERROR "Can't find WIN32 include directory: ${WIN32_INCLUDE_DIR}") endif() - #include_directories(${WIN32_INCLUDE_DIR}) endif() # Temporary solution until the VLC find module has been updated to work properly on macOS. @@ -278,17 +256,6 @@ if(DEFINED BCMHOST) "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/interface/vcos" "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/interface/vmcs_host/linux" "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/interface/vcos/pthreads") -# Add Vero4k include directory. -elseif(DEFINED VERO4K) - list(APPEND COMMON_INCLUDE_DIRS "${CMAKE_FIND_ROOT_PATH}/opt/vero3/include") -else() - # Add OpenGL include directory. - if(${GLSystem} MATCHES "Desktop OpenGL") - list(APPEND COMMON_INCLUDE_DIRS ${OPENGL_INCLUDE_DIR}) - else() - # Add OpenGL ES include directory. - list(APPEND COMMON_INCLUDE_DIRS ${OPENGLES_INCLUDE_DIR}) - endif() endif() #--------------------------------------------------------------------------------------------------- @@ -302,7 +269,7 @@ if(NOT WIN32) ${FREETYPE_LIBRARIES} ${PUGIXML_LIBRARIES} ${SDL2_LIBRARY}) - if(VLC_PLAYER OR RPI) + if(VLC_PLAYER) set(COMMON_LIBRARIES ${COMMON_LIBRARIES} ${VLC_LIBRARIES}) endif() elseif(WIN32) @@ -368,9 +335,9 @@ endif() # Add libCEC libraries. if(DEFINED libCEC_FOUND) -if(DEFINED BCMHOST) - list(APPEND COMMON_LIBRARIES vchiq_arm) -endif() + if(DEFINED BCMHOST) + list(APPEND COMMON_LIBRARIES vchiq_arm) + endif() list(APPEND COMMON_LIBRARIES dl ${libCEC_LIBRARIES}) endif() @@ -382,15 +349,12 @@ endif() if(DEFINED BCMHOST) link_directories("${CMAKE_FIND_ROOT_PATH}/opt/vc/lib") list(APPEND COMMON_LIBRARIES bcm_host brcmEGL ${OPENGLES_LIBRARIES}) -elseif(DEFINED VERO4K) - link_directories("${CMAKE_FIND_ROOT_PATH}/opt/vero3/lib") - list(APPEND COMMON_LIBRARIES EGL ${OPENGLES_LIBRARIES}) +endif() + +if(${GLSystem} MATCHES "Desktop OpenGL") + list(APPEND COMMON_LIBRARIES ${OPENGL_LIBRARIES}) else() - if(${GLSystem} MATCHES "Desktop OpenGL") - list(APPEND COMMON_LIBRARIES ${OPENGL_LIBRARIES}) - else() - list(APPEND COMMON_LIBRARIES EGL ${OPENGLES_LIBRARIES}) - endif() + list(APPEND COMMON_LIBRARIES EGL ${OPENGLES_LIBRARIES}) endif() #--------------------------------------------------------------------------------------------------- diff --git a/es-app/CMakeLists.txt b/es-app/CMakeLists.txt index ec49edca4..9187aa617 100644 --- a/es-app/CMakeLists.txt +++ b/es-app/CMakeLists.txt @@ -332,7 +332,7 @@ else() set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://es-de.org") set(CPACK_DEBIAN_PACKAGE_SECTION "games") set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") - if(VLC_PLAYER OR RPI) + if(VLC_PLAYER) set(CPACK_DEBIAN_PACKAGE_DEPENDS "vlc") endif() set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) From 1c31a8a49e4d1bb661c0856047264d838c97154b Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 8 Jul 2021 18:08:43 +0200 Subject: [PATCH 44/76] Hack to be able to build on Raspberry Pi. --- es-core/src/renderers/Shader_GL21.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/es-core/src/renderers/Shader_GL21.h b/es-core/src/renderers/Shader_GL21.h index 94a78b40f..f9e0a970c 100644 --- a/es-core/src/renderers/Shader_GL21.h +++ b/es-core/src/renderers/Shader_GL21.h @@ -18,7 +18,12 @@ #endif #include +// Hack until shader support has been added for OpenGL ES. +#if defined(USE_OPENGL_21) #include +#else +#include +#endif #include #include #include From 1d706ec7f3eaa42e1a9f4f040c4432ee6ce54cd4 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 8 Jul 2021 18:18:59 +0200 Subject: [PATCH 45/76] Removed some obsolete audio settings. Also set VLC as the default video player on Raspberry Pi. --- es-app/src/VolumeControl.cpp | 21 --------------------- es-core/src/Settings.cpp | 27 +++++---------------------- 2 files changed, 5 insertions(+), 43 deletions(-) diff --git a/es-app/src/VolumeControl.cpp b/es-app/src/VolumeControl.cpp index 2083f1688..4019b7754 100644 --- a/es-app/src/VolumeControl.cpp +++ b/es-app/src/VolumeControl.cpp @@ -11,27 +11,12 @@ #include "Log.h" #include "math/Misc.h" -#if defined(_RPI_) -#include "Settings.h" -#endif - #if defined(_WIN64) #include #endif -// The ALSA Audio Card and Audio Device selection code is disabled at the moment. -// As PulseAudio controls the sound devices for the desktop environment, it doesn't -// make much sense to be able to select ALSA devices directly. -// The code is still active for Raspberry Pi though as I'm not sure if this is -// useful for that device. -// Keeping mixerName and mixerCard at their default values should make sure that -// the rest of the volume control code in here compiles and works fine. #if defined(__linux__) -#if defined(_RPI_) || defined(_VERO4K_) -std::string VolumeControl::mixerName = "PCM"; -#else std::string VolumeControl::mixerName = "Master"; -#endif std::string VolumeControl::mixerCard = "default"; #endif @@ -83,12 +68,6 @@ void VolumeControl::init() #if defined(__linux__) // Try to open mixer device. if (mixerHandle == nullptr) { -#if defined(_RPI_) - // Allow user to override the AudioCard and AudioDevice in es_settings.xml. - mixerCard = Settings::getInstance()->getString("AudioCard"); - mixerName = Settings::getInstance()->getString("AudioDevice"); -#endif - snd_mixer_selem_id_alloca(&mixerSelemId); // Sets simple-mixer index and name. snd_mixer_selem_id_set_index(mixerSelemId, mixerIndex); diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index 70964667c..706400902 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -187,24 +187,7 @@ void Settings::setDefaults() mBoolMap["ScreensaverVideoScanlines"] = { true, true }; mBoolMap["ScreensaverVideoBlur"] = { false, false }; -#if defined(_RPI_) // Sound settings. - // The ALSA Audio Card and Audio Device selection code is disabled at the moment. - // As PulseAudio controls the sound devices for the desktop environment, it doesn't - // make much sense to be able to select ALSA devices directly. Normally (always?) - // the selection doesn't make any difference at all. But maybe some PulseAudio - // settings could be added later on, if needed. - // The code is still active for Raspberry Pi though as I'm not sure if this is - // useful for that device. - mStringMap["AudioCard"] = { "default", "default" }; - // Audio out device for volume control. - //#endif - //#if defined(_RPI_) - mStringMap["AudioDevice"] = { "PCM", "PCM" }; - // Audio out device for Video playback using OMX player. - mStringMap["OMXAudioDev"] = { "both", "both" }; -#endif - mIntMap["SoundVolumeNavigation"] = { 80, 80 }; mIntMap["SoundVolumeVideos"] = { 100, 100 }; mBoolMap["GamelistVideoAudio"] = { true, true }; @@ -235,12 +218,12 @@ void Settings::setDefaults() mStringMap["FullscreenMode"] = { "normal", "normal" }; #endif #if defined(BUILD_VLC_PLAYER) +#if defined(_RPI_) + // As the FFmpeg video player is not HW accelerated, use VLC as default on this weak device. + mStringMap["VideoPlayer"] = { "vlc", "vlc" }; +#else mStringMap["VideoPlayer"] = { "ffmpeg", "ffmpeg" }; #endif -#if defined(_RPI_) - mBoolMap["VideoOmxPlayer"] = { false, false }; - // We're defaulting to OMX Player for full screen video on the Pi. - mBoolMap["ScreensaverOmxPlayer"] = { true, true }; #endif mStringMap["SaveGamelistsMode"] = { "always", "always" }; #if defined(_WIN64) @@ -394,7 +377,7 @@ void Settings::loadFile() setString(node.attribute("name").as_string(), node.attribute("value").as_string()); } -// Macro to create the get and set functions for the various data types +// Macro to create the get and set functions for the various data types. #define SETTINGS_GETSET(type, mapName, getFunction, getDefaultFunction, setFunction) \ type Settings::getFunction(const std::string& name) \ { \ From 7ea91f08af6f4105bc614e11bb222183c9d74d7d Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 8 Jul 2021 18:26:01 +0200 Subject: [PATCH 46/76] Removed the deprecated VideoOmxComponent. --- es-app/src/SystemScreensaver.cpp | 13 +- es-app/src/guis/GuiMenu.cpp | 114 --------- es-app/src/guis/GuiScreensaverOptions.cpp | 15 -- .../src/views/gamelist/VideoGameListView.cpp | 15 +- es-core/CMakeLists.txt | 2 - es-core/src/components/VideoOmxComponent.cpp | 242 ------------------ es-core/src/components/VideoOmxComponent.h | 47 ---- 7 files changed, 4 insertions(+), 444 deletions(-) delete mode 100644 es-core/src/components/VideoOmxComponent.cpp delete mode 100644 es-core/src/components/VideoOmxComponent.h diff --git a/es-app/src/SystemScreensaver.cpp b/es-app/src/SystemScreensaver.cpp index e6aa7ea5c..e74b161d9 100644 --- a/es-app/src/SystemScreensaver.cpp +++ b/es-app/src/SystemScreensaver.cpp @@ -10,9 +10,6 @@ #include "SystemScreensaver.h" #include "components/VideoFFmpegComponent.h" -#if defined(_RPI_) -#include "components/VideoOmxComponent.h" -#endif #if defined(BUILD_VLC_PLAYER) #include "components/VideoVlcComponent.h" #endif @@ -163,15 +160,7 @@ void SystemScreensaver::startScreensaver(bool generateMediaList) if (Settings::getInstance()->getBool("ScreensaverVideoGameInfo")) generateOverlayInfo(); -#if defined(_RPI_) - // Create the correct type of video component. - if (Settings::getInstance()->getBool("ScreensaverOmxPlayer")) - mVideoScreensaver = new VideoOmxComponent(mWindow); - else if (Settings::getInstance()->getString("VideoPlayer") == "vlc") - mVideoScreensaver = new VideoVlcComponent(mWindow); - else - mVideoScreensaver = new VideoFFmpegComponent(mWindow); -#elif defined(BUILD_VLC_PLAYER) +#if defined(BUILD_VLC_PLAYER) if (Settings::getInstance()->getString("VideoPlayer") == "vlc") mVideoScreensaver = new VideoVlcComponent(mWindow); else diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 2333b83ed..bc9a2f77f 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -631,105 +631,6 @@ void GuiMenu::openSoundOptions() }); if (UIModeController::getInstance()->isUIModeFull()) { - // The ALSA Audio Card and Audio Device selection code is disabled at the moment. - // As PulseAudio controls the sound devices for the desktop environment, it doesn't - // make much sense to be able to select ALSA devices directly. Normally (always?) - // the selection doesn't make any difference at all. But maybe some PulseAudio - // settings could be added later on, if needed. - // The code is still active for Raspberry Pi though as I'm not sure if this is - // useful for that device. - // #if defined(__linux__) -#if defined(_RPI_) - // Audio card. - auto audio_card = std::make_shared>( - mWindow, getHelpStyle(), "AUDIO CARD", false); - std::vector audio_cards; -#if defined(_RPI_) - // RPi Specific Audio Cards. - audio_cards.push_back("local"); - audio_cards.push_back("hdmi"); - audio_cards.push_back("both"); -#endif - audio_cards.push_back("default"); - audio_cards.push_back("sysdefault"); - audio_cards.push_back("dmix"); - audio_cards.push_back("hw"); - audio_cards.push_back("plughw"); - audio_cards.push_back("null"); - if (Settings::getInstance()->getString("AudioCard") != "") { - if (std::find(audio_cards.begin(), audio_cards.end(), - Settings::getInstance()->getString("AudioCard")) == audio_cards.end()) { - audio_cards.push_back(Settings::getInstance()->getString("AudioCard")); - } - } - for (auto ac = audio_cards.cbegin(); ac != audio_cards.cend(); ac++) - audio_card->add(*ac, *ac, Settings::getInstance()->getString("AudioCard") == *ac); - s->addWithLabel("AUDIO CARD", audio_card); - s->addSaveFunc([audio_card, s] { - if (audio_card->getSelected() != Settings::getInstance()->getString("AudioCard")) { - Settings::getInstance()->setString("AudioCard", audio_card->getSelected()); - VolumeControl::getInstance()->deinit(); - VolumeControl::getInstance()->init(); - s->setNeedsSaving(); - } - }); - - // Volume control device. - auto vol_dev = std::make_shared>(mWindow, getHelpStyle(), - "AUDIO DEVICE", false); - std::vector transitions; - transitions.push_back("PCM"); - transitions.push_back("Speaker"); - transitions.push_back("Master"); - transitions.push_back("Digital"); - transitions.push_back("Analogue"); - if (Settings::getInstance()->getString("AudioDevice") != "") { - if (std::find(transitions.begin(), transitions.end(), - Settings::getInstance()->getString("AudioDevice")) == transitions.end()) { - transitions.push_back(Settings::getInstance()->getString("AudioDevice")); - } - } - for (auto it = transitions.cbegin(); it != transitions.cend(); it++) - vol_dev->add(*it, *it, Settings::getInstance()->getString("AudioDevice") == *it); - s->addWithLabel("AUDIO DEVICE", vol_dev); - s->addSaveFunc([vol_dev, s] { - if (vol_dev->getSelected() != Settings::getInstance()->getString("AudioDevice")) { - Settings::getInstance()->setString("AudioDevice", vol_dev->getSelected()); - VolumeControl::getInstance()->deinit(); - VolumeControl::getInstance()->init(); - s->setNeedsSaving(); - } - }); -#endif - -#if defined(_RPI_) - // OMXPlayer audio device. - auto omx_audio_dev = std::make_shared>( - mWindow, getHelpStyle(), "OMX PLAYER AUDIO DEVICE", false); - std::vector omx_cards; - // RPi Specific Audio Cards - omx_cards.push_back("local"); - omx_cards.push_back("hdmi"); - omx_cards.push_back("both"); - omx_cards.push_back("alsa:hw:0,0"); - omx_cards.push_back("alsa:hw:1,0"); - if (Settings::getInstance()->getString("OMXAudioDev") != "") { - if (std::find(omx_cards.begin(), omx_cards.end(), - Settings::getInstance()->getString("OMXAudioDev")) == omx_cards.end()) { - omx_cards.push_back(Settings::getInstance()->getString("OMXAudioDev")); - } - } - for (auto it = omx_cards.cbegin(); it != omx_cards.cend(); it++) - omx_audio_dev->add(*it, *it, Settings::getInstance()->getString("OMXAudioDev") == *it); - s->addWithLabel("OMX PLAYER AUDIO DEVICE", omx_audio_dev); - s->addSaveFunc([omx_audio_dev, s] { - if (omx_audio_dev->getSelected() != Settings::getInstance()->getString("OMXAudioDev")) { - Settings::getInstance()->setString("OMXAudioDev", omx_audio_dev->getSelected()); - s->setNeedsSaving(); - } - }); -#endif - // Play audio for gamelist videos. auto gamelist_video_audio = std::make_shared(mWindow); gamelist_video_audio->setState(Settings::getInstance()->getBool("GamelistVideoAudio")); @@ -1000,21 +901,6 @@ void GuiMenu::openOtherOptions() }); s->addRow(rowMediaDir); -#if defined(_RPI_) - // Video playing using OMXPlayer. - auto video_omx_player = std::make_shared(mWindow); - video_omx_player->setState(Settings::getInstance()->getBool("VideoOmxPlayer")); - s->addWithLabel("USE OMX PLAYER (HW ACCELERATED)", video_omx_player); - s->addSaveFunc([video_omx_player, s] { - if (video_omx_player->getState() != Settings::getInstance()->getBool("VideoOmxPlayer")) { - Settings::getInstance()->setBool("VideoOmxPlayer", video_omx_player->getState()); - s->setNeedsSaving(); - // Need to reload all views to re-create the right video components. - s->setNeedsReloading(); - } - }); -#endif - #if defined(_WIN64) // Hide taskbar during the ES program session. auto hide_taskbar = std::make_shared(mWindow); diff --git a/es-app/src/guis/GuiScreensaverOptions.cpp b/es-app/src/guis/GuiScreensaverOptions.cpp index 41491e7d5..8c7dd71ca 100644 --- a/es-app/src/guis/GuiScreensaverOptions.cpp +++ b/es-app/src/guis/GuiScreensaverOptions.cpp @@ -257,21 +257,6 @@ void GuiScreensaverOptions::openVideoScreensaverOptions() } }); -#if defined(_RPI_) - // Use OMX player for screensaver. - auto screensaver_omx_player = std::make_shared(mWindow); - screensaver_omx_player->setState(Settings::getInstance()->getBool("ScreensaverOmxPlayer")); - s->addWithLabel("USE OMX PLAYER FOR SCREENSAVER", screensaver_omx_player); - s->addSaveFunc([screensaver_omx_player, s] { - if (screensaver_omx_player->getState() != - Settings::getInstance()->getBool("ScreensaverOmxPlayer")) { - Settings::getInstance()->setBool("ScreensaverOmxPlayer", - screensaver_omx_player->getState()); - s->setNeedsSaving(); - } - }); -#endif - #if defined(USE_OPENGL_21) // Render scanlines using a shader. auto screensaver_video_scanlines = std::make_shared(mWindow); diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index 1431bd785..a1ddd7278 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -10,9 +10,6 @@ #include "animations/LambdaAnimation.h" #include "components/VideoFFmpegComponent.h" -#if defined(_RPI_) -#include "components/VideoOmxComponent.h" -#endif #if defined(BUILD_VLC_PLAYER) #include "components/VideoVlcComponent.h" #endif @@ -56,15 +53,9 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) { const float padding = 0.01f; -// Create the correct type of video window. -#if defined(_RPI_) - if (Settings::getInstance()->getBool("VideoOmxPlayer")) - mVideo = new VideoOmxComponent(window); - else if (Settings::getInstance()->getString("VideoPlayer") == "vlc") - mVideo = new VideoVlcComponent(window); - else - mVideo = new VideoFFmpegComponent(window); -#elif defined(BUILD_VLC_PLAYER) + // Create the correct type of video window. + +#if defined(BUILD_VLC_PLAYER) if (Settings::getInstance()->getString("VideoPlayer") == "vlc") mVideo = new VideoVlcComponent(window); else diff --git a/es-core/CMakeLists.txt b/es-core/CMakeLists.txt index bf9d750b1..422c27d35 100644 --- a/es-core/CMakeLists.txt +++ b/es-core/CMakeLists.txt @@ -48,7 +48,6 @@ set(CORE_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextListComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoFFmpegComponent.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoOmxComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoVlcComponent.h # GUIs @@ -126,7 +125,6 @@ set(CORE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoFFmpegComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoOmxComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoVlcComponent.cpp # GUIs diff --git a/es-core/src/components/VideoOmxComponent.cpp b/es-core/src/components/VideoOmxComponent.cpp deleted file mode 100644 index 4d537f244..000000000 --- a/es-core/src/components/VideoOmxComponent.cpp +++ /dev/null @@ -1,242 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// VideoOmxComponent.cpp -// -// Video playing using OMXPlayer for Raspberry Pi. -// - -#if defined(_RPI_) -#include "components/VideoOmxComponent.h" - -#include "AudioManager.h" -#include "Settings.h" -#include "renderers/Renderer.h" -#include "utils/StringUtil.h" - -#include -#include -#include - -class VolumeControl -{ -public: - static std::shared_ptr& getInstance(); - int getVolume() const; -}; - -VideoOmxComponent::VideoOmxComponent(Window* window) - : VideoComponent(window) - , mPlayerPid(-1) -{ -} - -VideoOmxComponent::~VideoOmxComponent() -{ - // Stop video when destroyed. - stopVideo(); -} - -void VideoOmxComponent::render(const Transform4x4f& parentTrans) -{ - if (!isVisible()) - return; - - VideoComponent::render(parentTrans); - - if (!mIsPlaying || mPlayerPid == -1) - VideoComponent::renderSnapshot(parentTrans); -} - -void VideoOmxComponent::setResize(float width, float height) -{ - setSize(width, height); - mTargetSize = Vector2f(width, height); - mTargetIsMax = false; - mStaticImage.setResize(width, height); - onSizeChanged(); -} - -void VideoOmxComponent::setMaxSize(float width, float height) -{ - setSize(width, height); - mTargetSize = Vector2f(width, height); - mTargetIsMax = true; - mStaticImage.setMaxSize(width, height); - onSizeChanged(); -} - -void VideoOmxComponent::startVideo() -{ - if (!mIsPlaying) { - mVideoWidth = 0; - mVideoHeight = 0; - - std::string path(mVideoPath.c_str()); - - // Make sure we have a video path. - if ((path.size() > 0) && (mPlayerPid == -1)) { - // Set the video that we are going to be playing so we don't attempt to restart it. - mPlayingVideoPath = mVideoPath; - - // Disable AudioManager so video can play, in case we're requesting ALSA. - if (Utils::String::startsWith(Settings::getInstance()->getString("OMXAudioDev").c_str(), - "alsa")) - AudioManager::getInstance()->deinit(); - - // Start the player process. - pid_t pid = fork(); - if (pid == -1) { - // Failed. - mPlayingVideoPath = ""; - } - else if (pid > 0) { - mPlayerPid = pid; - // Update the playing state. - signal(SIGCHLD, catch_child); - mIsPlaying = true; - mFadeIn = 0.0f; - } - else { - // Find out the pixel position of the video view and build a command line for - // OMXPlayer to position it in the right place. - char buf1[32]; - char buf2[32]; - float x = mPosition.x() - (mOrigin.x() * mSize.x()); - float y = mPosition.y() - (mOrigin.y() * mSize.y()); - - // Fix x and y. - switch (Renderer::getScreenRotate()) { - case 0: { - const int x1 = static_cast(Renderer::getScreenOffsetX() + x); - const int y1 = static_cast(Renderer::getScreenOffsetY() + y); - const int x2 = static_cast(x1 + mSize.x()); - const int y2 = static_cast(y1 + mSize.y()); - sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2); - } break; - - case 1: { - const int x1 = - static_cast(Renderer::getWindowWidth() - - Renderer::getScreenOffsetY() - y - mSize.y()); - const int y1 = static_cast(Renderer::getScreenOffsetX() + x); - const int x2 = static_cast(x1 + mSize.y()); - const int y2 = static_cast(y1 + mSize.x()); - sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2); - } break; - - case 2: { - const int x1 = - static_cast(Renderer::getWindowWidth() - - Renderer::getScreenOffsetX() - x - mSize.x()); - const int y1 = - static_cast(Renderer::getWindowHeight() - - Renderer::getScreenOffsetY() - y - mSize.y()); - const int x2 = static_cast(x1 + mSize.x()); - const int y2 = static_cast(y1 + mSize.y()); - sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2); - } break; - - case 3: { - const int x1 = static_cast(Renderer::getScreenOffsetY() + y); - const int y1 = - static_cast(Renderer::getWindowHeight() - - Renderer::getScreenOffsetX() - x - mSize.x()); - const int x2 = static_cast(x1 + mSize.y()); - const int y2 = static_cast(y1 + mSize.x()); - sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2); - } break; - } - - // Rotate the video. - switch (Renderer::getScreenRotate()) { - case 0: { - sprintf(buf2, "%d", static_cast(0)); - } break; - case 1: { - sprintf(buf2, "%d", static_cast(90)); - } break; - case 2: { - sprintf(buf2, "%d", static_cast(180)); - } break; - case 3: { - sprintf(buf2, "%d", static_cast(270)); - } break; - } - - // We need to specify the layer of 10000 or above to ensure the video is - // displayed on top of our SDL display. - const char* argv[] = { "", "--layer", - "10010", "--loop", - "--no-osd", "--aspect-mode", - "letterbox", "--vol", - "0", "-o", - "both", "--win", - buf1, "--orientation", - buf2, "", - "", "", - "", "", - "", "", - "", "", - "", "", - NULL }; - - // Check if we want to mute the audio. - if ((!Settings::getInstance()->getBool("GamelistVideoAudio") || - static_cast(VolumeControl::getInstance()->getVolume()) == 0) || - (!Settings::getInstance()->getBool("ScreensaverVideoAudio") && - mScreensaverMode)) { - argv[8] = "-1000000"; - } - else { - float percentVolume = - static_cast(VolumeControl::getInstance()->getVolume()); - int OMXVolume = static_cast((percentVolume - 98) * 105); - argv[8] = std::to_string(OMXVolume).c_str(); - } - - // If we are rendering a video gamelist. - if (!mTargetIsMax) - argv[6] = "stretch"; - argv[15] = mPlayingVideoPath.c_str(); - - argv[10] = Settings::getInstance()->getString("OMXAudioDev").c_str(); - - const char* env[] = { "LD_LIBRARY_PATH=/opt/vc/libs:/usr/lib/omxplayer", NULL }; - - // Redirect stdout. - int fdin = open("/dev/null", O_RDONLY); - int fdout = open("/dev/null", O_WRONLY); - dup2(fdin, 0); - dup2(fdout, 1); - // Run the OMXPlayer binary. - execve("/usr/bin/omxplayer.bin", (char**)argv, (char**)env); - - _exit(EXIT_FAILURE); - } - } - } -} - -void catch_child(int sig_num) -{ - // When we get here, we know there's a zombie child waiting. - int child_status; - wait(&child_status); -} - -void VideoOmxComponent::stopVideo() -{ - mIsPlaying = false; - mStartDelayed = false; - - // Stop the player process. - if (mPlayerPid != -1) { - int status; - kill(mPlayerPid, SIGKILL); - waitpid(mPlayerPid, &status, WNOHANG); - mPlayerPid = -1; - } -} - -#endif diff --git a/es-core/src/components/VideoOmxComponent.h b/es-core/src/components/VideoOmxComponent.h deleted file mode 100644 index 7a5b883ce..000000000 --- a/es-core/src/components/VideoOmxComponent.h +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: MIT -// -// EmulationStation Desktop Edition -// VideoOmxComponent.h -// -// Video playing using OMXPlayer for Raspberry Pi. -// - -#if defined(_RPI_) -#ifndef ES_CORE_COMPONENTS_VIDEO_OMX_COMPONENT_H -#define ES_CORE_COMPONENTS_VIDEO_OMX_COMPONENT_H - -#include "components/VideoComponent.h" - -void catch_child(int sig_num); - -class VideoOmxComponent : public VideoComponent -{ -public: - VideoOmxComponent(Window* window); - virtual ~VideoOmxComponent(); - - void render(const Transform4x4f& parentTrans) override; - - // Resize the video to fit this size. If one axis is zero, scale that axis to maintain - // aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are - // zero, no resizing. This can be set before or after a video is loaded. - // setMaxSize() and setResize() are mutually exclusive. - void setResize(float width, float height) override; - - // Resize the video to be as large as possible but fit within a box of this size. - // This can be set before or after a video is loaded. - // Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive. - void setMaxSize(float width, float height) override; - -private: - // Start the video Immediately. - virtual void startVideo() override; - // Stop the video. - virtual void stopVideo() override; - -private: - pid_t mPlayerPid; -}; - -#endif // ES_CORE_COMPONENTS_VIDEO_OMX_COMPONENT_H -#endif // _RPI_ From e4ee4855df0da42edffb25e764b10ef44e6cb0e3 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 8 Jul 2021 18:47:29 +0200 Subject: [PATCH 47/76] Changed the header text in main.cpp --- es-app/src/main.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index ea58e90d7..ea023c41b 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -1,15 +1,14 @@ // SPDX-License-Identifier: MIT // -// EmulationStation Desktop Edition, an emulator front-end -// with controller navigation and theming support. +// EmulationStation Desktop Edition (ES-DE) is a front-end for browsing +// and launching games from your multi-platform game collection. // -// Originally created by Alec "Aloshi" Lofquist. -// http://www.aloshi.com +// Originally created by Alec Lofquist. // Improved and extended by the RetroPie community. // Desktop Edition fork by Leon Styhre. // -// The line length limit is 100 characters and the indentations are 4 spaces. -// Line breaks are Unix-style (line feed only). +// The column limit is 100 characters. +// All ES-DE C++ source code is formatted using clang-format. // // main.cpp // From 3b4bb74ac027e5d6f47b575a103921f2a44aa493 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 9 Jul 2021 19:47:33 +0200 Subject: [PATCH 48/76] VideoFFmpegComponent now prioritizes audio by dropping video frames if the computer can't keep up. --- .../src/components/VideoFFmpegComponent.cpp | 21 +++++++++++++++---- es-core/src/components/VideoFFmpegComponent.h | 7 +++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/es-core/src/components/VideoFFmpegComponent.cpp b/es-core/src/components/VideoFFmpegComponent.cpp index 120d24c40..d1ca50b39 100644 --- a/es-core/src/components/VideoFFmpegComponent.cpp +++ b/es-core/src/components/VideoFFmpegComponent.cpp @@ -239,8 +239,8 @@ void VideoFFmpegComponent::frameProcessing() { mWindow->increaseVideoPlayerCount(); - bool videoFilter; - bool audioFilter; + bool videoFilter = false; + bool audioFilter = false; videoFilter = setupVideoFilters(); @@ -527,9 +527,22 @@ void VideoFFmpegComponent::readFrames() if (!avcodec_send_packet(mVideoCodecContext, mPacket) && !avcodec_receive_frame(mVideoCodecContext, mVideoFrame)) { + int returnValue = 0; + // We have a video frame that needs conversion to RGBA format. - int returnValue = av_buffersrc_add_frame_flags( - mVBufferSrcContext, mVideoFrame, AV_BUFFERSRC_FLAG_KEEP_REF); + // Prioritize audio by dropping video frames if the audio frame queue + // gets too small, i.e. if the computer can't keep up the processing. + if ((!mAudioCodecContext || !mDecodedFrame || + mAudioFrameCount < mAudioTargetQueueSize) || + mAudioFrameQueue.size() > 3) { + returnValue = av_buffersrc_add_frame_flags( + mVBufferSrcContext, mVideoFrame, AV_BUFFERSRC_FLAG_KEEP_REF); + } + else { + LOG(LogDebug) + << "VideoFFmpegComponent::readFrames(): Dropped video frame as " + "the audio buffer was too small"; + } if (returnValue < 0) { LOG(LogError) << "VideoFFmpegComponent::readFrames(): " diff --git a/es-core/src/components/VideoFFmpegComponent.h b/es-core/src/components/VideoFFmpegComponent.h index d7a134be8..11e9840e0 100644 --- a/es-core/src/components/VideoFFmpegComponent.h +++ b/es-core/src/components/VideoFFmpegComponent.h @@ -145,14 +145,13 @@ private: // Used for audio and video synchronization. std::chrono::high_resolution_clock::time_point mTimeReference; + int mAudioFrameCount; + int mVideoFrameCount; + double mAccumulatedTime; bool mStartTimeAccumulation; bool mDecodedFrame; bool mEndOfVideo; - - // These are only used for debugging. - int mAudioFrameCount; - int mVideoFrameCount; }; #endif // ES_CORE_COMPONENTS_VIDEO_FFMPEG_COMPONENT_H From 24fd136099f3f28224b938a6ae9ce5240a51a3b6 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 9 Jul 2021 19:50:59 +0200 Subject: [PATCH 49/76] Fixed the build of CEC support on the Raspberry Pi. --- CMakeLists.txt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d9a396d70..4dc5c7494 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,12 +32,11 @@ option(VLC_PLAYER "Set to ON to build the VLC-based video player" ${VLC_PLAYER}) # Check if we're running on a Raspberry Pi. if(EXISTS "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/bcm_host.h") message("-- Building on a Raspberry Pi (bcm_host.h found)") - # Setting BCMHOST seems to break OpenGL ES on the RPi 4? + # Setting BCMHOST seems to break OpenGL ES on the RPi 4 so set RPI instead. #set(BCMHOST found) - # Simply set RPI instead to enable the RPi-specific parts of the code. set(RPI ON) set(GLSystem "Embedded OpenGL" CACHE STRING "The OpenGL system to be used") -elseif(GLES) +elseif(GLES OR RPI) set(GLSystem "Embedded OpenGL" CACHE STRING "The OpenGL system to be used") else() set(GLSystem "Desktop OpenGL" CACHE STRING "The OpenGL system to be used") @@ -250,7 +249,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") list(APPEND COMMON_INCLUDE_DIRS ${ALSA_INCLUDE_DIRS}) endif() -if(DEFINED BCMHOST) +if(DEFINED BCMHOST OR RPI) list(APPEND COMMON_INCLUDE_DIRS "${CMAKE_FIND_ROOT_PATH}/opt/vc/include" "${CMAKE_FIND_ROOT_PATH}/opt/vc/include/interface/vcos" @@ -335,8 +334,8 @@ endif() # Add libCEC libraries. if(DEFINED libCEC_FOUND) - if(DEFINED BCMHOST) - list(APPEND COMMON_LIBRARIES vchiq_arm) + if(DEFINED BCMHOST OR RPI) + list(APPEND COMMON_LIBRARIES bcm_host vchiq_arm) endif() list(APPEND COMMON_LIBRARIES dl ${libCEC_LIBRARIES}) endif() @@ -349,6 +348,9 @@ endif() if(DEFINED BCMHOST) link_directories("${CMAKE_FIND_ROOT_PATH}/opt/vc/lib") list(APPEND COMMON_LIBRARIES bcm_host brcmEGL ${OPENGLES_LIBRARIES}) +elseif(DEFINED RPI) + link_directories("${CMAKE_FIND_ROOT_PATH}/opt/vc/lib") + list(APPEND COMMON_LIBRARIES ${OPENGLES_LIBRARIES}) endif() if(${GLSystem} MATCHES "Desktop OpenGL") From aed9e3970b3be8d0e1f5ae8e4918d4e2f80cb2ad Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 9 Jul 2021 19:54:54 +0200 Subject: [PATCH 50/76] Added support for building with the clang-tidy static analyzer. --- CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dc5c7494..9b5426a4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,15 @@ option(GL "Set to ON if targeting Desktop OpenGL" ${GL}) option(RPI "Set to ON to enable Raspberry Pi specific build" ${RPI}) option(CEC "Set to ON to enable CEC" ${CEC}) option(VLC_PLAYER "Set to ON to build the VLC-based video player" ${VLC_PLAYER}) +option(CLANG_TIDY "Set to ON to build using the clang-tidy static analyzer" ${CLANG_TIDY}) + +if (CLANG_TIDY) + message("-- Building with the clang-tidy static analyzer") + set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=*,-fuchsia-*,-hicpp-*,-llvm-*, \ + -readability-braces-*,-google-readability-braces-*, \ + -readability-uppercase-literal-suffix,-modernize-use-trailing-return-type, \ + -cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers") +endif() #--------------------------------------------------------------------------------------------------- # OpenGL setup. From 1cee40f291add2e7dd62b0f5d0ba9af23a846c98 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 9 Jul 2021 19:58:21 +0200 Subject: [PATCH 51/76] Some minor changes proposed by the static analyzer. --- es-app/src/MiximageGenerator.cpp | 2 +- es-core/src/AudioManager.cpp | 7 ++----- es-core/src/Scripting.cpp | 18 ++++++++++++------ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/es-app/src/MiximageGenerator.cpp b/es-app/src/MiximageGenerator.cpp index d16105bef..12e2f6f4a 100644 --- a/es-app/src/MiximageGenerator.cpp +++ b/es-app/src/MiximageGenerator.cpp @@ -627,7 +627,7 @@ void MiximageGenerator::sampleFrameColor(CImg& screenshotImage, // Convert to the HSL color space to be able to modify saturation and lightness. CImg colorHSL = CImg<>(1, 1, 1, 3).fill(redC, greenC, blueC).RGBtoHSL(); - float hue = colorHSL(0, 0, 0, 0); + // float hue = colorHSL(0, 0, 0, 0); float saturation = colorHSL(0, 0, 0, 1); float lightness = colorHSL(0, 0, 0, 2); diff --git a/es-core/src/AudioManager.cpp b/es-core/src/AudioManager.cpp index 8ca75fa41..b3f9e7632 100644 --- a/es-core/src/AudioManager.cpp +++ b/es-core/src/AudioManager.cpp @@ -307,17 +307,14 @@ void AudioManager::clearStream() // SDL_AudioStreamClear(sConversionStream); mIsClearingStream = true; - - int streamSize; int length = sAudioFormat.samples * 4; - while ((streamSize = SDL_AudioStreamAvailable(sConversionStream)) > 0) { + while (SDL_AudioStreamAvailable(sConversionStream) > 0) { std::vector readBuffer(length); int processedLength = SDL_AudioStreamGet(sConversionStream, static_cast(&readBuffer.at(0)), length); - if (processedLength <= 0) { + if (processedLength <= 0) break; - } } mIsClearingStream = false; diff --git a/es-core/src/Scripting.cpp b/es-core/src/Scripting.cpp index 5e3cd926f..dadb3db36 100644 --- a/es-core/src/Scripting.cpp +++ b/es-core/src/Scripting.cpp @@ -37,11 +37,9 @@ namespace Scripting if (Utils::FileSystem::exists(scriptDir)) scriptDirList.push_back(scriptDir); - for (std::list::const_iterator dirIt = scriptDirList.cbegin(); - dirIt != scriptDirList.cend(); dirIt++) { + for (auto dirIt = scriptDirList.cbegin(); dirIt != scriptDirList.cend(); dirIt++) { std::list scripts = Utils::FileSystem::getDirContent(*dirIt); - for (std::list::const_iterator it = scripts.cbegin(); // Line break. - it != scripts.cend(); it++) { + for (auto it = scripts.cbegin(); it != scripts.cend(); it++) { std::string arg1Quotation; std::string arg2Quotation; // Add quotation marks around the arguments as long as these are not already @@ -50,8 +48,16 @@ namespace Scripting arg1Quotation = "\""; if (arg2.front() != '\"') arg2Quotation = "\""; - std::string script = *it + " " + arg1Quotation + arg1 + arg1Quotation + " " + - arg2Quotation + arg2 + arg2Quotation; + std::string script; + script.append(*it) + .append(" ") + .append(arg1Quotation) + .append(arg1) + .append(arg1Quotation) + .append(" ") + .append(arg2Quotation) + .append(arg2) + .append(arg2Quotation); LOG(LogDebug) << "Executing: " << script; runSystemCommand(script); } From 6a7eb982f2219673edb7e9f3d38db42f221264b9 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 9 Jul 2021 21:32:47 +0200 Subject: [PATCH 52/76] Now checking that clang-tidy is actually installed before attempting to use it. --- CMakeLists.txt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b5426a4d..4059d6ef9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,11 +28,16 @@ option(VLC_PLAYER "Set to ON to build the VLC-based video player" ${VLC_PLAYER}) option(CLANG_TIDY "Set to ON to build using the clang-tidy static analyzer" ${CLANG_TIDY}) if (CLANG_TIDY) - message("-- Building with the clang-tidy static analyzer") - set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=*,-fuchsia-*,-hicpp-*,-llvm-*, \ - -readability-braces-*,-google-readability-braces-*, \ - -readability-uppercase-literal-suffix,-modernize-use-trailing-return-type, \ - -cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers") + find_program(CLANG_TIDY_BINARY NAMES clang-tidy) + if("${CLANG_TIDY_BINARY}" STREQUAL "CLANG_TIDY_BINARY-NOTFOUND") + message("-- CLANG_TIDY was set but the clang-tidy binary was not found") + else() + message("-- Building with the clang-tidy static analyzer") + set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=*,-fuchsia-*,-hicpp-*,-llvm-*, \ + -readability-braces-*,-google-readability-braces-*, \ + -readability-uppercase-literal-suffix,-modernize-use-trailing-return-type, \ + -cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers") + endif() endif() #--------------------------------------------------------------------------------------------------- From 717eb98a6e25a9f8ee2b148b905585578ec66c12 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 9 Jul 2021 22:04:16 +0200 Subject: [PATCH 53/76] Documentation update. --- CHANGELOG.md | 4 ++++ CREDITS.md | 2 +- INSTALL-DEV.md | 52 ++++++++++++++++++++++++++++++++++++-------------- README.md | 2 +- 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f0a78b2a..f68f891b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,9 +73,12 @@ Apart from this, many small improvements and bug fixes are part of the release, * Added the CImg library as a Git subtree and created some utility functions for it (used by the miximage generator and the game launch screen) * Added a function to ImageComponent to crop fully transparent areas around an image * Added a CMake option to control whether the VLC video player should be built, and set this to off by default +* Made it possible to build on the Raspberry Pi 4 (Raspberry Pi OS) +* Removed the deprecated VideoOmxComponent * Removed the pointless APPLE_SKIP_INSTALL_LIBS CMake option * Added a clang-format style configuration file to use for automatic code formatting * Formatted the entire codebase using clang-format +* Integrated clang-tidy with CMake and made it possible to enable it via a flag * Added the NanoSVG library as a proper Git subtree * Changed the language standard from C++11 to C++14 @@ -96,6 +99,7 @@ Apart from this, many small improvements and bug fixes are part of the release, * The Quick System Select help prompt was shown even when there was only a single game system present * The "Back (cancel)" help prompt was missing for the single-game scraper * The "Y" button help prompt wasn't displayed correctly when using the Grid view style +* Fractional game rating values would always get rounded up * Encountering a corrupt image file would lead to a continuous loop of attempts to load the image while filling the log file with error messages * Cropping in ImageComponent didn't work correctly * The debug logging for the analog controller inputs had some inconsistent signs diff --git a/CREDITS.md b/CREDITS.md index 109115805..e4a87ab34 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -2,7 +2,7 @@ # Programming -Alec "Aloshi" Lofquist (original version) \ +Alec Lofquist (original version) \ http://www.aloshi.com RetroPie Community (RetroPie fork) \ diff --git a/INSTALL-DEV.md b/INSTALL-DEV.md index 9823d176f..27dc44984 100644 --- a/INSTALL-DEV.md +++ b/INSTALL-DEV.md @@ -14,7 +14,7 @@ ES-DE is developed and compiled using Clang/LLVM and GCC on Unix, Clang/LLVM on CMake is the build system for all the supported operating systems, used in conjunction with `make` on Unix and macOS and `nmake` and `make` on Windows. Xcode on macOS or Visual Studio on Windows are not required for building ES-DE and they have not been used during the development. The only exception is notarization of codesigned macOS packages which require the `altool` and `stapler` binaries that come bundled with Xcode. -For automatic code formatting clang-format is used. +For automatic code formatting [clang-format](https://clang.llvm.org/docs/ClangFormat.html) is used. Any code editor can be used of course, but I recommend [VSCode](https://code.visualstudio.com). @@ -25,7 +25,7 @@ The code has some dependencies. For building, you'll need CMake and development **Installing dependencies:** -**On Debian/Ubuntu** +**Debian/Ubuntu** All of the required packages can be installed with apt-get: ``` @@ -37,7 +37,7 @@ If building with the optional VLC video player, the following packages are also sudo apt-get install vlc libvlc-dev ``` -**On Fedora** +**Fedora** Use dnf to install all the required packages: ``` @@ -53,7 +53,7 @@ sudo dnf install ./rpmfusion-free-release-33.noarch.rpm sudo dnf install vlc vlc-devel ``` -**On Manjaro** +**Manjaro** Use pacman to install all the required packages: @@ -66,7 +66,26 @@ If building with the optional VLC video player, the following package is also ne sudo pacman -S vlc ``` -**On FreeBSD** +**Raspberry Pi OS (Raspian)** + +Note: The Raspberry Pi 4 is the minimum recommended model to use with ES-DE. As this type of device is quite weak and because the FFmpeg video player currently lacks hardware decoding/acceleration it is strongly adviced to build with the VLC player, which is hardware accelerated. + +All of the required packages can be installed with apt-get: +``` +sudo apt-get install clang-format cmake libsdl2-dev libavcodec-dev libavfilter-dev libavformat-dev libavutil-dev libfreeimage-dev libcurl4-gnutls-dev libpugixml-dev rapidjson-dev +``` + +If building with the optional VLC video player, the following packages are also needed: +``` +sudo apt-get install vlc libvlc-dev +``` + +To build with CEC support you also need to install these packages: +``` +sudo apt-get install libcec-dev libp8-platform-dev +``` + +**FreeBSD** Use pkg to install the dependencies: ``` @@ -80,7 +99,7 @@ pkg install vlc Clang/LLVM and cURL should already be installed in the base OS image. -**On NetBSD** +**NetBSD** Use pkgin to install the dependencies: ``` @@ -94,7 +113,7 @@ pkgin vlc NetBSD ships with GCC by default, and although you should be able to use Clang/LLVM, it's probably easier to just stick to the default compiler environment. The reason why the clang package needs to be installed is to get clang-format onto the system. -**On OpenBSD** +**OpenBSD** Use pkg_add to install the dependencies: ``` @@ -209,7 +228,14 @@ scan-build cmake -DCMAKE_BUILD_TYPE=Debug . scan-build make -j6 ``` -You open the report with the `scan-view` command which lets you browse it using your web browser. Note that the compilation time is much higher when using the static analyzer compared to a normal compilation. As well this tool generates a lot of extra files and folders in the build tree, so it may make sense to run it in a separate copy of the source folder to avoid having to clean up all this extra data when the analysis has been completed. +You open the report with the `scan-view` command which lets you browse it using your web browser. Note that the compilation time is much longer when using the static analyzer compared to a normal build. As well this tool generates a lot of extra files and folders in the build tree, so it may make sense to run it in a separate copy of the source folder to avoid having to clean up all this extra data when the analysis has been completed. + +An even more advanced static analyzer is `clang-tidy`, to use it first make sure it's installed on your system and then run the following: +``` +cmake -DCLANG_TIDY=on . +``` + +Even though many irrelevant checks are filtered out via the configuration, this will still likely produce a quite huge report (at least until most of the recommendations have been implemented). In the same manner as for scan-view, the compilation time is much longer when using this static analyzer compared to a normal build. To build ES-DE with CEC support, enable the corresponding option, for example: @@ -217,14 +243,14 @@ To build ES-DE with CEC support, enable the corresponding option, for example: cmake -DCMAKE_BUILD_TYPE=Debug -DCEC=on . make ``` -I have however not been able to test the CEC support and I'm not entirely sure how it's supposed to work. +You will most likely need to install additional packages to get this to build. On Debian-based systems these are _libcec-dev_ and _libp8-platform-dev_. Note that the CEC support is currently untested. To build with the GLES renderer, run the following: ``` cmake -DCMAKE_BUILD_TYPE=Debug -DGLES=on . make ``` -Note that the GLES renderer is quite limited as there is no shader support for it, so ES-DE will definitely not look as pretty as when using the default OpenGL renderer. +The GLES renderer is quite limited as there is no shader support for it, so ES-DE will definitely not look as pretty as when using the default OpenGL renderer. When building on a Raspberry Pi, the GLES renderer will be automatically selected. Running multiple compile jobs in parallel is a good thing as it speeds up the build time a lot (scaling almost linearly). Here's an example telling make to run 6 parallel jobs: @@ -265,7 +291,7 @@ This Clang debug build is LLVM "native", i.e. intended to be debugged using the It's possible to activate the additional debug info needed by GDB by using the flag `-D_GLIBCXX_DEBUG`. I've added this to CMakeLists.txt when using Clang, but this bloats the binary and makes the code much slower. Actually, instead of a 4% faster application startup, it's now 25% slower. The same goes for the binary size, instead of 31% smaller it's now 5% larger. The compilation time is still less than GCC but only by 10% instead of 25%. -I'm expecting this to be resolved in the near future though, and as I think Clang has the best error messages and comes with good tools such as the static analyzer, this is now my primary compiler. +But I'm expecting this issue to be resolved in the near future so the workaround can be removed. It's by the way very easy to switch between LLVM and GCC using Ubuntu, just use the `update-alternatives` command: @@ -1149,9 +1175,7 @@ So the home directory will always take precedence, and any resources or themes l The entire ES-DE codebase is formatted using clang-format and all code commits must have been processed using this tool. -There is a style configuration file named .clang-format located directly at the root of the repository which contains the formatting rules. - -How to install clang-format is detailed per operating system earlier in this document. +There is a style configuration file named .clang-format located directly at the root of the repository which contains the formatting rules. How to install clang-format is detailed per operating system earlier in this document. There are two ways to run the tool, from the command line or from inside an editor such as VSCode. diff --git a/README.md b/README.md index 965ba6d40..7a2bf9373 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ The following operating systems have been tested with ES-DE (all for the x86 arc **Note:** If using a Mac with an ARM CPU (e.g. M1) you need to install the x86 version of RetroArch and any other emulators, or you won't be able to launch any games. This will be fixed whenever a native macOS ARM build of ES-DE is released. -At the moment Raspberry Pi is not supported, but this is planned for version 1.2. It may still be possible to compile and run ES-DE on this type of device, but as of v1.1 it's not actively used during development and therefore not tested. +The Raspberry Pi 4 has been tested as well (with Raspberry Pi OS) but it's a bit buggy at the moment and is missing some features like GLSL shaders. Official support will probably be added for this device as of ES-DE v1.2. ### Download From e000e23f64d90007fb7764c7caf606a3f2be6feb Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 10 Jul 2021 13:07:44 +0200 Subject: [PATCH 54/76] Added and clarified startup log warnings for missing or invalid es_systems.xml platform tags. --- es-app/src/SystemData.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/es-app/src/SystemData.cpp b/es-app/src/SystemData.cpp index 1a2f661c0..6f95bf97a 100644 --- a/es-app/src/SystemData.cpp +++ b/es-app/src/SystemData.cpp @@ -466,6 +466,12 @@ bool SystemData::loadConfig() // Platform ID list const std::string platformList = Utils::String::toLower(system.child("platform").text().get()); + + if (platformList == "") { + LOG(LogWarning) << "No platform defined for system \"" << name + << "\", scraper searches will be inaccurate"; + } + std::vector platformStrs = readList(platformList); std::vector platformIds; for (auto it = platformStrs.cbegin(); it != platformStrs.cend(); it++) { @@ -483,7 +489,7 @@ bool SystemData::loadConfig() // platforms, then generate a warning. if (str != "" && platformId == PlatformIds::PLATFORM_UNKNOWN) LOG(LogWarning) << "Unknown platform \"" << str << "\" defined for system \"" - << name << "\""; + << name << "\", scraper searches will be inaccurate"; else if (platformId != PlatformIds::PLATFORM_UNKNOWN) platformIds.push_back(platformId); } From 41f4cce5d6b14812e17650f1288b5720323ea764 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 10 Jul 2021 13:54:12 +0200 Subject: [PATCH 55/76] Documentation update. --- CHANGELOG.md | 1 + INSTALL-DEV.md | 253 ++++++++++++++++++++++++------------------------- 2 files changed, 126 insertions(+), 128 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f68f891b4..510cae626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,7 @@ Apart from this, many small improvements and bug fixes are part of the release, * Added a DebugSkipInputLogging option which is intended primarily for development and needs to be manually set in es_settings.xml * Added the CImg library as a Git subtree and created some utility functions for it (used by the miximage generator and the game launch screen) * Added a function to ImageComponent to crop fully transparent areas around an image +* Added and clarified startup log warnings for missing or invalid es_systems.xml platform tags * Added a CMake option to control whether the VLC video player should be built, and set this to off by default * Made it possible to build on the Raspberry Pi 4 (Raspberry Pi OS) * Removed the deprecated VideoOmxComponent diff --git a/INSTALL-DEV.md b/INSTALL-DEV.md index 27dc44984..092ed7250 100644 --- a/INSTALL-DEV.md +++ b/INSTALL-DEV.md @@ -21,9 +21,7 @@ Any code editor can be used of course, but I recommend [VSCode](https://code.vis ## Building on Unix -The code has some dependencies. For building, you'll need CMake and development packages for cURL, FFmpeg, FreeImage, FreeType, pugixml, SDL2 and RapidJSON (and optionally VLC). - -**Installing dependencies:** +There are some dependencies that need to be fulfilled in order to build ES-DE. These are detailed per operating system below. **Debian/Ubuntu** @@ -46,7 +44,7 @@ sudo dnf install gcc-c++ clang-tools-extra cmake SDL2-devel ffmpeg-devel freeima If building with the VLC video player, add the RPM Fusion repository. -Go to [https://rpmfusion.org/Configuration](https://rpmfusion.org/Configuration) and download the .rpm package for the free repository, then install it as well as VLC: +Go to [https://rpmfusion.org/Configuration](https://rpmfusion.org/Configuration) and download the .rpm package for the free repository. Then install it, followed by VLC: ``` sudo dnf install ./rpmfusion-free-release-33.noarch.rpm @@ -97,7 +95,7 @@ If building with the optional VLC video player, the following package is also ne pkg install vlc ``` -Clang/LLVM and cURL should already be installed in the base OS image. +Clang/LLVM and cURL should already be included in the base OS installation. **NetBSD** @@ -164,7 +162,7 @@ cmake . make ``` -By default the master branch will be used, which is where the development takes place. To instead build the latest stable release, switch to the `stable` branch: +By default the master branch will be used, which is where development takes place. To instead build the latest stable release, switch to the `stable` branch: ``` cd emulationstation-de @@ -228,7 +226,7 @@ scan-build cmake -DCMAKE_BUILD_TYPE=Debug . scan-build make -j6 ``` -You open the report with the `scan-view` command which lets you browse it using your web browser. Note that the compilation time is much longer when using the static analyzer compared to a normal build. As well this tool generates a lot of extra files and folders in the build tree, so it may make sense to run it in a separate copy of the source folder to avoid having to clean up all this extra data when the analysis has been completed. +You open the report with the `scan-view` command which lets you read it using your web browser. Note that the compilation time is much longer when using the static analyzer compared to a normal build. As well this tool generates a lot of extra files and folders in the build tree, so it may make sense to run it in a separate copy of the source folder to avoid having to clean up all this extra data when the analysis has been completed. An even more advanced static analyzer is `clang-tidy`, to use it first make sure it's installed on your system and then run the following: ``` @@ -240,14 +238,14 @@ Even though many irrelevant checks are filtered out via the configuration, this To build ES-DE with CEC support, enable the corresponding option, for example: ``` -cmake -DCMAKE_BUILD_TYPE=Debug -DCEC=on . +cmake -DCEC=on . make ``` You will most likely need to install additional packages to get this to build. On Debian-based systems these are _libcec-dev_ and _libp8-platform-dev_. Note that the CEC support is currently untested. To build with the GLES renderer, run the following: ``` -cmake -DCMAKE_BUILD_TYPE=Debug -DGLES=on . +cmake -DGLES=on . make ``` The GLES renderer is quite limited as there is no shader support for it, so ES-DE will definitely not look as pretty as when using the default OpenGL renderer. When building on a Raspberry Pi, the GLES renderer will be automatically selected. @@ -266,7 +264,7 @@ The following example will build the application for installtion under /opt: cmake -DCMAKE_INSTALL_PREFIX=/opt . ``` -It's important to know that this is not only the directory used by the install script, the CMAKE_INSTALL_PREFIX variable also modifies code inside ES-DE used to locate the required program resources. So it's necessary that the install prefix corresponds to the location where the application will actually be installed. +It's important to understand that this is not only the directory used by the install script, the CMAKE_INSTALL_PREFIX variable also modifies code inside ES-DE used to locate the required program resources. So it's necessary that the install prefix corresponds to the location where the application will actually be installed. On Linux, if you're not building a package and instead intend to install using `make install` it's recommended to set the installation prefix to /usr/local instead of /usr. @@ -291,7 +289,7 @@ This Clang debug build is LLVM "native", i.e. intended to be debugged using the It's possible to activate the additional debug info needed by GDB by using the flag `-D_GLIBCXX_DEBUG`. I've added this to CMakeLists.txt when using Clang, but this bloats the binary and makes the code much slower. Actually, instead of a 4% faster application startup, it's now 25% slower. The same goes for the binary size, instead of 31% smaller it's now 5% larger. The compilation time is still less than GCC but only by 10% instead of 25%. -But I'm expecting this issue to be resolved in the near future so the workaround can be removed. +But I'm expecting this issue to be resolved in the future so the workaround can be removed. It's by the way very easy to switch between LLVM and GCC using Ubuntu, just use the `update-alternatives` command: @@ -337,7 +335,7 @@ However, when installing manually instead of building a package, it's recommende Be aware that if using the GNOME desktop environment, /usr/share/pixmaps/emulationstation.svg must exist in order for the ES-DE icon to be shown in the Dash and task switcher. -ES-DE will look in the following locations for the resources, in the listed order: +ES-DE will look in the following locations for application resources, in the listed order: * \/.emulationstation/resources/ * \/share/emulationstation/resources/ @@ -345,7 +343,7 @@ ES-DE will look in the following locations for the resources, in the listed orde The resources directory is critical, without it the application won't start. -And it will look in the following locations for the themes, also in the listed order: +As well the following locations will be searched for themes, also in the listed order: * \/.emulationstation/themes/ * \/share/emulationstation/themes/ @@ -353,7 +351,7 @@ And it will look in the following locations for the themes, also in the listed o A theme is not mandatory to start the application, but ES-DE will be basically useless without it. -The home directory will always take precedence, and any resources or themes located there will override the ones in the installation path or in the path of the ES-DE executable. +As indicated above, the home directory will always take precedence and any resources or themes located there will override the ones in the installation path, or in the path of the ES-DE executable. **Creating .deb and .rpm packages** @@ -370,7 +368,7 @@ CPackDeb: - Generating dependency list CPack: - package: /home/myusername/emulationstation-de/emulationstation-de-1.1.0-x64.deb generated. ``` -You may like to check that the dependencies look fine, as they're automatically generated by CMake: +You may want to check that the dependencies look fine, as they're (mostly) automatically generated by CMake: ``` dpkg -I ./emulationstation-de-1.1.0-x64.deb @@ -401,17 +399,17 @@ CPackRPM: Will use GENERATED spec file: /home/myusername/emulationstation-de/_CP CPack: - package: /home/myusername/emulationstation-de/emulationstation-de-1.1.0-x64.rpm generated. ``` -On Fedora, you need to install rpmbuild before this command can be run though: +On Fedora, you need to install rpmbuild before this command can be run: ``` sudo dnf install rpm-build ``` -After the package generation you can check that the metadata looks fine using this command: +After the package generation you can check that the metadata looks fine using the `rpm` command: ``` rpm -qi ./emulationstation-de-1.1.0-x64.rpm ``` -And to see the automatically generated dependency requirements, run this: +To see the automatically generated dependencies, run this: ``` rpm -q --requires ./emulationstation-de-1.1.0-x64.rpm ``` @@ -431,7 +429,7 @@ As for code editing, I use [VSCode](https://code.visualstudio.com). I suppose Xc Install the Command Line Tools which include Clang/LLVM, Git, make etc. Simply open a terminal and enter the command `clang`. This will open a dialog that will let you download and install the tools. -Following this, install the Homebrew package manager which will simplify the rest of the installation greatly. Install it by runing the following in a terminal window: +Following this, install the Homebrew package manager which will greatly simplify the rest of the installation. Install it by runing the following in a terminal window: ``` /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" ``` @@ -453,7 +451,7 @@ brew install --cask vlc **Compiling FFmpeg:** -The FFmpeg build distributed via Homebrew has a lot of dependencies we don't need, and which would make it very difficult to package it in an installer. Instead we will build this software with only some limited options: +The FFmpeg build distributed via Homebrew has a lot of dependencies we don't need, and which would make it very difficult to package the application. Instead we will build this software with only some limited options: ``` git clone https://github.com/FFmpeg/FFmpeg.git @@ -470,7 +468,7 @@ Enable developer mode to avoid annoying password requests when attaching the deb ``` sudo /usr/sbin/DevToolsSecurity --enable ``` -It makes me wonder who designed this functionality and what was their thinking, but a simple command is enough to not having to ponder this any further. +It makes me wonder who designed this functionality and what was their thinking, I've never seen anything like this on any of the other systems I've been developing on. If required, define SDKROOT. This is only needed if during compilation you get error messages regarding missing include files. Running the following will properly setup the development environment paths: ``` @@ -504,36 +502,35 @@ cmake . make ``` -To build ES-DE with the VLC video player in addition to the default FFmpeg player, enable the VLC_PLAYER option, for example: +To build ES-DE with the VLC video player in addition to the default FFmpeg player, enable the VLC_PLAYER option: ``` cmake -DVLC_PLAYER=on . make ``` -To generate a debug build, run this instead: +To generate a debug build, run this: ``` cmake -DCMAKE_BUILD_TYPE=Debug . make ``` -Keep in mind though that a debug version will be much slower due to all compiler optimizations being disabled. +Keep in mind that the debug version will be much slower due to all compiler optimizations being disabled. Running `make -j6` (or whatever number of parallel jobs you prefer) speeds up the compilation time if you have cores to spare. -After building ES-DE and trying to execute the application, there could be issues with finding the dynamic link libraries for VLC (assuming VLC is actually enabled for the build) as these are not installed into a standard location but rather into the /Applications folder. As such, you may need to set the DYLD_LIBRARY_PATH environmental variable to find the VLC libraries. Note that this is not intended or required for the release build that will be shipped in a DMG installer or if you manually install ES-DE using 'make install'. It's only needed to be able to run the binary from the build directory. The following will of course only be active during your session, and you need to set the variable for each terminal window that you want to start ES-DE from, unless you add it to your shell profile file: - +After building ES-DE and trying to execute the application, there could be issues with finding the dynamic link libraries for VLC (assuming VLC was enabled for the build) as these are not installed into a standard location but rather into the /Applications folder. As such, you may need to set the DYLD_LIBRARY_PATH environmental variable to find the VLC libraries. Note that this is not intended or required for the release build that will be shipped in a DMG installer or if you manually install ES-DE using _make install_. It's only needed to be able to run the binary from the build directory. You should add this to your shell profile file to avoid having to set it each time you open a new terminal window: ``` export DYLD_LIBRARY_PATH=/Applications/VLC.app/Contents/MacOS/lib ``` -**Note:** Running ES-DE from the build directory may be a bit flaky as there is no Info.plist file available which is required for setting the proper window mode and such. It's therefore recommended to run the application from the installation directory for any more in-depth testing. But normal debugging can of course be done from the build directory. +Running ES-DE from the build directory may be a bit flaky as there is no Info.plist file available which is required for setting the proper window mode and such. It's therefore recommended to run the application from the installation directory for any more in-depth testing. But normal debugging can of course be done from the build directory. Be aware that the approach taken for macOS has the limitation that you can't build for previous operating system versions. You can certainly set CMAKE_OSX_DEPLOYMENT_TARGET to whatever version you like, but the problem is that the Homebrew libraries will most likely not work on earlier macOS versions. In theory this can be worked around by building all these libraries yourself with a lower deployment target, but it's hardly worth the effort. It's better to build on the lowest OS version that should be supported and rely on forward compatibility. **Code signing:** -Due to the Apple notarization requirement implemented as of macOS 10.14.5 a build with simple code signing is needed for versions up to 10.13 and another build with both code signing and notarization will be required for versions 10.14 and higher. +Due to the Apple notarization requirement implemented as of macOS 10.14.5 a build with simple code signing is needed for versions up to 10.13 and another build with both code signing and notarization is required for version 10.14 and higher. -macOS code signing is beyond the scope of this document, but the option MACOS_CODESIGN_IDENTITY is used to to specify the code signing certificate identity, for example: +macOS code signing is beyond the scope of this document, but the option MACOS_CODESIGN_IDENTITY is used to specify the code signing certificate identity, for example: ``` cmake -DMACOS_CODESIGN_IDENTITY="My Name" . ``` @@ -542,12 +539,14 @@ Assuming the code signing ceritificate is properly setup in Keychain Access, the **Legacy build:** -Normally ES-DE is meant to be built for macOS 10.14 and higher, but a legacy build for earlier operating system versions can be enabled. This has been tested with a minimum version of 10.11 and it's unclear if it works with even older macOS versions. +Normally ES-DE is meant to be built for macOS 10.14 and higher, but a legacy build for earlier operating system versions can be enabled. This has been tested with a minimum version of 10.11. It's unclear if it works with even older macOS versions. To enable a legacy build, change the CMAKE_OSX_DEPLOYMENT_TARGET variable in CMakeLists.txt from 10.14 to whatever version you would like to build for. This will disable Hardened Runtime if signing is enabled and it will add 'legacy' to the DMG installer file name when running CPack. You also need to modify es-app/assets/EmulationStation-DE_Info.plist and set the key SMinimumSystemVersion to the version you're building for. +Due to issues with getting FFmpeg to compile on some older macOS versions, ES-DE v1.0.1 is the last version where a legacy build has been tested. + **Installing:** As macOS does not have any package manager which would have handled the library dependencies, we need to bundle the required shared libraries with the application. Copy the following .dylib files from their respective installation directories to the emulationstation-de build directory: @@ -573,12 +572,12 @@ All except the VLC libraries should be located in /usr/local/lib. The VLC librar Note that the filenames could be slightly different depending on what versions you have installed on your system. -After copying the libraries to the build directory, set the permissions to writable: +After copying the libraries to the build directory, set their permissions like this: ``` chmod 755 ./*.dylib ``` -There are some secondary internal dependencies between some of these library files, and these are baked into the files as absolut paths. As such we need to rewrite these to rpaths (relative paths) which is done using the install_name_tool command. +There are some secondary internal dependencies between some of these library files, and these are baked into the files as absolute paths. As such we need to rewrite these to rpaths (relative paths) which is done using the install_name_tool command. A script is available to automate this: `tools/macOS_change_dylib_rpaths.sh` @@ -662,23 +661,23 @@ This will be the directory structure for the installation (the VLC-related files /Applications/EmulationStation Desktop Edition.app/Contents/Resources/themes/* ``` -ES will look in the following locations for the resources, in the listed order: +ES-DE will look in the following locations for application resources, in the listed order: * \/.emulationstation/resources/ * \/../Resources/resources/ * \/resources/ -**Note:** The resources directory is critical, without it the application won't start. +The resources directory is critical, without it the application won't start. -And it will look in the following locations for the themes, also in the listed order: +As well the following locations will be searched for themes, also in the listed order: * \/.emulationstation/themes/ * \/../Resources/themes/ * \/themes/ -A theme is not mandatory to start the application, but ES will be basically useless without it. +A theme is not mandatory to start the application, but ES-DE will be basically useless without it. -The home directory will always take precedence, and any resources or themes located there will override the ones in the installation path or in the path of the ES-DE executable. +As indicated above, the home directory will always take precedence and any resources or themes located there will override the ones in the path of the ES-DE executable. **Creating a .dmg installer:** @@ -694,7 +693,7 @@ CPack: Create package CPack: - package: /Users/myusername/emulationstation-de/EmulationStation-DE-1.1.0-x64.dmg generated. ``` -Generating .dmg installers on older version of macOS seems to make them forward compatible to a pretty good extent, for instance building on El Capitan seems to generate an application that is usable on Catalina and Big Sur. The other way around does however not seem to be true, which is quite unsurprising. +Generating .dmg installers on older version of macOS seems to make them forward compatible to a pretty good extent, for instance building on El Capitan seems to generate an application that is usable on Catalina and Big Sur. The other way around is not true due to the use of dependencies from the Homebrew repository. **Special considerations regarding run-paths:** @@ -727,29 +726,12 @@ This is what an incorrect line would look like: `/usr/local/opt/sdl2/lib/libSDL2-2.0.0.dylib (compatibility version 13.0.0, current version 13.0.0)` -This is the section in es-app/CMakeLists.txt that would need to be modified: - -``` -add_custom_command(TARGET EmulationStation POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} - -change /usr/local/lib/libavcodec.58.dylib @rpath/libavcodec.58.dylib - -change /usr/local/lib/libavfilter.7.dylib @rpath/libavfilter.7.dylib - -change /usr/local/lib/libavformat.58.dylib @rpath/libavformat.58.dylib - -change /usr/local/lib/libavutil.56.dylib @rpath/libavutil.56.dylib - -change /usr/local/lib/libswresample.3.dylib @rpath/libswresample.3.dylib - -change /usr/local/lib/libswscale.5.dylib @rpath/libswscale.5.dylib - -change /usr/local/opt/freeimage/lib/libfreeimage.dylib @rpath/libfreeimage.dylib - -change /usr/local/opt/freetype/lib/libfreetype.6.dylib @rpath/libfreetype.6.dylib - -change /usr/local/opt/libpng/lib/libpng16.16.dylib @rpath/libpng16.16.dylib - -change /usr/local/opt/sdl2/lib/libSDL2-2.0.0.dylib @rpath/libSDL2-2.0.0.dylib - $) -``` - ## Building on Windows Both MSVC and MinGW (GCC) work fine for building ES-DE on Windows. -Although I would prefer to exclude support for MSVC, this proprietary compiler simply works much better when developing as it's much faster than MinGW when linking debug builds and when actually debugging. For release builds though MinGW is very fast and it seems as if ES-DE starts around 18% faster when built with MinGW so this compiler probably generates more efficient code overall. As well MSVC requires a lot more junk DLL files to be distributed with the application so it's not a good candidate for the final release build. +Although I would prefer to exclude support for MSVC, this compiler simply works much better when developing as it's much faster than MinGW when linking debug builds and when actually debugging. But for release builds MinGW is very fast and ES-DE starts around 18% faster when built with MinGW meaning this compiler probably generates more efficient code overall. As well MSVC requires a lot more junk DLL files to be distributed with the application so it's not a good candidate for the final release build. For this reason I think MSVC makes sense for development and MinGW for the releases. @@ -772,7 +754,7 @@ Just-In-Time debugger C++ CMake tools for Windows ``` -If you intend to use both MinGW and MSVC on the machine, it's probably better to exclude CMake and install it manually as described in the MinGW setup instructions below. +If you intend to use both MinGW and MSVC on the same machine, it's probably better to exclude CMake and install it manually as described in the MinGW setup instructions below. The way the MSVC environment works is that a specific developer shell is provided where the build environment is properly configured. You open this from the Start menu via `Visual Studio 2019` -> `Visual Studio tools` -> `VC` -> `x64 Native Tools Command Prompt for VS 2019`. @@ -788,24 +770,24 @@ Download the following packages and install them: [https://jmeubank.github.io/tdm-gcc](https://jmeubank.github.io/tdm-gcc) -After installation, make a copy of `TDM-GCC-64/bin/mingw32-make` to `make` just for convenience. +After installation, make a copy of `TDM-GCC-64\bin\mingw32-make` to `make` just for convenience. -Note that most GDB builds for Windows have broken Python support so that pretty printing won't work. The recommended MinGW installation should work fine though. +Note that most GDB builds for Windows have broken Python support so that pretty printing won't work. The recommended MinGW distribution should work fine though. **Other preparations:** In order to get clang-format onto the system you need to download and install Clang: \ [https://llvm.org/builds](https://llvm.org/builds) -Just run the installer and make sure to select the option "Add LLVM to the system PATH for current user". +Just run the installer and make sure to select the option _Add LLVM to the system PATH for current user_. Install your editor of choice, I use [VSCode](https://code.visualstudio.com). -Configure Git. I won't get into the details on how it's done, but there are many resources available online to support with this. The `Git Bash` shell shipped with Git for Windows is very useful though as it's somewhat reproducing a Unix environment using MinGW/MSYS2. +Configure Git. I won't get into the details on how this is done, but there are many resources available online to support with this. The `Git Bash` shell shipped with Git for Windows is very useful though as it's somewhat reproducing a Unix environment using MinGW/MSYS2. -It's strongly recommended to set line breaks to Unix-style (line feed only) directly in the editor, although it can also be configured in Git for conversion during commits. The source code for ES-DE only uses Unix-style line breaks. +It's strongly recommended to set line breaks to Unix-style (line feed only) directly in the editor. But if not done, lines breaks will anyway be converted when running clang-format on the code, as explained [here](INSTALL-DEV.md#using-clang-format-for-automatic-code-formatting). -In the description below it's assumed that all build steps for MinGW/GCC will be done in the Git Bash shell, and all the build steps for MSVC will be done in the MSVC developer console (x64 Native Tools Command Prompt for VS). +In the descriptions below it's assumed that all build steps for MinGW/GCC will be done in the Git Bash shell, and all the build steps for MSVC will be done in the MSVC developer console (x64 Native Tools Command Prompt for VS). **Download the dependency packages:** @@ -816,7 +798,7 @@ FFmpeg (choose a package with win64-gpl-shared in the filename, the latest snaps FreeImage (binary distribution) \ [https://sourceforge.net/projects/freeimage](https://sourceforge.net/projects/freeimage) -cURL (Windows 64 bit binary, the MinGW version even if using MSVC) \ +cURL (Windows 64 bit binary, select the MinGW version even if using MSVC) \ [https://curl.haxx.se/download.html](https://curl.haxx.se/download.html) SDL2 (development libraries, MinGW or VC/MSVC) \ @@ -890,7 +872,7 @@ make [RapidJSON](http://rapidjson.org) -For RapidJSON, you don't need to compile it, you just need the include files: +For RapidJSON you don't need to compile, you just need the include files: ``` git clone git://github.com/Tencent/rapidjson.git @@ -908,7 +890,7 @@ This works the same as on Unix or macOS, just run the following: git clone https://gitlab.com/leonstyhre/emulationstation-de.git ``` -By default the master branch will be used, which is where the development takes place. To instead build the latest stable release, switch to the `stable` branch: +By default the master branch will be used, which is where development takes place. To instead build the latest stable release, switch to the `stable` branch: ``` cd emulationstation-de @@ -927,7 +909,7 @@ You may need to create the SDL2 directory manually and copy the header files the For FFmpeg, copy the directories libavcodec, libavfilter, libavformat and libavutil. -It should then look something like this: +It should look something like this: ``` $ ls -1 include/ @@ -1036,11 +1018,9 @@ vcomp140.dll (From Visual C++ Redistributable for Visual Studio 201 **Additional files for both MSVC and MinGW if building with the VLC video player:** -In addition to the files above, you need to copy some libraries from the VLC `plugins` folder to be able to play video files. There is a subdirectory structure under the plugins folder but there is no requirement to retain this as libVLC apparently looks recursively for the .dll files. +In addition to the files above, you need to copy some libraries from the VLC `plugins` folder. This contains a subdirectory structure but there is no requirement to retain this as libVLC apparently looks recursively for the .dll files. -It's a bit tricky to know which libraries are really needed. But as the plugins directory is around 120 MB (as of VLC version 3.0.11), we definitely only want to copy the files we need. - -The following files seem to be required to play most video and audio formats (place them in `emulationstation-de\plugins`): +The following libraries seem to be required to play most video and audio formats: ``` access\libfilesystem_plugin.dll @@ -1060,7 +1040,9 @@ video_chroma\libswscale_plugin.dll video_output\libvmem_plugin.dll ``` -The combined size of these files is around 22 MB which is more reasonable. +The reason to not simply copy all plugins is that the combined size of these is around 120 MB (as of VLC version 3.0.11) and the size of the selected files listed above is around 22 MB, which is more reasonable. + +Place the files in the `emulationstation-de\plugins\` directory. **Building ES-DE using MSVC:** @@ -1128,7 +1110,7 @@ Note that compilation time is much longer than on Unix or macOS, and linking tim If you are running Windows in a virtualized environment such as QEMU-KVM that does not support HW accelerated OpenGL, you can install the Mesa3D for Windows library, which can be downloaded at [https://fdossena.com/?p=mesa/index.frag](https://fdossena.com/?p=mesa/index.frag). -You simply extract the opengl32.dll file into the ES-DE directory and this will enable the llvmpipe renderer. The performance will be terrible of course, but everything should work and it should be good enough for test building on Windows without having to reboot your computer to a native Windows installation. (Note that you may need to copy opengl32.dll to your RetroArch installation directory as well to get the emulators to work correctly.) +You simply extract the opengl32.dll file into the ES-DE directory and this will enable the llvmpipe renderer. The performance will be terrible of course, but everything should work and it should be good enough for test building on Windows without having to reboot your computer to a native Windows installation. (Note that you may need to copy opengl32.dll to your RetroArch installation directory as well to get the emulators to work somehow correctly.) Obviously this library is only intended for development and will not be shipped with ES-DE. @@ -1154,26 +1136,26 @@ CPack: - package: C:/Programming/emulationstation-de/EmulationStation-DE-1.1.0-x The default installation directory suggested by the installer is `C:\Program Files\EmulationStation-DE` but this can of course be changed by the user. -ES will look in the following locations for the resources, in the listed order: +ES-DE will look in the following locations for application resources, in the listed order: * \\\.emulationstation\resources\ * \\resources\ The resources directory is critical, without it the application won't start. -And it will look in the following locations for the themes, also in the listed order: +As well the following locations will be searched for themes, also in the listed order: * \\\.emulationstation\themes\ * \\themes\ -The theme is not mandatory to start the application, but ES-DE will be basically useless without it. +A theme is not mandatory to start the application, but ES-DE will be basically useless without it. -So the home directory will always take precedence, and any resources or themes located there will override the ones in the path of the ES-DE executable. +As indicated above, the home directory will always take precedence and any resources or themes located there will override the ones in the path of the ES-DE executable. ## Using clang-format for automatic code formatting -The entire ES-DE codebase is formatted using clang-format and all code commits must have been processed using this tool. +The entire ES-DE codebase is formatted using clang-format and all new code must be formatted using this tool before being committed. There is a style configuration file named .clang-format located directly at the root of the repository which contains the formatting rules. How to install clang-format is detailed per operating system earlier in this document. @@ -1185,7 +1167,7 @@ To format a file from the command line, simply run: The -i flag will make an inplace edit of the file. -But the recommended way is to run clang-format from within the editor. If using VSCode, there is an extension available named Clang-Format. After installing this, simply open a source file, right click and choose `Format Document` or use the applicable keyboard shortcut. The first time you do this, you will have to make a choice to perform the formatting using clang-format. The rest should be completely automatic. +But the recommended approach is to run clang-format from within the editor. If using VSCode, there is an extension available named Clang-Format. After installing this, simply open a source file, right click and choose `Format Document` or use the applicable keyboard shortcut. The first time you do this, you will have to make a choice to perform the formatting using clang-format. The rest should be completely automatic. In some instances you may want to avoid getting code formatted, and you can accomplish this by simply enclosing the lines with the two comments "clang-format off" and "clang-format on", such as this: @@ -1201,7 +1183,7 @@ CollectionSystemDecl systemDecls[] = { // clang-format on ``` -Adding a comment (or a semicolon) on its own line will also prevent some formatting such as turning short functions and lambda expressions into single lines. For this function such a comment has been added: +Adding a comment on its own line will also prevent some formatting such as turning short functions and lambda expressions into single lines. For this function such a comment has been added: ```c++ const std::string FileData::get3DBoxPath() const @@ -1220,21 +1202,30 @@ const std::string FileData::get3DBoxPath() const { return getMediafilePath("3dbo Adding comments (even empty ones) can also force line breaks to avoid ugly formatting such as this: ```c++ -for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); - it++) { - (*it)->setScrapeFlag(false); +for (auto it = mCursorStackHistory.begin(); it != mCursorStackHistory.end(); + it++) { + if (std::find(listEntries.begin(), listEntries.end(), *it) != + listEntries.end()) { + newCursor = *it; + mCursorStackHistory.erase(it); + break; + } } ``` -Adding a comment at the right place produces this much nicer formatting: +A comment at the right place produces this much nicer formatting: ```c++ -for (auto it = SystemData::sSystemVector.cbegin(); // Line break. - it != SystemData::sSystemVector.cend(); it++) { - (*it)->setScrapeFlag(false); +for (auto it = mCursorStackHistory.begin(); // Line break. + it != mCursorStackHistory.end(); it++) { + if (std::find(listEntries.begin(), listEntries.end(), *it) != + listEntries.end()) { + newCursor = *it; + mCursorStackHistory.erase(it); + break; + } } ``` - Of course you would like to get the code formatted according to the clang-format rules in most instances, these workaround are only meant for rare exceptions. Some compromises are necessary when auto-formatting code, at least with clang-format in its current state. ## CA certificates and MAME ROM information @@ -1255,9 +1246,9 @@ emulationstation-de/resources/certificates/curl-ca-bundle.crt **MAME ROM info:** -This is a bit tricky as the data needs to be converted to an internal format used by ES-DE. The original file is huge and most of the information is not required. +ES-DE automatically identifies and excludes MAME BIOS and device files, as well as translating the short MAME ROM names to their full game names. This is done using information from the MAME driver file shipped with the official MAME distribution. The file needs to be converted to an internal format used by ES-DE as the original file is huge and most of the information is not required. -Go to [https://www.mamedev.org/release.php](https://www.mamedev.org/release.php) and select the Windows version, but only download the driver information in XML format and not MAME itself. This file will be named something like `mame0226lx.zip` and unzipping it will give you a file name such as `mame0226.xml`. +To get hold of the driver file, go to [https://www.mamedev.org/release.php](https://www.mamedev.org/release.php) and select the Windows version, but only download the driver information in XML format and not MAME itself. This file will be named something like `mame0226lx.zip` and unzipping it will give you a file name such as `mame0226.xml`. Move the XML driver file to the resources/MAME directory and then convert it to the ES-DE internal files: @@ -1294,14 +1285,14 @@ git diff mamebioses git diff mamedevices ``` -The reason to not simply replace the BIOS and devices files with the new version is that we want to retain entries from all older MAME versions as otherwise older ROM sets used on older MAME versions would have missing information. This is so as the MAME project sometimes removes older entries when they're reorganizing the ROM sets. By merging the files we retain backwards compatibility but still support the latest MAME version. To clarify, this of course does not affect the emulation itself, but rather the filtering of BIOS and device files inside ES-DE. The mamenames.xml file containing the translation of MAME ROM names to the full game names does not suffer from this problem as it's cumulative, which is why it is simply overwritten. +The reason to not simply replace the BIOS and devices files with the new version is that we want to retain entries from all older MAME versions as otherwise older ROM sets used on older MAME versions would have missing information. This is so as the MAME project sometimes removes older entries when they're reorganizing the ROM sets. By merging the files we retain backward compatibility but still support the latest MAME version. To clarify, this of course does not affect the emulation itself, but rather the filtering of BIOS and device files inside ES-DE. The mamenames.xml file containing the translation of MAME ROM names to the full game names does not suffer from this problem as it's cumulative, which is why it is simply overwritten. ## Configuration **~/.emulationstation/es_settings.xml:** -When ES-DE is first run, a configuration file will be created as `~/.emulationstation/es_settings.xml`. +When ES-DE is first started, a configuration file will be created as `~/.emulationstation/es_settings.xml` This file will contain all supported settings at their default values. Normally you shouldn't need to modify this file manually, instead you should be able to use the menu inside ES-DE to update all the necessary settings. @@ -1335,9 +1326,11 @@ There is also support to add the variable %ESPATH% to the ROM directory setting, **~/.emulationstation/es_input.xml:** -You normally don't need to modify this file manually as it's created by the built-in input configuration step. This procedure is detailed in the [User guide](USERGUIDE.md#input-device-configuration). +As ES-DE auto-configures the keyboard and controllers, neither the input configuration step or manual adjustments to the es_input.xml file should normally be needed. Actually, unless the button layout has been customized using the input configurator, the es_input.xml file will not even exist. -If your controller and keyboard stop working, you can delete the `~/.emulationstation/es_input.xml` file to make the input configuration screen re-appear on the next startup, or you can start ES-DE with the `--force-input-config` command line option. +But if you have customized your button layout and your controller or keyboard stop working, you can delete the `~/.emulationstation/es_input.xml` file to remove the customizations, or you can start ES-DE with the `--force-input-config` command line option to make the input configurator appear. + +The input configuration is described in the [User guide](USERGUIDE-DEV.md#input-device-configuration). ## Command line options @@ -1412,15 +1405,17 @@ ES-DE ships with a comprehensive `es_systems.xml` configuration file and normall To make a customized version of the systems configuration file, it first needs to be copied to `~/.emulationstation/custom_systems/es_systems.xml`. (The tilde symbol `~` translates to `$HOME` on Unix and macOS, and to `%HOMEPATH%` on Windows unless overridden using the --home command line option.) -The bundled es_systems.xml file is located in the resources directory that is part of the application installation. For example this could be `/usr/share/emulationstation/resources/systems/unix/es_systems.xml` on Unix, `/Applications/EmulationStation Desktop Edition.app/Contents/Resources/resources/systems/macos/es_systems.xml` on macOS and `C:\Program Files\EmulationStation-DE\resources\systems\windows\es_systems.xml` on Windows. The actual location may differ from these examples of course, depending on where ES-DE has been installed. +The bundled es_systems.xml file is located in the resources directory that is part of the application installation. For example this could be `/usr/share/emulationstation/resources/systems/unix/es_systems.xml` on Unix, `/Applications/EmulationStation Desktop Edition.app/Contents/Resources/resources/systems/macos/es_systems.xml` on macOS or `C:\Program Files\EmulationStation-DE\resources\systems\windows\es_systems.xml` on Windows. The actual location may differ from these examples of course, depending on where ES-DE has been installed. -Note that when copying the bundled es_systems.xml file to ~/.emulationstation/custom_systems/, it will completely replace the default file. So when upgrading to future ES-DE versions, any modifications such as additional game systems will not be enabled until the customized configuration file has been manually updated. +Note that when copying the bundled es_systems.xml file to ~/.emulationstation/custom_systems/, it will completely replace the default file processing. So when upgrading to future ES-DE versions, any modifications such as additional game systems will not be enabled until the customized configuration file has been manually updated. It doesn't matter in which order you define the systems as they will be sorted by the full system name inside the application, but it's still probably a good idea to add them in alphabetical order to make the file easier to maintain. Keep in mind that you have to set up your emulators separately from ES-DE as the es_systems.xml file assumes that your emulator environment is properly configured. -Below is an overview of the file layout with various examples. For a real system entry there can of course not be multiple entries for the same tag such as the multiple \ entries listed here. +Below is an overview of the file layout with various examples. For the command tag, the newer es_find_rules.xml logic described later in this document removes the need for most of the legacy options, but they are still supported for special configurations and for backward compatibility with old configuration files. + +For a real system entry there can of course not be multiple entries for the same tag such as the multiple \ entries listed here. ```xml @@ -1433,7 +1428,7 @@ Below is an overview of the file layout with various examples. For a real system snes - Super Nintendo Entertainment System + Nintendo SNES (Super Nintendo) retroarch -L %CORE_RETROARCH%/snes9x_libretro.so %ROM% - + /Applications/RetroArch.app/Contents/MacOS/RetroArch -L %CORE_RETROARCH%/snes9x_libretro.dylib %ROM% snes + It's recommended to include this tag even if it's just to clarify that the theme should correspond to the system name. --> snes @@ -1497,7 +1492,7 @@ The following variable is expanded for the `path` tag: The following variables are expanded for the `command` tag: -`%ROM%` - Replaced with the absolute path to the selected ROM, with most Bash special characters escaped with a backslash. +`%ROM%` - Replaced with the absolute path to the selected ROM, with most special characters escaped with a backslash. `%ROMRAW%` - Replaced with the unescaped, absolute path to the selected ROM. If your emulator is picky about paths, you might want to use this instead of %ROM%, but enclosed in quotes. @@ -1626,12 +1621,12 @@ The `name` attribute must correspond to the command tags in es_systems.xml, take Here %EMULATOR_ and %CORE_ are followed by the string RETROARCH which corresponds to the name attribute in es_find_rules.xml. The name is case sensitive but it's recommended to use uppercase names to make the variables feel consistent (%EMULATOR_retroarch% doesn't look so pretty). -Of course this makes it possible to add any number of emulators to the configuration file if not only using RetroArch. +Of course this makes it possible to add any number of emulators to the configuration file. -The `winregistrypath` rule searches the Windows Registry "App Paths" keys for the emulators defined in the `` tags. If for example this tag is set to `retroarch.exe`, the key `SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\retroarch.exe` will be searched for. HKEY_CURRENT_USER is tried first, and if no key is found there, HKEY_LOCAL_MACHINE is tried as well. In addition to this, ES-DE will check that the binary defined in the key actually exists, and if not, processing will proceed with the next rule. Be aware that the App Paths keys are added by the emulators during their installation, and although RetroArch does add this key, not all emulators do. +The `winregistrypath` rule searches the Windows Registry "App Paths" keys for the emulators defined in the `` tags. If for example this tag is set to `retroarch.exe`, the key `SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\retroarch.exe` will be searched for. HKEY_CURRENT_USER is tried first, and if no key is found there, HKEY_LOCAL_MACHINE is tried as well. In addition to this, ES-DE will check that the binary defined in the key actually exists, and if not, it will proceed with the next rule. Be aware that the App Paths keys are added by the emulators during their installation, and although RetroArch does add this key, not all emulators do. -The other rules are probably self-explanatory with `systempath` searching the PATH environment variable for the binary names defined by the `` tags and `staticpath` defines absolute paths to the emulators. For staticpath, the actual emulator binary must also be included in the entry tag. +The other rules are probably self-explanatory with `systempath` searching the PATH environment variable for the binary names defined by the `` tags and `staticpath` defines absolute paths to the emulators. For staticpath, the actual emulator binary must be included in the entry tag. The winregistrypath rules are always processed first, followed by systempath and then staticpath. This is done regardless of which order they are defined in the es_find_rules.xml file. @@ -1735,7 +1730,7 @@ There is also support to add the variable %ESPATH% to the media directory settin The default media directory is `~/.emulationstation/downloaded_media` -You can use ES-DE's scraping tools to populate the gamelist.xml files, or manually update individual entries using the metadata editor. All of this is explained in the [User guide](USERGUIDE.md). +You can use ES-DE's scrapers to populate the gamelist.xml files, or manually update individual entries using the metadata editor. All of this is explained in the [User guide](USERGUIDE-DEV.md). The gamelist.xml files are searched for in the ES-DE home directory, i.e. `~/.emulationstation/gamelists//gamelist.xml` @@ -1777,10 +1772,10 @@ There are a few different data types for the metadata which the string values in * `string` - just text * `float` - a floating-point decimal value (written as a string) * `integer` - an integer value (written as a string) -* `datetime` - a date and, potentially, a time. These are encoded as an ISO string, in the following format: "%Y%m%dT%H%M%S%F%q". For example, the release date for Chrono Trigger is encoded as "19950311T000000" (no time specified) +* `datetime` - a date and optionally a time encoded as an ISO string in the format "%Y%m%dT%H%M%S", for example "19950311T000000" * `bool` - a true or false value -Some metadata is also marked as "statistic" - these are kept track of by ES-DE and do not show up in the metadata editor. They are shown in certain views (for example, the detailed view and the video view both show `lastplayed`, although the label can be disabled by the theme). +Some metadata is also marked as "statistic", these are kept track of by ES-DE and do not show up in the metadata editor. They are shown in certain views (for example, the detailed view and the video view both show `lastplayed`, although the label can be disabled by the theme). There are two basic categories of metadata, `game` and `folders` and the metdata tags for these are described in detail below. @@ -1863,34 +1858,36 @@ This will draw a semi-transparent blue frame around all text elements. This will reload either a single gamelist or all gamelists depending on where you're located when entering the key combination (go to the system view to make a complete reload). Very useful for theme development as any changes to the theme files will be activated without requiring an application restart. Note that the menu needs to be closed for this key combination to have any effect. +By default all controller input (keyboard and controller button presses) will be logged when the --debug flag has been passed. To disable the input logging, the setting DebugSkipInputLogging kan be set to false in the es_settings.xml file. There is no menu entry to change this as it's intended for developers and not for end users. + ## Portable installation on Windows -It's possible to easily create a portable installation of ES-DE for Windows, for example to place on a USB memory stick. +It's possible to easily create a portable installation of ES-DE on Windows, for example to place on a USB memory stick. -For the sake of this example, let's assume that the removable media has the device name `f:\` +For the sake of this example, let's assume that the removable media has the device name `F:\` These are the steps to perform: -* Copy the EmulationStation-DE installation directory to f:\ -* Copy your emulator directories to f:\EmulationStation-DE\ -* Copy your ROMs directory to f:\EmulationStation-DE\ -* Create an empty file named portable.txt in f:\EmulationStation-DE\ +* Copy the EmulationStation-DE installation directory to F:\ +* Copy your emulator directories to F:\EmulationStation-DE\ +* Copy your ROMs directory to F:\EmulationStation-DE\ +* Create an empty file named portable.txt in F:\EmulationStation-DE\ You should end up with something like this: ``` -f:\EmulationStation-DE\ -f:\EmulationStation-DE\RetroArch-Win64\ -f:\EmulationStation-DE\yuzu\ -f:\EmulationStation-DE\ROMs\ -f:\EmulationStation-DE\portable.txt +F:\EmulationStation-DE\ +F:\EmulationStation-DE\RetroArch-Win64\ +F:\EmulationStation-DE\yuzu\ +F:\EmulationStation-DE\ROMs\ +F:\EmulationStation-DE\portable.txt ``` (Yuzu is an optional Nintendo Switch emulator.) Of course there will be many more files and directories from the normal installation than those listed above. -How this works is that when ES-DE finds a file named portable.txt in its executable directory, it will locate the .emulationstation directory directly in this folder. It's however also possible to modify portable.txt with a path relative to the ES-DE executable directory. For instance if two dots `..` are placed inside the portable.txt file, then the .emulationstation directory will be located in the parent folder, which would be directly under f:\ in this example. +How this works is that when ES-DE finds a file named portable.txt in its executable directory, it will by default locate the .emulationstation directory directly in this folder. It's also possible to modify portable.txt with a path relative to the ES-DE executable directory. For instance if two dots `..` are placed inside the portable.txt file, then the .emulationstation directory will be located in the parent folder, which would be directly under F:\ in this example. If the --home command line parameter is passed when starting ES-DE, that will override the portable.txt file. @@ -1905,15 +1902,15 @@ yuzu\yuzu-windows-msvc\yuzu.exe ..\yuzu\yuzu-windows-msvc\yuzu.exe ``` -If you want to place your emulators elsewhere, you need to create a customized es_find_rules.xml file, which is explained later in this document. +If you want to place your emulators elsewhere, you need to create a customized es_find_rules.xml file, which is explained earlier in this document. -Start ES-DE from the f:\ device and check that everything works as expected. +Start ES-DE from the F:\ device and check that everything works as expected. Following this, optionally copy any existing gamelist.xml files, game media files etc. to the removable media. For example: ``` -f:\EmulationStation-DE\.emulationstation\gamelists\ -f:\EmulationStation-DE\.emulationstation\downloaded_media\ +F:\EmulationStation-DE\.emulationstation\gamelists\ +F:\EmulationStation-DE\.emulationstation\downloaded_media\ ``` You now have a fully functional portable retro gaming installation! @@ -1928,10 +1925,10 @@ There are numerous locations throughout ES-DE where custom scripts will be execu The approach is quite straightforward, ES-DE will look for any files inside a script directory that corresponds to the event that is triggered and will then execute all these files. We'll go through two examples: -* Create a log file that will record the start and end time for each game we play, letting us see how much time we spend on retro-gaming -* Change the system resolution when launching and returning from a game in order to run the emulator at a lower resolution than ES-DE +* Creating a log file that will record the start and end time for each game we play, letting us see how much time we spend on retro-gaming +* Changing the system resolution when launching and returning from a game in order to run the emulator at a lower resolution than ES-DE -**Note:** The following examples are for Unix systems, but it works the same way in macOS (which is also Unix after all), and on Windows (although .bat batch files are then used instead of shell scripts and any spaces in the parameters are not escaped as is the case on Unix). +**Note:** The following examples are for Unix systems, but it works the same way on macOS (which is also Unix after all), and on Windows (although .bat batch files are then used instead of shell scripts and any spaces in the parameters are not escaped as is the case on Unix). The events executed when a game starts and ends are called `game-start` and `game-end` respectively. Finding these event names is easily achieved by starting ES-DE with the `--debug` flag. If this is done, all attempts to execute custom event scripts will be logged to es_log.txt, including the event names. @@ -1964,7 +1961,7 @@ After creating the two scripts, you should have something like this on the files ~/.emulationstation/scripts/game-end/game_end_logging.sh ``` -Don't forget to make the scripts executable (e.g. 'chmod 755 ./game_start_logging.sh'). +Don't forget to make the scripts executable (e.g. "chmod 755 ./game_start_logging.sh"). If we now start ES-DE with the debug flag and launch a game, something like the following should show up in es_log.txt: From 738f8854cb489ae4f55395baa5a2e4ab6c2c7d03 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 11 Jul 2021 13:31:43 +0200 Subject: [PATCH 56/76] Formatting some code that accidentally excluded from clang-format. --- es-app/src/guis/GuiScraperMenu.cpp | 149 +++++++++++++++-------------- 1 file changed, 77 insertions(+), 72 deletions(-) diff --git a/es-app/src/guis/GuiScraperMenu.cpp b/es-app/src/guis/GuiScraperMenu.cpp index c4a8889ab..a03ca047e 100644 --- a/es-app/src/guis/GuiScraperMenu.cpp +++ b/es-app/src/guis/GuiScraperMenu.cpp @@ -578,7 +578,7 @@ void GuiScraperMenu::openOtherOptions() scraper_language->add("Čeština", "cz", selectedScraperLanguage == "cz"); scraper_language->add("Slovenčina", "sk", selectedScraperLanguage == "sk"); scraper_language->add("Türkçe", "tr", selectedScraperLanguage == "tr"); - //clang-format on + // clang-format on // If there are no objects returned, then there must be a manually modified entry in the // configuration file. Simply set the language to "English" in this case. if (scraper_language->getSelectedObjects().size() == 0) @@ -586,7 +586,7 @@ void GuiScraperMenu::openOtherOptions() s->addWithLabel("PREFERRED LANGUAGE", scraper_language); s->addSaveFunc([scraper_language, s] { if (scraper_language->getSelected() != - Settings::getInstance()->getString("ScraperLanguage")) { + Settings::getInstance()->getString("ScraperLanguage")) { Settings::getInstance()->setString("ScraperLanguage", scraper_language->getSelected()); s->setNeedsSaving(); } @@ -596,8 +596,9 @@ void GuiScraperMenu::openOtherOptions() if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { scraper_language->setEnabled(false); scraper_language->setOpacity(DISABLED_OPACITY); - scraper_language->getParent()->getChild(scraper_language-> - getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); + scraper_language->getParent() + ->getChild(scraper_language->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); } // Overwrite files and data. @@ -606,37 +607,37 @@ void GuiScraperMenu::openOtherOptions() s->addWithLabel("OVERWRITE FILES AND DATA", scraper_overwrite_data); s->addSaveFunc([scraper_overwrite_data, s] { if (scraper_overwrite_data->getState() != - Settings::getInstance()->getBool("ScraperOverwriteData")) { + Settings::getInstance()->getBool("ScraperOverwriteData")) { Settings::getInstance()->setBool("ScraperOverwriteData", - scraper_overwrite_data->getState()); + scraper_overwrite_data->getState()); s->setNeedsSaving(); } }); // Halt scraping on invalid media files. auto scraper_halt_on_invalid_media = std::make_shared(mWindow); - scraper_halt_on_invalid_media-> - setState(Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia")); + scraper_halt_on_invalid_media->setState( + Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia")); s->addWithLabel("HALT ON INVALID MEDIA FILES", scraper_halt_on_invalid_media); s->addSaveFunc([scraper_halt_on_invalid_media, s] { if (scraper_halt_on_invalid_media->getState() != - Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia")) { + Settings::getInstance()->getBool("ScraperHaltOnInvalidMedia")) { Settings::getInstance()->setBool("ScraperHaltOnInvalidMedia", - scraper_halt_on_invalid_media->getState()); + scraper_halt_on_invalid_media->getState()); s->setNeedsSaving(); } }); // Search using metadata names. auto scraper_search_metadata_name = std::make_shared(mWindow); - scraper_search_metadata_name-> - setState(Settings::getInstance()->getBool("ScraperSearchMetadataName")); + scraper_search_metadata_name->setState( + Settings::getInstance()->getBool("ScraperSearchMetadataName")); s->addWithLabel("SEARCH USING METADATA NAMES", scraper_search_metadata_name); s->addSaveFunc([scraper_search_metadata_name, s] { if (scraper_search_metadata_name->getState() != - Settings::getInstance()->getBool("ScraperSearchMetadataName")) { + Settings::getInstance()->getBool("ScraperSearchMetadataName")) { Settings::getInstance()->setBool("ScraperSearchMetadataName", - scraper_search_metadata_name->getState()); + scraper_search_metadata_name->getState()); s->setNeedsSaving(); } }); @@ -647,7 +648,7 @@ void GuiScraperMenu::openOtherOptions() s->addWithLabel("INTERACTIVE MODE", scraper_interactive); s->addSaveFunc([scraper_interactive, s] { if (scraper_interactive->getState() != - Settings::getInstance()->getBool("ScraperInteractive")) { + Settings::getInstance()->getBool("ScraperInteractive")) { Settings::getInstance()->setBool("ScraperInteractive", scraper_interactive->getState()); s->setNeedsSaving(); } @@ -657,11 +658,11 @@ void GuiScraperMenu::openOtherOptions() auto scraper_semiautomatic = std::make_shared(mWindow); scraper_semiautomatic->setState(Settings::getInstance()->getBool("ScraperSemiautomatic")); s->addWithLabel("AUTO-ACCEPT SINGLE GAME MATCHES", scraper_semiautomatic); - s->addSaveFunc([scraper_semiautomatic ,s] { - if (scraper_semiautomatic->getState() != - Settings::getInstance()->getBool("ScraperSemiautomatic")) { + s->addSaveFunc([scraper_semiautomatic, s] { + if (scraper_semiautomatic->getState() != + Settings::getInstance()->getBool("ScraperSemiautomatic")) { Settings::getInstance()->setBool("ScraperSemiautomatic", - scraper_semiautomatic->getState()); + scraper_semiautomatic->getState()); s->setNeedsSaving(); } }); @@ -670,21 +671,21 @@ void GuiScraperMenu::openOtherOptions() if (!Settings::getInstance()->getBool("ScraperInteractive")) { scraper_semiautomatic->setEnabled(false); scraper_semiautomatic->setOpacity(DISABLED_OPACITY); - scraper_semiautomatic->getParent()->getChild(scraper_semiautomatic-> - getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); + scraper_semiautomatic->getParent() + ->getChild(scraper_semiautomatic->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); } - // Respect the per-file multi-scraper exclusion flag. auto scraper_respect_exclusions = std::make_shared(mWindow); scraper_respect_exclusions->setState( - Settings::getInstance()->getBool("ScraperRespectExclusions")); + Settings::getInstance()->getBool("ScraperRespectExclusions")); s->addWithLabel("RESPECT PER-FILE SCRAPER EXCLUSIONS", scraper_respect_exclusions); s->addSaveFunc([scraper_respect_exclusions, s] { if (scraper_respect_exclusions->getState() != - Settings::getInstance()->getBool("ScraperRespectExclusions")) { + Settings::getInstance()->getBool("ScraperRespectExclusions")) { Settings::getInstance()->setBool("ScraperRespectExclusions", - scraper_respect_exclusions->getState()); + scraper_respect_exclusions->getState()); s->setNeedsSaving(); } }); @@ -692,13 +693,13 @@ void GuiScraperMenu::openOtherOptions() // Exclude files recursively for excluded folders. auto scraper_exclude_recursively = std::make_shared(mWindow); scraper_exclude_recursively->setState( - Settings::getInstance()->getBool("ScraperExcludeRecursively")); + Settings::getInstance()->getBool("ScraperExcludeRecursively")); s->addWithLabel("EXCLUDE FOLDERS RECURSIVELY", scraper_exclude_recursively); s->addSaveFunc([scraper_exclude_recursively, s] { if (scraper_exclude_recursively->getState() != - Settings::getInstance()->getBool("ScraperExcludeRecursively")) { + Settings::getInstance()->getBool("ScraperExcludeRecursively")) { Settings::getInstance()->setBool("ScraperExcludeRecursively", - scraper_exclude_recursively->getState()); + scraper_exclude_recursively->getState()); s->setNeedsSaving(); } }); @@ -707,20 +708,20 @@ void GuiScraperMenu::openOtherOptions() if (!Settings::getInstance()->getBool("ScraperRespectExclusions")) { scraper_exclude_recursively->setEnabled(false); scraper_exclude_recursively->setOpacity(DISABLED_OPACITY); - scraper_exclude_recursively->getParent()->getChild(scraper_exclude_recursively-> - getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); + scraper_exclude_recursively->getParent() + ->getChild(scraper_exclude_recursively->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); } // Include actual folders when scraping. auto scraper_include_folders = std::make_shared(mWindow); - scraper_include_folders->setState( - Settings::getInstance()->getBool("ScraperIncludeFolders")); + scraper_include_folders->setState(Settings::getInstance()->getBool("ScraperIncludeFolders")); s->addWithLabel("SCRAPE ACTUAL FOLDERS", scraper_include_folders); s->addSaveFunc([scraper_include_folders, s] { if (scraper_include_folders->getState() != - Settings::getInstance()->getBool("ScraperIncludeFolders")) { + Settings::getInstance()->getBool("ScraperIncludeFolders")) { Settings::getInstance()->setBool("ScraperIncludeFolders", - scraper_include_folders->getState()); + scraper_include_folders->getState()); s->setNeedsSaving(); } }); @@ -728,13 +729,13 @@ void GuiScraperMenu::openOtherOptions() // Retry search on peer verification errors (TLS/certificate issues). auto retry_peer_verification = std::make_shared(mWindow); retry_peer_verification->setState( - Settings::getInstance()->getBool("ScraperRetryPeerVerification")); + Settings::getInstance()->getBool("ScraperRetryPeerVerification")); s->addWithLabel("AUTO-RETRY ON PEER VERIFICATION ERRORS", retry_peer_verification); s->addSaveFunc([retry_peer_verification, s] { if (retry_peer_verification->getState() != - Settings::getInstance()->getBool("ScraperRetryPeerVerification")) { + Settings::getInstance()->getBool("ScraperRetryPeerVerification")) { Settings::getInstance()->setBool("ScraperRetryPeerVerification", - retry_peer_verification->getState()); + retry_peer_verification->getState()); s->setNeedsSaving(); } }); @@ -744,14 +745,16 @@ void GuiScraperMenu::openOtherOptions() if (scraper_semiautomatic->getEnabled()) { scraper_semiautomatic->setEnabled(false); scraper_semiautomatic->setOpacity(DISABLED_OPACITY); - scraper_semiautomatic->getParent()->getChild(scraper_semiautomatic-> - getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); + scraper_semiautomatic->getParent() + ->getChild(scraper_semiautomatic->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); } else { scraper_semiautomatic->setEnabled(true); scraper_semiautomatic->setOpacity(255); - scraper_semiautomatic->getParent()->getChild(scraper_semiautomatic-> - getChildIndex() - 1)->setOpacity(255); + scraper_semiautomatic->getParent() + ->getChild(scraper_semiautomatic->getChildIndex() - 1) + ->setOpacity(255); } }; @@ -759,14 +762,16 @@ void GuiScraperMenu::openOtherOptions() if (scraper_exclude_recursively->getEnabled()) { scraper_exclude_recursively->setEnabled(false); scraper_exclude_recursively->setOpacity(DISABLED_OPACITY); - scraper_exclude_recursively->getParent()->getChild(scraper_exclude_recursively-> - getChildIndex() - 1)->setOpacity(DISABLED_OPACITY); + scraper_exclude_recursively->getParent() + ->getChild(scraper_exclude_recursively->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); } else { scraper_exclude_recursively->setEnabled(true); scraper_exclude_recursively->setOpacity(255); - scraper_exclude_recursively->getParent()->getChild(scraper_exclude_recursively-> - getChildIndex() - 1)->setOpacity(255); + scraper_exclude_recursively->getParent() + ->getChild(scraper_exclude_recursively->getChildIndex() - 1) + ->setOpacity(255); } }; @@ -789,19 +794,18 @@ void GuiScraperMenu::pressedStart() std::string warningString; if (sys.size() == 1) { warningString = "The selected system does not have a\n" - "platform set, results may be inaccurate\n" - "Continue anyway?"; + "platform set, results may be inaccurate\n" + "Continue anyway?"; } else { warningString = "At least one of your selected\n" - "systems does not have a platform\n" - "set, results may be inaccurate\n" - "Continue anyway?"; + "systems does not have a platform\n" + "set, results may be inaccurate\n" + "Continue anyway?"; } mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - Utils::String::toUpper(warningString), - "YES", std::bind(&GuiScraperMenu::start, this), - "NO", nullptr)); + Utils::String::toUpper(warningString), "YES", + std::bind(&GuiScraperMenu::start, this), "NO", nullptr)); return; } } @@ -811,8 +815,8 @@ void GuiScraperMenu::pressedStart() void GuiScraperMenu::start() { if (mSystems->getSelectedObjects().empty()) { - mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - "PLEASE SELECT AT LEAST ONE SYSTEM TO SCRAPE")); + mWindow->pushGui( + new GuiMsgBox(mWindow, getHelpStyle(), "PLEASE SELECT AT LEAST ONE SYSTEM TO SCRAPE")); return; } @@ -826,7 +830,7 @@ void GuiScraperMenu::start() break; } if (scraperService == "screenscraper" && - Settings::getInstance()->getBool("ScrapeRatings")) { + Settings::getInstance()->getBool("ScrapeRatings")) { contentToScrape = true; break; } @@ -834,8 +838,7 @@ void GuiScraperMenu::start() contentToScrape = true; break; } - if (scraperService == "screenscraper" && - Settings::getInstance()->getBool("ScrapeVideos")) { + if (scraperService == "screenscraper" && Settings::getInstance()->getBool("ScrapeVideos")) { contentToScrape = true; break; } @@ -852,7 +855,7 @@ void GuiScraperMenu::start() break; } if (scraperService == "screenscraper" && - Settings::getInstance()->getBool("Scrape3DBoxes")) { + Settings::getInstance()->getBool("Scrape3DBoxes")) { contentToScrape = true; break; } @@ -860,35 +863,35 @@ void GuiScraperMenu::start() if (!contentToScrape) { mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - "PLEASE SELECT AT LEAST ONE CONTENT TYPE TO SCRAPE")); + "PLEASE SELECT AT LEAST ONE CONTENT TYPE TO SCRAPE")); return; } std::queue searches = - getSearches(mSystems->getSelectedObjects(), mFilters->getSelected()); + getSearches(mSystems->getSelectedObjects(), mFilters->getSelected()); if (searches.empty()) { - mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - "ALL GAMES WERE FILTERED, NOTHING TO SCRAPE")); + mWindow->pushGui( + new GuiMsgBox(mWindow, getHelpStyle(), "ALL GAMES WERE FILTERED, NOTHING TO SCRAPE")); } else { - GuiScraperMulti* gsm = new GuiScraperMulti(mWindow, searches, - Settings::getInstance()->getBool("ScraperInteractive")); + GuiScraperMulti* gsm = new GuiScraperMulti( + mWindow, searches, Settings::getInstance()->getBool("ScraperInteractive")); mWindow->pushGui(gsm); mMenu.setCursorToList(); mMenu.setCursorToFirstListEntry(); } } -std::queue GuiScraperMenu::getSearches( - std::vector systems, GameFilterFunc selector) +std::queue GuiScraperMenu::getSearches(std::vector systems, + GameFilterFunc selector) { std::queue queue; for (auto sys = systems.cbegin(); sys != systems.cend(); sys++) { std::vector games = (*sys)->getRootFolder()->getScrapeFilesRecursive( - Settings::getInstance()->getBool("ScraperIncludeFolders"), - Settings::getInstance()->getBool("ScraperExcludeRecursively"), - Settings::getInstance()->getBool("ScraperRespectExclusions")); + Settings::getInstance()->getBool("ScraperIncludeFolders"), + Settings::getInstance()->getBool("ScraperExcludeRecursively"), + Settings::getInstance()->getBool("ScraperRespectExclusions")); for (auto game = games.cbegin(); game != games.cend(); game++) { if (selector((*sys), (*game))) { ScraperSearchParams search; @@ -902,8 +905,10 @@ std::queue GuiScraperMenu::getSearches( return queue; } -void GuiScraperMenu::addEntry(const std::string& name, unsigned int color, - bool add_arrow, const std::function& func) +void GuiScraperMenu::addEntry(const std::string& name, + unsigned int color, + bool add_arrow, + const std::function& func) { std::shared_ptr font = Font::get(FONT_SIZE_MEDIUM); From 3757b31fbc3ace0ac72d9aa3bc32713679845618 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 11 Jul 2021 13:47:31 +0200 Subject: [PATCH 57/76] Fixed an issue where a menu option was available when it shouldn't be. Also changed some misleading comments related to graying out menu options. --- es-app/src/guis/GuiMenu.cpp | 2 +- es-app/src/guis/GuiScraperMenu.cpp | 25 ++++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index bc9a2f77f..2c337a92a 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -937,7 +937,7 @@ void GuiMenu::openOtherOptions() } }); - // If the RunInBackground setting is enabled, then disable this option. + // If the RunInBackground setting is enabled, then gray out this option. if (Settings::getInstance()->getBool("RunInBackground")) { launch_workaround->setEnabled(false); launch_workaround->setOpacity(DISABLED_OPACITY); diff --git a/es-app/src/guis/GuiScraperMenu.cpp b/es-app/src/guis/GuiScraperMenu.cpp index a03ca047e..4040c0e50 100644 --- a/es-app/src/guis/GuiScraperMenu.cpp +++ b/es-app/src/guis/GuiScraperMenu.cpp @@ -240,7 +240,7 @@ void GuiScraperMenu::openContentOptions() } }); - // Ratings are not supported by TheGamesDB, so disable the option if this scraper is selected. + // Ratings are not supported by TheGamesDB, so gray out the option if this scraper is selected. if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { scrape_ratings->setEnabled(false); scrape_ratings->setOpacity(DISABLED_OPACITY); @@ -271,7 +271,7 @@ void GuiScraperMenu::openContentOptions() } }); - // Videos are not supported by TheGamesDB, so disable the option if this scraper is selected. + // Videos are not supported by TheGamesDB, so gray out the option if this scraper is selected. if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { scrape_videos->setEnabled(false); scrape_videos->setOpacity(DISABLED_OPACITY); @@ -325,7 +325,7 @@ void GuiScraperMenu::openContentOptions() } }); - // 3D box images are not supported by TheGamesDB, so disable the option if this scraper + // 3D box images are not supported by TheGamesDB, so gray out the option if this scraper // is selected. if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { scrape_3dboxes->setEnabled(false); @@ -544,7 +544,7 @@ void GuiScraperMenu::openOtherOptions() } }); - // Regions are not supported by TheGamesDB, so disable the option if this scraper is selected. + // Regions are not supported by TheGamesDB, so gray out the option if this scraper is selected. if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { scraper_region->setEnabled(false); scraper_region->setOpacity(DISABLED_OPACITY); @@ -592,7 +592,8 @@ void GuiScraperMenu::openOtherOptions() } }); - // Languages are not supported by TheGamesDB, so disable the option if this scraper is selected. + // Languages are not supported by TheGamesDB, so gray out the option if this scraper is + // selected. if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { scraper_language->setEnabled(false); scraper_language->setOpacity(DISABLED_OPACITY); @@ -667,7 +668,7 @@ void GuiScraperMenu::openOtherOptions() } }); - // If interactive mode is set to off, then disable this option. + // If interactive mode is set to off, then gray out this option. if (!Settings::getInstance()->getBool("ScraperInteractive")) { scraper_semiautomatic->setEnabled(false); scraper_semiautomatic->setOpacity(DISABLED_OPACITY); @@ -704,7 +705,7 @@ void GuiScraperMenu::openOtherOptions() } }); - // If respecting excluded files is set to off, then disable this option. + // If respecting excluded files is set to off, then gray out this option. if (!Settings::getInstance()->getBool("ScraperRespectExclusions")) { scraper_exclude_recursively->setEnabled(false); scraper_exclude_recursively->setOpacity(DISABLED_OPACITY); @@ -740,6 +741,16 @@ void GuiScraperMenu::openOtherOptions() } }); + // The TLS/certificate issue is not present for TheGamesDB, so gray out the option if this + // scraper is selected. + if (Settings::getInstance()->getString("Scraper") == "thegamesdb") { + retry_peer_verification->setEnabled(false); + retry_peer_verification->setOpacity(DISABLED_OPACITY); + retry_peer_verification->getParent() + ->getChild(retry_peer_verification->getChildIndex() - 1) + ->setOpacity(DISABLED_OPACITY); + } + // Switch callbacks. auto interactiveToggleFunc = [scraper_semiautomatic]() { if (scraper_semiautomatic->getEnabled()) { From c72d7da6720972d204a393ea3202a4005d2e0fbe Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 11 Jul 2021 16:08:11 +0200 Subject: [PATCH 58/76] Documentation update. --- USERGUIDE-DEV.md | 361 ++++++++++++++++++++++++----------------------- 1 file changed, 182 insertions(+), 179 deletions(-) diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index b99186bba..603adc608 100644 --- a/USERGUIDE-DEV.md +++ b/USERGUIDE-DEV.md @@ -1,36 +1,36 @@ # EmulationStation Desktop Edition (ES-DE) - User guide (development version) -**Note:** This document is intended as a quick start guide as well as a reference for the user interface settings and functionality. For more in-depth information and details on how to compile ES-DE and perform more advanced configuration, please refer to [INSTALL-DEV.md](INSTALL-DEV.md). +This document is intended as a quick start guide and as a reference for the application settings and general functionality. For details on how to build ES-DE from source code and to perform more advanced configuration, please refer to [INSTALL-DEV.md](INSTALL-DEV.md). -Also note that this document is only relevant for the current ES-DE development version, if you would like to see the user guide for the latest stable release, refer to [USERGUIDE.md](USERGUIDE.md) instead. +This version of the user guide is only relevant for the current ES-DE development version, if you are using the latest stable release, refer to [USERGUIDE.md](USERGUIDE.md) instead. Table of contents: [[_TOC_]] -## Basic steps to get ES-DE up and running +## Quick start guide If you just want to get started as quickly as possible, simply follow these steps: 1) Install ES-DE 2) Start the application and press the _Create directories_ button to generate the ROMs directory structure 3) Put your game ROMs in the directories created by the previous step, or see [here](USERGUIDE-DEV.md#supported-game-systems) for additional details -4) Install and configure [RetroArch](https://www.retroarch.com) -5) Start RetroArch and install the required emulator cores - to see which ones you need look in the systeminfo.txt files in the directories created by step 2, or again see [here](USERGUIDE-DEV.md#supported-game-systems) +4) Install [RetroArch](https://www.retroarch.com) +5) Start RetroArch and install the required emulator cores 6) Start ES-DE, scrape game media for your collection and play some games! -You can always press F4 to close the application. +You can always close the application immediately by pressing F4 on the keyboard. For additional details, read on below. There are also installation videos available at the ES-DE YouTube channel:\ [https://www.youtube.com/channel/UCosLuC9yIMQPKFBJXgDpvVQ](https://www.youtube.com/channel/UCosLuC9yIMQPKFBJXgDpvVQ) -## Getting started +## Installation and first startup -Getting started with ES-DE is easy, just make sure to install the software properly, either manually as built from source code or using one of the supplied packages. On Windows and macOS you'll use the installer instead of a package. +To install ES-DE, just download the package or installer from [https://es-de.org](https://es-de.org) and follow the brief instructions below. -The following operating systems have been tested with ES-DE (all for the x86 architecture unless otherwise stated): +The following operating systems have been tested (all for the x86 architecture unless otherwise stated): * Ubuntu 20.04 to 21.04 * Linux Mint 20 @@ -50,8 +50,7 @@ The installation procedure is just covered briefly here and may differ a bit for **Installing a Linux .deb package** -The .deb package is used for Linux distributions based on Debian, such as Ubuntu, Linux Mint etc. -Running the following should install ES-DE and resolve any dependencies: +The .deb package is intended for Linux distributions based on Debian, such as Ubuntu, Linux Mint etc. Your distribution should include a graphical package installer, but if you prefer to use the command line, run the following which will install ES-DE and resolve any dependencies: ``` sudo apt install ./emulationstation-de-1.0.0-x64.deb @@ -59,7 +58,7 @@ sudo apt install ./emulationstation-de-1.0.0-x64.deb **Installing a Linux .rpm package** -On Fedora you run this command to install ES-DE, which should automatically resolve all dependencies: +On Fedora you can use the graphical package installer or run this command, either method should automatically resolve the dependencies: ``` sudo dnf install ./emulationstation-de-1.0.0-x64.rpm @@ -67,7 +66,7 @@ sudo dnf install ./emulationstation-de-1.0.0-x64.rpm **Installing on macOS and Windows** -There's not really much to say about these operating systems, just install ES-DE as you would any other application. On maCOS it's via the .dmg drag-and-drop installer, and on Windows it's just a normal application installer. +There's not really much to say about these operating systems, just install ES-DE as you would any other application. On maCOS it's via the .dmg drag-and-drop installer, and on Windows via the normal application installer. **On first application startup** @@ -75,17 +74,17 @@ Upon first startup, ES-DE will create its `~/.emulationstation` home directory. On Unix this means /home/\/.emulationstation/, on macOS /Users/\/.emulationstation/ and on Windows C:\Users\\\\.emulationstation\ +**Note:** As of ES-DE v1.1 there is no internationalization support, which means that the application will always require the physical rather than the localized path to your home directory. For instance on macOS configured for the Swedish language /Users/myusername will be the physical path but /Användare/myusername is the localized path that is actually shown in the user interface. The same is true on Windows where the directories would be C:\Users\myusername and C:\Användare\myusername respectively. If attempting to enter the localized path for any directory-related setting, ES-DE will not be able to find it. But it's always possible to use the tilde `~` symbol when referring to your home directory, which ES-DE will expand to the physical location regardless of what language you have configured for your operating system. If you're using an English-localized system, this whole point is irrelevant as the physical and localized paths are always identical. + It's also possible to override the home directory path using the --home command line option, but this is normally required only for very special situations so we can safely ignore that option for now. -**Note:** As of ES-DE v1.1 there is no internationalization support, so you would always need to supply the English directory name for your home directory, which is by the way always the real physical name on the file system. For instance on macOS, /Users/myusername will be required instead of /Användare/myusername which is what's shown inside the operating system for a Swedish localized installation. But using the tilde home symbol '~' is a workaround for this, and it's recommended to always use it for any ES-DE configuration settings that require a path to your home directory as it removes any confusion regarding localized home directory paths. +Also on first startup the configuration file `es_settings.xml` will be generated in the ES-DE home directory, containing all the application settings at their default values. Following this, a file named `es_systems.xml` will be loaded from the resources directory (which is part of the ES-DE installation). This file contains the game system definitions including which emulator to use per platform, and it can be customized if needed. The customized version needs to be placed in the `custom_systems` folder in the ES-DE home directory, i.e. `~/.emulationstation/custom_systems/es_systems.xml`. You can find information on how to customize the systems configuration file in the [INSTALL-DEV.md](INSTALL-DEV.md#es_systemsxml) document. -On first startup the configuration file `es_settings.xml` will be generated in the ES-DE home directory, containing all the default settings. A file named `es_systems.xml` will be loaded from the resources directory (which is part of the ES-DE installation). This file contains the game ROM paths and emulator settings and it can be customized if needed. The customized version needs to be placed in the `custom_systems` folder in the ES-DE home directory, for example `~/.emulationstation/custom_systems/es_systems.xml`. For more information on how to do this, refer to the [INSTALL-DEV.md](INSTALL-DEV.md#es_systemsxml) document. +There's an application log file created in the ES-DE home directory named `es_log.txt`, please refer to this in case of any issues as it should hopefully provide information on what went wrong. Starting ES-DE with the --debug flag provides even more detailed information. -There's a log file in the ES-DE home directory named `es_log.txt`, please refer to this in case of any issues as it should hopefully provide information on what went wrong. Starting ES-DE with the --debug flag provides even more detailed information. +After ES-DE finds at least one game file, it will populate that game system and the application will start. If there are no game files, a dialog will be shown explaining that you need to install your game files into your ROMs directory. You will also be given a choice to change that ROMs directory path if you don't want to use the default one. As well you have the option to generate the complete game systems directory structure based on information in es_systems.xml. -After ES-DE finds at least one game file, it will populate that game system and the application will start. If there are no game files, a dialog will be shown explaining that you need to install your game files into your ROM directory, and you will also be given a choice to change that ROM directory path if you don't want to use the default one. As well you have the option to generate the complete game systems directory structure based on information in es_systems.xml. - -When generating the directory structure, a file named systeminfo.txt will be created in each game system folder which will provide you with some information about the system such as the supported file extensions. Here's an example for the _gc_ system as seen on macOS: +When generating the directory structure, a file named systeminfo.txt will be created in each game system folder which will provide you with some information about the system. Here's an example for the _gc_ system as seen on macOS: ``` System name: gc @@ -97,7 +96,7 @@ Supported file extensions: .gcm .GCM .iso .ISO .wbfs .WBFS .ciso .CISO .gcz .GCZ .elf .ELF .dol .DOL .dff .DFF .tgc .TGC .wad .WAD .7z .7Z .zip .ZIP Launch command: -/Applications/RetroArch.app/Contents/MacOS/RetroArch -L %EMUPATH%/../Resources/cores/dolphin_libretro.dylib %ROM% +%EMULATOR_RETROARCH% -L %CORE_RETROARCH%/dolphin_libretro.dylib %ROM% Platform (for scraping): gc @@ -106,7 +105,9 @@ Theme folder: gc ``` -In addition to this, a file named systems.txt will be created in the root ROM directory which shows the mapping between the directory names and the full system names. +The primary use of this file is to see which RetroArch core the system needs, which you will have to install manually from inside the RetroArch user interface. Also the supported file extensions can be quite useful to know. + +In addition to this, a file named systems.txt will be created in the root of the ROMs directory which shows the mapping between the directory names and the full system names. For example: @@ -116,57 +117,57 @@ genesis: Sega Genesis gx4000: Amstrad GX4000 ``` -Note that neither the systeminfo.txt files or the systems.txt file are needed to run ES-DE, they're only generated as a convenience. +Note that neither the systeminfo.txt files or the systems.txt file are needed to run ES-DE, they're only generated as a convenience to help with the setup. -Be aware that there will be a lot of directories created if using the template es_systems.xml file bundled with the installation, so it may be a good idea to remove the ones you don't need. It's recommended to move them to another location to be able to use them later if more systems should be added. For example a directory named _DISABLED could be created inside the ROMs folder (i.e. ~/ROMs/_DISABLED) and all game system directories you don't need could be moved there. Doing this reduces the application startup time significantly as ES-DE would otherwise need to scan for game files for all these systems. +There will be a lot of directories created if using the es_systems.xml file bundled with the installation, so it may be a good idea to remove the ones you don't need. It's recommended to move them to another location to be able to use them later if more systems should be added. For example a directory named _DISABLED could be created inside the ROMs folder (i.e. ~/ROMs/_DISABLED) and all game system directories you don't need could be moved there. Doing this reduces the application startup time as ES-DE would otherwise need to scan for game files for all these systems. ![alt text](images/current/es-de_ui_easy_setup.png "ES-DE Easy Setup") -_This is the dialog shown if no game files were found. It lets you configure the ROM directory if you don't want to use the default one, and you can also generate the game systems directory structure. Note that the directory is the real physical path, and that your operating system may present this as a localized path if you are using a language other than English._ +_This is the dialog shown if no game files were found. It lets you configure the ROM directory if you don't want to use the default one, and you can also generate the game systems directory structure. Note that the directory is the physical path, and that your operating system may present this as a localized path if you are using a language other than English._ ## Migrating from other EmulationStation forks **IMPORTANT!!! IMPORTANT!!! IMPORTANT!!!** -ES-DE is designed to be backwards compatible to a certain degree, that is, it should be able to read data from other/previous EmulationStation versions such as the RetroPie fork. But the opposite is not true and it's a one-way ticket for your gamelist.xml files and your custom collection files when migrating to ES-DE as they will be modified in ways that previous ES versions will see as data loss. For instance ES-DE does not use image tags inside the gamelist.xml files to find game media but instead matches the media to the names of the game/ROM files. So it will not save any such tags back to the gamelist files during updates, effectively removing the display of the game media if the files are opened in another ES fork. +ES-DE is designed to be backward compatible to a certain degree, that is, it should be able to read data from other/previous EmulationStation versions such as the RetroPie fork. But the opposite is not true and it's a one-way ticket for your gamelist.xml files and your custom collection files when migrating to ES-DE as they will be modified in ways that previous ES versions will see as data loss. For instance ES-DE does not use image tags inside the gamelist.xml files to find game media but instead matches the media to the names of the game/ROM files. So it will not save any such tags back to the gamelist files during updates, effectively removing the display of the game media if the files are opened in another ES fork. -Due to this, always make backups of at least the following data before using ES-DE: +Due to this, always make backups of at least the following directories before testing ES-DE for the first time: ``` ~/.emulationstation/gameslists/ ~/.emulationstation/collections/ ``` -## Running on 4K displays +## Running on high resolution displays -ES-DE fully supports 4K displays (as well as 1440p and other higher resolutions) but some emulators such as RetroArch will always run using the active screen resolution, meaning the emulation will also run in 4K. On slower computers and when resource intensive shaders are in use, the performance may be quite bad. Although it's possible to start ES-DE with the `--resolution` option (which also applies to any launched emulators), this is not really recommended. Full screen mode only works on Unix with this option and even then it's highly dependent on well-written graphics drivers for proper behavior. ES-DE uses the SDL library which insists on setting xrandr to panning mode when doing resolution changes, which is quite annoying especially when using Nvidia drivers. +ES-DE fully supports high resolution displays such as 4K, 6K, 1440p, ultrawide monitors etc. But some emulators such as RetroArch will also run using the same resolution which may cause performance problems on slower machines or when using resource intensive shaders. Although some emulator cores will have options to set their internal resolution, they still need to be scaled up to the screen resolution. On Unix it's possible to start ES-DE with the `--resolution` option to set a lower screen resolution (this will also affect the emulators) but this is not really recommended as it's highly dependent on well-written graphics drivers for proper behavior. -A better approach is to use the custom event scripts functionality in ES-DE to set a temporary resolution upon launching a game that will be reverted when returning from the emulator. This is detailed as an example for Unix in [INSTALL-DEV.md](INSTALL-DEV.md#custom-event-scripts) but should be possible to implement similarly on other operating systems such as macOS and Windows. +A better approach is to use the custom event scripts functionality to set a temporary resolution upon launching a game that will be reverted when returning to ES-DE. Such a setup is detailed in [INSTALL-DEV.md](INSTALL-DEV.md#custom-event-scripts) for Unix, but should hopefully be possible to implement similarly on macOS and Windows. ## Input device configuration -Upon startup of ES-DE, any connected controllers will be automatically detected and default button mappings will be applied. The same is true for the keyboard. Normally no additional configuration is required, but if you would like to apply custom button mappings for the devices, you can run the `Configure keyboard and controllers` tool from the `Input device settings` entry on the main menu. +ES-DE automatically configures the keyboard and any connected controllers using default button mappings, and normally no additional setup is required. But if you would like to apply custom button mappings for your devices, you can run the `Configure keyboard and controllers` tool from the `Input device settings` entry on the main menu. You can also force a run of this tool directly on startup via the command line argument `--force-input-config`. The actual procedure to map the inputs should be self-explanatory, just follow the on-screen instructions. -Be aware that any custom configuration is applied per unique device ID (GUID). So if two identical controllers are used with ES-DE, both will have the same configuration applied. But if connecting controllers of the same type but of different revisions, the GUID may differ and therefore the custom configuration would need to be applied to each device individually. +Any custom configuration is applied per unique device ID (GUID). So if two identical controllers are used with ES-DE, both will have the same configuration applied. If connecting controllers of the same type but of different revisions, the GUID may differ and therefore the custom configuration would need to be applied to each device individually. Note that custom button mappings will not change the help prompts. If you have issues with your input configuration, as a last resort you can reset all the mappings by deleting or renaming the file ~/.emulationstation/es_input.xml. -**Note:** If you experience double button presses with your DualShock 4 controller on macOS, please read about the workaround for this issue in the [Known issues](CHANGELOG.md#known-issues) section of the changelog. +If you experience double button presses with your DualShock 4 controller on macOS, please read about the workaround for this issue in the [Known issues](CHANGELOG.md#known-issues) section of the changelog. ## System view (main screen) -When starting EmulationStation with the default settings, you will see the main screen first. From here you can navigate your game systems and enter their respective gamelists. +When starting ES-DE with the default settings, you will see the System view first. From here you can navigate your game systems and enter their respective gamelists. -Depending on the theme, the system navigation carousel can be either horizontal or vertical. The default theme rbsimple-DE provides horizontal navigation, i.e. you browse your systems be scrolling left or right. +Depending on the theme, the system navigation carousel can be either horizontal or vertical. The default theme rbsimple-DE provides horizontal navigation, i.e. you browse your systems by scrolling left or right. -The game systems are sorted by their full names, as defined in es_systems.xml. +The game systems are sorted by their full names, as defined in the es_systems.xml file. ![alt text](images/current/es-de_system_view.png "ES-DE System View") _The **System view** is the default starting point for the application, it's here that you browse through your game systems._ @@ -175,13 +176,13 @@ _The **System view** is the default starting point for the application, it's her The gamelist view is where you browse and start your games, and it's where you will spend most of your time using ES-DE. -Upon startup with the default settings, ES-DE is set to the gamelist view style **Automatic**. In this mode the application will look for any game media files (videos and images) and set the view style accordingly. If at least one image is found for any game, the view style **Detailed** will be shown, and if at least one video file is found, the view style **Video** will be selected (superceding the Detailed style). If no game media files are found for a system, the simple **Basic** view will be selected. Note that this automatic selection is applied per game system. +Upon startup with the default settings, ES-DE is set to the gamelist view style **Automatic**. In this mode the application will look for any game media files (videos and images) and set the view style accordingly. If at least one image is found for any game, the view style **Detailed** will be shown, and if at least one video file is found, the view style **Video** will be selected (superceding the Detailed style). If no game media files are found for a system, the simple **Basic** view style will be selected. This automatic selection is applied per game system. -Also note that the Video view style requires that the theme supports it. If not, the Detailed style will be selected instead. (The default theme rbsimple-DE supports both of these view styles). +Note that the Video view style requires that the theme supports it. If not, the Detailed style will be selected instead. The default theme rbsimple-DE supports both of these view styles. -It's possible to manually set a specific gamelist view style in the UI settings entry of the main menu, but this is applied globally regardless of what media files are available per game system. The manual setting also overrides the theme-supported view styles which has the potential of making ES-DE very ugly indeed. +It's possible to manually set a specific gamelist view style in the UI settings entry of the main menu, but this is applied globally regardless of what media files are available per game system. The manual setting also overrides the theme-supported view styles which has the potential of making ES-DE very ugly indeed if the theme does not support the selected view style. -In addition to the styles just described, there is a **Grid** view style as well, but as of ES-DE version 1.0 this is highly experimental and its use is not recommended. Future versions will update this style to a more useful state. +In addition to the styles just described, there is a **Grid** view style as well, but as of ES-DE version 1.1 this is highly experimental and its use is not recommended. Future versions may update this style to a more useful state or it may be removed entirely. If the theme supports it, there's a gamelist information field displayed in the gamelist view, showing the number of games for the system (total and favorites) as well as a folder icon if a folder has been entered. When applying any filters to the gamelist, the game counter is replaced with the amount of games filtered, as in 'filtered / total games', e.g. '19 / 77'. If there are game entries in the filter result that are marked not to be counted as games, the number of such files will be indicated as 'filtered + filtered non-games / total games', for example '23 + 4 / 77' indicating 23 normal games, 4 non-games out of a total of 77. Due to this approach it's theoretically possible that the combined filtered game amount exceeds the number of counted games in the collection, for instance '69 + 11 / 77'. This is not considered a bug and is so by design. This gamelist information field functionality is specific to EmulationStation Desktop Edition so older themes will not support this. @@ -189,7 +190,7 @@ If the theme supports it, there's a gamelist information field displayed in the _The **Gamelist view** is where you browse the games for a specific system._ ![alt text](images/current/es-de_basic_view_style.png "ES-DE Basic View Style") -_Here's an example of what the Basic view style looks like. Needless to say, ES-DE is not intended to be used like this. After scraping some game media for the system, the view style will automatically change to Detailed or Video (assuming the Automatic view style has been selected)._ +_Here's an example of what the Basic view style looks like. Needless to say, ES-DE is not intended to be used like this. After scraping some game media for the system, the view style will automatically change to Detailed or Video (assuming the Automatic view style option has been selected)._ ## UI modes @@ -201,13 +202,13 @@ These modes mandate the functionalty provided by the application in the followin * Kiosk - The main menu will be severely restricted, only displaying the entry to change the audio volume. The game options menu will be restricted as well, removing the metadata editor and the ability to modify custom game collections. And finally the ability to flag or unflag games as favorites will be removed. Apart from this all games will be playable. * Kid - Only games marked as being suitable for children will be displayed (this flag is set manually per game using the metadata editor). Additionally, the game options menu is disabled, as well as the screensaver controls and the ability to flag and unflag games as favorites. There is also a separate option available to enable or disable the main menu when in Kid mode, see **Enable menu in kid mode** for additional information. -There is an unlock code available to revert to the Full mode from the Kiosk or Kid mode, as is described when changing this setting from the main menu. By default the button sequence is **Up, Up, Down, Down, Left, Right, Left, Right, B, A**. It works to use either a keyboard or a configured controller to input the passkey sequence, but it can't be entered when a menu is open. +There is an unlock code available to revert to the Full mode from the Kiosk or Kid mode, as is described when changing this setting from the main menu. By default the button sequence is **Up, Up, Down, Down, Left, Right, Left, Right, B, A** (or equivalent buttons if an Xbox controller is not used). Either the keyboard or a controller can be used to input the passkey sequence, but it can't be entered when a menu is open. -The application can also be forced into any of the three modes via the command line options `-force-full`, `--force-kiosk` and `-force-kid`. Note that this is only temporary until the restart of the application, unless the settings menu is entered and the setting is saved to the configuration file (this assumes that the main menu is available in the selected UI mode of course). +The application can also be forced into any of the three modes via the command line options `--force-full`, `--force-kiosk` and `--force-kid`. This is only temporary until the restart of the application, unless the settings menu is entered and the setting is saved to the configuration file (this assumes that the main menu is available in the selected UI mode of course). ## Help system -There is a help system available throughout the application that provides an overview of the possible actions and buttons that can be used. Note though that some general button actions are never shown, such as the ability to quick jump in gamelists, menus and text input fields using the shoulder and trigger buttons. It's also possible to disable the help system using a menu option for a somewhat cleaner look. +There is a help system available throughout the application that provides an overview of the possible actions and buttons that can be used. But some general actions are never shown, such as the ability to quick jump in gamelists, menus and text input fields using the shoulder and trigger buttons. It's also possible to disable the help system using a menu option for a somewhat cleaner look. ![alt text](images/current/es-de_folder_support.png "ES-DE Help System") _The help system is displayed at the bottom of the screen, showing the various actions currently available._ @@ -215,7 +216,7 @@ _The help system is displayed at the bottom of the screen, showing the various a ## General navigation -The built-in help system will provide a contextual summary of the available navigation options, but here's still a general overview. These are the buttons optionally mapped in the previous input device configuration step. Note that this is not an exhaustive list, but it covers most situations. The button names are based on the Xbox 360 controller as that is the naming convention used by the SDL library which handles the controller input in ES-DE. +The built-in help system will provide a contextual summary of the available navigation options, but here's still a general overview. These are the buttons mappings automatically applied by ES-DE, but they can be customized using the input configurator as described earlier in this document. It's not an exhaustive list, but it covers most situations. The button names are based on the Xbox 360 controller as that is the naming convention used by the SDL library which handles the controller input in ES-DE. The default keyboard mappings are shown in brackets. @@ -227,7 +228,7 @@ Navigate up and down in gamelists, between systems in the system view (if the th **Left and right**\ _(Arrow left / Arrow right)_ -Navigate between gamelists (if _Quick system select_ has been activated in the options), or between systems in the system view (if the theme has a horizontal carousel). +Navigate between gamelists (if the _Quick system select_ option has been enabled), between systems in the system view (if the theme has a horizontal carousel) and between media files in the media viewer. If the _Enable screensaver controls_ option has been enabled, either button also randomly selects a new game when using the Video or Slideshow screensavers. **Start button**\ _(Escape)_ @@ -237,7 +238,7 @@ Opens and closes the main menu. **Back button**\ _(F1)_ -Opens and closes the game options menu in the gamelist view, or toggles the screensaver in the system view (if the _Enable screensaver controls_ setting is activated). +Opens and closes the game options menu in the gamelist view, or toggles the screensaver in the system view (if the _Enable screensaver controls_ setting is enabled). **Left and right shoulder buttons**\ _(Page up / Page down)_ @@ -252,12 +253,12 @@ Jumps to the first and last entry of the gamelists, menus and text edit dialogs. **Left and right thumbstick click**\ _(F2 / F3)_ -Jumps to a random game or system depending on whether pressed when in the system view or gamelist view. This functionality can be enabled or disabled via a menu option. +Jumps to a random game or system depending on whether pressed when in the system view or gamelist view. Only available if the _Enable random system or game button_ option has been enabled. **A button**\ _(Enter)_ -Used to open gamelists from the system view, launch games, choose menu entries etc. +Opens gamelists from the system view, launches games, selects menu entries etc. **B button**\ _(Back key)_ @@ -272,24 +273,23 @@ Starts the game media viewer (which is accessible from the gamelist views). Used **Y button**\ _(Insert on Unix and Windows, F13 on macOS)_ -Marks games as favorites in the gamelist view (this functionality can be enabled or disabled via a menu option). Used by some other minor functions as explained by the help system and/or this guide. +Marks games as favorites in the gamelist view (if the _Enable toggle favorites button_ option has been enabled). Used by some other minor functions as explained by the help system and/or this guide. **F4 (keyboard only)** -Quits the application. - +Quits the application immediately. ## Getting your games into ES-DE -For most systems, this is very straightforward, just put your game files into the folder corresponding to the platform name (these names can be found at the [end](USERGUIDE-DEV.md#supported-game-systems) of this guide.) +For most systems this is straightforward, just put your game files into the folder corresponding to the platform name (these names can be found at the [end](USERGUIDE-DEV.md#supported-game-systems) of this guide.) -For some systems though, a more elaborate setup is required, and we will attempt to cover such situations in this guide as well. +But for some systems a more elaborate setup is required, and we will attempt to cover such situations in this guide as well. ### Single game file installation -Let's start with the simple scenario of a single ROM game file per platform, which is the case for the majority of systems. In this example we're setting up ES-DE to play Nintendo Entertainment System games. +Let's start with the simple scenario of a single ROM file per game, which is the case for the majority of platforms. In this example we're setting up ES-DE to play Nintendo Entertainment System games. -The supported file extensions are listed in [unix/es_systems.xml](resources/systems/unix/es_systems.xml), [macos/es_systems.xml](resources/systems/macos/es_systems.xml) and [windows/es_systems.xml](resources/systems/windows/es_systems.xml) but if you generated the game system directories on first application startup, there will be a file named systeminfo.txt in each game system directory that includes the supported file extensions. +The supported file extensions are listed in [unix/es_systems.xml](resources/systems/unix/es_systems.xml), [macos/es_systems.xml](resources/systems/macos/es_systems.xml) and [windows/es_systems.xml](resources/systems/windows/es_systems.xml) but if you generated the game system directories on first application startup, there will be a file named systeminfo.txt in each game system directory that includes the list of supported file extensions. Here is the snippet from the unix/es_systems.xml file: @@ -305,13 +305,13 @@ Here is the snippet from the unix/es_systems.xml file: ``` -It's required that the ROM files are in one of the supported file extensions, or ES-DE won't find them. +The ROM files must named using one of the supported file extensions, or ES-DE won't find them. It's highly recommended to use filenames that are corresponding to the full name of the game, otherwise you will need to manually feed the scraper the game name when scraping which is very tedious. -**Note:** Symlinks are supported for both ROM directories and individual game files, but make sure to not symlink between files within the same system or there may be undefined application behavior when scraping, launching games etc. +**Note:** Symlinks are supported for both ROM directories and individual game files, but make sure to not symlink between files within the same system directory or there may be undefined application behavior when scraping, launching games etc. -The default game directory folder is ~/ROMs. On Unix this defaults to /home/\/ROMs, on macOS /Users/\/ROMs and on Windows C:\Users\\\ROMs\. Be aware that if the --home command line option was used to start ES-DE, the tilde `~` symbol will resolve to whatever directory was passed as an argument to this option. +The default game directory folder is ~/ROMs. On Unix this defaults to /home/\/ROMs, on macOS /Users/\/ROMs and on Windows C:\Users\\\ROMs\. If the --home command line option was used to start ES-DE, the tilde `~` symbol will resolve to whatever directory was passed as an argument to this option. If ES-DE can't find any game files during startup, an error message will be displayed with the option to change the ROM directory path. @@ -370,11 +370,11 @@ It's of course also possible to skip this type of directory structure and put al When setting up games in this fashion, it's recommended to scrape the directory in addition to the .m3u file as it looks nicer to see the metadata for the games also when browsing the folders. ES fully supports scraping folders, although some metadata is not included for folders for logical reasons. If you only scrape the folders and not the actual game files, it may look somehow ok when browsing, but when a game is part of a collection, the metadata will be missing there. This includes the **Last played** and **All games** collections for instance. Also note that while it's possible to mark a folder as a favorite, it will never be part of a collection, such as **Favorites**. -As well it's recommended to set the flags **Exclude from game counter** and **Exclude from automatic scraper** for the actual game files so that they are not counted for the game statistics display and not scraped when running the multi-scraper. If you don't want to hide the individual game files but still want a cleaner look, it's also possible to set the flag **Hide metadata fields** for the game files. +It's also recommended to use the metadata editor to set the flags **Exclude from game counter** and **Exclude from automatic scraper** for the actual game files so that they are not counted for the game statistics display and not scraped when running the multi-scraper. If you don't want to hide the individual game files but still want a cleaner look, it's also possible to set the flag **Hide metadata fields** for the game files. ### Special game installation considerations -Not all systems are as simple as described above, or sometimes there are multiple ways to configure the systems. So specifics to such systems will be covered here. Consider this a work in progress as there are many systems supported by ES-DE. +Not all systems are as simple as described above, or there may be multiple ways to do the configuration. Specifics for such systems will be covered here. Consider this a work in progress as there are many platforms supported by ES-DE. #### Arcade and Neo Geo @@ -382,7 +382,7 @@ For all the supported MAME variants as well as Final Burn Alpha/FinalBurn Neo an For instance `topgunnr.7z` will be expanded to `Top Gunner`. -This is used by the TheGamesDB scraper where the expanded file names are used for game searches. (Screenscraper natively supports searches using the MAME names). It's also quite nice to have the gamelist populated with the expanded game names even before any scraping has taken place. +This is required by the TheGamesDB scraper where the expanded file names are used for game searches. (Screenscraper natively supports searches using the MAME names). It's also quite nice to have the gamelist populated with the expanded game names even before any scraping has taken place. #### Nintendo Switch @@ -412,7 +412,7 @@ Apart from the potential difficulty in locating the emulator binary, there are s There are multiple ways to run Amiga games, but the recommended approach is to use WHDLoad. The best way is to use hard disk images in `.hdf` or `.hdz` format, meaning there will be a single file per game. This makes it just as easy to play Amiga games as any console with game ROMs. -An alternative would be to use `.adf` images as not all games may be available with WHDLoad support. For this, you can either put single-disk images in the root folder or in a dedicated adf directory, or multiple-disk games in separate folders. It's highly recommended to create `.m3u` playlist files for multi-disk images as described above. +An alternative would be to use `.adf` images as not all games may be available with WHDLoad support. For this, you can either put single-disk images in the root folder or in a dedicated adf directory, or multiple-disk games in separate folders. It's highly recommended to create `.m3u` playlist files for multi-disk images as described earlier. Here's an example of what the file structure could look like: @@ -430,13 +430,13 @@ Advanced topics such as the need for the Amiga Kickstart ROMs to run Amiga games #### DOS / PC -The DOS (and PC) platform uses the DOSBox emulator and the recommended approach here is to keep the directory structure intact, just as if running the game on a real DOS computer. So this means one folder per game in ES-DE. It's also recommended to set the metadata field **Count as game** to off for all files but the actual file used to launch the game, i.e. the binary or a .bat batch file. This is done so that the game counter correctly reflects the number of games you have installed. It's also possible to mark files and subdirectories as hidden to avoid seeing them in ES-DE. Both of these fields can be set using the metadata editor. +The DOS (and PC) platform uses the DOSBox emulator and the recommended approach here is to keep the directory structure intact, just as if running the game on a real DOS computer. So this means one folder per game in ES-DE. It's also recommended to set the metadata field **Count as game** to off for all files but the actual file used to launch the game, i.e. the binary or the .bat batch file. This is done so that the game counter correctly reflects the number of games you have installed. It's also possible to mark files and subdirectories as hidden to avoid seeing them in ES-DE. Both of these fields can be set using the metadata editor. Apart from this, DOS games should work the same as any other system. The game folders can be scraped so that it looks nice when browsing the list of games, but make sure to also scrape the files used to launch the games or otherwise the entries in the collections **Last played**, **Favorites** and **All games** as well as any custom collections will miss the game metadata and game media. If you don't have these collections activated, then this can of course be skipped. #### Ports -Ports are not really executed using emulators, but are rather applications running natively in the operating system. The easiest way to handle these is to add a simple shell script or batch file where you can customize the exact launch parameters for the game. +Ports are not really executed using emulators, but are rather applications running natively on the operating system. The easiest way to handle these is to add a simple shell script or batch file where you can customize the exact launch parameters for the game. It's of course possible to add these as single files to the root folder, but normally it's recommended to setup a separate folder per game as there may be more than a single file available per game. You very often want to have easy access to the game setup utility for instance. @@ -486,9 +486,11 @@ You don't need to set execution permissions for these scripts, ES-DE will run th #### Lutris -Lutris runs only on Unix so it's only present as a placeholder in the es_systems.xml templates for macOS and Windows. +Lutris runs only on Unix so it's only present as a placeholder in the es_systems.xml files for macOS and Windows. -These games are executed via the Lutris binary (well it's actually a Python script), and you simply create a shell script per game using the syntax `lutris lutris:rungame/` +These games are executed via the Lutris binary (well it's actually a Python script) and you simply create a shell script per game inside the lutris system directory. + +Add the game information to each shell script using the syntax `lutris lutris:rungame/` You can see the list of installed games by running this command: ``` @@ -522,9 +524,9 @@ As an alternative, you can add the Lutris games to the Ports game system, if you **Note:** Launching Steam games currently has some limitations such as missing error messages when a game fails to start as well as missing game output logging. ES-DE also needs to keep running in the background when launching Steam games, which has some minor side effects. -As for the setup, it's recommended to place shell scripts/batch files directly in the root folder, with the filenames of these scripts corresponding to the game names. +As for the setup, it's recommended to place shell scripts/batch files directly in the steam system directory, with the filenames of these scripts corresponding to the game names. -Add the game information to each file using the syntax `steam://rungameid/` +Add the game information to each shell script using the syntax ` steam://rungameid/` Here's an example for the game Broforce, first on Unix with the filename `Broforce.sh`: @@ -551,6 +553,7 @@ On Linux it's very easy to find all your game ID's by looking in the desktop ent ``` grep steam ~/.local/share/applications/*desktop | grep rungameid +/home/myusername/.local/share/applications/Broforce.desktop:Exec=steam steam://rungameid/274190 /home/myusername/.local/share/applications/FEZ.desktop:Exec=steam steam://rungameid/224760 /home/myusername/.local/share/applications/INSIDE.desktop:Exec=steam steam://rungameid/304430 /home/myusername/.local/share/applications/Subnautica.desktop:Exec=steam steam://rungameid/264710 @@ -561,7 +564,7 @@ This of course assumes that you have menu entries setup for your Steam games. ## RetroArch setup -ES-DE is a game browsing frontend and does not provide any emulation by itself. It does however come preconfigured for use with emulators as setup in the `es_systems.xml` file. By default it's primarily setup for use with [RetroArch](https://www.retroarch.com) but this can be modified if needed. If you're interested in customizing your es_systems.xml file, please refer to the [INSTALL-DEV.md](INSTALL-DEV.md) document which goes into details on the structure of this file and more advanced configuration topics in general. +ES-DE is a game browsing front-end and does not provide any emulation by itself. It does however come preconfigured for use with emulators as setup in the `es_systems.xml` file. By default it's primarily setup for use with [RetroArch](https://www.retroarch.com) but this can be modified if needed. If you're interested in customizing your es_systems.xml file, please refer to the [INSTALL-DEV.md](INSTALL-DEV.md#es_systemsxml) document which goes into details on the structure of this file and more advanced configuration topics in general. Installation and configuration of RetroArch and other emulators is beyond the scope of this guide, but many good resources can be found online on how to do this. @@ -569,7 +572,7 @@ Keep in mind that ES-DE will not install any RetroArch cores, you need to do thi A general recommendation regarding installation on Linux is to try to avoid the RetroArch releases included in the OS repositories as they're usually quite limited with regards to the number of available cores, and they're usually older versions. Instead go for either the Snap, Flatpak or AppImage distributions or build from source. -The default es_systems.xml file is paired with a file named es_find_rules.xml which tries to find the emulators and cores using some predefined rules. For Windows this should normally just work, and for macOS as well as long as RetroArch is installed at the default location /Applications/RetroArch.app. For Unix/Linux there is one exception that is problematic which is AppImages as there is no real standardized directory for storing these images. ES-DE will look for the RetroArch AppImage in the following locations in addition to searching the system PATH: +The default es_systems.xml file is paired with a file named es_find_rules.xml which tries to find the emulators and cores using some predefined rules. For Windows this should normally just work, and for macOS too as long as RetroArch is installed at the default location /Applications/RetroArch.app. For Unix/Linux there is one exception that is problematic which is AppImages as there is no real standardized directory for storing these images. ES-DE will look for the RetroArch AppImage in the following locations in addition to searching the PATH variable: ``` ~/Applications/RetroArch-Linux-x86_64.AppImage @@ -586,7 +589,7 @@ Scraping means downloading metadata and game media files (images and videos) for ES-DE supports the two scrapers [ScreenScraper.fr](https://www.screenscraper.fr) and [TheGamesDB.net](https://thegamesdb.net). In general TheGamesDB supports less formats and less systems, but in some areas such PC gaming, the quality is better and sometimes ScreenScraper is missing some specific information such as release dates where TheGamesDB may be able to fill in the gaps. -Here is an overview of what's supported by ES-DE and these scrapers: +Here's an overview of what's supported when using these scrapers: | Media type or option | ScreenScraper | TheGamesDB | | :----------------------- | :-----------: | :--------: | @@ -616,7 +619,7 @@ The single-game scraper is launched from the metadata editor. You navigate to a ### Multi-scraper -The multi-scraper is accessed from the main menu by selecting **Scrape**. +The multi-scraper is accessed from the main menu by entering the **Scraper** menu and then selecting the **Start** button. ### Scraping process @@ -624,17 +627,17 @@ The process of scraping games is basically identical between the single-game scr By default, ES-DE will search using the metadata name of the game. If no name has been defined via scraping or manually using the metadata editor, this name will correspond to the physical file name minus all text inside either normal brackets `()` or square brackets `[]`. So for example the physical filename `Mygame (U) [v2].zip` will be stripped to simply `Mygame` when performing the scraping. -The behavior of using the metadata name rather than the file name can be changed using the setting **Search using metadata name**. +By disabling the option **Search using metadata name**, the physical file name will be used even if there is a scraped or manually entered name for the game. -Note that there is an exception to this behavior for arcade games (MAME and Neo Geo). For ScreenScraper the short MAME names are used by default as this scraper service fully supports that. For TheGamesDB the short names are instead expanded to the full games names using a lookup in the MAME name database supplied with the ES-DE installation. It's possible to override this automatic behavior by using the _Refine Search_ button in the scraper GUI if the search did not yield any results, or if the wrong game was returned. In general though, searching for arcade games is very reliable assuming the physical game files follow the MAME name standard. +There is however an exception to this for arcade games (MAME and Neo Geo) when using the TheGamesDB scraper. As this service does not support searches using the short MAME names, these will be expanded to the full game names via a lookup in the MAME name database supplied with the ES-DE installation. But if using ScreenScraper the _Search using metadata name_ option is always respected as this service fully support scraping based on the short MAME names. -Apart from this, hopefully the scraping process should be self-explanatory once you try it in ES-DE. +Apart from this, hopefully the scraping process should be self-explanatory. ### Manually copying game media files -If you already have a library of game media (images and videos) you can manually copy it into ES-DE. +If you already have a library of game media (images and videos) you can manually copy these files into ES-DE. -The default directory is `~/.emulationstation/downloaded_media//` +The default media directory is `~/.emulationstation/downloaded_media//` For example on Unix: ``` @@ -662,7 +665,7 @@ The media directories per game system are: * screenshots * videos -**Note:** The miximages are generated by ES-DE. Normally that takes place automatically when scraping, but in this example of manually copying existing media files, the miximage offline generator should be used instead. This tool can generate the miximages for the complete game collection in one go. How that works is explained elsewhere in this guide. +The miximages are generated by ES-DE. Normally that takes place automatically when scraping, but in this example of manually copying existing media files, the miximage offline generator should be used instead. This tool can generate the miximages for the complete game collection in one go. How that works is explained elsewhere in this guide. The media files must correspond exactly to the game files. For example the following game: @@ -677,7 +680,7 @@ Must have corresponding filenames for its media files in this fashion: ~/.emulationstation/downloaded_media/c64/videos/Multidisk/Last Ninja 2/Last Ninja 2.mp4 ``` -JPG and PNG file formats and file extensions are supported for images, and AVI, MKV, MOV, MP4 and WMV are supported for videos. +For images .jpg and .png file extensions are supported and for videos .avi, .mkv, .mov, .mp4 and .wmv are supported. Remember that on Unix filenames are case sensitive, and as well the file extensions must be in lower case, such as .png instead of .PNG or .Png or the file won't be found. @@ -686,7 +689,7 @@ It's possible to change the game media directory location from within ES-DE, for ## Main menu -This menu can be accessed from both the system view and gamelist views. It contains the scraper, the input configuration tool and the application settings. Settings are saved when navigating back from any menu screen, assuming at least one setting was changed. Pressing F4 to quit the application will also save any pending changes. +This menu can be accessed from both the system view and gamelist view. It contains the scraper, application settings and various tools such as the input configurator and the miximage generator. Settings are saved when navigating back from any menu screen, assuming at least one setting was changed. Pressing the application exit key (F4) will also save any pending changes. ![alt text](images/current/es-de_main_menu.png "ES-DE Main Menu") _The ES-DE main menu._ @@ -706,7 +709,7 @@ Scraper service selection, currently ScreenScraper.fr and TheGamesDB.net are sup **Scrape these games** -Criteria for what games to include in the scraping. It can be set to _All games, Favorite games, No metadata, No game image, No game video_ or _Folders only_. +Criteria for what games to include. It can be set to _All games, Favorite games, No metadata, No game image, No game video_ or _Folders only_. **Scrape these systems** @@ -718,7 +721,7 @@ Setup of ScreenScraper account. **Use this account for ScreenScraper** -Whether to use the account that has been setup here. If this is disabled, the username and password configured on this screen will be ignored during scraping. This can be useful if you have scraping issues and want to check whether it's related to your account or if it's a general problem. Note that screenscraper.fr does not seem to return a proper error message regarding incorrect username and password, but starting ES-DE with the --debug flag will indicate in the log file whether the username was included in the server response. +Whether to use the account that has been configured. If this is disabled, the username and password setup on this screen will be ignored during scraping. This can be useful if you have scraping issues and want to check whether it's related to your account or if it's a general problem. Note that screenscraper.fr does not seem to return a proper error message regarding incorrect username and password, but starting ES-DE with the --debug flag will indicate in the log file whether the username was included in the server response. **ScreenScraper username** @@ -726,15 +729,15 @@ Username as registered on screenscraper.fr. **ScreenScraper password** -The password as registered on screenscraper.fr. Note that the password is masked using asterisks on this screen, and the password input field will be blank when attempting to update an existing password. Entering a new password will of course work, and it will be saved accordingly. Be aware though that the es_settings.xml file contains the password in clear text. +The password as registered on screenscraper.fr. This is masked using asterisks on the screen, and the password input field will be blank when attempting to update an existing password. This is by design and not a bug. Be aware that the es_settings.xml file contains the password in clear text. #### Content settings -Describes the content types to include in the scraping. Most users will probably not need to adjust so many of these. +Describes the content types to include in the scraping. **Scrape game names** -Whether to scrape the names of the games. This does not affect the actual files on the filesystem and is only used for viewing and sorting purposes. The downloaded media files are also matched against the physical game files on the filesystem, and not against this metadata. See the comments under _Overwrite files and data_ below to understand some additional implications regarding the game names. +Whether to scrape the names of the games. This does not affect the actual files on the filesystem and the game name is primarily used for appearance and sorting purposes. The downloaded media files are matched against the physical game files on the filesystem, and not against this metadata. See the comments under _Overwrite files and data_ below to understand some additional implications regarding game names. **Scrape ratings** _(ScreenScraper only)_ @@ -762,15 +765,15 @@ Logotype for the game. **Scrape 3D box images** _(ScreenScraper only)_ -These images are primarily used for generating miximages, but they can also be viewed directly from the media viewer. +These are primarily used for generating miximages. #### Miximage settings -These are the settings for the miximage generator, which can either be run from the scraper (single-game scraper or multi-scraper) or from the offline miximage generator. The miximages combines the screenshot, marquee and box images to make a composite picture that is displayed in the gamelist view. There are various settings for the generator. +These are the settings for the miximage generator, which can either be run from the scraper (single-game scraper or multi-scraper) or from the offline generator. The miximage combines the screenshot, marquee and box images to make a composite picture that is displayed in the gamelist view. There are various settings for the generator. **Miximage resolution** -It's possible to select betweeen 1280x960, 1920x1440 and 640x480 resolutions for the generated miximages. It's normally recommended to use the default option 1280x960 which gives good image quality without slowing down ES-DE too much. But for very weak machines, 640x480 may be a better option. However 1920x1440 is normally not recommended as it brings little quality improvements over 1280x960 and makes the gamelist browsing feel less responsive. +It's possible to select betweeen the 1280x960, 1920x1440 and 640x480 resolutions for the generated miximages. It's normally recommended to use the default option 1280x960 which gives good image quality without slowing down ES-DE too much. But for very weak machines, 640x480 may be a better option. The 1920x1440 resolution is normally not recommended as it brings little quality improvements over 1280x960 and slows down the gamelist browsing. **Screenshot scaling method** @@ -810,7 +813,7 @@ This is not a setting, but instead a GUI to generate miximages offline without g #### Other settings -Various scraping settings. Most users will probably not need to adjust so many of these. +Various scraping settings. **Region** _(ScreenScraper only)_ @@ -818,7 +821,7 @@ The region to scrape for. This affects game names, game media and release dates. **Preferred language** _(ScreenScraper only)_ -Multiple languages are supported by ScreenScraper, and this affects translations of game genres and game descriptions. As the option name implies this is the preferred language only as not all games have had their metadata translated. Unfortunately some less used languages have quite few games translated to them but hopefully this will improve over time as there's an ongoing community effort to make more translations. If the preferred language is not available for a game, ES-DE will fall back to scraping the English metadata. +Multiple languages are supported by ScreenScraper, and this affects translations of game genres and game descriptions. As the option name implies this is the preferred language only as not all games have had their metadata translated. Unfortunately some less used languages have quite few games translated, but hopefully this will improve over time as there's an ongoing community effort to make more translations. If the preferred language is not available for a game, ES-DE will fall back to using the English metadata. **Overwrite files and data** @@ -826,17 +829,15 @@ Affects both overwriting of metadata as well as actual game media files on the f **Halt on invalid media files** -With this setting enabled, if any media files returned by the scraper seem to be invalid, the scraping is halted and an error message is presented where it's possible to retry or cancel the scraping of the specific game. In the case of multi-scraping it's also possible to skip the game and proceed to the next one in the queue. With this setting disabled, all media files will always be accepted and saved to disk. As of ES-DE v1.0 the file verification is crude as it's just checking if the file is less than 350 bytes in size which should indicate a server error response rather than a real media file. In some exceedingly rare situations, proper media files may be smaller than 350 bytes, and for those rare instances, simply disabling this setting temporarily allows these files to be scraped. Future versions of ES-DE will implement proper CRC/checksum verifications for ScreenScraper and possibly media file integrity checks for TheGamesDB (as this scraper service does not provide file checksums). +With this setting enabled, if any media files returned by the scraper seem to be invalid, the scraping is halted and an error message is presented where it's possible to retry or cancel the scraping of the specific game. In the case of multi-scraping it's also possible to skip the game and proceed to the next one in the queue. With this setting disabled, all media files will always be accepted and saved to disk. As of ES-DE v1.1 the file verification is quite basic and future versions will improve on this by using file checksums and other file integrity checks. **Search using metadata names** -By default ES-DE will perform scraper searches based on the game name that has been manually set in the metadata editor, or that has been previously scraped. If you prefer to search using the physical name regardless of such data being available, then turn off this option. - -Note that when using TheGamesDB as scraper service for arcade games (MAME/Neo Geo), the short MAME name will always be expanded to the full game name as this scraper does not properly support searches using MAME names. Also note that you need to save the game name in the metadata editor before you can use it for scraping. +By default ES-DE will perform scraper searches based on the game name that has been manually set in the metadata editor, or that has been previously scraped. If you prefer to search using the physical file name regardless of such data being available, then disable this option. Note that when using TheGamesDB as scraper service for arcade games (MAME and Neo Geo), the short MAME name will always be expanded to the full game name as this scraper service does not support searches using short MAME names. **Interactive mode** _(Multi-scraper only)_ -If turned off, the scraping will be fully automatic and will not stop on multiple results or on missing games. +If turned off, the scraping will be fully automatic and will not stop on multiple results or when no matching game was found. **Auto-accept single game matches** _(Multi-scraper only)_ @@ -844,7 +845,7 @@ Used in conjunction with interactive mode, to not having to confirm searches whe **Respect per-file scraper exclusions** _(Multi-scraper only)_ -It's possible to set a flag per game file or directory to indicate that it should be excluded from the multi-scraper. This setting makes it possible to override that restriction and scrape all entries anyway. +It's possible to set a flag per game file or folder using the metadata editor to indicate that it should be excluded from the multi-scraper. This setting makes it possible to override that restriction and scrape all entries anyway. **Exclude folders recursively** _(Multi-scraper only)_ @@ -854,17 +855,17 @@ If this setting is enabled and a folder has its flag set to be excluded from the Enabling this option causes folders themselves to be included by the scraper. This is useful for DOS games or any multi-disk games where there is a folder for each individual game. -**Auto-retry on peer verification errors** +**Auto-retry on peer verification errors** _(ScreenScraper only)_ ScreenScraper sometimes has issues with its TLS certificates which causes searches to randomly fail. It's normally resolved within a few days, but in the meanwhile activating this setting will have the scraper automatically make up to eight additional attempts when this error occurs. That is normally enough to complete the search, but if not, just press 'Retry' in the error dialog and ES-DE will try eight more times. This setting applies to both the single-game scraper and the multi-scraper. The following error notification dialog and corresponding es_log.txt entry is displayed when this error occurs: "Error downloading thumbnail: SSL peer certificate or SSH remote key was not OK". ### UI settings -Various settings that affects the user interface. +Various settings that affect the user interface. **Gamelist on startup** -If set to _None_, the system view will be showed. Any other value will jump to that game system automatically on startup. +If set to _None_, the system view will be displayed. Any other value will jump to that game system automatically on startup. **Gamelist view style** @@ -872,7 +873,7 @@ Sets the view style to _Automatic, Basic, Detailed, Video_ or _Grid_. See the de **Transition style** -Graphical transition effect, either _Slide, Fade_ or _Instant_. +Transition animation when navigating between the views. Can be set to _Slide, Fade_ or _Instant_. **Theme set** @@ -904,7 +905,7 @@ With this option enabled, there are black pillarboxes (and to a lesser extent le **Render scanlines for gamelist videos** _(OpenGL renderer only)_ -Whether to use a shader to render scanlines for videos in the gamelist view. The effect is usually pretty subtle as the video is normally renderered in a limited size in the GUI, and the scanlines are sized relative to the video window size. +Whether to use a shader to render scanlines for videos in the gamelist view. The effect is usually pretty subtle as the video is normally renderered in a limited size in the GUI and the scanlines are sized relative to the video window size. **Sort folders on top of gamelists** @@ -916,7 +917,7 @@ Whether to sort your favorite games above your other games in the gamelists. **Add star markings to favorite games** -With this setting enabled, there is a star symbol added at the beginning of the game name in the gamelist views. It's strongly recommended to keep this setting enabled if the option to sort favorite games above non-favorites has been enabled. If not, favorite games would be sorted on top of the gamelist with no visual indication that they are favorites, which would be very confusing. +With this setting enabled, there is a star symbol added at the beginning of the game name in the gamelist views. It's strongly recommended to keep this setting enabled if the option to sort favorite games above non-favorites has been enabled. If not, favorite games would be sorted on top of the gamelist with no visual indication that they are favorites, which would be quite confusing. **Use plain ASCII for special gamelist characters** @@ -924,15 +925,15 @@ There are some special characters in ES-DE such as the favorites star, the folde **Enable quick list scrolling overlay** -With this option enabled, there will be an overlay displayed when scrolling the gamelists quickly, i.e. when holding down the _Up_, _Down_, _Left shoulder_ or _Right shoulder_ buttons for some time. The overlay will darken the background slightly and display the first two characters of the game name. If the game is a favorite and the setting to sort favorites above non-favorites has been enabled, a star will be shown instead. +With this option enabled, there will be an overlay displayed when scrolling the gamelists quickly, i.e. when holding down the _Up_, _Down_, _Left shoulder_ or _Right shoulder_ buttons for some time. The overlay will darken the background slightly and display the first two characters of the game names. If the game is a favorite and the setting to sort favorites above non-favorites has been enabled, a star will be shown instead. **Enable toggle favorites button** -This setting enables the _Y_ button for quickly toggling a game as favorite. Although this may be convenient at times, it's also quite easy to accidentally remove a favorite tagging of a game when using the application more casually. As such it could sometimes make sense to disable this functionality. It's of course still possible to mark a game as favorite using the metadata editor when this setting is disabled. For additional restrictions, the application can be set to Kid or Kiosk mode as is explained [elsewhere](USERGUIDE-DEV.md#ui-modes) in this guide. Note that this setting does not affect the functionality to use the _Y_ button to add games to custom collections. +This setting enables the _Y_ button for quickly toggling a game as favorite. Although this may be convenient at times, it's also quite easy to accidentally remove a favorite tagging of a game when using the application more casually. As such it could sometimes make sense to disable this functionality. It's of course still possible to mark a game as favorite using the metadata editor when this setting is disabled. The option does not affect the use of the _Y_ button to add or remove games when editing custom collections. **Enable random system or game button** -This enables or disables the functionality to jump to a random system or game. It's mapped to the thumbstick click button, either the left or right thumbstick will work. The help prompts will also visually indicate whether this option is enabled or not. +This enables or disables the ability to jump to a random system or game. It's mapped to the thumbstick click button, either the left or right thumbstick will work. The help prompts will also visually indicate whether this option is enabled or not. **Enable gamelist filters** @@ -940,11 +941,11 @@ Activating or deactivating the ability to filter your gamelists. This can normal **Enable quick system select** -If enabled, it will be possible to jump between gamelists using the _Left_ and _Right_ buttons without having to first go back to the system view. +If enabled, it's possible to navigate between gamelists using the _Left_ and _Right_ buttons without having to first go back to the System view. **Display on-screen help** -Activating or deactivating the built-in help system that provides contextual information regarding button usage. +Activates or deactivates the built-in help system that provides contextual information regarding button usage. **Play videos immediately (override theme)** @@ -960,7 +961,7 @@ With this option enabled, the video will continue to run when viewing the images **Stretch videos to screen resolution** -This will fill the entire screen surface but will possibly break the aspect ratio of the video. +This will fill the entire screen surface but will probably break the aspect ratio of the video. **Render scanlines for videos** _(OpenGL renderer only)_ @@ -980,7 +981,7 @@ Settings for the built-in screensaver. **Start screensaver after (minutes)** -After how many minutes to start the screensaver. If set to 0 minutes, the timer will be deactivated and the screensaver will never start automatically. It's however still possible to start the screensaver manually in this case, assuming the _Enable screensaver controls_ setting is enabled. Note that while any menu is open, the screensaver will not start regardless of how this timer setting is configured. +After how many minutes to start the screensaver. If set to 0 minutes, the timer will be deactivated and the screensaver will never start automatically. It's however still possible to start the screensaver manually in this case, assuming the _Enable screensaver controls_ setting is enabled. While any menu is open, the screensaver will not start regardless of how this timer setting is configured. **Screensaver type** @@ -988,7 +989,7 @@ The screensaven type to use; _Dim_, _Black_, _Slideshow_ or _Video_. **Enable screensaver controls** -This includes the ability to start the screensaver manually using the _Back_ button from the system view, but also while the screensaver is running to jump to a new random game using the _Left_ and _Right_ buttons, to launch the game currently shown using the _A_ button and to jump to the game in its gamelist using the _Y_ button. If this setting is disabled, any key or button press will stop the screensaver. +This enables the ability to start the screensaver manually using the _Back_ button from the system view, but also while the screensaver is running to jump to a new random game using the _Left_ and _Right_ buttons, to launch the game currently shown using the _A_ button and to jump to the game in its gamelist using the _Y_ button. If this setting is disabled, any key or button press will stop the screensaver. #### Slideshow screensaver settings @@ -1000,11 +1001,11 @@ For how long to display images before changing to the next game. Allowed range i **Stretch images to screen resolution** -This will fill the entire screen surface but will possibly break the aspect ratio of the image. +This will fill the entire screen surface but will probably break the aspect ratio of the image. **Display game info overlay** -This will display an overlay in the upper left corner, showing the game name and the game system name. A star following the game name indicates that it's a favorite. +This will display an overlay in the upper left corner, showing the game name and the game system name. A star following the game name indicates that it's flagged as a favorite. **Render scanlines** _(OpenGL renderer only)_ @@ -1020,7 +1021,7 @@ Whether to search the custom image directory recursively. **Custom image directory** -The directory for the custom images. The tilde `~` symbol can be used which will expand to the ES-DE home directory. It's also possible to use the %ESPATH% and %ROMPATH% variables which will set the directory relative to the ES-DE binary directory or the ROM directory. +The directory for the custom images. The tilde `~` symbol can be used which will expand to the user home directory. It's also possible to use the %ESPATH% and %ROMPATH% variables which will set the directory relative to the ES-DE binary directory or the ROMs directory. #### Video screensaver settings @@ -1028,15 +1029,15 @@ Options specific to the video screensaver. **Swap videos after (seconds)** -For how long to play videos before changing to the next game. Allowed range is between 0 and 120 seconds in 2-second increments. If set to 0 (which is the default setting), the next game will be selected after the entire video has finished playing. +For how long to play videos before changing to the next game. Allowed range is between 0 and 120 seconds in 2-second increments. If set to 0 (which is the default value), the next game will be selected after the entire video has finished playing. **Stretch videos to screen resolution** -This will fill the entire screen surface but will possibly break the aspect ratio of the video. +This will fill the entire screen surface but will probably break the aspect ratio of the video. **Display game info overlay** -This will display an overlay in the upper left corner, showing the game name and the game system name. A star following the game name indicates that it's a favorite. +This will display an overlay in the upper left corner, showing the game name and the game system name. A star following the game name indicates that it's flagged as a favorite. **Render scanlines** _(OpenGL renderer only)_ @@ -1049,11 +1050,11 @@ Whether to use a shader to render a slight horizontal blur which somewhat simula ### Sound settings -General sound settings. +Various sound settings. **System volume** _(Linux and Windows only)_ -As the name implies, this sets the overall system volume and not the volume specifically for ES-DE. Note that the volume change is applied only after leaving the sound settings menu. +As the name implies, this sets the overall system volume and not the volume specifically for ES-DE. The volume change is applied when leaving the sound settings menu and not immediately when moving the slider. **Navigation sounds volume** @@ -1061,11 +1062,11 @@ Sets the volume for the navigation sounds. **Video player volume** -Sets the volume for the video player. This applies to the gamelist views, the media viewer and the video screensaver. +Sets the volume for the video player. This applies to the gamelist view, the media viewer and the video screensaver. **Play audio for videos in the gamelist view** -With this turned off, audio won't play for game videos in the gamelists. +With this turned off, audio won't play for videos in the gamelists. **Play audio for media viewer videos** @@ -1086,36 +1087,36 @@ Settings related to the input devices, i.e. the keyboard and controllers. **Controller type** -This setting gives the ability to choose between the controller types Xbox, Xbox 360, PlayStation 4, PlayStation 5 and SNES (Super Nintendo). Doing so changes the help icons and help text as well as the icons and text for the input device configuration tool. The setting is only cosmetic and does not change the controller behavior or the controller button mappings. +This setting gives the ability to choose between the controller types _Xbox, Xbox 360, PlayStation 4, PlayStation 5_ and _SNES_ (Super Nintendo). Doing so changes the help icons as well as the icons and text for the input device configurator. The setting is only cosmetic and does not change the controller behavior or the controller button mappings. **Only accept input from first controller** -If enabling this option, only the first controller detected during startup will send its input to ES-DE. This is a good way to limit potential chaos with mutliple users fighting over which games to start. Note though that disconnecting and reconnecting controllers while ES-DE is running may change what is considered the first controller. This setting does not affect the emulators in any way, it's only applied to ES-DE itself. +If enabling this option, only the first controller detected during startup will send its input to ES-DE. This is a good way to limit potential chaos with mutliple users fighting over which games to start. Disconnecting and reconnecting controllers while ES-DE is running may change what is considered the first controller. This setting does not affect the emulators in any way, it's only applied to ES-DE. **Configure keyboard and controllers** -This tool allows the configuration of button mappings for the keyboard and any connected controllers, as explained [here](USERGUIDE.md#input-device-configuration). Normally this is not required as ES-DE automatically configures the input devices, but this tool allows overriding the default button mappings which may be useful in some instances, primarily for the keyboard configuration. Be aware that custom button mappings will not change the help icons or text. +This tool provides configuration of button mappings for the keyboard and controllers, as explained [here](USERGUIDE.md#input-device-configuration). Normally this is not required as ES-DE automatically configures all input devices, but button customizations may be useful in some special situations. Be aware that custom button mappings will not change the help icons or help text. ### Game collection settings -Handles collections, which are built using the games already present in your game systems. See the [collections](USERGUIDE-DEV.md#game-collections) section below in this document for more information. +Handles collections, which are assembled using games present in the game systems. See the [collections](USERGUIDE-DEV.md#game-collections) section below in this document for more information. -**Finish editing _'COLLECTION NAME'_ collection** _(Entry only displayed when editing a custom collection)_ +**Finish editing _'COLLECTION NAME'_ collection** _(Entry only visible when editing a custom collection)_ Self explanatory. **Automatic game collections** -This opens a screen that lets you enable or disable the automatic game collections _All games, Favorites_ and _Last played_. +This lets you enable or disable the automatic game collections _All games, Favorites_ and _Last played_. **Custom game collections** This lets you enable or disable your own custom game collections. -**Create new custom collection from theme** _(Entry only displayed if the ability is provided by the theme set)_ +**Create new custom collection from theme** _(Entry only visible if the ability is provided by the theme set)_ -If the theme set in use provides themes for custom collections, then this entry can be selected here. For example, there could be themes for _"Fighting games"_ or _"Driving games"_ etc. The default rbsimple-DE theme set does not provide such themes for custom collections and in general it's not recommended to use this approach, as is explained [later](USERGUIDE-DEV.md#custom-collections) in this guide. +If the theme set in use provides themes for custom collections, then this entry can be selected. For example, there could be themes for _"Fighting games"_ or _"Driving games"_ etc. The default rbsimple-DE theme set does not provide such themes for custom collections and in general it's not recommended to use this approach, as is explained [later](USERGUIDE-DEV.md#custom-collections) in this guide. **Create new custom collection** @@ -1123,7 +1124,7 @@ This lets you create a completely custom collection with a name of your choice. **Delete custom collection** -This permanently deletes a custom collection, including its configuration file on the file system. A list of available collections is shown, and a confirmation dialog is displayed before committing the actual deletion. Only one collection at a time can be deleted. +This permanently deletes a custom collection, including its configuration file on the file system. A list of available collections is shown, and a confirmation dialog is displayed before performing the actual deletion. Only one collection at a time can be deleted. **Sort favorites on top for custom collections** @@ -1131,11 +1132,11 @@ Whether to sort your favorite games above your other games. This is disabled by **Display star markings for custom collections** -With this option enabled, there is a star marking added to each favorite game name. It works identically to the setting _Add star markings to favorite games_ under the _UI settings_ but is applied specifically to custom collections. It's disabled by default. +With this option enabled, there is a star marking added to each favorite game name. It works identically to the setting _Add star markings to favorite games_ under the _UI settings_ menu but is applied specifically to custom collections. It's disabled by default. **Group unthemed custom collections** -With this enabled, if you have created custom collections and there is no theme support for the names you've selected, the collections will be grouped in a general collection which is correctly themed. It's strongly recommended to keep this option enabled as otherwise your collections would be completely unthemed which doesn't make much sense. This option is provided mostly for testing and theme development purposes. +With this enabled, if you have created custom collections and there is no theme support for the names you've selected, the collections will be grouped in a general collection system which is correctly themed. It's strongly recommended to keep this option enabled as otherwise your collections would be completely unthemed which doesn't make much sense. This option is provided mostly for testing and theme development purposes. **Show system names in collections** @@ -1147,11 +1148,11 @@ These are mostly technical settings. **VRAM limit** -The amount of video RAM to use for the application. Defaults to 256 MiB which works fine most of the time when running at 1080p resolution and with a moderate amount of game systems. If running at 4K resolution and with lots of game systems enabled, it's recommended to increase this number to 512 MiB or so to avoid stuttering transition animations caused by unloading and loading of textures from the cache. Enabling the GPU statistics overlay gives some indications regarding the amount of texture memory currently used by ES-DE, which is helpful to determine a reasonable value for this option. The allowed range for the settings is 80 to 1024 MiB. If you try to set it lower or higher than this by passing such values as command line parameters or by editing the es_settings.xml file manually, ES-DE will log a warning and automatically adjust the value within the allowable range. +The amount of video RAM to use for the application. Defaults to 256 MiB (80 MiB on the Raspberry Pi) which works fine most of the time when running at 1080p resolution and with a moderate amount of game systems. If running at 4K resolution or similar and with lots of game systems enabled, it's recommended to increase this number to 512 MiB or possibly more to avoid stuttering transition animations caused by unloading and loading of textures from the cache. Enabling the GPU statistics overlay gives some indications regarding the amount of texture memory currently used by ES-DE, which is helpful to determine a reasonable value for this option. The allowed range for the settings is 80 to 1024 MiB. If you try to set it lower or higher than this by passing such values as command line parameters or by editing the es_settings.xml file manually, ES-DE will log a warning and automatically adjust the value within the allowable range. **Display/monitor index (requires restart)** -This option sets the display to use for ES-DE for multi-monitor setups. The possible values are the monitor index numbers 1, 2, 3 or 4. If a value is set here for a display that does not actually exist, then ES-DE will set it to 1 upon startup. Index 1 is the primary display of the computer. It's also possible to override the setting by passing the --display command line argument. Doing so will also overwrite the display index setting in es_settings.xml. Be aware that the Display/monitor index option only changes the display used by ES-DE; the emulators need to be configured separately (which can easily be done globally if using RetroArch). +This option sets the display to use for ES-DE for multi-monitor setups. The possible values are the monitor index numbers 1, 2, 3 or 4. If a value is set here for a display that does not actually exist, then ES-DE will set it to 1 upon startup. Index 1 is the primary display for the computer. It's also possible to override the setting by passing the --display command line argument. Doing so will also overwrite the display index setting in es_settings.xml. The Display/monitor index option only changes the display used by ES-DE; the emulators need to be configured separately (which can easily be done globally if using RetroArch). **Fullscreen mode (requires restart)** _(Unix only)_ @@ -1159,11 +1160,11 @@ This gives you a choice between _Normal_ and _Borderless_ modes. With the border **Video player** _(Only on some builds and operating systems)_ -This gives the choice between FFmpeg and VLC, which is applied to the gamelist videos, the media viewer and the video screensaver. The VLC video player is not included by default and is omitted on some builds. If this option is not available in the menu, it means that the FFmpeg video player is in use. +This gives the choice between FFmpeg and VLC, which is applied to the gamelist videos, the media viewer and the video screensaver. The VLC video player is not included on some builds. If this option is not available in the menu, it means that the FFmpeg video player is in use. **When to save game metadata** -The metadata for a game is updated by scraping and by manual editing (using the metadata editor), but also when launching it as this updates the _Times played_ counter and the _Last played_ date. This setting enables you to define when to write such metadata changes to the gamelist.xml files. Setting the option to _Never_ will disable writing to these files altogether, except for some special conditions such as when a game is manually deleted using the metadata editor, or when scraping using the multi-scraper (the multi-scraper will always save any updates immediately to the gamelist.xml files). In theory _On exit_ will give some performance gains, but it's normally recommended to leave the setting at its default value which is _Always_. Note that with the settings set to _Never_, any updates such as the _Last played_ date will still be shown on screen, but during the next application startup, any values previously saved to the gamelist.xml files will be read in again. As well, when changing this setting to _Always_ from either of the two other options, any pending changes will be immediately written to the gamelist.xml files. +The metadata for a game is updated by scraping or by manual editing using the metadata editor, but also when launching it as this updates the _Times played_ counter and the _Last played_ timestamp. This setting enables you to define when to write such metadata changes to the gamelist.xml files. Setting the option to _Never_ will disable writing to these files altogether, except for some special conditions such as when a game is manually deleted using the metadata editor, or when scraping using the multi-scraper (the multi-scraper will always save any updates immediately to the gamelist.xml files). In theory _On exit_ will give some performance gains, but it's normally recommended to leave the setting at its default value which is _Always_. Note that with the settings set to _Never_, any updates such as the _Last played_ date will still be shown on screen, but during the next application startup, any values previously saved to the gamelist.xml files will be read in again. As well, when changing this setting to _Always_ from either of the two other options, any pending changes will be immediately written to the gamelist.xml files. **Game media directory** @@ -1171,7 +1172,7 @@ This setting defines the directory for the game media, i.e. game images and vide **Hide taskbar (requires restart)** _(Windows only)_ -With this setting enabled, the taskbar will be hidden when launching ES-DE, and it will be restored when the application exits. This can make for a more seamless experience as the taskbar could otherwise flash by briefly when launching games and when returning from games. It could potentially cause some issues on some Windows installations though, so the option is disabled by default. +With this setting enabled, the taskbar will be hidden when launching ES-DE, and it will be restored when the application exits. This can make for a more seamless experience as the taskbar could otherwise flash by briefly when launching and when returning from games. But it can potentially cause issues on some Windows installations so it's disabled by default. **Run in background (while game is launched)** @@ -1179,11 +1180,11 @@ Enabling this option makes ES-DE continue to run while a game is launched. This **AMD and Intel GPU game launch workaround** _(Windows only)_ -There is an issue with launching games on some Windows computers, seemingly on those with AMD and Intel GPUs. The emulator will start and work correctly, but the screen will be blank. Enabling this option is a workaround for that problem, with the drawback that the screen will become white instead of black when the emulator is loading. This option is enabled by default, so experiment with disabling it for a slightly better user experience. If you're using an Nvidia GPU, chances are high that it will then work fine. An alternative workaround is to enable the _Run in background (while game is launched)_ option described above, so test which gives the best result. The two options can however not be enabled at the same time. Hopefully this whole game launching issue can be resolved completely in a future ES-DE release. +There is an issue with launching games on some Windows computers, seemingly on those with AMD and Intel GPUs. The emulator will start and work correctly, but the screen will be blank. Enabling this option is a workaround for that problem, with the drawback that the screen will become white instead of black when the emulator is loading. This option is enabled by default, so experiment with disabling it for a slightly better experience. If you're using an Nvidia GPU, chances are high that it will then work fine. An alternative workaround is to enable the _Run in background (while game is launched)_ option described above, so test which gives the best result. The two options can however not be enabled at the same time. Hopefully this whole game launching issue can be resolved completely in a future ES-DE release. **Upscale video frame rate to 60 FPS (FFmpeg)** -With this option enabled, videos with lower frame rates than 60 FPS, such as 24 and 30 will get upscaled to 60 FPS. This results in slightly smoother playback for some videos. There is a small performance hit from this option, so on slower machines it may be necessary to disable it for fluent video playback. This setting has no effect when using the VLC video player. If the VLC video player is not included in the ES-DE build, the "(FFmpeg)" text is omitted from the setting name. +With this option enabled, videos with lower frame rates than 60 FPS, such as 24 and 30 will get upscaled to 60 FPS. This results in slightly smoother playback for some videos. There is a small performance hit from this option, so on weaker machines it may be necessary to disable it for fluent video playback. This setting has no effect when using the VLC video player. If the VLC video player is not included in the ES-DE build, the "(FFmpeg)" text is omitted from the setting name. **Per game launch command override** @@ -1191,15 +1192,15 @@ If enabled, you can override the launch command defined in es_systems.xml on a p **Show hidden files and folders (requires restart)** -If this option is disabled, hidden files and folders within the ROM directory tree are excluded from ES-DE. On Unix this means those starting with a dot, and on Windows it's those set as hidden by using an NTFS attribute. This setting is probably mostly useful for special situations and is not to be confused with the next option which hides files based on metadata configuration within ES-DE. +If this option is disabled, hidden files and folders within the ROMs directory tree are excluded from ES-DE. On Unix this means those starting with a dot, and on Windows it's those set as hidden by using an NTFS attribute. This setting is only intended for special situations and is not to be confused with the next option which hides files based on metadata configuration within ES-DE. **Show hidden games (requires restart)** -You can mark games as hidden in the metadata editor, which is useful for instance for DOS games where you may not want to see some batch files and executables inside ES-DE, or for multi-disk games where you may only want to show the .m3u playlists and not the individual game files, as is discussed [here](USERGUIDE-DEV.md#multiple-gamefiles-installation). By disabling this option these files will not be processed at all when ES-dE starts up. If you enable the setting you will see the files, but their name entries will be almost transparent in the gamelist view to visually indicate that they are hidden. +You can mark games as hidden in the metadata editor, which is useful for instance for DOS games where you may not want to see some batch files and executables inside ES-DE, or for multi-disk games where you may only want to show the .m3u playlists and not the individual game files. By disabling this option these files will not be processed at all when ES-DE starts up. If you enable the option you will see the files, but their name entries will be almost transparent in the gamelist view to visually indicate that they are hidden. **Enable custom event scripts** -It's possible to trigger custom scripts for a number of actions in ES-DE, as is discussed [below](USERGUIDE-DEV.md#custom-event-scripts), and this setting decides whether this functionality is enabled. It's recommended to leave it at its default off value unless you need it as it generates unnecessary debug logging. +It's possible to trigger custom scripts for a number of actions in ES-DE, as is discussed [below](USERGUIDE-DEV.md#custom-event-scripts), and this setting decides whether this functionality is enabled. It's recommended to leave it at its default off value unless you need it as it otherwise generates unnecessary debug logging. **Only show ROMs from gamelist.xml files** @@ -1207,19 +1208,19 @@ If enabled, only ROMs that have metadata saved to the gamelist.xml files will be **Disable desktop composition (requires restart)** _(Unix only)_ -The window manager desktop composition can adversely affect the framerate of ES-DE, especially on weaker graphic cards and when running in 4K resolution. As such the desktop compositor is disabled by default, although the window manager has to be configured to allow applications to do this for the option to have any effect. Note that this setting can cause problems with some graphic drivers (notably the Nvidia proprietary drivers) so if you see strange flickering and similar after quitting ES-DE, then disable the setting. In case of such issues, make sure that the emulator is also not blocking the composition (e.g. RetroArch has a corresponding option). +The window manager desktop composition can adversely affect the framerate of ES-DE, especially on weaker graphic cards and when running at higher resolution. As such the desktop compositor is disabled by default, although the window manager has to be configured to allow applications to do this for the option to have any effect. Note that this setting can cause problems with some graphic drivers (notably the Nvidia proprietary drivers) so if you see strange flickering and similar after quitting ES-DE, then disable the setting. In case of such issues, make sure that the emulator is also not blocking the composition (e.g. RetroArch has a corresponding option). **Display GPU statistics overlay** -Displays the framerate and VRAM statistics as an overlay. You normally never need to use this unless you're debugging a performance problem or similar. **Note:** As of ES-DE version 1.0 the VRAM usage statistics is not accurate. This will be addressed in a future version. +Displays the framerate and VRAM statistics as an overlay. You normally never need to use this unless you're debugging a performance problem or similar. **Note:** As of ES-DE v1.1 the VRAM usage statistics is not accurate. This will be addressed in a future version. **Enable menu in kid mode** -Enabling or disabling the menu when the UI mode is set to Kid. Mostly intended for testing purposes as it's normally not recommended to enable the menu in this restricted mode. +Enabling or disabling the menu when the UI mode is set to Kid. Mostly intended for testing purposes as it's not recommended to enable the menu in this restricted mode. **Show quit menu (reboot and power off entries)** _(Unix and Windows only)_ -With this setting enabled, there is a Quit menu shown as the last entry on the main menu which provides options to quit ES-DE, to reboot the computer or to power off the computer. With this setting disabled, there will simply be an entry shown to quit the application instead of the complete quit menu. +With this setting enabled, there is a Quit menu shown as the last entry on the main menu which provides options to quit ES-DE, to reboot the computer or to power off the computer. With this setting disabled, there will simply be an entry to quit the application instead of the complete quit menu. ### Quit @@ -1240,9 +1241,9 @@ Self explanatory. ## Game options menu -This menu is opened from the gamelist views, and can't be accessed from the system view. The menu changes slightly depending on the context, for example if a game file or a folder is selected, or whether the current system is a collection or a normal game platform. +This menu is opened from the gamelist view, and can't be accessed from the system view. The menu changes slightly depending on the context, for example if a game file or a folder is selected, or whether the current system is a collection or a normal game platform. -You open the menu using the **Back** button, and by pressing **B** or selecting the **Apply** button any settings such as letter jumping using the quick selector or sorting changes are applied. If you instead press the Back button again, the menu is closed without applying any changes. +You open the menu using the **Back** button, and by pressing **B** or selecting the **Apply** button any settings such as letter jumping using the quick selector or sorting changes are applied. If you instead press the Back button again or select the **Cancel** button, the menu is closed without applying any changes. ![alt text](images/current/es-de_game_options_menu.png "ES-DE Game Options Menu") _The game options menu as laid out when opening it from within a custom collection, which adds the menu entry to add or remove games from the collection._ @@ -1251,7 +1252,7 @@ Here's a summary of the menu entries: ### Jump to.. -This provides the ability to jump to a certain letter using a quick selector. If the setting to sort favorite games above non-favorites has been selected (it is enabled by default), then it's also possible to jump to the favorites games by choosing the star symbol. If there are only folders or only favorite games in a certain game list, these games and folders will be indexed as well, making it possible to jump betwen them using the quick selector. +This provides a quick selector for jumping to a certain letter. If the setting to sort favorite games above non-favorites has been enabled, then it's also possible to jump to the favorites games by choosing the star symbol. Similarly if there is a mix of folders and game files in the system and folders are configured to be sorted on top, then it's possible to jump to the folders using the folder icon. If there are only folders or only favorite games for the system, these games and folders will be indexed by their first letters and the star and folder symbols will be disabled in the quick selector. ### Sort games by @@ -1274,14 +1275,14 @@ The secondary sorting is always in ascending filename order. ### Filter gamelist -Choosing this entry opens a separate screen where it's possible to apply a filter to the gamelist. The filter is persistent throughout the program session, or until it's manually reset. The option to reset all filters is also shown on the same screen. +Choosing this entry opens a separate screen where it's possible to apply a filter to the gamelist. The filter is persistent throughout the program session, or until it's manually reset. The option to reset all filters is shown on the same screen. ![alt text](images/current/es-de_gamelist_filters.png "ES-DE Gamelist Filters") _The gamelist filter screen, accessed from the game options menu._ The following filters can be applied: -**Text Filter (Game name)** +**Text Filter (game name)** **Favorites** @@ -1322,7 +1323,7 @@ This opens the metadata editor for the currently selected game file or folder. ## Metadata editor -In the metadata editor, you can modify the metadata for a game, scrape for game info and media files, delete media files and gamelist entries, or delete the entire game. When manually modifying a value, it will change color from gray to blue, and if the scraper has changed a value, it will change to red. When leaving the metadata editor you will be asked whether you want to save any settings done manually or by the scraper. +In the metadata editor, you can modify the metadata, scrape for game info and media files, clear the entry which will delete all metadata and game media files, or delete the entire game which also removes its file on the filesystem. When manually modifying a value, it will change color from gray to blue, and if the scraper has changed a value, it will change to red. When leaving the metadata editor you will be asked whether you want to save any settings done manually or by the scraper. ![alt text](images/current/es-de_metadata_editor.png "ES-DE Metadata Editor") _The metadata editor._ @@ -1337,7 +1338,7 @@ This is the name that will be shown when browsing the gamelist. If no sortname h **Sortname** _(files only)_ -This entry makes it possible to change the sorting of a game without having to change its name. For instance it can be used to sort _Mille Miglia_ as _1000 Miglia_ or _The Punisher_ as _Punisher, The_. Be aware though that the _Jump to..._ quick selector on the game options menu will base its index on the first character of the sortname if it exists for a game, which could be slightly confusing in some instances when quick jumping in the gamelist. +This entry makes it possible to change the sorting of a game without having to change its name. For instance it can be used to sort _Mille Miglia_ as _1000 Miglia_ or _The Punisher_ as _Punisher, The_. Note that the _Jump to..._ quick selector on the game options menu will base its index on the first character of the sortname if it exists for a game, which could be slightly confusing in some instances when quick jumping in the gamelist. **Description** @@ -1365,11 +1366,11 @@ One or multiple genres for the game. **Players** -The amount of players the game supports. +The amount of players the game supports. This could be an absolute number such as 1 or 3, or it could be a range, such as 2-4. **Favorite** -A flag to indicate whether this is a favorite. Can also be set directly from the gamelist view by using the _Y_ button (unless this has been disabled in the main menu settings). +A flag to indicate whether this is a favorite game. This flag can also be set directly from the gamelist view by using the _Y_ button (assuming the _Enable toggle favorites buttom_ option is enabled). **Completed** @@ -1381,7 +1382,7 @@ A flag to mark whether the game is suitable for children. This will be applied a **Hidden** -A flag to indicate that the game is hidden. If the corresponding option has been set in the main menu, the game will not be shown. Useful for example for DOS games to hide batch scripts and unnecessary binaries or to hide the actual game files for multi-disk games. If a file or folder is flagged as hidden but the correponding option to hide hidden games has not been enabled, then the opacity of the text will be lowered significantly to make it clear that it's a hidden game. +A flag to indicate that the game is hidden. If the corresponding option has been set in the main menu, the game will not be shown. Useful for example for DOS games to hide batch scripts and unnecessary binaries or to hide the actual game files for multi-disk games. If a file or folder is flagged as hidden but the corresponding option to hide hidden games has not been enabled, then the opacity of the text will be lowered significantly to make it clear that it's a hidden entry. **Broken/not working** @@ -1393,7 +1394,7 @@ A flag to indicate whether the game should be excluded from being counted. If th **Exclude from multi-scraper** -Whether to exclude the file from the multi-scraper. This is quite useful in order to avoid scraping all the disks for multi-disk games for example. There is an option in the scraper settings to ignore this flag, but by default the scraper will respect it. Note that the manual single-file scraper will work regardless of whether this flag is set or not. +Whether to exclude the file from the multi-scraper. This is quite useful in order to avoid scraping all the disks for multi-disk games for example. There is an option in the scraper settings to ignore this flag, but by default the multi-scraper will respect it. **Hide metadata fields** @@ -1401,7 +1402,7 @@ This option will hide most metadata fields in the gamelist view. The intention i **Launch command** _(files only)_ -Here you can override the launch command for the game, for example to use a different emulator than the default one for the game system. Very useful for MAME/arcade games. +Here you can override the launch command for the game, for example to use a different emulator than the default one defined for the game system. Very useful for MAME/arcade games. **Times played** _(files only)_ @@ -1413,7 +1414,7 @@ For game files, there will be five buttons displayed on the bottom of the metada **Scrape** -Opens the single-game scraper, which is explained [above](USERGUIDE-DEV.md#single-game-scraper) in this guide. The _Y_ button can also be used as a shortcut to start the scraper without having to navigate to this button. +Opens the single-game scraper. The _Y_ button can also be used as a shortcut to start the scraper without having to navigate to this button. **Save** @@ -1425,7 +1426,7 @@ Cancels any changes and closes the window. **Clear** -This will remove any media files for the file or folder and also remove its entry from the gamelist.xml file. The actual game file or folder will however _not_ be deleted. A prompt will be shown asking for confirmation. +This will remove any media files for the file or folder and also remove its entry from the gamelist.xml file, effectively deleting all metadata. The actual game file or folder will however _not_ be deleted. A prompt will be shown asking for confirmation. **Delete** _(Files only)_ @@ -1437,7 +1438,7 @@ The game media viewer displays videos and images in fullscreen mode and is launc If a game video is available, this will be played automatically when launching the viewer. The _left_ and _right_ buttons are used to navigate betweeen the game media files. By default the video will continue to play when browsing the images, but this can be changed with a setting as described earlier in this document. -The other settings for the media viewer are similar to what is available for the screensavers; the audio can be enabled or disabled, the video can be stretched to fill the entire screen and scanlines and blur can be rendered on top of it. For screenshots, scanlines can be rendered. All these options are configurable using the main menu. +The other settings for the media viewer are similar to what is available for the screensavers; the audio can be enabled or disabled, the video can be stretched to fill the entire screen and scanlines and blur can be rendered on top of it. For screenshots, scanlines can be rendered. All these options are configurable via the _UI Settings_ menu. Pressing any other button than _left_ or _right_ closes the media viewer. @@ -1449,7 +1450,9 @@ Numerous options can be set for these screensavers, as detailed [here](USERGUIDE The Dim screensaver simply dims and desaturates the current view and Black will show a black screen. The Slideshow and Video screensavers are more interesting as they can display images and videos from your game collection. In addition to this, the Slideshow screensaver can be configured to only show images from a specified directory. -If the option **Enable screensaver controls** has been activated, you can manually toggle the screensaver from the system view by pressing the **Back** button. In addition to this, for the Slideshow and Video screensavers, the controls will allow you to jump to a new random image or video using the **Left** and **Right** buttons on your keyboard or controller. It's also possible to launch the game currently displayed using the **A** button, and the **Y** button will jump to the game in its gamelist without starting it. +If the option **Enable screensaver controls** has been activated, you can manually toggle the screensaver from the system view by pressing the **Back** button. In addition to this, for the Slideshow and Video screensavers, the controls will allow you to jump to a new random image or video by using the **Left** and **Right** buttons on your keyboard or controller. It's also possible to launch the game currently displayed using the **A** button, and the **Y** button will jump to the game in its gamelist without starting it. + +For the video and slideshow screensavers, an overlay can be enabled via the screensaver options that displays the game name and the game system as well as a star to indicate that the game is marked as a favorite. If the Video screensaver has been selected and there are no videos available, a fallback to the Dim screensaver will take place. The same is true for the Slideshow screensaver if no game images are available. @@ -1458,13 +1461,13 @@ _An example of what the video screensaver looks like._ ## Game collections -ES-DE provides two types of collections, **Automatic collections** and **Custom collections**, the latter being defined by the user. Collections are as the name implies only collections of games already present in your actual game systems, so they're basically grouping your games together into convenient views. As such the use of collections is entirely optional, but it is a very nice feature and it's worth some effort to setup. +ES-DE provides two types of collections, **Automatic collections** and **Custom collections**, the latter being defined by the user. Collections are as the name implies only collections of games already present in your actual game systems, so they're basically grouping of games into convenient views. As such the use of collections is entirely optional, but especially the custom collection support is a very nice feature which is worth some effort to setup. The numerous collection settings available are covered [here](USERGUIDE-DEV.md#game-collection-settings). ### Automatic collections -The automatic collections are named **All games**, **Favorites** and **Last played**. The 'All games' collection simply groups all your game systems into one big list, 'Favorites' combines all your games marked as favorites from all your game systems, and 'Last played' is a list of the 50 last games you have launched. +The automatic collections are named **All games**, **Favorites** and **Last played**. The _All games_ collection simply groups all your game systems into one big list, _Favorites_ combines all your games marked as favorites from all your game systems, and _Last played_ is a list of the 50 last games you have launched. These automatic collections can be individually enabled or disabled by going to the main menu, selecting **Game collection settings** and then **Automatic game collections**. @@ -1476,7 +1479,7 @@ These are collections that you create yourself. Examples could be grouping in ge If the theme set supports it, you can create a custom collection directly from a theme. However, rbsimple-DE does not provide such themes as it's believed that grouping them together in a dedicated Collections system is a more elegant solution. Especially since the theme set would need to ship with an almost endless amount of collection themes for whatever categories the users would like to use for their game collections. -So if you have enabled the option **Group unthemed custom collections** (it's enabled by default), any collections you add will show up in the special Collections system. Here you can access them just as you would access folders inside a regular gamelist. The amount of games per collection is shown in the description, and a random game is displayed each time you browse through the list (you can quick jump to this game by pressing the **Y** button). +So if you have enabled the option **Group unthemed custom collections** (it's enabled by default), any collections you add will show up in the special Collections system. Here you can access them just as you would access folders inside a regular gamelist. The amount of games per collection is shown in the description, and a random game is displayed each time you browse through the list (you can also quick jump to this random game by pressing the **Y** button). To create a custom collection, go to **Game collection settings** from the main menu and choose **Create new custom collection**. @@ -1486,7 +1489,7 @@ The collection will now be created and the collection edit mode will be entered. Removing games works the same way, just press **Y** to remove it if it's already present in your collection. You can do this either from the gamelist where the game was added, or from the collection itself. -Only files can be part of collections, not folders. As well, games marked as hidden or to not be counted as games can't be added either. +Only files can be part of collections, not folders. Games marked as hidden or to not be counted as games can't be added either. During the time that the collection is being edited, any game that is part of the collection is marked with a leading tick symbol in the game name. @@ -1519,7 +1522,7 @@ The file contents is simply a list of ROM files, such as the following: Any changes to custom collections (for example adding or removing a game) will be immediately written to the corresponding collection configuration file. -Note that if you copy or migrate a collection from a previous version of EmulationStation or if you're setting up ES-DE on a new computer, even though you copy the files into the collections directory, they will not show up inside the application as you always need to enable the collections from the menu. ES-DE looks inside the es_settings.xml file during startup to see which collections should be enabled. +If you copy or migrate a collection from a previous version of EmulationStation or if you're setting up ES-DE on a new computer, the collection will not be enabled by just copying its configuration file to the `~/.emulationstation/collections` directory. You always need to explicitly enable each collection via the menu. If you're migrating from a previous version of EmulationStation that has absolute paths in the collection files, these will be rewritten with the %ROMPATH% variable the first time you make a change to the collection. @@ -1530,11 +1533,11 @@ ES-DE is fully themeable, and although the application ships with the comprehens Somewhat confusingly the terms _theme_ and _theme set_ are used to refer to the same thing. The technically correct term for what you apply to the application to achieve a different look is actually _theme set_ as it's a collection of a number of themes for a number of game systems. The bundled rbsimple-DE is an example of such a theme set. But in this guide and in other EmulationStation resources on the Internet, the term theme is often used to refer to the same thing as a theme set. -Note that this Desktop Edition fork adds additional features to the themes and more still will be added in future versions. This means that you may not get the full benefits of the application if you're using a different theme set. But effort is spent trying to make ES-DE backwards compatible with the available themes used by other EmulationStation versions. The exception to this are some themes made for the Recalbox and Batocera forks of EmulationStation as they have added a lot of additional theme functionality that ES-DE has no intention to replicate. +Note that the Desktop Edition fork has added additional features to the themes and more still will be added in future versions. This means that you may not get the full benefits of the application if you're using a different theme set which has not been updated specifically for ES-DE. But effort is spent trying to make ES-DE backward compatible with the available themes used by other EmulationStation versions. The exception to this are some themes made for the Recalbox and Batocera forks as they have added a lot of additional theme functionality that ES-DE does not support. Themes are most easily installed to your ES-DE home directory, i.e. `~/.emulationstation/themes`. By just adding the theme sets there, one folder each, they will be found during startup and you can then choose between them via the UI Settings menu on the main menu. -Note that although you can put additional themes in your ES-DE home directory, the default rbsimple-DE theme is located in your installation folder. For example this could be `/usr/share/emulationstation/themes` or `/usr/local/share/emulationstation/themes` on Unix, `/Applications/EmulationStation Desktop Edition.app/Contents/Resources/themes` on macOS or `C:\Program Files\EmulationStation-DE\themes` on Windows. +Although you can put additional themes in your ES-DE home directory, the default rbsimple-DE theme is located in your installation folder. For example this could be `/usr/share/emulationstation/themes` or `/usr/local/share/emulationstation/themes` on Unix, `/Applications/EmulationStation Desktop Edition.app/Contents/Resources/themes` on macOS or `C:\Program Files\EmulationStation-DE\themes` on Windows. If you would like to customize the rbsimple-DE theme, simply make a copy of the complete rbsimple-DE directory to ~/.emulationstation/themes and then that copy of the theme will take precedence over the one in the application installation directory. @@ -1545,7 +1548,7 @@ In this example, we've downloaded the [Carbon](https://github.com/RetroPie/es-th ~/.emulationstation/themes/es-theme-fundamental ``` -You would now have three entries for the Theme set selector in the UI settings menu, i.e. rbsimple-DE, es-theme-carbon and es-theme-fundamental. +We now have three entries in the _Theme set_ selector in the UI settings menu, i.e. rbsimple-DE, es-theme-carbon and es-theme-fundamental. Here are some resources where additional theme sets can be downloaded. @@ -1566,22 +1569,22 @@ _An example of a modified version of the [Fundamental](https://github.com/G-rila There are numerous locations throughout ES-DE where custom scripts will be executed if the option to do so has been enabled in the settings. By default it's deactivated so be sure to enable it to use this feature. -The setup for event scripts is a bit technical, so please refer to the [INSTALL-DEV.md](INSTALL-DEV.md#custom-event-scripts) document to see how it's configured. +The setup for event scripts is a bit technical, so refer to the [INSTALL-DEV.md](INSTALL-DEV.md#custom-event-scripts) document to see how it's configured. ## Portable installation (Windows only) -On Windows, ES-DE can be installed to and run from a removable media device such as a USB memory stick. Together with games and emulators this makes for a fully portable retro gaming solution. The setup is somewhat technical, please refer to [INSTALL-DEV.md](INSTALL-DEV.md#portable-installation-on-windows) to see how it's done. +On Windows, ES-DE can be installed to and run from a removable media device such as a USB memory stick. Together with games and emulators this makes for a fully portable retro gaming solution. The setup is somewhat technical, refer to [INSTALL-DEV.md](INSTALL-DEV.md#portable-installation-on-windows) to see how it's done. -## Command line arguments +## Command line options -Please refer to the [INSTALL-DEV.md](INSTALL-DEV.md#command-line-arguments) document for a list of the command line arguments per operating system. +Refer to the [INSTALL-DEV.md](INSTALL-DEV.md#command-line-options) document for a list of the command line options per operating system. ## Supported game systems -**Note:** The following list is what the default es_systems.xml files and the rbsimple-DE theme supports. This theme set is very comprehensive, so if you're using another theme, it may be that some or many of these systems are not supported. ES-DE will still work but the game system will not be themed which looks very ugly. +**Note:** The following list is what the default es_systems.xml files and the rbsimple-DE theme supports. This theme set is very comprehensive, so if you're using another theme, it may be that some or many of these systems are not supported. ES-DE will still work but the game system will unthemed which looks very ugly. Note as well that the list and corresponding es_systems.xml templates may not reflect what is readily available for all supported operating system. This is especially true on Unix/Linux if installing RetroArch via the OS repository instead of using the Snap or Flatpak distributions (or compiling from source code) as the repository versions are normally quite crippled. @@ -1589,20 +1592,20 @@ The column **Game system name** corresponds to the directory where you should pu Regional differences are handled by simply using the game system name corresponding to your region. For example for Sega Mega Drive, _megadrive_ would be used by most people in the world, although persons from North America would use _genesis_ instead. The same is true for _pcengine_ vs _tg16_ etc. This only affects the theme selection and the corresponding theme graphics, the same emulator and scraper settings are still used for the regional variants although that can of course be modified in the es_systems.xml file if you wish. -Sometimes the name of the console is (more or less) the same for multiple regions, and in those circumstances the region has been added as a suffix to the game system name. For instance `na` for North America has been added to `snes` (Super Nintendo) giving the system name `snesna`. The same goes for Japan, as in `megacd` and `megacdjp`. Again, this only affects the theme and theme graphics. +Sometimes the name of the console is (more or less) the same for multiple regions, and in those cases the region has been added as a suffix to the game system name. For instance `na` for North America has been added to `snes` (Super Nintendo) giving the system name `snesna`. The same goes for Japan, as in `megacd` and `megacdjp`. Again, this only affects the theme and theme graphics. For the **Full name** column, text inside square brackets [] are comments and not part of the actual game system name. The **Default emulator** column shows the emulator configured in es_systems.xml, and for emulators that support multiple cores, the configured core is shown inside brackets. Any system marked with an asterisk (*) in this column requires additional system/BIOS ROMs to run, as should be explained in the emulator documentation. -For additional details regarding which game file extensions are supported per system, refer to the es_systems.xml templates [unix/es_systems.xml](resources/systems/unix/es_systems.xml), [macos/es_systems.xml](resources/systems/macos/es_systems.xml) and [windows/es_systems.xml](resources/systems/windows/es_systems.xml). Normally the extensions setup in these files should cover everything that the emulators support. +For additional details regarding which game file extensions are supported per system, refer to the es_systems.xml files [unix/es_systems.xml](resources/systems/unix/es_systems.xml), [macos/es_systems.xml](resources/systems/macos/es_systems.xml) and [windows/es_systems.xml](resources/systems/windows/es_systems.xml). Normally the extensions setup in these files should cover everything that the emulators support. -MAME emulation is a bit special as the choice of emulator or core depends on which ROM set you're using. It's recommended to go for the latest available set, as MAME is constantly improved with more complete and accurate emulation. Therefore the default `arcade` system is preconfigured to use the RetroArch core _MAME - Current_ which as the name implies will be the latest available MAME version. For really slow computers though, the 0.78 ROM set is a popular choice. To use this you either need to modify the es_systems.xml file, or you can use the `mame` system which comes preconfigured for the RetroArch core _MAME 2003-Plus_. There are other alternatives as well such as _MAME 2010_ that uses the 0.139 ROM set but this would require a manual change of the es_systems.xml file and is generally not recommended. +If you generated the ROMs directory structure when first starting ES-DE, the systeminfo.txt files located in each game system directory will also contain the information about the emulator core and supported file extensions. + +MAME emulation is a bit special as the choice of emulator or core depends on which ROM set you're using. It's recommended to go for the latest available set, as MAME is constantly improved with more complete and accurate emulation. Therefore the default `arcade` system is preconfigured to use the RetroArch core _MAME - Current_ which as the name implies will be the latest available MAME version. For really slow computers though, the 0.78 ROM set is a popular choice. To use this you either need to make a customized es_systems.xml file, or you can use the `mame` system which comes preconfigured for the RetroArch core _MAME 2003-Plus_ that is compatible with the 0.78 ROM set. There are other alternatives as well such as _MAME 2010_ that uses the 0.139 ROM set but this is generally not recommended. There are other MAME versions and derivates available as well such as MAME4ALL, AdvanceMAME, FinalBurn Alpha and FinalBurn Neo but it's beyond the scope of this document to describe those in detail. For more information, refer to the [RetroPie arcade documentation](https://retropie.org.uk/docs/Arcade) which has a good overview of the various MAME alternatives. -Running RetroArch on macOS is a bit problematic as some cores (e.g. the Nintendo 64 emulators) don't exist at all, and some cores are unusable on older macOS versions as the compilation was done without the necessary backwards compatibility support. On macOS you may therefore need to compile some cores yourself. - Consider the table below a work in progress as it's obvioulsy not fully populated yet! | Game system name | Full name | Default emulator | Recommended game setup | From 122b9b23747f9353c1adbfe58f268a2f6493a595 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 11 Jul 2021 16:17:35 +0200 Subject: [PATCH 59/76] Patch/diff files are now ignored by Git. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index c03bab04d..b8fa18ac4 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,9 @@ emulationstation.core # Profiling data gmon.out +# Patch/diff files +*.diff + # Build directories build Debug From abbc3384fd34901368b700f78208d233ccad5a56 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 11 Jul 2021 22:26:53 +0200 Subject: [PATCH 60/76] Fixed an issue where attempting to refine or skip a scraper search could lead to a crash. --- es-app/src/guis/GuiGameScraper.cpp | 7 +++++-- es-app/src/guis/GuiScraperMulti.cpp | 17 +++++++++++++---- es-app/src/guis/GuiScraperSearch.cpp | 23 ++++++++++++++++------- es-app/src/guis/GuiScraperSearch.h | 3 +++ 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/es-app/src/guis/GuiGameScraper.cpp b/es-app/src/guis/GuiGameScraper.cpp index ded44f56b..98b74fd02 100644 --- a/es-app/src/guis/GuiGameScraper.cpp +++ b/es-app/src/guis/GuiGameScraper.cpp @@ -72,8 +72,11 @@ GuiGameScraper::GuiGameScraper(Window* window, buttons.push_back( std::make_shared(mWindow, "REFINE SEARCH", "refine search", [&] { - mSearch->openInputScreen(mSearchParams); - mGrid.resetCursor(); + // Refine the search, unless the result has already been accepted. + if (!mSearch->getAcceptedResult()) { + mSearch->openInputScreen(mSearchParams); + mGrid.resetCursor(); + } })); buttons.push_back(std::make_shared(mWindow, "CANCEL", "cancel", [&] { if (mSearch->getSavedNewMedia()) { diff --git a/es-app/src/guis/GuiScraperMulti.cpp b/es-app/src/guis/GuiScraperMulti.cpp index ce1e20574..73d99670d 100644 --- a/es-app/src/guis/GuiScraperMulti.cpp +++ b/es-app/src/guis/GuiScraperMulti.cpp @@ -79,13 +79,22 @@ GuiScraperMulti::GuiScraperMulti(Window* window, if (mApproveResults) { buttons.push_back( std::make_shared(mWindow, "REFINE SEARCH", "refine search", [&] { - mSearchComp->openInputScreen(mSearchQueue.front()); - mGrid.resetCursor(); + // Refine the search, unless the result has already been accepted or we're in + // semi-automatic mode and there are less than 2 search results. + if (!mSearchComp->getAcceptedResult() && + !(mSearchComp->getSearchType() == GuiScraperSearch::ACCEPT_SINGLE_MATCHES && + mSearchComp->getScraperResultSize() < 2)) { + mSearchComp->openInputScreen(mSearchQueue.front()); + mGrid.resetCursor(); + } })); buttons.push_back(std::make_shared(mWindow, "SKIP", "skip game", [&] { - skip(); - mGrid.resetCursor(); + // Skip game, unless the result has already been accepted. + if (!mSearchComp->getAcceptedResult()) { + skip(); + mGrid.resetCursor(); + } })); } diff --git a/es-app/src/guis/GuiScraperSearch.cpp b/es-app/src/guis/GuiScraperSearch.cpp index 046a9c192..4fd119939 100644 --- a/es-app/src/guis/GuiScraperSearch.cpp +++ b/es-app/src/guis/GuiScraperSearch.cpp @@ -49,6 +49,7 @@ GuiScraperSearch::GuiScraperSearch(Window* window, SearchType type, unsigned int addChild(&mGrid); mBlockAccept = false; + mAcceptedResult = false; mRetrySearch = false; mRetryCount = 0; @@ -325,6 +326,7 @@ void GuiScraperSearch::updateViewStyle() void GuiScraperSearch::search(const ScraperSearchParams& params) { mBlockAccept = true; + mAcceptedResult = false; mMiximageResult = false; mScrapeResult = {}; @@ -347,6 +349,7 @@ void GuiScraperSearch::stop() mMDRetrieveURLsHandle.reset(); mMiximageGenerator.reset(); mBlockAccept = false; + mAcceptedResult = false; mMiximageResult = false; mScrapeResult = {}; } @@ -546,12 +549,17 @@ bool GuiScraperSearch::input(InputConfig* config, Input input) return true; } - // Refine search. - if (config->isMappedTo("y", input) && input.value != 0) - openInputScreen(mLastSearch); + // Refine the search, unless the result has already been accepted or we're in semi-automatic + // mode and there are less than 2 search results. + if (!mAcceptedResult && config->isMappedTo("y", input) && input.value != 0) { + if (mSearchType != ACCEPT_SINGLE_MATCHES || + mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() > 1) { + openInputScreen(mLastSearch); + } + } - // Skip game. - if (mScrapeCount > 1 && config->isMappedTo("x", input) && input.value != 0) + // Skip game, unless the result has already been accepted. + if (!mAcceptedResult && mScrapeCount > 1 && config->isMappedTo("x", input) && input.value != 0) mSkipCallback(); return GuiComponent::input(config, input); @@ -573,6 +581,7 @@ void GuiScraperSearch::render(const Transform4x4f& parentTrans) void GuiScraperSearch::returnResult(ScraperSearchResult result) { mBlockAccept = true; + mAcceptedResult = true; // Resolve metadata image before returning. if (result.mediaFilesDownloadStatus != COMPLETED) { @@ -791,8 +800,8 @@ void GuiScraperSearch::openInputScreen(ScraperSearchParams& params) } else { // If searching based on the actual file name, then expand to the full game name - // in case the scraper is set to TheGamesDB and it's an arcade game. This is required - // as TheGamesDB has issues with searches using the short MAME names. + // in case the scraper is set to TheGamesDB and it's an arcade game. This is + // required as TheGamesDB does not support searches using the short MAME names. if (params.game->isArcadeGame() && Settings::getInstance()->getString("Scraper") == "thegamesdb") searchString = MameNames::getInstance()->getCleanName(params.game->getCleanName()); diff --git a/es-app/src/guis/GuiScraperSearch.h b/es-app/src/guis/GuiScraperSearch.h index aeb607b79..6f71ab865 100644 --- a/es-app/src/guis/GuiScraperSearch.h +++ b/es-app/src/guis/GuiScraperSearch.h @@ -47,6 +47,8 @@ public: void search(const ScraperSearchParams& params); void openInputScreen(ScraperSearchParams& from); void stop(); + int getScraperResultSize() { return static_cast(mScraperResults.size()); } + bool getAcceptedResult() { return mAcceptedResult; } SearchType getSearchType() const { return mSearchType; } bool getSavedNewMedia() { @@ -145,6 +147,7 @@ private: unsigned int mScrapeCount; bool mRefinedSearch; bool mBlockAccept; + bool mAcceptedResult; bool mFoundGame; bool mScrapeRatings; From ea55efb523acd524c928dedb0ea673c6067afc4b Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 11 Jul 2021 22:30:23 +0200 Subject: [PATCH 61/76] Fixed an issue where refining a scraper search could lead to an empty screen. --- es-app/src/guis/GuiScraperSearch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-app/src/guis/GuiScraperSearch.cpp b/es-app/src/guis/GuiScraperSearch.cpp index 4fd119939..b96fee63d 100644 --- a/es-app/src/guis/GuiScraperSearch.cpp +++ b/es-app/src/guis/GuiScraperSearch.cpp @@ -782,12 +782,12 @@ void GuiScraperSearch::updateThumbnail() void GuiScraperSearch::openInputScreen(ScraperSearchParams& params) { auto searchForFunc = [&](const std::string& name) { + stop(); mRefinedSearch = true; params.nameOverride = name; search(params); }; - stop(); mRetryCount = 0; std::string searchString; From 61827b4de1654adf91b3d0c0bec5288d57c51040 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 11 Jul 2021 22:35:01 +0200 Subject: [PATCH 62/76] Fixed a typo in a previous commit. --- es-app/src/guis/GuiScraperMulti.cpp | 2 +- es-app/src/guis/GuiScraperSearch.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/es-app/src/guis/GuiScraperMulti.cpp b/es-app/src/guis/GuiScraperMulti.cpp index 73d99670d..222bf926e 100644 --- a/es-app/src/guis/GuiScraperMulti.cpp +++ b/es-app/src/guis/GuiScraperMulti.cpp @@ -83,7 +83,7 @@ GuiScraperMulti::GuiScraperMulti(Window* window, // semi-automatic mode and there are less than 2 search results. if (!mSearchComp->getAcceptedResult() && !(mSearchComp->getSearchType() == GuiScraperSearch::ACCEPT_SINGLE_MATCHES && - mSearchComp->getScraperResultSize() < 2)) { + mSearchComp->getScraperResultsSize() < 2)) { mSearchComp->openInputScreen(mSearchQueue.front()); mGrid.resetCursor(); } diff --git a/es-app/src/guis/GuiScraperSearch.h b/es-app/src/guis/GuiScraperSearch.h index 6f71ab865..d2f813ea3 100644 --- a/es-app/src/guis/GuiScraperSearch.h +++ b/es-app/src/guis/GuiScraperSearch.h @@ -47,7 +47,7 @@ public: void search(const ScraperSearchParams& params); void openInputScreen(ScraperSearchParams& from); void stop(); - int getScraperResultSize() { return static_cast(mScraperResults.size()); } + int getScraperResultsSize() { return static_cast(mScraperResults.size()); } bool getAcceptedResult() { return mAcceptedResult; } SearchType getSearchType() const { return mSearchType; } bool getSavedNewMedia() From 20d9721728605256096b5c63a6258c3aa0616e38 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 11 Jul 2021 22:38:08 +0200 Subject: [PATCH 63/76] Fixed an issue where a scrape followed by an aborted re-scrape could crash the application. --- es-app/src/guis/GuiMetaDataEd.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index 42fa42030..3f2d1addd 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -507,6 +507,7 @@ void GuiMetaDataEd::save() void GuiMetaDataEd::fetch() { + mMediaFilesUpdated = false; GuiGameScraper* scr = new GuiGameScraper( mWindow, mScraperParams, std::bind(&GuiMetaDataEd::fetchDone, this, std::placeholders::_1)); mWindow->pushGui(scr); From 5687af5e6b5a89f973192a95ab90b5bfe92686ac Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 11 Jul 2021 22:48:55 +0200 Subject: [PATCH 64/76] Documentation update. --- CHANGELOG.md | 17 ++++++++++------- CREDITS.md | 2 +- USERGUIDE-DEV.md | 18 +++++++++--------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 510cae626..c5216dd1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,9 +12,9 @@ The 1.1 release brings many large changes including a fullscreen media viewer, a A much better mechanism to find emulators and emulator cores has been implemented as well, which among other things removes the need to manually modify the Path variable on Windows to find RetroArch. It also eliminates the requirement for a separate Flatpak-specific es_systems.xml file on Linux. -The User guide contains additional in-depth explanations of the new functionality. +There are also several changes under the hood, such as the addition of the CImg image processing library, automatic code formatting of the entire codebase using clang-format, change of language standard from C++11 to C++14 and lots of general code refactoring. -Apart from this, many small improvements and bug fixes are part of the release, as listed below. +Apart from this, numerous small improvements and bug fixes are part of the release, as detailed below. ### Detailed list of changes @@ -25,14 +25,14 @@ Apart from this, many small improvements and bug fixes are part of the release, * Added a new video player based on FFmpeg * Added a 60 FPS frame rate upscaler option to the video player which results in slightly smoother playback for low frame rate videos (e.g. 24 and 30 FPS) * Implemented a new mechanism for locating emulators and cores, with configurable find rules (this eliminates some hacks such as the separate Flatpak es_systems.cfg file) -* Added a Windows-specific find rule that searches the Registry for the App Paths keys, which should eliminate the need to modify the Path manually to find RetroArch +* Added a Windows-specific find rule that searches the Registry for the App Paths keys, which eliminates the need to modify the Path manually to find RetroArch * Removed the deprecated %COREPATH% setting and corresponding menu entry * The "Run in background (while game is launched)" option can now be enabled on all operating systems instead of only on Windows * Added a workaround for a game launch issue on Windows when using AMD and Intel GPUs * Moved to the SDL GameController API which gives numerous improvements to the controller handling * Default controller configuration is now automatically applied, input configuration should rarely if ever be required any longer except for deliberate button customization * Added support for selecting the controller type (Xbox, Xbox 360, PS4, PS5 and SNES), which changes the help icons, help text and the input configuration tool icons and text -* Added an option to limit the input in ES-DE to only the first controller (does not affect the emulators) +* Added an option to limit the input in ES-DE to only the first controller (this does not affect the emulators) * Switched the order of the "Back" and "Start" buttons (or equivalents) in the input configurator to align with the other button entries which go from left to right * Added separate controller deadzone values for the triggers and thumbsticks * Removed the startup notification regarding default keyboard mappings being in use, instead default mappings are now considered the recommended input configuration @@ -44,7 +44,7 @@ Apart from this, many small improvements and bug fixes are part of the release, * The help text for the "A" button now shows "Enter" instead of "Launch" in the grouped custom collections view * Added navigation sounds for some actions where it was missing, such as when attempting to add folders, placeholders or systems to custom collections * Changed the custom collection "Jump to" navigation sound to the select sound instead of the scroll sound -* A notification is now displayed in the grouped custom collections view if a filter is applied to the collection +* A notification is now displayed in the grouped custom collections view if a filter is applied to the selected collection * Changed the default screensaver type from "dim" to "video" and made the fallback screensaver "dim" instead of "black" * Moved the video screensaver audio setting to the sound settings menu * Added support for the Nintendo Switch game system (using the Yuzu emulator) @@ -74,7 +74,7 @@ Apart from this, many small improvements and bug fixes are part of the release, * Added a function to ImageComponent to crop fully transparent areas around an image * Added and clarified startup log warnings for missing or invalid es_systems.xml platform tags * Added a CMake option to control whether the VLC video player should be built, and set this to off by default -* Made it possible to build on the Raspberry Pi 4 (Raspberry Pi OS) +* Made it possible to build on the Raspberry Pi 4 (tested on Raspberry Pi OS) * Removed the deprecated VideoOmxComponent * Removed the pointless APPLE_SKIP_INSTALL_LIBS CMake option * Added a clang-format style configuration file to use for automatic code formatting @@ -86,7 +86,10 @@ Apart from this, many small improvements and bug fixes are part of the release, ### Bug fixes * Marking all games as favorites for a system or folder or removing all favorite markings would sometimes crash the application -* Attempting to load a non-existent font file defined by the theme crashed the application instead of using the bundled fallback font +* Scraping new game media using the single-game scraper followed by a re-scrape that was aborted could crash the application +* The scraper search could be refined or skipped after the result was accepted which sometimes crashed the application +* Attempting to load a non-existent font file defined by the theme crashed the application instead of using the bundled font as fallback +* Refining a search before it was completed and then cancelling the dialog would lead to an empty scraper screen * Games that were filtered out were included in the random game selection for the grouped custom collections view * After switching theme sets with only a single system available, diagonal slide transitions would sometimes play when moving to the system view * Ongoing slide transition animations would continue to play after switching theme sets diff --git a/CREDITS.md b/CREDITS.md index e4a87ab34..626b7a3c4 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -110,6 +110,6 @@ https://freesound.org/people/farpro/sounds/264762 https://freesound.org/people/farpro/sounds/264763/ https://freesound.org/people/newlocknew/sounds/515827 \ -(Sample cut slightly.) +(Sample cut slightly) https://freesound.org/people/ertfelda/sounds/243701/ diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index 603adc608..b23a3512a 100644 --- a/USERGUIDE-DEV.md +++ b/USERGUIDE-DEV.md @@ -74,7 +74,7 @@ Upon first startup, ES-DE will create its `~/.emulationstation` home directory. On Unix this means /home/\/.emulationstation/, on macOS /Users/\/.emulationstation/ and on Windows C:\Users\\\\.emulationstation\ -**Note:** As of ES-DE v1.1 there is no internationalization support, which means that the application will always require the physical rather than the localized path to your home directory. For instance on macOS configured for the Swedish language /Users/myusername will be the physical path but /Användare/myusername is the localized path that is actually shown in the user interface. The same is true on Windows where the directories would be C:\Users\myusername and C:\Användare\myusername respectively. If attempting to enter the localized path for any directory-related setting, ES-DE will not be able to find it. But it's always possible to use the tilde `~` symbol when referring to your home directory, which ES-DE will expand to the physical location regardless of what language you have configured for your operating system. If you're using an English-localized system, this whole point is irrelevant as the physical and localized paths are always identical. +**Note:** As of ES-DE v1.1 there is no internationalization support, which means that the application will always require the physical rather than the localized path to your home directory. For instance on macOS configured for the Swedish language /Users/myusername will be the physical path but /Användare/myusername is the localized path that is actually shown in the user interface. The same is true on Windows where the directories would be C:\Users\myusername and C:\Användare\myusername respectively. If attempting to enter the localized path for any directory-related setting, ES-DE will not be able to find it. But it's always possible to use the tilde `~` symbol when referring to your home directory, which ES-DE will expand to the physical location regardless of what language you have configured for your operating system. If you're using an English-localized system, this whole point is irrelevant as the physical and localized paths are then identical. It's also possible to override the home directory path using the --home command line option, but this is normally required only for very special situations so we can safely ignore that option for now. @@ -151,12 +151,10 @@ ES-DE automatically configures the keyboard and any connected controllers using You can also force a run of this tool directly on startup via the command line argument `--force-input-config`. -The actual procedure to map the inputs should be self-explanatory, just follow the on-screen instructions. +The actual procedure to map the inputs should be self-explanatory, just follow the on-screen instructions. But note that custom button mappings will not change the help prompts. Any custom configuration is applied per unique device ID (GUID). So if two identical controllers are used with ES-DE, both will have the same configuration applied. If connecting controllers of the same type but of different revisions, the GUID may differ and therefore the custom configuration would need to be applied to each device individually. -Note that custom button mappings will not change the help prompts. - If you have issues with your input configuration, as a last resort you can reset all the mappings by deleting or renaming the file ~/.emulationstation/es_input.xml. If you experience double button presses with your DualShock 4 controller on macOS, please read about the workaround for this issue in the [Known issues](CHANGELOG.md#known-issues) section of the changelog. @@ -1087,7 +1085,7 @@ Settings related to the input devices, i.e. the keyboard and controllers. **Controller type** -This setting gives the ability to choose between the controller types _Xbox, Xbox 360, PlayStation 4, PlayStation 5_ and _SNES_ (Super Nintendo). Doing so changes the help icons as well as the icons and text for the input device configurator. The setting is only cosmetic and does not change the controller behavior or the controller button mappings. +This setting gives the ability to choose between the controller types _Xbox, Xbox 360, PlayStation 4, PlayStation 5_ and _SNES_ (Super Nintendo). Doing so alters the help icons and help text as well as the icons and text for the input device configurator. The setting is only cosmetic and does not change the controller behavior or the controller button mappings. **Only accept input from first controller** @@ -1223,12 +1221,14 @@ Enabling or disabling the menu when the UI mode is set to Kid. Mostly intended f With this setting enabled, there is a Quit menu shown as the last entry on the main menu which provides options to quit ES-DE, to reboot the computer or to power off the computer. With this setting disabled, there will simply be an entry to quit the application instead of the complete quit menu. -### Quit -The menu where you quit ES-DE, or reboot or power off your system. This menu is disabled by default, but can be enabled using a setting under _Other settings_. If disabled, the menu is replaced with a _Quit EmulationStation_ entry. +### Quit / Quit EmulationStation +The _Quit_ menu or _Quit EmulationStation_ entry as described by the _Show quit menu (reboot and power off entries)_ option above. + +If the menu is enabled, these are its entries: **Quit EmulationStation** -If the option _When to save game metadata_ has been set to _On exit_, the gamelist.xml files will be updated at this point. +If the option _When to save game metadata_ has been set to _On exit_, the gamelist.xml files will be updated at this point. This of course also applies if the Quit menu is disabled and replaced by the _Quit EmulationStation_ entry. **Reboot system** _(Unix and Windows only)_ @@ -1452,7 +1452,7 @@ The Dim screensaver simply dims and desaturates the current view and Black will If the option **Enable screensaver controls** has been activated, you can manually toggle the screensaver from the system view by pressing the **Back** button. In addition to this, for the Slideshow and Video screensavers, the controls will allow you to jump to a new random image or video by using the **Left** and **Right** buttons on your keyboard or controller. It's also possible to launch the game currently displayed using the **A** button, and the **Y** button will jump to the game in its gamelist without starting it. -For the video and slideshow screensavers, an overlay can be enabled via the screensaver options that displays the game name and the game system as well as a star to indicate that the game is marked as a favorite. +For the video and slideshow screensavers, an overlay can be enabled via the screensaver options that displays the game name and the game system as well as a star if the game is marked as a favorite. If the Video screensaver has been selected and there are no videos available, a fallback to the Dim screensaver will take place. The same is true for the Slideshow screensaver if no game images are available. From 7b01f3f8006b8721c1677af3e147e272839101b1 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 12 Jul 2021 00:01:38 +0200 Subject: [PATCH 65/76] Changed the patch/diff file type to the correct extension in .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b8fa18ac4..5bb24e254 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,7 @@ emulationstation.core gmon.out # Patch/diff files -*.diff +*.patch # Build directories build From 840dc13285ca4d64b429ab89671fef298a21ed9a Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 13 Jul 2021 21:47:55 +0200 Subject: [PATCH 66/76] (Windows) Fixed an issue where the wrong FFmpeg version was bundled. --- CMakeLists.txt | 12 ++++++------ es-app/CMakeLists.txt | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4059d6ef9..782a2a7b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -307,12 +307,12 @@ elseif(WIN32) endif() else() set(COMMON_LIBRARIES - "${PROJECT_SOURCE_DIR}/avcodec-59.dll" - "${PROJECT_SOURCE_DIR}/avfilter-8.dll" - "${PROJECT_SOURCE_DIR}/avformat-59.dll" - "${PROJECT_SOURCE_DIR}/avutil-57.dll" - "${PROJECT_SOURCE_DIR}/swresample-4.dll" - "${PROJECT_SOURCE_DIR}/swscale-6.dll" + "${PROJECT_SOURCE_DIR}/avcodec-58.dll" + "${PROJECT_SOURCE_DIR}/avfilter-7.dll" + "${PROJECT_SOURCE_DIR}/avformat-58.dll" + "${PROJECT_SOURCE_DIR}/avutil-56.dll" + "${PROJECT_SOURCE_DIR}/swresample-3.dll" + "${PROJECT_SOURCE_DIR}/swscale-5.dll" "${PROJECT_SOURCE_DIR}/FreeImage.dll" "${PROJECT_SOURCE_DIR}/glew32.dll" "${PROJECT_SOURCE_DIR}/libcurl-x64.dll" diff --git a/es-app/CMakeLists.txt b/es-app/CMakeLists.txt index 9187aa617..271ed4cfa 100644 --- a/es-app/CMakeLists.txt +++ b/es-app/CMakeLists.txt @@ -127,8 +127,8 @@ endif() if(WIN32) install(TARGETS EmulationStation RUNTIME DESTINATION .) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - install(FILES ../avcodec-59.dll ../avfilter-8.dll ../avformat-59.dll ../avutil-57.dll - ../postproc-56.dll ../swresample-4.dll ../swscale-6.dll ../FreeImage.dll + install(FILES ../avcodec-58.dll ../avfilter-7.dll ../avformat-58.dll ../avutil-56.dll + ../postproc-55.dll ../swresample-3.dll ../swscale-5.dll ../FreeImage.dll ../glew32.dll ../libcrypto-1_1-x64.dll ../libcurl-x64.dll ../freetype.dll ../pugixml.dll ../libssl-1_1-x64.dll ../SDL2.dll ../MSVCP140.dll ../VCOMP140.DLL ../VCRUNTIME140.dll ../VCRUNTIME140_1.dll DESTINATION .) @@ -136,8 +136,8 @@ if(WIN32) install(FILES ../libvlc.dll ../libvlccore.dll DESTINATION .) endif() else() - install(FILES ../avcodec-59.dll ../avfilter-8.dll ../avformat-59.dll ../avutil-57.dll - ../postproc-56.dll ../swresample-4.dll ../swscale-6.dll ../FreeImage.dll + install(FILES ../avcodec-58.dll ../avfilter-7.dll ../avformat-58.dll ../avutil-56.dll + ../postproc-55.dll ../swresample-3.dll ../swscale-5.dll ../FreeImage.dll ../glew32.dll ../libcrypto-1_1-x64.dll ../libcurl-x64.dll ../libfreetype.dll ../libpugixml.dll ../libssl-1_1-x64.dll ../SDL2.dll ../vcomp140.dll DESTINATION .) if(VLC_PLAYER) From 9bbba93edf7166fc3c10046658036ebdc7f4cf92 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 14 Jul 2021 19:13:25 +0200 Subject: [PATCH 67/76] Added experimental hardware decoding support to VideoFFmpegComponent. Also fixed some memory leaks and removed the video frame skipping code as it caused more harm than good. --- es-app/src/guis/GuiMenu.cpp | 19 + es-core/src/Settings.cpp | 3 + .../src/components/VideoFFmpegComponent.cpp | 440 ++++++++++++++++-- es-core/src/components/VideoFFmpegComponent.h | 12 + 4 files changed, 426 insertions(+), 48 deletions(-) diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 2c337a92a..47fa53498 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -947,6 +947,25 @@ void GuiMenu::openOtherOptions() } #endif +#if !defined(_RPI_) + // Whether to enable hardware decoding for the FFmpeg video player. + auto video_hardware_decoding = std::make_shared(mWindow); + video_hardware_decoding->setState(Settings::getInstance()->getBool("VideoHardwareDecoding")); +#if defined(BUILD_VLC_PLAYER) + s->addWithLabel("FFMPEG HARDWARE DECODING (EXPERIMENTAL)", video_hardware_decoding); +#else + s->addWithLabel("VIDEO HARDWARE DECODING (EXPERIMENTAL)", video_hardware_decoding); +#endif + s->addSaveFunc([video_hardware_decoding, s] { + if (video_hardware_decoding->getState() != + Settings::getInstance()->getBool("VideoHardwareDecoding")) { + Settings::getInstance()->setBool("VideoHardwareDecoding", + video_hardware_decoding->getState()); + s->setNeedsSaving(); + } + }); +#endif + // Whether to upscale the video frame rate to 60 FPS. auto video_upscale_frame_rate = std::make_shared(mWindow); video_upscale_frame_rate->setState(Settings::getInstance()->getBool("VideoUpscaleFrameRate")); diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index 706400902..86c4b663e 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -234,6 +234,9 @@ void Settings::setDefaults() mBoolMap["LaunchWorkaround"] = { true, true }; #endif mStringMap["MediaDirectory"] = { "", "" }; +#if !defined(_RPI_) + mBoolMap["VideoHardwareDecoding"] = { false, false }; +#endif mBoolMap["VideoUpscaleFrameRate"] = { false, false }; mBoolMap["LaunchCommandOverride"] = { true, true }; mBoolMap["ShowHiddenFiles"] = { true, true }; diff --git a/es-core/src/components/VideoFFmpegComponent.cpp b/es-core/src/components/VideoFFmpegComponent.cpp index d1ca50b39..bfd364e14 100644 --- a/es-core/src/components/VideoFFmpegComponent.cpp +++ b/es-core/src/components/VideoFFmpegComponent.cpp @@ -15,6 +15,11 @@ #define DEBUG_VIDEO false +enum AVHWDeviceType VideoFFmpegComponent::sDeviceType = AV_HWDEVICE_TYPE_NONE; +enum AVPixelFormat VideoFFmpegComponent::sPixelFormat = AV_PIX_FMT_NONE; +std::vector VideoFFmpegComponent::sHWDecodedVideos; +std::vector VideoFFmpegComponent::sSWDecodedVideos; + VideoFFmpegComponent::VideoFFmpegComponent(Window* window) : VideoComponent(window) , mFrameProcessingThread(nullptr) @@ -23,6 +28,8 @@ VideoFFmpegComponent::VideoFFmpegComponent(Window* window) , mAudioStream(nullptr) , mVideoCodec(nullptr) , mAudioCodec(nullptr) + , mHardwareCodec(nullptr) + , mHwContext(nullptr) , mVideoCodecContext(nullptr) , mAudioCodecContext(nullptr) , mVBufferSrcContext(nullptr) @@ -529,19 +536,41 @@ void VideoFFmpegComponent::readFrames() int returnValue = 0; - // We have a video frame that needs conversion to RGBA format. - // Prioritize audio by dropping video frames if the audio frame queue - // gets too small, i.e. if the computer can't keep up the processing. - if ((!mAudioCodecContext || !mDecodedFrame || - mAudioFrameCount < mAudioTargetQueueSize) || - mAudioFrameQueue.size() > 3) { + if (mSWDecoder) { returnValue = av_buffersrc_add_frame_flags( - mVBufferSrcContext, mVideoFrame, AV_BUFFERSRC_FLAG_KEEP_REF); + mVBufferSrcContext, mVideoFrame, AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT); } else { - LOG(LogDebug) - << "VideoFFmpegComponent::readFrames(): Dropped video frame as " - "the audio buffer was too small"; + AVFrame* destFrame = nullptr; + destFrame = av_frame_alloc(); + + if (mVideoFrame->format == sPixelFormat) { + if (av_hwframe_transfer_data(destFrame, mVideoFrame, 0) < 0) { + LOG(LogError) << "VideoFFmpegComponent::readFrames(): " + "Couldn't transfer decoded video frame to " + "system memory"; + av_frame_free(&destFrame); + av_packet_unref(mPacket); + break; + } + else { + destFrame->pts = mVideoFrame->pts; + destFrame->pkt_dts = mVideoFrame->pkt_dts; + destFrame->pict_type = mVideoFrame->pict_type; + destFrame->chroma_location = mVideoFrame->chroma_location; + destFrame->pkt_pos = mVideoFrame->pkt_pos; + destFrame->pkt_duration = mVideoFrame->pkt_duration; + destFrame->pkt_size = mVideoFrame->pkt_size; + } + } + else { + LOG(LogError) << "VideoFFmpegComponent::readFrames(): " + "Couldn't decode video frame"; + } + + returnValue = av_buffersrc_add_frame_flags( + mVBufferSrcContext, destFrame, AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT); + av_frame_free(&destFrame); } if (returnValue < 0) { @@ -552,6 +581,9 @@ void VideoFFmpegComponent::readFrames() av_packet_unref(mPacket); break; } + else { + av_packet_unref(mPacket); + } } else if (mPacket->stream_index == mAudioStreamIndex) { if (!avcodec_send_packet(mAudioCodecContext, mPacket) && @@ -569,6 +601,13 @@ void VideoFFmpegComponent::readFrames() av_packet_unref(mPacket); continue; } + else { + av_packet_unref(mPacket); + } + } + else { + // Ignore any stream that is not video or audio. + av_packet_unref(mPacket); } } } @@ -808,14 +847,290 @@ void VideoFFmpegComponent::calculateBlackRectangle() } } +void VideoFFmpegComponent::detectHWDecoder() +{ +#if defined(__APPLE__) + LOG(LogDebug) << "VideoFFmpegComponent::detectHWDecoder(): Using hardware decoder VideoToolbox"; + sDeviceType = AV_HWDEVICE_TYPE_VIDEOTOOLBOX; + return; +#elif defined(_WIN64) + bool hasDXVA2 = false; + bool hasD3D11VA = false; + + AVBufferRef* testContext = nullptr; + AVHWDeviceType tempDevice = AV_HWDEVICE_TYPE_NONE; + + while ((tempDevice = av_hwdevice_iterate_types(tempDevice)) != AV_HWDEVICE_TYPE_NONE) { + // The Direct3D 11 decoder detection seems to cause stability issues on some machines + // so disabling it for now. + if (tempDevice == AV_HWDEVICE_TYPE_DXVA2) { + // if (tempDevice == AV_HWDEVICE_TYPE_DXVA2 || tempDevice == AV_HWDEVICE_TYPE_D3D11VA) { + if (av_hwdevice_ctx_create(&testContext, tempDevice, nullptr, nullptr, 0) >= 0) { + if (tempDevice == AV_HWDEVICE_TYPE_DXVA2) + hasDXVA2 = true; + else + hasD3D11VA = true; + } + av_buffer_unref(&testContext); + } + } + + // Prioritize DXVA2. + if (hasDXVA2) { + LOG(LogDebug) << "VideoFFmpegComponent::detectHWDecoder(): Using hardware decoder DXVA2"; + sDeviceType = AV_HWDEVICE_TYPE_DXVA2; + } + else if (hasD3D11VA) { + LOG(LogDebug) << "VideoFFmpegComponent::detectHWDecoder(): Using hardware decoder D3D11VA"; + sDeviceType = AV_HWDEVICE_TYPE_D3D11VA; + } + else { + LOG(LogWarning) << "VideoFFmpegComponent::detectHWDecoder(): Unable to detect any usable " + "hardware decoder"; + } +#else + // This would mostly be Linux, but possibly also BSD Unix. + + bool hasVAAPI = false; + bool hasVDPAU = false; + + AVBufferRef* testContext = nullptr; + AVHWDeviceType tempDevice = AV_HWDEVICE_TYPE_NONE; + + while ((tempDevice = av_hwdevice_iterate_types(tempDevice)) != AV_HWDEVICE_TYPE_NONE) { + if (tempDevice == AV_HWDEVICE_TYPE_VDPAU || tempDevice == AV_HWDEVICE_TYPE_VAAPI) { + if (av_hwdevice_ctx_create(&testContext, tempDevice, nullptr, nullptr, 0) >= 0) { + if (tempDevice == AV_HWDEVICE_TYPE_VAAPI) + hasVAAPI = true; + else + hasVDPAU = true; + } + av_buffer_unref(&testContext); + } + } + + // Prioritize VAAPI. + if (hasVAAPI) { + LOG(LogDebug) << "VideoFFmpegComponent::detectHWDecoder(): Using hardware decoder VAAPI"; + sDeviceType = AV_HWDEVICE_TYPE_VAAPI; + } + else if (hasVDPAU) { + LOG(LogDebug) << "VideoFFmpegComponent::detectHWDecoder(): Using hardware decoder VDPAU"; + sDeviceType = AV_HWDEVICE_TYPE_VDPAU; + } + else { + LOG(LogWarning) << "VideoFFmpegComponent::detectHWDecoder(): Unable to detect any " + "usable hardware decoder"; + } +#endif +} + +bool VideoFFmpegComponent::decoderInitHW() +{ + // This should only be required the first time any video is played. + if (sDeviceType == AV_HWDEVICE_TYPE_NONE) + detectHWDecoder(); + + // If there is no device, the detection failed. + if (sDeviceType == AV_HWDEVICE_TYPE_NONE) + return true; + + // If the hardware decoding of the file was previously unsuccessful during the program + // session, then don't attempt it again. + if (std::find(sSWDecodedVideos.begin(), sSWDecodedVideos.end(), mVideoPath) != + sSWDecodedVideos.end()) { + return true; + } + + // 50 is just an arbitrary number so we don't potentially get stuck in an endless loop. + for (int i = 0; i < 50; i++) { + const AVCodecHWConfig* config = avcodec_get_hw_config(mHardwareCodec, i); + if (!config) { + LOG(LogDebug) << "VideoFFmpegComponent::decoderInitHW(): Hardware decoder \"" + << av_hwdevice_get_type_name(sDeviceType) + << "\" does not seem to support codec \"" << mHardwareCodec->name << "\""; + } + else if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && + config->device_type == sDeviceType) { + sPixelFormat = config->pix_fmt; + break; + } + } + + // If the pixel format is not set properly, then hardware decoding won't work for the file. + if (sPixelFormat == AV_PIX_FMT_NONE) + return true; + + if (av_hwdevice_ctx_create(&mHwContext, sDeviceType, nullptr, nullptr, 0) < 0) { + LOG(LogDebug) << "VideoFFmpegComponent::decoderInitHW(): Unable to open hardware device \"" + << av_hwdevice_get_type_name(sDeviceType) << "\""; + av_buffer_unref(&mHwContext); + return true; + } + + // Callback function for AVCodecContext. + // clang-format off + auto formatFunc = + [](AVCodecContext* ctx, const enum AVPixelFormat* pix_fmts) -> enum AVPixelFormat { + + const enum AVPixelFormat* pixelFormats; + + for (pixelFormats = pix_fmts; *pixelFormats != -1; pixelFormats++) + if (*pixelFormats == sPixelFormat) + return static_cast(sPixelFormat); + + return AV_PIX_FMT_NONE; + }; + + // Check if the video can actually be hardware decoded (unless this has already been done). + if (std::find(sHWDecodedVideos.begin(), sHWDecodedVideos.end(), mVideoPath) == + sHWDecodedVideos.end()) { + + // clang-format on + AVCodecContext* checkCodecContext = avcodec_alloc_context3(mHardwareCodec); + + if (avcodec_parameters_to_context(checkCodecContext, mVideoStream->codecpar)) { + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't fill the video codec context parameters for file \"" + << mVideoPath << "\""; + avcodec_free_context(&checkCodecContext); + return true; + } + else { + bool onlySWDecode = false; + + checkCodecContext->get_format = formatFunc; + checkCodecContext->hw_device_ctx = av_buffer_ref(mHwContext); + + if (avcodec_open2(checkCodecContext, mHardwareCodec, nullptr)) { + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't initialize the video codec context for file \"" + << mVideoPath << "\""; + } + + AVPacket* checkPacket = av_packet_alloc(); + int readFrameReturn = 0; + + while ((readFrameReturn = av_read_frame(mFormatContext, checkPacket)) == 0) { + if (checkPacket->stream_index != mVideoStreamIndex) + av_packet_unref(checkPacket); + else + break; + } + + // Supplying a packet to the decoder will cause an immediate error for some videos + // while others will require that one or several frame receive attempts are performed + // before we get a definitive result. On error we fall back to the software decoder. + if (readFrameReturn == 0 && checkPacket->stream_index == mVideoStreamIndex) { + if (avcodec_send_packet(checkCodecContext, checkPacket) < 0) { + // Save the file path to the list of videos that require software decoding + // so we don't have to check it again during the program session. + sSWDecodedVideos.emplace_back(mVideoPath); + onlySWDecode = true; + } + else { + AVFrame* checkFrame; + checkFrame = av_frame_alloc(); + + onlySWDecode = true; + + // For some videos we need to process at least one extra frame to verify + // that the hardware encoder can actually be used, otherwise the fallback + // to software decoding would take place when it's not necessary. + for (int i = 0; i < 3; i++) { + if (avcodec_receive_frame(checkCodecContext, checkFrame) < 0) { + av_packet_unref(checkPacket); + while (av_read_frame(mFormatContext, checkPacket) == 0) { + if (checkPacket->stream_index != mVideoStreamIndex) + av_packet_unref(checkPacket); + else + break; + } + + avcodec_send_packet(checkCodecContext, checkPacket); + av_packet_unref(checkPacket); + + if (avcodec_receive_frame(checkCodecContext, checkFrame) == 0) { + onlySWDecode = false; + break; + } + else { + onlySWDecode = true; + } + } + else { + onlySWDecode = false; + } + av_packet_unref(checkPacket); + av_frame_unref(checkFrame); + } + + av_frame_free(&checkFrame); + + if (onlySWDecode == false) { + // Save the file path to the list of videos that work with hardware + // decoding so we don't have to check it again during the program session. + sHWDecodedVideos.emplace_back(mVideoPath); + } + } + + av_packet_free(&checkPacket); + avcodec_free_context(&checkCodecContext); + + // Seek back to the start position of the file. + av_seek_frame(mFormatContext, -1, 0, AVSEEK_FLAG_ANY); + + if (onlySWDecode) + return true; + } + } + } + + // The hardware decoding check passed successfully or it was done previously for the file. + // Now perform the real setup. + mVideoCodecContext = avcodec_alloc_context3(mHardwareCodec); + + if (!mVideoCodecContext) { + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't allocate video codec context for file \"" + << mVideoPath << "\""; + avcodec_free_context(&mVideoCodecContext); + return true; + } + + if (avcodec_parameters_to_context(mVideoCodecContext, mVideoStream->codecpar)) { + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't fill the video codec context parameters for file \"" + << mVideoPath << "\""; + avcodec_free_context(&mVideoCodecContext); + return true; + } + + mVideoCodecContext->get_format = formatFunc; + mVideoCodecContext->hw_device_ctx = av_buffer_ref(mHwContext); + + if (avcodec_open2(mVideoCodecContext, mHardwareCodec, nullptr)) { + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't initialize the video codec context for file \"" + << mVideoPath << "\""; + avcodec_free_context(&mVideoCodecContext); + return true; + } + + return false; +} + void VideoFFmpegComponent::startVideo() { if (!mFormatContext) { + mHardwareCodec = nullptr; + mHwContext = nullptr; mFrameProcessingThread = nullptr; mVideoWidth = 0; mVideoHeight = 0; mAccumulatedTime = 0; mStartTimeAccumulation = false; + mSWDecoder = true; mDecodedFrame = false; mEndOfVideo = false; mVideoFrameCount = 0; @@ -858,13 +1173,21 @@ void VideoFFmpegComponent::startVideo() // Video stream setup. +#if defined(_RPI_) + bool hwDecoding = false; +#else + bool hwDecoding = Settings::getInstance()->getBool("VideoHardwareDecoding"); +#endif + mVideoStreamIndex = - av_find_best_stream(mFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0); + av_find_best_stream(mFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, &mHardwareCodec, 0); if (mVideoStreamIndex < 0) { LOG(LogError) << "VideoFFmpegComponent::startVideo(): " "Couldn't retrieve video stream for file \"" << mVideoPath << "\""; + avformat_close_input(&mFormatContext); + avformat_free_context(mFormatContext); return; } @@ -872,50 +1195,69 @@ void VideoFFmpegComponent::startVideo() mVideoWidth = mFormatContext->streams[mVideoStreamIndex]->codecpar->width; mVideoHeight = mFormatContext->streams[mVideoStreamIndex]->codecpar->height; - mVideoCodec = const_cast(avcodec_find_decoder(mVideoStream->codecpar->codec_id)); + LOG(LogDebug) << "VideoFFmpegComponent::startVideo(): " + << "Playing video \"" << mVideoPath << "\" (codec: " + << avcodec_get_name( + mFormatContext->streams[mVideoStreamIndex]->codecpar->codec_id) + << ", decoder: " << (hwDecoding ? "hardware" : "software") << ")"; - if (!mVideoCodec) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " - "Couldn't find a suitable video codec for file \"" - << mVideoPath << "\""; - return; + if (hwDecoding) + mSWDecoder = decoderInitHW(); + else + mSWDecoder = true; + + if (mSWDecoder) { + // The hardware decoder initialization failed, which can happen for a number of reasons. + if (hwDecoding) { + LOG(LogDebug) << "VideoFFmpegComponent::startVideo(): Hardware decoding failed, " + "falling back to software decoder"; + } + + mVideoCodec = + const_cast(avcodec_find_decoder(mVideoStream->codecpar->codec_id)); + + if (!mVideoCodec) { + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't find a suitable video codec for file \"" + << mVideoPath << "\""; + return; + } + + mVideoCodecContext = avcodec_alloc_context3(mVideoCodec); + + if (!mVideoCodecContext) { + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't allocate video codec context for file \"" + << mVideoPath << "\""; + return; + } + + if (mVideoCodec->capabilities & AV_CODEC_CAP_TRUNCATED) + mVideoCodecContext->flags |= AV_CODEC_FLAG_TRUNCATED; + + if (avcodec_parameters_to_context(mVideoCodecContext, mVideoStream->codecpar)) { + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't fill the video codec context parameters for file \"" + << mVideoPath << "\""; + return; + } + + if (avcodec_open2(mVideoCodecContext, mVideoCodec, nullptr)) { + LOG(LogError) << "VideoFFmpegComponent::startVideo(): " + "Couldn't initialize the video codec context for file \"" + << mVideoPath << "\""; + return; + } } - mVideoCodecContext = avcodec_alloc_context3(mVideoCodec); - - if (!mVideoCodec) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " - "Couldn't allocate video codec context for file \"" - << mVideoPath << "\""; - return; - } - - if (mVideoCodec->capabilities & AV_CODEC_CAP_TRUNCATED) - mVideoCodecContext->flags |= AV_CODEC_FLAG_TRUNCATED; - - if (avcodec_parameters_to_context(mVideoCodecContext, mVideoStream->codecpar)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " - "Couldn't fill the video codec context parameters for file \"" - << mVideoPath << "\""; - return; - } - - if (avcodec_open2(mVideoCodecContext, mVideoCodec, nullptr)) { - LOG(LogError) << "VideoFFmpegComponent::startVideo(): " - "Couldn't initialize the video codec context for file \"" - << mVideoPath << "\""; - return; - } - - // Audio stream setup, optional as some videos may not have any audio tracks. + // Audio stream setup, optional as some videos do not have any audio tracks. mAudioStreamIndex = av_find_best_stream(mFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); if (mAudioStreamIndex < 0) { LOG(LogDebug) << "VideoFFmpegComponent::startVideo(): " - "Couldn't retrieve audio stream for file \"" - << mVideoPath << "\""; + "File does not seem to contain any audio streams"; } if (mAudioStreamIndex >= 0) { @@ -996,13 +1338,15 @@ void VideoFFmpegComponent::stopVideo() mFrameProcessingThread->join(); mFrameProcessingThread.reset(); mOutputAudio.clear(); - AudioManager::getInstance()->clearStream(); } // Clear the video and audio frame queues. std::queue().swap(mVideoFrameQueue); std::queue().swap(mAudioFrameQueue); + // Clear the audio buffer. + AudioManager::getInstance()->clearStream(); + if (mFormatContext) { av_frame_free(&mVideoFrame); av_frame_free(&mVideoFrameResampled); @@ -1010,7 +1354,7 @@ void VideoFFmpegComponent::stopVideo() av_frame_free(&mAudioFrameResampled); av_packet_unref(mPacket); av_packet_free(&mPacket); - + av_buffer_unref(&mHwContext); avcodec_free_context(&mVideoCodecContext); avcodec_free_context(&mAudioCodecContext); avformat_close_input(&mFormatContext); diff --git a/es-core/src/components/VideoFFmpegComponent.h b/es-core/src/components/VideoFFmpegComponent.h index 11e9840e0..2d06b9709 100644 --- a/es-core/src/components/VideoFFmpegComponent.h +++ b/es-core/src/components/VideoFFmpegComponent.h @@ -69,6 +69,10 @@ private: // Calculate the black rectangle that is shown behind videos with non-standard aspect ratios. void calculateBlackRectangle(); + // Detect and initialize the hardware decoder. + static void detectHWDecoder(); + bool decoderInitHW(); + // Start the video immediately. virtual void startVideo() override; // Stop the video. @@ -78,6 +82,11 @@ private: // Handle looping the video. Must be called periodically. virtual void handleLooping() override; + static enum AVHWDeviceType sDeviceType; + static enum AVPixelFormat sPixelFormat; + static std::vector sSWDecodedVideos; + static std::vector sHWDecodedVideos; + std::shared_ptr mTexture; std::vector mVideoRectangleCoords; @@ -90,6 +99,8 @@ private: AVStream* mAudioStream; AVCodec* mVideoCodec; AVCodec* mAudioCodec; + AVCodec* mHardwareCodec; + AVBufferRef* mHwContext; AVCodecContext* mVideoCodecContext; AVCodecContext* mAudioCodecContext; int mVideoStreamIndex; @@ -152,6 +163,7 @@ private: bool mStartTimeAccumulation; bool mDecodedFrame; bool mEndOfVideo; + bool mSWDecoder; }; #endif // ES_CORE_COMPONENTS_VIDEO_FFMPEG_COMPONENT_H From ea0d129e6d85d28e7aaeaebf5ee7a70e11a8a5b2 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 14 Jul 2021 19:17:28 +0200 Subject: [PATCH 68/76] (Unix) Changed the RetroArch core for the Atari 2600 system to Stella 2014. --- resources/systems/unix/es_systems.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/systems/unix/es_systems.xml b/resources/systems/unix/es_systems.xml index bf7b62ec3..a96740be9 100644 --- a/resources/systems/unix/es_systems.xml +++ b/resources/systems/unix/es_systems.xml @@ -114,7 +114,7 @@ Atari 2600 %ROMPATH%/atari2600 .a26 .A26 .bin .BIN .7z .7Z .zip .ZIP - %EMULATOR_RETROARCH% -L %CORE_RETROARCH%/stella_libretro.so %ROM% + %EMULATOR_RETROARCH% -L %CORE_RETROARCH%/stella2014_libretro.so %ROM% atari2600 atari2600 From 977ab5e6836f97cbaea9961cec8ff8f0af48528b Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 14 Jul 2021 19:19:45 +0200 Subject: [PATCH 69/76] (RPi) Fixed an issue where the window focus would sometimes get lost when returning from a game. --- es-core/src/Platform.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/es-core/src/Platform.cpp b/es-core/src/Platform.cpp index c90b4940f..5d2a9ed81 100644 --- a/es-core/src/Platform.cpp +++ b/es-core/src/Platform.cpp @@ -100,6 +100,14 @@ int launchGameUnix(const std::string& cmd_utf8, bool runInBackground) } returnValue = pclose(commandPipe); + +#if defined(_RPI_) + // Hack to avoid that the application window occasionally loses focus when returning from + // a game, which only seems to happen on the Raspberry Pi. + SDL_Delay(50); + SDL_SetWindowInputFocus(Renderer::getSDLWindow()); +#endif + // We need to shift the return value as it contains some flags (which we don't need). returnValue >>= 8; From 5c0cf89ac1d9bd39669b27d20664e62bd7e2401b Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Wed, 14 Jul 2021 19:27:39 +0200 Subject: [PATCH 70/76] Documentation update. --- INSTALL-DEV.md | 32 ++++++++++++++++---------------- USERGUIDE-DEV.md | 6 +++++- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/INSTALL-DEV.md b/INSTALL-DEV.md index 092ed7250..97245a23d 100644 --- a/INSTALL-DEV.md +++ b/INSTALL-DEV.md @@ -66,7 +66,7 @@ sudo pacman -S vlc **Raspberry Pi OS (Raspian)** -Note: The Raspberry Pi 4 is the minimum recommended model to use with ES-DE. As this type of device is quite weak and because the FFmpeg video player currently lacks hardware decoding/acceleration it is strongly adviced to build with the VLC player, which is hardware accelerated. +Note: The Raspberry Pi 4 is the minimum recommended model to use with ES-DE. As this type of device is quite weak and because the FFmpeg video player does not support hardware decoding on this platform, it's strongly adviced to build with the VLC player, which is hardware accelerated. All of the required packages can be installed with apt-get: ``` @@ -792,7 +792,7 @@ In the descriptions below it's assumed that all build steps for MinGW/GCC will b **Download the dependency packages:** -FFmpeg (choose a package with win64-gpl-shared in the filename, the latest snapshot build should generally be fine to use) \ +FFmpeg (choose the n4.4 package with win64-gpl-shared in the filename, the snapshot version will not work) \ [https://github.com/BtbN/FFmpeg-Builds/releases](https://github.com/BtbN/FFmpeg-Builds/releases) FreeImage (binary distribution) \ @@ -937,18 +937,18 @@ Copy the files to the `emulationstation-de` build directory. Most of them will c **Required files for MSVC:** ``` -avcodec-59.dll +avcodec-58.dll avcodec.lib avfilter.lib -avfilter-8.dll -avformat-59.dll +avfilter-7.dll +avformat-58.dll avformat.lib -avutil-57.dll +avutil-56.dll avutil.lib -postproc-56.dll -swresample-4.dll +postproc-55.dll +swresample-3.dll swresample.lib -swscale-6.dll +swscale-5.dll swscale.lib FreeImage.dll FreeImage.lib @@ -995,13 +995,13 @@ lib /def:libvlc.def /out:libvlc.lib /machine:x64 **Required files for MinGW:** ``` -avcodec-59.dll -avfilter-8.dll -avformat-59.dll -avutil-57.dll -postproc-56.dll -swresample-4.dll -swscale-6.dll +avcodec-58.dll +avfilter-7.dll +avformat-58.dll +avutil-56.dll +postproc-55.dll +swresample-3.dll +swscale-5.dll FreeImage.dll glew32.dll libcrypto-1_1-x64.dll (from the OpenSSL package, located in Git MinGW/MSYS2 under \mingw64\bin) diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index b23a3512a..6430e62c2 100644 --- a/USERGUIDE-DEV.md +++ b/USERGUIDE-DEV.md @@ -1180,6 +1180,10 @@ Enabling this option makes ES-DE continue to run while a game is launched. This There is an issue with launching games on some Windows computers, seemingly on those with AMD and Intel GPUs. The emulator will start and work correctly, but the screen will be blank. Enabling this option is a workaround for that problem, with the drawback that the screen will become white instead of black when the emulator is loading. This option is enabled by default, so experiment with disabling it for a slightly better experience. If you're using an Nvidia GPU, chances are high that it will then work fine. An alternative workaround is to enable the _Run in background (while game is launched)_ option described above, so test which gives the best result. The two options can however not be enabled at the same time. Hopefully this whole game launching issue can be resolved completely in a future ES-DE release. +**FFmpeg/Video hardware decoding (experimental)** _(All platforms except Raspberry Pi)_ + +Enabling this option offloads video decoding to the GPU. Whether this actually increases performance is another matter. As desktop-class CPUs are very capable of decoding the video streams directly, the performance may actually get worse as hardware decoding requires data to be passed back and forth between the CPU and GPU. This potential degradation is especially true for integrated GPUs, and even more so if the GPU is heavily utilized. In addition to this, most GPUs can only decode a subset of video codecs and profiles, meaning ES-DE will anyway have to fallback to software rendering when the GPU is unable to process the stream. With that said, there are still situations where hardware decoding can help, so experiment with the setting to see if it's useful for your configuration. Unfortunately hardware decoding for the FFmpeg video player is not supported on the Raspberry Pi, and it will probably stay this way unless FFmpeg starts to support the MMAL decoder internally. For this device, the VLC-based video player can be used instead, for which hardware decoding is available. + **Upscale video frame rate to 60 FPS (FFmpeg)** With this option enabled, videos with lower frame rates than 60 FPS, such as 24 and 30 will get upscaled to 60 FPS. This results in slightly smoother playback for some videos. There is a small performance hit from this option, so on weaker machines it may be necessary to disable it for fluent video playback. This setting has no effect when using the VLC video player. If the VLC video player is not included in the ES-DE build, the "(FFmpeg)" text is omitted from the setting name. @@ -1622,7 +1626,7 @@ Consider the table below a work in progress as it's obvioulsy not fully populate | apple2gs | Apple IIGS | | | | arcade | Arcade | RetroArch (MAME - Current)* | Single archive file following MAME name standard in root folder | | astrocade | Bally Astrocade | | | -| atari2600 | Atari 2600 | RetroArch (Stella) | Single archive or ROM file in root folder | +| atari2600 | Atari 2600 | RetroArch (Stella on macOS and Windows, Stella 2014 on Unix) | Single archive or ROM file in root folder | | atari5200 | Atari 5200 | | | | atari7800 | Atari 7800 ProSystem | | | | atari800 | Atari 800 | | | From b4d85c5b349982551c773241834e9f98b8cc6989 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 15 Jul 2021 18:07:01 +0200 Subject: [PATCH 71/76] Fixed a heisenbug in AudioManager that was actually caused by a bug in SDL_AudioStreamAvailable(). --- es-core/src/AudioManager.cpp | 37 ++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/es-core/src/AudioManager.cpp b/es-core/src/AudioManager.cpp index b3f9e7632..c773a3ca3 100644 --- a/es-core/src/AudioManager.cpp +++ b/es-core/src/AudioManager.cpp @@ -306,16 +306,37 @@ void AudioManager::clearStream() // to empty the stream. // SDL_AudioStreamClear(sConversionStream); - mIsClearingStream = true; - int length = sAudioFormat.samples * 4; + // If sSoundVector is empty it means we are shutting down. In this case don't attempt + // to clear the stream as this could lead to a crash. + if (sSoundVector.empty()) + return; - while (SDL_AudioStreamAvailable(sConversionStream) > 0) { - std::vector readBuffer(length); - int processedLength = - SDL_AudioStreamGet(sConversionStream, static_cast(&readBuffer.at(0)), length); - if (processedLength <= 0) - break; + mIsClearingStream = true; + + // This code is required as there's seemingly a bug in SDL_AudioStreamAvailable(). + // The function sometimes returns 0 even if there is data left in the buffer, possibly + // because the remaining data is less than the configured sample size. It happens almost + // permanently on NetBSD but also on at least Linux from time to time. Adding some data + // to the stream buffer to get above this threshold before calling the function will + // return the proper number. So adding 10000 as we do here would give a return value of + // for instance 10880 instead of 0, assuming there were 880 bytes of data left in the buffer. + // Fortunately the SDL_AudioStreamGet() function acts correctly on any arbitrary sample size + // so we can actually clear the entire buffer. If this workaround was not implemented, there + // would be a sound glitch when some samples from the previous video would play any time a + // new video was started (assuming the issue was triggered be some remaining buffer data). + std::vector writeBuffer(10000); + if (SDL_AudioStreamPut(sConversionStream, reinterpret_cast(&writeBuffer.at(0)), + 10000) == -1) { + LOG(LogError) << "Failed to put samples in the conversion stream:"; + LOG(LogError) << SDL_GetError(); + mIsClearingStream = false; + return; } + int length = SDL_AudioStreamAvailable(sConversionStream); + + std::vector readBuffer(length); + SDL_AudioStreamGet(sConversionStream, static_cast(&readBuffer.at(0)), length); + mIsClearingStream = false; } From c60481c7b412d95d0c6c544b5a90a02bda8ffec9 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 15 Jul 2021 18:07:37 +0200 Subject: [PATCH 72/76] Small documentation update. --- USERGUIDE-DEV.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index 6430e62c2..63f638c54 100644 --- a/USERGUIDE-DEV.md +++ b/USERGUIDE-DEV.md @@ -1182,7 +1182,7 @@ There is an issue with launching games on some Windows computers, seemingly on t **FFmpeg/Video hardware decoding (experimental)** _(All platforms except Raspberry Pi)_ -Enabling this option offloads video decoding to the GPU. Whether this actually increases performance is another matter. As desktop-class CPUs are very capable of decoding the video streams directly, the performance may actually get worse as hardware decoding requires data to be passed back and forth between the CPU and GPU. This potential degradation is especially true for integrated GPUs, and even more so if the GPU is heavily utilized. In addition to this, most GPUs can only decode a subset of video codecs and profiles, meaning ES-DE will anyway have to fallback to software rendering when the GPU is unable to process the stream. With that said, there are still situations where hardware decoding can help, so experiment with the setting to see if it's useful for your configuration. Unfortunately hardware decoding for the FFmpeg video player is not supported on the Raspberry Pi, and it will probably stay this way unless FFmpeg starts to support the MMAL decoder internally. For this device, the VLC-based video player can be used instead, for which hardware decoding is available. +Enabling this option offloads video decoding to the GPU. Whether this actually increases performance is another matter. As desktop-class CPUs are very capable of decoding the video streams directly, the performance may actually get worse as hardware decoding requires data to be passed back and forth between the CPU and GPU. This potential degradation is especially true for integrated GPUs, and even more so if the GPU is heavily utilized. In addition to this, most GPUs can only decode a subset of video codecs and profiles, meaning ES-DE will anyway have to fallback to software decoding when the GPU is unable to process the stream. With that said, there are still situations where hardware decoding can help, so experiment with the setting to see if it's useful for your configuration. Unfortunately hardware decoding for the FFmpeg video player is not supported on the Raspberry Pi and it will probably stay this way unless FFmpeg starts to support the MMAL decoder internally. For this device the VLC-based video player can be used instead, which supports hardware decoding. **Upscale video frame rate to 60 FPS (FFmpeg)** @@ -1190,7 +1190,7 @@ With this option enabled, videos with lower frame rates than 60 FPS, such as 24 **Per game launch command override** -If enabled, you can override the launch command defined in es_systems.xml on a per-game basis. It's only recommended to disable this option for testing purposes, such as when a game won't start and you're unsure if it's your custom launch command that causes the problem. +If enabled, you can override the launch command defined in es_systems.xml on a per-game basis using the metadata editor. It's only recommended to disable this option for testing purposes, such as when a game won't start and you're unsure if it's your custom launch command that causes the problem. **Show hidden files and folders (requires restart)** From bc6d9625032516d7a5a6c9c887635ac83186bc86 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 15 Jul 2021 18:23:03 +0200 Subject: [PATCH 73/76] (macOS) Fixed a Clang compiler warning. --- es-app/src/guis/GuiScraperSearch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-app/src/guis/GuiScraperSearch.cpp b/es-app/src/guis/GuiScraperSearch.cpp index b96fee63d..04c8dc48a 100644 --- a/es-app/src/guis/GuiScraperSearch.cpp +++ b/es-app/src/guis/GuiScraperSearch.cpp @@ -553,7 +553,7 @@ bool GuiScraperSearch::input(InputConfig* config, Input input) // mode and there are less than 2 search results. if (!mAcceptedResult && config->isMappedTo("y", input) && input.value != 0) { if (mSearchType != ACCEPT_SINGLE_MATCHES || - mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() > 1) { + (mSearchType == ACCEPT_SINGLE_MATCHES && mScraperResults.size() > 1)) { openInputScreen(mLastSearch); } } From 80f9114e27b61330c1b575aa6ec382d790dab936 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 15 Jul 2021 18:30:20 +0200 Subject: [PATCH 74/76] Bumped the version to v1.1.0-rc --- es-app/CMakeLists.txt | 2 +- es-app/assets/EmulationStation-DE_Info.plist | 2 +- es-app/assets/emulationstation.6.gz | Bin 1051 -> 1048 bytes es-app/assets/emulationstation.desktop | 2 +- es-app/src/EmulationStation.h | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/es-app/CMakeLists.txt b/es-app/CMakeLists.txt index 271ed4cfa..436d4e4f3 100644 --- a/es-app/CMakeLists.txt +++ b/es-app/CMakeLists.txt @@ -275,7 +275,7 @@ endif() set(CPACK_PACKAGE_VENDOR "Leon Styhre") # Update this when there has been a new release. -set(CPACK_PACKAGE_VERSION "1.1.0-rc-dev") +set(CPACK_PACKAGE_VERSION "1.1.0-rc") # Use the shorter x64 descriptor if on the x86_64/AMD64 architecture. if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL x86_64 OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL AMD64) diff --git a/es-app/assets/EmulationStation-DE_Info.plist b/es-app/assets/EmulationStation-DE_Info.plist index f6d034d20..ac1f66a70 100644 --- a/es-app/assets/EmulationStation-DE_Info.plist +++ b/es-app/assets/EmulationStation-DE_Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleVersion - 1.1.0-rc-dev + 1.1.0-rc LSMinimumSystemVersion 10.14.0 LSUIPresentationMode diff --git a/es-app/assets/emulationstation.6.gz b/es-app/assets/emulationstation.6.gz index a008bd6c04857878f8cde9024861b6c941a97a7a..42584d0cbc9331e8709dba8c6d53a05b01589fd2 100644 GIT binary patch delta 308 zcmV-40n7fI2$%>5ABzYG=wa}Y2eT2V{EgPk^hliE!To49A^*A@kw%bf_(7p%uPt_|j5I%J^t}|TS zhDpoucZp5k`k38@h6Oo#Sl!K+WXkYbYTF8_*FS#*(A(1m{9IYvn9qX&Gni9vtn_y9 z3n5|kE_hg5wZqNXLjSug00|l4vWMPCC{ytsOjY@}^Q`VG&9_|Qo;oCU5hCiPlL?Hb z>AXigW1vvEt1%2eTAW!FG7zH(E2(BXN2M_oLZ_{0k6~%QFEwk^e?2 zWw#1844lDFF7cxL3k`=*_S{y7ER{B3TwVco%vKDR%xX2#D9Dv&2K%#b0lNWz02ejS zPbQ{PH4B_Gp+`xQqN1@amI&!!|DXTOhx@VLYM}zYXd6g4kgzx*5Oh zCVv-b+ifN|Xx{_-=js(12%owd*BP#E!=&Z-yTqn%eavn{!-5<=tnTJZGG%xzwQYse z>z_XY=2*+Xw6l&N?R zrmFngc~Ipj57d5s4QtCS eN@I*;Aees!eKLddXAwiku)hJZUaAWW2mk={?ws)e diff --git a/es-app/assets/emulationstation.desktop b/es-app/assets/emulationstation.desktop index 73ae2bb76..1969d7e88 100644 --- a/es-app/assets/emulationstation.desktop +++ b/es-app/assets/emulationstation.desktop @@ -1,5 +1,5 @@ [Desktop Entry] -Version=1.1.0-rc-dev +Version=1.1.0-rc Name=EmulationStation Desktop Edition GenericName=Emulator Front-end Type=Application diff --git a/es-app/src/EmulationStation.h b/es-app/src/EmulationStation.h index 49c986296..70d02fca7 100644 --- a/es-app/src/EmulationStation.h +++ b/es-app/src/EmulationStation.h @@ -15,7 +15,7 @@ #define PROGRAM_VERSION_MINOR 1 #define PROGRAM_VERSION_MAINTENANCE 0 // clang-format on -#define PROGRAM_VERSION_STRING "1.1.0-rc-dev" +#define PROGRAM_VERSION_STRING "1.1.0-rc" #define PROGRAM_BUILT_STRING __DATE__ " - " __TIME__ From a93cef1eb32ec3af05cc366daab2ed3a5ce9f026 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 15 Jul 2021 19:29:56 +0200 Subject: [PATCH 75/76] Fixed a small CMake configuration error. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 782a2a7b6..b33826f47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -362,7 +362,7 @@ endif() if(DEFINED BCMHOST) link_directories("${CMAKE_FIND_ROOT_PATH}/opt/vc/lib") list(APPEND COMMON_LIBRARIES bcm_host brcmEGL ${OPENGLES_LIBRARIES}) -elseif(DEFINED RPI) +elseif(RPI) link_directories("${CMAKE_FIND_ROOT_PATH}/opt/vc/lib") list(APPEND COMMON_LIBRARIES ${OPENGLES_LIBRARIES}) endif() From fd742ab452abc15612ed80a96f286117bed1be35 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 15 Jul 2021 22:30:23 +0200 Subject: [PATCH 76/76] (Unix) Small change to the man page. --- es-app/assets/emulationstation.6.gz | Bin 1048 -> 1041 bytes tools/generate_man_page.sh | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/es-app/assets/emulationstation.6.gz b/es-app/assets/emulationstation.6.gz index 42584d0cbc9331e8709dba8c6d53a05b01589fd2..0c30fdb75d50039a425c8c1d7f1e90da6aa7135a 100644 GIT binary patch literal 1041 zcmV+s1n&DEiwFown($x(17&S>Y+-b1Z*FsRVRUJ4ZZ0+eg;m>b<2Dd|=T}VqvJPO$ zak}UNi=u#2JB?c3U^!bXB0ta)WwDk>l@~ke*YA*W)|V#jgJE%oXRc?4VbX!o9A@(s zOy{HM;VY~jqZl5d$@m9=$iRVW*fPNsH7tWo0|hJVyNU`JgdabJgC9c@t{!1LeVPnc z(R>!K{2z3F>OkjrTQ+cadpE#uR@ssoE`>J!5B7s_5Z(qVMdfd_V!A`Z_z`Bq>6rZM zascC3QG67QnBEy#!#Lya?7{KZ*?bX4F$wSC+6H+}@+j%a+RKuDk}uaDG#bahE~AAD zoBW>_=#JxHG`<0@fkLjNFaZ-8VbCs?+(_6kuo^$Pz>DH9H0(n;a8n$!luCoPX#vt-N1J!O??@ z1j9H-qG?%L7mNoVcxH+qex$EQ3C(v3|FGndfV=vP!_07`4#KQ3~CRIp3bJ?b)h!Zer! z>q6lQld5?oCGH}31)Ta~->{AK{uanDXBdy@#BD=*y&(43igw0(?d0!0ZQIQRN9}uH z_f(ybp761)aGl}m)=U_VzaBPy>tePW>KEkjY4tc?k_p3Wv1tmVUibPDKySMn__;8q z)}Q-*rZJ~bs%`%lLj3AoaKE-Hi<`57?su675;DYP4-=XHZ5^4rQt>SpxSo#r-1tB` z@pufwNj&cmM-<3pYAcMm?TdSV_LP?tNe|R~K1H+T2Bjgo@0ZNIelCap^=pU`W3=A@ LRc$wf{|5j7UgH6E literal 1048 zcmV+z1n2u7iwFqlVenuA17&S>Y+-b1Z*FsRVRUJ4ZZ0+eg;i~H;x-Weu3xd`%aNJb zg!HC2bJOVzDWq`=fx+ZDH!&ZCEn_vZ-~I@CMk(;;M@jz%*rIYR85d&gRESx186)+e_bXE zA2#_vFVLT)(RgwJ+yI4AD`_Jp3n-O_jaGZZUwlF2PjF ziNfcet9rvFDni0~( zQ?aM50Zb4{a&c^LXIhzuvJ3~P#`;|iR9UXc%4rE-Qq)2xM=A6RR#GH}=arHthHCnZ z)-omDlqJMT!noX@~8iY&YY ze-Al8i*68J48+g1I5y0;*fMtH08J-B7UFvmL&>on(8tuVBVqWE?{0&_N-2v`pH$H2 zmqisX3g6s-4Ke$)JL$n74U;Ue+va_InCEkR5T4LU>6~?}@^^3jG34-&bkxDl-J0QttUi&7h_zz6+27=4RVGD7klzjV&?aoejbE`@S_n=@qo}WxirD_&9*-#vN zTo)Qwn9}VFDRCG1E8x{9`-W{?@V7vIdBb=_Cw?2!^98ZPR&+Cd*-ic~(6-x5aL~R7 z_D|I-G7vs>HLf#U-G)ia@pp+$-};!{hK2<>dRX1fmt@NDT58)0snAXigW1vvEt1 RetroPie community (RetroPie fork) -Alec \"Aloshi\" Lofquist (original version) +Alec Lofquist (original version) [SEE ALSO] Full documentation is available at: