Large code cleanup and code documentation update.

The initial code cleanup of es-app is now complete as of this commit.
This commit is contained in:
Leon Styhre 2020-06-23 20:07:00 +02:00
parent d29e16778d
commit 6b62065595
41 changed files with 2371 additions and 2289 deletions

View file

@ -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`

View file

@ -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
<?xml version="1.0"?>
@ -194,23 +198,25 @@ Here's an overview of the `es_systems.cfg` file layout:
</systemList>
```
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:
`<string name="MediaDirectory" value="~/games/images/emulationstation" />`

View file

@ -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
========================

View file

@ -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:

View file

@ -75,8 +75,8 @@ CollectionSystemManager::CollectionSystemManager(Window* window) : mWindow(windo
mIsEditingCustom = false;
mEditingCollection = "Favorites";
mEditingCollectionSystemData = NULL;
mCustomCollectionsBundle = NULL;
mEditingCollectionSystemData = nullptr;
mCustomCollectionsBundle = nullptr;
}
CollectionSystemManager::~CollectionSystemManager()

View file

@ -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() {

View file

@ -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++) {

View file

@ -1,7 +1,7 @@
//
// PlatformId.h
//
// Index of all supported platforms.
// Index of all supported systems/platforms.
//
#pragma once

View file

@ -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);

View file

@ -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 <unordered_map>
#include <time.h>
#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<SystemData*>::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<SystemData*>::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<FileData*> allFiles = rootFileData->getFilesRecursive(type, true);
std::vector<FileData*>::const_iterator itf; // declare an iterator to a vector of strings
FileType type = GAME;
std::vector<FileData*> allFiles = rootFileData->getFilesRecursive(type, true);
std::vector<FileData*>::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<SystemData*>::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<SystemData*>::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<FileData*> allFiles = rootFileData->getFilesRecursive(type, true);
std::vector<FileData*>::const_iterator itf; // declare an iterator to a vector of strings
FileType type = GAME;
std::vector<FileData*> allFiles = rootFileData->getFilesRecursive(type, true);
std::vector<FileData*>::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<std::string> 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<std::string> 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);
}
}

View file

@ -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<Sound> 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<Sound> mBackgroundAudio;
bool mStopBackgroundAudio;
};
#endif // ES_APP_SYSTEM_SCREEN_SAVER_H

View file

@ -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> 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> & VolumeControl::getInstance()
{
//check if an VolumeControl instance is already created, if not create one
static std::shared_ptr<VolumeControl> 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<VolumeControl> 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<LPOSVERSIONINFOA>(&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<LPOSVERSIONINFOA>(&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
}

View file

@ -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 <memory>
#if defined (__APPLE__)
#error TODO: Not implemented for MacOS yet!!!
#error TODO: Not implemented for MacOS yet!!!
#elif defined(__linux__)
#include <unistd.h>
#include <fcntl.h>
#include <alsa/asoundlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <alsa/asoundlib.h>
#elif defined(WIN32) || defined(_WIN32)
#include <Windows.h>
#include <endpointvolume.h>
#include <mmeapi.h>
#include <Windows.h>
#include <endpointvolume.h>
#include <mmeapi.h>
#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<VolumeControl> sInstance;
static std::weak_ptr<VolumeControl> sInstance;
VolumeControl();
VolumeControl(const VolumeControl & right);
VolumeControl();
VolumeControl(const VolumeControl & right);
VolumeControl & operator=(const VolumeControl & right);
public:
static std::shared_ptr<VolumeControl> & getInstance();
static std::shared_ptr<VolumeControl> & 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

View file

@ -238,7 +238,7 @@ void GuiGamelistOptions::openMetaDataEd()
std::function<void()> deleteBtnFunc;
if (file->getType() == FOLDER) {
deleteBtnFunc = NULL;
deleteBtnFunc = nullptr;
}
else {
deleteBtnFunc = [this, file] {

View file

@ -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"

View file

@ -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<HelpPrompt> GuiSettings::getHelpPrompts()
{
std::vector<HelpPrompt> prompts = mMenu.getHelpPrompts();
prompts.push_back(HelpPrompt("b", "back"));
return prompts;
std::vector<HelpPrompt> prompts = mMenu.getHelpPrompts();
prompts.push_back(HelpPrompt("b", "back"));
return prompts;
}

View file

@ -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<GuiComponent>& comp) { mMenu.addWithLabel(label, comp); };
inline void addSaveFunc(const std::function<void()>& 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<GuiComponent>& comp) { mMenu.addWithLabel(label, comp); };
inline void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); };
bool input(InputConfig* config, Input input) override;
std::vector<HelpPrompt> getHelpPrompts() override;
HelpStyle getHelpStyle() override;
bool input(InputConfig* config, Input input) override;
std::vector<HelpPrompt> getHelpPrompts() override;
HelpStyle getHelpStyle() override;
private:
MenuComponent mMenu;
std::vector< std::function<void()> > mSaveFuncs;
MenuComponent mMenu;
std::vector< std::function<void()> > mSaveFuncs;
};
#endif // ES_APP_GUIS_GUI_SETTINGS_H

View file

@ -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<SliderComponent>(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<SliderComponent>(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<SwitchComponent>(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<SwitchComponent>(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<TextComponent>(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<TextComponent>(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<SwitchComponent>(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<SwitchComponent>(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<TextComponent>(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<TextComponent>(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<SwitchComponent>(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<SwitchComponent>(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<TextComponent>(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<TextComponent>(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<GuiComponent> component)
void GuiSlideshowScreensaverOptions::addWithLabel(ComponentListRow row,
const std::string label, std::shared_ptr<GuiComponent> component)
{
row.elements.clear();
row.elements.clear();
auto lbl = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(label), Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
row.addElement(lbl, true); // label
auto lbl = std::make_shared<TextComponent>(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);
}

View file

@ -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<GuiComponent> component);
void addWithLabel(ComponentListRow row, const std::string label,
std::shared_ptr<GuiComponent> component);
};
#endif // ES_APP_GUIS_GUI_SLIDESHOW_SCREENSAVER_OPTIONS_H

View file

@ -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<SliderComponent>(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<SliderComponent>(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<SwitchComponent>(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<SwitchComponent>(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<SwitchComponent>(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<SwitchComponent>(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<std::string> >(mWindow, getHelpStyle(), "SHOW GAME INFO", false);
std::vector<std::string> 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<OptionListComponent<std::string>>
(mWindow,getHelpStyle(), "SHOW GAME INFO", false);
std::vector<std::string> 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<std::string> >(mWindow, "GAME INFO ALIGNMENT", false);
std::vector<std::string> 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<OptionListComponent<std::string>>
(mWindow, "GAME INFO ALIGNMENT", false);
std::vector<std::string> 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<SliderComponent>(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<SliderComponent>(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<SwitchComponent>(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<SwitchComponent>(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<TextComponent>(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<TextComponent>(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<TextComponent>(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<TextComponent>(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<SwitchComponent>(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<SwitchComponent>(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
}

View file

@ -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

View file

@ -30,6 +30,7 @@
#include "Settings.h"
#include "SystemData.h"
#include "SystemScreenSaver.h"
#include <FreeImage.h>
#include <SDL_events.h>
#include <SDL_main.h>
#include <SDL_timer.h>
@ -39,8 +40,6 @@
#include <Windows.h>
#endif
#include <FreeImage.h>
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"));

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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) {

View file

@ -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());

View file

@ -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();
}

View file

@ -1,233 +1,234 @@
//
// InputConfig.cpp
//
// Input device configuration functions.
//
#include "InputConfig.h"
#include "Log.h"
#include <pugixml/src/pugixml.hpp>
//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<std::string> InputConfig::getMappedTo(Input input)
{
std::vector<std::string> maps;
std::vector<std::string> maps;
typedef std::map<std::string, Input>::const_iterator it_type;
for(it_type iterator = mNameMap.cbegin(); iterator != mNameMap.cend(); iterator++)
{
Input chk = iterator->second;
typedef std::map<std::string, Input>::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<std::string, Input>::const_iterator it_type;
for(it_type iterator = mNameMap.cbegin(); iterator != mNameMap.cend(); iterator++)
{
if(!iterator->second.configured)
continue;
typedef std::map<std::string, Input>::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);
}
}

View file

@ -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<std::string> getMappedTo(Input input);
// Returns a list of names this input is mapped to.
std::vector<std::string> 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<std::string, Input> mNameMap;
const int mDeviceId;
const std::string mDeviceName;
const std::string mDeviceGUID;
std::map<std::string, Input> mNameMap;
const int mDeviceId;
const std::string mDeviceName;
const std::string mDeviceGUID;
};
#endif // ES_CORE_INPUT_CONFIG_H

View file

@ -455,7 +455,7 @@ std::string InputManager::getTemporaryConfigPath()
bool InputManager::initialized() const
{
return mKeyboardInputConfig != NULL;
return mKeyboardInputConfig != nullptr;
}
int InputManager::getNumConfiguredDevices()

View file

@ -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<void()>& doneCallback) : GuiComponent(window), mFirstRun(firstRun),
mBackground(window, ":/graphics/frame.png"), mGrid(window, Vector2i(1, 5))
GuiDetectDevice::GuiDetectDevice(
Window* window,
bool firstRun,
const std::function<void()>& 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<TextComponent>(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<TextComponent>(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<TextComponent>(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<TextComponent>(mWindow, deviceInfo.str(),
Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER);
mGrid.setEntry(mDeviceInfo, Vector2i(0, 1), false, true);
// message
mMsg1 = std::make_shared<TextComponent>(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<TextComponent>(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<TextComponent>(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<TextComponent>(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<TextComponent>(mWindow, "", Font::get(FONT_SIZE_MEDIUM), 0xFFFFFFFF, ALIGN_CENTER);
mGrid.setEntry(mDeviceHeld, Vector2i(0, 4), false, true);
// Currently held device.
mDeviceHeld = std::make_shared<TextComponent>(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;
}
}
}
}

View file

@ -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<void()>& doneCallback);
GuiDetectDevice(Window* window, bool firstRun, const std::function<void()>& 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<TextComponent> mTitle;
std::shared_ptr<TextComponent> mMsg1;
std::shared_ptr<TextComponent> mMsg2;
std::shared_ptr<TextComponent> mDeviceInfo;
std::shared_ptr<TextComponent> mDeviceHeld;
std::shared_ptr<TextComponent> mTitle;
std::shared_ptr<TextComponent> mMsg1;
std::shared_ptr<TextComponent> mMsg2;
std::shared_ptr<TextComponent> mDeviceInfo;
std::shared_ptr<TextComponent> mDeviceHeld;
std::function<void()> mDoneCallback;
std::function<void()> mDoneCallback;
};
#endif // ES_CORE_GUIS_GUI_DETECT_DEVICE_H

View file

@ -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<void()>& 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<void()>& 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<GuiComponent>(mWindow), Vector2i(0, 0), false);
// 0 is a spacer row.
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0), false);
mTitle = std::make_shared<TextComponent>(mWindow, "CONFIGURING", Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
mGrid.setEntry(mTitle, Vector2i(0, 1), false, true);
mTitle = std::make_shared<TextComponent>(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<TextComponent>(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<TextComponent>(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<TextComponent>(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<TextComponent>(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<ComponentList>(mWindow);
mGrid.setEntry(mList, Vector2i(0, 5), true, true);
for (int i = 0; i < inputCount; i++) {
ComponentListRow row;
mList = std::make_shared<ComponentList>(mWindow);
mGrid.setEntry(mList, Vector2i(0, 5), true, true);
for(int i = 0; i < inputCount; i++)
{
ComponentListRow row;
// Icon.
auto icon = std::make_shared<ImageComponent>(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<ImageComponent>(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<GuiComponent>(mWindow);
spacer->setSize(16, 0);
row.addElement(spacer, false);
// spacer between icon and text
auto spacer = std::make_shared<GuiComponent>(mWindow);
spacer->setSize(16, 0);
row.addElement(spacer, false);
auto text = std::make_shared<TextComponent>(mWindow,
GUI_INPUT_CONFIG_LIST[i].dispName, Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
row.addElement(text, true);
auto text = std::make_shared<TextComponent>(mWindow, GUI_INPUT_CONFIG_LIST[i].dispName, Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
row.addElement(text, true);
auto mapping = std::make_shared<TextComponent>(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<TextComponent>(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<ButtonComponent> > buttons;
std::function<void()> okFunction = [this, okCallback] {
InputManager::getInstance()->writeDeviceConfig(mTargetConfig); // save
if(okCallback)
okCallback();
delete this;
};
buttons.push_back(std::make_shared<ButtonComponent>(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<ButtonComponent> > buttons;
std::function<void()> okFunction = [this, okCallback] {
InputManager::getInstance()->writeDeviceConfig(mTargetConfig); // save
if (okCallback)
okCallback();
delete this;
};
buttons.push_back(std::make_shared<ButtonComponent>(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<TextComponent>& text)
{
text->setText("PRESS ANYTHING");
text->setColor(0x656565FF);
text->setText("PRESS ANYTHING");
text->setColor(0x656565FF);
}
void GuiInputConfig::setNotDefined(const std::shared_ptr<TextComponent>& text)
{
text->setText("-NOT DEFINED-");
text->setColor(0x999999FF);
text->setText("-NOT DEFINED-");
text->setColor(0x999999FF);
}
void GuiInputConfig::setAssignedTo(const std::shared_ptr<TextComponent>& 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<TextComponent>& 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;
}

View file

@ -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<void()>& okCallback);
GuiInputConfig(Window* window, InputConfig* target, bool reconfigureAll,
const std::function<void()>& okCallback);
void update(int deltaTime) override;
void onSizeChanged() override;
void update(int deltaTime) override;
void onSizeChanged() override;
private:
void error(const std::shared_ptr<TextComponent>& 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<TextComponent>& text, const std::string& msg);
void setPress(const std::shared_ptr<TextComponent>& text); // set text to "PRESS ANYTHING" + not greyed out
void setNotDefined(const std::shared_ptr<TextComponent>& text); // set text to -NOT DEFINED- + greyed out
void setAssignedTo(const std::shared_ptr<TextComponent>& 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<TextComponent>& text);
// Set text to -NOT DEFINED- + greyed out.
void setNotDefined(const std::shared_ptr<TextComponent>& text);
// Set text to "BUTTON 2"/"AXIS 2+", etc.
void setAssignedTo(const std::shared_ptr<TextComponent>& 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<TextComponent> mTitle;
std::shared_ptr<TextComponent> mSubtitle1;
std::shared_ptr<TextComponent> mSubtitle2;
std::shared_ptr<ComponentList> mList;
std::vector< std::shared_ptr<TextComponent> > mMappings;
std::shared_ptr<ComponentGrid> mButtonGrid;
std::shared_ptr<TextComponent> mTitle;
std::shared_ptr<TextComponent> mSubtitle1;
std::shared_ptr<TextComponent> mSubtitle2;
std::shared_ptr<ComponentList> mList;
std::vector< std::shared_ptr<TextComponent> > mMappings;
std::shared_ptr<ComponentGrid> 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

View file

@ -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 ".."

View file

@ -2,6 +2,8 @@
// FileSystemUtil.h
//
// Low-level filesystem functions.
// Resolve relative paths, resolve symlinks, create directories,
// remove files etc.
//
#pragma once

View file

@ -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 <algorithm>
@ -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::

View file

@ -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<std::string> stringVector;
namespace String
{
typedef std::vector<std::string> 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::

View file

@ -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 <time.h>
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::

View file

@ -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