mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-02-16 20:15:38 +00:00
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:
parent
d29e16778d
commit
6b62065595
|
@ -18,14 +18,13 @@ Some key points:
|
||||||
* Comments should be proper sentences, starting with a capital letter and ending with a dot
|
* 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
|
* 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.
|
* 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
|
* 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
|
* 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 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
|
* 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()
|
* 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
|
* 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
|
* 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! :)
|
* For the rest, check the code and have fun! :)
|
||||||
|
@ -34,7 +33,7 @@ Some key points:
|
||||||
Development Environment
|
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:
|
For debugging purposes, starting the application like this could make sense:
|
||||||
|
|
||||||
`emulationstation --windowed --debug --resolution 1280 720`
|
`emulationstation --windowed --debug --resolution 1280 720`
|
||||||
|
|
54
INSTALL.md
54
INSTALL.md
|
@ -8,35 +8,35 @@ The code has a few dependencies. For building, you'll need CMake and development
|
||||||
|
|
||||||
**On Debian/Ubuntu:**
|
**On Debian/Ubuntu:**
|
||||||
All of the required packages can be easily installed with `apt-get`:
|
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 \
|
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 \
|
libasound2-dev libgl1-mesa-dev build-essential cmake fonts-droid-fallback libvlc-dev
|
||||||
libvlccore-dev vlc-bin
|
libvlccore-dev vlc-bin
|
||||||
```
|
```
|
||||||
**On Fedora:**
|
**On Fedora:**
|
||||||
For this operating system, use `dnf` (with rpmfusion activated) :
|
For this operating system, use `dnf` (with rpmfusion activated) :
|
||||||
```bash
|
```
|
||||||
sudo dnf install SDL2-devel freeimage-devel freetype-devel curl-devel \
|
sudo dnf install SDL2-devel freeimage-devel freetype-devel curl-devel
|
||||||
alsa-lib-devel mesa-libGL-devel cmake \
|
alsa-lib-devel mesa-libGL-devel cmake
|
||||||
vlc-devel rapidjson-devel
|
vlc-devel rapidjson-devel
|
||||||
```
|
```
|
||||||
|
|
||||||
To checkout the source, run the following:
|
To checkout the source, run the following:
|
||||||
|
|
||||||
```bash
|
```
|
||||||
git clone https://gitlab.com/leonstyhre/emulationstation-de
|
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
|
cd emulationstation-de
|
||||||
cmake -DOpenGL_GL_PREFERENCE=GLVND .
|
cmake -DOpenGL_GL_PREFERENCE=GLVND .
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: to generate a `Debug` build on Unix/Linux, run this:
|
NOTE: to generate a `Debug` build on Unix/Linux, run this instead:
|
||||||
```bash
|
```
|
||||||
cmake -DOpenGL_GL_PREFERENCE=GLVND -DCMAKE_BUILD_TYPE=Debug .
|
cmake -DOpenGL_GL_PREFERENCE=GLVND -DCMAKE_BUILD_TYPE=Debug .
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
@ -65,9 +65,9 @@ Configuring
|
||||||
|
|
||||||
**~/.emulationstation/es_systems.cfg:**
|
**~/.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.
|
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:**
|
**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
|
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!**
|
**Keep in mind that you'll have to set up your emulator separately from EmulationStation!**
|
||||||
|
|
||||||
**~/.emulationstation/es_input.cfg:**
|
**~/.emulationstation/es_input.cfg:**
|
||||||
|
|
||||||
When you first start EmulationStation, you will be prompted to configure an input device. The process is thus:
|
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.
|
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.**
|
**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
|
--resolution [width] [height] Try to force a particular resolution
|
||||||
--gamelist-only Skip automatic game ROM search, only read from gamelist.xml
|
--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
|
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.
|
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!
|
**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
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
|
@ -194,23 +198,25 @@ Here's an overview of the `es_systems.cfg` file layout:
|
||||||
</systemList>
|
</systemList>
|
||||||
```
|
```
|
||||||
|
|
||||||
The following variables are replaced by ES in launch commands:
|
The following variables are expanded by ES for the `command` tag:
|
||||||
|
|
||||||
`%ROMPATH%` - Replaced with the path defined for the setting ROMDirectory in es_settings.cfg.
|
|
||||||
|
|
||||||
`%ROM%` - Replaced with absolute path to the selected ROM, with most Bash special characters escaped with a backslash.
|
`%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.
|
`%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
|
gamelist.xml
|
||||||
============
|
============
|
||||||
|
|
||||||
The gamelist.xml file for a system defines metadata for games, such as a name, description, release date, and rating.
|
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" />`
|
`<string name="MediaDirectory" value="~/games/images/emulationstation" />`
|
||||||
|
|
||||||
|
|
15
README.md
15
README.md
|
@ -3,7 +3,7 @@ EmulationStation Desktop Edition
|
||||||
|
|
||||||
EmulationStation Desktop Edition is a cross-platform graphical front-end for emulators with controller and keyboard navigation.
|
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.
|
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.
|
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. \
|
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
|
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.
|
[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.
|
[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
|
||||||
==============
|
========================
|
||||||
|
|
||||||
|
|
|
@ -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.
|
providing an error message if any of the .wav sound files could not be loaded.
|
||||||
|
|
||||||
Example debug output: \
|
Example debug output: \
|
||||||
May 12 21:12:37 lvl2: Sound::getFromTheme() looking for [all.selectSound] \
|
`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: [selectSound] found, ready to play sound file`
|
||||||
|
|
||||||
Example `navigationsounds.xml`, to be included from the main theme file:
|
Example `navigationsounds.xml`, to be included from the main theme file:
|
||||||
|
|
||||||
|
|
|
@ -75,8 +75,8 @@ CollectionSystemManager::CollectionSystemManager(Window* window) : mWindow(windo
|
||||||
|
|
||||||
mIsEditingCustom = false;
|
mIsEditingCustom = false;
|
||||||
mEditingCollection = "Favorites";
|
mEditingCollection = "Favorites";
|
||||||
mEditingCollectionSystemData = NULL;
|
mEditingCollectionSystemData = nullptr;
|
||||||
mCustomCollectionsBundle = NULL;
|
mCustomCollectionsBundle = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectionSystemManager::~CollectionSystemManager()
|
CollectionSystemManager::~CollectionSystemManager()
|
||||||
|
|
|
@ -304,7 +304,7 @@ FileData* FileData::getSourceFileData()
|
||||||
void FileData::addChild(FileData* file)
|
void FileData::addChild(FileData* file)
|
||||||
{
|
{
|
||||||
assert(mType == FOLDER);
|
assert(mType == FOLDER);
|
||||||
assert(file->getParent() == NULL);
|
assert(file->getParent() == nullptr);
|
||||||
|
|
||||||
const std::string key = file->getKey();
|
const std::string key = file->getKey();
|
||||||
if (mChildrenByFilename.find(key) == mChildrenByFilename.cend()) {
|
if (mChildrenByFilename.find(key) == mChildrenByFilename.cend()) {
|
||||||
|
@ -321,7 +321,7 @@ void FileData::removeChild(FileData* file)
|
||||||
mChildrenByFilename.erase(file->getKey());
|
mChildrenByFilename.erase(file->getKey());
|
||||||
for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
|
for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
|
||||||
if (*it == file) {
|
if (*it == file) {
|
||||||
file->mParent = NULL;
|
file->mParent = nullptr;
|
||||||
mChildren.erase(it);
|
mChildren.erase(it);
|
||||||
return;
|
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.
|
// We use this constructor to create a clone of the filedata, and change its system.
|
||||||
mSourceFileData = file->getSourceFileData();
|
mSourceFileData = file->getSourceFileData();
|
||||||
refreshMetadata();
|
refreshMetadata();
|
||||||
mParent = NULL;
|
mParent = nullptr;
|
||||||
metadata = mSourceFileData->metadata;
|
metadata = mSourceFileData->metadata;
|
||||||
mSystemName = mSourceFileData->getSystem()->getName();
|
mSystemName = mSourceFileData->getSystem()->getName();
|
||||||
}
|
}
|
||||||
|
@ -501,7 +501,7 @@ CollectionFileData::~CollectionFileData()
|
||||||
// Need to remove collection file data at the collection object destructor.
|
// Need to remove collection file data at the collection object destructor.
|
||||||
if (mParent)
|
if (mParent)
|
||||||
mParent->removeChild(this);
|
mParent->removeChild(this);
|
||||||
mParent = NULL;
|
mParent = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CollectionFileData::getKey() {
|
std::string CollectionFileData::getKey() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// PlatformId.h
|
// PlatformId.h
|
||||||
//
|
//
|
||||||
// Index of all supported platforms.
|
// Index of all supported systems/platforms.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "PlatformId.h"
|
#include "PlatformId.h"
|
||||||
|
@ -89,7 +89,7 @@ namespace PlatformIds
|
||||||
|
|
||||||
PlatformId getPlatformId(const char* str)
|
PlatformId getPlatformId(const char* str)
|
||||||
{
|
{
|
||||||
if (str == NULL)
|
if (str == nullptr)
|
||||||
return PLATFORM_UNKNOWN;
|
return PLATFORM_UNKNOWN;
|
||||||
|
|
||||||
for (unsigned int i = 1; i < PLATFORM_COUNT; i++) {
|
for (unsigned int i = 1; i < PLATFORM_COUNT; i++) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// PlatformId.h
|
// PlatformId.h
|
||||||
//
|
//
|
||||||
// Index of all supported platforms.
|
// Index of all supported systems/platforms.
|
||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -270,7 +270,7 @@ bool SystemData::loadConfig()
|
||||||
|
|
||||||
// If there appears to be an actual platform ID supplied
|
// If there appears to be an actual platform ID supplied
|
||||||
// but it didn't match the list, generate a warning.
|
// 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 \""
|
LOG(LogWarning) << " Unknown platform for system \"" << name << "\" (platform \""
|
||||||
<< str << "\" from list \"" << platformList << "\")";
|
<< str << "\" from list \"" << platformList << "\")";
|
||||||
else if (platformId != PlatformIds::PLATFORM_UNKNOWN)
|
else if (platformId != PlatformIds::PLATFORM_UNKNOWN)
|
||||||
|
@ -499,7 +499,7 @@ SystemData* SystemData::getRandomSystem()
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we end up here, there is no valid system.
|
// If we end up here, there is no valid system.
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileData* SystemData::getRandomGame()
|
FileData* SystemData::getRandomGame()
|
||||||
|
@ -510,7 +510,7 @@ FileData* SystemData::getRandomGame()
|
||||||
|
|
||||||
// Get a random number in range.
|
// Get a random number in range.
|
||||||
if (total == 0)
|
if (total == 0)
|
||||||
return NULL;
|
return nullptr;
|
||||||
target = (int)Math::round((std::rand() / (float)RAND_MAX) * (total - 1));
|
target = (int)Math::round((std::rand() / (float)RAND_MAX) * (total - 1));
|
||||||
|
|
||||||
return list.at(target);
|
return list.at(target);
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
//
|
||||||
|
// SystemScreenSaver.cpp
|
||||||
|
//
|
||||||
|
// Screensaver, supporting the following modes:
|
||||||
|
// Dim, black, video, slideshow.
|
||||||
|
//
|
||||||
|
|
||||||
#include "SystemScreenSaver.h"
|
#include "SystemScreenSaver.h"
|
||||||
|
|
||||||
#ifdef _RPI_
|
#ifdef _RPI_
|
||||||
|
@ -15,460 +22,427 @@
|
||||||
#include "SystemData.h"
|
#include "SystemData.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#define FADE_TIME 300
|
#define FADE_TIME 300
|
||||||
|
|
||||||
SystemScreenSaver::SystemScreenSaver(Window* window) :
|
SystemScreenSaver::SystemScreenSaver(
|
||||||
mVideoScreensaver(NULL),
|
Window* window)
|
||||||
mImageScreensaver(NULL),
|
: mVideoScreensaver(nullptr),
|
||||||
mWindow(window),
|
mImageScreensaver(nullptr),
|
||||||
mVideosCounted(false),
|
mWindow(window),
|
||||||
mVideoCount(0),
|
mVideosCounted(false),
|
||||||
mImagesCounted(false),
|
mVideoCount(0),
|
||||||
mImageCount(0),
|
mImagesCounted(false),
|
||||||
mState(STATE_INACTIVE),
|
mImageCount(0),
|
||||||
mOpacity(0.0f),
|
mState(STATE_INACTIVE),
|
||||||
mTimer(0),
|
mOpacity(0.0f),
|
||||||
mSystemName(""),
|
mTimer(0),
|
||||||
mGameName(""),
|
mSystemName(""),
|
||||||
mCurrentGame(NULL),
|
mGameName(""),
|
||||||
mStopBackgroundAudio(true)
|
mCurrentGame(nullptr),
|
||||||
|
mStopBackgroundAudio(true)
|
||||||
{
|
{
|
||||||
mWindow->setScreenSaver(this);
|
mWindow->setScreenSaver(this);
|
||||||
std::string path = getTitleFolder();
|
std::string path = getTitleFolder();
|
||||||
if(!Utils::FileSystem::exists(path))
|
if (!Utils::FileSystem::exists(path))
|
||||||
Utils::FileSystem::createDirectory(path);
|
Utils::FileSystem::createDirectory(path);
|
||||||
srand((unsigned int)time(NULL));
|
srand((unsigned int)time(nullptr));
|
||||||
mVideoChangeTime = 30000;
|
mVideoChangeTime = 30000;
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemScreenSaver::~SystemScreenSaver()
|
SystemScreenSaver::~SystemScreenSaver()
|
||||||
{
|
{
|
||||||
// Delete subtitle file, if existing
|
// Delete subtitle file, if it exists.
|
||||||
remove(getTitlePath().c_str());
|
remove(getTitlePath().c_str());
|
||||||
mCurrentGame = NULL;
|
mCurrentGame = nullptr;
|
||||||
delete mVideoScreensaver;
|
delete mVideoScreensaver;
|
||||||
delete mImageScreensaver;
|
delete mImageScreensaver;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemScreenSaver::allowSleep()
|
bool SystemScreenSaver::allowSleep()
|
||||||
{
|
{
|
||||||
//return false;
|
return ((mVideoScreensaver == nullptr) && (mImageScreensaver == nullptr));
|
||||||
return ((mVideoScreensaver == NULL) && (mImageScreensaver == NULL));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemScreenSaver::isScreenSaverActive()
|
bool SystemScreenSaver::isScreenSaverActive()
|
||||||
{
|
{
|
||||||
return (mState != STATE_INACTIVE);
|
return (mState != STATE_INACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemScreenSaver::startScreenSaver()
|
void SystemScreenSaver::startScreenSaver()
|
||||||
{
|
{
|
||||||
std::string screensaver_behavior = Settings::getInstance()->getString("ScreenSaverBehavior");
|
std::string screensaver_behavior = Settings::getInstance()->getString("ScreenSaverBehavior");
|
||||||
if (!mVideoScreensaver && (screensaver_behavior == "random video"))
|
if (!mVideoScreensaver && (screensaver_behavior == "random video")) {
|
||||||
{
|
// Configure to fade out the windows, skip fading if mode is set to Instant.
|
||||||
// Configure to fade out the windows, Skip Fading if Instant mode
|
mState = PowerSaver::getMode() == PowerSaver::INSTANT
|
||||||
mState = PowerSaver::getMode() == PowerSaver::INSTANT
|
? STATE_SCREENSAVER_ACTIVE
|
||||||
? STATE_SCREENSAVER_ACTIVE
|
: STATE_FADE_OUT_WINDOW;
|
||||||
: STATE_FADE_OUT_WINDOW;
|
mVideoChangeTime = Settings::getInstance()->getInt("ScreenSaverSwapVideoTimeout");
|
||||||
mVideoChangeTime = Settings::getInstance()->getInt("ScreenSaverSwapVideoTimeout");
|
mOpacity = 0.0f;
|
||||||
mOpacity = 0.0f;
|
|
||||||
|
|
||||||
// Load a random video
|
// Load a random video.
|
||||||
std::string path = "";
|
std::string path = "";
|
||||||
pickRandomVideo(path);
|
pickRandomVideo(path);
|
||||||
|
|
||||||
int retry = 200;
|
int retry = 200;
|
||||||
while(retry > 0 && ((path.empty() || !Utils::FileSystem::exists(path)) || mCurrentGame == NULL))
|
while (retry > 0 && ((path.empty() || !Utils::FileSystem::exists(path)) ||
|
||||||
{
|
mCurrentGame == nullptr)) {
|
||||||
retry--;
|
retry--;
|
||||||
pickRandomVideo(path);
|
pickRandomVideo(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!path.empty() && Utils::FileSystem::exists(path))
|
if (!path.empty() && Utils::FileSystem::exists(path)) {
|
||||||
{
|
#ifdef _RPI_
|
||||||
#ifdef _RPI_
|
// Create the correct type of video component
|
||||||
// Create the correct type of video component
|
if (Settings::getInstance()->getBool("ScreenSaverOmxPlayer"))
|
||||||
if (Settings::getInstance()->getBool("ScreenSaverOmxPlayer"))
|
mVideoScreensaver = new VideoPlayerComponent(mWindow, getTitlePath());
|
||||||
mVideoScreensaver = new VideoPlayerComponent(mWindow, getTitlePath());
|
else
|
||||||
else
|
mVideoScreensaver = new VideoVlcComponent(mWindow, getTitlePath());
|
||||||
mVideoScreensaver = new VideoVlcComponent(mWindow, getTitlePath());
|
#else
|
||||||
#else
|
mVideoScreensaver = new VideoVlcComponent(mWindow, getTitlePath());
|
||||||
mVideoScreensaver = new VideoVlcComponent(mWindow, getTitlePath());
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
mVideoScreensaver->topWindow(true);
|
mVideoScreensaver->topWindow(true);
|
||||||
mVideoScreensaver->setOrigin(0.5f, 0.5f);
|
mVideoScreensaver->setOrigin(0.5f, 0.5f);
|
||||||
mVideoScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f);
|
mVideoScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f,
|
||||||
|
Renderer::getScreenHeight() / 2.0f);
|
||||||
|
|
||||||
if (Settings::getInstance()->getBool("StretchVideoOnScreenSaver"))
|
if (Settings::getInstance()->getBool("StretchVideoOnScreenSaver"))
|
||||||
{
|
mVideoScreensaver->setResize((float)Renderer::getScreenWidth(),
|
||||||
mVideoScreensaver->setResize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
(float)Renderer::getScreenHeight());
|
||||||
}
|
else
|
||||||
else
|
mVideoScreensaver->setMaxSize((float)Renderer::getScreenWidth(),
|
||||||
{
|
(float)Renderer::getScreenHeight());
|
||||||
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;
|
|
||||||
|
|
||||||
// Load a random image
|
mVideoScreensaver->setVideo(path);
|
||||||
std::string path = "";
|
mVideoScreensaver->setScreensaverMode(true);
|
||||||
if (Settings::getInstance()->getBool("SlideshowScreenSaverCustomImageSource"))
|
mVideoScreensaver->onShow();
|
||||||
{
|
PowerSaver::runningScreenSaver(true);
|
||||||
pickRandomCustomImage(path);
|
mTimer = 0;
|
||||||
// Custom images are not tied to the game list
|
return;
|
||||||
mCurrentGame = NULL;
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (screensaver_behavior == "slideshow") {
|
||||||
{
|
// Configure to fade out the windows, skip fading if mode is set to Instant.
|
||||||
pickRandomGameListImage(path);
|
mState = PowerSaver::getMode() == PowerSaver::INSTANT
|
||||||
}
|
? STATE_SCREENSAVER_ACTIVE
|
||||||
|
: STATE_FADE_OUT_WINDOW;
|
||||||
|
mVideoChangeTime = Settings::getInstance()->getInt("ScreenSaverSwapImageTimeout");
|
||||||
|
mOpacity = 0.0f;
|
||||||
|
|
||||||
if (!mImageScreensaver)
|
// Load a random image.
|
||||||
{
|
std::string path = "";
|
||||||
mImageScreensaver = new ImageComponent(mWindow, false, false);
|
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);
|
mTimer = 0;
|
||||||
mImageScreensaver->setOrigin(0.5f, 0.5f);
|
|
||||||
mImageScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f);
|
|
||||||
|
|
||||||
if (Settings::getInstance()->getBool("SlideshowScreenSaverStretch"))
|
mImageScreensaver->setImage(path);
|
||||||
{
|
mImageScreensaver->setOrigin(0.5f, 0.5f);
|
||||||
mImageScreensaver->setResize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
mImageScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f,
|
||||||
}
|
Renderer::getScreenHeight() / 2.0f);
|
||||||
else
|
|
||||||
{
|
|
||||||
mImageScreensaver->setMaxSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string bg_audio_file = Settings::getInstance()->getString("SlideshowScreenSaverBackgroundAudioFile");
|
if (Settings::getInstance()->getBool("SlideshowScreenSaverStretch"))
|
||||||
if ((!mBackgroundAudio) && (bg_audio_file != ""))
|
mImageScreensaver->setResize((float)Renderer::getScreenWidth(),
|
||||||
{
|
(float)Renderer::getScreenHeight());
|
||||||
if (Utils::FileSystem::exists(bg_audio_file))
|
else
|
||||||
{
|
mImageScreensaver->setMaxSize((float)Renderer::getScreenWidth(),
|
||||||
// paused PS so that the background audio keeps playing
|
(float)Renderer::getScreenHeight());
|
||||||
PowerSaver::pause();
|
|
||||||
mBackgroundAudio = Sound::get(bg_audio_file);
|
|
||||||
mBackgroundAudio->play();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerSaver::runningScreenSaver(true);
|
std::string bg_audio_file = Settings::getInstance()->
|
||||||
mTimer = 0;
|
getString("SlideshowScreenSaverBackgroundAudioFile");
|
||||||
return;
|
if ((!mBackgroundAudio) && (bg_audio_file != "")) {
|
||||||
}
|
if (Utils::FileSystem::exists(bg_audio_file)) {
|
||||||
// No videos. Just use a standard screensaver
|
// Pause PowerSaver so that the background audio keeps playing.
|
||||||
mState = STATE_SCREENSAVER_ACTIVE;
|
PowerSaver::pause();
|
||||||
mCurrentGame = NULL;
|
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()
|
void SystemScreenSaver::stopScreenSaver()
|
||||||
{
|
{
|
||||||
if ((mBackgroundAudio) && (mStopBackgroundAudio))
|
if ((mBackgroundAudio) && (mStopBackgroundAudio)) {
|
||||||
{
|
mBackgroundAudio->stop();
|
||||||
mBackgroundAudio->stop();
|
mBackgroundAudio.reset();
|
||||||
mBackgroundAudio.reset();
|
// If we were playing audio, we paused PowerSaver.
|
||||||
// if we were playing audio, we paused PS
|
PowerSaver::resume();
|
||||||
PowerSaver::resume();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// so that we stop the background audio next time, unless we're restarting the screensaver
|
// So that we stop the background audio next time, unless we're restarting the screensaver.
|
||||||
mStopBackgroundAudio = true;
|
mStopBackgroundAudio = true;
|
||||||
|
|
||||||
delete mVideoScreensaver;
|
delete mVideoScreensaver;
|
||||||
mVideoScreensaver = NULL;
|
mVideoScreensaver = nullptr;
|
||||||
delete mImageScreensaver;
|
delete mImageScreensaver;
|
||||||
mImageScreensaver = NULL;
|
mImageScreensaver = nullptr;
|
||||||
|
|
||||||
// we need this to loop through different videos
|
// We need this to loop through different videos.
|
||||||
mState = STATE_INACTIVE;
|
mState = STATE_INACTIVE;
|
||||||
PowerSaver::runningScreenSaver(false);
|
PowerSaver::runningScreenSaver(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemScreenSaver::renderScreenSaver()
|
void SystemScreenSaver::renderScreenSaver()
|
||||||
{
|
{
|
||||||
std::string screensaver_behavior = Settings::getInstance()->getString("ScreenSaverBehavior");
|
std::string screensaver_behavior = Settings::getInstance()->getString("ScreenSaverBehavior");
|
||||||
if (mVideoScreensaver && screensaver_behavior == "random video")
|
if (mVideoScreensaver && screensaver_behavior == "random video") {
|
||||||
{
|
// Render black background.
|
||||||
// Render black background
|
Renderer::setMatrix(Transform4x4f::Identity());
|
||||||
Renderer::setMatrix(Transform4x4f::Identity());
|
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
|
||||||
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0x000000FF, 0x000000FF);
|
Renderer::getScreenHeight(), 0x000000FF, 0x000000FF);
|
||||||
|
|
||||||
// Only render the video if the state requires it
|
// Only render the video if the state requires it.
|
||||||
if ((int)mState >= STATE_FADE_IN_VIDEO)
|
if ((int)mState >= STATE_FADE_IN_VIDEO) {
|
||||||
{
|
Transform4x4f transform = Transform4x4f::Identity();
|
||||||
Transform4x4f transform = Transform4x4f::Identity();
|
mVideoScreensaver->render(transform);
|
||||||
mVideoScreensaver->render(transform);
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (mImageScreensaver && screensaver_behavior == "slideshow") {
|
||||||
else if (mImageScreensaver && screensaver_behavior == "slideshow")
|
// Render black background.
|
||||||
{
|
Renderer::setMatrix(Transform4x4f::Identity());
|
||||||
// Render black background
|
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
|
||||||
Renderer::setMatrix(Transform4x4f::Identity());
|
Renderer::getScreenHeight(), 0x000000FF, 0x000000FF);
|
||||||
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0x000000FF, 0x000000FF);
|
|
||||||
|
|
||||||
// Only render the video if the state requires it
|
// Only render the video if the state requires it.
|
||||||
if ((int)mState >= STATE_FADE_IN_VIDEO)
|
if ((int)mState >= STATE_FADE_IN_VIDEO) {
|
||||||
{
|
if (mImageScreensaver->hasImage()) {
|
||||||
if (mImageScreensaver->hasImage())
|
mImageScreensaver->setOpacity(255- (unsigned char) (mOpacity * 255));
|
||||||
{
|
|
||||||
mImageScreensaver->setOpacity(255- (unsigned char) (mOpacity * 255));
|
|
||||||
|
|
||||||
Transform4x4f transform = Transform4x4f::Identity();
|
Transform4x4f transform = Transform4x4f::Identity();
|
||||||
mImageScreensaver->render(transform);
|
mImageScreensaver->render(transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we need to restart the background audio
|
// Check if we need to restart the background audio.
|
||||||
if ((mBackgroundAudio) && (Settings::getInstance()->getString("SlideshowScreenSaverBackgroundAudioFile") != ""))
|
if ((mBackgroundAudio) && (Settings::getInstance()->
|
||||||
{
|
getString("SlideshowScreenSaverBackgroundAudioFile") != "")) {
|
||||||
if (!mBackgroundAudio->isPlaying())
|
if (!mBackgroundAudio->isPlaying())
|
||||||
{
|
mBackgroundAudio->play();
|
||||||
mBackgroundAudio->play();
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (mState != STATE_INACTIVE) {
|
||||||
}
|
Renderer::setMatrix(Transform4x4f::Identity());
|
||||||
else if (mState != STATE_INACTIVE)
|
unsigned char color = screensaver_behavior == "dim" ? 0x000000A0 : 0x000000FF;
|
||||||
{
|
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
|
||||||
Renderer::setMatrix(Transform4x4f::Identity());
|
Renderer::getScreenHeight(), color, color);
|
||||||
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 SystemScreenSaver::countGameListNodes(const char *nodeName)
|
||||||
{
|
{
|
||||||
unsigned long nodeCount = 0;
|
unsigned long nodeCount = 0;
|
||||||
std::vector<SystemData*>::const_iterator it;
|
std::vector<SystemData*>::const_iterator it;
|
||||||
for (it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); ++it)
|
for (it = SystemData::sSystemVector.cbegin();
|
||||||
{
|
it != SystemData::sSystemVector.cend(); ++it) {
|
||||||
// We only want nodes from game systems that are not collections
|
// We only want nodes from game systems that are not collections.
|
||||||
if (!(*it)->isGameSystem() || (*it)->isCollection())
|
if (!(*it)->isGameSystem() || (*it)->isCollection())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
FileData* rootFileData = (*it)->getRootFolder();
|
FileData* rootFileData = (*it)->getRootFolder();
|
||||||
|
|
||||||
FileType type = GAME;
|
FileType type = GAME;
|
||||||
std::vector<FileData*> allFiles = rootFileData->getFilesRecursive(type, true);
|
std::vector<FileData*> allFiles = rootFileData->getFilesRecursive(type, true);
|
||||||
std::vector<FileData*>::const_iterator itf; // declare an iterator to a vector of strings
|
std::vector<FileData*>::const_iterator itf; // Declare an iterator to a vector of strings.
|
||||||
|
|
||||||
for(itf=allFiles.cbegin() ; itf < allFiles.cend(); itf++) {
|
for (itf=allFiles.cbegin() ; itf < allFiles.cend(); itf++) {
|
||||||
if ((strcmp(nodeName, "video") == 0 && (*itf)->getVideoPath() != "") ||
|
if ((strcmp(nodeName, "video") == 0 && (*itf)->getVideoPath() != "") ||
|
||||||
(strcmp(nodeName, "image") == 0 && (*itf)->getImagePath() != ""))
|
(strcmp(nodeName, "image") == 0 && (*itf)->getImagePath() != ""))
|
||||||
{
|
nodeCount++;
|
||||||
nodeCount++;
|
}
|
||||||
}
|
}
|
||||||
}
|
return nodeCount;
|
||||||
}
|
|
||||||
return nodeCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemScreenSaver::countVideos()
|
void SystemScreenSaver::countVideos()
|
||||||
{
|
{
|
||||||
if (!mVideosCounted)
|
if (!mVideosCounted) {
|
||||||
{
|
mVideoCount = countGameListNodes("video");
|
||||||
mVideoCount = countGameListNodes("video");
|
mVideosCounted = true;
|
||||||
mVideosCounted = true;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemScreenSaver::countImages()
|
void SystemScreenSaver::countImages()
|
||||||
{
|
{
|
||||||
if (!mImagesCounted)
|
if (!mImagesCounted) {
|
||||||
{
|
mImageCount = countGameListNodes("image");
|
||||||
mImageCount = countGameListNodes("image");
|
mImagesCounted = true;
|
||||||
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;
|
std::vector<SystemData*>::const_iterator it;
|
||||||
for (it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); ++it)
|
for (it = SystemData::sSystemVector.cbegin();
|
||||||
{
|
it != SystemData::sSystemVector.cend(); ++it) {
|
||||||
// We only want nodes from game systems that are not collections
|
// We only want nodes from game systems that are not collections.
|
||||||
if (!(*it)->isGameSystem() || (*it)->isCollection())
|
if (!(*it)->isGameSystem() || (*it)->isCollection())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
FileData* rootFileData = (*it)->getRootFolder();
|
FileData* rootFileData = (*it)->getRootFolder();
|
||||||
|
|
||||||
FileType type = GAME;
|
FileType type = GAME;
|
||||||
std::vector<FileData*> allFiles = rootFileData->getFilesRecursive(type, true);
|
std::vector<FileData*> allFiles = rootFileData->getFilesRecursive(type, true);
|
||||||
std::vector<FileData*>::const_iterator itf; // declare an iterator to a vector of strings
|
std::vector<FileData*>::const_iterator itf; // Declare an iterator to a vector of strings.
|
||||||
|
|
||||||
for(itf=allFiles.cbegin() ; itf < allFiles.cend(); itf++) {
|
for (itf=allFiles.cbegin() ; itf < allFiles.cend(); itf++) {
|
||||||
if ((strcmp(nodeName, "video") == 0 && (*itf)->getVideoPath() != "") ||
|
if ((strcmp(nodeName, "video") == 0 && (*itf)->getVideoPath() != "") ||
|
||||||
(strcmp(nodeName, "image") == 0 && (*itf)->getImagePath() != ""))
|
(strcmp(nodeName, "image") == 0 && (*itf)->getImagePath() != "")) {
|
||||||
{
|
if (index-- == 0) {
|
||||||
if (index-- == 0)
|
// We have it.
|
||||||
{
|
path = "";
|
||||||
// We have it
|
if (strcmp(nodeName, "video") == 0)
|
||||||
path = "";
|
path = (*itf)->getVideoPath();
|
||||||
if (strcmp(nodeName, "video") == 0)
|
else if (strcmp(nodeName, "image") == 0)
|
||||||
path = (*itf)->getVideoPath();
|
path = (*itf)->getImagePath();
|
||||||
else if (strcmp(nodeName, "image") == 0)
|
mSystemName = (*it)->getFullName();
|
||||||
path = (*itf)->getImagePath();
|
mGameName = (*itf)->getName();
|
||||||
mSystemName = (*it)->getFullName();
|
mCurrentGame = (*itf);
|
||||||
mGameName = (*itf)->getName();
|
|
||||||
mCurrentGame = (*itf);
|
|
||||||
|
|
||||||
// end of getting FileData
|
// End of getting FileData.
|
||||||
if (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never")
|
if (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never")
|
||||||
writeSubtitle(mGameName.c_str(), mSystemName.c_str(),
|
writeSubtitle(mGameName.c_str(), mSystemName.c_str(),
|
||||||
(Settings::getInstance()->getString("ScreenSaverGameInfo") == "always"));
|
(Settings::getInstance()->getString("ScreenSaverGameInfo") ==
|
||||||
return;
|
"always"));
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemScreenSaver::pickRandomVideo(std::string& path)
|
void SystemScreenSaver::pickRandomVideo(std::string& path)
|
||||||
{
|
{
|
||||||
countVideos();
|
countVideos();
|
||||||
mCurrentGame = NULL;
|
mCurrentGame = nullptr;
|
||||||
if (mVideoCount > 0)
|
|
||||||
{
|
|
||||||
int video = (int)(((float)rand() / float(RAND_MAX)) * (float)mVideoCount);
|
|
||||||
|
|
||||||
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)
|
void SystemScreenSaver::pickRandomGameListImage(std::string& path)
|
||||||
{
|
{
|
||||||
countImages();
|
countImages();
|
||||||
mCurrentGame = NULL;
|
mCurrentGame = nullptr;
|
||||||
if (mImageCount > 0)
|
|
||||||
{
|
|
||||||
int image = (int)(((float)rand() / float(RAND_MAX)) * (float)mImageCount);
|
|
||||||
|
|
||||||
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)
|
void SystemScreenSaver::pickRandomCustomImage(std::string& path)
|
||||||
{
|
{
|
||||||
std::string imageDir = Settings::getInstance()->getString("SlideshowScreenSaverImageDir");
|
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"));
|
|
||||||
|
|
||||||
for(Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); it != dirContent.cend(); ++it)
|
if ((imageDir != "") && (Utils::FileSystem::exists(imageDir))) {
|
||||||
{
|
std::string imageFilter = Settings::getInstance()->
|
||||||
if (Utils::FileSystem::isRegularFile(*it))
|
getString("SlideshowScreenSaverImageFilter");
|
||||||
{
|
std::vector<std::string> matchingFiles;
|
||||||
// If the image filter is empty, or the file extension is in the filter string,
|
Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(
|
||||||
// add it to the matching files list
|
imageDir, Settings::getInstance()->getBool("SlideshowScreenSaverRecurse"));
|
||||||
if ((imageFilter.length() <= 0) ||
|
|
||||||
(imageFilter.find(Utils::FileSystem::getExtension(*it)) != std::string::npos))
|
|
||||||
{
|
|
||||||
matchingFiles.push_back(*it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int fileCount = (int)matchingFiles.size();
|
for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin();
|
||||||
if (fileCount > 0)
|
it != dirContent.cend(); ++it) {
|
||||||
{
|
if (Utils::FileSystem::isRegularFile(*it)) {
|
||||||
// get a random index in the range 0 to fileCount (exclusive)
|
// If the image filter is empty, or the file extension is in the filter
|
||||||
int randomIndex = rand() % fileCount;
|
// string, add it to the matching files list.
|
||||||
path = matchingFiles[randomIndex];
|
if ((imageFilter.length() <= 0) || (imageFilter.find(
|
||||||
}
|
Utils::FileSystem::getExtension(*it)) != std::string::npos))
|
||||||
else
|
matchingFiles.push_back(*it);
|
||||||
{
|
}
|
||||||
LOG(LogError) << "Slideshow Screensaver - No image files found\n";
|
}
|
||||||
}
|
|
||||||
}
|
int fileCount = (int)matchingFiles.size();
|
||||||
else
|
if (fileCount > 0) {
|
||||||
{
|
// Get a random index in the range 0 to fileCount (exclusive).
|
||||||
LOG(LogError) << "Slideshow Screensaver - Image directory does not exist: " << imageDir << "\n";
|
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)
|
void SystemScreenSaver::update(int deltaTime)
|
||||||
{
|
{
|
||||||
// Use this to update the fade value for the current fade stage
|
// Use this to update the fade value for the current fade stage.
|
||||||
if (mState == STATE_FADE_OUT_WINDOW)
|
if (mState == STATE_FADE_OUT_WINDOW) {
|
||||||
{
|
mOpacity += (float)deltaTime / FADE_TIME;
|
||||||
mOpacity += (float)deltaTime / FADE_TIME;
|
if (mOpacity >= 1.0f) {
|
||||||
if (mOpacity >= 1.0f)
|
mOpacity = 1.0f;
|
||||||
{
|
|
||||||
mOpacity = 1.0f;
|
|
||||||
|
|
||||||
// Update to the next state
|
// Update to the next state.
|
||||||
mState = STATE_FADE_IN_VIDEO;
|
mState = STATE_FADE_IN_VIDEO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mState == STATE_FADE_IN_VIDEO)
|
else if (mState == STATE_FADE_IN_VIDEO) {
|
||||||
{
|
mOpacity -= (float)deltaTime / FADE_TIME;
|
||||||
mOpacity -= (float)deltaTime / FADE_TIME;
|
if (mOpacity <= 0.0f) {
|
||||||
if (mOpacity <= 0.0f)
|
mOpacity = 0.0f;
|
||||||
{
|
// Update to the next state.
|
||||||
mOpacity = 0.0f;
|
mState = STATE_SCREENSAVER_ACTIVE;
|
||||||
// Update to the next state
|
}
|
||||||
mState = STATE_SCREENSAVER_ACTIVE;
|
}
|
||||||
}
|
else if (mState == STATE_SCREENSAVER_ACTIVE) {
|
||||||
}
|
// Update the timer that swaps the videos.
|
||||||
else if (mState == STATE_SCREENSAVER_ACTIVE)
|
mTimer += deltaTime;
|
||||||
{
|
if (mTimer > mVideoChangeTime)
|
||||||
// Update the timer that swaps the videos
|
nextVideo();
|
||||||
mTimer += deltaTime;
|
}
|
||||||
if (mTimer > mVideoChangeTime)
|
|
||||||
{
|
|
||||||
nextVideo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have a loaded video then update it
|
// If we have a loaded video then update it.
|
||||||
if (mVideoScreensaver)
|
if (mVideoScreensaver)
|
||||||
mVideoScreensaver->update(deltaTime);
|
mVideoScreensaver->update(deltaTime);
|
||||||
if (mImageScreensaver)
|
if (mImageScreensaver)
|
||||||
mImageScreensaver->update(deltaTime);
|
mImageScreensaver->update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemScreenSaver::nextVideo() {
|
void SystemScreenSaver::nextVideo() {
|
||||||
mStopBackgroundAudio = false;
|
mStopBackgroundAudio = false;
|
||||||
stopScreenSaver();
|
stopScreenSaver();
|
||||||
startScreenSaver();
|
startScreenSaver();
|
||||||
mState = STATE_SCREENSAVER_ACTIVE;
|
mState = STATE_SCREENSAVER_ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileData* SystemScreenSaver::getCurrentGame()
|
FileData* SystemScreenSaver::getCurrentGame()
|
||||||
{
|
{
|
||||||
return mCurrentGame;
|
return mCurrentGame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemScreenSaver::launchGame()
|
void SystemScreenSaver::launchGame()
|
||||||
{
|
{
|
||||||
if (mCurrentGame != NULL)
|
if (mCurrentGame != nullptr) {
|
||||||
{
|
// Launching game
|
||||||
// launching Game
|
ViewController::get()->goToGameList(mCurrentGame->getSystem());
|
||||||
ViewController::get()->goToGameList(mCurrentGame->getSystem());
|
IGameListView* view = ViewController::get()->
|
||||||
IGameListView* view = ViewController::get()->getGameListView(mCurrentGame->getSystem()).get();
|
getGameListView(mCurrentGame->getSystem()).get();
|
||||||
view->setCursor(mCurrentGame);
|
view->setCursor(mCurrentGame);
|
||||||
if (Settings::getInstance()->getBool("ScreenSaverControls"))
|
if (Settings::getInstance()->getBool("ScreenSaverControls"))
|
||||||
{
|
view->launch(mCurrentGame);
|
||||||
view->launch(mCurrentGame);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
//
|
||||||
|
// SystemScreenSaver.h
|
||||||
|
//
|
||||||
|
// Screensaver, supporting the following modes:
|
||||||
|
// Dim, black, video, slideshow.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_APP_SYSTEM_SCREEN_SAVER_H
|
#ifndef ES_APP_SYSTEM_SCREEN_SAVER_H
|
||||||
#define ES_APP_SYSTEM_SCREEN_SAVER_H
|
#define ES_APP_SYSTEM_SCREEN_SAVER_H
|
||||||
|
@ -8,60 +15,60 @@ class ImageComponent;
|
||||||
class Sound;
|
class Sound;
|
||||||
class VideoComponent;
|
class VideoComponent;
|
||||||
|
|
||||||
// Screensaver implementation for main window
|
// Screensaver implementation for main window.
|
||||||
class SystemScreenSaver : public Window::ScreenSaver
|
class SystemScreenSaver : public Window::ScreenSaver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SystemScreenSaver(Window* window);
|
SystemScreenSaver(Window* window);
|
||||||
virtual ~SystemScreenSaver();
|
virtual ~SystemScreenSaver();
|
||||||
|
|
||||||
virtual void startScreenSaver();
|
virtual void startScreenSaver();
|
||||||
virtual void stopScreenSaver();
|
virtual void stopScreenSaver();
|
||||||
virtual void nextVideo();
|
virtual void nextVideo();
|
||||||
virtual void renderScreenSaver();
|
virtual void renderScreenSaver();
|
||||||
virtual bool allowSleep();
|
virtual bool allowSleep();
|
||||||
virtual void update(int deltaTime);
|
virtual void update(int deltaTime);
|
||||||
virtual bool isScreenSaverActive();
|
virtual bool isScreenSaverActive();
|
||||||
|
|
||||||
virtual FileData* getCurrentGame();
|
virtual FileData* getCurrentGame();
|
||||||
virtual void launchGame();
|
virtual void launchGame();
|
||||||
inline virtual void resetCounts() { mVideosCounted = false; mImagesCounted = false; };
|
inline virtual void resetCounts() { mVideosCounted = false; mImagesCounted = false; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned long countGameListNodes(const char *nodeName);
|
unsigned long countGameListNodes(const char *nodeName);
|
||||||
void countVideos();
|
void countVideos();
|
||||||
void countImages();
|
void countImages();
|
||||||
void pickGameListNode(unsigned long index, const char *nodeName, std::string& path);
|
void pickGameListNode(unsigned long index, const char *nodeName, std::string& path);
|
||||||
void pickRandomVideo(std::string& path);
|
void pickRandomVideo(std::string& path);
|
||||||
void pickRandomGameListImage(std::string& path);
|
void pickRandomGameListImage(std::string& path);
|
||||||
void pickRandomCustomImage(std::string& path);
|
void pickRandomCustomImage(std::string& path);
|
||||||
|
|
||||||
void input(InputConfig* config, Input input);
|
void input(InputConfig* config, Input input);
|
||||||
|
|
||||||
enum STATE {
|
enum STATE {
|
||||||
STATE_INACTIVE,
|
STATE_INACTIVE,
|
||||||
STATE_FADE_OUT_WINDOW,
|
STATE_FADE_OUT_WINDOW,
|
||||||
STATE_FADE_IN_VIDEO,
|
STATE_FADE_IN_VIDEO,
|
||||||
STATE_SCREENSAVER_ACTIVE
|
STATE_SCREENSAVER_ACTIVE
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool mVideosCounted;
|
bool mVideosCounted;
|
||||||
unsigned long mVideoCount;
|
unsigned long mVideoCount;
|
||||||
VideoComponent* mVideoScreensaver;
|
VideoComponent* mVideoScreensaver;
|
||||||
bool mImagesCounted;
|
bool mImagesCounted;
|
||||||
unsigned long mImageCount;
|
unsigned long mImageCount;
|
||||||
ImageComponent* mImageScreensaver;
|
ImageComponent* mImageScreensaver;
|
||||||
Window* mWindow;
|
Window* mWindow;
|
||||||
STATE mState;
|
STATE mState;
|
||||||
float mOpacity;
|
float mOpacity;
|
||||||
int mTimer;
|
int mTimer;
|
||||||
FileData* mCurrentGame;
|
FileData* mCurrentGame;
|
||||||
std::string mGameName;
|
std::string mGameName;
|
||||||
std::string mSystemName;
|
std::string mSystemName;
|
||||||
int mVideoChangeTime;
|
int mVideoChangeTime;
|
||||||
std::shared_ptr<Sound> mBackgroundAudio;
|
std::shared_ptr<Sound> mBackgroundAudio;
|
||||||
bool mStopBackgroundAudio;
|
bool mStopBackgroundAudio;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_APP_SYSTEM_SCREEN_SAVER_H
|
#endif // ES_APP_SYSTEM_SCREEN_SAVER_H
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// VolumeControl.cpp
|
||||||
|
//
|
||||||
|
// Controls system audio volume.
|
||||||
|
//
|
||||||
|
|
||||||
#include "VolumeControl.h"
|
#include "VolumeControl.h"
|
||||||
|
|
||||||
#include "math/Misc.h"
|
#include "math/Misc.h"
|
||||||
|
@ -8,399 +14,372 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
#if defined(_RPI_) || defined(_VERO4K_)
|
#if defined(_RPI_) || defined(_VERO4K_)
|
||||||
const char * VolumeControl::mixerName = "PCM";
|
const char * VolumeControl::mixerName = "PCM";
|
||||||
#else
|
#else
|
||||||
const char * VolumeControl::mixerName = "Master";
|
const char * VolumeControl::mixerName = "Master";
|
||||||
#endif
|
#endif
|
||||||
const char * VolumeControl::mixerCard = "default";
|
const char * VolumeControl::mixerCard = "default";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::weak_ptr<VolumeControl> VolumeControl::sInstance;
|
std::weak_ptr<VolumeControl> VolumeControl::sInstance;
|
||||||
|
|
||||||
|
|
||||||
VolumeControl::VolumeControl()
|
VolumeControl::VolumeControl()
|
||||||
: originalVolume(0), internalVolume(0)
|
: originalVolume(0),
|
||||||
#if defined (__APPLE__)
|
internalVolume(0)
|
||||||
#error TODO: Not implemented for MacOS yet!!!
|
#if defined (__APPLE__)
|
||||||
#elif defined(__linux__)
|
#error TODO: Not implemented for MacOS yet!!!
|
||||||
, mixerIndex(0), mixerHandle(nullptr), mixerElem(nullptr), mixerSelemId(nullptr)
|
#elif defined(__linux__)
|
||||||
#elif defined(WIN32) || defined(_WIN32)
|
, mixerIndex(0),
|
||||||
, mixerHandle(nullptr), endpointVolume(nullptr)
|
mixerHandle(nullptr),
|
||||||
#endif
|
mixerElem(nullptr),
|
||||||
|
mixerSelemId(nullptr)
|
||||||
|
#elif defined(WIN32) || defined(_WIN32)
|
||||||
|
, mixerHandle(nullptr),
|
||||||
|
endpointVolume(nullptr)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
|
|
||||||
//get original volume levels for system
|
// Get original volume levels for system.
|
||||||
originalVolume = getVolume();
|
originalVolume = getVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
VolumeControl::VolumeControl(const VolumeControl & right):
|
VolumeControl::VolumeControl(
|
||||||
originalVolume(0), internalVolume(0)
|
const VolumeControl & right):
|
||||||
#if defined (__APPLE__)
|
originalVolume(0),
|
||||||
#error TODO: Not implemented for MacOS yet!!!
|
internalVolume(0)
|
||||||
#elif defined(__linux__)
|
#if defined (__APPLE__)
|
||||||
, mixerIndex(0), mixerHandle(nullptr), mixerElem(nullptr), mixerSelemId(nullptr)
|
#error TODO: Not implemented for MacOS yet!!!
|
||||||
#elif defined(WIN32) || defined(_WIN32)
|
#elif defined(__linux__)
|
||||||
, mixerHandle(nullptr), endpointVolume(nullptr)
|
, mixerIndex(0),
|
||||||
#endif
|
mixerHandle(nullptr),
|
||||||
|
mixerElem(nullptr),
|
||||||
|
mixerSelemId(nullptr)
|
||||||
|
#elif defined(WIN32) || defined(_WIN32)
|
||||||
|
, mixerHandle(nullptr),
|
||||||
|
endpointVolume(nullptr)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
(void)right;
|
(void)right;
|
||||||
sInstance = right.sInstance;
|
sInstance = right.sInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
VolumeControl & VolumeControl::operator=(const VolumeControl & right)
|
VolumeControl & VolumeControl::operator=(const VolumeControl& right)
|
||||||
{
|
{
|
||||||
if (this != &right) {
|
if (this != &right)
|
||||||
sInstance = right.sInstance;
|
sInstance = right.sInstance;
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
VolumeControl::~VolumeControl()
|
VolumeControl::~VolumeControl()
|
||||||
{
|
{
|
||||||
//set original volume levels for system
|
// Set original volume levels for system.
|
||||||
//setVolume(originalVolume);
|
//setVolume(originalVolume);
|
||||||
|
|
||||||
deinit();
|
deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<VolumeControl> & VolumeControl::getInstance()
|
std::shared_ptr<VolumeControl> & VolumeControl::getInstance()
|
||||||
{
|
{
|
||||||
//check if an VolumeControl instance is already created, if not create one
|
// Check if an VolumeControl instance is already created, if not create one.
|
||||||
static std::shared_ptr<VolumeControl> sharedInstance = sInstance.lock();
|
static std::shared_ptr<VolumeControl> sharedInstance = sInstance.lock();
|
||||||
if (sharedInstance == nullptr) {
|
if (sharedInstance == nullptr) {
|
||||||
sharedInstance.reset(new VolumeControl);
|
sharedInstance.reset(new VolumeControl);
|
||||||
sInstance = sharedInstance;
|
sInstance = sharedInstance;
|
||||||
}
|
}
|
||||||
return sharedInstance;
|
return sharedInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeControl::init()
|
void VolumeControl::init()
|
||||||
{
|
{
|
||||||
//initialize audio mixer interface
|
// Initialize audio mixer interface.
|
||||||
#if defined (__APPLE__)
|
#if defined (__APPLE__)
|
||||||
#error TODO: Not implemented for MacOS yet!!!
|
#error TODO: Not implemented for MacOS yet!!!
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
//try to open mixer device
|
// Try to open mixer device.
|
||||||
if (mixerHandle == nullptr)
|
if (mixerHandle == nullptr) {
|
||||||
{
|
// Allow user to override the AudioCard and AudioDevice in es_settings.cfg.
|
||||||
// Allow users to override the AudioCard and MixerName in es_settings.cfg
|
mixerCard = Settings::getInstance()->getString("AudioCard").c_str();
|
||||||
mixerCard = Settings::getInstance()->getString("AudioCard").c_str();
|
mixerName = Settings::getInstance()->getString("AudioDevice").c_str();
|
||||||
mixerName = Settings::getInstance()->getString("AudioDevice").c_str();
|
|
||||||
|
|
||||||
snd_mixer_selem_id_alloca(&mixerSelemId);
|
snd_mixer_selem_id_alloca(&mixerSelemId);
|
||||||
//sets simple-mixer index and name
|
// Sets simple-mixer index and name.
|
||||||
snd_mixer_selem_id_set_index(mixerSelemId, mixerIndex);
|
snd_mixer_selem_id_set_index(mixerSelemId, mixerIndex);
|
||||||
snd_mixer_selem_id_set_name(mixerSelemId, mixerName);
|
snd_mixer_selem_id_set_name(mixerSelemId, mixerName);
|
||||||
//open mixer
|
// Open mixer.
|
||||||
if (snd_mixer_open(&mixerHandle, 0) >= 0)
|
if (snd_mixer_open(&mixerHandle, 0) >= 0) {
|
||||||
{
|
LOG(LogDebug) << "VolumeControl::init() - Opened ALSA mixer";
|
||||||
LOG(LogDebug) << "VolumeControl::init() - Opened ALSA mixer";
|
// Ok, attach to defualt card.
|
||||||
//ok. attach to defualt card
|
if (snd_mixer_attach(mixerHandle, mixerCard) >= 0) {
|
||||||
if (snd_mixer_attach(mixerHandle, mixerCard) >= 0)
|
LOG(LogDebug) << "VolumeControl::init() - Attached to default card";
|
||||||
{
|
// Ok, register simple element class.
|
||||||
LOG(LogDebug) << "VolumeControl::init() - Attached to default card";
|
if (snd_mixer_selem_register(mixerHandle, nullptr, nullptr) >= 0) {
|
||||||
//ok. register simple element class
|
LOG(LogDebug) << "VolumeControl::init() - Registered simple element class";
|
||||||
if (snd_mixer_selem_register(mixerHandle, NULL, NULL) >= 0)
|
// Ok, load registered elements.
|
||||||
{
|
if (snd_mixer_load(mixerHandle) >= 0) {
|
||||||
LOG(LogDebug) << "VolumeControl::init() - Registered simple element class";
|
LOG(LogDebug) << "VolumeControl::init() - Loaded mixer elements";
|
||||||
//ok. load registered elements
|
// Ok, find elements now.
|
||||||
if (snd_mixer_load(mixerHandle) >= 0)
|
mixerElem = snd_mixer_find_selem(mixerHandle, mixerSelemId);
|
||||||
{
|
if (mixerElem != nullptr) {
|
||||||
LOG(LogDebug) << "VolumeControl::init() - Loaded mixer elements";
|
// Wohoo. good to go...
|
||||||
//ok. find elements now
|
LOG(LogDebug) << "VolumeControl::init() - Mixer initialized";
|
||||||
mixerElem = snd_mixer_find_selem(mixerHandle, mixerSelemId);
|
}
|
||||||
if (mixerElem != nullptr)
|
else {
|
||||||
{
|
LOG(LogError) <<
|
||||||
//wohoo. good to go...
|
"VolumeControl::init() - Failed to find mixer elements!";
|
||||||
LOG(LogDebug) << "VolumeControl::init() - Mixer initialized";
|
snd_mixer_close(mixerHandle);
|
||||||
}
|
mixerHandle = nullptr;
|
||||||
else
|
}
|
||||||
{
|
}
|
||||||
LOG(LogError) << "VolumeControl::init() - Failed to find mixer elements!";
|
else {
|
||||||
snd_mixer_close(mixerHandle);
|
LOG(LogError) << "VolumeControl::init() - Failed to load mixer elements!";
|
||||||
mixerHandle = nullptr;
|
snd_mixer_close(mixerHandle);
|
||||||
}
|
mixerHandle = nullptr;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
else {
|
||||||
LOG(LogError) << "VolumeControl::init() - Failed to load mixer elements!";
|
LOG(LogError) <<
|
||||||
snd_mixer_close(mixerHandle);
|
"VolumeControl::init() - Failed to register simple element class!";
|
||||||
mixerHandle = nullptr;
|
snd_mixer_close(mixerHandle);
|
||||||
}
|
mixerHandle = nullptr;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
else {
|
||||||
LOG(LogError) << "VolumeControl::init() - Failed to register simple element class!";
|
LOG(LogError) << "VolumeControl::init() - Failed to attach to default card!";
|
||||||
snd_mixer_close(mixerHandle);
|
snd_mixer_close(mixerHandle);
|
||||||
mixerHandle = nullptr;
|
mixerHandle = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
LOG(LogError) << "VolumeControl::init() - Failed to open ALSA mixer!";
|
||||||
LOG(LogError) << "VolumeControl::init() - Failed to attach to default card!";
|
}
|
||||||
snd_mixer_close(mixerHandle);
|
}
|
||||||
mixerHandle = nullptr;
|
#elif defined(WIN32) || defined(_WIN32)
|
||||||
}
|
// Get windows version information.
|
||||||
}
|
OSVERSIONINFOEXA osVer = {sizeof(OSVERSIONINFO)};
|
||||||
else
|
::GetVersionExA(reinterpret_cast<LPOSVERSIONINFOA>(&osVer));
|
||||||
{
|
// Check windows version.
|
||||||
LOG(LogError) << "VolumeControl::init() - Failed to open ALSA mixer!";
|
if (osVer.dwMajorVersion < 6) {
|
||||||
}
|
// Windows older than Vista. use mixer API. open default mixer.
|
||||||
}
|
if (mixerHandle == nullptr) {
|
||||||
#elif defined(WIN32) || defined(_WIN32)
|
if (mixerOpen(&mixerHandle, 0, nullptr, 0, 0) == MMSYSERR_NOERROR) {
|
||||||
//get windows version information
|
// Retrieve info on the volume slider control for the "Speaker Out" line.
|
||||||
OSVERSIONINFOEXA osVer = {sizeof(OSVERSIONINFO)};
|
MIXERLINECONTROLS mixerLineControls;
|
||||||
::GetVersionExA(reinterpret_cast<LPOSVERSIONINFOA>(&osVer));
|
mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
|
||||||
//check windows version
|
mixerLineControls.dwLineID = 0xFFFF0000; // Id of "Speaker Out" line.
|
||||||
if(osVer.dwMajorVersion < 6)
|
mixerLineControls.cControls = 1;
|
||||||
{
|
// Id of "Speaker Out" line's volume slider.
|
||||||
//Windows older than Vista. use mixer API. open default mixer
|
//mixerLineControls.dwControlID = 0x00000000;
|
||||||
if (mixerHandle == nullptr)
|
//Get volume control.
|
||||||
{
|
mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
|
||||||
if (mixerOpen(&mixerHandle, 0, NULL, 0, 0) == MMSYSERR_NOERROR)
|
mixerLineControls.pamxctrl = &mixerControl;
|
||||||
{
|
mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
|
||||||
//retrieve info on the volume slider control for the "Speaker Out" line
|
if (mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls,
|
||||||
MIXERLINECONTROLS mixerLineControls;
|
MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR) {
|
||||||
mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
|
LOG(LogError) <<
|
||||||
mixerLineControls.dwLineID = 0xFFFF0000; //Id of "Speaker Out" line
|
"VolumeControl::getVolume() - Failed to get mixer volume control!";
|
||||||
mixerLineControls.cControls = 1;
|
mixerClose(mixerHandle);
|
||||||
//mixerLineControls.dwControlID = 0x00000000; //Id of "Speaker Out" line's volume slider
|
mixerHandle = nullptr;
|
||||||
mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; //Get volume control
|
}
|
||||||
mixerLineControls.pamxctrl = &mixerControl;
|
}
|
||||||
mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
|
else {
|
||||||
if (mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR)
|
LOG(LogError) << "VolumeControl::init() - Failed to open mixer!";
|
||||||
{
|
}
|
||||||
LOG(LogError) << "VolumeControl::getVolume() - Failed to get mixer volume control!";
|
}
|
||||||
mixerClose(mixerHandle);
|
}
|
||||||
mixerHandle = nullptr;
|
else {
|
||||||
}
|
// Windows Vista or above. use EndpointVolume API. get device enumerator.
|
||||||
}
|
if (endpointVolume == nullptr) {
|
||||||
else
|
CoInitialize(nullptr);
|
||||||
{
|
IMMDeviceEnumerator * deviceEnumerator = nullptr;
|
||||||
LOG(LogError) << "VolumeControl::init() - Failed to open mixer!";
|
CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER,
|
||||||
}
|
__uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
|
||||||
}
|
if (deviceEnumerator != nullptr) {
|
||||||
}
|
// Get default endpoint.
|
||||||
else
|
IMMDevice * defaultDevice = nullptr;
|
||||||
{
|
deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
|
||||||
//Windows Vista or above. use EndpointVolume API. get device enumerator
|
if (defaultDevice != nullptr) {
|
||||||
if (endpointVolume == nullptr)
|
// Retrieve endpoint volume.
|
||||||
{
|
defaultDevice->Activate(__uuidof(IAudioEndpointVolume),
|
||||||
CoInitialize(nullptr);
|
CLSCTX_INPROC_SERVER, nullptr, (LPVOID *)&endpointVolume);
|
||||||
IMMDeviceEnumerator * deviceEnumerator = nullptr;
|
if (endpointVolume == nullptr)
|
||||||
CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
|
LOG(LogError) << "VolumeControl::init() - "
|
||||||
if (deviceEnumerator != nullptr)
|
"Failed to get default audio endpoint volume!";
|
||||||
{
|
// Release default device. we don't need it anymore.
|
||||||
//get default endpoint
|
defaultDevice->Release();
|
||||||
IMMDevice * defaultDevice = nullptr;
|
}
|
||||||
deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
|
else {
|
||||||
if (defaultDevice != nullptr)
|
LOG(LogError) <<
|
||||||
{
|
"VolumeControl::init() - Failed to get default audio endpoint!";
|
||||||
//retrieve endpoint volume
|
}
|
||||||
defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr, (LPVOID *)&endpointVolume);
|
// Release device enumerator. we don't need it anymore.
|
||||||
if (endpointVolume == nullptr)
|
deviceEnumerator->Release();
|
||||||
{
|
}
|
||||||
LOG(LogError) << "VolumeControl::init() - Failed to get default audio endpoint volume!";
|
else {
|
||||||
}
|
LOG(LogError) << "VolumeControl::init() - Failed to get audio endpoint enumerator!";
|
||||||
//release default device. we don't need it anymore
|
CoUninitialize();
|
||||||
defaultDevice->Release();
|
}
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
#endif
|
||||||
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()
|
void VolumeControl::deinit()
|
||||||
{
|
{
|
||||||
//deinitialize audio mixer interface
|
// Deinitialize audio mixer interface.
|
||||||
#if defined (__APPLE__)
|
#if defined (__APPLE__)
|
||||||
#error TODO: Not implemented for MacOS yet!!!
|
#error TODO: Not implemented for MacOS yet!!!
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
if (mixerHandle != nullptr) {
|
if (mixerHandle != nullptr) {
|
||||||
snd_mixer_detach(mixerHandle, mixerCard);
|
snd_mixer_detach(mixerHandle, mixerCard);
|
||||||
snd_mixer_free(mixerHandle);
|
snd_mixer_free(mixerHandle);
|
||||||
snd_mixer_close(mixerHandle);
|
snd_mixer_close(mixerHandle);
|
||||||
mixerHandle = nullptr;
|
mixerHandle = nullptr;
|
||||||
mixerElem = nullptr;
|
mixerElem = nullptr;
|
||||||
}
|
}
|
||||||
#elif defined(WIN32) || defined(_WIN32)
|
#elif defined(WIN32) || defined(_WIN32)
|
||||||
if (mixerHandle != nullptr) {
|
if (mixerHandle != nullptr) {
|
||||||
mixerClose(mixerHandle);
|
mixerClose(mixerHandle);
|
||||||
mixerHandle = nullptr;
|
mixerHandle = nullptr;
|
||||||
}
|
}
|
||||||
else if (endpointVolume != nullptr) {
|
else if (endpointVolume != nullptr) {
|
||||||
endpointVolume->Release();
|
endpointVolume->Release();
|
||||||
endpointVolume = nullptr;
|
endpointVolume = nullptr;
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int VolumeControl::getVolume() const
|
int VolumeControl::getVolume() const
|
||||||
{
|
{
|
||||||
int volume = 0;
|
int volume = 0;
|
||||||
|
|
||||||
#if defined (__APPLE__)
|
#if defined (__APPLE__)
|
||||||
#error TODO: Not implemented for MacOS yet!!!
|
#error TODO: Not implemented for MacOS yet!!!
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
if (mixerElem != nullptr)
|
if (mixerElem != nullptr) {
|
||||||
{
|
// Get volume range.
|
||||||
//get volume range
|
long minVolume;
|
||||||
long minVolume;
|
long maxVolume;
|
||||||
long maxVolume;
|
if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) {
|
||||||
if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0)
|
// Ok, now get volume.
|
||||||
{
|
long rawVolume;
|
||||||
//ok. now get volume
|
if (snd_mixer_selem_get_playback_volume(mixerElem,
|
||||||
long rawVolume;
|
SND_MIXER_SCHN_MONO, &rawVolume) == 0) {
|
||||||
if (snd_mixer_selem_get_playback_volume(mixerElem, SND_MIXER_SCHN_MONO, &rawVolume) == 0)
|
// Worked. bring into range 0-100.
|
||||||
{
|
rawVolume -= minVolume;
|
||||||
//worked. bring into range 0-100
|
if (rawVolume > 0)
|
||||||
rawVolume -= minVolume;
|
volume = (rawVolume * 100.0) / (maxVolume - minVolume) + 0.5;
|
||||||
if (rawVolume > 0)
|
//else
|
||||||
{
|
// volume = 0;
|
||||||
volume = (rawVolume * 100.0) / (maxVolume - minVolume) + 0.5;
|
}
|
||||||
}
|
else {
|
||||||
//else volume = 0;
|
LOG(LogError) << "VolumeControl::getVolume() - Failed to get mixer volume!";
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
else {
|
||||||
LOG(LogError) << "VolumeControl::getVolume() - Failed to get mixer volume!";
|
LOG(LogError) << "VolumeControl::getVolume() - Failed to get volume range!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
#elif defined(WIN32) || defined(_WIN32)
|
||||||
{
|
if (mixerHandle != nullptr) {
|
||||||
LOG(LogError) << "VolumeControl::getVolume() - Failed to get volume range!";
|
// Windows older than Vista. use mixer API. get volume from line control.
|
||||||
}
|
MIXERCONTROLDETAILS_UNSIGNED value;
|
||||||
}
|
MIXERCONTROLDETAILS mixerControlDetails;
|
||||||
#elif defined(WIN32) || defined(_WIN32)
|
mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
|
||||||
if (mixerHandle != nullptr)
|
mixerControlDetails.dwControlID = mixerControl.dwControlID;
|
||||||
{
|
// Always 1 for a MIXERCONTROL_CONTROLF_UNIFORM control.
|
||||||
//Windows older than Vista. use mixer API. get volume from line control
|
mixerControlDetails.cChannels = 1;
|
||||||
MIXERCONTROLDETAILS_UNSIGNED value;
|
// Always 0 except for a MIXERCONTROL_CONTROLF_MULTIPLE control.
|
||||||
MIXERCONTROLDETAILS mixerControlDetails;
|
mixerControlDetails.cMultipleItems = 0;
|
||||||
mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
|
mixerControlDetails.paDetails = &value;
|
||||||
mixerControlDetails.dwControlID = mixerControl.dwControlID;
|
mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
|
||||||
mixerControlDetails.cChannels = 1; //always 1 for a MIXERCONTROL_CONTROLF_UNIFORM control
|
if (mixerGetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails,
|
||||||
mixerControlDetails.cMultipleItems = 0; //always 0 except for a MIXERCONTROL_CONTROLF_MULTIPLE control
|
MIXER_GETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR)
|
||||||
mixerControlDetails.paDetails = &value;
|
volume = (int)Math::round((value.dwValue * 100) / 65535.0f);
|
||||||
mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
|
else
|
||||||
if (mixerGetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_GETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR)
|
LOG(LogError) << "VolumeControl::getVolume() - Failed to get mixer volume!";
|
||||||
{
|
}
|
||||||
volume = (int)Math::round((value.dwValue * 100) / 65535.0f);
|
else if (endpointVolume != nullptr) {
|
||||||
}
|
// Windows Vista or above. use EndpointVolume API.
|
||||||
else
|
float floatVolume = 0.0f; // 0-1
|
||||||
{
|
if (endpointVolume->GetMasterVolumeLevelScalar(&floatVolume) == S_OK) {
|
||||||
LOG(LogError) << "VolumeControl::getVolume() - Failed to get mixer volume!";
|
volume = (int)Math::round(floatVolume * 100.0f);
|
||||||
}
|
LOG(LogInfo) << " getting volume as " << volume <<
|
||||||
}
|
" ( from float " << floatVolume << ")";
|
||||||
else if (endpointVolume != nullptr)
|
}
|
||||||
{
|
else {
|
||||||
//Windows Vista or above. use EndpointVolume API
|
LOG(LogError) << "VolumeControl::getVolume() - Failed to get master volume!";
|
||||||
float floatVolume = 0.0f; //0-1
|
}
|
||||||
if (endpointVolume->GetMasterVolumeLevelScalar(&floatVolume) == S_OK)
|
}
|
||||||
{
|
#endif
|
||||||
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!";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
// Clamp to 0-100 range.
|
||||||
#endif
|
if (volume < 0)
|
||||||
//clamp to 0-100 range
|
volume = 0;
|
||||||
if (volume < 0)
|
if (volume > 100)
|
||||||
{
|
volume = 100;
|
||||||
volume = 0;
|
return volume;
|
||||||
}
|
|
||||||
if (volume > 100)
|
|
||||||
{
|
|
||||||
volume = 100;
|
|
||||||
}
|
|
||||||
return volume;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VolumeControl::setVolume(int volume)
|
void VolumeControl::setVolume(int volume)
|
||||||
{
|
{
|
||||||
//clamp to 0-100 range
|
// Clamp to 0-100 range.
|
||||||
if (volume < 0)
|
if (volume < 0)
|
||||||
{
|
volume = 0;
|
||||||
volume = 0;
|
if (volume > 100)
|
||||||
}
|
volume = 100;
|
||||||
if (volume > 100)
|
|
||||||
{
|
// Store values in internal variables.
|
||||||
volume = 100;
|
internalVolume = volume;
|
||||||
}
|
#if defined (__APPLE__)
|
||||||
//store values in internal variables
|
#error TODO: Not implemented for MacOS yet!!!
|
||||||
internalVolume = volume;
|
#elif defined(__linux__)
|
||||||
#if defined (__APPLE__)
|
if (mixerElem != nullptr) {
|
||||||
#error TODO: Not implemented for MacOS yet!!!
|
// Get volume range.
|
||||||
#elif defined(__linux__)
|
long minVolume;
|
||||||
if (mixerElem != nullptr)
|
long maxVolume;
|
||||||
{
|
if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0) {
|
||||||
//get volume range
|
// Ok, bring into minVolume-maxVolume range and set.
|
||||||
long minVolume;
|
long rawVolume = (volume * (maxVolume - minVolume) / 100) + minVolume;
|
||||||
long maxVolume;
|
if (snd_mixer_selem_set_playback_volume(mixerElem,
|
||||||
if (snd_mixer_selem_get_playback_volume_range(mixerElem, &minVolume, &maxVolume) == 0)
|
SND_MIXER_SCHN_FRONT_LEFT, rawVolume) < 0 ||
|
||||||
{
|
snd_mixer_selem_set_playback_volume(mixerElem,
|
||||||
//ok. bring into minVolume-maxVolume range and set
|
SND_MIXER_SCHN_FRONT_RIGHT, rawVolume) < 0) {
|
||||||
long rawVolume = (volume * (maxVolume - minVolume) / 100) + minVolume;
|
LOG(LogError) << "VolumeControl::getVolume() - Failed to set mixer volume!";
|
||||||
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)
|
}
|
||||||
{
|
else {
|
||||||
LOG(LogError) << "VolumeControl::getVolume() - Failed to set mixer volume!";
|
LOG(LogError) << "VolumeControl::getVolume() - Failed to get volume range!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
#elif defined(WIN32) || defined(_WIN32)
|
||||||
{
|
if (mixerHandle != nullptr) {
|
||||||
LOG(LogError) << "VolumeControl::getVolume() - Failed to get volume range!";
|
// Windows older than Vista. use mixer API. get volume from line control.
|
||||||
}
|
MIXERCONTROLDETAILS_UNSIGNED value;
|
||||||
}
|
value.dwValue = (volume * 65535) / 100;
|
||||||
#elif defined(WIN32) || defined(_WIN32)
|
MIXERCONTROLDETAILS mixerControlDetails;
|
||||||
if (mixerHandle != nullptr)
|
mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
|
||||||
{
|
mixerControlDetails.dwControlID = mixerControl.dwControlID;
|
||||||
//Windows older than Vista. use mixer API. get volume from line control
|
// Always 1 for a MIXERCONTROL_CONTROLF_UNIFORM control.
|
||||||
MIXERCONTROLDETAILS_UNSIGNED value;
|
mixerControlDetails.cChannels = 1;
|
||||||
value.dwValue = (volume * 65535) / 100;
|
// Always 0 except for a MIXERCONTROL_CONTROLF_MULTIPLE control.
|
||||||
MIXERCONTROLDETAILS mixerControlDetails;
|
mixerControlDetails.cMultipleItems = 0;
|
||||||
mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
|
mixerControlDetails.paDetails = &value;
|
||||||
mixerControlDetails.dwControlID = mixerControl.dwControlID;
|
mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
|
||||||
mixerControlDetails.cChannels = 1; //always 1 for a MIXERCONTROL_CONTROLF_UNIFORM control
|
if (mixerSetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails,
|
||||||
mixerControlDetails.cMultipleItems = 0; //always 0 except for a MIXERCONTROL_CONTROLF_MULTIPLE control
|
MIXER_SETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
|
||||||
mixerControlDetails.paDetails = &value;
|
LOG(LogError) << "VolumeControl::setVolume() - Failed to set mixer volume!";
|
||||||
mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
|
}
|
||||||
if (mixerSetControlDetails((HMIXEROBJ)mixerHandle, &mixerControlDetails, MIXER_SETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
|
else if (endpointVolume != nullptr) {
|
||||||
{
|
// Windows Vista or above. use EndpointVolume API.
|
||||||
LOG(LogError) << "VolumeControl::setVolume() - Failed to set mixer volume!";
|
float floatVolume = 0.0f; // 0-1
|
||||||
}
|
if (volume > 0)
|
||||||
}
|
floatVolume = (float)volume / 100.0f;
|
||||||
else if (endpointVolume != nullptr)
|
if (endpointVolume->SetMasterVolumeLevelScalar(floatVolume, nullptr) != S_OK)
|
||||||
{
|
LOG(LogError) << "VolumeControl::setVolume() - Failed to set master volume!";
|
||||||
//Windows Vista or above. use EndpointVolume API
|
}
|
||||||
float floatVolume = 0.0f; //0-1
|
#endif
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// VolumeControl.h
|
||||||
|
//
|
||||||
|
// Controls system audio volume.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_APP_VOLUME_CONTROL_H
|
#ifndef ES_APP_VOLUME_CONTROL_H
|
||||||
#define ES_APP_VOLUME_CONTROL_H
|
#define ES_APP_VOLUME_CONTROL_H
|
||||||
|
@ -5,56 +11,54 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#if defined (__APPLE__)
|
#if defined (__APPLE__)
|
||||||
#error TODO: Not implemented for MacOS yet!!!
|
#error TODO: Not implemented for MacOS yet!!!
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
#elif defined(WIN32) || defined(_WIN32)
|
#elif defined(WIN32) || defined(_WIN32)
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <endpointvolume.h>
|
#include <endpointvolume.h>
|
||||||
#include <mmeapi.h>
|
#include <mmeapi.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*!
|
// Singleton pattern. Call getInstance() to get an object.
|
||||||
Singleton pattern. Call getInstance() to get an object.
|
|
||||||
*/
|
|
||||||
class VolumeControl
|
class VolumeControl
|
||||||
{
|
{
|
||||||
#if defined (__APPLE__)
|
#if defined (__APPLE__)
|
||||||
#error TODO: Not implemented for MacOS yet!!!
|
#error TODO: Not implemented for MacOS yet!!!
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
static const char * mixerName;
|
static const char * mixerName;
|
||||||
static const char * mixerCard;
|
static const char * mixerCard;
|
||||||
int mixerIndex;
|
int mixerIndex;
|
||||||
snd_mixer_t* mixerHandle;
|
snd_mixer_t* mixerHandle;
|
||||||
snd_mixer_elem_t* mixerElem;
|
snd_mixer_elem_t* mixerElem;
|
||||||
snd_mixer_selem_id_t* mixerSelemId;
|
snd_mixer_selem_id_t* mixerSelemId;
|
||||||
#elif defined(WIN32) || defined(_WIN32)
|
#elif defined(WIN32) || defined(_WIN32)
|
||||||
HMIXER mixerHandle;
|
HMIXER mixerHandle;
|
||||||
MIXERCONTROL mixerControl;
|
MIXERCONTROL mixerControl;
|
||||||
IAudioEndpointVolume * endpointVolume;
|
IAudioEndpointVolume * endpointVolume;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int originalVolume;
|
int originalVolume;
|
||||||
int internalVolume;
|
int internalVolume;
|
||||||
|
|
||||||
static std::weak_ptr<VolumeControl> sInstance;
|
static std::weak_ptr<VolumeControl> sInstance;
|
||||||
|
|
||||||
VolumeControl();
|
VolumeControl();
|
||||||
VolumeControl(const VolumeControl & right);
|
VolumeControl(const VolumeControl & right);
|
||||||
VolumeControl & operator=(const VolumeControl & right);
|
VolumeControl & operator=(const VolumeControl & right);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static std::shared_ptr<VolumeControl> & getInstance();
|
static std::shared_ptr<VolumeControl> & getInstance();
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void deinit();
|
void deinit();
|
||||||
|
|
||||||
int getVolume() const;
|
int getVolume() const;
|
||||||
void setVolume(int volume);
|
void setVolume(int volume);
|
||||||
|
|
||||||
~VolumeControl();
|
~VolumeControl();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_APP_VOLUME_CONTROL_H
|
#endif // ES_APP_VOLUME_CONTROL_H
|
||||||
|
|
|
@ -238,7 +238,7 @@ void GuiGamelistOptions::openMetaDataEd()
|
||||||
std::function<void()> deleteBtnFunc;
|
std::function<void()> deleteBtnFunc;
|
||||||
|
|
||||||
if (file->getType() == FOLDER) {
|
if (file->getType() == FOLDER) {
|
||||||
deleteBtnFunc = NULL;
|
deleteBtnFunc = nullptr;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
deleteBtnFunc = [this, file] {
|
deleteBtnFunc = [this, file] {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
//
|
//
|
||||||
// User interface for the screensaver options.
|
// User interface for the screensaver options.
|
||||||
// Based on the GuiScreenSaverOptions template.
|
// Based on the GuiScreenSaverOptions template.
|
||||||
// Submenu to the GuiMenu main menu.
|
// Submenu to the GuiMenu main menu.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "guis/GuiGeneralScreensaverOptions.h"
|
#include "guis/GuiGeneralScreensaverOptions.h"
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// GuiSettings.cpp
|
||||||
|
//
|
||||||
|
// User interface template for a settings GUI.
|
||||||
|
//
|
||||||
|
|
||||||
#include "guis/GuiSettings.h"
|
#include "guis/GuiSettings.h"
|
||||||
|
|
||||||
#include "views/ViewController.h"
|
#include "views/ViewController.h"
|
||||||
|
@ -5,63 +11,62 @@
|
||||||
#include "SystemData.h"
|
#include "SystemData.h"
|
||||||
#include "Window.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()
|
GuiSettings::~GuiSettings()
|
||||||
{
|
{
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiSettings::save()
|
void GuiSettings::save()
|
||||||
{
|
{
|
||||||
if(!mSaveFuncs.size())
|
if (!mSaveFuncs.size())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for(auto it = mSaveFuncs.cbegin(); it != mSaveFuncs.cend(); it++)
|
for (auto it = mSaveFuncs.cbegin(); it != mSaveFuncs.cend(); it++)
|
||||||
(*it)();
|
(*it)();
|
||||||
|
|
||||||
Settings::getInstance()->saveFile();
|
Settings::getInstance()->saveFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GuiSettings::input(InputConfig* config, Input input)
|
bool GuiSettings::input(InputConfig* config, Input input)
|
||||||
{
|
{
|
||||||
if(config->isMappedTo("b", input) && input.value != 0)
|
if (config->isMappedTo("b", input) && input.value != 0) {
|
||||||
{
|
delete this;
|
||||||
delete this;
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Keep code for potential future use.
|
// Keep code for potential future use.
|
||||||
// if(config->isMappedTo("start", input) && input.value != 0)
|
// if (config->isMappedTo("start", input) && input.value != 0)
|
||||||
// {
|
// {
|
||||||
// // close everything
|
// // close everything
|
||||||
// Window* window = mWindow;
|
// Window* window = mWindow;
|
||||||
// while(window->peekGui() && window->peekGui() != ViewController::get())
|
// while (window->peekGui() && window->peekGui() != ViewController::get())
|
||||||
// delete window->peekGui();
|
// delete window->peekGui();
|
||||||
// return true;
|
// return true;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return GuiComponent::input(config, input);
|
return GuiComponent::input(config, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
HelpStyle GuiSettings::getHelpStyle()
|
HelpStyle GuiSettings::getHelpStyle()
|
||||||
{
|
{
|
||||||
HelpStyle style = HelpStyle();
|
HelpStyle style = HelpStyle();
|
||||||
style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system");
|
style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system");
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HelpPrompt> GuiSettings::getHelpPrompts()
|
std::vector<HelpPrompt> GuiSettings::getHelpPrompts()
|
||||||
{
|
{
|
||||||
std::vector<HelpPrompt> prompts = mMenu.getHelpPrompts();
|
std::vector<HelpPrompt> prompts = mMenu.getHelpPrompts();
|
||||||
prompts.push_back(HelpPrompt("b", "back"));
|
prompts.push_back(HelpPrompt("b", "back"));
|
||||||
return prompts;
|
return prompts;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// GuiSettings.h
|
||||||
|
//
|
||||||
|
// User interface template for a settings GUI.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_APP_GUIS_GUI_SETTINGS_H
|
#ifndef ES_APP_GUIS_GUI_SETTINGS_H
|
||||||
#define ES_APP_GUIS_GUI_SETTINGS_H
|
#define ES_APP_GUIS_GUI_SETTINGS_H
|
||||||
|
@ -8,21 +14,22 @@
|
||||||
class GuiSettings : public GuiComponent
|
class GuiSettings : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GuiSettings(Window* window, const char* title);
|
GuiSettings(Window* window, const char* title);
|
||||||
virtual ~GuiSettings(); // just calls save();
|
virtual ~GuiSettings(); // Just calls save()
|
||||||
|
|
||||||
void save();
|
void save();
|
||||||
inline void addRow(const ComponentListRow& row) { mMenu.addRow(row); };
|
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 addWithLabel(const std::string& label,
|
||||||
inline void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); };
|
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;
|
bool input(InputConfig* config, Input input) override;
|
||||||
std::vector<HelpPrompt> getHelpPrompts() override;
|
std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
HelpStyle getHelpStyle() override;
|
HelpStyle getHelpStyle() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MenuComponent mMenu;
|
MenuComponent mMenu;
|
||||||
std::vector< std::function<void()> > mSaveFuncs;
|
std::vector< std::function<void()> > mSaveFuncs;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_APP_GUIS_GUI_SETTINGS_H
|
#endif // ES_APP_GUIS_GUI_SETTINGS_H
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
//
|
||||||
|
// GuiSlideshowScreensaverOptions.cpp
|
||||||
|
//
|
||||||
|
// User interface for the slideshow screensaver options.
|
||||||
|
// Submenu to GuiGeneralScreensaverOptions.
|
||||||
|
//
|
||||||
|
|
||||||
#include "guis/GuiSlideshowScreensaverOptions.h"
|
#include "guis/GuiSlideshowScreensaverOptions.h"
|
||||||
|
|
||||||
#include "components/SliderComponent.h"
|
#include "components/SliderComponent.h"
|
||||||
|
@ -7,76 +14,90 @@
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "Window.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)
|
// Image duration (in seconds).
|
||||||
auto sss_image_sec = std::make_shared<SliderComponent>(mWindow, 1.f, 60.f, 1.f, "s");
|
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)));
|
sss_image_sec->setValue((float)(Settings::getInstance()->
|
||||||
addWithLabel(row, "SWAP IMAGE AFTER (SECS)", sss_image_sec);
|
getInt("ScreenSaverSwapImageTimeout") / (1000)));
|
||||||
addSaveFunc([sss_image_sec] {
|
addWithLabel(row, "SWAP IMAGE AFTER (SECS)", sss_image_sec);
|
||||||
int playNextTimeout = (int)Math::round(sss_image_sec->getValue()) * (1000);
|
addSaveFunc([sss_image_sec] {
|
||||||
Settings::getInstance()->setInt("ScreenSaverSwapImageTimeout", playNextTimeout);
|
int playNextTimeout = (int)Math::round(sss_image_sec->getValue()) * (1000);
|
||||||
PowerSaver::updateTimeouts();
|
Settings::getInstance()->setInt("ScreenSaverSwapImageTimeout", playNextTimeout);
|
||||||
});
|
PowerSaver::updateTimeouts();
|
||||||
|
});
|
||||||
|
|
||||||
// stretch
|
// Stretch image.
|
||||||
auto sss_stretch = std::make_shared<SwitchComponent>(mWindow);
|
auto sss_stretch = std::make_shared<SwitchComponent>(mWindow);
|
||||||
sss_stretch->setState(Settings::getInstance()->getBool("SlideshowScreenSaverStretch"));
|
sss_stretch->setState(Settings::getInstance()->getBool("SlideshowScreenSaverStretch"));
|
||||||
addWithLabel(row, "STRETCH IMAGES", sss_stretch);
|
addWithLabel(row, "STRETCH IMAGES", sss_stretch);
|
||||||
addSaveFunc([sss_stretch] {
|
addSaveFunc([sss_stretch] {
|
||||||
Settings::getInstance()->setBool("SlideshowScreenSaverStretch", sss_stretch->getState());
|
Settings::getInstance()->setBool("SlideshowScreenSaverStretch", sss_stretch->getState());
|
||||||
});
|
});
|
||||||
|
|
||||||
// background audio file
|
// Background audio file.
|
||||||
auto sss_bg_audio_file = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
auto sss_bg_audio_file = std::make_shared<TextComponent>(mWindow, "",
|
||||||
addEditableTextComponent(row, "BACKGROUND AUDIO", sss_bg_audio_file, Settings::getInstance()->getString("SlideshowScreenSaverBackgroundAudioFile"));
|
Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
||||||
addSaveFunc([sss_bg_audio_file] {
|
addEditableTextComponent(row, "BACKGROUND AUDIO", sss_bg_audio_file,
|
||||||
Settings::getInstance()->setString("SlideshowScreenSaverBackgroundAudioFile", sss_bg_audio_file->getValue());
|
Settings::getInstance()->getString("SlideshowScreenSaverBackgroundAudioFile"));
|
||||||
});
|
addSaveFunc([sss_bg_audio_file] {
|
||||||
|
Settings::getInstance()->setString("SlideshowScreenSaverBackgroundAudioFile",
|
||||||
|
sss_bg_audio_file->getValue());
|
||||||
|
});
|
||||||
|
|
||||||
// image source
|
// Image source.
|
||||||
auto sss_custom_source = std::make_shared<SwitchComponent>(mWindow);
|
auto sss_custom_source = std::make_shared<SwitchComponent>(mWindow);
|
||||||
sss_custom_source->setState(Settings::getInstance()->getBool("SlideshowScreenSaverCustomImageSource"));
|
sss_custom_source->setState(Settings::getInstance()->
|
||||||
addWithLabel(row, "USE CUSTOM IMAGES", sss_custom_source);
|
getBool("SlideshowScreenSaverCustomImageSource"));
|
||||||
addSaveFunc([sss_custom_source] { Settings::getInstance()->setBool("SlideshowScreenSaverCustomImageSource", sss_custom_source->getState()); });
|
addWithLabel(row, "USE CUSTOM IMAGES", sss_custom_source);
|
||||||
|
addSaveFunc([sss_custom_source] { Settings::getInstance()->
|
||||||
|
setBool("SlideshowScreenSaverCustomImageSource", sss_custom_source->getState()); });
|
||||||
|
|
||||||
// custom image directory
|
// Custom image directory.
|
||||||
auto sss_image_dir = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
auto sss_image_dir = std::make_shared<TextComponent>(mWindow, "",
|
||||||
addEditableTextComponent(row, "CUSTOM IMAGE DIR", sss_image_dir, Settings::getInstance()->getString("SlideshowScreenSaverImageDir"));
|
Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
||||||
addSaveFunc([sss_image_dir] {
|
addEditableTextComponent(row, "CUSTOM IMAGE DIR", sss_image_dir,
|
||||||
Settings::getInstance()->setString("SlideshowScreenSaverImageDir", sss_image_dir->getValue());
|
Settings::getInstance()->getString("SlideshowScreenSaverImageDir"));
|
||||||
});
|
addSaveFunc([sss_image_dir] {
|
||||||
|
Settings::getInstance()->setString("SlideshowScreenSaverImageDir",
|
||||||
|
sss_image_dir->getValue());
|
||||||
|
});
|
||||||
|
|
||||||
// recurse custom image directory
|
// Recurse custom image directory.
|
||||||
auto sss_recurse = std::make_shared<SwitchComponent>(mWindow);
|
auto sss_recurse = std::make_shared<SwitchComponent>(mWindow);
|
||||||
sss_recurse->setState(Settings::getInstance()->getBool("SlideshowScreenSaverRecurse"));
|
sss_recurse->setState(Settings::getInstance()->getBool("SlideshowScreenSaverRecurse"));
|
||||||
addWithLabel(row, "CUSTOM IMAGE DIR RECURSIVE", sss_recurse);
|
addWithLabel(row, "CUSTOM IMAGE DIR RECURSIVE", sss_recurse);
|
||||||
addSaveFunc([sss_recurse] {
|
addSaveFunc([sss_recurse] {
|
||||||
Settings::getInstance()->setBool("SlideshowScreenSaverRecurse", sss_recurse->getState());
|
Settings::getInstance()->setBool("SlideshowScreenSaverRecurse", sss_recurse->getState());
|
||||||
});
|
});
|
||||||
|
|
||||||
// custom image filter
|
// Custom image filter (file extensions).
|
||||||
auto sss_image_filter = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
auto sss_image_filter = std::make_shared<TextComponent>(mWindow, "",
|
||||||
addEditableTextComponent(row, "CUSTOM IMAGE FILTER", sss_image_filter, Settings::getInstance()->getString("SlideshowScreenSaverImageFilter"));
|
Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
||||||
addSaveFunc([sss_image_filter] {
|
addEditableTextComponent(row, "CUSTOM IMAGE FILTER", sss_image_filter,
|
||||||
Settings::getInstance()->setString("SlideshowScreenSaverImageFilter", sss_image_filter->getValue());
|
Settings::getInstance()->getString("SlideshowScreenSaverImageFilter"));
|
||||||
});
|
addSaveFunc([sss_image_filter] {
|
||||||
|
Settings::getInstance()->setString("SlideshowScreenSaverImageFilter",
|
||||||
|
sss_image_filter->getValue());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
GuiSlideshowScreensaverOptions::~GuiSlideshowScreensaverOptions()
|
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);
|
auto lbl = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(label),
|
||||||
row.addElement(lbl, true); // label
|
Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||||
|
|
||||||
row.addElement(component, false, true);
|
row.addElement(lbl, true); // Label.
|
||||||
|
row.addElement(component, false, true);
|
||||||
addRow(row);
|
addRow(row);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
//
|
||||||
|
// GuiSlideshowScreensaverOptions.h
|
||||||
|
//
|
||||||
|
// User interface for the slideshow screensaver options.
|
||||||
|
// Submenu to GuiGeneralScreensaverOptions.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_APP_GUIS_GUI_SLIDESHOW_SCREENSAVER_OPTIONS_H
|
#ifndef ES_APP_GUIS_GUI_SLIDESHOW_SCREENSAVER_OPTIONS_H
|
||||||
#define 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
|
class GuiSlideshowScreensaverOptions : public GuiScreensaverOptions
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GuiSlideshowScreensaverOptions(Window* window, const char* title);
|
GuiSlideshowScreensaverOptions(Window* window, const char* title);
|
||||||
virtual ~GuiSlideshowScreensaverOptions();
|
virtual ~GuiSlideshowScreensaverOptions();
|
||||||
|
|
||||||
private:
|
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
|
#endif // ES_APP_GUIS_GUI_SLIDESHOW_SCREENSAVER_OPTIONS_H
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
//
|
||||||
|
// GuiVideoScreensaverOptions.cpp
|
||||||
|
//
|
||||||
|
// User interface for the video screensaver options.
|
||||||
|
// Submenu to GuiGeneralScreensaverOptions.
|
||||||
|
//
|
||||||
|
|
||||||
#include "guis/GuiVideoScreensaverOptions.h"
|
#include "guis/GuiVideoScreensaverOptions.h"
|
||||||
|
|
||||||
#include "components/OptionListComponent.h"
|
#include "components/OptionListComponent.h"
|
||||||
|
@ -6,89 +13,105 @@
|
||||||
#include "guis/GuiMsgBox.h"
|
#include "guis/GuiMsgBox.h"
|
||||||
#include "Settings.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
|
// Timer for swapping videos.
|
||||||
auto swap = std::make_shared<SliderComponent>(mWindow, 10.f, 1000.f, 1.f, "s");
|
auto swap = std::make_shared<SliderComponent>(mWindow, 10.f, 1000.f, 1.f, "s");
|
||||||
swap->setValue((float)(Settings::getInstance()->getInt("ScreenSaverSwapVideoTimeout") / (1000)));
|
swap->setValue((float)(Settings::getInstance()->
|
||||||
addWithLabel("SWAP VIDEO AFTER (SECS)", swap);
|
getInt("ScreenSaverSwapVideoTimeout") / (1000)));
|
||||||
addSaveFunc([swap] {
|
addWithLabel("SWAP VIDEO AFTER (SECS)", swap);
|
||||||
int playNextTimeout = (int)Math::round(swap->getValue()) * (1000);
|
addSaveFunc([swap] {
|
||||||
Settings::getInstance()->setInt("ScreenSaverSwapVideoTimeout", playNextTimeout);
|
int playNextTimeout = (int)Math::round(swap->getValue()) * (1000);
|
||||||
PowerSaver::updateTimeouts();
|
Settings::getInstance()->setInt("ScreenSaverSwapVideoTimeout", playNextTimeout);
|
||||||
});
|
PowerSaver::updateTimeouts();
|
||||||
|
});
|
||||||
|
|
||||||
auto stretch_screensaver = std::make_shared<SwitchComponent>(mWindow);
|
auto stretch_screensaver = std::make_shared<SwitchComponent>(mWindow);
|
||||||
stretch_screensaver->setState(Settings::getInstance()->getBool("StretchVideoOnScreenSaver"));
|
stretch_screensaver->setState(Settings::getInstance()->getBool("StretchVideoOnScreenSaver"));
|
||||||
addWithLabel("STRETCH VIDEO ON SCREENSAVER", stretch_screensaver);
|
addWithLabel("STRETCH VIDEO ON SCREENSAVER", stretch_screensaver);
|
||||||
addSaveFunc([stretch_screensaver] { Settings::getInstance()->setBool("StretchVideoOnScreenSaver", stretch_screensaver->getState()); });
|
addSaveFunc([stretch_screensaver] { Settings::getInstance()->
|
||||||
|
setBool("StretchVideoOnScreenSaver", stretch_screensaver->getState()); });
|
||||||
|
|
||||||
#ifdef _RPI_
|
#ifdef _RPI_
|
||||||
auto ss_omx = std::make_shared<SwitchComponent>(mWindow);
|
auto ss_omx = std::make_shared<SwitchComponent>(mWindow);
|
||||||
ss_omx->setState(Settings::getInstance()->getBool("ScreenSaverOmxPlayer"));
|
ss_omx->setState(Settings::getInstance()->getBool("ScreenSaverOmxPlayer"));
|
||||||
addWithLabel("USE OMX PLAYER FOR SCREENSAVER", ss_omx);
|
addWithLabel("USE OMX PLAYER FOR SCREENSAVER", ss_omx);
|
||||||
addSaveFunc([ss_omx, this] { Settings::getInstance()->setBool("ScreenSaverOmxPlayer", ss_omx->getState()); });
|
addSaveFunc([ss_omx, this] { Settings::getInstance()->
|
||||||
#endif
|
setBool("ScreenSaverOmxPlayer", ss_omx->getState()); });
|
||||||
|
#endif
|
||||||
|
|
||||||
// Render Video Game Name as subtitles
|
// Render video game name as subtitles.
|
||||||
auto ss_info = std::make_shared< OptionListComponent<std::string> >(mWindow, getHelpStyle(), "SHOW GAME INFO", false);
|
auto ss_info = std::make_shared<OptionListComponent<std::string>>
|
||||||
std::vector<std::string> info_type;
|
(mWindow,getHelpStyle(), "SHOW GAME INFO", false);
|
||||||
info_type.push_back("always");
|
std::vector<std::string> info_type;
|
||||||
info_type.push_back("start & end");
|
info_type.push_back("always");
|
||||||
info_type.push_back("never");
|
info_type.push_back("start & end");
|
||||||
for(auto it = info_type.cbegin(); it != info_type.cend(); it++)
|
info_type.push_back("never");
|
||||||
ss_info->add(*it, *it, Settings::getInstance()->getString("ScreenSaverGameInfo") == *it);
|
for(auto it = info_type.cbegin(); it != info_type.cend(); it++)
|
||||||
addWithLabel("SHOW GAME INFO ON SCREENSAVER", ss_info);
|
ss_info->add(*it, *it, Settings::getInstance()->getString("ScreenSaverGameInfo") == *it);
|
||||||
addSaveFunc([ss_info, this] { Settings::getInstance()->setString("ScreenSaverGameInfo", ss_info->getSelected()); });
|
addWithLabel("SHOW GAME INFO ON SCREENSAVER", ss_info);
|
||||||
|
addSaveFunc([ss_info, this] { Settings::getInstance()->
|
||||||
|
setString("ScreenSaverGameInfo", ss_info->getSelected()); });
|
||||||
|
|
||||||
#ifdef _RPI_
|
#ifdef _RPI_
|
||||||
ComponentListRow row;
|
ComponentListRow row;
|
||||||
|
|
||||||
// Set subtitle position
|
// Set subtitle position.
|
||||||
auto ss_omx_subs_align = std::make_shared< OptionListComponent<std::string> >(mWindow, "GAME INFO ALIGNMENT", false);
|
auto ss_omx_subs_align = std::make_shared<OptionListComponent<std::string>>
|
||||||
std::vector<std::string> align_mode;
|
(mWindow, "GAME INFO ALIGNMENT", false);
|
||||||
align_mode.push_back("left");
|
std::vector<std::string> align_mode;
|
||||||
align_mode.push_back("center");
|
align_mode.push_back("left");
|
||||||
for(auto it = align_mode.cbegin(); it != align_mode.cend(); it++)
|
align_mode.push_back("center");
|
||||||
ss_omx_subs_align->add(*it, *it, Settings::getInstance()->getString("SubtitleAlignment") == *it);
|
for(auto it = align_mode.cbegin(); it != align_mode.cend(); it++)
|
||||||
addWithLabel("GAME INFO ALIGNMENT", ss_omx_subs_align);
|
ss_omx_subs_align->add(*it, *it, Settings::getInstance()->
|
||||||
addSaveFunc([ss_omx_subs_align, this] { Settings::getInstance()->setString("SubtitleAlignment", ss_omx_subs_align->getSelected()); });
|
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
|
// Set font size.
|
||||||
auto ss_omx_font_size = std::make_shared<SliderComponent>(mWindow, 1.f, 64.f, 1.f, "h");
|
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")));
|
ss_omx_font_size->setValue((float)(Settings::getInstance()->getInt("SubtitleSize")));
|
||||||
addWithLabel("GAME INFO FONT SIZE", ss_omx_font_size);
|
addWithLabel("GAME INFO FONT SIZE", ss_omx_font_size);
|
||||||
addSaveFunc([ss_omx_font_size] {
|
addSaveFunc([ss_omx_font_size] {
|
||||||
int subSize = (int)Math::round(ss_omx_font_size->getValue());
|
int subSize = (int)Math::round(ss_omx_font_size->getValue());
|
||||||
Settings::getInstance()->setInt("SubtitleSize", subSize);
|
Settings::getInstance()->setInt("SubtitleSize", subSize);
|
||||||
});
|
});
|
||||||
|
|
||||||
auto ss_video_mute = std::make_shared<SwitchComponent>(mWindow);
|
auto ss_video_mute = std::make_shared<SwitchComponent>(mWindow);
|
||||||
ss_video_mute->setState(Settings::getInstance()->getBool("ScreenSaverVideoMute"));
|
ss_video_mute->setState(Settings::getInstance()->getBool("ScreenSaverVideoMute"));
|
||||||
addWithLabel("MUTE SCREENSAVER AUDIO", ss_video_mute);
|
addWithLabel("MUTE SCREENSAVER AUDIO", ss_video_mute);
|
||||||
addSaveFunc([ss_video_mute] { Settings::getInstance()->setBool("ScreenSaverVideoMute", ss_video_mute->getState()); });
|
addSaveFunc([ss_video_mute] { Settings::getInstance()->
|
||||||
|
setBool("ScreenSaverVideoMute", ss_video_mute->getState()); });
|
||||||
|
|
||||||
// Define subtitle font
|
// Define subtitle font.
|
||||||
auto ss_omx_font_file = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
auto ss_omx_font_file = std::make_shared<TextComponent>(mWindow, "",
|
||||||
addEditableTextComponent(row, "PATH TO FONT FILE", ss_omx_font_file, Settings::getInstance()->getString("SubtitleFont"));
|
Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
||||||
addSaveFunc([ss_omx_font_file] {
|
addEditableTextComponent(row, "PATH TO FONT FILE", ss_omx_font_file,
|
||||||
Settings::getInstance()->setString("SubtitleFont", ss_omx_font_file->getValue());
|
Settings::getInstance()->getString("SubtitleFont"));
|
||||||
});
|
addSaveFunc([ss_omx_font_file] {
|
||||||
|
Settings::getInstance()->setString("SubtitleFont", ss_omx_font_file->getValue());
|
||||||
|
});
|
||||||
|
|
||||||
// Define subtitle italic font
|
// Define subtitle italic font.
|
||||||
auto ss_omx_italic_font_file = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
auto ss_omx_italic_font_file = std::make_shared<TextComponent>(mWindow, "",
|
||||||
addEditableTextComponent(row, "PATH TO ITALIC FONT FILE", ss_omx_italic_font_file, Settings::getInstance()->getString("SubtitleItalicFont"));
|
Font::get(FONT_SIZE_SMALL), 0x777777FF);
|
||||||
addSaveFunc([ss_omx_italic_font_file] {
|
addEditableTextComponent(row, "PATH TO ITALIC FONT FILE", ss_omx_italic_font_file,
|
||||||
Settings::getInstance()->setString("SubtitleItalicFont", ss_omx_italic_font_file->getValue());
|
Settings::getInstance()->getString("SubtitleItalicFont"));
|
||||||
});
|
addSaveFunc([ss_omx_italic_font_file] {
|
||||||
#endif
|
Settings::getInstance()->setString("SubtitleItalicFont",
|
||||||
|
ss_omx_italic_font_file->getValue());
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef _RPI_
|
#ifndef _RPI_
|
||||||
auto captions_compatibility = std::make_shared<SwitchComponent>(mWindow);
|
auto captions_compatibility = std::make_shared<SwitchComponent>(mWindow);
|
||||||
captions_compatibility->setState(Settings::getInstance()->getBool("CaptionsCompatibility"));
|
captions_compatibility->setState(Settings::getInstance()->getBool("CaptionsCompatibility"));
|
||||||
addWithLabel("USE COMPATIBLE LOW RESOLUTION FOR CAPTIONS", captions_compatibility);
|
addWithLabel("USE COMPATIBLE LOW RESOLUTION FOR CAPTIONS", captions_compatibility);
|
||||||
addSaveFunc([captions_compatibility] { Settings::getInstance()->setBool("CaptionsCompatibility", captions_compatibility->getState()); });
|
addSaveFunc([captions_compatibility] { Settings::getInstance()->
|
||||||
#endif
|
setBool("CaptionsCompatibility", captions_compatibility->getState()); });
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
GuiVideoScreensaverOptions::~GuiVideoScreensaverOptions()
|
GuiVideoScreensaverOptions::~GuiVideoScreensaverOptions()
|
||||||
|
@ -97,18 +120,25 @@ GuiVideoScreensaverOptions::~GuiVideoScreensaverOptions()
|
||||||
|
|
||||||
void GuiVideoScreensaverOptions::save()
|
void GuiVideoScreensaverOptions::save()
|
||||||
{
|
{
|
||||||
#ifdef _RPI_
|
#ifdef _RPI_
|
||||||
bool startingStatusNotRisky = (Settings::getInstance()->getString("ScreenSaverGameInfo") == "never" || !Settings::getInstance()->getBool("ScreenSaverOmxPlayer"));
|
bool startingStatusNotRisky = (Settings::getInstance()->
|
||||||
#endif
|
getString("ScreenSaverGameInfo") == "never" ||
|
||||||
GuiScreensaverOptions::save();
|
!Settings::getInstance()->getBool("ScreenSaverOmxPlayer"));
|
||||||
|
#endif
|
||||||
|
GuiScreensaverOptions::save();
|
||||||
|
|
||||||
#ifdef _RPI_
|
#ifdef _RPI_
|
||||||
bool endStatusRisky = (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never" && Settings::getInstance()->getBool("ScreenSaverOmxPlayer"));
|
bool endStatusRisky = (Settings::getInstance()->getString("ScreenSaverGameInfo") !=
|
||||||
if (startingStatusNotRisky && endStatusRisky) {
|
"never" && Settings::getInstance()->getBool("ScreenSaverOmxPlayer"));
|
||||||
// if before it wasn't risky but now there's a risk of problems, show warning
|
if (startingStatusNotRisky && endStatusRisky) {
|
||||||
mWindow->pushGui(new GuiMsgBox(mWindow,
|
// If before it wasn't risky but now there's a risk of problems, show warning.
|
||||||
"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.",
|
mWindow->pushGui(new GuiMsgBox(mWindow,
|
||||||
"GOT IT!", [] { return; }));
|
"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\" "
|
||||||
#endif
|
"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
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
//
|
||||||
|
// GuiVideoScreensaverOptions.h
|
||||||
|
//
|
||||||
|
// User interface for the video screensaver options.
|
||||||
|
// Submenu to GuiGeneralScreensaverOptions.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_APP_GUIS_GUI_VIDEO_SCREENSAVER_OPTIONS_H
|
#ifndef ES_APP_GUIS_GUI_VIDEO_SCREENSAVER_OPTIONS_H
|
||||||
#define 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
|
class GuiVideoScreensaverOptions : public GuiScreensaverOptions
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GuiVideoScreensaverOptions(Window* window, const char* title);
|
GuiVideoScreensaverOptions(Window* window, const char* title);
|
||||||
virtual ~GuiVideoScreensaverOptions();
|
virtual ~GuiVideoScreensaverOptions();
|
||||||
|
|
||||||
void save() override;
|
void save() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_APP_GUIS_GUI_VIDEO_SCREENSAVER_OPTIONS_H
|
#endif // ES_APP_GUIS_GUI_VIDEO_SCREENSAVER_OPTIONS_H
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "SystemData.h"
|
#include "SystemData.h"
|
||||||
#include "SystemScreenSaver.h"
|
#include "SystemScreenSaver.h"
|
||||||
|
#include <FreeImage.h>
|
||||||
#include <SDL_events.h>
|
#include <SDL_events.h>
|
||||||
#include <SDL_main.h>
|
#include <SDL_main.h>
|
||||||
#include <SDL_timer.h>
|
#include <SDL_timer.h>
|
||||||
|
@ -39,8 +40,6 @@
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <FreeImage.h>
|
|
||||||
|
|
||||||
enum eErrorCodes {
|
enum eErrorCodes {
|
||||||
NO_ERRORS,
|
NO_ERRORS,
|
||||||
NO_SYSTEMS_FILE,
|
NO_SYSTEMS_FILE,
|
||||||
|
@ -289,7 +288,7 @@ void onExit()
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
srand((unsigned int)time(NULL));
|
srand((unsigned int)time(nullptr));
|
||||||
|
|
||||||
std::locale::global(std::locale("C"));
|
std::locale::global(std::locale("C"));
|
||||||
|
|
||||||
|
|
|
@ -360,7 +360,7 @@ bool resizeImage(const std::string& path, int maxWidth, int maxHeight)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
FREE_IMAGE_FORMAT format = FIF_UNKNOWN;
|
FREE_IMAGE_FORMAT format = FIF_UNKNOWN;
|
||||||
FIBITMAP* image = NULL;
|
FIBITMAP* image = nullptr;
|
||||||
|
|
||||||
// Detect the filetype.
|
// Detect the filetype.
|
||||||
format = FreeImage_GetFileType(path.c_str(), 0);
|
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);
|
FIBITMAP* imageRescaled = FreeImage_Rescale(image, maxWidth, maxHeight, FILTER_BILINEAR);
|
||||||
FreeImage_Unload(image);
|
FreeImage_Unload(image);
|
||||||
|
|
||||||
if (imageRescaled == NULL) {
|
if (imageRescaled == nullptr) {
|
||||||
LOG(LogError) << "Could not resize image! (not enough memory? invalid bitdepth?)";
|
LOG(LogError) << "Could not resize image! (not enough memory? invalid bitdepth?)";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
void screenscraper_generate_scraper_requests(const ScraperSearchParams& params,
|
||||||
|
@ -307,7 +307,7 @@ void ScreenScraperRequest::processMedia(
|
||||||
std::string& fileFormat,
|
std::string& fileFormat,
|
||||||
std::string region)
|
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.
|
// 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
|
// We need to do this because any child of 'medias' has the form
|
||||||
|
|
|
@ -12,11 +12,11 @@
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
|
||||||
UIModeController * UIModeController::sInstance = NULL;
|
UIModeController * UIModeController::sInstance = nullptr;
|
||||||
|
|
||||||
UIModeController * UIModeController::getInstance()
|
UIModeController * UIModeController::getInstance()
|
||||||
{
|
{
|
||||||
if (sInstance == NULL)
|
if (sInstance == nullptr)
|
||||||
sInstance = new UIModeController();
|
sInstance = new UIModeController();
|
||||||
|
|
||||||
return sInstance;
|
return sInstance;
|
||||||
|
|
|
@ -232,7 +232,7 @@ void DetailedGameListView::initMDValues()
|
||||||
|
|
||||||
void DetailedGameListView::updateInfoPanel()
|
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;
|
bool fadingOut;
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
|
|
|
@ -264,7 +264,7 @@ void VideoGameListView::initMDValues()
|
||||||
|
|
||||||
void VideoGameListView::updateInfoPanel()
|
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());
|
Utils::FileSystem::removeFile(getTitlePath());
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
GuiComponent::GuiComponent(Window* window)
|
GuiComponent::GuiComponent(Window* window)
|
||||||
: mWindow(window),
|
: mWindow(window),
|
||||||
mParent(NULL),
|
mParent(nullptr),
|
||||||
mOpacity(255),
|
mOpacity(255),
|
||||||
mPosition(Vector3f::Zero()),
|
mPosition(Vector3f::Zero()),
|
||||||
mOrigin(Vector2f::Zero()),
|
mOrigin(Vector2f::Zero()),
|
||||||
|
@ -27,7 +27,7 @@ GuiComponent::GuiComponent(Window* window)
|
||||||
mVisible(true)
|
mVisible(true)
|
||||||
{
|
{
|
||||||
for (unsigned char i = 0; i < MAX_ANIMATIONS; i++)
|
for (unsigned char i = 0; i < MAX_ANIMATIONS; i++)
|
||||||
mAnimationMap[i] = NULL;
|
mAnimationMap[i] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
GuiComponent::~GuiComponent()
|
GuiComponent::~GuiComponent()
|
||||||
|
@ -40,7 +40,7 @@ GuiComponent::~GuiComponent()
|
||||||
mParent->removeChild(this);
|
mParent->removeChild(this);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < getChildCount(); i++)
|
for (unsigned int i = 0; i < getChildCount(); i++)
|
||||||
getChild(i)->setParent(NULL);
|
getChild(i)->setParent(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GuiComponent::input(InputConfig* config, Input input)
|
bool GuiComponent::input(InputConfig* config, Input input)
|
||||||
|
@ -203,7 +203,7 @@ void GuiComponent::removeChild(GuiComponent* cmp)
|
||||||
if (cmp->getParent() != this)
|
if (cmp->getParent() != this)
|
||||||
LOG(LogError) << "Tried to remove child from incorrect parent!";
|
LOG(LogError) << "Tried to remove child from incorrect parent!";
|
||||||
|
|
||||||
cmp->setParent(NULL);
|
cmp->setParent(nullptr);
|
||||||
|
|
||||||
for (auto i = mChildren.cbegin(); i != mChildren.cend(); i++) {
|
for (auto i = mChildren.cbegin(); i != mChildren.cend(); i++) {
|
||||||
if (*i == cmp) {
|
if (*i == cmp) {
|
||||||
|
@ -334,7 +334,7 @@ bool GuiComponent::stopAnimation(unsigned char slot)
|
||||||
assert(slot < MAX_ANIMATIONS);
|
assert(slot < MAX_ANIMATIONS);
|
||||||
if (mAnimationMap[slot]) {
|
if (mAnimationMap[slot]) {
|
||||||
delete mAnimationMap[slot];
|
delete mAnimationMap[slot];
|
||||||
mAnimationMap[slot] = NULL;
|
mAnimationMap[slot] = nullptr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -348,7 +348,7 @@ bool GuiComponent::cancelAnimation(unsigned char slot)
|
||||||
if (mAnimationMap[slot]) {
|
if (mAnimationMap[slot]) {
|
||||||
mAnimationMap[slot]->removeFinishedCallback();
|
mAnimationMap[slot]->removeFinishedCallback();
|
||||||
delete mAnimationMap[slot];
|
delete mAnimationMap[slot];
|
||||||
mAnimationMap[slot] = NULL;
|
mAnimationMap[slot] = nullptr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -366,7 +366,7 @@ bool GuiComponent::finishAnimation(unsigned char slot)
|
||||||
assert(done);
|
assert(done);
|
||||||
|
|
||||||
delete mAnimationMap[slot]; // Will also call finishedCallback
|
delete mAnimationMap[slot]; // Will also call finishedCallback
|
||||||
mAnimationMap[slot] = NULL;
|
mAnimationMap[slot] = nullptr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -381,7 +381,7 @@ bool GuiComponent::advanceAnimation(unsigned char slot, unsigned int time)
|
||||||
if (anim) {
|
if (anim) {
|
||||||
bool done = anim->update(time);
|
bool done = anim->update(time);
|
||||||
if (done) {
|
if (done) {
|
||||||
mAnimationMap[slot] = NULL;
|
mAnimationMap[slot] = nullptr;
|
||||||
delete anim;
|
delete anim;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -405,18 +405,18 @@ void GuiComponent::cancelAllAnimations()
|
||||||
|
|
||||||
bool GuiComponent::isAnimationPlaying(unsigned char slot) const
|
bool GuiComponent::isAnimationPlaying(unsigned char slot) const
|
||||||
{
|
{
|
||||||
return mAnimationMap[slot] != NULL;
|
return mAnimationMap[slot] != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GuiComponent::isAnimationReversed(unsigned char slot) const
|
bool GuiComponent::isAnimationReversed(unsigned char slot) const
|
||||||
{
|
{
|
||||||
assert(mAnimationMap[slot] != NULL);
|
assert(mAnimationMap[slot] != nullptr);
|
||||||
return mAnimationMap[slot]->isReversed();
|
return mAnimationMap[slot]->isReversed();
|
||||||
}
|
}
|
||||||
|
|
||||||
int GuiComponent::getAnimationTime(unsigned char slot) const
|
int GuiComponent::getAnimationTime(unsigned char slot) const
|
||||||
{
|
{
|
||||||
assert(mAnimationMap[slot] != NULL);
|
assert(mAnimationMap[slot] != nullptr);
|
||||||
return mAnimationMap[slot]->getTime();
|
return mAnimationMap[slot]->getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,233 +1,234 @@
|
||||||
|
//
|
||||||
|
// InputConfig.cpp
|
||||||
|
//
|
||||||
|
// Input device configuration functions.
|
||||||
|
//
|
||||||
|
|
||||||
#include "InputConfig.h"
|
#include "InputConfig.h"
|
||||||
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include <pugixml/src/pugixml.hpp>
|
#include <pugixml/src/pugixml.hpp>
|
||||||
|
|
||||||
//some util functions
|
// Some utility functions.
|
||||||
std::string inputTypeToString(InputType type)
|
std::string inputTypeToString(InputType type)
|
||||||
{
|
{
|
||||||
switch(type)
|
switch (type) {
|
||||||
{
|
case TYPE_AXIS:
|
||||||
case TYPE_AXIS:
|
return "axis";
|
||||||
return "axis";
|
case TYPE_BUTTON:
|
||||||
case TYPE_BUTTON:
|
return "button";
|
||||||
return "button";
|
case TYPE_HAT:
|
||||||
case TYPE_HAT:
|
return "hat";
|
||||||
return "hat";
|
case TYPE_KEY:
|
||||||
case TYPE_KEY:
|
return "key";
|
||||||
return "key";
|
case TYPE_CEC_BUTTON:
|
||||||
case TYPE_CEC_BUTTON:
|
return "cec-button";
|
||||||
return "cec-button";
|
default:
|
||||||
default:
|
return "error";
|
||||||
return "error";
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InputType stringToInputType(const std::string& type)
|
InputType stringToInputType(const std::string& type)
|
||||||
{
|
{
|
||||||
if(type == "axis")
|
if (type == "axis")
|
||||||
return TYPE_AXIS;
|
return TYPE_AXIS;
|
||||||
if(type == "button")
|
if (type == "button")
|
||||||
return TYPE_BUTTON;
|
return TYPE_BUTTON;
|
||||||
if(type == "hat")
|
if (type == "hat")
|
||||||
return TYPE_HAT;
|
return TYPE_HAT;
|
||||||
if(type == "key")
|
if (type == "key")
|
||||||
return TYPE_KEY;
|
return TYPE_KEY;
|
||||||
if(type == "cec-button")
|
if (type == "cec-button")
|
||||||
return TYPE_CEC_BUTTON;
|
return TYPE_CEC_BUTTON;
|
||||||
return TYPE_COUNT;
|
return TYPE_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string toLower(std::string str)
|
std::string toLower(std::string str)
|
||||||
{
|
{
|
||||||
for(unsigned int i = 0; i < str.length(); i++)
|
for (unsigned int i = 0; i < str.length(); i++)
|
||||||
{
|
str[i] = (char)tolower(str[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()
|
void InputConfig::clear()
|
||||||
{
|
{
|
||||||
mNameMap.clear();
|
mNameMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputConfig::isConfigured()
|
bool InputConfig::isConfigured()
|
||||||
{
|
{
|
||||||
return mNameMap.size() > 0;
|
return mNameMap.size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputConfig::mapInput(const std::string& name, Input input)
|
void InputConfig::mapInput(const std::string& name, Input input)
|
||||||
{
|
{
|
||||||
mNameMap[toLower(name)] = input;
|
mNameMap[toLower(name)] = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputConfig::unmapInput(const std::string& name)
|
void InputConfig::unmapInput(const std::string& name)
|
||||||
{
|
{
|
||||||
auto it = mNameMap.find(toLower(name));
|
auto it = mNameMap.find(toLower(name));
|
||||||
if(it != mNameMap.cend())
|
if (it != mNameMap.cend())
|
||||||
mNameMap.erase(it);
|
mNameMap.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputConfig::getInputByName(const std::string& name, Input* result)
|
bool InputConfig::getInputByName(const std::string& name, Input* result)
|
||||||
{
|
{
|
||||||
auto it = mNameMap.find(toLower(name));
|
auto it = mNameMap.find(toLower(name));
|
||||||
if(it != mNameMap.cend())
|
if (it != mNameMap.cend()) {
|
||||||
{
|
*result = it->second;
|
||||||
*result = it->second;
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
return false;
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputConfig::isMappedTo(const std::string& name, Input input)
|
bool InputConfig::isMappedTo(const std::string& name, Input input)
|
||||||
{
|
{
|
||||||
Input comp;
|
Input comp;
|
||||||
if(!getInputByName(name, &comp))
|
if (!getInputByName(name, &comp))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(comp.configured && comp.type == input.type && comp.id == input.id)
|
if (comp.configured && comp.type == input.type && comp.id == input.id) {
|
||||||
{
|
if (comp.type == TYPE_HAT)
|
||||||
if(comp.type == TYPE_HAT)
|
return (input.value == 0 || input.value & comp.value);
|
||||||
{
|
|
||||||
return (input.value == 0 || input.value & comp.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(comp.type == TYPE_AXIS)
|
if (comp.type == TYPE_AXIS)
|
||||||
{
|
return input.value == 0 || comp.value == input.value;
|
||||||
return input.value == 0 || comp.value == input.value;
|
else
|
||||||
}else{
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
return false;
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputConfig::isMappedLike(const std::string& name, Input input)
|
bool InputConfig::isMappedLike(const std::string& name, Input input)
|
||||||
{
|
{
|
||||||
if(name == "left")
|
if (name == "left") {
|
||||||
{
|
return isMappedTo("left", input) || isMappedTo("leftanalogleft", input) ||
|
||||||
return isMappedTo("left", input) || isMappedTo("leftanalogleft", input) || isMappedTo("rightanalogleft", input);
|
isMappedTo("rightanalogleft", input);
|
||||||
}else if(name == "right"){
|
}
|
||||||
return isMappedTo("right", input) || isMappedTo("leftanalogright", input) || isMappedTo("rightanalogright", input);
|
else if (name == "right") {
|
||||||
}else if(name == "up"){
|
return isMappedTo("right", input) || isMappedTo("leftanalogright", input) ||
|
||||||
return isMappedTo("up", input) || isMappedTo("leftanalogup", input) || isMappedTo("rightanalogup", input);
|
isMappedTo("rightanalogright", input);
|
||||||
}else if(name == "down"){
|
}
|
||||||
return isMappedTo("down", input) || isMappedTo("leftanalogdown", input) || isMappedTo("rightanalogdown", input);
|
else if (name == "up") {
|
||||||
}else if(name == "leftshoulder"){
|
return isMappedTo("up", input) || isMappedTo("leftanalogup", input) ||
|
||||||
return isMappedTo("leftshoulder", input) || isMappedTo("pageup", input);
|
isMappedTo("rightanalogup", input);
|
||||||
}else if(name == "rightshoulder"){
|
}
|
||||||
return isMappedTo("rightshoulder", input) || isMappedTo("pagedown", input);
|
else if (name == "down") {
|
||||||
}else if(name == "lefttrigger"){
|
return isMappedTo("down", input) || isMappedTo("leftanalogdown", input) ||
|
||||||
return isMappedTo("lefttrigger", input) || isMappedTo("home", input);
|
isMappedTo("rightanalogdown", input);
|
||||||
}else if(name == "righttrigger"){
|
}
|
||||||
return isMappedTo("righttrigger", input) || isMappedTo("end", input);
|
else if (name == "leftshoulder") {
|
||||||
}
|
return isMappedTo("leftshoulder", input) || isMappedTo("pageup", input);
|
||||||
return isMappedTo(name, 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> InputConfig::getMappedTo(Input input)
|
||||||
{
|
{
|
||||||
std::vector<std::string> maps;
|
std::vector<std::string> maps;
|
||||||
|
|
||||||
typedef std::map<std::string, Input>::const_iterator it_type;
|
typedef std::map<std::string, Input>::const_iterator it_type;
|
||||||
for(it_type iterator = mNameMap.cbegin(); iterator != mNameMap.cend(); iterator++)
|
for (it_type iterator = mNameMap.cbegin(); iterator != mNameMap.cend(); iterator++) {
|
||||||
{
|
Input chk = iterator->second;
|
||||||
Input chk = iterator->second;
|
|
||||||
|
|
||||||
if(!chk.configured)
|
if (!chk.configured)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(chk.device == input.device && chk.type == input.type && chk.id == input.id)
|
if (chk.device == input.device && chk.type == input.type && chk.id == input.id) {
|
||||||
{
|
if (chk.type == TYPE_HAT) {
|
||||||
if(chk.type == TYPE_HAT)
|
if (input.value == 0 || input.value & chk.value)
|
||||||
{
|
maps.push_back(iterator->first);
|
||||||
if(input.value == 0 || input.value & chk.value)
|
continue;
|
||||||
{
|
}
|
||||||
maps.push_back(iterator->first);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(input.type == TYPE_AXIS)
|
if (input.type == TYPE_AXIS) {
|
||||||
{
|
if (input.value == 0 || chk.value == input.value)
|
||||||
if(input.value == 0 || chk.value == input.value)
|
maps.push_back(iterator->first);
|
||||||
maps.push_back(iterator->first);
|
}
|
||||||
}else{
|
else {
|
||||||
maps.push_back(iterator->first);
|
maps.push_back(iterator->first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return maps;
|
||||||
return maps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputConfig::loadFromXML(pugi::xml_node& node)
|
void InputConfig::loadFromXML(pugi::xml_node& node)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
for(pugi::xml_node input = node.child("input"); input; input = input.next_sibling("input"))
|
for (pugi::xml_node input = node.child("input"); input; input = input.next_sibling("input")) {
|
||||||
{
|
std::string name = input.attribute("name").as_string();
|
||||||
std::string name = input.attribute("name").as_string();
|
std::string type = input.attribute("type").as_string();
|
||||||
std::string type = input.attribute("type").as_string();
|
InputType typeEnum = stringToInputType(type);
|
||||||
InputType typeEnum = stringToInputType(type);
|
|
||||||
|
|
||||||
if(typeEnum == TYPE_COUNT)
|
if (typeEnum == TYPE_COUNT) {
|
||||||
{
|
LOG(LogError) << "InputConfig load error - input of type \"" << type <<
|
||||||
LOG(LogError) << "InputConfig load error - input of type \"" << type << "\" is invalid! Skipping input \"" << name << "\".\n";
|
"\" is invalid! Skipping input \"" << name << "\".\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int id = input.attribute("id").as_int();
|
int id = input.attribute("id").as_int();
|
||||||
int value = input.attribute("value").as_int();
|
int value = input.attribute("value").as_int();
|
||||||
|
|
||||||
if(value == 0)
|
if (value == 0)
|
||||||
LOG(LogWarning) << "WARNING: InputConfig value is 0 for " << type << " " << id << "!\n";
|
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)
|
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)
|
if (mDeviceId == DEVICE_KEYBOARD) {
|
||||||
{
|
cfg.append_attribute("type") = "keyboard";
|
||||||
cfg.append_attribute("type") = "keyboard";
|
cfg.append_attribute("deviceName") = "Keyboard";
|
||||||
cfg.append_attribute("deviceName") = "Keyboard";
|
}
|
||||||
}
|
else if (mDeviceId == DEVICE_CEC) {
|
||||||
else if(mDeviceId == DEVICE_CEC)
|
cfg.append_attribute("type") = "cec";
|
||||||
{
|
cfg.append_attribute("deviceName") = "CEC";
|
||||||
cfg.append_attribute("type") = "cec";
|
}
|
||||||
cfg.append_attribute("deviceName") = "CEC";
|
else {
|
||||||
}
|
cfg.append_attribute("type") = "joystick";
|
||||||
else
|
cfg.append_attribute("deviceName") = mDeviceName.c_str();
|
||||||
{
|
}
|
||||||
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;
|
typedef std::map<std::string, Input>::const_iterator it_type;
|
||||||
for(it_type iterator = mNameMap.cbegin(); iterator != mNameMap.cend(); iterator++)
|
for (it_type iterator = mNameMap.cbegin(); iterator != mNameMap.cend(); iterator++) {
|
||||||
{
|
if (!iterator->second.configured)
|
||||||
if(!iterator->second.configured)
|
continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
pugi::xml_node input = cfg.append_child("input");
|
pugi::xml_node input = cfg.append_child("input");
|
||||||
input.append_attribute("name") = iterator->first.c_str();
|
input.append_attribute("name") = iterator->first.c_str();
|
||||||
input.append_attribute("type") = inputTypeToString(iterator->second.type).c_str();
|
input.append_attribute("type") = inputTypeToString(iterator->second.type).c_str();
|
||||||
input.append_attribute("id").set_value(iterator->second.id);
|
input.append_attribute("id").set_value(iterator->second.id);
|
||||||
input.append_attribute("value").set_value(iterator->second.value);
|
input.append_attribute("value").set_value(iterator->second.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// InputConfig.h
|
||||||
|
//
|
||||||
|
// Input device configuration functions.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_CORE_INPUT_CONFIG_H
|
#ifndef ES_CORE_INPUT_CONFIG_H
|
||||||
#define 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_KEYBOARD -1
|
||||||
#define DEVICE_CEC -2
|
#define DEVICE_CEC -2
|
||||||
|
|
||||||
enum InputType
|
enum InputType {
|
||||||
{
|
TYPE_AXIS,
|
||||||
TYPE_AXIS,
|
TYPE_BUTTON,
|
||||||
TYPE_BUTTON,
|
TYPE_HAT,
|
||||||
TYPE_HAT,
|
TYPE_KEY,
|
||||||
TYPE_KEY,
|
TYPE_CEC_BUTTON,
|
||||||
TYPE_CEC_BUTTON,
|
TYPE_COUNT
|
||||||
TYPE_COUNT
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Input
|
struct Input
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int device;
|
int device;
|
||||||
InputType type;
|
InputType type;
|
||||||
int id;
|
int id;
|
||||||
int value;
|
int value;
|
||||||
bool configured;
|
bool configured;
|
||||||
|
|
||||||
Input()
|
Input()
|
||||||
{
|
{
|
||||||
device = DEVICE_KEYBOARD;
|
device = DEVICE_KEYBOARD;
|
||||||
configured = false;
|
configured = false;
|
||||||
id = -1;
|
id = -1;
|
||||||
value = -999;
|
value = -999;
|
||||||
type = TYPE_COUNT;
|
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)
|
std::string getHatDir(int val)
|
||||||
{
|
{
|
||||||
if(val & SDL_HAT_UP)
|
if (val & SDL_HAT_UP)
|
||||||
return "up";
|
return "up";
|
||||||
else if(val & SDL_HAT_DOWN)
|
else if (val & SDL_HAT_DOWN)
|
||||||
return "down";
|
return "down";
|
||||||
else if(val & SDL_HAT_LEFT)
|
else if (val & SDL_HAT_LEFT)
|
||||||
return "left";
|
return "left";
|
||||||
else if(val & SDL_HAT_RIGHT)
|
else if (val & SDL_HAT_RIGHT)
|
||||||
return "right";
|
return "right";
|
||||||
return "neutral?";
|
return "neutral?";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getCECButtonName(int keycode)
|
std::string getCECButtonName(int keycode)
|
||||||
{
|
{
|
||||||
return CECInput::getKeyCodeString(keycode);
|
return CECInput::getKeyCodeString(keycode);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string string()
|
std::string string()
|
||||||
{
|
{
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
switch(type)
|
switch (type) {
|
||||||
{
|
case TYPE_BUTTON:
|
||||||
case TYPE_BUTTON:
|
stream << "Button " << id;
|
||||||
stream << "Button " << id;
|
break;
|
||||||
break;
|
case TYPE_AXIS:
|
||||||
case TYPE_AXIS:
|
stream << "Axis " << id << (value > 0 ? "+" : "-");
|
||||||
stream << "Axis " << id << (value > 0 ? "+" : "-");
|
break;
|
||||||
break;
|
case TYPE_HAT:
|
||||||
case TYPE_HAT:
|
stream << "Hat " << id << " " << getHatDir(value);
|
||||||
stream << "Hat " << id << " " << getHatDir(value);
|
break;
|
||||||
break;
|
case TYPE_KEY:
|
||||||
case TYPE_KEY:
|
stream << "Key " << SDL_GetKeyName((SDL_Keycode)id);
|
||||||
stream << "Key " << SDL_GetKeyName((SDL_Keycode)id);
|
break;
|
||||||
break;
|
case TYPE_CEC_BUTTON:
|
||||||
case TYPE_CEC_BUTTON:
|
stream << "CEC-Button " << getCECButtonName(id);
|
||||||
stream << "CEC-Button " << getCECButtonName(id);
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
stream << "Input to string error";
|
||||||
stream << "Input to string error";
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class InputConfig
|
class InputConfig
|
||||||
{
|
{
|
||||||
public:
|
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 clear();
|
||||||
void mapInput(const std::string& name, Input input);
|
void mapInput(const std::string& name, Input input);
|
||||||
void unmapInput(const std::string& name); // unmap all Inputs mapped to this name
|
void unmapInput(const std::string& name); // Unmap all Inputs mapped to this name.
|
||||||
|
|
||||||
inline int getDeviceId() const { return mDeviceId; };
|
inline int getDeviceId() const { return mDeviceId; };
|
||||||
inline const std::string& getDeviceName() { return mDeviceName; }
|
inline const std::string& getDeviceName() { return mDeviceName; }
|
||||||
inline const std::string& getDeviceGUIDString() { return mDeviceGUID; }
|
inline const std::string& getDeviceGUIDString() { return mDeviceGUID; }
|
||||||
|
|
||||||
//Returns true if Input is mapped to this name, false otherwise.
|
// Returns true if Input is mapped to this name, false otherwise.
|
||||||
bool isMappedTo(const std::string& name, Input input);
|
bool isMappedTo(const std::string& name, Input input);
|
||||||
bool isMappedLike(const std::string& name, Input input);
|
bool isMappedLike(const std::string& name, Input input);
|
||||||
|
|
||||||
//Returns a list of names this input is mapped to.
|
// Returns a list of names this input is mapped to.
|
||||||
std::vector<std::string> getMappedTo(Input input);
|
std::vector<std::string> getMappedTo(Input input);
|
||||||
|
|
||||||
// Returns true if there is an Input mapped to this name, false otherwise.
|
// Returns true if there is an Input mapped to this name, false otherwise.
|
||||||
// Writes Input mapped to this name to result if true.
|
// Writes Input mapped to this name to result if true.
|
||||||
bool getInputByName(const std::string& name, Input* result);
|
bool getInputByName(const std::string& name, Input* result);
|
||||||
|
|
||||||
void loadFromXML(pugi::xml_node& root);
|
void loadFromXML(pugi::xml_node& root);
|
||||||
void writeToXML(pugi::xml_node& parent);
|
void writeToXML(pugi::xml_node& parent);
|
||||||
|
|
||||||
bool isConfigured();
|
bool isConfigured();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<std::string, Input> mNameMap;
|
std::map<std::string, Input> mNameMap;
|
||||||
const int mDeviceId;
|
const int mDeviceId;
|
||||||
const std::string mDeviceName;
|
const std::string mDeviceName;
|
||||||
const std::string mDeviceGUID;
|
const std::string mDeviceGUID;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_INPUT_CONFIG_H
|
#endif // ES_CORE_INPUT_CONFIG_H
|
||||||
|
|
|
@ -455,7 +455,7 @@ std::string InputManager::getTemporaryConfigPath()
|
||||||
|
|
||||||
bool InputManager::initialized() const
|
bool InputManager::initialized() const
|
||||||
{
|
{
|
||||||
return mKeyboardInputConfig != NULL;
|
return mKeyboardInputConfig != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int InputManager::getNumConfiguredDevices()
|
int InputManager::getNumConfiguredDevices()
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// GuiDetectDevice.cpp
|
||||||
|
//
|
||||||
|
// Detect input devices (keyboards, joysticks and gamepads).
|
||||||
|
//
|
||||||
|
|
||||||
#include "guis/GuiDetectDevice.h"
|
#include "guis/GuiDetectDevice.h"
|
||||||
|
|
||||||
#include "components/TextComponent.h"
|
#include "components/TextComponent.h"
|
||||||
|
@ -10,117 +16,124 @@
|
||||||
|
|
||||||
#define HOLD_TIME 1000
|
#define HOLD_TIME 1000
|
||||||
|
|
||||||
GuiDetectDevice::GuiDetectDevice(Window* window, bool firstRun, const std::function<void()>& doneCallback) : GuiComponent(window), mFirstRun(firstRun),
|
GuiDetectDevice::GuiDetectDevice(
|
||||||
mBackground(window, ":/graphics/frame.png"), mGrid(window, Vector2i(1, 5))
|
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;
|
mHoldingConfig = nullptr;
|
||||||
mHoldTime = 0;
|
mHoldTime = 0;
|
||||||
mDoneCallback = doneCallback;
|
mDoneCallback = doneCallback;
|
||||||
|
|
||||||
addChild(&mBackground);
|
addChild(&mBackground);
|
||||||
addChild(&mGrid);
|
addChild(&mGrid);
|
||||||
|
|
||||||
// title
|
// Title.
|
||||||
mTitle = std::make_shared<TextComponent>(mWindow, firstRun ? "WELCOME" : "CONFIGURE INPUT",
|
mTitle = std::make_shared<TextComponent>(mWindow, firstRun ? "WELCOME" : "CONFIGURE INPUT",
|
||||||
Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
|
Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
|
||||||
mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(1, 1), GridFlags::BORDER_BOTTOM);
|
mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(1, 1), GridFlags::BORDER_BOTTOM);
|
||||||
|
|
||||||
// device info
|
// Device info.
|
||||||
std::stringstream deviceInfo;
|
std::stringstream deviceInfo;
|
||||||
int numDevices = InputManager::getInstance()->getNumJoysticks();
|
int numDevices = InputManager::getInstance()->getNumJoysticks();
|
||||||
|
|
||||||
if(numDevices > 0)
|
if (numDevices > 0)
|
||||||
deviceInfo << numDevices << " GAMEPAD" << (numDevices > 1 ? "S" : "") << " DETECTED";
|
deviceInfo << numDevices << " GAMEPAD" << (numDevices > 1 ? "S" : "") << " DETECTED";
|
||||||
else
|
else
|
||||||
deviceInfo << "NO GAMEPADS DETECTED";
|
deviceInfo << "NO GAMEPADS DETECTED";
|
||||||
mDeviceInfo = std::make_shared<TextComponent>(mWindow, deviceInfo.str(), Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER);
|
mDeviceInfo = std::make_shared<TextComponent>(mWindow, deviceInfo.str(),
|
||||||
mGrid.setEntry(mDeviceInfo, Vector2i(0, 1), false, true);
|
Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER);
|
||||||
|
mGrid.setEntry(mDeviceInfo, Vector2i(0, 1), false, true);
|
||||||
|
|
||||||
// message
|
// Message.
|
||||||
mMsg1 = std::make_shared<TextComponent>(mWindow, "HOLD A BUTTON ON YOUR DEVICE TO CONFIGURE IT.", Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER);
|
mMsg1 = std::make_shared<TextComponent>(mWindow,
|
||||||
mGrid.setEntry(mMsg1, Vector2i(0, 2), false, true);
|
"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.";
|
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);
|
mMsg2 = std::make_shared<TextComponent>(mWindow, msg2str,
|
||||||
mGrid.setEntry(mMsg2, Vector2i(0, 3), false, true);
|
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER);
|
||||||
|
mGrid.setEntry(mMsg2, Vector2i(0, 3), false, true);
|
||||||
|
|
||||||
// currently held device
|
// Currently held device.
|
||||||
mDeviceHeld = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_MEDIUM), 0xFFFFFFFF, ALIGN_CENTER);
|
mDeviceHeld = std::make_shared<TextComponent>(mWindow, "",
|
||||||
mGrid.setEntry(mDeviceHeld, Vector2i(0, 4), false, true);
|
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);
|
setSize(Renderer::getScreenWidth() * 0.6f, Renderer::getScreenHeight() * 0.5f);
|
||||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2);
|
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2,
|
||||||
|
(Renderer::getScreenHeight() - mSize.y()) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiDetectDevice::onSizeChanged()
|
void GuiDetectDevice::onSizeChanged()
|
||||||
{
|
{
|
||||||
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
||||||
|
|
||||||
// grid
|
// Grid.
|
||||||
mGrid.setSize(mSize);
|
mGrid.setSize(mSize);
|
||||||
mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y());
|
mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y());
|
||||||
//mGrid.setRowHeightPerc(1, mDeviceInfo->getFont()->getHeight() / mSize.y());
|
//mGrid.setRowHeightPerc(1, mDeviceInfo->getFont()->getHeight() / mSize.y());
|
||||||
mGrid.setRowHeightPerc(2, mMsg1->getFont()->getHeight() / mSize.y());
|
mGrid.setRowHeightPerc(2, mMsg1->getFont()->getHeight() / mSize.y());
|
||||||
mGrid.setRowHeightPerc(3, mMsg2->getFont()->getHeight() / mSize.y());
|
mGrid.setRowHeightPerc(3, mMsg2->getFont()->getHeight() / mSize.y());
|
||||||
//mGrid.setRowHeightPerc(4, mDeviceHeld->getFont()->getHeight() / mSize.y());
|
//mGrid.setRowHeightPerc(4, mDeviceHeld->getFont()->getHeight() / mSize.y());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GuiDetectDevice::input(InputConfig* config, Input input)
|
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)
|
if (!mFirstRun && input.device == DEVICE_KEYBOARD && input.type == TYPE_KEY &&
|
||||||
{
|
input.value && input.id == SDLK_ESCAPE) {
|
||||||
// cancel configuring
|
// Cancel the configuration.
|
||||||
PowerSaver::resume();
|
PowerSaver::resume();
|
||||||
delete this;
|
delete this;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(input.type == TYPE_BUTTON || input.type == TYPE_KEY ||input.type == TYPE_CEC_BUTTON)
|
if (input.type == TYPE_BUTTON || input.type == TYPE_KEY ||input.type == TYPE_CEC_BUTTON) {
|
||||||
{
|
if (input.value && mHoldingConfig == nullptr) {
|
||||||
if(input.value && mHoldingConfig == NULL)
|
// Started holding.
|
||||||
{
|
mHoldingConfig = config;
|
||||||
// started holding
|
mHoldTime = HOLD_TIME;
|
||||||
mHoldingConfig = config;
|
mDeviceHeld->setText(Utils::String::toUpper(config->getDeviceName()));
|
||||||
mHoldTime = HOLD_TIME;
|
}
|
||||||
mDeviceHeld->setText(Utils::String::toUpper(config->getDeviceName()));
|
else if (!input.value && mHoldingConfig == config) {
|
||||||
}else if(!input.value && mHoldingConfig == config)
|
// Cancel.
|
||||||
{
|
mHoldingConfig = nullptr;
|
||||||
// cancel
|
mDeviceHeld->setText("");
|
||||||
mHoldingConfig = NULL;
|
}
|
||||||
mDeviceHeld->setText("");
|
}
|
||||||
}
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiDetectDevice::update(int deltaTime)
|
void GuiDetectDevice::update(int deltaTime)
|
||||||
{
|
{
|
||||||
if(mHoldingConfig)
|
if (mHoldingConfig) {
|
||||||
{
|
// If ES starts and if a known device is connected after startup
|
||||||
// If ES starts and if a known device is connected after startup skip controller configuration
|
// skip controller configuration.
|
||||||
if(mFirstRun && Utils::FileSystem::exists(InputManager::getConfigPath()) && InputManager::getInstance()->getNumConfiguredDevices() > 0)
|
if (mFirstRun && Utils::FileSystem::exists(InputManager::getConfigPath()) &&
|
||||||
{
|
InputManager::getInstance()->getNumConfiguredDevices() > 0) {
|
||||||
if(mDoneCallback)
|
if (mDoneCallback)
|
||||||
mDoneCallback();
|
mDoneCallback();
|
||||||
PowerSaver::resume();
|
PowerSaver::resume();
|
||||||
delete this; // delete GUI element
|
delete this; // Delete GUI element.
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
mHoldTime -= deltaTime;
|
||||||
mHoldTime -= deltaTime;
|
const float t = (float)mHoldTime / HOLD_TIME;
|
||||||
const float t = (float)mHoldTime / HOLD_TIME;
|
unsigned int c = (unsigned char)(t * 255);
|
||||||
unsigned int c = (unsigned char)(t * 255);
|
mDeviceHeld->setColor((c << 24) | (c << 16) | (c << 8) | 0xFF);
|
||||||
mDeviceHeld->setColor((c << 24) | (c << 16) | (c << 8) | 0xFF);
|
if (mHoldTime <= 0) {
|
||||||
if(mHoldTime <= 0)
|
// Picked one!
|
||||||
{
|
mWindow->pushGui(new GuiInputConfig(mWindow, mHoldingConfig, true, mDoneCallback));
|
||||||
// picked one!
|
PowerSaver::resume();
|
||||||
mWindow->pushGui(new GuiInputConfig(mWindow, mHoldingConfig, true, mDoneCallback));
|
delete this;
|
||||||
PowerSaver::resume();
|
}
|
||||||
delete this;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// GuiDetectDevice.h
|
||||||
|
//
|
||||||
|
// Detect input devices (keyboards, joysticks and gamepads).
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_CORE_GUIS_GUI_DETECT_DEVICE_H
|
#ifndef ES_CORE_GUIS_GUI_DETECT_DEVICE_H
|
||||||
#define 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
|
class GuiDetectDevice : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
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;
|
bool input(InputConfig* config, Input input) override;
|
||||||
void update(int deltaTime) override;
|
void update(int deltaTime) override;
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool mFirstRun;
|
bool mFirstRun;
|
||||||
InputConfig* mHoldingConfig;
|
InputConfig* mHoldingConfig;
|
||||||
int mHoldTime;
|
int mHoldTime;
|
||||||
|
|
||||||
NinePatchComponent mBackground;
|
NinePatchComponent mBackground;
|
||||||
ComponentGrid mGrid;
|
ComponentGrid mGrid;
|
||||||
|
|
||||||
std::shared_ptr<TextComponent> mTitle;
|
std::shared_ptr<TextComponent> mTitle;
|
||||||
std::shared_ptr<TextComponent> mMsg1;
|
std::shared_ptr<TextComponent> mMsg1;
|
||||||
std::shared_ptr<TextComponent> mMsg2;
|
std::shared_ptr<TextComponent> mMsg2;
|
||||||
std::shared_ptr<TextComponent> mDeviceInfo;
|
std::shared_ptr<TextComponent> mDeviceInfo;
|
||||||
std::shared_ptr<TextComponent> mDeviceHeld;
|
std::shared_ptr<TextComponent> mDeviceHeld;
|
||||||
|
|
||||||
std::function<void()> mDoneCallback;
|
std::function<void()> mDoneCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_GUIS_GUI_DETECT_DEVICE_H
|
#endif // ES_CORE_GUIS_GUI_DETECT_DEVICE_H
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// GuiInputConfig.cpp
|
||||||
|
//
|
||||||
|
// Input device configuration GUI (for keyboards, joysticks and gamepads).
|
||||||
|
//
|
||||||
|
|
||||||
#include "guis/GuiInputConfig.h"
|
#include "guis/GuiInputConfig.h"
|
||||||
|
|
||||||
#include "components/ButtonComponent.h"
|
#include "components/ButtonComponent.h"
|
||||||
|
@ -7,194 +13,209 @@
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
|
||||||
struct InputConfigStructure
|
struct InputConfigStructure {
|
||||||
{
|
const char* name;
|
||||||
const char* name;
|
const bool skippable;
|
||||||
const bool skippable;
|
const char* dispName;
|
||||||
const char* dispName;
|
const char* icon;
|
||||||
const char* icon;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int inputCount = 22;
|
static const int inputCount = 22;
|
||||||
|
|
||||||
static const InputConfigStructure GUI_INPUT_CONFIG_LIST[inputCount] =
|
static const InputConfigStructure GUI_INPUT_CONFIG_LIST[inputCount] =
|
||||||
{
|
{
|
||||||
{ "Up", false, "D-PAD UP", ":/help/dpad_up.svg" },
|
{ "Up", false, "D-PAD UP", ":/help/dpad_up.svg" },
|
||||||
{ "Down", false, "D-PAD DOWN", ":/help/dpad_down.svg" },
|
{ "Down", false, "D-PAD DOWN", ":/help/dpad_down.svg" },
|
||||||
{ "Left", false, "D-PAD LEFT", ":/help/dpad_left.svg" },
|
{ "Left", false, "D-PAD LEFT", ":/help/dpad_left.svg" },
|
||||||
{ "Right", false, "D-PAD RIGHT", ":/help/dpad_right.svg" },
|
{ "Right", false, "D-PAD RIGHT", ":/help/dpad_right.svg" },
|
||||||
{ "Start", true, "START", ":/help/button_start.svg" },
|
{ "Start", true, "START", ":/help/button_start.svg" },
|
||||||
{ "Select", true, "SELECT", ":/help/button_select.svg" },
|
{ "Select", true, "SELECT", ":/help/button_select.svg" },
|
||||||
{ "A", false, "BUTTON A / EAST", ":/help/buttons_east.svg" },
|
{ "A", false, "BUTTON A / EAST", ":/help/buttons_east.svg" },
|
||||||
{ "B", true, "BUTTON B / SOUTH", ":/help/buttons_south.svg" },
|
{ "B", true, "BUTTON B / SOUTH", ":/help/buttons_south.svg" },
|
||||||
{ "X", true, "BUTTON X / NORTH", ":/help/buttons_north.svg" },
|
{ "X", true, "BUTTON X / NORTH", ":/help/buttons_north.svg" },
|
||||||
{ "Y", true, "BUTTON Y / WEST", ":/help/buttons_west.svg" },
|
{ "Y", true, "BUTTON Y / WEST", ":/help/buttons_west.svg" },
|
||||||
{ "LeftShoulder", true, "LEFT SHOULDER", ":/help/button_l.svg" },
|
{ "LeftShoulder", true, "LEFT SHOULDER", ":/help/button_l.svg" },
|
||||||
{ "RightShoulder", true, "RIGHT SHOULDER", ":/help/button_r.svg" },
|
{ "RightShoulder", true, "RIGHT SHOULDER", ":/help/button_r.svg" },
|
||||||
{ "LeftTrigger", true, "LEFT TRIGGER", ":/help/button_lt.svg" },
|
{ "LeftTrigger", true, "LEFT TRIGGER", ":/help/button_lt.svg" },
|
||||||
{ "RightTrigger", true, "RIGHT TRIGGER", ":/help/button_rt.svg" },
|
{ "RightTrigger", true, "RIGHT TRIGGER", ":/help/button_rt.svg" },
|
||||||
// { "LeftThumb", true, "LEFT THUMB", ":/help/analog_thumb.svg" },
|
// { "LeftThumb", true, "LEFT THUMB", ":/help/analog_thumb.svg" },
|
||||||
// { "RightThumb", true, "RIGHT THUMB", ":/help/analog_thumb.svg" },
|
// { "RightThumb", true, "RIGHT THUMB", ":/help/analog_thumb.svg" },
|
||||||
{ "LeftAnalogUp", true, "LEFT ANALOG UP", ":/help/analog_up.svg" },
|
{ "LeftAnalogUp", true, "LEFT ANALOG UP", ":/help/analog_up.svg" },
|
||||||
{ "LeftAnalogDown", true, "LEFT ANALOG DOWN", ":/help/analog_down.svg" },
|
{ "LeftAnalogDown", true, "LEFT ANALOG DOWN", ":/help/analog_down.svg" },
|
||||||
{ "LeftAnalogLeft", true, "LEFT ANALOG LEFT", ":/help/analog_left.svg" },
|
{ "LeftAnalogLeft", true, "LEFT ANALOG LEFT", ":/help/analog_left.svg" },
|
||||||
{ "LeftAnalogRight", true, "LEFT ANALOG RIGHT", ":/help/analog_right.svg" },
|
{ "LeftAnalogRight", true, "LEFT ANALOG RIGHT", ":/help/analog_right.svg" },
|
||||||
{ "RightAnalogUp", true, "RIGHT ANALOG UP", ":/help/analog_up.svg" },
|
{ "RightAnalogUp", true, "RIGHT ANALOG UP", ":/help/analog_up.svg" },
|
||||||
{ "RightAnalogDown", true, "RIGHT ANALOG DOWN", ":/help/analog_down.svg" },
|
{ "RightAnalogDown", true, "RIGHT ANALOG DOWN", ":/help/analog_down.svg" },
|
||||||
{ "RightAnalogLeft", true, "RIGHT ANALOG LEFT", ":/help/analog_left.svg" },
|
{ "RightAnalogLeft", true, "RIGHT ANALOG LEFT", ":/help/analog_left.svg" },
|
||||||
{ "RightAnalogRight", true, "RIGHT ANALOG RIGHT", ":/help/analog_right.svg" },
|
{ "RightAnalogRight", true, "RIGHT ANALOG RIGHT", ":/help/analog_right.svg" },
|
||||||
// { "HotKeyEnable", true, "HOTKEY ENABLE", ":/help/button_hotkey.svg" }
|
// { "HotKeyEnable", true, "HOTKEY ENABLE", ":/help/button_hotkey.svg" }
|
||||||
};
|
};
|
||||||
|
|
||||||
//MasterVolUp and MasterVolDown are also hooked up, but do not appear on this screen.
|
// 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.
|
// If you want, you can manually add them to es_input.cfg.
|
||||||
|
|
||||||
#define HOLD_TO_SKIP_MS 1000
|
#define HOLD_TO_SKIP_MS 1000
|
||||||
|
|
||||||
GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target, bool reconfigureAll, const std::function<void()>& okCallback) : GuiComponent(window),
|
GuiInputConfig::GuiInputConfig(
|
||||||
mBackground(window, ":/graphics/frame.png"), mGrid(window, Vector2i(1, 7)),
|
Window* window,
|
||||||
mTargetConfig(target), mHoldingInput(false), mBusyAnim(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)
|
if (reconfigureAll)
|
||||||
target->clear();
|
target->clear();
|
||||||
|
|
||||||
mConfiguringAll = reconfigureAll;
|
mConfiguringAll = reconfigureAll;
|
||||||
mConfiguringRow = mConfiguringAll;
|
mConfiguringRow = mConfiguringAll;
|
||||||
|
|
||||||
addChild(&mBackground);
|
addChild(&mBackground);
|
||||||
addChild(&mGrid);
|
addChild(&mGrid);
|
||||||
|
|
||||||
// 0 is a spacer row
|
// 0 is a spacer row.
|
||||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0), false);
|
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);
|
mTitle = std::make_shared<TextComponent>(mWindow, "CONFIGURING",
|
||||||
mGrid.setEntry(mTitle, Vector2i(0, 1), false, true);
|
Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
|
||||||
|
mGrid.setEntry(mTitle, Vector2i(0, 1), false, true);
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
if(target->getDeviceId() == DEVICE_KEYBOARD)
|
if (target->getDeviceId() == DEVICE_KEYBOARD)
|
||||||
ss << "KEYBOARD";
|
ss << "KEYBOARD";
|
||||||
else if(target->getDeviceId() == DEVICE_CEC)
|
else if (target->getDeviceId() == DEVICE_CEC)
|
||||||
ss << "CEC";
|
ss << "CEC";
|
||||||
else
|
else
|
||||||
ss << "GAMEPAD " << (target->getDeviceId() + 1);
|
ss << "GAMEPAD " << (target->getDeviceId() + 1);
|
||||||
mSubtitle1 = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(ss.str()), Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
|
mSubtitle1 = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(ss.str()),
|
||||||
mGrid.setEntry(mSubtitle1, Vector2i(0, 2), false, true);
|
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);
|
mSubtitle2 = std::make_shared<TextComponent>(mWindow, "HOLD ANY BUTTON TO SKIP",
|
||||||
mGrid.setEntry(mSubtitle2, Vector2i(0, 3), false, true);
|
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);
|
// Icon.
|
||||||
mGrid.setEntry(mList, Vector2i(0, 5), true, true);
|
auto icon = std::make_shared<ImageComponent>(mWindow);
|
||||||
for(int i = 0; i < inputCount; i++)
|
icon->setImage(GUI_INPUT_CONFIG_LIST[i].icon);
|
||||||
{
|
icon->setColorShift(0x777777FF);
|
||||||
ComponentListRow row;
|
icon->setResize(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * 1.25f);
|
||||||
|
row.addElement(icon, false);
|
||||||
|
|
||||||
// icon
|
// Spacer between icon and text.
|
||||||
auto icon = std::make_shared<ImageComponent>(mWindow);
|
auto spacer = std::make_shared<GuiComponent>(mWindow);
|
||||||
icon->setImage(GUI_INPUT_CONFIG_LIST[i].icon);
|
spacer->setSize(16, 0);
|
||||||
icon->setColorShift(0x777777FF);
|
row.addElement(spacer, false);
|
||||||
icon->setResize(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight() * 1.25f);
|
|
||||||
row.addElement(icon, false);
|
|
||||||
|
|
||||||
// spacer between icon and text
|
auto text = std::make_shared<TextComponent>(mWindow,
|
||||||
auto spacer = std::make_shared<GuiComponent>(mWindow);
|
GUI_INPUT_CONFIG_LIST[i].dispName, Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||||
spacer->setSize(16, 0);
|
row.addElement(text, true);
|
||||||
row.addElement(spacer, false);
|
|
||||||
|
|
||||||
auto text = std::make_shared<TextComponent>(mWindow, GUI_INPUT_CONFIG_LIST[i].dispName, Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
auto mapping = std::make_shared<TextComponent>(mWindow, "-NOT DEFINED-",
|
||||||
row.addElement(text, true);
|
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);
|
row.input_handler = [this, i, mapping](InputConfig* config, Input input) -> bool {
|
||||||
setNotDefined(mapping); // overrides text and color set above
|
// Ignore input not from our target device.
|
||||||
row.addElement(mapping, true);
|
if (config != mTargetConfig)
|
||||||
mMappings.push_back(mapping);
|
return false;
|
||||||
|
|
||||||
row.input_handler = [this, i, mapping](InputConfig* config, Input input) -> bool
|
// If we're not configuring, start configuring when A is pressed.
|
||||||
{
|
if (!mConfiguringRow) {
|
||||||
// ignore input not from our target device
|
if (config->isMappedTo("a", input) && input.value) {
|
||||||
if(config != mTargetConfig)
|
mList->stopScrolling();
|
||||||
return false;
|
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
|
// Apply filtering for quirks related to trigger mapping.
|
||||||
if(!mConfiguringRow)
|
if (filterTrigger(input, config, i))
|
||||||
{
|
return false;
|
||||||
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
|
// We are configuring.
|
||||||
return false;
|
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
|
mHoldingInput = true;
|
||||||
if(filterTrigger(input, config, i))
|
mHeldInput = input;
|
||||||
return false;
|
mHeldTime = 0;
|
||||||
|
mHeldInputId = i;
|
||||||
|
|
||||||
// we are configuring
|
return true;
|
||||||
if(input.value != 0)
|
}
|
||||||
{
|
else {
|
||||||
// input down
|
// Input up.
|
||||||
// if we're already holding something, ignore this, otherwise plan to map this input
|
// Make sure we were holding something and we let go of what we
|
||||||
if(mHoldingInput)
|
// were previously holding.
|
||||||
return true;
|
if (!mHoldingInput || mHeldInput.device != input.device || mHeldInput.id !=
|
||||||
|
input.id || mHeldInput.type != input.type)
|
||||||
|
return true;
|
||||||
|
|
||||||
mHoldingInput = true;
|
mHoldingInput = false;
|
||||||
mHeldInput = input;
|
|
||||||
mHeldTime = 0;
|
|
||||||
mHeldInputId = i;
|
|
||||||
|
|
||||||
return true;
|
if (assign(mHeldInput, i))
|
||||||
}else{
|
// If successful, move cursor/stop configuring - if not,
|
||||||
// input up
|
// we'll just try again.
|
||||||
// make sure we were holding something and we let go of what we were previously holding
|
rowDone();
|
||||||
if(!mHoldingInput || mHeldInput.device != input.device || mHeldInput.id != input.id || mHeldInput.type != input.type)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
mHoldingInput = false;
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mList->addRow(row);
|
||||||
|
}
|
||||||
|
|
||||||
if(assign(mHeldInput, i))
|
// Only show "HOLD TO SKIP" if this input is skippable.
|
||||||
rowDone(); // if successful, move cursor/stop configuring - if not, we'll just try again
|
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);
|
// Buttons.
|
||||||
}
|
std::vector< std::shared_ptr<ButtonComponent> > buttons;
|
||||||
|
std::function<void()> okFunction = [this, okCallback] {
|
||||||
// only show "HOLD TO SKIP" if this input is skippable
|
InputManager::getInstance()->writeDeviceConfig(mTargetConfig); // save
|
||||||
mList->setCursorChangedCallback([this](CursorState /*state*/) {
|
if (okCallback)
|
||||||
bool skippable = GUI_INPUT_CONFIG_LIST[mList->getCursorId()].skippable;
|
okCallback();
|
||||||
mSubtitle2->setOpacity(skippable * 255);
|
delete this;
|
||||||
});
|
};
|
||||||
|
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "OK", "ok", [this, okFunction] {
|
||||||
// make the first one say "PRESS ANYTHING" if we're re-configuring everything
|
// Check if the hotkey enable button is set. if not prompt the
|
||||||
if(mConfiguringAll)
|
// user to use select or nothing.
|
||||||
setPress(mMappings.front());
|
Input input;
|
||||||
|
okFunction();
|
||||||
// buttons
|
// Temporarily commented out, needs to be properly cleaned up later.
|
||||||
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.
|
|
||||||
// if (!mTargetConfig->getInputByName("HotKeyEnable", &input)) {
|
// if (!mTargetConfig->getInputByName("HotKeyEnable", &input)) {
|
||||||
// mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
// 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] {
|
// "YES", [this, okFunction] {
|
||||||
// Input input;
|
// Input input;
|
||||||
// mTargetConfig->getInputByName("Select", &input);
|
// mTargetConfig->getInputByName("Select", &input);
|
||||||
|
@ -204,184 +225,186 @@ GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target, bool reconfi
|
||||||
// "NO", [this, okFunction] {
|
// "NO", [this, okFunction] {
|
||||||
// // for a disabled hotkey enable button, set to a key with id 0,
|
// // for a disabled hotkey enable button, set to a key with id 0,
|
||||||
// // so the input configuration script can be backwards compatible.
|
// // 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();
|
// okFunction();
|
||||||
// }
|
// }
|
||||||
// ));
|
// ));
|
||||||
// } else {
|
// }
|
||||||
|
// else {
|
||||||
// okFunction();
|
// okFunction();
|
||||||
// }
|
// }
|
||||||
}));
|
}));
|
||||||
mButtonGrid = makeButtonGrid(mWindow, buttons);
|
|
||||||
mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false);
|
|
||||||
|
|
||||||
setSize(Renderer::getScreenWidth() * 0.6f, Renderer::getScreenHeight() * 0.75f);
|
mButtonGrid = makeButtonGrid(mWindow, buttons);
|
||||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2);
|
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()
|
void GuiInputConfig::onSizeChanged()
|
||||||
{
|
{
|
||||||
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
||||||
|
|
||||||
// update grid
|
// Update grid.
|
||||||
mGrid.setSize(mSize);
|
mGrid.setSize(mSize);
|
||||||
|
|
||||||
//mGrid.setRowHeightPerc(0, 0.025f);
|
//mGrid.setRowHeightPerc(0, 0.025f);
|
||||||
mGrid.setRowHeightPerc(1, mTitle->getFont()->getHeight()*0.75f / mSize.y());
|
mGrid.setRowHeightPerc(1, mTitle->getFont()->getHeight()*0.75f / mSize.y());
|
||||||
mGrid.setRowHeightPerc(2, mSubtitle1->getFont()->getHeight() / mSize.y());
|
mGrid.setRowHeightPerc(2, mSubtitle1->getFont()->getHeight() / mSize.y());
|
||||||
mGrid.setRowHeightPerc(3, mSubtitle2->getFont()->getHeight() / mSize.y());
|
mGrid.setRowHeightPerc(3, mSubtitle2->getFont()->getHeight() / mSize.y());
|
||||||
//mGrid.setRowHeightPerc(4, 0.03f);
|
//mGrid.setRowHeightPerc(4, 0.03f);
|
||||||
mGrid.setRowHeightPerc(5, (mList->getRowHeight(0) * 5 + 2) / mSize.y());
|
mGrid.setRowHeightPerc(5, (mList->getRowHeight(0) * 5 + 2) / mSize.y());
|
||||||
mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y() / mSize.y());
|
mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y() / mSize.y());
|
||||||
|
|
||||||
mBusyAnim.setSize(mSize);
|
mBusyAnim.setSize(mSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiInputConfig::update(int deltaTime)
|
void GuiInputConfig::update(int deltaTime)
|
||||||
{
|
{
|
||||||
if(mConfiguringRow && mHoldingInput && GUI_INPUT_CONFIG_LIST[mHeldInputId].skippable)
|
if (mConfiguringRow && mHoldingInput && GUI_INPUT_CONFIG_LIST[mHeldInputId].skippable) {
|
||||||
{
|
int prevSec = mHeldTime / 1000;
|
||||||
int prevSec = mHeldTime / 1000;
|
mHeldTime += deltaTime;
|
||||||
mHeldTime += deltaTime;
|
int curSec = mHeldTime / 1000;
|
||||||
int curSec = mHeldTime / 1000;
|
|
||||||
|
|
||||||
if(mHeldTime >= HOLD_TO_SKIP_MS)
|
if (mHeldTime >= HOLD_TO_SKIP_MS) {
|
||||||
{
|
setNotDefined(mMappings.at(mHeldInputId));
|
||||||
setNotDefined(mMappings.at(mHeldInputId));
|
clearAssignment(mHeldInputId);
|
||||||
clearAssignment(mHeldInputId);
|
mHoldingInput = false;
|
||||||
mHoldingInput = false;
|
rowDone();
|
||||||
rowDone();
|
}
|
||||||
}else{
|
else {
|
||||||
if(prevSec != curSec)
|
if (prevSec != curSec) {
|
||||||
{
|
// Crossed the second boundary, update text.
|
||||||
// crossed the second boundary, update text
|
const auto& text = mMappings.at(mHeldInputId);
|
||||||
const auto& text = mMappings.at(mHeldInputId);
|
std::stringstream ss;
|
||||||
std::stringstream ss;
|
ss << "HOLD FOR " << HOLD_TO_SKIP_MS/1000 - curSec << "S TO SKIP";
|
||||||
ss << "HOLD FOR " << HOLD_TO_SKIP_MS/1000 - curSec << "S TO SKIP";
|
text->setText(ss.str());
|
||||||
text->setText(ss.str());
|
text->setColor(0x777777FF);
|
||||||
text->setColor(0x777777FF);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// move cursor to the next thing if we're configuring all,
|
// Move cursor to the next thing if we're configuring all,
|
||||||
// or come out of "configure mode" if we were only configuring one row
|
// or come out of "configure mode" if we were only configuring one row.
|
||||||
void GuiInputConfig::rowDone()
|
void GuiInputConfig::rowDone()
|
||||||
{
|
{
|
||||||
if(mConfiguringAll)
|
if (mConfiguringAll) {
|
||||||
{
|
// Try to move to the next one.
|
||||||
if(!mList->moveCursor(1)) // try to move to the next one
|
if (!mList->moveCursor(1)) {
|
||||||
{
|
// At bottom of list, we're done.
|
||||||
// at bottom of list, done
|
mConfiguringAll = false;
|
||||||
mConfiguringAll = false;
|
mConfiguringRow = false;
|
||||||
mConfiguringRow = false;
|
mGrid.moveCursor(Vector2i(0, 1));
|
||||||
mGrid.moveCursor(Vector2i(0, 1));
|
}
|
||||||
}else{
|
else {
|
||||||
// on another one
|
// On another one.
|
||||||
setPress(mMappings.at(mList->getCursorId()));
|
setPress(mMappings.at(mList->getCursorId()));
|
||||||
}
|
}
|
||||||
}else{
|
}
|
||||||
// only configuring one row, so stop
|
else {
|
||||||
mConfiguringRow = false;
|
// Only configuring one row, so stop.
|
||||||
}
|
mConfiguringRow = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiInputConfig::setPress(const std::shared_ptr<TextComponent>& text)
|
void GuiInputConfig::setPress(const std::shared_ptr<TextComponent>& text)
|
||||||
{
|
{
|
||||||
text->setText("PRESS ANYTHING");
|
text->setText("PRESS ANYTHING");
|
||||||
text->setColor(0x656565FF);
|
text->setColor(0x656565FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiInputConfig::setNotDefined(const std::shared_ptr<TextComponent>& text)
|
void GuiInputConfig::setNotDefined(const std::shared_ptr<TextComponent>& text)
|
||||||
{
|
{
|
||||||
text->setText("-NOT DEFINED-");
|
text->setText("-NOT DEFINED-");
|
||||||
text->setColor(0x999999FF);
|
text->setColor(0x999999FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiInputConfig::setAssignedTo(const std::shared_ptr<TextComponent>& text, Input input)
|
void GuiInputConfig::setAssignedTo(const std::shared_ptr<TextComponent>& text, Input input)
|
||||||
{
|
{
|
||||||
text->setText(Utils::String::toUpper(input.string()));
|
text->setText(Utils::String::toUpper(input.string()));
|
||||||
text->setColor(0x777777FF);
|
text->setColor(0x777777FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiInputConfig::error(const std::shared_ptr<TextComponent>& text, const std::string& /*msg*/)
|
void GuiInputConfig::error(const std::shared_ptr<TextComponent>& text, const std::string& /*msg*/)
|
||||||
{
|
{
|
||||||
text->setText("ALREADY TAKEN");
|
text->setText("ALREADY TAKEN");
|
||||||
text->setColor(0x656565FF);
|
text->setColor(0x656565FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GuiInputConfig::assign(Input input, int inputId)
|
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 this input is mapped to something other than "nothing" or the current row,
|
||||||
// (if it's the same as what it was before, allow it)
|
// generate an error.
|
||||||
if(mTargetConfig->getMappedTo(input).size() > 0 && !mTargetConfig->isMappedTo(GUI_INPUT_CONFIG_LIST[inputId].name, input) && strcmp(GUI_INPUT_CONFIG_LIST[inputId].name, "HotKeyEnable") != 0)
|
// (If it's the same as what it was before, allow it.)
|
||||||
{
|
if (mTargetConfig->getMappedTo(input).size() > 0 &&
|
||||||
error(mMappings.at(inputId), "Already mapped!");
|
!mTargetConfig->isMappedTo(GUI_INPUT_CONFIG_LIST[inputId].name, input) &&
|
||||||
return false;
|
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;
|
input.configured = true;
|
||||||
mTargetConfig->mapInput(GUI_INPUT_CONFIG_LIST[inputId].name, input);
|
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)
|
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)
|
bool GuiInputConfig::filterTrigger(Input input, InputConfig* config, int inputId)
|
||||||
{
|
{
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
// on Linux, some gamepads return both an analog axis and a digital button for the trigger;
|
// 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
|
// we want the analog axis only, so this function removes the button press event.
|
||||||
|
|
||||||
if((
|
if ((
|
||||||
// match PlayStation joystick with 6 axes only
|
// Match PlayStation joystick with 6 axes only.
|
||||||
strstr(config->getDeviceName().c_str(), "PLAYSTATION") != NULL
|
strstr(config->getDeviceName().c_str(), "PLAYSTATION") != nullptr ||
|
||||||
|| strstr(config->getDeviceName().c_str(), "PS3 Ga") != NULL
|
strstr(config->getDeviceName().c_str(), "PS3 Ga") != nullptr ||
|
||||||
|| strstr(config->getDeviceName().c_str(), "PS(R) Ga") != NULL
|
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
|
// BigBen kid's PS3 gamepad 146b:0902, matched on SDL GUID because its name "Bigben
|
||||||
|| strcmp(config->getDeviceGUIDString().c_str(), "030000006b1400000209000011010000") == 0
|
// Interactive Bigben Game Pad" may be too generic.
|
||||||
) && InputManager::getInstance()->getAxisCountByDevice(config->getDeviceId()) == 6)
|
strcmp(config->getDeviceGUIDString().c_str(), "030000006b1400000209000011010000") == 0)
|
||||||
{
|
&& InputManager::getInstance()->getAxisCountByDevice(config->getDeviceId()) == 6) {
|
||||||
// digital triggers are unwanted
|
// Digital triggers are unwanted.
|
||||||
if(input.type == TYPE_BUTTON && (input.id == 6 || input.id == 7))
|
if (input.type == TYPE_BUTTON && (input.id == 6 || input.id == 7)) {
|
||||||
{
|
mHoldingInput = false;
|
||||||
mHoldingInput = false;
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ignore negative pole for axes 2/5 only when triggers are being configured
|
// 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 (input.type == TYPE_AXIS && (input.id == 2 || input.id == 5)) {
|
||||||
{
|
if (strstr(GUI_INPUT_CONFIG_LIST[inputId].name, "Trigger") != nullptr) {
|
||||||
if(strstr(GUI_INPUT_CONFIG_LIST[inputId].name, "Trigger") != NULL)
|
if (input.value == 1)
|
||||||
{
|
mSkipAxis = true;
|
||||||
if(input.value == 1)
|
else if (input.value == -1)
|
||||||
mSkipAxis = true;
|
return true;
|
||||||
else if(input.value == -1)
|
}
|
||||||
return true;
|
else if (mSkipAxis) {
|
||||||
}
|
mSkipAxis = false;
|
||||||
else if(mSkipAxis)
|
return true;
|
||||||
{
|
}
|
||||||
mSkipAxis = false;
|
}
|
||||||
return true;
|
#else
|
||||||
}
|
(void)input;
|
||||||
}
|
(void)config;
|
||||||
#else
|
(void)inputId;
|
||||||
(void)input;
|
#endif
|
||||||
(void)config;
|
|
||||||
(void)inputId;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// GuiInputConfig.h
|
||||||
|
//
|
||||||
|
// Input device configuration GUI (for keyboards, joysticks and gamepads).
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_CORE_GUIS_GUI_INPUT_CONFIG_H
|
#ifndef ES_CORE_GUIS_GUI_INPUT_CONFIG_H
|
||||||
#define 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
|
class GuiInputConfig : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
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 update(int deltaTime) override;
|
||||||
|
void onSizeChanged() override;
|
||||||
void onSizeChanged() override;
|
|
||||||
|
|
||||||
private:
|
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
|
// Set text to "PRESS ANYTHING" + not greyed out.
|
||||||
void setNotDefined(const std::shared_ptr<TextComponent>& text); // set text to -NOT DEFINED- + greyed out
|
void setPress(const std::shared_ptr<TextComponent>& text);
|
||||||
void setAssignedTo(const std::shared_ptr<TextComponent>& text, Input input); // set text to "BUTTON 2"/"AXIS 2+", etc.
|
// 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);
|
bool assign(Input input, int inputId);
|
||||||
void clearAssignment(int inputId);
|
void clearAssignment(int inputId);
|
||||||
bool filterTrigger(Input input, InputConfig* config, int inputId);
|
bool filterTrigger(Input input, InputConfig* config, int inputId);
|
||||||
|
|
||||||
void rowDone();
|
void rowDone();
|
||||||
|
|
||||||
NinePatchComponent mBackground;
|
NinePatchComponent mBackground;
|
||||||
ComponentGrid mGrid;
|
ComponentGrid mGrid;
|
||||||
|
|
||||||
std::shared_ptr<TextComponent> mTitle;
|
std::shared_ptr<TextComponent> mTitle;
|
||||||
std::shared_ptr<TextComponent> mSubtitle1;
|
std::shared_ptr<TextComponent> mSubtitle1;
|
||||||
std::shared_ptr<TextComponent> mSubtitle2;
|
std::shared_ptr<TextComponent> mSubtitle2;
|
||||||
std::shared_ptr<ComponentList> mList;
|
std::shared_ptr<ComponentList> mList;
|
||||||
std::vector< std::shared_ptr<TextComponent> > mMappings;
|
std::vector< std::shared_ptr<TextComponent> > mMappings;
|
||||||
std::shared_ptr<ComponentGrid> mButtonGrid;
|
std::shared_ptr<ComponentGrid> mButtonGrid;
|
||||||
|
|
||||||
InputConfig* mTargetConfig;
|
InputConfig* mTargetConfig;
|
||||||
bool mConfiguringRow; // next input captured by mList will be interpretted as a remap
|
// 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
|
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;
|
bool mHoldingInput;
|
||||||
Input mHeldInput;
|
Input mHeldInput;
|
||||||
int mHeldTime;
|
int mHeldTime;
|
||||||
int mHeldInputId;
|
int mHeldInputId;
|
||||||
bool mSkipAxis;
|
bool mSkipAxis;
|
||||||
|
|
||||||
BusyComponent mBusyAnim;
|
BusyComponent mBusyAnim;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_GUIS_GUI_INPUT_CONFIG_H
|
#endif // ES_CORE_GUIS_GUI_INPUT_CONFIG_H
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// FileSystemUtil.cpp
|
// FileSystemUtil.cpp
|
||||||
//
|
//
|
||||||
// Low-level filesystem functions.
|
// Low-level filesystem functions.
|
||||||
|
// Resolve relative paths, resolve symlinks, create directories,
|
||||||
|
// remove files etc.
|
||||||
//
|
//
|
||||||
|
|
||||||
#define _FILE_OFFSET_BITS 64
|
#define _FILE_OFFSET_BITS 64
|
||||||
|
@ -100,10 +102,10 @@ namespace Utils
|
||||||
#else
|
#else
|
||||||
DIR* dir = opendir(path.c_str());
|
DIR* dir = opendir(path.c_str());
|
||||||
|
|
||||||
if (dir != NULL) {
|
if (dir != nullptr) {
|
||||||
struct dirent* entry;
|
struct dirent* entry;
|
||||||
// Loop over all files in the directory.
|
// Loop over all files in the directory.
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
while ((entry = readdir(dir)) != nullptr) {
|
||||||
std::string name(entry->d_name);
|
std::string name(entry->d_name);
|
||||||
|
|
||||||
// Ignore "." and ".."
|
// Ignore "." and ".."
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// FileSystemUtil.h
|
// FileSystemUtil.h
|
||||||
//
|
//
|
||||||
// Low-level filesystem functions.
|
// Low-level filesystem functions.
|
||||||
|
// Resolve relative paths, resolve symlinks, create directories,
|
||||||
|
// remove files etc.
|
||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -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 "utils/StringUtil.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -5,300 +12,276 @@
|
||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
{
|
{
|
||||||
namespace String
|
namespace String
|
||||||
{
|
{
|
||||||
unsigned int chars2Unicode(const std::string& _string, size_t& _cursor)
|
unsigned int chars2Unicode(const std::string& _string, size_t& _cursor)
|
||||||
{
|
{
|
||||||
const char& c = _string[_cursor];
|
const char& c = _string[_cursor];
|
||||||
unsigned int result = '?';
|
unsigned int result = '?';
|
||||||
|
|
||||||
if((c & 0x80) == 0) // 0xxxxxxx, one byte character
|
// 0xxxxxxx, one byte character.
|
||||||
{
|
if ((c & 0x80) == 0) {
|
||||||
// 0xxxxxxx
|
// 0xxxxxxx
|
||||||
result = ((_string[_cursor++] ) );
|
result = ((_string[_cursor++] ) );
|
||||||
}
|
}
|
||||||
else if((c & 0xE0) == 0xC0) // 110xxxxx, two byte character
|
// 110xxxxx, two byte character.
|
||||||
{
|
else if ((c & 0xE0) == 0xC0) {
|
||||||
// 110xxxxx 10xxxxxx
|
// 110xxxxx 10xxxxxx
|
||||||
result = ((_string[_cursor++] & 0x1F) << 6) |
|
result = ((_string[_cursor++] & 0x1F) << 6) |
|
||||||
((_string[_cursor++] & 0x3F) );
|
((_string[_cursor++] & 0x3F) );
|
||||||
}
|
}
|
||||||
else if((c & 0xF0) == 0xE0) // 1110xxxx, three byte character
|
// 1110xxxx, three byte character.
|
||||||
{
|
else if ((c & 0xF0) == 0xE0) {
|
||||||
// 1110xxxx 10xxxxxx 10xxxxxx
|
// 1110xxxx 10xxxxxx 10xxxxxx
|
||||||
result = ((_string[_cursor++] & 0x0F) << 12) |
|
result = ((_string[_cursor++] & 0x0F) << 12) |
|
||||||
((_string[_cursor++] & 0x3F) << 6) |
|
((_string[_cursor++] & 0x3F) << 6) |
|
||||||
((_string[_cursor++] & 0x3F) );
|
((_string[_cursor++] & 0x3F) );
|
||||||
}
|
}
|
||||||
else if((c & 0xF8) == 0xF0) // 11110xxx, four byte character
|
// 11110xxx, four byte character.
|
||||||
{
|
else if ((c & 0xF8) == 0xF0) {
|
||||||
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||||
result = ((_string[_cursor++] & 0x07) << 18) |
|
result = ((_string[_cursor++] & 0x07) << 18) |
|
||||||
((_string[_cursor++] & 0x3F) << 12) |
|
((_string[_cursor++] & 0x3F) << 12) |
|
||||||
((_string[_cursor++] & 0x3F) << 6) |
|
((_string[_cursor++] & 0x3F) << 6) |
|
||||||
((_string[_cursor++] & 0x3F) );
|
((_string[_cursor++] & 0x3F) );
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
// Error, invalid unicode.
|
||||||
// error, invalid unicode
|
++_cursor;
|
||||||
++_cursor;
|
}
|
||||||
}
|
|
||||||
|
return result;
|
||||||
return result;
|
}
|
||||||
|
|
||||||
} // chars2Unicode
|
std::string unicode2Chars(const unsigned int _unicode)
|
||||||
|
{
|
||||||
std::string unicode2Chars(const unsigned int _unicode)
|
std::string result;
|
||||||
{
|
|
||||||
std::string result;
|
// One byte character.
|
||||||
|
if (_unicode < 0x80) {
|
||||||
if(_unicode < 0x80) // one byte character
|
result += ((_unicode ) & 0xFF);
|
||||||
{
|
}
|
||||||
result += ((_unicode ) & 0xFF);
|
// Two byte character.
|
||||||
}
|
else if (_unicode < 0x800) {
|
||||||
else if(_unicode < 0x800) // two byte character
|
result += ((_unicode >> 6) & 0xFF) | 0xC0;
|
||||||
{
|
result += ((_unicode ) & 0x3F) | 0x80;
|
||||||
result += ((_unicode >> 6) & 0xFF) | 0xC0;
|
}
|
||||||
result += ((_unicode ) & 0x3F) | 0x80;
|
// Three byte character.
|
||||||
}
|
else if (_unicode < 0xFFFF) {
|
||||||
else if(_unicode < 0xFFFF) // three byte character
|
result += ((_unicode >> 12) & 0xFF) | 0xE0;
|
||||||
{
|
result += ((_unicode >> 6) & 0x3F) | 0x80;
|
||||||
result += ((_unicode >> 12) & 0xFF) | 0xE0;
|
result += ((_unicode ) & 0x3F) | 0x80;
|
||||||
result += ((_unicode >> 6) & 0x3F) | 0x80;
|
}
|
||||||
result += ((_unicode ) & 0x3F) | 0x80;
|
// Four byte character.
|
||||||
}
|
else if (_unicode <= 0x1fffff) {
|
||||||
else if(_unicode <= 0x1fffff) // four byte character
|
result += ((_unicode >> 18) & 0xFF) | 0xF0;
|
||||||
{
|
result += ((_unicode >> 12) & 0x3F) | 0x80;
|
||||||
result += ((_unicode >> 18) & 0xFF) | 0xF0;
|
result += ((_unicode >> 6) & 0x3F) | 0x80;
|
||||||
result += ((_unicode >> 12) & 0x3F) | 0x80;
|
result += ((_unicode ) & 0x3F) | 0x80;
|
||||||
result += ((_unicode >> 6) & 0x3F) | 0x80;
|
}
|
||||||
result += ((_unicode ) & 0x3F) | 0x80;
|
else {
|
||||||
}
|
// Error, invalid unicode.
|
||||||
else
|
result += '?';
|
||||||
{
|
}
|
||||||
// error, invalid unicode
|
|
||||||
result += '?';
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
size_t nextCursor(const std::string& _string, const size_t _cursor)
|
||||||
|
{
|
||||||
} // unicode2Chars
|
size_t result = _cursor;
|
||||||
|
|
||||||
size_t nextCursor(const std::string& _string, const size_t _cursor)
|
while (result < _string.length()) {
|
||||||
{
|
++result;
|
||||||
size_t result = _cursor;
|
|
||||||
|
// Break if current character is not 10xxxxxx
|
||||||
while(result < _string.length())
|
if ((_string[result] & 0xC0) != 0x80)
|
||||||
{
|
break;
|
||||||
++result;
|
}
|
||||||
|
|
||||||
if((_string[result] & 0xC0) != 0x80) // break if current character is not 10xxxxxx
|
return result;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
size_t prevCursor(const std::string& _string, const size_t _cursor)
|
||||||
return result;
|
{
|
||||||
|
size_t result = _cursor;
|
||||||
} // nextCursor
|
|
||||||
|
while (result > 0) {
|
||||||
size_t prevCursor(const std::string& _string, const size_t _cursor)
|
--result;
|
||||||
{
|
|
||||||
size_t result = _cursor;
|
// Break if current character is not 10xxxxxx
|
||||||
|
if ((_string[result] & 0xC0) != 0x80)
|
||||||
while(result > 0)
|
break;
|
||||||
{
|
}
|
||||||
--result;
|
|
||||||
|
return result;
|
||||||
if((_string[result] & 0xC0) != 0x80) // break if current character is not 10xxxxxx
|
}
|
||||||
break;
|
|
||||||
}
|
size_t moveCursor(const std::string& _string, const size_t _cursor, const int _amount)
|
||||||
|
{
|
||||||
return result;
|
size_t result = _cursor;
|
||||||
|
|
||||||
} // prevCursor
|
if (_amount > 0) {
|
||||||
|
for (int i = 0; i < _amount; ++i)
|
||||||
size_t moveCursor(const std::string& _string, const size_t _cursor, const int _amount)
|
result = nextCursor(_string, result);
|
||||||
{
|
}
|
||||||
size_t result = _cursor;
|
else if (_amount < 0) {
|
||||||
|
for (int i = _amount; i < 0; ++i)
|
||||||
if(_amount > 0)
|
result = prevCursor(_string, result);
|
||||||
{
|
}
|
||||||
for(int i = 0; i < _amount; ++i)
|
|
||||||
result = nextCursor(_string, result);
|
return result;
|
||||||
}
|
}
|
||||||
else if(_amount < 0)
|
|
||||||
{
|
std::string toLower(const std::string& _string)
|
||||||
for(int i = _amount; i < 0; ++i)
|
{
|
||||||
result = prevCursor(_string, result);
|
std::string string;
|
||||||
}
|
|
||||||
|
for (size_t i = 0; i < _string.length(); ++i)
|
||||||
return result;
|
string += (char)tolower(_string[i]);
|
||||||
|
|
||||||
} // moveCursor
|
return string;
|
||||||
|
}
|
||||||
std::string toLower(const std::string& _string)
|
|
||||||
{
|
std::string toUpper(const std::string& _string)
|
||||||
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)
|
bool endsWith(const std::string& _string, const std::string& _end)
|
||||||
string += (char)tolower(_string[i]);
|
{
|
||||||
|
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)
|
for (size_t i = 0; i < sizeof(remove); i += 2) {
|
||||||
{
|
end = string.find_first_of(remove[i + 1]);
|
||||||
std::string string;
|
start = string.find_last_of( remove[i + 0], end);
|
||||||
|
|
||||||
for(size_t i = 0; i < _string.length(); ++i)
|
if ((start != std::string::npos) && (end != std::string::npos)) {
|
||||||
string += (char)toupper(_string[i]);
|
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)
|
while (comma != std::string::npos) {
|
||||||
{
|
vector.push_back(_string.substr(start, comma - start));
|
||||||
const size_t strBegin = _string.find_first_not_of(" \t");
|
start = comma + 1;
|
||||||
const size_t strEnd = _string.find_last_not_of(" \t");
|
comma = _string.find(_delimiter, start);
|
||||||
|
}
|
||||||
|
|
||||||
if(strBegin == std::string::npos)
|
vector.push_back(_string.substr(start));
|
||||||
return "";
|
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 vectorToCommaString(stringVector _vector)
|
||||||
{
|
{
|
||||||
std::string string = _string;
|
std::string string;
|
||||||
size_t pos;
|
|
||||||
|
|
||||||
while((pos = string.find(_replace)) != std::string::npos)
|
std::sort(_vector.begin(), _vector.end());
|
||||||
string = string.replace(pos, _replace.length(), _with.c_str(), _with.length());
|
|
||||||
|
|
||||||
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)
|
std::string format(const char* _format, ...)
|
||||||
{
|
{
|
||||||
return (_string.find(_start) == 0);
|
va_list args;
|
||||||
|
va_list copy;
|
||||||
|
|
||||||
} // startsWith
|
va_start(args, _format);
|
||||||
|
|
||||||
bool endsWith(const std::string& _string, const std::string& _end)
|
va_copy(copy, args);
|
||||||
{
|
const int length = vsnprintf(nullptr, 0, _format, copy);
|
||||||
return (_string.find(_end) == (_string.size() - _end.size()));
|
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)
|
va_end(args);
|
||||||
{
|
|
||||||
static const char remove[4] = { '(', ')', '[', ']' };
|
|
||||||
std::string string = _string;
|
|
||||||
size_t start;
|
|
||||||
size_t end;
|
|
||||||
bool done = false;
|
|
||||||
|
|
||||||
while(!done)
|
std::string out(buffer);
|
||||||
{
|
delete buffer;
|
||||||
done = true;
|
|
||||||
|
|
||||||
for(size_t i = 0; i < sizeof(remove); i += 2)
|
return out;
|
||||||
{
|
}
|
||||||
end = string.find_first_of(remove[i + 1]);
|
|
||||||
start = string.find_last_of( remove[i + 0], end);
|
|
||||||
|
|
||||||
if((start != std::string::npos) && (end != std::string::npos))
|
std::string scramble(const std::string& _input, const std::string& _key)
|
||||||
{
|
{
|
||||||
string.erase(start, end - start + 1);
|
std::string buffer = _input;
|
||||||
done = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
} // String::
|
||||||
{
|
|
||||||
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::
|
|
||||||
|
|
||||||
} // Utils::
|
} // Utils::
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
//
|
||||||
|
// StringUtil.h
|
||||||
|
//
|
||||||
|
// Low-level string functions.
|
||||||
|
// Convert characters to Unicode, upper-/lowercase conversion, string formatting etc.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_CORE_UTILS_STRING_UTIL_H
|
#ifndef ES_CORE_UTILS_STRING_UTIL_H
|
||||||
#define ES_CORE_UTILS_STRING_UTIL_H
|
#define ES_CORE_UTILS_STRING_UTIL_H
|
||||||
|
@ -7,29 +14,31 @@
|
||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
{
|
{
|
||||||
namespace String
|
namespace String
|
||||||
{
|
{
|
||||||
typedef std::vector<std::string> stringVector;
|
typedef std::vector<std::string> stringVector;
|
||||||
|
|
||||||
unsigned int chars2Unicode (const std::string& _string, size_t& _cursor);
|
unsigned int chars2Unicode(const std::string& _string, size_t& _cursor);
|
||||||
std::string unicode2Chars (const unsigned int _unicode);
|
std::string unicode2Chars(const unsigned int _unicode);
|
||||||
size_t nextCursor (const std::string& _string, const size_t _cursor);
|
size_t nextCursor(const std::string& _string, const size_t _cursor);
|
||||||
size_t prevCursor (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);
|
size_t moveCursor(const std::string& _string, const size_t _cursor, const int _amount);
|
||||||
std::string toLower (const std::string& _string);
|
std::string toLower(const std::string& _string);
|
||||||
std::string toUpper (const std::string& _string);
|
std::string toUpper(const std::string& _string);
|
||||||
std::string trim (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);
|
std::string replace(const std::string& _string, const std::string& _replace,
|
||||||
bool startsWith (const std::string& _string, const std::string& _start);
|
const std::string& _with);
|
||||||
bool endsWith (const std::string& _string, const std::string& _end);
|
bool startsWith(const std::string& _string, const std::string& _start);
|
||||||
std::string removeParenthesis (const std::string& _string);
|
bool endsWith(const std::string& _string, const std::string& _end);
|
||||||
stringVector delimitedStringToVector(const std::string& _string, const std::string& _delimiter, bool sort = false);
|
std::string removeParenthesis(const std::string& _string);
|
||||||
stringVector commaStringToVector (const std::string& _string, bool sort = false);
|
stringVector delimitedStringToVector(const std::string& _string,
|
||||||
std::string vectorToCommaString (stringVector _vector);
|
const std::string& _delimiter, bool sort = false);
|
||||||
std::string format (const char* _string, ...);
|
stringVector commaStringToVector(const std::string& _string, bool sort = false);
|
||||||
std::string scramble (const std::string& _input, const std::string& key);
|
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::
|
} // Utils::
|
||||||
|
|
||||||
|
|
|
@ -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 "utils/TimeUtil.h"
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
{
|
{
|
||||||
namespace Time
|
namespace Time
|
||||||
{
|
{
|
||||||
DateTime::DateTime()
|
DateTime::DateTime()
|
||||||
{
|
{
|
||||||
mTime = 0;
|
mTime = 0;
|
||||||
mTimeStruct = { 0, 0, 0, 1, 0, 0, 0, 0, -1 };
|
mTimeStruct = { 0, 0, 0, 1, 0, 0, 0, 0, -1 };
|
||||||
mIsoString = "00000000T000000";
|
mIsoString = "00000000T000000";
|
||||||
|
}
|
||||||
|
|
||||||
} // DateTime::DateTime
|
DateTime::DateTime(const time_t& _time)
|
||||||
|
{
|
||||||
|
setTime(_time);
|
||||||
|
}
|
||||||
|
|
||||||
DateTime::DateTime(const time_t& _time)
|
DateTime::DateTime(const tm& _timeStruct)
|
||||||
{
|
{
|
||||||
setTime(_time);
|
setTimeStruct(_timeStruct);
|
||||||
|
}
|
||||||
|
|
||||||
} // DateTime::DateTime
|
DateTime::DateTime(const std::string& _isoString)
|
||||||
|
{
|
||||||
|
setIsoString(_isoString);
|
||||||
|
}
|
||||||
|
|
||||||
DateTime::DateTime(const tm& _timeStruct)
|
DateTime::~DateTime()
|
||||||
{
|
{
|
||||||
setTimeStruct(_timeStruct);
|
}
|
||||||
|
|
||||||
} // 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)
|
void DateTime::setTimeStruct(const tm& _timeStruct)
|
||||||
{
|
{
|
||||||
setIsoString(_isoString);
|
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)
|
time_t now()
|
||||||
{
|
{
|
||||||
mTime = (_time < 0) ? 0 : _time;
|
time_t time;
|
||||||
mTimeStruct = *localtime(&mTime);
|
::time(&time);
|
||||||
mIsoString = timeToString(mTime);
|
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)
|
if (_string == "not-a-date-time")
|
||||||
{
|
return mktime(&timeStruct);
|
||||||
setTime(mktime((tm*)&_timeStruct));
|
|
||||||
|
|
||||||
} // DateTime::setTimeStruct
|
while (*f && (parsedChars < _string.length())) {
|
||||||
|
if (*f == '%') {
|
||||||
|
++f;
|
||||||
|
|
||||||
void DateTime::setIsoString(const std::string& _isoString)
|
switch (*f++) {
|
||||||
{
|
// The year [1970,xxxx]
|
||||||
setTime(stringToTime(_isoString));
|
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)
|
// The day of the month [01,31]
|
||||||
{
|
case 'd': {
|
||||||
mTotalSeconds = (unsigned int)_time;
|
if ((parsedChars + 2) <= _string.length()) {
|
||||||
mDays = (mTotalSeconds - (mTotalSeconds % (60*60*24))) / (60*60*24);
|
timeStruct.tm_mday = (*s++ - '0') * 10;
|
||||||
mHours = ((mTotalSeconds % (60*60*24)) - (mTotalSeconds % (60*60))) / (60*60);
|
timeStruct.tm_mday += (*s++ - '0');
|
||||||
mMinutes = ((mTotalSeconds % (60*60)) - (mTotalSeconds % (60))) / 60;
|
}
|
||||||
mSeconds = mTotalSeconds % 60;
|
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()
|
std::string timeToString(const time_t& _time, const std::string& _format)
|
||||||
{
|
{
|
||||||
time_t time;
|
const char* f = _format.c_str();
|
||||||
::time(&time);
|
const tm timeStruct = *localtime(&_time);
|
||||||
return 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)
|
switch (*f++) {
|
||||||
{
|
// The year, including the century (1900)
|
||||||
const char* s = _string.c_str();
|
case 'Y': {
|
||||||
const char* f = _format.c_str();
|
const int year = timeStruct.tm_year + 1900;
|
||||||
tm timeStruct = { 0, 0, 0, 1, 0, 0, 0, 0, -1 };
|
*s++ = (char)((year - (year % 1000)) / 1000) + '0';
|
||||||
size_t parsedChars = 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")
|
// The month number [00,11]
|
||||||
return mktime(&timeStruct);
|
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()))
|
// The day of the month [01,31]
|
||||||
{
|
case 'd': {
|
||||||
if(*f == '%')
|
*s++ = (char)(timeStruct.tm_mday / 10) + '0';
|
||||||
{
|
*s++ = (char)(timeStruct.tm_mday % 10) + '0';
|
||||||
++f;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
switch(*f++)
|
// The hour (24-hour clock) [00,23]
|
||||||
{
|
case 'H': {
|
||||||
case 'Y': // The year [1970,xxxx]
|
*s++ = (char)(timeStruct.tm_hour / 10) + '0';
|
||||||
{
|
*s++ = (char)(timeStruct.tm_hour % 10) + '0';
|
||||||
if((parsedChars + 4) <= _string.length())
|
}
|
||||||
{
|
break;
|
||||||
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;
|
// The minute [00,59]
|
||||||
}
|
case 'M': {
|
||||||
break;
|
*s++ = (char)(timeStruct.tm_min / 10) + '0';
|
||||||
|
*s++ = (char)(timeStruct.tm_min % 10) + '0';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'm': // The month number [01,12]
|
// The second [00,59]
|
||||||
{
|
case 'S': {
|
||||||
if((parsedChars + 2) <= _string.length())
|
*s++ = (char)(timeStruct.tm_sec / 10) + '0';
|
||||||
{
|
*s++ = (char)(timeStruct.tm_sec % 10) + '0';
|
||||||
timeStruct.tm_mon = (*s++ - '0') * 10;
|
}
|
||||||
timeStruct.tm_mon += (*s++ - '0');
|
break;
|
||||||
if(timeStruct.tm_mon >= 1)
|
}
|
||||||
timeStruct.tm_mon -= 1;
|
}
|
||||||
}
|
else {
|
||||||
|
*s++ = *f++;
|
||||||
|
}
|
||||||
|
*s = '\0';
|
||||||
|
}
|
||||||
|
return std::string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
parsedChars += 2;
|
int daysInMonth(const int _year, const int _month)
|
||||||
}
|
{
|
||||||
break;
|
tm timeStruct = { 0, 0, 0, 0, _month, _year - 1900, 0, 0, -1 };
|
||||||
|
mktime(&timeStruct);
|
||||||
|
|
||||||
case 'd': // The day of the month [01,31]
|
return timeStruct.tm_mday;
|
||||||
{
|
}
|
||||||
if((parsedChars + 2) <= _string.length())
|
|
||||||
{
|
|
||||||
timeStruct.tm_mday = (*s++ - '0') * 10;
|
|
||||||
timeStruct.tm_mday += (*s++ - '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedChars += 2;
|
int daysInYear(const int _year)
|
||||||
}
|
{
|
||||||
break;
|
tm timeStruct = { 0, 0, 0, 0, 0, _year - 1900 + 1, 0, 0, -1 };
|
||||||
|
mktime(&timeStruct);
|
||||||
|
|
||||||
case 'H': // The hour (24-hour clock) [00,23]
|
return timeStruct.tm_yday + 1;
|
||||||
{
|
}
|
||||||
if((parsedChars + 2) <= _string.length())
|
|
||||||
{
|
|
||||||
timeStruct.tm_hour = (*s++ - '0') * 10;
|
|
||||||
timeStruct.tm_hour += (*s++ - '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedChars += 2;
|
} // Time::
|
||||||
}
|
|
||||||
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::
|
|
||||||
|
|
||||||
} // Utils::
|
} // Utils::
|
||||||
|
|
|
@ -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
|
#pragma once
|
||||||
#ifndef ES_CORE_UTILS_TIME_UTIL_H
|
#ifndef ES_CORE_UTILS_TIME_UTIL_H
|
||||||
#define ES_CORE_UTILS_TIME_UTIL_H
|
#define ES_CORE_UTILS_TIME_UTIL_H
|
||||||
|
@ -6,73 +13,67 @@
|
||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
{
|
{
|
||||||
namespace Time
|
namespace Time
|
||||||
{
|
{
|
||||||
static int NOT_A_DATE_TIME = 0;
|
static int NOT_A_DATE_TIME = 0;
|
||||||
|
|
||||||
class DateTime
|
class DateTime
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
DateTime();
|
||||||
|
DateTime(const time_t& _time);
|
||||||
|
DateTime(const tm& _timeStruct);
|
||||||
|
DateTime(const std::string& _isoString);
|
||||||
|
~DateTime();
|
||||||
|
|
||||||
DateTime();
|
const bool operator<(const DateTime& _other) const { return (mTime < _other.mTime); }
|
||||||
DateTime(const time_t& _time);
|
const bool operator<=(const DateTime& _other) const { return (mTime <= _other.mTime); }
|
||||||
DateTime(const tm& _timeStruct);
|
const bool operator>(const DateTime& _other) const { return (mTime > _other.mTime); }
|
||||||
DateTime(const std::string& _isoString);
|
const bool operator>=(const DateTime& _other) const { return (mTime >= _other.mTime); }
|
||||||
~DateTime();
|
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); }
|
void setTime(const time_t& _time);
|
||||||
const bool operator<= (const DateTime& _other) const { return (mTime <= _other.mTime); }
|
const time_t& getTime() const { return mTime; }
|
||||||
const bool operator> (const DateTime& _other) const { return (mTime > _other.mTime); }
|
void setTimeStruct(const tm& _timeStruct);
|
||||||
const bool operator>= (const DateTime& _other) const { return (mTime >= _other.mTime); }
|
const tm& getTimeStruct() const { return mTimeStruct; }
|
||||||
operator time_t () const { return mTime; }
|
void setIsoString (const std::string& _isoString);
|
||||||
operator tm () const { return mTimeStruct; }
|
const std::string& getIsoString () const { return mIsoString; }
|
||||||
operator std::string() const { return mIsoString; }
|
|
||||||
|
|
||||||
void setTime (const time_t& _time);
|
private:
|
||||||
const time_t& getTime () const { return mTime; }
|
time_t mTime;
|
||||||
void setTimeStruct(const tm& _timeStruct);
|
tm mTimeStruct;
|
||||||
const tm& getTimeStruct() const { return mTimeStruct; }
|
std::string mIsoString;
|
||||||
void setIsoString (const std::string& _isoString);
|
};
|
||||||
const std::string& getIsoString () const { return mIsoString; }
|
|
||||||
|
|
||||||
private:
|
class Duration
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Duration(const time_t& _time);
|
||||||
|
~Duration();
|
||||||
|
|
||||||
time_t mTime;
|
unsigned int getDays() const { return mDays; }
|
||||||
tm mTimeStruct;
|
unsigned int getHours() const { return mHours; }
|
||||||
std::string mIsoString;
|
unsigned int getMinutes() const { return mMinutes; }
|
||||||
|
unsigned int getSeconds() const { return mSeconds; }
|
||||||
|
|
||||||
}; // DateTime
|
private:
|
||||||
|
unsigned int mTotalSeconds;
|
||||||
class Duration
|
unsigned int mDays;
|
||||||
{
|
unsigned int mHours;
|
||||||
public:
|
unsigned int mMinutes;
|
||||||
|
unsigned int mSeconds;
|
||||||
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::
|
|
||||||
|
|
||||||
|
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::
|
} // Utils::
|
||||||
|
|
||||||
#endif // ES_CORE_UTILS_TIME_UTIL_H
|
#endif // ES_CORE_UTILS_TIME_UTIL_H
|
||||||
|
|
Loading…
Reference in a new issue