diff --git a/DEVNOTES.md b/DEVNOTES.md index dfe997347..adf33b753 100644 --- a/DEVNOTES.md +++ b/DEVNOTES.md @@ -18,14 +18,13 @@ Some key points: * 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. -* Use `std::string` instead of `char *` or `char []` unless there is a very specific reason requiring the latter +* Use `std::string` instead of `char *` or `char []` unless there is a specific reason requiring the latter * If the arguments (and initializer list) for a function or class exceeds 4 items, arrange them vertically to make the code easier to read * 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 a small 'm', e.g. mMyMemberVariable -* Don't pad variable declarations with spaces to make them align in columns, I'm sure it's well intended but it looks terrible * Use the same naming convention for functions as for local variables, e.g. someFunction() -* Inline functions can be used but don't overdo it by using them for functions that won't be called very frequently +* Inline functions makes perfect sense to use, but don't overdo it by using them for functions that won't be called very frequently * Never put more than one statement on a single line, except for lambda expressions * Avoid overoptimizations, especially if it sacrifices readability, makes the code hard to expand on or is error prone * For the rest, check the code and have fun! :) @@ -34,7 +33,7 @@ Some key points: Development Environment ======================= -EmulationStation-DE is developed and compiled using GCC and GDB. \ +EmulationStation-DE is developed and compiled using GCC and GDB. Any code editor can be used of course, I use [VSCodium](https://vscodium.com). \ For debugging purposes, starting the application like this could make sense: `emulationstation --windowed --debug --resolution 1280 720` diff --git a/INSTALL.md b/INSTALL.md index b362565d7..b3b718a26 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -8,35 +8,35 @@ The code has a few dependencies. For building, you'll need CMake and development **On Debian/Ubuntu:** All of the required packages can be easily installed with `apt-get`: -```bash -sudo apt-get install libsdl2-dev libfreeimage-dev libfreetype6-dev libcurl4-openssl-dev rapidjson-dev \ - libasound2-dev libgl1-mesa-dev build-essential cmake fonts-droid-fallback libvlc-dev \ +``` +sudo apt-get install libsdl2-dev libfreeimage-dev libfreetype6-dev libcurl4-openssl-dev rapidjson-dev + libasound2-dev libgl1-mesa-dev build-essential cmake fonts-droid-fallback libvlc-dev libvlccore-dev vlc-bin ``` **On Fedora:** For this operating system, use `dnf` (with rpmfusion activated) : -```bash -sudo dnf install SDL2-devel freeimage-devel freetype-devel curl-devel \ - alsa-lib-devel mesa-libGL-devel cmake \ +``` +sudo dnf install SDL2-devel freeimage-devel freetype-devel curl-devel + alsa-lib-devel mesa-libGL-devel cmake vlc-devel rapidjson-devel ``` To checkout the source, run the following: -```bash +``` git clone https://gitlab.com/leonstyhre/emulationstation-de ``` -Then generate and build the Makefile using CMake: +Then generate the Makefile and build the software like this: -```bash +``` cd emulationstation-de cmake -DOpenGL_GL_PREFERENCE=GLVND . make ``` -NOTE: to generate a `Debug` build on Unix/Linux, run this: -```bash +NOTE: to generate a `Debug` build on Unix/Linux, run this instead: +``` cmake -DOpenGL_GL_PREFERENCE=GLVND -DCMAKE_BUILD_TYPE=Debug . make ``` @@ -65,9 +65,9 @@ Configuring **~/.emulationstation/es_systems.cfg:** -EmulationStation Desktop Edition ships with a comprehensive `es_systems.cfg` configuration file, and as the logic is to use a `%ROMPATH%` variable to locate the ROM files (with a corresponding setting in `es_settings.cfg`), normally you shouldn't need to modify this file to the same extent as previous versions of EmulationStation. Still, see below in this document on how to adjust es_systems.cfg file if required. +EmulationStation Desktop Edition ships with a comprehensive `es_systems.cfg` configuration file, and as the logic is to use a `%ROMPATH%` variable to locate the ROM files (with a corresponding setting in `es_settings.cfg`), normally you shouldn't need to modify this file to the same extent as previous versions of EmulationStation. Still, see below in this document on how to adjust the es_systems.cfg file if required. -Upon first startup of the application, if there is no es_systems.cfg file present, it will be copied from the template directory of the resources directory. This directory is located in the installation path of the application, for instance `/usr/local/share/emulationstation/resources/templates`. +Upon first startup of the application, if there is no es_systems.cfg file present, it will be copied from the template subdirectory inside the resources directory. This directory is located in the installation path of the application, for instance `/usr/local/share/emulationstation/resources/templates`. The template file will be copied to `~/.emulationstation/es_systems.cfg`. `~` is `$HOME` on Linux, and `%HOMEPATH%` on Windows. @@ -80,7 +80,7 @@ The exception would be the ROMDirectory setting as ES won't start if no ROM file **Setting the ROM directory:** -By default, ES looks in `~/ROMs` for the ROM files, where they are expected to be grouped into directories corresponding to the systems, for example: +By default, ES looks in `~/ROMs` for the ROM files, where they are expected to be grouped into directories corresponding to the game systems, for example: ``` user@computer:~ROMs$ ls -1 @@ -99,6 +99,7 @@ Keep in mind though that you still need to group the ROMs into directories corre **Keep in mind that you'll have to set up your emulator separately from EmulationStation!** **~/.emulationstation/es_input.cfg:** + When you first start EmulationStation, you will be prompted to configure an input device. The process is thus: 1. Hold a button on the device you want to configure. This includes the keyboard. @@ -116,7 +117,10 @@ The new configuration will be added to the `~/.emulationstation/es_input.cfg` fi **If your controller stops working, you can delete the `~/.emulationstation/es_input.cfg` file to make the input configuration screen re-appear on the next run.** -You can use `--help` or `-h` to view a list of command-line options. Briefly outlined here: +**Command line arguments:** + +You can use `--help` or `-h` to view a list of command line options. Briefly outlined here: + ``` --resolution [width] [height] Try to force a particular resolution --gamelist-only Skip automatic game ROM search, only read from gamelist.xml @@ -147,16 +151,16 @@ As you can see above, you can override the home directory path using the `--home Writing an es_systems.cfg ========================= -The `es_systems.cfg` file contains the system configuration data for EmulationStation, written in XML. \ +The es_systems.cfg file contains the system configuration data for EmulationStation, written in XML format. \ This tells EmulationStation what systems you have, what platform they correspond to (for scraping), and where the games are located. -ES will only check in your home directory for an `es_systems.cfg` file, for example `~/.emulationstation/es_systems.cfg`. +ES will only check in your home directory for an es_systems.cfg file, for example `~/.emulationstation/es_systems.cfg`. -The order EmulationStation displays systems reflects the order you define them in. In the case of the default `es_systems.cfg` file, the systems are listed in alphabetical order. +The order EmulationStation displays systems reflects the order you define them in. In the case of the default es_systems.cfg file, the systems are listed in alphabetical order. **NOTE:** A system *must* have at least one game present in its "path" directory, or ES will ignore it! If no valid systems are found, ES will report an error and quit! -Here's an overview of the `es_systems.cfg` file layout: +Here's an overview of the file layout: ```xml @@ -194,23 +198,25 @@ Here's an overview of the `es_systems.cfg` file layout: ``` -The following variables are replaced by ES in launch commands: - -`%ROMPATH%` - Replaced with the path defined for the setting ROMDirectory in es_settings.cfg. +The following variables are expanded by ES for the `command` tag: `%ROM%` - Replaced with absolute path to the selected ROM, with most Bash special characters escaped with a backslash. -`%BASENAME%` - Replaced with the "base" name of the path to the selected ROM. For example, a path of "/foo/bar.rom", this tag would be "bar". This tag is useful for setting up AdvanceMAME. +`%BASENAME%` - Replaced with the "base" name of the path to the selected ROM. For example, a path of `/foo/bar.rom`, this tag would be `bar`. This tag is useful for setting up AdvanceMAME. `%ROM_RAW%` - 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. +For the `path` tag, the following variable is expanded by ES: + +`%ROMPATH%` - Replaced with the path defined for the setting ROMDirectory in `es_settings.cfg`. + gamelist.xml ============ The gamelist.xml file for a system defines metadata for games, such as a name, description, release date, and rating. -As of the fork to EmulationStation Desktop Edition, game media information no longer needs to be defined in the `gamelist.xml` files. Instead the application will look for any media matching the ROM filename. The media path where to look for game art is configurable either manually in `es_settings.cfg` or via the GUI. If configured manually in `es_settings.cfg`, it looks something like this: +As of the fork to EmulationStation Desktop Edition, game media information no longer needs to be defined in the gamelist.xml files. Instead the application will look for any media matching the ROM filename. The media path where to look for game art is configurable either manually in `es_settings.cfg` or via the GUI. If configured manually in es_settings.cfg, it looks something like this: `` diff --git a/README.md b/README.md index a4e046b28..1429bb922 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ EmulationStation Desktop Edition EmulationStation Desktop Edition is a cross-platform graphical front-end for emulators with controller and keyboard navigation. -This is a fork intended for use primarily on desktop computers where EmulationStation is not the primary interface for the computer. As such, this software will not provide full control over emulator settings or emulator button mappings or provide system utility functions and similar. Instead it's assumed that the emulators and the overall environment has been properly configured upfront. +The difference to other versions of EmulationStation is that this fork is intended for use primarily on desktop computers where it's not the primary interface for the computer. As such, this version does not aim to provide full control over emulator settings or emulator button mappings, or include system administration functions and similar. Instead it's assumed that the emulators and the overall environment has been properly configured upfront. The software comes preconfigured for use primarily with [RetroArch](https://www.retroarch.com), although this can certainly be changed as all emulator settings are fully configurable, even on a per-game basis. @@ -13,24 +13,25 @@ Apart from code commits, help is especially needed for thorough testing of the s It's impossible for me to test every game system (RBSimple-DE has support for almost a 100 different systems!) so it would be especially useful to hear about any issues with starting games using the default es_systems.cfg configuration file and also if there are any issues regarding scraping for certain systems. -In general, a review of the [es_systems.cfg](resources/templates/es_systems.cfg_unix) file including the supported file extensions would be great. +In general, a review of the [es_systems.cfg](resources/templates/es_systems.cfg_unix) file including the supported file extensions would be great! As for RBSimple-DE there are quite some missing graphic files and other customizations for a number of game systems. \ -Check out [MISSING.md](themes/rbsimple-DE/MISSING.md) for more details of what needs to be added. +Check out [MISSING.md](themes/rbsimple-DE/MISSING.md) for more details of what needs to be added or updated. -Finally, if someone could make a proper port to the Macintosh Operating System, that would be great as then all of the three major desktop operating systems would be supported. There is some code present specifically for macOS but I've been unable to test it. +Finally, if someone could make a proper port to macOS, that would be great as then all of the three major desktop operating systems would be supported. There is some code present specifically for macOS but I've been unable to test it. And according to the code comments, some things seem to be missing altogether for this operating system, so it may take some effort. General information =================== -[NEWS.md](NEWS.md) contains information about new functionality, improvements and bug fixes. An overview of all previous versions will be included here as well. +[NEWS.md](NEWS.md) contains information about new functionality, improvements and bug fixes. \ +(An overview of all previous versions will be included here as well.) [INSTALL.md](INSTALL.md) provides details on how to build and configure the application. [DEVNOTES.md](DEVNOTES.md) is the place to go if you're interested in participating in the development of EmulationStation Desktop Edition. -What it can do -============== +Overview of the software +======================== diff --git a/THEMES.md b/THEMES.md index 8e70f3215..c5a72ac2e 100644 --- a/THEMES.md +++ b/THEMES.md @@ -285,8 +285,8 @@ Starting EmulationStation with the --debug flag will provide feedback on whether providing an error message if any of the .wav sound files could not be loaded. Example debug output: \ -May 12 21:12:37 lvl2: Sound::getFromTheme() looking for [all.selectSound] \ -May 12 21:12:37 lvl2: [selectSound] found, ready to play sound file +`May 12 21:12:37 lvl2: Sound::getFromTheme() looking for [all.selectSound]` \ +`May 12 21:12:37 lvl2: [selectSound] found, ready to play sound file` Example `navigationsounds.xml`, to be included from the main theme file: diff --git a/es-app/src/CollectionSystemManager.cpp b/es-app/src/CollectionSystemManager.cpp index ac016daad..c0fcc2cb1 100644 --- a/es-app/src/CollectionSystemManager.cpp +++ b/es-app/src/CollectionSystemManager.cpp @@ -75,8 +75,8 @@ CollectionSystemManager::CollectionSystemManager(Window* window) : mWindow(windo mIsEditingCustom = false; mEditingCollection = "Favorites"; - mEditingCollectionSystemData = NULL; - mCustomCollectionsBundle = NULL; + mEditingCollectionSystemData = nullptr; + mCustomCollectionsBundle = nullptr; } CollectionSystemManager::~CollectionSystemManager() diff --git a/es-app/src/FileData.cpp b/es-app/src/FileData.cpp index 866acaf06..2560988d2 100644 --- a/es-app/src/FileData.cpp +++ b/es-app/src/FileData.cpp @@ -304,7 +304,7 @@ FileData* FileData::getSourceFileData() void FileData::addChild(FileData* file) { assert(mType == FOLDER); - assert(file->getParent() == NULL); + assert(file->getParent() == nullptr); const std::string key = file->getKey(); if (mChildrenByFilename.find(key) == mChildrenByFilename.cend()) { @@ -321,7 +321,7 @@ void FileData::removeChild(FileData* file) mChildrenByFilename.erase(file->getKey()); for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) { if (*it == file) { - file->mParent = NULL; + file->mParent = nullptr; mChildren.erase(it); return; } @@ -491,7 +491,7 @@ CollectionFileData::CollectionFileData(FileData* file, SystemData* system) // We use this constructor to create a clone of the filedata, and change its system. mSourceFileData = file->getSourceFileData(); refreshMetadata(); - mParent = NULL; + mParent = nullptr; metadata = mSourceFileData->metadata; mSystemName = mSourceFileData->getSystem()->getName(); } @@ -501,7 +501,7 @@ CollectionFileData::~CollectionFileData() // Need to remove collection file data at the collection object destructor. if (mParent) mParent->removeChild(this); - mParent = NULL; + mParent = nullptr; } std::string CollectionFileData::getKey() { diff --git a/es-app/src/PlatformId.cpp b/es-app/src/PlatformId.cpp index cbe95ba0d..bd387ec42 100644 --- a/es-app/src/PlatformId.cpp +++ b/es-app/src/PlatformId.cpp @@ -1,7 +1,7 @@ // // PlatformId.h // -// Index of all supported platforms. +// Index of all supported systems/platforms. // #include "PlatformId.h" @@ -89,7 +89,7 @@ namespace PlatformIds PlatformId getPlatformId(const char* str) { - if (str == NULL) + if (str == nullptr) return PLATFORM_UNKNOWN; for (unsigned int i = 1; i < PLATFORM_COUNT; i++) { diff --git a/es-app/src/PlatformId.h b/es-app/src/PlatformId.h index d834af572..8f846e4dd 100644 --- a/es-app/src/PlatformId.h +++ b/es-app/src/PlatformId.h @@ -1,7 +1,7 @@ // // PlatformId.h // -// Index of all supported platforms. +// Index of all supported systems/platforms. // #pragma once diff --git a/es-app/src/SystemData.cpp b/es-app/src/SystemData.cpp index 1bc64fe00..977529d35 100644 --- a/es-app/src/SystemData.cpp +++ b/es-app/src/SystemData.cpp @@ -270,7 +270,7 @@ bool SystemData::loadConfig() // If there appears to be an actual platform ID supplied // but it didn't match the list, generate a warning. - if (str != NULL && str[0] != '\0' && platformId == PlatformIds::PLATFORM_UNKNOWN) + if (str != nullptr && str[0] != '\0' && platformId == PlatformIds::PLATFORM_UNKNOWN) LOG(LogWarning) << " Unknown platform for system \"" << name << "\" (platform \"" << str << "\" from list \"" << platformList << "\")"; else if (platformId != PlatformIds::PLATFORM_UNKNOWN) @@ -499,7 +499,7 @@ SystemData* SystemData::getRandomSystem() } // If we end up here, there is no valid system. - return NULL; + return nullptr; } FileData* SystemData::getRandomGame() @@ -510,7 +510,7 @@ FileData* SystemData::getRandomGame() // Get a random number in range. if (total == 0) - return NULL; + return nullptr; target = (int)Math::round((std::rand() / (float)RAND_MAX) * (total - 1)); return list.at(target); diff --git a/es-app/src/SystemScreenSaver.cpp b/es-app/src/SystemScreenSaver.cpp index 942a8a742..4df8a1d3d 100644 --- a/es-app/src/SystemScreenSaver.cpp +++ b/es-app/src/SystemScreenSaver.cpp @@ -1,3 +1,10 @@ +// +// SystemScreenSaver.cpp +// +// Screensaver, supporting the following modes: +// Dim, black, video, slideshow. +// + #include "SystemScreenSaver.h" #ifdef _RPI_ @@ -15,460 +22,427 @@ #include "SystemData.h" #include #include -#define FADE_TIME 300 +#define FADE_TIME 300 -SystemScreenSaver::SystemScreenSaver(Window* window) : - mVideoScreensaver(NULL), - mImageScreensaver(NULL), - mWindow(window), - mVideosCounted(false), - mVideoCount(0), - mImagesCounted(false), - mImageCount(0), - mState(STATE_INACTIVE), - mOpacity(0.0f), - mTimer(0), - mSystemName(""), - mGameName(""), - mCurrentGame(NULL), - mStopBackgroundAudio(true) +SystemScreenSaver::SystemScreenSaver( + Window* window) + : mVideoScreensaver(nullptr), + mImageScreensaver(nullptr), + mWindow(window), + mVideosCounted(false), + mVideoCount(0), + mImagesCounted(false), + mImageCount(0), + mState(STATE_INACTIVE), + mOpacity(0.0f), + mTimer(0), + mSystemName(""), + mGameName(""), + mCurrentGame(nullptr), + mStopBackgroundAudio(true) { - mWindow->setScreenSaver(this); - std::string path = getTitleFolder(); - if(!Utils::FileSystem::exists(path)) - Utils::FileSystem::createDirectory(path); - srand((unsigned int)time(NULL)); - mVideoChangeTime = 30000; + mWindow->setScreenSaver(this); + std::string path = getTitleFolder(); + if (!Utils::FileSystem::exists(path)) + Utils::FileSystem::createDirectory(path); + srand((unsigned int)time(nullptr)); + mVideoChangeTime = 30000; } SystemScreenSaver::~SystemScreenSaver() { - // Delete subtitle file, if existing - remove(getTitlePath().c_str()); - mCurrentGame = NULL; - delete mVideoScreensaver; - delete mImageScreensaver; + // Delete subtitle file, if it exists. + remove(getTitlePath().c_str()); + mCurrentGame = nullptr; + delete mVideoScreensaver; + delete mImageScreensaver; } bool SystemScreenSaver::allowSleep() { - //return false; - return ((mVideoScreensaver == NULL) && (mImageScreensaver == NULL)); + return ((mVideoScreensaver == nullptr) && (mImageScreensaver == nullptr)); } bool SystemScreenSaver::isScreenSaverActive() { - return (mState != STATE_INACTIVE); + return (mState != STATE_INACTIVE); } void SystemScreenSaver::startScreenSaver() { - std::string screensaver_behavior = Settings::getInstance()->getString("ScreenSaverBehavior"); - if (!mVideoScreensaver && (screensaver_behavior == "random video")) - { - // Configure to fade out the windows, Skip Fading if Instant mode - mState = PowerSaver::getMode() == PowerSaver::INSTANT - ? STATE_SCREENSAVER_ACTIVE - : STATE_FADE_OUT_WINDOW; - mVideoChangeTime = Settings::getInstance()->getInt("ScreenSaverSwapVideoTimeout"); - mOpacity = 0.0f; + std::string screensaver_behavior = Settings::getInstance()->getString("ScreenSaverBehavior"); + if (!mVideoScreensaver && (screensaver_behavior == "random video")) { + // Configure to fade out the windows, skip fading if mode is set to Instant. + mState = PowerSaver::getMode() == PowerSaver::INSTANT + ? STATE_SCREENSAVER_ACTIVE + : STATE_FADE_OUT_WINDOW; + mVideoChangeTime = Settings::getInstance()->getInt("ScreenSaverSwapVideoTimeout"); + mOpacity = 0.0f; - // Load a random video - std::string path = ""; - pickRandomVideo(path); + // Load a random video. + std::string path = ""; + pickRandomVideo(path); - int retry = 200; - while(retry > 0 && ((path.empty() || !Utils::FileSystem::exists(path)) || mCurrentGame == NULL)) - { - retry--; - pickRandomVideo(path); - } + int retry = 200; + while (retry > 0 && ((path.empty() || !Utils::FileSystem::exists(path)) || + mCurrentGame == nullptr)) { + retry--; + pickRandomVideo(path); + } - if (!path.empty() && Utils::FileSystem::exists(path)) - { -#ifdef _RPI_ - // Create the correct type of video component - if (Settings::getInstance()->getBool("ScreenSaverOmxPlayer")) - mVideoScreensaver = new VideoPlayerComponent(mWindow, getTitlePath()); - else - mVideoScreensaver = new VideoVlcComponent(mWindow, getTitlePath()); -#else - mVideoScreensaver = new VideoVlcComponent(mWindow, getTitlePath()); -#endif + if (!path.empty() && Utils::FileSystem::exists(path)) { + #ifdef _RPI_ + // Create the correct type of video component + if (Settings::getInstance()->getBool("ScreenSaverOmxPlayer")) + mVideoScreensaver = new VideoPlayerComponent(mWindow, getTitlePath()); + else + mVideoScreensaver = new VideoVlcComponent(mWindow, getTitlePath()); + #else + mVideoScreensaver = new VideoVlcComponent(mWindow, getTitlePath()); + #endif - mVideoScreensaver->topWindow(true); - mVideoScreensaver->setOrigin(0.5f, 0.5f); - mVideoScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f); + mVideoScreensaver->topWindow(true); + mVideoScreensaver->setOrigin(0.5f, 0.5f); + mVideoScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f, + Renderer::getScreenHeight() / 2.0f); - if (Settings::getInstance()->getBool("StretchVideoOnScreenSaver")) - { - mVideoScreensaver->setResize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - } - else - { - mVideoScreensaver->setMaxSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - } - mVideoScreensaver->setVideo(path); - mVideoScreensaver->setScreensaverMode(true); - mVideoScreensaver->onShow(); - PowerSaver::runningScreenSaver(true); - mTimer = 0; - return; - } - } - else if (screensaver_behavior == "slideshow") - { - // Configure to fade out the windows, Skip Fading if Instant mode - mState = PowerSaver::getMode() == PowerSaver::INSTANT - ? STATE_SCREENSAVER_ACTIVE - : STATE_FADE_OUT_WINDOW; - mVideoChangeTime = Settings::getInstance()->getInt("ScreenSaverSwapImageTimeout"); - mOpacity = 0.0f; + if (Settings::getInstance()->getBool("StretchVideoOnScreenSaver")) + mVideoScreensaver->setResize((float)Renderer::getScreenWidth(), + (float)Renderer::getScreenHeight()); + else + mVideoScreensaver->setMaxSize((float)Renderer::getScreenWidth(), + (float)Renderer::getScreenHeight()); - // Load a random image - std::string path = ""; - if (Settings::getInstance()->getBool("SlideshowScreenSaverCustomImageSource")) - { - pickRandomCustomImage(path); - // Custom images are not tied to the game list - mCurrentGame = NULL; - } - else - { - pickRandomGameListImage(path); - } + mVideoScreensaver->setVideo(path); + mVideoScreensaver->setScreensaverMode(true); + mVideoScreensaver->onShow(); + PowerSaver::runningScreenSaver(true); + mTimer = 0; + return; + } + } + else if (screensaver_behavior == "slideshow") { + // Configure to fade out the windows, skip fading if mode is set to Instant. + mState = PowerSaver::getMode() == PowerSaver::INSTANT + ? STATE_SCREENSAVER_ACTIVE + : STATE_FADE_OUT_WINDOW; + mVideoChangeTime = Settings::getInstance()->getInt("ScreenSaverSwapImageTimeout"); + mOpacity = 0.0f; - if (!mImageScreensaver) - { - mImageScreensaver = new ImageComponent(mWindow, false, false); - } + // Load a random image. + std::string path = ""; + if (Settings::getInstance()->getBool("SlideshowScreenSaverCustomImageSource")) { + pickRandomCustomImage(path); + // Custom images are not tied to the game list. + mCurrentGame = nullptr; + } + else { + pickRandomGameListImage(path); + } - mTimer = 0; + if (!mImageScreensaver) + mImageScreensaver = new ImageComponent(mWindow, false, false); - mImageScreensaver->setImage(path); - mImageScreensaver->setOrigin(0.5f, 0.5f); - mImageScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f); + mTimer = 0; - if (Settings::getInstance()->getBool("SlideshowScreenSaverStretch")) - { - mImageScreensaver->setResize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - } - else - { - mImageScreensaver->setMaxSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - } + mImageScreensaver->setImage(path); + mImageScreensaver->setOrigin(0.5f, 0.5f); + mImageScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f, + Renderer::getScreenHeight() / 2.0f); - std::string bg_audio_file = Settings::getInstance()->getString("SlideshowScreenSaverBackgroundAudioFile"); - if ((!mBackgroundAudio) && (bg_audio_file != "")) - { - if (Utils::FileSystem::exists(bg_audio_file)) - { - // paused PS so that the background audio keeps playing - PowerSaver::pause(); - mBackgroundAudio = Sound::get(bg_audio_file); - mBackgroundAudio->play(); - } - } + if (Settings::getInstance()->getBool("SlideshowScreenSaverStretch")) + mImageScreensaver->setResize((float)Renderer::getScreenWidth(), + (float)Renderer::getScreenHeight()); + else + mImageScreensaver->setMaxSize((float)Renderer::getScreenWidth(), + (float)Renderer::getScreenHeight()); - PowerSaver::runningScreenSaver(true); - mTimer = 0; - return; - } - // No videos. Just use a standard screensaver - mState = STATE_SCREENSAVER_ACTIVE; - mCurrentGame = NULL; + std::string bg_audio_file = Settings::getInstance()-> + getString("SlideshowScreenSaverBackgroundAudioFile"); + if ((!mBackgroundAudio) && (bg_audio_file != "")) { + if (Utils::FileSystem::exists(bg_audio_file)) { + // Pause PowerSaver so that the background audio keeps playing. + PowerSaver::pause(); + mBackgroundAudio = Sound::get(bg_audio_file); + mBackgroundAudio->play(); + } + } + + PowerSaver::runningScreenSaver(true); + mTimer = 0; + return; + } + // No videos. Just use a standard screensaver. + mState = STATE_SCREENSAVER_ACTIVE; + mCurrentGame = nullptr; } void SystemScreenSaver::stopScreenSaver() { - if ((mBackgroundAudio) && (mStopBackgroundAudio)) - { - mBackgroundAudio->stop(); - mBackgroundAudio.reset(); - // if we were playing audio, we paused PS - PowerSaver::resume(); - } + if ((mBackgroundAudio) && (mStopBackgroundAudio)) { + mBackgroundAudio->stop(); + mBackgroundAudio.reset(); + // If we were playing audio, we paused PowerSaver. + PowerSaver::resume(); + } - // so that we stop the background audio next time, unless we're restarting the screensaver - mStopBackgroundAudio = true; + // So that we stop the background audio next time, unless we're restarting the screensaver. + mStopBackgroundAudio = true; - delete mVideoScreensaver; - mVideoScreensaver = NULL; - delete mImageScreensaver; - mImageScreensaver = NULL; + delete mVideoScreensaver; + mVideoScreensaver = nullptr; + delete mImageScreensaver; + mImageScreensaver = nullptr; - // we need this to loop through different videos - mState = STATE_INACTIVE; - PowerSaver::runningScreenSaver(false); + // We need this to loop through different videos. + mState = STATE_INACTIVE; + PowerSaver::runningScreenSaver(false); } void SystemScreenSaver::renderScreenSaver() { - std::string screensaver_behavior = Settings::getInstance()->getString("ScreenSaverBehavior"); - if (mVideoScreensaver && screensaver_behavior == "random video") - { - // Render black background - Renderer::setMatrix(Transform4x4f::Identity()); - Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0x000000FF, 0x000000FF); + std::string screensaver_behavior = Settings::getInstance()->getString("ScreenSaverBehavior"); + if (mVideoScreensaver && screensaver_behavior == "random video") { + // Render black background. + Renderer::setMatrix(Transform4x4f::Identity()); + Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), + Renderer::getScreenHeight(), 0x000000FF, 0x000000FF); - // Only render the video if the state requires it - if ((int)mState >= STATE_FADE_IN_VIDEO) - { - Transform4x4f transform = Transform4x4f::Identity(); - mVideoScreensaver->render(transform); - } - } - else if (mImageScreensaver && screensaver_behavior == "slideshow") - { - // Render black background - Renderer::setMatrix(Transform4x4f::Identity()); - Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0x000000FF, 0x000000FF); + // Only render the video if the state requires it. + if ((int)mState >= STATE_FADE_IN_VIDEO) { + Transform4x4f transform = Transform4x4f::Identity(); + mVideoScreensaver->render(transform); + } + } + else if (mImageScreensaver && screensaver_behavior == "slideshow") { + // Render black background. + Renderer::setMatrix(Transform4x4f::Identity()); + Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), + Renderer::getScreenHeight(), 0x000000FF, 0x000000FF); - // Only render the video if the state requires it - if ((int)mState >= STATE_FADE_IN_VIDEO) - { - if (mImageScreensaver->hasImage()) - { - mImageScreensaver->setOpacity(255- (unsigned char) (mOpacity * 255)); + // Only render the video if the state requires it. + if ((int)mState >= STATE_FADE_IN_VIDEO) { + if (mImageScreensaver->hasImage()) { + mImageScreensaver->setOpacity(255- (unsigned char) (mOpacity * 255)); - Transform4x4f transform = Transform4x4f::Identity(); - mImageScreensaver->render(transform); - } - } + Transform4x4f transform = Transform4x4f::Identity(); + mImageScreensaver->render(transform); + } + } - // Check if we need to restart the background audio - if ((mBackgroundAudio) && (Settings::getInstance()->getString("SlideshowScreenSaverBackgroundAudioFile") != "")) - { - if (!mBackgroundAudio->isPlaying()) - { - mBackgroundAudio->play(); - } - } - } - else if (mState != STATE_INACTIVE) - { - Renderer::setMatrix(Transform4x4f::Identity()); - unsigned char color = screensaver_behavior == "dim" ? 0x000000A0 : 0x000000FF; - Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), color, color); - } + // Check if we need to restart the background audio. + if ((mBackgroundAudio) && (Settings::getInstance()-> + getString("SlideshowScreenSaverBackgroundAudioFile") != "")) { + if (!mBackgroundAudio->isPlaying()) + mBackgroundAudio->play(); + } + } + else if (mState != STATE_INACTIVE) { + Renderer::setMatrix(Transform4x4f::Identity()); + unsigned char color = screensaver_behavior == "dim" ? 0x000000A0 : 0x000000FF; + Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), + Renderer::getScreenHeight(), color, color); + } } unsigned long SystemScreenSaver::countGameListNodes(const char *nodeName) { - unsigned long nodeCount = 0; - std::vector::const_iterator it; - for (it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); ++it) - { - // We only want nodes from game systems that are not collections - if (!(*it)->isGameSystem() || (*it)->isCollection()) - continue; + unsigned long nodeCount = 0; + std::vector::const_iterator it; + for (it = SystemData::sSystemVector.cbegin(); + it != SystemData::sSystemVector.cend(); ++it) { + // We only want nodes from game systems that are not collections. + if (!(*it)->isGameSystem() || (*it)->isCollection()) + continue; - FileData* rootFileData = (*it)->getRootFolder(); + FileData* rootFileData = (*it)->getRootFolder(); - FileType type = GAME; - std::vector allFiles = rootFileData->getFilesRecursive(type, true); - std::vector::const_iterator itf; // declare an iterator to a vector of strings + FileType type = GAME; + std::vector allFiles = rootFileData->getFilesRecursive(type, true); + std::vector::const_iterator itf; // Declare an iterator to a vector of strings. - for(itf=allFiles.cbegin() ; itf < allFiles.cend(); itf++) { - if ((strcmp(nodeName, "video") == 0 && (*itf)->getVideoPath() != "") || - (strcmp(nodeName, "image") == 0 && (*itf)->getImagePath() != "")) - { - nodeCount++; - } - } - } - return nodeCount; + for (itf=allFiles.cbegin() ; itf < allFiles.cend(); itf++) { + if ((strcmp(nodeName, "video") == 0 && (*itf)->getVideoPath() != "") || + (strcmp(nodeName, "image") == 0 && (*itf)->getImagePath() != "")) + nodeCount++; + } + } + return nodeCount; } void SystemScreenSaver::countVideos() { - if (!mVideosCounted) - { - mVideoCount = countGameListNodes("video"); - mVideosCounted = true; - } + if (!mVideosCounted) { + mVideoCount = countGameListNodes("video"); + mVideosCounted = true; + } } void SystemScreenSaver::countImages() { - if (!mImagesCounted) - { - mImageCount = countGameListNodes("image"); - mImagesCounted = true; - } + if (!mImagesCounted) { + mImageCount = countGameListNodes("image"); + mImagesCounted = true; + } } -void SystemScreenSaver::pickGameListNode(unsigned long index, const char *nodeName, std::string& path) +void SystemScreenSaver::pickGameListNode(unsigned long index, + const char *nodeName, std::string& path) { - std::vector::const_iterator it; - for (it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); ++it) - { - // We only want nodes from game systems that are not collections - if (!(*it)->isGameSystem() || (*it)->isCollection()) - continue; + std::vector::const_iterator it; + for (it = SystemData::sSystemVector.cbegin(); + it != SystemData::sSystemVector.cend(); ++it) { + // We only want nodes from game systems that are not collections. + if (!(*it)->isGameSystem() || (*it)->isCollection()) + continue; - FileData* rootFileData = (*it)->getRootFolder(); + FileData* rootFileData = (*it)->getRootFolder(); - FileType type = GAME; - std::vector allFiles = rootFileData->getFilesRecursive(type, true); - std::vector::const_iterator itf; // declare an iterator to a vector of strings + FileType type = GAME; + std::vector allFiles = rootFileData->getFilesRecursive(type, true); + std::vector::const_iterator itf; // Declare an iterator to a vector of strings. - for(itf=allFiles.cbegin() ; itf < allFiles.cend(); itf++) { - if ((strcmp(nodeName, "video") == 0 && (*itf)->getVideoPath() != "") || - (strcmp(nodeName, "image") == 0 && (*itf)->getImagePath() != "")) - { - if (index-- == 0) - { - // We have it - path = ""; - if (strcmp(nodeName, "video") == 0) - path = (*itf)->getVideoPath(); - else if (strcmp(nodeName, "image") == 0) - path = (*itf)->getImagePath(); - mSystemName = (*it)->getFullName(); - mGameName = (*itf)->getName(); - mCurrentGame = (*itf); + for (itf=allFiles.cbegin() ; itf < allFiles.cend(); itf++) { + if ((strcmp(nodeName, "video") == 0 && (*itf)->getVideoPath() != "") || + (strcmp(nodeName, "image") == 0 && (*itf)->getImagePath() != "")) { + if (index-- == 0) { + // We have it. + path = ""; + if (strcmp(nodeName, "video") == 0) + path = (*itf)->getVideoPath(); + else if (strcmp(nodeName, "image") == 0) + path = (*itf)->getImagePath(); + mSystemName = (*it)->getFullName(); + mGameName = (*itf)->getName(); + mCurrentGame = (*itf); - // end of getting FileData - if (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never") - writeSubtitle(mGameName.c_str(), mSystemName.c_str(), - (Settings::getInstance()->getString("ScreenSaverGameInfo") == "always")); - return; - } - } - } - } + // End of getting FileData. + if (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never") + writeSubtitle(mGameName.c_str(), mSystemName.c_str(), + (Settings::getInstance()->getString("ScreenSaverGameInfo") == + "always")); + return; + } + } + } + } } void SystemScreenSaver::pickRandomVideo(std::string& path) { - countVideos(); - mCurrentGame = NULL; - if (mVideoCount > 0) - { - int video = (int)(((float)rand() / float(RAND_MAX)) * (float)mVideoCount); + countVideos(); + mCurrentGame = nullptr; - pickGameListNode(video, "video", path); - } + if (mVideoCount > 0) { + int video = (int)(((float)rand() / float(RAND_MAX)) * (float)mVideoCount); + pickGameListNode(video, "video", path); + } } void SystemScreenSaver::pickRandomGameListImage(std::string& path) { - countImages(); - mCurrentGame = NULL; - if (mImageCount > 0) - { - int image = (int)(((float)rand() / float(RAND_MAX)) * (float)mImageCount); + countImages(); + mCurrentGame = nullptr; - pickGameListNode(image, "image", path); - } + if (mImageCount > 0) { + int image = (int)(((float)rand() / float(RAND_MAX)) * (float)mImageCount); + pickGameListNode(image, "image", path); + } } void SystemScreenSaver::pickRandomCustomImage(std::string& path) { - std::string imageDir = Settings::getInstance()->getString("SlideshowScreenSaverImageDir"); - if ((imageDir != "") && (Utils::FileSystem::exists(imageDir))) - { - std::string imageFilter = Settings::getInstance()->getString("SlideshowScreenSaverImageFilter"); - std::vector matchingFiles; - Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(imageDir, Settings::getInstance()->getBool("SlideshowScreenSaverRecurse")); + std::string imageDir = Settings::getInstance()->getString("SlideshowScreenSaverImageDir"); - for(Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); it != dirContent.cend(); ++it) - { - if (Utils::FileSystem::isRegularFile(*it)) - { - // If the image filter is empty, or the file extension is in the filter string, - // add it to the matching files list - if ((imageFilter.length() <= 0) || - (imageFilter.find(Utils::FileSystem::getExtension(*it)) != std::string::npos)) - { - matchingFiles.push_back(*it); - } - } - } + if ((imageDir != "") && (Utils::FileSystem::exists(imageDir))) { + std::string imageFilter = Settings::getInstance()-> + getString("SlideshowScreenSaverImageFilter"); + std::vector matchingFiles; + Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent( + imageDir, Settings::getInstance()->getBool("SlideshowScreenSaverRecurse")); - int fileCount = (int)matchingFiles.size(); - if (fileCount > 0) - { - // get a random index in the range 0 to fileCount (exclusive) - int randomIndex = rand() % fileCount; - path = matchingFiles[randomIndex]; - } - else - { - LOG(LogError) << "Slideshow Screensaver - No image files found\n"; - } - } - else - { - LOG(LogError) << "Slideshow Screensaver - Image directory does not exist: " << imageDir << "\n"; - } + for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); + it != dirContent.cend(); ++it) { + if (Utils::FileSystem::isRegularFile(*it)) { + // If the image filter is empty, or the file extension is in the filter + // string, add it to the matching files list. + if ((imageFilter.length() <= 0) || (imageFilter.find( + Utils::FileSystem::getExtension(*it)) != std::string::npos)) + matchingFiles.push_back(*it); + } + } + + int fileCount = (int)matchingFiles.size(); + if (fileCount > 0) { + // Get a random index in the range 0 to fileCount (exclusive). + int randomIndex = rand() % fileCount; + path = matchingFiles[randomIndex]; + } + else { + LOG(LogError) << "Slideshow Screensaver - No image files found\n"; + } + } + else { + LOG(LogError) << "Slideshow Screensaver - Image directory does not exist: " << + imageDir << "\n"; + } } void SystemScreenSaver::update(int deltaTime) { - // Use this to update the fade value for the current fade stage - if (mState == STATE_FADE_OUT_WINDOW) - { - mOpacity += (float)deltaTime / FADE_TIME; - if (mOpacity >= 1.0f) - { - mOpacity = 1.0f; + // Use this to update the fade value for the current fade stage. + if (mState == STATE_FADE_OUT_WINDOW) { + mOpacity += (float)deltaTime / FADE_TIME; + if (mOpacity >= 1.0f) { + mOpacity = 1.0f; - // Update to the next state - mState = STATE_FADE_IN_VIDEO; - } - } - else if (mState == STATE_FADE_IN_VIDEO) - { - mOpacity -= (float)deltaTime / FADE_TIME; - if (mOpacity <= 0.0f) - { - mOpacity = 0.0f; - // Update to the next state - mState = STATE_SCREENSAVER_ACTIVE; - } - } - else if (mState == STATE_SCREENSAVER_ACTIVE) - { - // Update the timer that swaps the videos - mTimer += deltaTime; - if (mTimer > mVideoChangeTime) - { - nextVideo(); - } - } + // Update to the next state. + mState = STATE_FADE_IN_VIDEO; + } + } + else if (mState == STATE_FADE_IN_VIDEO) { + mOpacity -= (float)deltaTime / FADE_TIME; + if (mOpacity <= 0.0f) { + mOpacity = 0.0f; + // Update to the next state. + mState = STATE_SCREENSAVER_ACTIVE; + } + } + else if (mState == STATE_SCREENSAVER_ACTIVE) { + // Update the timer that swaps the videos. + mTimer += deltaTime; + if (mTimer > mVideoChangeTime) + nextVideo(); + } - // If we have a loaded video then update it - if (mVideoScreensaver) - mVideoScreensaver->update(deltaTime); - if (mImageScreensaver) - mImageScreensaver->update(deltaTime); + // If we have a loaded video then update it. + if (mVideoScreensaver) + mVideoScreensaver->update(deltaTime); + if (mImageScreensaver) + mImageScreensaver->update(deltaTime); } void SystemScreenSaver::nextVideo() { - mStopBackgroundAudio = false; - stopScreenSaver(); - startScreenSaver(); - mState = STATE_SCREENSAVER_ACTIVE; + mStopBackgroundAudio = false; + stopScreenSaver(); + startScreenSaver(); + mState = STATE_SCREENSAVER_ACTIVE; } FileData* SystemScreenSaver::getCurrentGame() { - return mCurrentGame; + return mCurrentGame; } void SystemScreenSaver::launchGame() { - if (mCurrentGame != NULL) - { - // launching Game - ViewController::get()->goToGameList(mCurrentGame->getSystem()); - IGameListView* view = ViewController::get()->getGameListView(mCurrentGame->getSystem()).get(); - view->setCursor(mCurrentGame); - if (Settings::getInstance()->getBool("ScreenSaverControls")) - { - view->launch(mCurrentGame); - } - } + if (mCurrentGame != nullptr) { + // Launching game + ViewController::get()->goToGameList(mCurrentGame->getSystem()); + IGameListView* view = ViewController::get()-> + getGameListView(mCurrentGame->getSystem()).get(); + view->setCursor(mCurrentGame); + if (Settings::getInstance()->getBool("ScreenSaverControls")) + view->launch(mCurrentGame); + } } diff --git a/es-app/src/SystemScreenSaver.h b/es-app/src/SystemScreenSaver.h index a26f4407e..4f5461a44 100644 --- a/es-app/src/SystemScreenSaver.h +++ b/es-app/src/SystemScreenSaver.h @@ -1,3 +1,10 @@ +// +// SystemScreenSaver.h +// +// Screensaver, supporting the following modes: +// Dim, black, video, slideshow. +// + #pragma once #ifndef ES_APP_SYSTEM_SCREEN_SAVER_H #define ES_APP_SYSTEM_SCREEN_SAVER_H @@ -8,60 +15,60 @@ class ImageComponent; class Sound; class VideoComponent; -// Screensaver implementation for main window +// Screensaver implementation for main window. class SystemScreenSaver : public Window::ScreenSaver { public: - SystemScreenSaver(Window* window); - virtual ~SystemScreenSaver(); + SystemScreenSaver(Window* window); + virtual ~SystemScreenSaver(); - virtual void startScreenSaver(); - virtual void stopScreenSaver(); - virtual void nextVideo(); - virtual void renderScreenSaver(); - virtual bool allowSleep(); - virtual void update(int deltaTime); - virtual bool isScreenSaverActive(); + virtual void startScreenSaver(); + virtual void stopScreenSaver(); + virtual void nextVideo(); + virtual void renderScreenSaver(); + virtual bool allowSleep(); + virtual void update(int deltaTime); + virtual bool isScreenSaverActive(); - virtual FileData* getCurrentGame(); - virtual void launchGame(); - inline virtual void resetCounts() { mVideosCounted = false; mImagesCounted = false; }; + virtual FileData* getCurrentGame(); + virtual void launchGame(); + inline virtual void resetCounts() { mVideosCounted = false; mImagesCounted = false; }; private: - unsigned long countGameListNodes(const char *nodeName); - void countVideos(); - void countImages(); - void pickGameListNode(unsigned long index, const char *nodeName, std::string& path); - void pickRandomVideo(std::string& path); - void pickRandomGameListImage(std::string& path); - void pickRandomCustomImage(std::string& path); + unsigned long countGameListNodes(const char *nodeName); + void countVideos(); + void countImages(); + void pickGameListNode(unsigned long index, const char *nodeName, std::string& path); + void pickRandomVideo(std::string& path); + void pickRandomGameListImage(std::string& path); + void pickRandomCustomImage(std::string& path); - void input(InputConfig* config, Input input); + void input(InputConfig* config, Input input); - enum STATE { - STATE_INACTIVE, - STATE_FADE_OUT_WINDOW, - STATE_FADE_IN_VIDEO, - STATE_SCREENSAVER_ACTIVE - }; + enum STATE { + STATE_INACTIVE, + STATE_FADE_OUT_WINDOW, + STATE_FADE_IN_VIDEO, + STATE_SCREENSAVER_ACTIVE + }; private: - bool mVideosCounted; - unsigned long mVideoCount; - VideoComponent* mVideoScreensaver; - bool mImagesCounted; - unsigned long mImageCount; - ImageComponent* mImageScreensaver; - Window* mWindow; - STATE mState; - float mOpacity; - int mTimer; - FileData* mCurrentGame; - std::string mGameName; - std::string mSystemName; - int mVideoChangeTime; - std::shared_ptr mBackgroundAudio; - bool mStopBackgroundAudio; + bool mVideosCounted; + unsigned long mVideoCount; + VideoComponent* mVideoScreensaver; + bool mImagesCounted; + unsigned long mImageCount; + ImageComponent* mImageScreensaver; + Window* mWindow; + STATE mState; + float mOpacity; + int mTimer; + FileData* mCurrentGame; + std::string mGameName; + std::string mSystemName; + int mVideoChangeTime; + std::shared_ptr mBackgroundAudio; + bool mStopBackgroundAudio; }; #endif // ES_APP_SYSTEM_SCREEN_SAVER_H diff --git a/es-app/src/VolumeControl.cpp b/es-app/src/VolumeControl.cpp index 9153523ed..68ca55b93 100644 --- a/es-app/src/VolumeControl.cpp +++ b/es-app/src/VolumeControl.cpp @@ -1,3 +1,9 @@ +// +// VolumeControl.cpp +// +// Controls system audio volume. +// + #include "VolumeControl.h" #include "math/Misc.h" @@ -8,399 +14,372 @@ #endif #if defined(__linux__) - #if defined(_RPI_) || defined(_VERO4K_) - const char * VolumeControl::mixerName = "PCM"; - #else - const char * VolumeControl::mixerName = "Master"; - #endif - const char * VolumeControl::mixerCard = "default"; +#if defined(_RPI_) || defined(_VERO4K_) +const char * VolumeControl::mixerName = "PCM"; +#else +const char * VolumeControl::mixerName = "Master"; +#endif +const char * VolumeControl::mixerCard = "default"; #endif std::weak_ptr VolumeControl::sInstance; - VolumeControl::VolumeControl() - : originalVolume(0), internalVolume(0) -#if defined (__APPLE__) - #error TODO: Not implemented for MacOS yet!!! -#elif defined(__linux__) - , mixerIndex(0), mixerHandle(nullptr), mixerElem(nullptr), mixerSelemId(nullptr) -#elif defined(WIN32) || defined(_WIN32) - , mixerHandle(nullptr), endpointVolume(nullptr) -#endif + : originalVolume(0), + internalVolume(0) + #if defined (__APPLE__) + #error TODO: Not implemented for MacOS yet!!! + #elif defined(__linux__) + , mixerIndex(0), + mixerHandle(nullptr), + mixerElem(nullptr), + mixerSelemId(nullptr) + #elif defined(WIN32) || defined(_WIN32) + , mixerHandle(nullptr), + endpointVolume(nullptr) + #endif { - init(); + init(); - //get original volume levels for system - originalVolume = getVolume(); + // Get original volume levels for system. + originalVolume = getVolume(); } -VolumeControl::VolumeControl(const VolumeControl & right): - originalVolume(0), internalVolume(0) -#if defined (__APPLE__) - #error TODO: Not implemented for MacOS yet!!! -#elif defined(__linux__) - , mixerIndex(0), mixerHandle(nullptr), mixerElem(nullptr), mixerSelemId(nullptr) -#elif defined(WIN32) || defined(_WIN32) - , mixerHandle(nullptr), endpointVolume(nullptr) -#endif +VolumeControl::VolumeControl( + const VolumeControl & right): + originalVolume(0), + internalVolume(0) + #if defined (__APPLE__) + #error TODO: Not implemented for MacOS yet!!! + #elif defined(__linux__) + , mixerIndex(0), + mixerHandle(nullptr), + mixerElem(nullptr), + mixerSelemId(nullptr) + #elif defined(WIN32) || defined(_WIN32) + , mixerHandle(nullptr), + endpointVolume(nullptr) + #endif { - (void)right; - sInstance = right.sInstance; + (void)right; + sInstance = right.sInstance; } -VolumeControl & VolumeControl::operator=(const VolumeControl & right) +VolumeControl & VolumeControl::operator=(const VolumeControl& right) { - if (this != &right) { - sInstance = right.sInstance; - } + if (this != &right) + sInstance = right.sInstance; - return *this; + return *this; } VolumeControl::~VolumeControl() { - //set original volume levels for system - //setVolume(originalVolume); + // Set original volume levels for system. + //setVolume(originalVolume); - deinit(); + deinit(); } std::shared_ptr & VolumeControl::getInstance() { - //check if an VolumeControl instance is already created, if not create one - static std::shared_ptr sharedInstance = sInstance.lock(); - if (sharedInstance == nullptr) { - sharedInstance.reset(new VolumeControl); - sInstance = sharedInstance; - } - return sharedInstance; + // Check if an VolumeControl instance is already created, if not create one. + static std::shared_ptr sharedInstance = sInstance.lock(); + if (sharedInstance == nullptr) { + sharedInstance.reset(new VolumeControl); + sInstance = sharedInstance; + } + return sharedInstance; } void VolumeControl::init() { - //initialize audio mixer interface -#if defined (__APPLE__) - #error TODO: Not implemented for MacOS yet!!! -#elif defined(__linux__) - //try to open mixer device - if (mixerHandle == nullptr) - { - // Allow users to override the AudioCard and MixerName in es_settings.cfg - mixerCard = Settings::getInstance()->getString("AudioCard").c_str(); - mixerName = Settings::getInstance()->getString("AudioDevice").c_str(); + // Initialize audio mixer interface. + #if defined (__APPLE__) + #error TODO: Not implemented for MacOS yet!!! + #elif defined(__linux__) + // Try to open mixer device. + if (mixerHandle == nullptr) { + // Allow user to override the AudioCard and AudioDevice in es_settings.cfg. + mixerCard = Settings::getInstance()->getString("AudioCard").c_str(); + mixerName = Settings::getInstance()->getString("AudioDevice").c_str(); - snd_mixer_selem_id_alloca(&mixerSelemId); - //sets simple-mixer index and name - snd_mixer_selem_id_set_index(mixerSelemId, mixerIndex); - snd_mixer_selem_id_set_name(mixerSelemId, mixerName); - //open mixer - if (snd_mixer_open(&mixerHandle, 0) >= 0) - { - LOG(LogDebug) << "VolumeControl::init() - Opened ALSA mixer"; - //ok. attach to defualt card - if (snd_mixer_attach(mixerHandle, mixerCard) >= 0) - { - LOG(LogDebug) << "VolumeControl::init() - Attached to default card"; - //ok. register simple element class - if (snd_mixer_selem_register(mixerHandle, NULL, NULL) >= 0) - { - LOG(LogDebug) << "VolumeControl::init() - Registered simple element class"; - //ok. load registered elements - if (snd_mixer_load(mixerHandle) >= 0) - { - LOG(LogDebug) << "VolumeControl::init() - Loaded mixer elements"; - //ok. find elements now - mixerElem = snd_mixer_find_selem(mixerHandle, mixerSelemId); - if (mixerElem != nullptr) - { - //wohoo. good to go... - LOG(LogDebug) << "VolumeControl::init() - Mixer initialized"; - } - else - { - LOG(LogError) << "VolumeControl::init() - Failed to find mixer elements!"; - snd_mixer_close(mixerHandle); - mixerHandle = nullptr; - } - } - else - { - LOG(LogError) << "VolumeControl::init() - Failed to load mixer elements!"; - snd_mixer_close(mixerHandle); - mixerHandle = nullptr; - } - } - else - { - LOG(LogError) << "VolumeControl::init() - Failed to register simple element class!"; - snd_mixer_close(mixerHandle); - mixerHandle = nullptr; - } - } - else - { - LOG(LogError) << "VolumeControl::init() - Failed to attach to default card!"; - snd_mixer_close(mixerHandle); - mixerHandle = nullptr; - } - } - else - { - LOG(LogError) << "VolumeControl::init() - Failed to open ALSA mixer!"; - } - } -#elif defined(WIN32) || defined(_WIN32) - //get windows version information - OSVERSIONINFOEXA osVer = {sizeof(OSVERSIONINFO)}; - ::GetVersionExA(reinterpret_cast(&osVer)); - //check windows version - if(osVer.dwMajorVersion < 6) - { - //Windows older than Vista. use mixer API. open default mixer - if (mixerHandle == nullptr) - { - if (mixerOpen(&mixerHandle, 0, NULL, 0, 0) == MMSYSERR_NOERROR) - { - //retrieve info on the volume slider control for the "Speaker Out" line - MIXERLINECONTROLS mixerLineControls; - mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS); - mixerLineControls.dwLineID = 0xFFFF0000; //Id of "Speaker Out" line - mixerLineControls.cControls = 1; - //mixerLineControls.dwControlID = 0x00000000; //Id of "Speaker Out" line's volume slider - mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; //Get volume control - mixerLineControls.pamxctrl = &mixerControl; - mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL); - if (mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR) - { - LOG(LogError) << "VolumeControl::getVolume() - Failed to get mixer volume control!"; - mixerClose(mixerHandle); - mixerHandle = nullptr; - } - } - else - { - LOG(LogError) << "VolumeControl::init() - Failed to open mixer!"; - } - } - } - else - { - //Windows Vista or above. use EndpointVolume API. get device enumerator - if (endpointVolume == nullptr) - { - CoInitialize(nullptr); - IMMDeviceEnumerator * deviceEnumerator = nullptr; - CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator); - if (deviceEnumerator != nullptr) - { - //get default endpoint - IMMDevice * defaultDevice = nullptr; - deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice); - if (defaultDevice != nullptr) - { - //retrieve endpoint volume - defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr, (LPVOID *)&endpointVolume); - if (endpointVolume == nullptr) - { - LOG(LogError) << "VolumeControl::init() - 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!"; - } - //release device enumerator. we don't need it anymore - deviceEnumerator->Release(); - } - else - { - LOG(LogError) << "VolumeControl::init() - Failed to get audio endpoint enumerator!"; - CoUninitialize(); - } - } - } -#endif + snd_mixer_selem_id_alloca(&mixerSelemId); + // Sets simple-mixer index and name. + snd_mixer_selem_id_set_index(mixerSelemId, mixerIndex); + snd_mixer_selem_id_set_name(mixerSelemId, mixerName); + // Open mixer. + if (snd_mixer_open(&mixerHandle, 0) >= 0) { + LOG(LogDebug) << "VolumeControl::init() - Opened ALSA mixer"; + // Ok, attach to defualt card. + if (snd_mixer_attach(mixerHandle, mixerCard) >= 0) { + LOG(LogDebug) << "VolumeControl::init() - Attached to default card"; + // Ok, register simple element class. + if (snd_mixer_selem_register(mixerHandle, nullptr, nullptr) >= 0) { + LOG(LogDebug) << "VolumeControl::init() - Registered simple element class"; + // Ok, load registered elements. + if (snd_mixer_load(mixerHandle) >= 0) { + LOG(LogDebug) << "VolumeControl::init() - Loaded mixer elements"; + // Ok, find elements now. + mixerElem = snd_mixer_find_selem(mixerHandle, mixerSelemId); + if (mixerElem != nullptr) { + // Wohoo. good to go... + LOG(LogDebug) << "VolumeControl::init() - Mixer initialized"; + } + else { + LOG(LogError) << + "VolumeControl::init() - Failed to find mixer elements!"; + snd_mixer_close(mixerHandle); + mixerHandle = nullptr; + } + } + else { + LOG(LogError) << "VolumeControl::init() - Failed to load mixer elements!"; + snd_mixer_close(mixerHandle); + mixerHandle = nullptr; + } + } + else { + LOG(LogError) << + "VolumeControl::init() - Failed to register simple element class!"; + snd_mixer_close(mixerHandle); + mixerHandle = nullptr; + } + } + else { + LOG(LogError) << "VolumeControl::init() - Failed to attach to default card!"; + snd_mixer_close(mixerHandle); + mixerHandle = nullptr; + } + } + else { + LOG(LogError) << "VolumeControl::init() - Failed to open ALSA mixer!"; + } + } + #elif defined(WIN32) || defined(_WIN32) + // Get windows version information. + OSVERSIONINFOEXA osVer = {sizeof(OSVERSIONINFO)}; + ::GetVersionExA(reinterpret_cast(&osVer)); + // Check windows version. + if (osVer.dwMajorVersion < 6) { + // Windows older than Vista. use mixer API. open default mixer. + if (mixerHandle == nullptr) { + if (mixerOpen(&mixerHandle, 0, nullptr, 0, 0) == MMSYSERR_NOERROR) { + // Retrieve info on the volume slider control for the "Speaker Out" line. + MIXERLINECONTROLS mixerLineControls; + mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS); + mixerLineControls.dwLineID = 0xFFFF0000; // Id of "Speaker Out" line. + mixerLineControls.cControls = 1; + // Id of "Speaker Out" line's volume slider. + //mixerLineControls.dwControlID = 0x00000000; + //Get volume control. + mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; + mixerLineControls.pamxctrl = &mixerControl; + mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL); + if (mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls, + MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR) { + LOG(LogError) << + "VolumeControl::getVolume() - Failed to get mixer volume control!"; + mixerClose(mixerHandle); + mixerHandle = nullptr; + } + } + else { + LOG(LogError) << "VolumeControl::init() - Failed to open mixer!"; + } + } + } + else { + // Windows Vista or above. use EndpointVolume API. get device enumerator. + if (endpointVolume == nullptr) { + CoInitialize(nullptr); + IMMDeviceEnumerator * deviceEnumerator = nullptr; + CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, + __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator); + if (deviceEnumerator != nullptr) { + // Get default endpoint. + IMMDevice * defaultDevice = nullptr; + deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice); + if (defaultDevice != nullptr) { + // Retrieve endpoint volume. + defaultDevice->Activate(__uuidof(IAudioEndpointVolume), + CLSCTX_INPROC_SERVER, nullptr, (LPVOID *)&endpointVolume); + if (endpointVolume == nullptr) + LOG(LogError) << "VolumeControl::init() - " + "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!"; + } + // Release device enumerator. we don't need it anymore. + deviceEnumerator->Release(); + } + else { + LOG(LogError) << "VolumeControl::init() - Failed to get audio endpoint enumerator!"; + CoUninitialize(); + } + } + } + #endif } void VolumeControl::deinit() { - //deinitialize audio mixer interface -#if defined (__APPLE__) - #error TODO: Not implemented for MacOS yet!!! -#elif defined(__linux__) - if (mixerHandle != nullptr) { - snd_mixer_detach(mixerHandle, mixerCard); - snd_mixer_free(mixerHandle); - snd_mixer_close(mixerHandle); - mixerHandle = nullptr; - mixerElem = nullptr; - } -#elif defined(WIN32) || defined(_WIN32) - if (mixerHandle != nullptr) { - mixerClose(mixerHandle); - mixerHandle = nullptr; - } - else if (endpointVolume != nullptr) { - endpointVolume->Release(); - endpointVolume = nullptr; - CoUninitialize(); - } -#endif + // Deinitialize audio mixer interface. + #if defined (__APPLE__) + #error TODO: Not implemented for MacOS yet!!! + #elif defined(__linux__) + if (mixerHandle != nullptr) { + snd_mixer_detach(mixerHandle, mixerCard); + snd_mixer_free(mixerHandle); + snd_mixer_close(mixerHandle); + mixerHandle = nullptr; + mixerElem = nullptr; + } + #elif defined(WIN32) || defined(_WIN32) + if (mixerHandle != nullptr) { + mixerClose(mixerHandle); + mixerHandle = nullptr; + } + else if (endpointVolume != nullptr) { + endpointVolume->Release(); + endpointVolume = nullptr; + CoUninitialize(); + } + #endif } int VolumeControl::getVolume() const { - int volume = 0; + int volume = 0; -#if defined (__APPLE__) - #error TODO: Not implemented for MacOS yet!!! -#elif defined(__linux__) - if (mixerElem != nullptr) - { - //get volume range - long minVolume; - long maxVolume; - if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) - { - //ok. now get volume - long rawVolume; - if (snd_mixer_selem_get_playback_volume(mixerElem, SND_MIXER_SCHN_MONO, &rawVolume) == 0) - { - //worked. bring into range 0-100 - rawVolume -= minVolume; - if (rawVolume > 0) - { - volume = (rawVolume * 100.0) / (maxVolume - minVolume) + 0.5; - } - //else volume = 0; - } - else - { - LOG(LogError) << "VolumeControl::getVolume() - Failed to get mixer volume!"; - } - } - else - { - LOG(LogError) << "VolumeControl::getVolume() - Failed to get volume range!"; - } - } -#elif defined(WIN32) || defined(_WIN32) - if (mixerHandle != nullptr) - { - //Windows older than Vista. use mixer API. get volume from line control - MIXERCONTROLDETAILS_UNSIGNED value; - MIXERCONTROLDETAILS mixerControlDetails; - mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); - mixerControlDetails.dwControlID = mixerControl.dwControlID; - mixerControlDetails.cChannels = 1; //always 1 for a MIXERCONTROL_CONTROLF_UNIFORM control - mixerControlDetails.cMultipleItems = 0; //always 0 except for a MIXERCONTROL_CONTROLF_MULTIPLE control - mixerControlDetails.paDetails = &value; - mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); - if (mixerGetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_GETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR) - { - volume = (int)Math::round((value.dwValue * 100) / 65535.0f); - } - else - { - LOG(LogError) << "VolumeControl::getVolume() - Failed to get mixer volume!"; - } - } - else if (endpointVolume != nullptr) - { - //Windows Vista or above. use EndpointVolume API - float floatVolume = 0.0f; //0-1 - if (endpointVolume->GetMasterVolumeLevelScalar(&floatVolume) == S_OK) - { - volume = (int)Math::round(floatVolume * 100.0f); - LOG(LogInfo) << " getting volume as " << volume << " ( from float " << floatVolume << ")"; - } - else - { - LOG(LogError) << "VolumeControl::getVolume() - Failed to get master volume!"; - } + #if defined (__APPLE__) + #error TODO: Not implemented for MacOS yet!!! + #elif defined(__linux__) + if (mixerElem != nullptr) { + // Get volume range. + long minVolume; + long maxVolume; + if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) { + // Ok, now get volume. + long rawVolume; + if (snd_mixer_selem_get_playback_volume(mixerElem, + SND_MIXER_SCHN_MONO, &rawVolume) == 0) { + // Worked. bring into range 0-100. + rawVolume -= minVolume; + if (rawVolume > 0) + volume = (rawVolume * 100.0) / (maxVolume - minVolume) + 0.5; + //else + // volume = 0; + } + else { + LOG(LogError) << "VolumeControl::getVolume() - Failed to get mixer volume!"; + } + } + else { + LOG(LogError) << "VolumeControl::getVolume() - Failed to get volume range!"; + } + } + #elif defined(WIN32) || defined(_WIN32) + if (mixerHandle != nullptr) { + // Windows older than Vista. use mixer API. get volume from line control. + MIXERCONTROLDETAILS_UNSIGNED value; + MIXERCONTROLDETAILS mixerControlDetails; + mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); + mixerControlDetails.dwControlID = mixerControl.dwControlID; + // Always 1 for a MIXERCONTROL_CONTROLF_UNIFORM control. + mixerControlDetails.cChannels = 1; + // Always 0 except for a MIXERCONTROL_CONTROLF_MULTIPLE control. + mixerControlDetails.cMultipleItems = 0; + mixerControlDetails.paDetails = &value; + mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); + if (mixerGetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, + MIXER_GETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR) + volume = (int)Math::round((value.dwValue * 100) / 65535.0f); + else + LOG(LogError) << "VolumeControl::getVolume() - Failed to get mixer volume!"; + } + else if (endpointVolume != nullptr) { + // Windows Vista or above. use EndpointVolume API. + float floatVolume = 0.0f; // 0-1 + if (endpointVolume->GetMasterVolumeLevelScalar(&floatVolume) == S_OK) { + volume = (int)Math::round(floatVolume * 100.0f); + LOG(LogInfo) << " getting volume as " << volume << + " ( from float " << floatVolume << ")"; + } + else { + LOG(LogError) << "VolumeControl::getVolume() - Failed to get master volume!"; + } + } + #endif - } -#endif - //clamp to 0-100 range - if (volume < 0) - { - volume = 0; - } - if (volume > 100) - { - volume = 100; - } - return volume; + // Clamp to 0-100 range. + if (volume < 0) + volume = 0; + if (volume > 100) + volume = 100; + return volume; } void VolumeControl::setVolume(int volume) { - //clamp to 0-100 range - if (volume < 0) - { - volume = 0; - } - if (volume > 100) - { - volume = 100; - } - //store values in internal variables - internalVolume = volume; -#if defined (__APPLE__) - #error TODO: Not implemented for MacOS yet!!! -#elif defined(__linux__) - if (mixerElem != nullptr) - { - //get volume range - long minVolume; - long maxVolume; - if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) - { - //ok. 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) - { - LOG(LogError) << "VolumeControl::getVolume() - Failed to set mixer volume!"; - } - } - else - { - LOG(LogError) << "VolumeControl::getVolume() - Failed to get volume range!"; - } - } -#elif defined(WIN32) || defined(_WIN32) - if (mixerHandle != nullptr) - { - //Windows older than Vista. use mixer API. get volume from line control - MIXERCONTROLDETAILS_UNSIGNED value; - value.dwValue = (volume * 65535) / 100; - MIXERCONTROLDETAILS mixerControlDetails; - mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); - mixerControlDetails.dwControlID = mixerControl.dwControlID; - mixerControlDetails.cChannels = 1; //always 1 for a MIXERCONTROL_CONTROLF_UNIFORM control - mixerControlDetails.cMultipleItems = 0; //always 0 except for a MIXERCONTROL_CONTROLF_MULTIPLE control - mixerControlDetails.paDetails = &value; - mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); - if (mixerSetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR) - { - LOG(LogError) << "VolumeControl::setVolume() - Failed to set mixer volume!"; - } - } - else if (endpointVolume != nullptr) - { - //Windows Vista or above. use EndpointVolume API - float floatVolume = 0.0f; //0-1 - if (volume > 0) { - floatVolume = (float)volume / 100.0f; - } - if (endpointVolume->SetMasterVolumeLevelScalar(floatVolume, nullptr) != S_OK) - { - LOG(LogError) << "VolumeControl::setVolume() - Failed to set master volume!"; - } - } -#endif + // Clamp to 0-100 range. + if (volume < 0) + volume = 0; + if (volume > 100) + volume = 100; + + // Store values in internal variables. + internalVolume = volume; + #if defined (__APPLE__) + #error TODO: Not implemented for MacOS yet!!! + #elif defined(__linux__) + if (mixerElem != nullptr) { + // Get volume range. + long minVolume; + long maxVolume; + if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) { + // Ok, 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) { + LOG(LogError) << "VolumeControl::getVolume() - Failed to set mixer volume!"; + } + } + else { + LOG(LogError) << "VolumeControl::getVolume() - Failed to get volume range!"; + } + } + #elif defined(WIN32) || defined(_WIN32) + if (mixerHandle != nullptr) { + // Windows older than Vista. use mixer API. get volume from line control. + MIXERCONTROLDETAILS_UNSIGNED value; + value.dwValue = (volume * 65535) / 100; + MIXERCONTROLDETAILS mixerControlDetails; + mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); + mixerControlDetails.dwControlID = mixerControl.dwControlID; + // Always 1 for a MIXERCONTROL_CONTROLF_UNIFORM control. + mixerControlDetails.cChannels = 1; + // Always 0 except for a MIXERCONTROL_CONTROLF_MULTIPLE control. + mixerControlDetails.cMultipleItems = 0; + mixerControlDetails.paDetails = &value; + mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); + if (mixerSetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, + MIXER_SETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR) + LOG(LogError) << "VolumeControl::setVolume() - Failed to set mixer volume!"; + } + else if (endpointVolume != nullptr) { + // Windows Vista or above. use EndpointVolume API. + float floatVolume = 0.0f; // 0-1 + if (volume > 0) + floatVolume = (float)volume / 100.0f; + if (endpointVolume->SetMasterVolumeLevelScalar(floatVolume, nullptr) != S_OK) + LOG(LogError) << "VolumeControl::setVolume() - Failed to set master volume!"; + } + #endif } diff --git a/es-app/src/VolumeControl.h b/es-app/src/VolumeControl.h index a2e420e7e..eb67e027a 100644 --- a/es-app/src/VolumeControl.h +++ b/es-app/src/VolumeControl.h @@ -1,3 +1,9 @@ +// +// VolumeControl.h +// +// Controls system audio volume. +// + #pragma once #ifndef ES_APP_VOLUME_CONTROL_H #define ES_APP_VOLUME_CONTROL_H @@ -5,56 +11,54 @@ #include #if defined (__APPLE__) - #error TODO: Not implemented for MacOS yet!!! +#error TODO: Not implemented for MacOS yet!!! #elif defined(__linux__) - #include - #include - #include +#include +#include +#include #elif defined(WIN32) || defined(_WIN32) - #include - #include - #include +#include +#include +#include #endif -/*! -Singleton pattern. Call getInstance() to get an object. -*/ +// Singleton pattern. Call getInstance() to get an object. class VolumeControl { -#if defined (__APPLE__) + #if defined (__APPLE__) #error TODO: Not implemented for MacOS yet!!! -#elif defined(__linux__) + #elif defined(__linux__) static const char * mixerName; static const char * mixerCard; int mixerIndex; snd_mixer_t* mixerHandle; snd_mixer_elem_t* mixerElem; snd_mixer_selem_id_t* mixerSelemId; -#elif defined(WIN32) || defined(_WIN32) - HMIXER mixerHandle; - MIXERCONTROL mixerControl; - IAudioEndpointVolume * endpointVolume; -#endif + #elif defined(WIN32) || defined(_WIN32) + HMIXER mixerHandle; + MIXERCONTROL mixerControl; + IAudioEndpointVolume * endpointVolume; + #endif - int originalVolume; - int internalVolume; + int originalVolume; + int internalVolume; - static std::weak_ptr sInstance; + static std::weak_ptr sInstance; - VolumeControl(); - VolumeControl(const VolumeControl & right); + VolumeControl(); + VolumeControl(const VolumeControl & right); VolumeControl & operator=(const VolumeControl & right); public: - static std::shared_ptr & getInstance(); + static std::shared_ptr & getInstance(); - void init(); - void deinit(); + void init(); + void deinit(); - int getVolume() const; - void setVolume(int volume); + int getVolume() const; + void setVolume(int volume); - ~VolumeControl(); + ~VolumeControl(); }; #endif // ES_APP_VOLUME_CONTROL_H diff --git a/es-app/src/guis/GuiGamelistOptions.cpp b/es-app/src/guis/GuiGamelistOptions.cpp index 4ccfea5e6..7624fa40e 100644 --- a/es-app/src/guis/GuiGamelistOptions.cpp +++ b/es-app/src/guis/GuiGamelistOptions.cpp @@ -238,7 +238,7 @@ void GuiGamelistOptions::openMetaDataEd() std::function deleteBtnFunc; if (file->getType() == FOLDER) { - deleteBtnFunc = NULL; + deleteBtnFunc = nullptr; } else { deleteBtnFunc = [this, file] { diff --git a/es-app/src/guis/GuiGeneralScreensaverOptions.cpp b/es-app/src/guis/GuiGeneralScreensaverOptions.cpp index 60af7e32d..f3d8ecf90 100644 --- a/es-app/src/guis/GuiGeneralScreensaverOptions.cpp +++ b/es-app/src/guis/GuiGeneralScreensaverOptions.cpp @@ -3,7 +3,7 @@ // // User interface for the screensaver options. // Based on the GuiScreenSaverOptions template. -// Submenu to the GuiMenu main menu. +// Submenu to the GuiMenu main menu. // #include "guis/GuiGeneralScreensaverOptions.h" diff --git a/es-app/src/guis/GuiSettings.cpp b/es-app/src/guis/GuiSettings.cpp index a7087af9c..ebfd5e0e2 100644 --- a/es-app/src/guis/GuiSettings.cpp +++ b/es-app/src/guis/GuiSettings.cpp @@ -1,3 +1,9 @@ +// +// GuiSettings.cpp +// +// User interface template for a settings GUI. +// + #include "guis/GuiSettings.h" #include "views/ViewController.h" @@ -5,63 +11,62 @@ #include "SystemData.h" #include "Window.h" -GuiSettings::GuiSettings(Window* window, const char* title) : GuiComponent(window), mMenu(window, title) +GuiSettings::GuiSettings(Window* window, const char* title) + : GuiComponent(window), mMenu(window, title) { - addChild(&mMenu); + addChild(&mMenu); + mMenu.addButton("BACK", "back", [this] { delete this; }); - mMenu.addButton("BACK", "back", [this] { delete this; }); - - setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f); + setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); + mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f); } GuiSettings::~GuiSettings() { - save(); + save(); } void GuiSettings::save() { - if(!mSaveFuncs.size()) - return; + if (!mSaveFuncs.size()) + return; - for(auto it = mSaveFuncs.cbegin(); it != mSaveFuncs.cend(); it++) - (*it)(); + for (auto it = mSaveFuncs.cbegin(); it != mSaveFuncs.cend(); it++) + (*it)(); - Settings::getInstance()->saveFile(); + Settings::getInstance()->saveFile(); } bool GuiSettings::input(InputConfig* config, Input input) { - if(config->isMappedTo("b", input) && input.value != 0) - { - delete this; - return true; - } + if (config->isMappedTo("b", input) && input.value != 0) { + delete this; + 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; -// } +// 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); + return GuiComponent::input(config, input); } HelpStyle GuiSettings::getHelpStyle() { - HelpStyle style = HelpStyle(); - style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system"); - return style; + HelpStyle style = HelpStyle(); + style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system"); + return style; } std::vector GuiSettings::getHelpPrompts() { - std::vector prompts = mMenu.getHelpPrompts(); - prompts.push_back(HelpPrompt("b", "back")); - return prompts; + std::vector prompts = mMenu.getHelpPrompts(); + prompts.push_back(HelpPrompt("b", "back")); + return prompts; } diff --git a/es-app/src/guis/GuiSettings.h b/es-app/src/guis/GuiSettings.h index d5b0d9799..5d700c35c 100644 --- a/es-app/src/guis/GuiSettings.h +++ b/es-app/src/guis/GuiSettings.h @@ -1,3 +1,9 @@ +// +// GuiSettings.h +// +// User interface template for a settings GUI. +// + #pragma once #ifndef ES_APP_GUIS_GUI_SETTINGS_H #define ES_APP_GUIS_GUI_SETTINGS_H @@ -8,21 +14,22 @@ class GuiSettings : public GuiComponent { public: - GuiSettings(Window* window, const char* title); - virtual ~GuiSettings(); // just calls save(); + GuiSettings(Window* window, const char* title); + virtual ~GuiSettings(); // Just calls save() - 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); }; - inline void addSaveFunc(const std::function& func) { mSaveFuncs.push_back(func); }; + 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); }; + inline void addSaveFunc(const std::function& func) { mSaveFuncs.push_back(func); }; - bool input(InputConfig* config, Input input) override; - std::vector getHelpPrompts() override; - HelpStyle getHelpStyle() override; + bool input(InputConfig* config, Input input) override; + std::vector getHelpPrompts() override; + HelpStyle getHelpStyle() override; private: - MenuComponent mMenu; - std::vector< std::function > mSaveFuncs; + MenuComponent mMenu; + std::vector< std::function > mSaveFuncs; }; #endif // ES_APP_GUIS_GUI_SETTINGS_H diff --git a/es-app/src/guis/GuiSlideshowScreensaverOptions.cpp b/es-app/src/guis/GuiSlideshowScreensaverOptions.cpp index 9f3c5b9db..0bae4b8ba 100644 --- a/es-app/src/guis/GuiSlideshowScreensaverOptions.cpp +++ b/es-app/src/guis/GuiSlideshowScreensaverOptions.cpp @@ -1,3 +1,10 @@ +// +// GuiSlideshowScreensaverOptions.cpp +// +// User interface for the slideshow screensaver options. +// Submenu to GuiGeneralScreensaverOptions. +// + #include "guis/GuiSlideshowScreensaverOptions.h" #include "components/SliderComponent.h" @@ -7,76 +14,90 @@ #include "Settings.h" #include "Window.h" -GuiSlideshowScreensaverOptions::GuiSlideshowScreensaverOptions(Window* window, const char* title) : GuiScreensaverOptions(window, title) +GuiSlideshowScreensaverOptions::GuiSlideshowScreensaverOptions(Window* window, const char* title) + : GuiScreensaverOptions(window, title) { - ComponentListRow row; + ComponentListRow row; - // image duration (seconds) - auto sss_image_sec = std::make_shared(mWindow, 1.f, 60.f, 1.f, "s"); - sss_image_sec->setValue((float)(Settings::getInstance()->getInt("ScreenSaverSwapImageTimeout") / (1000))); - addWithLabel(row, "SWAP IMAGE AFTER (SECS)", sss_image_sec); - addSaveFunc([sss_image_sec] { - int playNextTimeout = (int)Math::round(sss_image_sec->getValue()) * (1000); - Settings::getInstance()->setInt("ScreenSaverSwapImageTimeout", playNextTimeout); - PowerSaver::updateTimeouts(); - }); + // Image duration (in seconds). + auto sss_image_sec = std::make_shared(mWindow, 1.f, 60.f, 1.f, "s"); + sss_image_sec->setValue((float)(Settings::getInstance()-> + getInt("ScreenSaverSwapImageTimeout") / (1000))); + addWithLabel(row, "SWAP IMAGE AFTER (SECS)", sss_image_sec); + addSaveFunc([sss_image_sec] { + int playNextTimeout = (int)Math::round(sss_image_sec->getValue()) * (1000); + Settings::getInstance()->setInt("ScreenSaverSwapImageTimeout", playNextTimeout); + PowerSaver::updateTimeouts(); + }); - // stretch - auto sss_stretch = std::make_shared(mWindow); - sss_stretch->setState(Settings::getInstance()->getBool("SlideshowScreenSaverStretch")); - addWithLabel(row, "STRETCH IMAGES", sss_stretch); - addSaveFunc([sss_stretch] { - Settings::getInstance()->setBool("SlideshowScreenSaverStretch", sss_stretch->getState()); - }); + // Stretch image. + auto sss_stretch = std::make_shared(mWindow); + sss_stretch->setState(Settings::getInstance()->getBool("SlideshowScreenSaverStretch")); + addWithLabel(row, "STRETCH IMAGES", sss_stretch); + addSaveFunc([sss_stretch] { + Settings::getInstance()->setBool("SlideshowScreenSaverStretch", sss_stretch->getState()); + }); - // background audio file - auto sss_bg_audio_file = std::make_shared(mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF); - addEditableTextComponent(row, "BACKGROUND AUDIO", sss_bg_audio_file, Settings::getInstance()->getString("SlideshowScreenSaverBackgroundAudioFile")); - addSaveFunc([sss_bg_audio_file] { - Settings::getInstance()->setString("SlideshowScreenSaverBackgroundAudioFile", sss_bg_audio_file->getValue()); - }); + // Background audio file. + auto sss_bg_audio_file = std::make_shared(mWindow, "", + Font::get(FONT_SIZE_SMALL), 0x777777FF); + addEditableTextComponent(row, "BACKGROUND AUDIO", sss_bg_audio_file, + Settings::getInstance()->getString("SlideshowScreenSaverBackgroundAudioFile")); + addSaveFunc([sss_bg_audio_file] { + Settings::getInstance()->setString("SlideshowScreenSaverBackgroundAudioFile", + sss_bg_audio_file->getValue()); + }); - // image source - auto sss_custom_source = std::make_shared(mWindow); - sss_custom_source->setState(Settings::getInstance()->getBool("SlideshowScreenSaverCustomImageSource")); - addWithLabel(row, "USE CUSTOM IMAGES", sss_custom_source); - addSaveFunc([sss_custom_source] { Settings::getInstance()->setBool("SlideshowScreenSaverCustomImageSource", sss_custom_source->getState()); }); + // Image source. + auto sss_custom_source = std::make_shared(mWindow); + sss_custom_source->setState(Settings::getInstance()-> + getBool("SlideshowScreenSaverCustomImageSource")); + addWithLabel(row, "USE CUSTOM IMAGES", sss_custom_source); + addSaveFunc([sss_custom_source] { Settings::getInstance()-> + setBool("SlideshowScreenSaverCustomImageSource", sss_custom_source->getState()); }); - // custom image directory - auto sss_image_dir = std::make_shared(mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF); - addEditableTextComponent(row, "CUSTOM IMAGE DIR", sss_image_dir, Settings::getInstance()->getString("SlideshowScreenSaverImageDir")); - addSaveFunc([sss_image_dir] { - Settings::getInstance()->setString("SlideshowScreenSaverImageDir", sss_image_dir->getValue()); - }); + // Custom image directory. + auto sss_image_dir = std::make_shared(mWindow, "", + Font::get(FONT_SIZE_SMALL), 0x777777FF); + addEditableTextComponent(row, "CUSTOM IMAGE DIR", sss_image_dir, + Settings::getInstance()->getString("SlideshowScreenSaverImageDir")); + addSaveFunc([sss_image_dir] { + Settings::getInstance()->setString("SlideshowScreenSaverImageDir", + sss_image_dir->getValue()); + }); - // recurse custom image directory - auto sss_recurse = std::make_shared(mWindow); - sss_recurse->setState(Settings::getInstance()->getBool("SlideshowScreenSaverRecurse")); - addWithLabel(row, "CUSTOM IMAGE DIR RECURSIVE", sss_recurse); - addSaveFunc([sss_recurse] { - Settings::getInstance()->setBool("SlideshowScreenSaverRecurse", sss_recurse->getState()); - }); + // Recurse custom image directory. + auto sss_recurse = std::make_shared(mWindow); + sss_recurse->setState(Settings::getInstance()->getBool("SlideshowScreenSaverRecurse")); + addWithLabel(row, "CUSTOM IMAGE DIR RECURSIVE", sss_recurse); + addSaveFunc([sss_recurse] { + Settings::getInstance()->setBool("SlideshowScreenSaverRecurse", sss_recurse->getState()); + }); - // custom image filter - auto sss_image_filter = std::make_shared(mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF); - addEditableTextComponent(row, "CUSTOM IMAGE FILTER", sss_image_filter, Settings::getInstance()->getString("SlideshowScreenSaverImageFilter")); - addSaveFunc([sss_image_filter] { - Settings::getInstance()->setString("SlideshowScreenSaverImageFilter", sss_image_filter->getValue()); - }); + // Custom image filter (file extensions). + auto sss_image_filter = std::make_shared(mWindow, "", + Font::get(FONT_SIZE_SMALL), 0x777777FF); + addEditableTextComponent(row, "CUSTOM IMAGE FILTER", sss_image_filter, + Settings::getInstance()->getString("SlideshowScreenSaverImageFilter")); + addSaveFunc([sss_image_filter] { + Settings::getInstance()->setString("SlideshowScreenSaverImageFilter", + sss_image_filter->getValue()); + }); } GuiSlideshowScreensaverOptions::~GuiSlideshowScreensaverOptions() { } -void GuiSlideshowScreensaverOptions::addWithLabel(ComponentListRow row, const std::string label, std::shared_ptr component) +void GuiSlideshowScreensaverOptions::addWithLabel(ComponentListRow row, + const std::string label, std::shared_ptr component) { - row.elements.clear(); + row.elements.clear(); - auto lbl = std::make_shared(mWindow, Utils::String::toUpper(label), Font::get(FONT_SIZE_MEDIUM), 0x777777FF); - row.addElement(lbl, true); // label + auto lbl = std::make_shared(mWindow, Utils::String::toUpper(label), + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); - row.addElement(component, false, true); - - addRow(row); + row.addElement(lbl, true); // Label. + row.addElement(component, false, true); + addRow(row); } diff --git a/es-app/src/guis/GuiSlideshowScreensaverOptions.h b/es-app/src/guis/GuiSlideshowScreensaverOptions.h index 4232a69ba..c4e1741d3 100644 --- a/es-app/src/guis/GuiSlideshowScreensaverOptions.h +++ b/es-app/src/guis/GuiSlideshowScreensaverOptions.h @@ -1,3 +1,10 @@ +// +// GuiSlideshowScreensaverOptions.h +// +// User interface for the slideshow screensaver options. +// Submenu to GuiGeneralScreensaverOptions. +// + #pragma once #ifndef ES_APP_GUIS_GUI_SLIDESHOW_SCREENSAVER_OPTIONS_H #define ES_APP_GUIS_GUI_SLIDESHOW_SCREENSAVER_OPTIONS_H @@ -7,11 +14,12 @@ class GuiSlideshowScreensaverOptions : public GuiScreensaverOptions { public: - GuiSlideshowScreensaverOptions(Window* window, const char* title); - virtual ~GuiSlideshowScreensaverOptions(); + GuiSlideshowScreensaverOptions(Window* window, const char* title); + virtual ~GuiSlideshowScreensaverOptions(); private: - void addWithLabel(ComponentListRow row, const std::string label, std::shared_ptr component); + void addWithLabel(ComponentListRow row, const std::string label, + std::shared_ptr component); }; #endif // ES_APP_GUIS_GUI_SLIDESHOW_SCREENSAVER_OPTIONS_H diff --git a/es-app/src/guis/GuiVideoScreensaverOptions.cpp b/es-app/src/guis/GuiVideoScreensaverOptions.cpp index 840b7ac1c..5304b157d 100644 --- a/es-app/src/guis/GuiVideoScreensaverOptions.cpp +++ b/es-app/src/guis/GuiVideoScreensaverOptions.cpp @@ -1,3 +1,10 @@ +// +// GuiVideoScreensaverOptions.cpp +// +// User interface for the video screensaver options. +// Submenu to GuiGeneralScreensaverOptions. +// + #include "guis/GuiVideoScreensaverOptions.h" #include "components/OptionListComponent.h" @@ -6,89 +13,105 @@ #include "guis/GuiMsgBox.h" #include "Settings.h" -GuiVideoScreensaverOptions::GuiVideoScreensaverOptions(Window* window, const char* title) : GuiScreensaverOptions(window, title) +GuiVideoScreensaverOptions::GuiVideoScreensaverOptions(Window* window, const char* title) + : GuiScreensaverOptions(window, title) { - // timeout to swap videos - auto swap = std::make_shared(mWindow, 10.f, 1000.f, 1.f, "s"); - swap->setValue((float)(Settings::getInstance()->getInt("ScreenSaverSwapVideoTimeout") / (1000))); - addWithLabel("SWAP VIDEO AFTER (SECS)", swap); - addSaveFunc([swap] { - int playNextTimeout = (int)Math::round(swap->getValue()) * (1000); - Settings::getInstance()->setInt("ScreenSaverSwapVideoTimeout", playNextTimeout); - PowerSaver::updateTimeouts(); - }); + // Timer for swapping videos. + auto swap = std::make_shared(mWindow, 10.f, 1000.f, 1.f, "s"); + swap->setValue((float)(Settings::getInstance()-> + getInt("ScreenSaverSwapVideoTimeout") / (1000))); + addWithLabel("SWAP VIDEO AFTER (SECS)", swap); + addSaveFunc([swap] { + int playNextTimeout = (int)Math::round(swap->getValue()) * (1000); + Settings::getInstance()->setInt("ScreenSaverSwapVideoTimeout", playNextTimeout); + PowerSaver::updateTimeouts(); + }); - auto stretch_screensaver = std::make_shared(mWindow); - stretch_screensaver->setState(Settings::getInstance()->getBool("StretchVideoOnScreenSaver")); - addWithLabel("STRETCH VIDEO ON SCREENSAVER", stretch_screensaver); - addSaveFunc([stretch_screensaver] { Settings::getInstance()->setBool("StretchVideoOnScreenSaver", stretch_screensaver->getState()); }); + auto stretch_screensaver = std::make_shared(mWindow); + stretch_screensaver->setState(Settings::getInstance()->getBool("StretchVideoOnScreenSaver")); + addWithLabel("STRETCH VIDEO ON SCREENSAVER", stretch_screensaver); + addSaveFunc([stretch_screensaver] { Settings::getInstance()-> + setBool("StretchVideoOnScreenSaver", stretch_screensaver->getState()); }); -#ifdef _RPI_ - auto ss_omx = std::make_shared(mWindow); - ss_omx->setState(Settings::getInstance()->getBool("ScreenSaverOmxPlayer")); - addWithLabel("USE OMX PLAYER FOR SCREENSAVER", ss_omx); - addSaveFunc([ss_omx, this] { Settings::getInstance()->setBool("ScreenSaverOmxPlayer", ss_omx->getState()); }); -#endif + #ifdef _RPI_ + auto ss_omx = std::make_shared(mWindow); + ss_omx->setState(Settings::getInstance()->getBool("ScreenSaverOmxPlayer")); + addWithLabel("USE OMX PLAYER FOR SCREENSAVER", ss_omx); + addSaveFunc([ss_omx, this] { Settings::getInstance()-> + setBool("ScreenSaverOmxPlayer", ss_omx->getState()); }); + #endif - // Render Video Game Name as subtitles - auto ss_info = std::make_shared< OptionListComponent >(mWindow, getHelpStyle(), "SHOW GAME INFO", false); - std::vector info_type; - info_type.push_back("always"); - info_type.push_back("start & end"); - info_type.push_back("never"); - for(auto it = info_type.cbegin(); it != info_type.cend(); it++) - ss_info->add(*it, *it, Settings::getInstance()->getString("ScreenSaverGameInfo") == *it); - addWithLabel("SHOW GAME INFO ON SCREENSAVER", ss_info); - addSaveFunc([ss_info, this] { Settings::getInstance()->setString("ScreenSaverGameInfo", ss_info->getSelected()); }); + // Render video game name as subtitles. + auto ss_info = std::make_shared> + (mWindow,getHelpStyle(), "SHOW GAME INFO", false); + std::vector info_type; + info_type.push_back("always"); + info_type.push_back("start & end"); + info_type.push_back("never"); + for(auto it = info_type.cbegin(); it != info_type.cend(); it++) + ss_info->add(*it, *it, Settings::getInstance()->getString("ScreenSaverGameInfo") == *it); + addWithLabel("SHOW GAME INFO ON SCREENSAVER", ss_info); + addSaveFunc([ss_info, this] { Settings::getInstance()-> + setString("ScreenSaverGameInfo", ss_info->getSelected()); }); -#ifdef _RPI_ - ComponentListRow row; + #ifdef _RPI_ + ComponentListRow row; - // Set subtitle position - auto ss_omx_subs_align = std::make_shared< OptionListComponent >(mWindow, "GAME INFO ALIGNMENT", false); - std::vector align_mode; - align_mode.push_back("left"); - align_mode.push_back("center"); - for(auto it = align_mode.cbegin(); it != align_mode.cend(); it++) - ss_omx_subs_align->add(*it, *it, Settings::getInstance()->getString("SubtitleAlignment") == *it); - addWithLabel("GAME INFO ALIGNMENT", ss_omx_subs_align); - addSaveFunc([ss_omx_subs_align, this] { Settings::getInstance()->setString("SubtitleAlignment", ss_omx_subs_align->getSelected()); }); + // Set subtitle position. + auto ss_omx_subs_align = std::make_shared> + (mWindow, "GAME INFO ALIGNMENT", false); + std::vector align_mode; + align_mode.push_back("left"); + align_mode.push_back("center"); + for(auto it = align_mode.cbegin(); it != align_mode.cend(); it++) + ss_omx_subs_align->add(*it, *it, Settings::getInstance()-> + getString("SubtitleAlignment") == *it); + addWithLabel("GAME INFO ALIGNMENT", ss_omx_subs_align); + addSaveFunc([ss_omx_subs_align, this] { Settings::getInstance()-> + setString("SubtitleAlignment", ss_omx_subs_align->getSelected()); }); - // Set font size - auto ss_omx_font_size = std::make_shared(mWindow, 1.f, 64.f, 1.f, "h"); - ss_omx_font_size->setValue((float)(Settings::getInstance()->getInt("SubtitleSize"))); - addWithLabel("GAME INFO FONT SIZE", ss_omx_font_size); - addSaveFunc([ss_omx_font_size] { - int subSize = (int)Math::round(ss_omx_font_size->getValue()); - Settings::getInstance()->setInt("SubtitleSize", subSize); - }); + // Set font size. + auto ss_omx_font_size = std::make_shared(mWindow, 1.f, 64.f, 1.f, "h"); + ss_omx_font_size->setValue((float)(Settings::getInstance()->getInt("SubtitleSize"))); + addWithLabel("GAME INFO FONT SIZE", ss_omx_font_size); + addSaveFunc([ss_omx_font_size] { + int subSize = (int)Math::round(ss_omx_font_size->getValue()); + Settings::getInstance()->setInt("SubtitleSize", subSize); + }); - auto ss_video_mute = std::make_shared(mWindow); - ss_video_mute->setState(Settings::getInstance()->getBool("ScreenSaverVideoMute")); - addWithLabel("MUTE SCREENSAVER AUDIO", ss_video_mute); - addSaveFunc([ss_video_mute] { Settings::getInstance()->setBool("ScreenSaverVideoMute", ss_video_mute->getState()); }); + auto ss_video_mute = std::make_shared(mWindow); + ss_video_mute->setState(Settings::getInstance()->getBool("ScreenSaverVideoMute")); + addWithLabel("MUTE SCREENSAVER AUDIO", ss_video_mute); + addSaveFunc([ss_video_mute] { Settings::getInstance()-> + setBool("ScreenSaverVideoMute", ss_video_mute->getState()); }); - // Define subtitle font - auto ss_omx_font_file = std::make_shared(mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF); - addEditableTextComponent(row, "PATH TO FONT FILE", ss_omx_font_file, Settings::getInstance()->getString("SubtitleFont")); - addSaveFunc([ss_omx_font_file] { - Settings::getInstance()->setString("SubtitleFont", ss_omx_font_file->getValue()); - }); + // Define subtitle font. + auto ss_omx_font_file = std::make_shared(mWindow, "", + Font::get(FONT_SIZE_SMALL), 0x777777FF); + addEditableTextComponent(row, "PATH TO FONT FILE", ss_omx_font_file, + Settings::getInstance()->getString("SubtitleFont")); + addSaveFunc([ss_omx_font_file] { + Settings::getInstance()->setString("SubtitleFont", ss_omx_font_file->getValue()); + }); - // Define subtitle italic font - auto ss_omx_italic_font_file = std::make_shared(mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF); - addEditableTextComponent(row, "PATH TO ITALIC FONT FILE", ss_omx_italic_font_file, Settings::getInstance()->getString("SubtitleItalicFont")); - addSaveFunc([ss_omx_italic_font_file] { - Settings::getInstance()->setString("SubtitleItalicFont", ss_omx_italic_font_file->getValue()); - }); -#endif + // Define subtitle italic font. + auto ss_omx_italic_font_file = std::make_shared(mWindow, "", + Font::get(FONT_SIZE_SMALL), 0x777777FF); + addEditableTextComponent(row, "PATH TO ITALIC FONT FILE", ss_omx_italic_font_file, + Settings::getInstance()->getString("SubtitleItalicFont")); + addSaveFunc([ss_omx_italic_font_file] { + Settings::getInstance()->setString("SubtitleItalicFont", + ss_omx_italic_font_file->getValue()); + }); + #endif -#ifndef _RPI_ - auto captions_compatibility = std::make_shared(mWindow); - captions_compatibility->setState(Settings::getInstance()->getBool("CaptionsCompatibility")); - addWithLabel("USE COMPATIBLE LOW RESOLUTION FOR CAPTIONS", captions_compatibility); - addSaveFunc([captions_compatibility] { Settings::getInstance()->setBool("CaptionsCompatibility", captions_compatibility->getState()); }); -#endif + #ifndef _RPI_ + auto captions_compatibility = std::make_shared(mWindow); + captions_compatibility->setState(Settings::getInstance()->getBool("CaptionsCompatibility")); + addWithLabel("USE COMPATIBLE LOW RESOLUTION FOR CAPTIONS", captions_compatibility); + addSaveFunc([captions_compatibility] { Settings::getInstance()-> + setBool("CaptionsCompatibility", captions_compatibility->getState()); }); + #endif } GuiVideoScreensaverOptions::~GuiVideoScreensaverOptions() @@ -97,18 +120,25 @@ GuiVideoScreensaverOptions::~GuiVideoScreensaverOptions() void GuiVideoScreensaverOptions::save() { -#ifdef _RPI_ - bool startingStatusNotRisky = (Settings::getInstance()->getString("ScreenSaverGameInfo") == "never" || !Settings::getInstance()->getBool("ScreenSaverOmxPlayer")); -#endif - GuiScreensaverOptions::save(); + #ifdef _RPI_ + bool startingStatusNotRisky = (Settings::getInstance()-> + getString("ScreenSaverGameInfo") == "never" || + !Settings::getInstance()->getBool("ScreenSaverOmxPlayer")); + #endif + GuiScreensaverOptions::save(); -#ifdef _RPI_ - bool endStatusRisky = (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never" && Settings::getInstance()->getBool("ScreenSaverOmxPlayer")); - if (startingStatusNotRisky && endStatusRisky) { - // if before it wasn't risky but now there's a risk of problems, show warning - mWindow->pushGui(new GuiMsgBox(mWindow, - "Using OMX Player and displaying Game Info may result in the video flickering in some TV modes. If that happens, consider:\n\n• Disabling the \"Show Game Info\" option;\n• Disabling \"Overscan\" on the Pi configuration menu might help:\nRetroPie > Raspi-Config > Advanced Options > Overscan > \"No\".\n• Disabling the use of OMX Player for the screensaver.", - "GOT IT!", [] { return; })); - } -#endif + #ifdef _RPI_ + bool endStatusRisky = (Settings::getInstance()->getString("ScreenSaverGameInfo") != + "never" && Settings::getInstance()->getBool("ScreenSaverOmxPlayer")); + if (startingStatusNotRisky && endStatusRisky) { + // If before it wasn't risky but now there's a risk of problems, show warning. + mWindow->pushGui(new GuiMsgBox(mWindow, + "Using OMX Player and displaying Game Info may result in the video flickering in " + "some TV modes. If that happens, consider:\n\n• Disabling the \"Show Game Info\" " + "option;\n• Disabling \"Overscan\" on the Pi configuration menu might help:\nRetroPie > " + "Raspi-Config > Advanced Options > Overscan > \"No\".\n• Disabling the use of OMX Player " + "for the screensaver.", + "GOT IT!", [] { return; })); + } + #endif } diff --git a/es-app/src/guis/GuiVideoScreensaverOptions.h b/es-app/src/guis/GuiVideoScreensaverOptions.h index ecf4e4b2d..2690070dd 100644 --- a/es-app/src/guis/GuiVideoScreensaverOptions.h +++ b/es-app/src/guis/GuiVideoScreensaverOptions.h @@ -1,3 +1,10 @@ +// +// GuiVideoScreensaverOptions.h +// +// User interface for the video screensaver options. +// Submenu to GuiGeneralScreensaverOptions. +// + #pragma once #ifndef ES_APP_GUIS_GUI_VIDEO_SCREENSAVER_OPTIONS_H #define ES_APP_GUIS_GUI_VIDEO_SCREENSAVER_OPTIONS_H @@ -7,10 +14,10 @@ class GuiVideoScreensaverOptions : public GuiScreensaverOptions { public: - GuiVideoScreensaverOptions(Window* window, const char* title); - virtual ~GuiVideoScreensaverOptions(); + GuiVideoScreensaverOptions(Window* window, const char* title); + virtual ~GuiVideoScreensaverOptions(); - void save() override; + void save() override; }; #endif // ES_APP_GUIS_GUI_VIDEO_SCREENSAVER_OPTIONS_H diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index 6087de69c..153b2e1a0 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -30,6 +30,7 @@ #include "Settings.h" #include "SystemData.h" #include "SystemScreenSaver.h" +#include #include #include #include @@ -39,8 +40,6 @@ #include #endif -#include - enum eErrorCodes { NO_ERRORS, NO_SYSTEMS_FILE, @@ -289,7 +288,7 @@ void onExit() int main(int argc, char* argv[]) { - srand((unsigned int)time(NULL)); + srand((unsigned int)time(nullptr)); std::locale::global(std::locale("C")); diff --git a/es-app/src/scrapers/Scraper.cpp b/es-app/src/scrapers/Scraper.cpp index 0a974c2c2..d65c35c87 100644 --- a/es-app/src/scrapers/Scraper.cpp +++ b/es-app/src/scrapers/Scraper.cpp @@ -360,7 +360,7 @@ bool resizeImage(const std::string& path, int maxWidth, int maxHeight) return true; FREE_IMAGE_FORMAT format = FIF_UNKNOWN; - FIBITMAP* image = NULL; + FIBITMAP* image = nullptr; // Detect the filetype. format = FreeImage_GetFileType(path.c_str(), 0); @@ -396,7 +396,7 @@ bool resizeImage(const std::string& path, int maxWidth, int maxHeight) FIBITMAP* imageRescaled = FreeImage_Rescale(image, maxWidth, maxHeight, FILTER_BILINEAR); FreeImage_Unload(image); - if (imageRescaled == NULL) { + if (imageRescaled == nullptr) { LOG(LogError) << "Could not resize image! (not enough memory? invalid bitdepth?)"; return false; } diff --git a/es-app/src/scrapers/ScreenScraper.cpp b/es-app/src/scrapers/ScreenScraper.cpp index 2e8cc82f3..c9952376b 100644 --- a/es-app/src/scrapers/ScreenScraper.cpp +++ b/es-app/src/scrapers/ScreenScraper.cpp @@ -122,7 +122,7 @@ pugi::xml_node find_child_by_attribute_list(const pugi::xml_node& node_parent, } } - return pugi::xml_node(NULL); + return pugi::xml_node(nullptr); } void screenscraper_generate_scraper_requests(const ScraperSearchParams& params, @@ -307,7 +307,7 @@ void ScreenScraperRequest::processMedia( std::string& fileFormat, std::string region) { - pugi::xml_node art = pugi::xml_node(NULL); + pugi::xml_node art = pugi::xml_node(nullptr); // Do an XPath query for media[type='$media_type'], then filter by region. // We need to do this because any child of 'medias' has the form diff --git a/es-app/src/views/UIModeController.cpp b/es-app/src/views/UIModeController.cpp index 4de65e0ed..45ffbadc4 100644 --- a/es-app/src/views/UIModeController.cpp +++ b/es-app/src/views/UIModeController.cpp @@ -12,11 +12,11 @@ #include "Log.h" #include "Window.h" -UIModeController * UIModeController::sInstance = NULL; +UIModeController * UIModeController::sInstance = nullptr; UIModeController * UIModeController::getInstance() { - if (sInstance == NULL) + if (sInstance == nullptr) sInstance = new UIModeController(); return sInstance; diff --git a/es-app/src/views/gamelist/DetailedGameListView.cpp b/es-app/src/views/gamelist/DetailedGameListView.cpp index d4631588a..63b7320c3 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.cpp +++ b/es-app/src/views/gamelist/DetailedGameListView.cpp @@ -232,7 +232,7 @@ void DetailedGameListView::initMDValues() void DetailedGameListView::updateInfoPanel() { - FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected(); + FileData* file = (mList.size() == 0 || mList.isScrolling()) ? nullptr : mList.getSelected(); bool fadingOut; if (file == nullptr) { diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index de3baf58b..526d180c2 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -264,7 +264,7 @@ void VideoGameListView::initMDValues() void VideoGameListView::updateInfoPanel() { - FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected(); + FileData* file = (mList.size() == 0 || mList.isScrolling()) ? nullptr : mList.getSelected(); Utils::FileSystem::removeFile(getTitlePath()); diff --git a/es-core/src/GuiComponent.cpp b/es-core/src/GuiComponent.cpp index a09676f46..94b6de634 100644 --- a/es-core/src/GuiComponent.cpp +++ b/es-core/src/GuiComponent.cpp @@ -16,7 +16,7 @@ GuiComponent::GuiComponent(Window* window) : mWindow(window), - mParent(NULL), + mParent(nullptr), mOpacity(255), mPosition(Vector3f::Zero()), mOrigin(Vector2f::Zero()), @@ -27,7 +27,7 @@ GuiComponent::GuiComponent(Window* window) mVisible(true) { for (unsigned char i = 0; i < MAX_ANIMATIONS; i++) - mAnimationMap[i] = NULL; + mAnimationMap[i] = nullptr; } GuiComponent::~GuiComponent() @@ -40,7 +40,7 @@ GuiComponent::~GuiComponent() mParent->removeChild(this); for (unsigned int i = 0; i < getChildCount(); i++) - getChild(i)->setParent(NULL); + getChild(i)->setParent(nullptr); } bool GuiComponent::input(InputConfig* config, Input input) @@ -203,7 +203,7 @@ void GuiComponent::removeChild(GuiComponent* cmp) if (cmp->getParent() != this) LOG(LogError) << "Tried to remove child from incorrect parent!"; - cmp->setParent(NULL); + cmp->setParent(nullptr); for (auto i = mChildren.cbegin(); i != mChildren.cend(); i++) { if (*i == cmp) { @@ -334,7 +334,7 @@ bool GuiComponent::stopAnimation(unsigned char slot) assert(slot < MAX_ANIMATIONS); if (mAnimationMap[slot]) { delete mAnimationMap[slot]; - mAnimationMap[slot] = NULL; + mAnimationMap[slot] = nullptr; return true; } else { @@ -348,7 +348,7 @@ bool GuiComponent::cancelAnimation(unsigned char slot) if (mAnimationMap[slot]) { mAnimationMap[slot]->removeFinishedCallback(); delete mAnimationMap[slot]; - mAnimationMap[slot] = NULL; + mAnimationMap[slot] = nullptr; return true; } else { @@ -366,7 +366,7 @@ bool GuiComponent::finishAnimation(unsigned char slot) assert(done); delete mAnimationMap[slot]; // Will also call finishedCallback - mAnimationMap[slot] = NULL; + mAnimationMap[slot] = nullptr; return true; } else { @@ -381,7 +381,7 @@ bool GuiComponent::advanceAnimation(unsigned char slot, unsigned int time) if (anim) { bool done = anim->update(time); if (done) { - mAnimationMap[slot] = NULL; + mAnimationMap[slot] = nullptr; delete anim; } return true; @@ -405,18 +405,18 @@ void GuiComponent::cancelAllAnimations() bool GuiComponent::isAnimationPlaying(unsigned char slot) const { - return mAnimationMap[slot] != NULL; + return mAnimationMap[slot] != nullptr; } bool GuiComponent::isAnimationReversed(unsigned char slot) const { - assert(mAnimationMap[slot] != NULL); + assert(mAnimationMap[slot] != nullptr); return mAnimationMap[slot]->isReversed(); } int GuiComponent::getAnimationTime(unsigned char slot) const { - assert(mAnimationMap[slot] != NULL); + assert(mAnimationMap[slot] != nullptr); return mAnimationMap[slot]->getTime(); } diff --git a/es-core/src/InputConfig.cpp b/es-core/src/InputConfig.cpp index 499dbcbae..69a783705 100644 --- a/es-core/src/InputConfig.cpp +++ b/es-core/src/InputConfig.cpp @@ -1,233 +1,234 @@ +// +// InputConfig.cpp +// +// Input device configuration functions. +// + #include "InputConfig.h" #include "Log.h" #include -//some util functions +// Some utility functions. std::string inputTypeToString(InputType type) { - switch(type) - { - case TYPE_AXIS: - return "axis"; - case TYPE_BUTTON: - return "button"; - case TYPE_HAT: - return "hat"; - case TYPE_KEY: - return "key"; - case TYPE_CEC_BUTTON: - return "cec-button"; - default: - return "error"; - } + switch (type) { + case TYPE_AXIS: + return "axis"; + case TYPE_BUTTON: + return "button"; + case TYPE_HAT: + return "hat"; + case TYPE_KEY: + return "key"; + case TYPE_CEC_BUTTON: + return "cec-button"; + default: + return "error"; + } } InputType stringToInputType(const std::string& type) { - if(type == "axis") - return TYPE_AXIS; - if(type == "button") - return TYPE_BUTTON; - if(type == "hat") - return TYPE_HAT; - if(type == "key") - return TYPE_KEY; - if(type == "cec-button") - return TYPE_CEC_BUTTON; - return TYPE_COUNT; + if (type == "axis") + return TYPE_AXIS; + if (type == "button") + return TYPE_BUTTON; + if (type == "hat") + return TYPE_HAT; + if (type == "key") + return TYPE_KEY; + if (type == "cec-button") + return TYPE_CEC_BUTTON; + return TYPE_COUNT; } - std::string toLower(std::string str) { - for(unsigned int i = 0; i < str.length(); i++) - { - str[i] = (char)tolower(str[i]); - } + for (unsigned int i = 0; i < str.length(); i++) + str[i] = (char)tolower(str[i]); - return str; + return str; } -//end util functions +// End of utility functions. -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) { } void InputConfig::clear() { - mNameMap.clear(); + mNameMap.clear(); } bool InputConfig::isConfigured() { - return mNameMap.size() > 0; + return mNameMap.size() > 0; } void InputConfig::mapInput(const std::string& name, Input input) { - mNameMap[toLower(name)] = input; + mNameMap[toLower(name)] = input; } void InputConfig::unmapInput(const std::string& name) { - auto it = mNameMap.find(toLower(name)); - if(it != mNameMap.cend()) - mNameMap.erase(it); + auto it = mNameMap.find(toLower(name)); + if (it != mNameMap.cend()) + mNameMap.erase(it); } bool InputConfig::getInputByName(const std::string& name, Input* result) { - auto it = mNameMap.find(toLower(name)); - if(it != mNameMap.cend()) - { - *result = it->second; - return true; - } - - return false; + auto it = mNameMap.find(toLower(name)); + if (it != mNameMap.cend()) { + *result = it->second; + return true; + } + return false; } bool InputConfig::isMappedTo(const std::string& name, Input input) { - Input comp; - if(!getInputByName(name, &comp)) - return false; + Input comp; + if (!getInputByName(name, &comp)) + return false; - if(comp.configured && comp.type == input.type && comp.id == input.id) - { - if(comp.type == TYPE_HAT) - { - return (input.value == 0 || input.value & comp.value); - } + if (comp.configured && comp.type == input.type && comp.id == input.id) { + if (comp.type == TYPE_HAT) + return (input.value == 0 || input.value & comp.value); - if(comp.type == TYPE_AXIS) - { - return input.value == 0 || comp.value == input.value; - }else{ - return true; - } - } - return false; + if (comp.type == TYPE_AXIS) + return input.value == 0 || comp.value == input.value; + else + return true; + } + return false; } bool InputConfig::isMappedLike(const std::string& name, Input input) { - if(name == "left") - { - return isMappedTo("left", input) || isMappedTo("leftanalogleft", input) || isMappedTo("rightanalogleft", input); - }else if(name == "right"){ - return isMappedTo("right", input) || isMappedTo("leftanalogright", input) || isMappedTo("rightanalogright", input); - }else if(name == "up"){ - return isMappedTo("up", input) || isMappedTo("leftanalogup", input) || isMappedTo("rightanalogup", input); - }else if(name == "down"){ - return isMappedTo("down", input) || isMappedTo("leftanalogdown", input) || isMappedTo("rightanalogdown", input); - }else if(name == "leftshoulder"){ - return isMappedTo("leftshoulder", input) || isMappedTo("pageup", input); - }else if(name == "rightshoulder"){ - return isMappedTo("rightshoulder", input) || isMappedTo("pagedown", input); - }else if(name == "lefttrigger"){ - return isMappedTo("lefttrigger", input) || isMappedTo("home", input); - }else if(name == "righttrigger"){ - return isMappedTo("righttrigger", input) || isMappedTo("end", input); - } - return isMappedTo(name, input); + if (name == "left") { + return isMappedTo("left", input) || isMappedTo("leftanalogleft", input) || + isMappedTo("rightanalogleft", input); + } + else if (name == "right") { + return isMappedTo("right", input) || isMappedTo("leftanalogright", input) || + isMappedTo("rightanalogright", input); + } + else if (name == "up") { + return isMappedTo("up", input) || isMappedTo("leftanalogup", input) || + isMappedTo("rightanalogup", input); + } + else if (name == "down") { + return isMappedTo("down", input) || isMappedTo("leftanalogdown", input) || + isMappedTo("rightanalogdown", input); + } + else if (name == "leftshoulder") { + return isMappedTo("leftshoulder", input) || isMappedTo("pageup", input); + } + else if (name == "rightshoulder") { + return isMappedTo("rightshoulder", input) || isMappedTo("pagedown", input); + } + else if (name == "lefttrigger") { + return isMappedTo("lefttrigger", input) || isMappedTo("home", input); + } + else if (name == "righttrigger") { + return isMappedTo("righttrigger", input) || isMappedTo("end", input); + } + return isMappedTo(name, input); } std::vector InputConfig::getMappedTo(Input input) { - std::vector maps; + std::vector maps; - typedef std::map::const_iterator it_type; - for(it_type iterator = mNameMap.cbegin(); iterator != mNameMap.cend(); iterator++) - { - Input chk = iterator->second; + typedef std::map::const_iterator it_type; + for (it_type iterator = mNameMap.cbegin(); iterator != mNameMap.cend(); iterator++) { + Input chk = iterator->second; - if(!chk.configured) - continue; + if (!chk.configured) + continue; - if(chk.device == input.device && chk.type == input.type && chk.id == input.id) - { - if(chk.type == TYPE_HAT) - { - if(input.value == 0 || input.value & chk.value) - { - maps.push_back(iterator->first); - } - continue; - } + if (chk.device == input.device && chk.type == input.type && chk.id == input.id) { + if (chk.type == TYPE_HAT) { + if (input.value == 0 || input.value & chk.value) + maps.push_back(iterator->first); + continue; + } - if(input.type == TYPE_AXIS) - { - if(input.value == 0 || chk.value == input.value) - maps.push_back(iterator->first); - }else{ - maps.push_back(iterator->first); - } - } - } - - return maps; + if (input.type == TYPE_AXIS) { + if (input.value == 0 || chk.value == input.value) + maps.push_back(iterator->first); + } + else { + maps.push_back(iterator->first); + } + } + } + return maps; } void InputConfig::loadFromXML(pugi::xml_node& node) { - clear(); + clear(); - for(pugi::xml_node input = node.child("input"); input; input = input.next_sibling("input")) - { - std::string name = input.attribute("name").as_string(); - std::string type = input.attribute("type").as_string(); - InputType typeEnum = stringToInputType(type); + for (pugi::xml_node input = node.child("input"); input; input = input.next_sibling("input")) { + std::string name = input.attribute("name").as_string(); + std::string type = input.attribute("type").as_string(); + InputType typeEnum = stringToInputType(type); - if(typeEnum == TYPE_COUNT) - { - LOG(LogError) << "InputConfig load error - input of type \"" << type << "\" is invalid! Skipping input \"" << name << "\".\n"; - continue; - } + if (typeEnum == TYPE_COUNT) { + LOG(LogError) << "InputConfig load error - input of type \"" << type << + "\" is invalid! Skipping input \"" << name << "\".\n"; + continue; + } - int id = input.attribute("id").as_int(); - int value = input.attribute("value").as_int(); + int id = input.attribute("id").as_int(); + int value = input.attribute("value").as_int(); - if(value == 0) - LOG(LogWarning) << "WARNING: InputConfig value is 0 for " << type << " " << id << "!\n"; + if (value == 0) + LOG(LogWarning) << "WARNING: InputConfig value is 0 for " << + type << " " << id << "!\n"; - mNameMap[toLower(name)] = Input(mDeviceId, typeEnum, id, value, true); - } + mNameMap[toLower(name)] = Input(mDeviceId, typeEnum, id, value, true); + } } void InputConfig::writeToXML(pugi::xml_node& parent) { - pugi::xml_node cfg = parent.append_child("inputConfig"); + pugi::xml_node cfg = parent.append_child("inputConfig"); - if(mDeviceId == DEVICE_KEYBOARD) - { - cfg.append_attribute("type") = "keyboard"; - cfg.append_attribute("deviceName") = "Keyboard"; - } - else if(mDeviceId == DEVICE_CEC) - { - cfg.append_attribute("type") = "cec"; - cfg.append_attribute("deviceName") = "CEC"; - } - else - { - cfg.append_attribute("type") = "joystick"; - cfg.append_attribute("deviceName") = mDeviceName.c_str(); - } + if (mDeviceId == DEVICE_KEYBOARD) { + cfg.append_attribute("type") = "keyboard"; + cfg.append_attribute("deviceName") = "Keyboard"; + } + else if (mDeviceId == DEVICE_CEC) { + cfg.append_attribute("type") = "cec"; + cfg.append_attribute("deviceName") = "CEC"; + } + else { + cfg.append_attribute("type") = "joystick"; + cfg.append_attribute("deviceName") = mDeviceName.c_str(); + } - cfg.append_attribute("deviceGUID") = mDeviceGUID.c_str(); + cfg.append_attribute("deviceGUID") = mDeviceGUID.c_str(); - typedef std::map::const_iterator it_type; - for(it_type iterator = mNameMap.cbegin(); iterator != mNameMap.cend(); iterator++) - { - if(!iterator->second.configured) - continue; + typedef std::map::const_iterator it_type; + for (it_type iterator = mNameMap.cbegin(); iterator != mNameMap.cend(); iterator++) { + if (!iterator->second.configured) + continue; - pugi::xml_node input = cfg.append_child("input"); - input.append_attribute("name") = iterator->first.c_str(); - input.append_attribute("type") = inputTypeToString(iterator->second.type).c_str(); - input.append_attribute("id").set_value(iterator->second.id); - input.append_attribute("value").set_value(iterator->second.value); - } + pugi::xml_node input = cfg.append_child("input"); + input.append_attribute("name") = iterator->first.c_str(); + input.append_attribute("type") = inputTypeToString(iterator->second.type).c_str(); + input.append_attribute("id").set_value(iterator->second.id); + input.append_attribute("value").set_value(iterator->second.value); + } } diff --git a/es-core/src/InputConfig.h b/es-core/src/InputConfig.h index 4969d8ff0..5473742eb 100644 --- a/es-core/src/InputConfig.h +++ b/es-core/src/InputConfig.h @@ -1,3 +1,9 @@ +// +// InputConfig.h +// +// Input device configuration functions. +// + #pragma once #ifndef ES_CORE_INPUT_CONFIG_H #define ES_CORE_INPUT_CONFIG_H @@ -14,119 +20,126 @@ namespace pugi { class xml_node; } #define DEVICE_KEYBOARD -1 #define DEVICE_CEC -2 -enum InputType -{ - TYPE_AXIS, - TYPE_BUTTON, - TYPE_HAT, - TYPE_KEY, - TYPE_CEC_BUTTON, - TYPE_COUNT +enum InputType { + TYPE_AXIS, + TYPE_BUTTON, + TYPE_HAT, + TYPE_KEY, + TYPE_CEC_BUTTON, + TYPE_COUNT }; struct Input { public: - int device; - InputType type; - int id; - int value; - bool configured; + int device; + InputType type; + int id; + int value; + bool configured; - Input() - { - device = DEVICE_KEYBOARD; - configured = false; - id = -1; - value = -999; - type = TYPE_COUNT; - } + Input() + { + device = DEVICE_KEYBOARD; + configured = false; + id = -1; + value = -999; + 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 getHatDir(int val) - { - if(val & SDL_HAT_UP) - return "up"; - else if(val & SDL_HAT_DOWN) - return "down"; - else if(val & SDL_HAT_LEFT) - return "left"; - else if(val & SDL_HAT_RIGHT) - return "right"; - return "neutral?"; - } + std::string getHatDir(int val) + { + if (val & SDL_HAT_UP) + return "up"; + else if (val & SDL_HAT_DOWN) + return "down"; + else if (val & SDL_HAT_LEFT) + return "left"; + else if (val & SDL_HAT_RIGHT) + return "right"; + return "neutral?"; + } - std::string getCECButtonName(int keycode) - { - return CECInput::getKeyCodeString(keycode); - } + std::string getCECButtonName(int keycode) + { + return CECInput::getKeyCodeString(keycode); + } - std::string string() - { - std::stringstream stream; - switch(type) - { - case TYPE_BUTTON: - stream << "Button " << id; - break; - case TYPE_AXIS: - stream << "Axis " << id << (value > 0 ? "+" : "-"); - break; - case TYPE_HAT: - stream << "Hat " << id << " " << getHatDir(value); - break; - case TYPE_KEY: - stream << "Key " << SDL_GetKeyName((SDL_Keycode)id); - break; - case TYPE_CEC_BUTTON: - stream << "CEC-Button " << getCECButtonName(id); - break; - default: - stream << "Input to string error"; - break; - } + std::string string() + { + std::stringstream stream; + switch (type) { + case TYPE_BUTTON: + stream << "Button " << id; + break; + case TYPE_AXIS: + stream << "Axis " << id << (value > 0 ? "+" : "-"); + break; + case TYPE_HAT: + stream << "Hat " << id << " " << getHatDir(value); + break; + case TYPE_KEY: + stream << "Key " << SDL_GetKeyName((SDL_Keycode)id); + break; + case TYPE_CEC_BUTTON: + stream << "CEC-Button " << getCECButtonName(id); + break; + default: + stream << "Input to string error"; + break; + } - return stream.str(); - } + return stream.str(); + } }; class InputConfig { public: - InputConfig(int deviceId, const std::string& deviceName, const std::string& deviceGUID); + InputConfig(int deviceId, const std::string& deviceName, const std::string& deviceGUID); - void clear(); - void mapInput(const std::string& name, Input input); - void unmapInput(const std::string& name); // unmap all Inputs mapped to this name + void clear(); + void mapInput(const std::string& name, Input input); + void unmapInput(const std::string& name); // Unmap all Inputs mapped to this name. - inline int getDeviceId() const { return mDeviceId; }; - inline const std::string& getDeviceName() { return mDeviceName; } - inline const std::string& getDeviceGUIDString() { return mDeviceGUID; } + inline int getDeviceId() const { return mDeviceId; }; + inline const std::string& getDeviceName() { return mDeviceName; } + inline const std::string& getDeviceGUIDString() { return mDeviceGUID; } - //Returns true if Input is mapped to this name, false otherwise. - bool isMappedTo(const std::string& name, Input input); - bool isMappedLike(const std::string& name, Input input); + // Returns true if Input is mapped to this name, false otherwise. + bool isMappedTo(const std::string& name, Input input); + bool isMappedLike(const std::string& name, Input input); - //Returns a list of names this input is mapped to. - std::vector getMappedTo(Input input); + // Returns a list of names this input is mapped to. + std::vector getMappedTo(Input input); - // Returns true if there is an Input mapped to this name, false otherwise. - // Writes Input mapped to this name to result if true. - bool getInputByName(const std::string& name, Input* result); + // Returns true if there is an Input mapped to this name, false otherwise. + // Writes Input mapped to this name to result if true. + bool getInputByName(const std::string& name, Input* result); - void loadFromXML(pugi::xml_node& root); - void writeToXML(pugi::xml_node& parent); + void loadFromXML(pugi::xml_node& root); + void writeToXML(pugi::xml_node& parent); - bool isConfigured(); + bool isConfigured(); private: - std::map mNameMap; - const int mDeviceId; - const std::string mDeviceName; - const std::string mDeviceGUID; + std::map mNameMap; + const int mDeviceId; + const std::string mDeviceName; + const std::string mDeviceGUID; }; #endif // ES_CORE_INPUT_CONFIG_H diff --git a/es-core/src/InputManager.cpp b/es-core/src/InputManager.cpp index 296ca37ba..8d1ea42b8 100644 --- a/es-core/src/InputManager.cpp +++ b/es-core/src/InputManager.cpp @@ -455,7 +455,7 @@ std::string InputManager::getTemporaryConfigPath() bool InputManager::initialized() const { - return mKeyboardInputConfig != NULL; + return mKeyboardInputConfig != nullptr; } int InputManager::getNumConfiguredDevices() diff --git a/es-core/src/guis/GuiDetectDevice.cpp b/es-core/src/guis/GuiDetectDevice.cpp index a12c28ccf..9dfb82c9e 100644 --- a/es-core/src/guis/GuiDetectDevice.cpp +++ b/es-core/src/guis/GuiDetectDevice.cpp @@ -1,3 +1,9 @@ +// +// GuiDetectDevice.cpp +// +// Detect input devices (keyboards, joysticks and gamepads). +// + #include "guis/GuiDetectDevice.h" #include "components/TextComponent.h" @@ -10,117 +16,124 @@ #define HOLD_TIME 1000 -GuiDetectDevice::GuiDetectDevice(Window* window, bool firstRun, const std::function& doneCallback) : GuiComponent(window), mFirstRun(firstRun), - mBackground(window, ":/graphics/frame.png"), mGrid(window, Vector2i(1, 5)) +GuiDetectDevice::GuiDetectDevice( + Window* window, + bool firstRun, + const std::function& doneCallback) + : GuiComponent(window), + mFirstRun(firstRun), + mBackground(window, ":/graphics/frame.png"), + mGrid(window, Vector2i(1, 5)) { - mHoldingConfig = NULL; - mHoldTime = 0; - mDoneCallback = doneCallback; + mHoldingConfig = nullptr; + mHoldTime = 0; + mDoneCallback = doneCallback; - addChild(&mBackground); - addChild(&mGrid); + addChild(&mBackground); + addChild(&mGrid); - // title - mTitle = std::make_shared(mWindow, firstRun ? "WELCOME" : "CONFIGURE INPUT", - Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); - mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(1, 1), GridFlags::BORDER_BOTTOM); + // Title. + mTitle = std::make_shared(mWindow, firstRun ? "WELCOME" : "CONFIGURE INPUT", + Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); + mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(1, 1), GridFlags::BORDER_BOTTOM); - // device info - std::stringstream deviceInfo; - int numDevices = InputManager::getInstance()->getNumJoysticks(); + // Device info. + std::stringstream deviceInfo; + int numDevices = InputManager::getInstance()->getNumJoysticks(); - if(numDevices > 0) - deviceInfo << numDevices << " GAMEPAD" << (numDevices > 1 ? "S" : "") << " DETECTED"; - else - deviceInfo << "NO GAMEPADS DETECTED"; - mDeviceInfo = std::make_shared(mWindow, deviceInfo.str(), Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER); - mGrid.setEntry(mDeviceInfo, Vector2i(0, 1), false, true); + if (numDevices > 0) + deviceInfo << numDevices << " GAMEPAD" << (numDevices > 1 ? "S" : "") << " DETECTED"; + else + deviceInfo << "NO GAMEPADS DETECTED"; + mDeviceInfo = std::make_shared(mWindow, deviceInfo.str(), + Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER); + mGrid.setEntry(mDeviceInfo, Vector2i(0, 1), false, true); - // message - mMsg1 = std::make_shared(mWindow, "HOLD A BUTTON ON YOUR DEVICE TO CONFIGURE IT.", Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER); - mGrid.setEntry(mMsg1, Vector2i(0, 2), false, true); + // Message. + mMsg1 = std::make_shared(mWindow, + "HOLD A BUTTON ON YOUR DEVICE TO CONFIGURE IT.", + Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER); + mGrid.setEntry(mMsg1, Vector2i(0, 2), false, true); - const char* msg2str = firstRun ? "PRESS 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); + const char* msg2str = firstRun ? "PRESS 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); - mGrid.setEntry(mDeviceHeld, Vector2i(0, 4), false, true); + // Currently held device. + mDeviceHeld = std::make_shared(mWindow, "", + Font::get(FONT_SIZE_MEDIUM), 0xFFFFFFFF, ALIGN_CENTER); + mGrid.setEntry(mDeviceHeld, Vector2i(0, 4), false, true); - setSize(Renderer::getScreenWidth() * 0.6f, Renderer::getScreenHeight() * 0.5f); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2); + setSize(Renderer::getScreenWidth() * 0.6f, Renderer::getScreenHeight() * 0.5f); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, + (Renderer::getScreenHeight() - mSize.y()) / 2); } void GuiDetectDevice::onSizeChanged() { - mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); - // grid - mGrid.setSize(mSize); - mGrid.setRowHeightPerc(0, mTitle->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()); + // Grid. + mGrid.setSize(mSize); + mGrid.setRowHeightPerc(0, mTitle->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()); } bool GuiDetectDevice::input(InputConfig* config, Input input) { - PowerSaver::pause(); + PowerSaver::pause(); - if(!mFirstRun && input.device == DEVICE_KEYBOARD && input.type == TYPE_KEY && input.value && input.id == SDLK_ESCAPE) - { - // cancel configuring - PowerSaver::resume(); - delete this; - return true; - } + if (!mFirstRun && input.device == DEVICE_KEYBOARD && input.type == TYPE_KEY && + input.value && input.id == SDLK_ESCAPE) { + // Cancel the configuration. + PowerSaver::resume(); + delete this; + return true; + } - if(input.type == TYPE_BUTTON || input.type == TYPE_KEY ||input.type == TYPE_CEC_BUTTON) - { - if(input.value && mHoldingConfig == NULL) - { - // started holding - mHoldingConfig = config; - mHoldTime = HOLD_TIME; - mDeviceHeld->setText(Utils::String::toUpper(config->getDeviceName())); - }else if(!input.value && mHoldingConfig == config) - { - // cancel - mHoldingConfig = NULL; - mDeviceHeld->setText(""); - } - } - - return true; + if (input.type == TYPE_BUTTON || input.type == TYPE_KEY ||input.type == TYPE_CEC_BUTTON) { + if (input.value && mHoldingConfig == nullptr) { + // Started holding. + mHoldingConfig = config; + mHoldTime = HOLD_TIME; + mDeviceHeld->setText(Utils::String::toUpper(config->getDeviceName())); + } + else if (!input.value && mHoldingConfig == config) { + // Cancel. + mHoldingConfig = nullptr; + mDeviceHeld->setText(""); + } + } + return true; } void GuiDetectDevice::update(int deltaTime) { - if(mHoldingConfig) - { - // If ES starts and if a known device is connected after startup skip controller configuration - if(mFirstRun && Utils::FileSystem::exists(InputManager::getConfigPath()) && InputManager::getInstance()->getNumConfiguredDevices() > 0) - { - if(mDoneCallback) - mDoneCallback(); - PowerSaver::resume(); - delete this; // delete GUI element - } - else - { - mHoldTime -= deltaTime; - const float t = (float)mHoldTime / HOLD_TIME; - unsigned int c = (unsigned char)(t * 255); - mDeviceHeld->setColor((c << 24) | (c << 16) | (c << 8) | 0xFF); - if(mHoldTime <= 0) - { - // picked one! - mWindow->pushGui(new GuiInputConfig(mWindow, mHoldingConfig, true, mDoneCallback)); - PowerSaver::resume(); - delete this; - } - } - } + if (mHoldingConfig) { + // If ES starts and if a known device is connected after startup + // skip controller configuration. + if (mFirstRun && Utils::FileSystem::exists(InputManager::getConfigPath()) && + InputManager::getInstance()->getNumConfiguredDevices() > 0) { + if (mDoneCallback) + mDoneCallback(); + PowerSaver::resume(); + delete this; // Delete GUI element. + } + else { + mHoldTime -= deltaTime; + const float t = (float)mHoldTime / HOLD_TIME; + unsigned int c = (unsigned char)(t * 255); + mDeviceHeld->setColor((c << 24) | (c << 16) | (c << 8) | 0xFF); + if (mHoldTime <= 0) { + // Picked one! + mWindow->pushGui(new GuiInputConfig(mWindow, mHoldingConfig, true, mDoneCallback)); + PowerSaver::resume(); + delete this; + } + } + } } diff --git a/es-core/src/guis/GuiDetectDevice.h b/es-core/src/guis/GuiDetectDevice.h index f1b0f603d..f0bc9651e 100644 --- a/es-core/src/guis/GuiDetectDevice.h +++ b/es-core/src/guis/GuiDetectDevice.h @@ -1,3 +1,9 @@ +// +// GuiDetectDevice.h +// +// Detect input devices (keyboards, joysticks and gamepads). +// + #pragma once #ifndef ES_CORE_GUIS_GUI_DETECT_DEVICE_H #define ES_CORE_GUIS_GUI_DETECT_DEVICE_H @@ -11,27 +17,27 @@ class TextComponent; class GuiDetectDevice : public GuiComponent { public: - GuiDetectDevice(Window* window, bool firstRun, const std::function& doneCallback); + GuiDetectDevice(Window* window, bool firstRun, const std::function& doneCallback); - bool input(InputConfig* config, Input input) override; - void update(int deltaTime) override; - void onSizeChanged() override; + bool input(InputConfig* config, Input input) override; + void update(int deltaTime) override; + void onSizeChanged() override; private: - bool mFirstRun; - InputConfig* mHoldingConfig; - int mHoldTime; + bool mFirstRun; + InputConfig* mHoldingConfig; + int mHoldTime; - NinePatchComponent mBackground; - ComponentGrid mGrid; + NinePatchComponent mBackground; + ComponentGrid mGrid; - std::shared_ptr mTitle; - std::shared_ptr mMsg1; - std::shared_ptr mMsg2; - std::shared_ptr mDeviceInfo; - std::shared_ptr mDeviceHeld; + std::shared_ptr mTitle; + std::shared_ptr mMsg1; + std::shared_ptr mMsg2; + std::shared_ptr mDeviceInfo; + std::shared_ptr mDeviceHeld; - std::function mDoneCallback; + std::function mDoneCallback; }; #endif // ES_CORE_GUIS_GUI_DETECT_DEVICE_H diff --git a/es-core/src/guis/GuiInputConfig.cpp b/es-core/src/guis/GuiInputConfig.cpp index 0a8ce29ab..17ce6150c 100755 --- a/es-core/src/guis/GuiInputConfig.cpp +++ b/es-core/src/guis/GuiInputConfig.cpp @@ -1,3 +1,9 @@ +// +// GuiInputConfig.cpp +// +// Input device configuration GUI (for keyboards, joysticks and gamepads). +// + #include "guis/GuiInputConfig.h" #include "components/ButtonComponent.h" @@ -7,194 +13,209 @@ #include "Log.h" #include "Window.h" -struct InputConfigStructure -{ - const char* name; - const bool skippable; - const char* dispName; - const char* icon; +struct InputConfigStructure { + const char* name; + const bool skippable; + const char* dispName; + const char* icon; }; static const int inputCount = 22; + static const InputConfigStructure GUI_INPUT_CONFIG_LIST[inputCount] = { - { "Up", false, "D-PAD UP", ":/help/dpad_up.svg" }, - { "Down", false, "D-PAD DOWN", ":/help/dpad_down.svg" }, - { "Left", false, "D-PAD LEFT", ":/help/dpad_left.svg" }, - { "Right", false, "D-PAD RIGHT", ":/help/dpad_right.svg" }, - { "Start", true, "START", ":/help/button_start.svg" }, - { "Select", true, "SELECT", ":/help/button_select.svg" }, - { "A", false, "BUTTON A / EAST", ":/help/buttons_east.svg" }, - { "B", true, "BUTTON B / SOUTH", ":/help/buttons_south.svg" }, - { "X", true, "BUTTON X / NORTH", ":/help/buttons_north.svg" }, - { "Y", true, "BUTTON Y / WEST", ":/help/buttons_west.svg" }, - { "LeftShoulder", true, "LEFT SHOULDER", ":/help/button_l.svg" }, - { "RightShoulder", true, "RIGHT SHOULDER", ":/help/button_r.svg" }, - { "LeftTrigger", true, "LEFT TRIGGER", ":/help/button_lt.svg" }, - { "RightTrigger", true, "RIGHT TRIGGER", ":/help/button_rt.svg" }, + { "Up", false, "D-PAD UP", ":/help/dpad_up.svg" }, + { "Down", false, "D-PAD DOWN", ":/help/dpad_down.svg" }, + { "Left", false, "D-PAD LEFT", ":/help/dpad_left.svg" }, + { "Right", false, "D-PAD RIGHT", ":/help/dpad_right.svg" }, + { "Start", true, "START", ":/help/button_start.svg" }, + { "Select", true, "SELECT", ":/help/button_select.svg" }, + { "A", false, "BUTTON A / EAST", ":/help/buttons_east.svg" }, + { "B", true, "BUTTON B / SOUTH", ":/help/buttons_south.svg" }, + { "X", true, "BUTTON X / NORTH", ":/help/buttons_north.svg" }, + { "Y", true, "BUTTON Y / WEST", ":/help/buttons_west.svg" }, + { "LeftShoulder", true, "LEFT SHOULDER", ":/help/button_l.svg" }, + { "RightShoulder", true, "RIGHT SHOULDER", ":/help/button_r.svg" }, + { "LeftTrigger", true, "LEFT TRIGGER", ":/help/button_lt.svg" }, + { "RightTrigger", true, "RIGHT TRIGGER", ":/help/button_rt.svg" }, // { "LeftThumb", true, "LEFT THUMB", ":/help/analog_thumb.svg" }, // { "RightThumb", true, "RIGHT THUMB", ":/help/analog_thumb.svg" }, - { "LeftAnalogUp", true, "LEFT ANALOG UP", ":/help/analog_up.svg" }, - { "LeftAnalogDown", true, "LEFT ANALOG DOWN", ":/help/analog_down.svg" }, - { "LeftAnalogLeft", true, "LEFT ANALOG LEFT", ":/help/analog_left.svg" }, - { "LeftAnalogRight", true, "LEFT ANALOG RIGHT", ":/help/analog_right.svg" }, - { "RightAnalogUp", true, "RIGHT ANALOG UP", ":/help/analog_up.svg" }, - { "RightAnalogDown", true, "RIGHT ANALOG DOWN", ":/help/analog_down.svg" }, - { "RightAnalogLeft", true, "RIGHT ANALOG LEFT", ":/help/analog_left.svg" }, - { "RightAnalogRight", true, "RIGHT ANALOG RIGHT", ":/help/analog_right.svg" }, + { "LeftAnalogUp", true, "LEFT ANALOG UP", ":/help/analog_up.svg" }, + { "LeftAnalogDown", true, "LEFT ANALOG DOWN", ":/help/analog_down.svg" }, + { "LeftAnalogLeft", true, "LEFT ANALOG LEFT", ":/help/analog_left.svg" }, + { "LeftAnalogRight", true, "LEFT ANALOG RIGHT", ":/help/analog_right.svg" }, + { "RightAnalogUp", true, "RIGHT ANALOG UP", ":/help/analog_up.svg" }, + { "RightAnalogDown", true, "RIGHT ANALOG DOWN", ":/help/analog_down.svg" }, + { "RightAnalogLeft", true, "RIGHT ANALOG LEFT", ":/help/analog_left.svg" }, + { "RightAnalogRight", true, "RIGHT ANALOG RIGHT", ":/help/analog_right.svg" }, // { "HotKeyEnable", true, "HOTKEY ENABLE", ":/help/button_hotkey.svg" } }; -//MasterVolUp and MasterVolDown are also hooked up, but do not appear on this screen. -//If you want, you can manually add them to es_input.cfg. +// MasterVolUp and MasterVolDown are also hooked up, but do not appear on this screen. +// If you want, you can manually add them to es_input.cfg. #define HOLD_TO_SKIP_MS 1000 -GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target, bool reconfigureAll, const std::function& okCallback) : GuiComponent(window), - mBackground(window, ":/graphics/frame.png"), mGrid(window, Vector2i(1, 7)), - mTargetConfig(target), mHoldingInput(false), mBusyAnim(window) +GuiInputConfig::GuiInputConfig( + Window* window, + InputConfig* target, + bool reconfigureAll, + const std::function& okCallback) + : GuiComponent(window), + mBackground(window, ":/graphics/frame.png"), + mGrid(window, Vector2i(1, 7)), + mTargetConfig(target), + mHoldingInput(false), + mBusyAnim(window) { - LOG(LogInfo) << "Configuring device " << target->getDeviceId() << " (" << target->getDeviceName() << ")."; + LOG(LogInfo) << "Configuring device " << target->getDeviceId() << " (" << + target->getDeviceName() << ")."; - if(reconfigureAll) - target->clear(); + if (reconfigureAll) + target->clear(); - mConfiguringAll = reconfigureAll; - mConfiguringRow = mConfiguringAll; + mConfiguringAll = reconfigureAll; + mConfiguringRow = mConfiguringAll; - addChild(&mBackground); - addChild(&mGrid); + addChild(&mBackground); + addChild(&mGrid); - // 0 is a spacer row - mGrid.setEntry(std::make_shared(mWindow), Vector2i(0, 0), false); + // 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); - mGrid.setEntry(mTitle, Vector2i(0, 1), false, true); + 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; - if(target->getDeviceId() == DEVICE_KEYBOARD) - ss << "KEYBOARD"; - else if(target->getDeviceId() == DEVICE_CEC) - ss << "CEC"; - else - ss << "GAMEPAD " << (target->getDeviceId() + 1); - 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); + std::stringstream ss; + if (target->getDeviceId() == DEVICE_KEYBOARD) + ss << "KEYBOARD"; + else if (target->getDeviceId() == DEVICE_CEC) + ss << "CEC"; + else + ss << "GAMEPAD " << (target->getDeviceId() + 1); + 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 TO SKIP", Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER); - mGrid.setEntry(mSubtitle2, Vector2i(0, 3), false, true); + mSubtitle2 = std::make_shared(mWindow, "HOLD ANY BUTTON TO SKIP", + Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER); + mGrid.setEntry(mSubtitle2, Vector2i(0, 3), false, true); - // 4 is a spacer row + // 4 is a spacer row. + mList = std::make_shared(mWindow); + mGrid.setEntry(mList, Vector2i(0, 5), true, true); + for (int i = 0; i < inputCount; i++) { + ComponentListRow row; - mList = std::make_shared(mWindow); - mGrid.setEntry(mList, Vector2i(0, 5), true, true); - for(int i = 0; i < inputCount; i++) - { - ComponentListRow row; + // Icon. + auto icon = std::make_shared(mWindow); + icon->setImage(GUI_INPUT_CONFIG_LIST[i].icon); + icon->setColorShift(0x777777FF); + icon->setResize(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * 1.25f); + row.addElement(icon, false); - // icon - auto icon = std::make_shared(mWindow); - icon->setImage(GUI_INPUT_CONFIG_LIST[i].icon); - icon->setColorShift(0x777777FF); - icon->setResize(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * 1.25f); - row.addElement(icon, false); + // Spacer between icon and text. + auto spacer = std::make_shared(mWindow); + spacer->setSize(16, 0); + row.addElement(spacer, false); - // spacer between icon and text - auto spacer = std::make_shared(mWindow); - spacer->setSize(16, 0); - row.addElement(spacer, false); + auto text = std::make_shared(mWindow, + GUI_INPUT_CONFIG_LIST[i].dispName, Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + row.addElement(text, true); - auto text = std::make_shared(mWindow, GUI_INPUT_CONFIG_LIST[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); + setNotDefined(mapping); // Overrides the text and color set above. + row.addElement(mapping, true); + mMappings.push_back(mapping); - auto mapping = std::make_shared(mWindow, "-NOT DEFINED-", Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT), 0x999999FF, ALIGN_RIGHT); - setNotDefined(mapping); // overrides text and color set above - row.addElement(mapping, true); - mMappings.push_back(mapping); + row.input_handler = [this, i, mapping](InputConfig* config, Input input) -> bool { + // Ignore input not from our target device. + if (config != mTargetConfig) + return false; - row.input_handler = [this, i, mapping](InputConfig* config, Input input) -> bool - { - // ignore input not from our target device - if(config != mTargetConfig) - return false; + // If we're not configuring, start configuring when A is pressed. + if (!mConfiguringRow) { + if (config->isMappedTo("a", input) && input.value) { + mList->stopScrolling(); + mConfiguringRow = true; + setPress(mapping); + return true; + } + // We're not configuring and they didn't press A to start, so ignore this. + return false; + } - // if we're not configuring, start configuring when A is pressed - if(!mConfiguringRow) - { - if(config->isMappedTo("a", input) && input.value) - { - mList->stopScrolling(); - mConfiguringRow = true; - setPress(mapping); - return true; - } + // Apply filtering for quirks related to trigger mapping. + if (filterTrigger(input, config, i)) + return false; - // we're not configuring and they didn't press A to start, so ignore this - return false; - } + // We are configuring. + if (input.value != 0) { + // Input down. + // If we're already holding something, ignore this, + // otherwise plan to map this input. + if (mHoldingInput) + return true; - // apply filtering for quirks related to trigger mapping - if(filterTrigger(input, config, i)) - return false; + mHoldingInput = true; + mHeldInput = input; + mHeldTime = 0; + mHeldInputId = i; - // we are configuring - if(input.value != 0) - { - // input down - // if we're already holding something, ignore this, otherwise plan to map this input - if(mHoldingInput) - return true; + return true; + } + else { + // Input up. + // 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) + return true; - mHoldingInput = true; - mHeldInput = input; - mHeldTime = 0; - mHeldInputId = i; + mHoldingInput = false; - return true; - }else{ - // input up - // 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) - return true; + if (assign(mHeldInput, i)) + // If successful, move cursor/stop configuring - if not, + // we'll just try again. + rowDone(); - mHoldingInput = false; + return true; + } + }; + mList->addRow(row); + } - if(assign(mHeldInput, i)) - rowDone(); // if successful, move cursor/stop configuring - if not, we'll just try again + // Only show "HOLD TO SKIP" if this input is skippable. + mList->setCursorChangedCallback([this](CursorState /*state*/) { + bool skippable = GUI_INPUT_CONFIG_LIST[mList->getCursorId()].skippable; + mSubtitle2->setOpacity(skippable * 255); + }); - return true; - } - }; + // Make the first one say "PRESS ANYTHING" if we're re-configuring everything. + if (mConfiguringAll) + setPress(mMappings.front()); - mList->addRow(row); - } - - // only show "HOLD TO SKIP" if this input is skippable - mList->setCursorChangedCallback([this](CursorState /*state*/) { - bool skippable = GUI_INPUT_CONFIG_LIST[mList->getCursorId()].skippable; - mSubtitle2->setOpacity(skippable * 255); - }); - - // make the first one say "PRESS ANYTHING" if we're re-configuring everything - if(mConfiguringAll) - setPress(mMappings.front()); - - // buttons - std::vector< std::shared_ptr > buttons; - std::function okFunction = [this, okCallback] { - InputManager::getInstance()->writeDeviceConfig(mTargetConfig); // save - if(okCallback) - okCallback(); - delete this; - }; - 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(); - // Temporary comments, needs to be properly cleaned up later. + // Buttons. + std::vector< std::shared_ptr > buttons; + std::function okFunction = [this, okCallback] { + InputManager::getInstance()->writeDeviceConfig(mTargetConfig); // save + if (okCallback) + okCallback(); + delete this; + }; + 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(); + // Temporarily commented out, needs to be properly cleaned up later. // 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.", +// "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("Select", &input); @@ -204,184 +225,186 @@ GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target, bool reconfi // "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)); +// mTargetConfig->mapInput("HotKeyEnable", Input(DEVICE_KEYBOARD, +// TYPE_KEY, 0, 1, true)); // okFunction(); // } // )); -// } else { +// } +// else { // okFunction(); // } - })); - mButtonGrid = makeButtonGrid(mWindow, buttons); - mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false); + })); - setSize(Renderer::getScreenWidth() * 0.6f, Renderer::getScreenHeight() * 0.75f); - setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2); + mButtonGrid = makeButtonGrid(mWindow, buttons); + mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false); + + setSize(Renderer::getScreenWidth() * 0.6f, Renderer::getScreenHeight() * 0.75f); + setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, + (Renderer::getScreenHeight() - mSize.y()) / 2); } void GuiInputConfig::onSizeChanged() { - mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); - // update grid - mGrid.setSize(mSize); + // Update grid. + mGrid.setSize(mSize); - //mGrid.setRowHeightPerc(0, 0.025f); - mGrid.setRowHeightPerc(1, mTitle->getFont()->getHeight()*0.75f / mSize.y()); - mGrid.setRowHeightPerc(2, mSubtitle1->getFont()->getHeight() / mSize.y()); - mGrid.setRowHeightPerc(3, mSubtitle2->getFont()->getHeight() / mSize.y()); - //mGrid.setRowHeightPerc(4, 0.03f); - mGrid.setRowHeightPerc(5, (mList->getRowHeight(0) * 5 + 2) / mSize.y()); - mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y() / mSize.y()); + //mGrid.setRowHeightPerc(0, 0.025f); + mGrid.setRowHeightPerc(1, mTitle->getFont()->getHeight()*0.75f / mSize.y()); + mGrid.setRowHeightPerc(2, mSubtitle1->getFont()->getHeight() / mSize.y()); + mGrid.setRowHeightPerc(3, mSubtitle2->getFont()->getHeight() / mSize.y()); + //mGrid.setRowHeightPerc(4, 0.03f); + mGrid.setRowHeightPerc(5, (mList->getRowHeight(0) * 5 + 2) / mSize.y()); + mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y() / mSize.y()); - mBusyAnim.setSize(mSize); + mBusyAnim.setSize(mSize); } void GuiInputConfig::update(int deltaTime) { - if(mConfiguringRow && mHoldingInput && GUI_INPUT_CONFIG_LIST[mHeldInputId].skippable) - { - int prevSec = mHeldTime / 1000; - mHeldTime += deltaTime; - int curSec = mHeldTime / 1000; + if (mConfiguringRow && mHoldingInput && GUI_INPUT_CONFIG_LIST[mHeldInputId].skippable) { + int prevSec = mHeldTime / 1000; + mHeldTime += deltaTime; + int curSec = mHeldTime / 1000; - if(mHeldTime >= HOLD_TO_SKIP_MS) - { - setNotDefined(mMappings.at(mHeldInputId)); - clearAssignment(mHeldInputId); - mHoldingInput = false; - rowDone(); - }else{ - if(prevSec != curSec) - { - // crossed the second boundary, update text - const auto& text = mMappings.at(mHeldInputId); - std::stringstream ss; - ss << "HOLD FOR " << HOLD_TO_SKIP_MS/1000 - curSec << "S TO SKIP"; - text->setText(ss.str()); - text->setColor(0x777777FF); - } - } - } + if (mHeldTime >= HOLD_TO_SKIP_MS) { + setNotDefined(mMappings.at(mHeldInputId)); + clearAssignment(mHeldInputId); + mHoldingInput = false; + rowDone(); + } + else { + if (prevSec != curSec) { + // Crossed the second boundary, update text. + const auto& text = mMappings.at(mHeldInputId); + std::stringstream ss; + ss << "HOLD FOR " << HOLD_TO_SKIP_MS/1000 - curSec << "S TO SKIP"; + text->setText(ss.str()); + text->setColor(0x777777FF); + } + } + } } -// move cursor to the next thing if we're configuring all, -// or come out of "configure mode" if we were only configuring one row +// Move cursor to the next thing if we're configuring all, +// or come out of "configure mode" if we were only configuring one row. void GuiInputConfig::rowDone() { - if(mConfiguringAll) - { - if(!mList->moveCursor(1)) // try to move to the next one - { - // at bottom of list, done - mConfiguringAll = false; - mConfiguringRow = false; - mGrid.moveCursor(Vector2i(0, 1)); - }else{ - // on another one - setPress(mMappings.at(mList->getCursorId())); - } - }else{ - // only configuring one row, so stop - mConfiguringRow = false; - } + if (mConfiguringAll) { + // Try to move to the next one. + if (!mList->moveCursor(1)) { + // At bottom of list, we're done. + mConfiguringAll = false; + mConfiguringRow = false; + mGrid.moveCursor(Vector2i(0, 1)); + } + else { + // On another one. + setPress(mMappings.at(mList->getCursorId())); + } + } + else { + // Only configuring one row, so stop. + mConfiguringRow = false; + } } void GuiInputConfig::setPress(const std::shared_ptr& text) { - text->setText("PRESS ANYTHING"); - text->setColor(0x656565FF); + text->setText("PRESS ANYTHING"); + text->setColor(0x656565FF); } void GuiInputConfig::setNotDefined(const std::shared_ptr& text) { - text->setText("-NOT DEFINED-"); - text->setColor(0x999999FF); + text->setText("-NOT DEFINED-"); + text->setColor(0x999999FF); } void GuiInputConfig::setAssignedTo(const std::shared_ptr& text, Input input) { - text->setText(Utils::String::toUpper(input.string())); - text->setColor(0x777777FF); + text->setText(Utils::String::toUpper(input.string())); + text->setColor(0x777777FF); } void GuiInputConfig::error(const std::shared_ptr& text, const std::string& /*msg*/) { - text->setText("ALREADY TAKEN"); - text->setColor(0x656565FF); + text->setText("ALREADY TAKEN"); + text->setColor(0x656565FF); } bool GuiInputConfig::assign(Input input, int inputId) { - // input is from InputConfig* mTargetConfig + // Input is from InputConfig* mTargetConfig. - // if this input is mapped to something other than "nothing" or the current row, error - // (if it's the same as what it was before, allow it) - if(mTargetConfig->getMappedTo(input).size() > 0 && !mTargetConfig->isMappedTo(GUI_INPUT_CONFIG_LIST[inputId].name, input) && strcmp(GUI_INPUT_CONFIG_LIST[inputId].name, "HotKeyEnable") != 0) - { - error(mMappings.at(inputId), "Already mapped!"); - return false; - } + // 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(GUI_INPUT_CONFIG_LIST[inputId].name, input) && + strcmp(GUI_INPUT_CONFIG_LIST[inputId].name, "HotKeyEnable") != 0) { + error(mMappings.at(inputId), "Already mapped!"); + return false; + } - setAssignedTo(mMappings.at(inputId), input); + setAssignedTo(mMappings.at(inputId), input); - input.configured = true; - mTargetConfig->mapInput(GUI_INPUT_CONFIG_LIST[inputId].name, input); + input.configured = true; + mTargetConfig->mapInput(GUI_INPUT_CONFIG_LIST[inputId].name, input); - LOG(LogInfo) << " Mapping [" << input.string() << "] -> " << GUI_INPUT_CONFIG_LIST[inputId].name; + LOG(LogInfo) << " Mapping [" << input.string() << "] -> " << + GUI_INPUT_CONFIG_LIST[inputId].name; - return true; + return true; } void GuiInputConfig::clearAssignment(int inputId) { - mTargetConfig->unmapInput(GUI_INPUT_CONFIG_LIST[inputId].name); + mTargetConfig->unmapInput(GUI_INPUT_CONFIG_LIST[inputId].name); } bool GuiInputConfig::filterTrigger(Input input, InputConfig* config, int inputId) { -#if defined(__linux__) - // on Linux, some gamepads return both an analog axis and a digital button for the trigger; - // we want the analog axis only, so this function removes the button press event + #if defined(__linux__) + // On Linux, some gamepads return both an analog axis and a digital button for the trigger; + // we want the analog axis only, so this function removes the button press event. - if(( - // match PlayStation joystick with 6 axes only - strstr(config->getDeviceName().c_str(), "PLAYSTATION") != NULL - || strstr(config->getDeviceName().c_str(), "PS3 Ga") != NULL - || strstr(config->getDeviceName().c_str(), "PS(R) Ga") != NULL - // BigBen kid's PS3 gamepad 146b:0902, matched on SDL GUID because its name "Bigben Interactive Bigben Game Pad" may be too generic - || strcmp(config->getDeviceGUIDString().c_str(), "030000006b1400000209000011010000") == 0 - ) && InputManager::getInstance()->getAxisCountByDevice(config->getDeviceId()) == 6) - { - // digital triggers are unwanted - if(input.type == TYPE_BUTTON && (input.id == 6 || input.id == 7)) - { - mHoldingInput = false; - return true; - } - } + if (( + // Match PlayStation joystick with 6 axes only. + strstr(config->getDeviceName().c_str(), "PLAYSTATION") != nullptr || + strstr(config->getDeviceName().c_str(), "PS3 Ga") != nullptr || + strstr(config->getDeviceName().c_str(), "PS(R) Ga") != nullptr || + // BigBen kid's PS3 gamepad 146b:0902, matched on SDL GUID because its name "Bigben + // Interactive Bigben Game Pad" may be too generic. + strcmp(config->getDeviceGUIDString().c_str(), "030000006b1400000209000011010000") == 0) + && InputManager::getInstance()->getAxisCountByDevice(config->getDeviceId()) == 6) { + // Digital triggers are unwanted. + if (input.type == TYPE_BUTTON && (input.id == 6 || input.id == 7)) { + mHoldingInput = false; + return true; + } + } - // ignore negative pole for axes 2/5 only when triggers are being configured - if(input.type == TYPE_AXIS && (input.id == 2 || input.id == 5)) - { - if(strstr(GUI_INPUT_CONFIG_LIST[inputId].name, "Trigger") != NULL) - { - if(input.value == 1) - mSkipAxis = true; - else if(input.value == -1) - return true; - } - else if(mSkipAxis) - { - mSkipAxis = false; - return true; - } - } -#else - (void)input; - (void)config; - (void)inputId; -#endif + // Ignore negative pole for axes 2/5 only when triggers are being configured. + if (input.type == TYPE_AXIS && (input.id == 2 || input.id == 5)) { + if (strstr(GUI_INPUT_CONFIG_LIST[inputId].name, "Trigger") != nullptr) { + if (input.value == 1) + mSkipAxis = true; + else if (input.value == -1) + return true; + } + else if (mSkipAxis) { + mSkipAxis = false; + return true; + } + } + #else + (void)input; + (void)config; + (void)inputId; + #endif - return false; + return false; } diff --git a/es-core/src/guis/GuiInputConfig.h b/es-core/src/guis/GuiInputConfig.h index 3cdf0f910..151e0f8b2 100644 --- a/es-core/src/guis/GuiInputConfig.h +++ b/es-core/src/guis/GuiInputConfig.h @@ -1,3 +1,9 @@ +// +// GuiInputConfig.h +// +// Input device configuration GUI (for keyboards, joysticks and gamepads). +// + #pragma once #ifndef ES_CORE_GUIS_GUI_INPUT_CONFIG_H #define ES_CORE_GUIS_GUI_INPUT_CONFIG_H @@ -13,46 +19,53 @@ 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 update(int deltaTime) override; - - void onSizeChanged() override; + void update(int deltaTime) override; + void onSizeChanged() override; private: - void error(const std::shared_ptr& text, const std::string& msg); // set text to "msg" + not greyed out + // Set text to "msg" + not greyed out. + void error(const std::shared_ptr& text, const std::string& msg); - void setPress(const std::shared_ptr& text); // set text to "PRESS ANYTHING" + not greyed out - void setNotDefined(const std::shared_ptr& text); // set text to -NOT DEFINED- + greyed out - void setAssignedTo(const std::shared_ptr& text, Input input); // set text to "BUTTON 2"/"AXIS 2+", etc. + // Set text to "PRESS ANYTHING" + not greyed out. + void setPress(const std::shared_ptr& text); + // Set text to -NOT DEFINED- + greyed out. + void setNotDefined(const std::shared_ptr& text); + // Set text to "BUTTON 2"/"AXIS 2+", etc. + void setAssignedTo(const std::shared_ptr& text, Input input); - bool assign(Input input, int inputId); - void clearAssignment(int inputId); - bool filterTrigger(Input input, InputConfig* config, int inputId); + bool assign(Input input, int inputId); + void clearAssignment(int inputId); + bool filterTrigger(Input input, InputConfig* config, int inputId); - void rowDone(); + void rowDone(); - NinePatchComponent mBackground; - ComponentGrid mGrid; + NinePatchComponent mBackground; + ComponentGrid mGrid; - std::shared_ptr mTitle; - std::shared_ptr mSubtitle1; - std::shared_ptr mSubtitle2; - std::shared_ptr mList; - std::vector< std::shared_ptr > mMappings; - std::shared_ptr mButtonGrid; + std::shared_ptr mTitle; + std::shared_ptr mSubtitle1; + std::shared_ptr mSubtitle2; + std::shared_ptr mList; + std::vector< std::shared_ptr > mMappings; + std::shared_ptr mButtonGrid; - InputConfig* mTargetConfig; - bool mConfiguringRow; // next input captured by mList will be interpretted as a remap - bool mConfiguringAll; // move the cursor down after configuring a row and start configuring the next row until we reach the bottom + InputConfig* mTargetConfig; + // Next input captured by mList will be interpretted as a remap. + bool mConfiguringRow; + // Move the cursor down after configuring a row and start configuring + // the next row until we reach the bottom. + bool mConfiguringAll; - bool mHoldingInput; - Input mHeldInput; - int mHeldTime; - int mHeldInputId; - bool mSkipAxis; + bool mHoldingInput; + Input mHeldInput; + int mHeldTime; + int mHeldInputId; + bool mSkipAxis; - BusyComponent mBusyAnim; + BusyComponent mBusyAnim; }; #endif // ES_CORE_GUIS_GUI_INPUT_CONFIG_H diff --git a/es-core/src/utils/FileSystemUtil.cpp b/es-core/src/utils/FileSystemUtil.cpp index 6ebb0d0b3..0e0ad2aed 100644 --- a/es-core/src/utils/FileSystemUtil.cpp +++ b/es-core/src/utils/FileSystemUtil.cpp @@ -2,6 +2,8 @@ // FileSystemUtil.cpp // // Low-level filesystem functions. +// Resolve relative paths, resolve symlinks, create directories, +// remove files etc. // #define _FILE_OFFSET_BITS 64 @@ -100,10 +102,10 @@ namespace Utils #else DIR* dir = opendir(path.c_str()); - if (dir != NULL) { + if (dir != nullptr) { struct dirent* entry; // Loop over all files in the directory. - while ((entry = readdir(dir)) != NULL) { + while ((entry = readdir(dir)) != nullptr) { std::string name(entry->d_name); // Ignore "." and ".." diff --git a/es-core/src/utils/FileSystemUtil.h b/es-core/src/utils/FileSystemUtil.h index 1c6122e1b..ac806a195 100644 --- a/es-core/src/utils/FileSystemUtil.h +++ b/es-core/src/utils/FileSystemUtil.h @@ -2,6 +2,8 @@ // FileSystemUtil.h // // Low-level filesystem functions. +// Resolve relative paths, resolve symlinks, create directories, +// remove files etc. // #pragma once diff --git a/es-core/src/utils/StringUtil.cpp b/es-core/src/utils/StringUtil.cpp index 97b4616d4..fa3979b05 100644 --- a/es-core/src/utils/StringUtil.cpp +++ b/es-core/src/utils/StringUtil.cpp @@ -1,3 +1,10 @@ +// +// StringUtil.cpp +// +// Low-level string functions. +// Convert characters to Unicode, upper-/lowercase conversion, string formatting etc. +// + #include "utils/StringUtil.h" #include @@ -5,300 +12,276 @@ namespace Utils { - namespace String - { - unsigned int chars2Unicode(const std::string& _string, size_t& _cursor) - { - const char& c = _string[_cursor]; - unsigned int result = '?'; - - if((c & 0x80) == 0) // 0xxxxxxx, one byte character - { - // 0xxxxxxx - result = ((_string[_cursor++] ) ); - } - else if((c & 0xE0) == 0xC0) // 110xxxxx, two byte character - { - // 110xxxxx 10xxxxxx - result = ((_string[_cursor++] & 0x1F) << 6) | - ((_string[_cursor++] & 0x3F) ); - } - else if((c & 0xF0) == 0xE0) // 1110xxxx, three byte character - { - // 1110xxxx 10xxxxxx 10xxxxxx - result = ((_string[_cursor++] & 0x0F) << 12) | - ((_string[_cursor++] & 0x3F) << 6) | - ((_string[_cursor++] & 0x3F) ); - } - else if((c & 0xF8) == 0xF0) // 11110xxx, four byte character - { - // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - result = ((_string[_cursor++] & 0x07) << 18) | - ((_string[_cursor++] & 0x3F) << 12) | - ((_string[_cursor++] & 0x3F) << 6) | - ((_string[_cursor++] & 0x3F) ); - } - else - { - // error, invalid unicode - ++_cursor; - } - - return result; - - } // chars2Unicode - - std::string unicode2Chars(const unsigned int _unicode) - { - std::string result; - - if(_unicode < 0x80) // one byte character - { - result += ((_unicode ) & 0xFF); - } - else if(_unicode < 0x800) // two byte character - { - result += ((_unicode >> 6) & 0xFF) | 0xC0; - result += ((_unicode ) & 0x3F) | 0x80; - } - else if(_unicode < 0xFFFF) // three byte character - { - result += ((_unicode >> 12) & 0xFF) | 0xE0; - result += ((_unicode >> 6) & 0x3F) | 0x80; - result += ((_unicode ) & 0x3F) | 0x80; - } - else if(_unicode <= 0x1fffff) // four byte character - { - result += ((_unicode >> 18) & 0xFF) | 0xF0; - result += ((_unicode >> 12) & 0x3F) | 0x80; - result += ((_unicode >> 6) & 0x3F) | 0x80; - result += ((_unicode ) & 0x3F) | 0x80; - } - else - { - // error, invalid unicode - result += '?'; - } - - return result; - - } // unicode2Chars - - size_t nextCursor(const std::string& _string, const size_t _cursor) - { - size_t result = _cursor; - - while(result < _string.length()) - { - ++result; - - if((_string[result] & 0xC0) != 0x80) // break if current character is not 10xxxxxx - break; - } - - return result; - - } // nextCursor - - size_t prevCursor(const std::string& _string, const size_t _cursor) - { - size_t result = _cursor; - - while(result > 0) - { - --result; - - if((_string[result] & 0xC0) != 0x80) // break if current character is not 10xxxxxx - break; - } - - return result; - - } // prevCursor - - size_t moveCursor(const std::string& _string, const size_t _cursor, const int _amount) - { - size_t result = _cursor; - - if(_amount > 0) - { - for(int i = 0; i < _amount; ++i) - result = nextCursor(_string, result); - } - else if(_amount < 0) - { - for(int i = _amount; i < 0; ++i) - result = prevCursor(_string, result); - } - - return result; - - } // moveCursor - - std::string toLower(const std::string& _string) - { - std::string string; + namespace String + { + unsigned int chars2Unicode(const std::string& _string, size_t& _cursor) + { + const char& c = _string[_cursor]; + unsigned int result = '?'; + + // 0xxxxxxx, one byte character. + if ((c & 0x80) == 0) { + // 0xxxxxxx + result = ((_string[_cursor++] ) ); + } + // 110xxxxx, two byte character. + else if ((c & 0xE0) == 0xC0) { + // 110xxxxx 10xxxxxx + result = ((_string[_cursor++] & 0x1F) << 6) | + ((_string[_cursor++] & 0x3F) ); + } + // 1110xxxx, three byte character. + else if ((c & 0xF0) == 0xE0) { + // 1110xxxx 10xxxxxx 10xxxxxx + result = ((_string[_cursor++] & 0x0F) << 12) | + ((_string[_cursor++] & 0x3F) << 6) | + ((_string[_cursor++] & 0x3F) ); + } + // 11110xxx, four byte character. + else if ((c & 0xF8) == 0xF0) { + // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + result = ((_string[_cursor++] & 0x07) << 18) | + ((_string[_cursor++] & 0x3F) << 12) | + ((_string[_cursor++] & 0x3F) << 6) | + ((_string[_cursor++] & 0x3F) ); + } + else { + // Error, invalid unicode. + ++_cursor; + } + + return result; + } + + std::string unicode2Chars(const unsigned int _unicode) + { + std::string result; + + // One byte character. + if (_unicode < 0x80) { + result += ((_unicode ) & 0xFF); + } + // Two byte character. + else if (_unicode < 0x800) { + result += ((_unicode >> 6) & 0xFF) | 0xC0; + result += ((_unicode ) & 0x3F) | 0x80; + } + // Three byte character. + else if (_unicode < 0xFFFF) { + result += ((_unicode >> 12) & 0xFF) | 0xE0; + result += ((_unicode >> 6) & 0x3F) | 0x80; + result += ((_unicode ) & 0x3F) | 0x80; + } + // Four byte character. + else if (_unicode <= 0x1fffff) { + result += ((_unicode >> 18) & 0xFF) | 0xF0; + result += ((_unicode >> 12) & 0x3F) | 0x80; + result += ((_unicode >> 6) & 0x3F) | 0x80; + result += ((_unicode ) & 0x3F) | 0x80; + } + else { + // Error, invalid unicode. + result += '?'; + } + + return result; + } + + size_t nextCursor(const std::string& _string, const size_t _cursor) + { + size_t result = _cursor; + + while (result < _string.length()) { + ++result; + + // Break if current character is not 10xxxxxx + if ((_string[result] & 0xC0) != 0x80) + break; + } + + return result; + } + + size_t prevCursor(const std::string& _string, const size_t _cursor) + { + size_t result = _cursor; + + while (result > 0) { + --result; + + // Break if current character is not 10xxxxxx + if ((_string[result] & 0xC0) != 0x80) + break; + } + + return result; + } + + size_t moveCursor(const std::string& _string, const size_t _cursor, const int _amount) + { + size_t result = _cursor; + + if (_amount > 0) { + for (int i = 0; i < _amount; ++i) + result = nextCursor(_string, result); + } + else if (_amount < 0) { + for (int i = _amount; i < 0; ++i) + result = prevCursor(_string, result); + } + + return result; + } + + std::string toLower(const std::string& _string) + { + std::string string; + + for (size_t i = 0; i < _string.length(); ++i) + string += (char)tolower(_string[i]); + + return string; + } + + std::string toUpper(const std::string& _string) + { + std::string string; + + for (size_t i = 0; i < _string.length(); ++i) + string += (char)toupper(_string[i]); + + return string; + } + + std::string trim(const std::string& _string) + { + const size_t strBegin = _string.find_first_not_of(" \t"); + const size_t strEnd = _string.find_last_not_of(" \t"); + + if (strBegin == std::string::npos) + return ""; + + return _string.substr(strBegin, strEnd - strBegin + 1); + } + + std::string replace(const std::string& _string, const std::string& _replace, + const std::string& _with) + { + std::string string = _string; + size_t pos; + + while ((pos = string.find(_replace)) != std::string::npos) + string = string.replace(pos, _replace.length(), _with.c_str(), _with.length()); + + return string; + } + + bool startsWith(const std::string& _string, const std::string& _start) + { + return (_string.find(_start) == 0); + } - for(size_t i = 0; i < _string.length(); ++i) - string += (char)tolower(_string[i]); + bool endsWith(const std::string& _string, const std::string& _end) + { + return (_string.find(_end) == (_string.size() - _end.size())); + } - return string; + std::string removeParenthesis(const std::string& _string) + { + static const char remove[4] = { '(', ')', '[', ']' }; + std::string string = _string; + size_t start; + size_t end; + bool done = false; - } // toLower + while (!done) { + done = true; - std::string toUpper(const std::string& _string) - { - std::string string; + for (size_t i = 0; i < sizeof(remove); i += 2) { + end = string.find_first_of(remove[i + 1]); + start = string.find_last_of( remove[i + 0], end); - for(size_t i = 0; i < _string.length(); ++i) - string += (char)toupper(_string[i]); + if ((start != std::string::npos) && (end != std::string::npos)) { + string.erase(start, end - start + 1); + done = false; + } + } + } - return string; + return trim(string); + } - } // toUpper + stringVector delimitedStringToVector(const std::string& _string, + const std::string& _delimiter, bool sort) + { + stringVector vector; + size_t start = 0; + size_t comma = _string.find(_delimiter); - std::string trim(const std::string& _string) - { - const size_t strBegin = _string.find_first_not_of(" \t"); - const size_t strEnd = _string.find_last_not_of(" \t"); + while (comma != std::string::npos) { + vector.push_back(_string.substr(start, comma - start)); + start = comma + 1; + comma = _string.find(_delimiter, start); + } - if(strBegin == std::string::npos) - return ""; + vector.push_back(_string.substr(start)); + if (sort) + std::sort(vector.begin(), vector.end()); - return _string.substr(strBegin, strEnd - strBegin + 1); + return vector; + } - } // trim + stringVector commaStringToVector(const std::string& _string, bool sort) + { + return delimitedStringToVector(_string, ",", sort); + } - std::string replace(const std::string& _string, const std::string& _replace, const std::string& _with) - { - std::string string = _string; - size_t pos; + std::string vectorToCommaString(stringVector _vector) + { + std::string string; - while((pos = string.find(_replace)) != std::string::npos) - string = string.replace(pos, _replace.length(), _with.c_str(), _with.length()); + std::sort(_vector.begin(), _vector.end()); - return string; + for (stringVector::const_iterator it = _vector.cbegin(); it != _vector.cend(); ++it) + string += (string.length() ? "," : "") + (*it); - } // replace + return string; + } - bool startsWith(const std::string& _string, const std::string& _start) - { - return (_string.find(_start) == 0); + std::string format(const char* _format, ...) + { + va_list args; + va_list copy; - } // startsWith + va_start(args, _format); - bool endsWith(const std::string& _string, const std::string& _end) - { - return (_string.find(_end) == (_string.size() - _end.size())); + va_copy(copy, args); + const int length = vsnprintf(nullptr, 0, _format, copy); + va_end(copy); - } // endsWith + char* buffer = new char[length + 1]; + va_copy(copy, args); + vsnprintf(buffer, length + 1, _format, copy); + va_end(copy); - std::string removeParenthesis(const std::string& _string) - { - static const char remove[4] = { '(', ')', '[', ']' }; - std::string string = _string; - size_t start; - size_t end; - bool done = false; + va_end(args); - while(!done) - { - done = true; + std::string out(buffer); + delete buffer; - for(size_t i = 0; i < sizeof(remove); i += 2) - { - end = string.find_first_of(remove[i + 1]); - start = string.find_last_of( remove[i + 0], end); + return out; + } - if((start != std::string::npos) && (end != std::string::npos)) - { - string.erase(start, end - start + 1); - done = false; - } - } - } + std::string scramble(const std::string& _input, const std::string& _key) + { + std::string buffer = _input; - return trim(string); + for (size_t i = 0; i < _input.size(); ++i) + buffer[i] = _input[i] ^ _key[i]; - } // removeParenthesis + return buffer; + } - stringVector delimitedStringToVector(const std::string& _string, const std::string& _delimiter, bool sort) - { - stringVector vector; - size_t start = 0; - size_t comma = _string.find(_delimiter); - - while(comma != std::string::npos) - { - vector.push_back(_string.substr(start, comma - start)); - start = comma + 1; - comma = _string.find(_delimiter, start); - } - - vector.push_back(_string.substr(start)); - if (sort) - std::sort(vector.begin(), vector.end()); - - return vector; - - } // delimitedStringToVector - - stringVector commaStringToVector(const std::string& _string, bool sort) - { - return delimitedStringToVector(_string, ",", sort); - } // commaStringToVector - - std::string vectorToCommaString(stringVector _vector) - { - std::string string; - - std::sort(_vector.begin(), _vector.end()); - - for(stringVector::const_iterator it = _vector.cbegin(); it != _vector.cend(); ++it) - string += (string.length() ? "," : "") + (*it); - - return string; - - } // vectorToCommaString - - std::string format(const char* _format, ...) - { - va_list args; - va_list copy; - - va_start(args, _format); - - va_copy(copy, args); - const int length = vsnprintf(nullptr, 0, _format, copy); - va_end(copy); - - char* buffer = new char[length + 1]; - va_copy(copy, args); - vsnprintf(buffer, length + 1, _format, copy); - va_end(copy); - - va_end(args); - - std::string out(buffer); - delete buffer; - - return out; - - } // format - - std::string scramble(const std::string& _input, const std::string& _key) - { - std::string buffer = _input; - - for(size_t i = 0; i < _input.size(); ++i) - { - buffer[i] = _input[i] ^ _key[i]; - } - - return buffer; - - } // scramble - - } // String:: + } // String:: } // Utils:: diff --git a/es-core/src/utils/StringUtil.h b/es-core/src/utils/StringUtil.h index 7639d9cab..c9241dcfb 100644 --- a/es-core/src/utils/StringUtil.h +++ b/es-core/src/utils/StringUtil.h @@ -1,3 +1,10 @@ +// +// StringUtil.h +// +// Low-level string functions. +// Convert characters to Unicode, upper-/lowercase conversion, string formatting etc. +// + #pragma once #ifndef ES_CORE_UTILS_STRING_UTIL_H #define ES_CORE_UTILS_STRING_UTIL_H @@ -7,29 +14,31 @@ namespace Utils { - namespace String - { - typedef std::vector stringVector; + namespace String + { + typedef std::vector stringVector; - unsigned int chars2Unicode (const std::string& _string, size_t& _cursor); - std::string unicode2Chars (const unsigned int _unicode); - size_t nextCursor (const std::string& _string, const size_t _cursor); - size_t prevCursor (const std::string& _string, const size_t _cursor); - size_t moveCursor (const std::string& _string, const size_t _cursor, const int _amount); - std::string toLower (const std::string& _string); - std::string toUpper (const std::string& _string); - std::string trim (const std::string& _string); - std::string replace (const std::string& _string, const std::string& _replace, const std::string& _with); - bool startsWith (const std::string& _string, const std::string& _start); - bool endsWith (const std::string& _string, const std::string& _end); - std::string removeParenthesis (const std::string& _string); - stringVector delimitedStringToVector(const std::string& _string, const std::string& _delimiter, bool sort = false); - stringVector commaStringToVector (const std::string& _string, bool sort = false); - std::string vectorToCommaString (stringVector _vector); - std::string format (const char* _string, ...); - std::string scramble (const std::string& _input, const std::string& key); + unsigned int chars2Unicode(const std::string& _string, size_t& _cursor); + std::string unicode2Chars(const unsigned int _unicode); + size_t nextCursor(const std::string& _string, const size_t _cursor); + size_t prevCursor(const std::string& _string, const size_t _cursor); + size_t moveCursor(const std::string& _string, const size_t _cursor, const int _amount); + std::string toLower(const std::string& _string); + std::string toUpper(const std::string& _string); + std::string trim(const std::string& _string); + std::string replace(const std::string& _string, const std::string& _replace, + const std::string& _with); + bool startsWith(const std::string& _string, const std::string& _start); + bool endsWith(const std::string& _string, const std::string& _end); + std::string removeParenthesis(const std::string& _string); + stringVector delimitedStringToVector(const std::string& _string, + const std::string& _delimiter, bool sort = false); + stringVector commaStringToVector(const std::string& _string, bool sort = false); + std::string vectorToCommaString(stringVector _vector); + std::string format(const char* _string, ...); + std::string scramble(const std::string& _input, const std::string& key); - } // String:: + } // String:: } // Utils:: diff --git a/es-core/src/utils/TimeUtil.cpp b/es-core/src/utils/TimeUtil.cpp index 3bad4eb37..3a371aaaf 100644 --- a/es-core/src/utils/TimeUtil.cpp +++ b/es-core/src/utils/TimeUtil.cpp @@ -1,285 +1,254 @@ +// +// TimeUtil.cpp +// +// Low-level date and time functions. +// Set and get time, format to string formats, count days and months etc. +// + #include "utils/TimeUtil.h" #include namespace Utils { - namespace Time - { - DateTime::DateTime() - { - mTime = 0; - mTimeStruct = { 0, 0, 0, 1, 0, 0, 0, 0, -1 }; - mIsoString = "00000000T000000"; + namespace Time + { + DateTime::DateTime() + { + mTime = 0; + mTimeStruct = { 0, 0, 0, 1, 0, 0, 0, 0, -1 }; + mIsoString = "00000000T000000"; + } - } // DateTime::DateTime + DateTime::DateTime(const time_t& _time) + { + setTime(_time); + } - DateTime::DateTime(const time_t& _time) - { - setTime(_time); + DateTime::DateTime(const tm& _timeStruct) + { + setTimeStruct(_timeStruct); + } - } // DateTime::DateTime + DateTime::DateTime(const std::string& _isoString) + { + setIsoString(_isoString); + } - DateTime::DateTime(const tm& _timeStruct) - { - setTimeStruct(_timeStruct); + DateTime::~DateTime() + { + } - } // DateTime::DateTime + void DateTime::setTime(const time_t& _time) + { + mTime = (_time < 0) ? 0 : _time; + mTimeStruct = *localtime(&mTime); + mIsoString = timeToString(mTime); + } - DateTime::DateTime(const std::string& _isoString) - { - setIsoString(_isoString); + void DateTime::setTimeStruct(const tm& _timeStruct) + { + setTime(mktime((tm*)&_timeStruct)); + } - } // DateTime::DateTime + void DateTime::setIsoString(const std::string& _isoString) + { + setTime(stringToTime(_isoString)); + } - DateTime::~DateTime() - { + Duration::Duration(const time_t& _time) + { + mTotalSeconds = (unsigned int)_time; + mDays = (mTotalSeconds - (mTotalSeconds % (60*60*24))) / (60*60*24); + mHours = ((mTotalSeconds % (60*60*24)) - (mTotalSeconds % (60*60))) / (60*60); + mMinutes = ((mTotalSeconds % (60*60)) - (mTotalSeconds % (60))) / 60; + mSeconds = mTotalSeconds % 60; + } - } // DateTime::~DateTime + Duration::~Duration() + { + } - void DateTime::setTime(const time_t& _time) - { - mTime = (_time < 0) ? 0 : _time; - mTimeStruct = *localtime(&mTime); - mIsoString = timeToString(mTime); + time_t now() + { + time_t time; + ::time(&time); + return time; + } - } // DateTime::setTime + time_t stringToTime(const std::string& _string, const std::string& _format) + { + const char* s = _string.c_str(); + const char* f = _format.c_str(); + tm timeStruct = { 0, 0, 0, 1, 0, 0, 0, 0, -1 }; + size_t parsedChars = 0; - void DateTime::setTimeStruct(const tm& _timeStruct) - { - setTime(mktime((tm*)&_timeStruct)); + if (_string == "not-a-date-time") + return mktime(&timeStruct); - } // DateTime::setTimeStruct + while (*f && (parsedChars < _string.length())) { + if (*f == '%') { + ++f; - void DateTime::setIsoString(const std::string& _isoString) - { - setTime(stringToTime(_isoString)); + switch (*f++) { + // The year [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; - } // DateTime::setIsoString + // The 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; - Duration::Duration(const time_t& _time) - { - mTotalSeconds = (unsigned int)_time; - mDays = (mTotalSeconds - (mTotalSeconds % (60*60*24))) / (60*60*24); - mHours = ((mTotalSeconds % (60*60*24)) - (mTotalSeconds % (60*60))) / (60*60); - mMinutes = ((mTotalSeconds % (60*60)) - (mTotalSeconds % (60))) / 60; - mSeconds = mTotalSeconds % 60; + // The 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; - } // Duration::Duration + // The 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; - Duration::~Duration() - { + // The minute [00,59] + case 'M': { + if ((parsedChars + 2) <= _string.length()) { + timeStruct.tm_min = (*s++ - '0') * 10; + timeStruct.tm_min += (*s++ - '0'); + } + parsedChars += 2; + } + break; - } // Duration::~Duration + // The 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 { + ++s; + ++f; + } + } + return mktime(&timeStruct); + } - time_t now() - { - time_t time; - ::time(&time); - return time; + std::string timeToString(const time_t& _time, const std::string& _format) + { + const char* f = _format.c_str(); + const tm timeStruct = *localtime(&_time); + char buf[256] = { '\0' }; + char* s = buf; - } // now + while (*f) { + if (*f == '%') { + ++f; - time_t stringToTime(const std::string& _string, const std::string& _format) - { - const char* s = _string.c_str(); - const char* f = _format.c_str(); - tm timeStruct = { 0, 0, 0, 1, 0, 0, 0, 0, -1 }; - size_t parsedChars = 0; + switch (*f++) { + // The year, including the century (1900) + case 'Y': { + const int year = timeStruct.tm_year + 1900; + *s++ = (char)((year - (year % 1000)) / 1000) + '0'; + *s++ = (char)(((year % 1000) - (year % 100)) / 100) + '0'; + *s++ = (char)(((year % 100) - (year % 10)) / 10) + '0'; + *s++ = (char)(year % 10) + '0'; + } + break; - if(_string == "not-a-date-time") - return mktime(&timeStruct); + // The month number [00,11] + case 'm': { + const int mon = timeStruct.tm_mon + 1; + *s++ = (char)(mon / 10) + '0'; + *s++ = (char)(mon % 10) + '0'; + } + break; - while(*f && (parsedChars < _string.length())) - { - if(*f == '%') - { - ++f; + // The day of the month [01,31] + case 'd': { + *s++ = (char)(timeStruct.tm_mday / 10) + '0'; + *s++ = (char)(timeStruct.tm_mday % 10) + '0'; + } + break; - switch(*f++) - { - case 'Y': // The year [1970,xxxx] - { - 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; - } + // The hour (24-hour clock) [00,23] + case 'H': { + *s++ = (char)(timeStruct.tm_hour / 10) + '0'; + *s++ = (char)(timeStruct.tm_hour % 10) + '0'; + } + break; - parsedChars += 4; - } - break; + // The minute [00,59] + case 'M': { + *s++ = (char)(timeStruct.tm_min / 10) + '0'; + *s++ = (char)(timeStruct.tm_min % 10) + '0'; + } + break; - case 'm': // The month number [01,12] - { - 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; - } + // The second [00,59] + case 'S': { + *s++ = (char)(timeStruct.tm_sec / 10) + '0'; + *s++ = (char)(timeStruct.tm_sec % 10) + '0'; + } + break; + } + } + else { + *s++ = *f++; + } + *s = '\0'; + } + return std::string(buf); + } - parsedChars += 2; - } - break; + int daysInMonth(const int _year, const int _month) + { + tm timeStruct = { 0, 0, 0, 0, _month, _year - 1900, 0, 0, -1 }; + mktime(&timeStruct); - case 'd': // The day of the month [01,31] - { - if((parsedChars + 2) <= _string.length()) - { - timeStruct.tm_mday = (*s++ - '0') * 10; - timeStruct.tm_mday += (*s++ - '0'); - } + return timeStruct.tm_mday; + } - parsedChars += 2; - } - break; + int daysInYear(const int _year) + { + tm timeStruct = { 0, 0, 0, 0, 0, _year - 1900 + 1, 0, 0, -1 }; + mktime(&timeStruct); - case 'H': // The hour (24-hour clock) [00,23] - { - if((parsedChars + 2) <= _string.length()) - { - timeStruct.tm_hour = (*s++ - '0') * 10; - timeStruct.tm_hour += (*s++ - '0'); - } + return timeStruct.tm_yday + 1; + } - parsedChars += 2; - } - break; - - case 'M': // The minute [00,59] - { - if((parsedChars + 2) <= _string.length()) - { - timeStruct.tm_min = (*s++ - '0') * 10; - timeStruct.tm_min += (*s++ - '0'); - } - - parsedChars += 2; - } - break; - - case 'S': // The second [00,59] - { - if((parsedChars + 2) <= _string.length()) - { - timeStruct.tm_sec = (*s++ - '0') * 10; - timeStruct.tm_sec += (*s++ - '0'); - } - - parsedChars += 2; - } - break; - } - } - else - { - ++s; - ++f; - } - } - - return mktime(&timeStruct); - - } // stringToTime - - std::string timeToString(const time_t& _time, const std::string& _format) - { - const char* f = _format.c_str(); - const tm timeStruct = *localtime(&_time); - char buf[256] = { '\0' }; - char* s = buf; - - while(*f) - { - if(*f == '%') - { - ++f; - - switch(*f++) - { - case 'Y': // The year, including the century (1900) - { - const int year = timeStruct.tm_year + 1900; - *s++ = (char)((year - (year % 1000)) / 1000) + '0'; - *s++ = (char)(((year % 1000) - (year % 100)) / 100) + '0'; - *s++ = (char)(((year % 100) - (year % 10)) / 10) + '0'; - *s++ = (char)(year % 10) + '0'; - } - break; - - case 'm': // The month number [00,11] - { - const int mon = timeStruct.tm_mon + 1; - *s++ = (char)(mon / 10) + '0'; - *s++ = (char)(mon % 10) + '0'; - } - break; - - case 'd': // The day of the month [01,31] - { - *s++ = (char)(timeStruct.tm_mday / 10) + '0'; - *s++ = (char)(timeStruct.tm_mday % 10) + '0'; - } - break; - - case 'H': // The hour (24-hour clock) [00,23] - { - *s++ = (char)(timeStruct.tm_hour / 10) + '0'; - *s++ = (char)(timeStruct.tm_hour % 10) + '0'; - } - break; - - case 'M': // The minute [00,59] - { - *s++ = (char)(timeStruct.tm_min / 10) + '0'; - *s++ = (char)(timeStruct.tm_min % 10) + '0'; - } - break; - - case 'S': // The second [00,59] - { - *s++ = (char)(timeStruct.tm_sec / 10) + '0'; - *s++ = (char)(timeStruct.tm_sec % 10) + '0'; - } - break; - } - } - else - { - *s++ = *f++; - } - - *s = '\0'; - } - - return std::string(buf); - - } // timeToString - - int daysInMonth(const int _year, const int _month) - { - tm timeStruct = { 0, 0, 0, 0, _month, _year - 1900, 0, 0, -1 }; - mktime(&timeStruct); - - return timeStruct.tm_mday; - - } // daysInMonth - - int daysInYear(const int _year) - { - tm timeStruct = { 0, 0, 0, 0, 0, _year - 1900 + 1, 0, 0, -1 }; - mktime(&timeStruct); - - return timeStruct.tm_yday + 1; - - } // daysInYear - - } // Time:: + } // Time:: } // Utils:: diff --git a/es-core/src/utils/TimeUtil.h b/es-core/src/utils/TimeUtil.h index 9b44207b3..882b875f7 100644 --- a/es-core/src/utils/TimeUtil.h +++ b/es-core/src/utils/TimeUtil.h @@ -1,3 +1,10 @@ +// +// TimeUtil.h +// +// Low-level date and time functions. +// Set and get time, format to string formats, count days and months etc. +// + #pragma once #ifndef ES_CORE_UTILS_TIME_UTIL_H #define ES_CORE_UTILS_TIME_UTIL_H @@ -6,73 +13,67 @@ namespace Utils { - namespace Time - { - static int NOT_A_DATE_TIME = 0; + namespace Time + { + static int NOT_A_DATE_TIME = 0; - class DateTime - { - public: + class DateTime + { + public: + DateTime(); + DateTime(const time_t& _time); + DateTime(const tm& _timeStruct); + DateTime(const std::string& _isoString); + ~DateTime(); - DateTime(); - DateTime(const time_t& _time); - DateTime(const tm& _timeStruct); - DateTime(const std::string& _isoString); - ~DateTime(); + const bool operator<(const DateTime& _other) const { return (mTime < _other.mTime); } + const bool operator<=(const DateTime& _other) const { return (mTime <= _other.mTime); } + const bool operator>(const DateTime& _other) const { return (mTime > _other.mTime); } + const bool operator>=(const DateTime& _other) const { return (mTime >= _other.mTime); } + operator time_t() const { return mTime; } + operator tm() const { return mTimeStruct; } + operator std::string() const { return mIsoString; } - const bool operator< (const DateTime& _other) const { return (mTime < _other.mTime); } - const bool operator<= (const DateTime& _other) const { return (mTime <= _other.mTime); } - const bool operator> (const DateTime& _other) const { return (mTime > _other.mTime); } - const bool operator>= (const DateTime& _other) const { return (mTime >= _other.mTime); } - operator time_t () const { return mTime; } - operator tm () const { return mTimeStruct; } - operator std::string() const { return mIsoString; } + void setTime(const time_t& _time); + 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 setTime (const time_t& _time); - 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; } + private: + time_t mTime; + tm mTimeStruct; + std::string mIsoString; + }; - private: + class Duration + { + public: + Duration(const time_t& _time); + ~Duration(); - time_t mTime; - tm mTimeStruct; - std::string mIsoString; + unsigned int getDays() const { return mDays; } + unsigned int getHours() const { return mHours; } + unsigned int getMinutes() const { return mMinutes; } + unsigned int getSeconds() const { return mSeconds; } - }; // DateTime - - class Duration - { - public: - - Duration(const time_t& _time); - ~Duration(); - - unsigned int getDays () const { return mDays; } - unsigned int getHours () const { return mHours; } - unsigned int getMinutes() const { return mMinutes; } - unsigned int getSeconds() const { return mSeconds; } - - private: - - unsigned int mTotalSeconds; - unsigned int mDays; - unsigned int mHours; - unsigned int mMinutes; - unsigned int mSeconds; - - }; // Duration - - time_t now (); - time_t stringToTime(const std::string& _string, const std::string& _format = "%Y%m%dT%H%M%S"); - 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); - - } // Time:: + private: + unsigned int mTotalSeconds; + unsigned int mDays; + unsigned int mHours; + unsigned int mMinutes; + unsigned int mSeconds; + }; + time_t now(); + time_t stringToTime(const std::string& _string, + const std::string& _format = "%Y%m%dT%H%M%S"); + 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:: #endif // ES_CORE_UTILS_TIME_UTIL_H