ES-DE/es-app/src/SystemScreensaver.cpp

441 lines
14 KiB
C++
Raw Normal View History

// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// SystemScreensaver.cpp
//
2020-11-10 21:18:20 +00:00
// Screensaver, supporting the following types:
// Dim, black, slideshow, video.
//
#include "SystemScreensaver.h"
2017-11-01 22:21:10 +00:00
#if defined(_RPI_)
#include "components/VideoPlayerComponent.h"
#endif
#include "components/VideoVlcComponent.h"
#include "utils/FileSystemUtil.h"
2017-11-01 22:21:10 +00:00
#include "views/gamelist/IGameListView.h"
#include "views/ViewController.h"
#include "FileData.h"
#include "Log.h"
#include "PowerSaver.h"
#include "SystemData.h"
2020-11-10 21:18:20 +00:00
#include <random>
#include <time.h>
#include <unordered_map>
#if defined(_WIN64)
#include <cstring>
#endif
#define FADE_TIME 300
2020-11-10 21:18:20 +00:00
SystemScreensaver::SystemScreensaver(
Window* window)
: mImageScreensaver(nullptr),
mVideoScreensaver(nullptr),
mWindow(window),
mState(STATE_INACTIVE),
mOpacity(0.0f),
mTimer(0),
mSystemName(""),
mGameName(""),
mCurrentGame(nullptr),
mHasMediaFiles(false)
{
2020-11-10 21:18:20 +00:00
mWindow->setScreensaver(this);
mVideoChangeTime = 30000;
}
2020-11-10 21:18:20 +00:00
SystemScreensaver::~SystemScreensaver()
{
mCurrentGame = nullptr;
delete mVideoScreensaver;
delete mImageScreensaver;
}
2020-11-10 21:18:20 +00:00
bool SystemScreensaver::allowSleep()
{
return ((mVideoScreensaver == nullptr) && (mImageScreensaver == nullptr));
}
2020-11-10 21:18:20 +00:00
bool SystemScreensaver::isScreensaverActive()
{
return (mState != STATE_INACTIVE);
}
2020-11-10 21:18:20 +00:00
void SystemScreensaver::startScreensaver(bool generateMediaList)
{
2020-11-10 21:18:20 +00:00
std::string path = "";
std::string screensaverType = Settings::getInstance()->getString("ScreensaverType");
mHasMediaFiles = false;
// Set mPreviousGame which will be used to avoid showing the same game again during
// the random selection.
2020-11-10 21:18:20 +00:00
if ((screensaverType == "video" || screensaverType == "slideshow") &&
mCurrentGame != nullptr)
mPreviousGame = mCurrentGame;
2020-11-10 21:18:20 +00:00
if (screensaverType == "slideshow") {
if (generateMediaList) {
mImageFiles.clear();
mImageCustomFiles.clear();
}
// This creates a fade transition between the images.
mState = STATE_FADE_OUT_WINDOW;
mVideoChangeTime = Settings::getInstance()->getInt("ScreensaverSwapImageTimeout");
mOpacity = 0.0f;
// Load a random image.
if (Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages")) {
if (generateMediaList)
generateCustomImageList();
pickRandomCustomImage(path);
if (mImageCustomFiles.size() > 0)
mHasMediaFiles = true;
// Custom images are not tied to the game list.
mCurrentGame = nullptr;
}
else {
if (generateMediaList)
generateImageList();
pickRandomImage(path);
}
if (mImageFiles.size() > 0)
mHasMediaFiles = true;
// Don't attempt to render the screensaver if there are no images available, but
// do flag it as running. This way Window::render() will fade to a black screen, i.e.
// it will activate the 'Black' screensaver type.
if (mImageFiles.size() > 0 || mImageCustomFiles.size() > 0) {
if (!mImageScreensaver)
mImageScreensaver = new ImageComponent(mWindow, false, false);
mTimer = 0;
mImageScreensaver->setImage(path);
mImageScreensaver->setOrigin(0.5f, 0.5f);
mImageScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f,
Renderer::getScreenHeight() / 2.0f);
if (Settings::getInstance()->getBool("ScreensaverStretchImages"))
mImageScreensaver->setResize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
else
mImageScreensaver->setMaxSize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
}
PowerSaver::runningScreensaver(true);
mTimer = 0;
return;
}
else if (!mVideoScreensaver && (screensaverType == "video")) {
if (generateMediaList)
mVideoFiles.clear();
// This creates a fade transition between the videos.
mState = STATE_FADE_OUT_WINDOW;
2020-11-05 17:18:11 +00:00
mVideoChangeTime = Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout");
mOpacity = 0.0f;
// Load a random video.
2020-11-10 21:18:20 +00:00
if (generateMediaList)
generateVideoList();
pickRandomVideo(path);
2020-11-10 21:18:20 +00:00
if (mVideoFiles.size() > 0)
mHasMediaFiles = true;
if (!path.empty() && Utils::FileSystem::exists(path)) {
#if defined(_RPI_)
// Create the correct type of video component
2020-11-05 17:18:11 +00:00
if (Settings::getInstance()->getBool("ScreensaverOmxPlayer"))
mVideoScreensaver = new VideoPlayerComponent(mWindow);
else
mVideoScreensaver = new VideoVlcComponent(mWindow);
#else
mVideoScreensaver = new VideoVlcComponent(mWindow);
#endif
mVideoScreensaver->topWindow(true);
mVideoScreensaver->setOrigin(0.5f, 0.5f);
mVideoScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f,
Renderer::getScreenHeight() / 2.0f);
2020-11-05 17:18:11 +00:00
if (Settings::getInstance()->getBool("ScreensaverStretchVideos"))
mVideoScreensaver->setResize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
else
mVideoScreensaver->setMaxSize(static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight()));
mVideoScreensaver->setVideo(path);
mVideoScreensaver->setScreensaverMode(true);
mVideoScreensaver->onShow();
2020-11-10 21:18:20 +00:00
PowerSaver::runningScreensaver(true);
mTimer = 0;
return;
}
}
2020-11-10 21:18:20 +00:00
// No videos or images, just use a standard screensaver.
mState = STATE_SCREENSAVER_ACTIVE;
mCurrentGame = nullptr;
}
2020-11-10 21:18:20 +00:00
void SystemScreensaver::stopScreensaver()
{
delete mVideoScreensaver;
mVideoScreensaver = nullptr;
delete mImageScreensaver;
mImageScreensaver = nullptr;
mState = STATE_INACTIVE;
2020-11-10 21:18:20 +00:00
PowerSaver::runningScreensaver(false);
}
void SystemScreensaver::nextGame() {
stopScreensaver();
startScreensaver(false);
}
void SystemScreensaver::launchGame()
{
if (mCurrentGame != nullptr) {
// Launching game
ViewController::get()->goToGameList(mCurrentGame->getSystem());
IGameListView* view = ViewController::get()->
getGameListView(mCurrentGame->getSystem()).get();
view->setCursor(mCurrentGame);
ViewController::get()->resetMovingCamera();
ViewController::get()->launch(mCurrentGame);
}
}
2020-11-10 21:18:20 +00:00
void SystemScreensaver::renderScreensaver()
{
2020-11-10 21:18:20 +00:00
std::string screensaverType = Settings::getInstance()->getString("ScreensaverType");
if (mVideoScreensaver && screensaverType == "video") {
// Render black background.
Renderer::setMatrix(Transform4x4f::Identity());
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
Renderer::getScreenHeight(), 0x000000FF, 0x000000FF);
// Only render the video if the state requires it.
if (static_cast<int>(mState) >= STATE_FADE_IN_VIDEO) {
Transform4x4f transform = Transform4x4f::Identity();
mVideoScreensaver->render(transform);
}
}
2020-11-10 21:18:20 +00:00
else if (mImageScreensaver && screensaverType == "slideshow") {
// Render a black background.
Renderer::setMatrix(Transform4x4f::Identity());
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
Renderer::getScreenHeight(), 0x000000FF, 0x000000FF);
// Only render the video if the state requires it.
2020-11-10 21:18:20 +00:00
if (static_cast<int>(mState) >= STATE_FADE_IN_VIDEO) {
if (mImageScreensaver->hasImage()) {
mImageScreensaver->setOpacity(255 - static_cast<unsigned char>(mOpacity * 255));
Transform4x4f transform = Transform4x4f::Identity();
mImageScreensaver->render(transform);
}
}
}
2020-11-10 21:18:20 +00:00
#if !defined(USE_OPENGL_21)
else if (mState != STATE_INACTIVE) {
Renderer::setMatrix(Transform4x4f::Identity());
2020-11-10 21:18:20 +00:00
unsigned char color = screensaverType == "dim" ? 0x000000A0 : 0x000000FF;
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
Renderer::getScreenHeight(), color, color);
}
2020-11-10 21:18:20 +00:00
#endif
}
2020-11-10 21:18:20 +00:00
void SystemScreensaver::update(int deltaTime)
{
2020-11-10 21:18:20 +00:00
// Use this to update the fade value for the current fade stage.
if (mState == STATE_FADE_OUT_WINDOW) {
mOpacity += static_cast<float>(deltaTime) / FADE_TIME;
if (mOpacity >= 1.0f) {
mOpacity = 1.0f;
2020-11-10 21:18:20 +00:00
// Update to the next state.
mState = STATE_FADE_IN_VIDEO;
}
}
2020-11-10 21:18:20 +00:00
else if (mState == STATE_FADE_IN_VIDEO) {
mOpacity -= static_cast<float>(deltaTime) / FADE_TIME;
if (mOpacity <= 0.0f) {
mOpacity = 0.0f;
// Update to the next state.
mState = STATE_SCREENSAVER_ACTIVE;
}
}
2020-11-10 21:18:20 +00:00
else if (mState == STATE_SCREENSAVER_ACTIVE) {
// Update the timer that swaps the videos.
mTimer += deltaTime;
if (mTimer > mVideoChangeTime)
nextGame();
}
2020-11-10 21:18:20 +00:00
// If we have a loaded video then update it.
if (mVideoScreensaver)
mVideoScreensaver->update(deltaTime);
if (mImageScreensaver)
mImageScreensaver->update(deltaTime);
}
2020-11-10 21:18:20 +00:00
void SystemScreensaver::generateImageList()
{
2020-11-10 21:18:20 +00:00
for (auto it = SystemData::sSystemVector.cbegin();
it != SystemData::sSystemVector.cend(); ++it) {
// We only want nodes from game systems that are not collections.
if (!(*it)->isGameSystem() || (*it)->isCollection())
continue;
2020-11-10 21:18:20 +00:00
std::vector<FileData*> allFiles = (*it)->getRootFolder()->getFilesRecursive(GAME, true);
for (auto it = allFiles.begin(); it != allFiles.end(); it++) {
std::string imagePath = (*it)->getImagePath();
if (imagePath != "")
mImageFiles.push_back((*it));
}
}
}
2020-11-10 21:18:20 +00:00
void SystemScreensaver::generateVideoList()
{
2020-11-10 21:18:20 +00:00
for (auto it = SystemData::sSystemVector.cbegin();
it != SystemData::sSystemVector.cend(); ++it) {
// We only want nodes from game systems that are not collections.
if (!(*it)->isGameSystem() || (*it)->isCollection())
continue;
2020-11-10 21:18:20 +00:00
std::vector<FileData*> allFiles = (*it)->getRootFolder()->getFilesRecursive(GAME, true);
for (auto it = allFiles.begin(); it != allFiles.end(); it++) {
std::string videoPath = (*it)->getVideoPath();
if (videoPath != "")
mVideoFiles.push_back((*it));
}
}
}
2020-11-10 21:18:20 +00:00
void SystemScreensaver::generateCustomImageList()
{
2020-11-10 21:18:20 +00:00
std::string imageDir = Utils::FileSystem::expandHomePath(
Settings::getInstance()->getString("ScreensaverSlideshowImageDir"));
2020-11-10 21:18:20 +00:00
if (imageDir != "" && Utils::FileSystem::isDirectory(imageDir)) {
2020-11-05 17:18:11 +00:00
std::string imageFilter = ".jpg, .JPG, .png, .PNG";
Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(
2020-11-05 17:18:11 +00:00
imageDir, Settings::getInstance()->getBool("ScreensaverSlideshowRecurse"));
2020-11-10 21:18:20 +00:00
for (auto it = dirContent.begin(); it != dirContent.end(); it++) {
if (Utils::FileSystem::isRegularFile(*it)) {
2020-11-10 21:18:20 +00:00
if (imageFilter.find(Utils::FileSystem::getExtension(*it)) != std::string::npos)
mImageCustomFiles.push_back(*it);
}
}
}
else {
2020-11-10 21:18:20 +00:00
LOG(LogWarning) << "Custom screensaver image directory '" <<
imageDir << "' does not exist.";
}
}
2020-11-10 21:18:20 +00:00
void SystemScreensaver::pickRandomImage(std::string& path)
{
2020-11-10 21:18:20 +00:00
mCurrentGame = nullptr;
2020-11-10 21:18:20 +00:00
if (mImageFiles.size() == 0)
return;
if (mImageFiles.size() == 1) {
mPreviousGame = nullptr;
mCurrentGame = mImageFiles.front();
path = mImageFiles.front()->getImagePath();
return;
}
2020-11-10 21:18:20 +00:00
unsigned int index;
do {
// Get a random number in range.
std::random_device randDev;
// Mersenne Twister pseudorandom number generator.
std::mt19937 engine{randDev()};
std::uniform_int_distribution<int> uniform_dist(0, mImageFiles.size() - 1);
index = uniform_dist(engine);
}
while (mPreviousGame && mImageFiles.at(index) == mPreviousGame);
2020-11-10 21:18:20 +00:00
path = mImageFiles.at(index)->getImagePath();
mGameName = mImageFiles.at(index)->getName();
mSystemName = mImageFiles.at(index)->getSystem()->getFullName();
mCurrentGame = mImageFiles.at(index);
}
2020-11-10 21:18:20 +00:00
void SystemScreensaver::pickRandomVideo(std::string& path)
{
2020-11-10 21:18:20 +00:00
mCurrentGame = nullptr;
if (mVideoFiles.size() == 0)
return;
if (mVideoFiles.size() == 1) {
mPreviousGame = nullptr;
mCurrentGame = mVideoFiles.front();
path = mVideoFiles.front()->getVideoPath();
return;
}
unsigned int index;
do {
// Get a random number in range.
std::random_device randDev;
// Mersenne Twister pseudorandom number generator.
std::mt19937 engine{randDev()};
std::uniform_int_distribution<int> uniform_dist(0, mVideoFiles.size() - 1);
index = uniform_dist(engine);
}
while (mPreviousGame && mVideoFiles.at(index) == mPreviousGame);
path = mVideoFiles.at(index)->getVideoPath();
mGameName = mVideoFiles.at(index)->getName();
mSystemName = mVideoFiles.at(index)->getSystem()->getFullName();
mCurrentGame = mVideoFiles.at(index);
}
2020-11-10 21:18:20 +00:00
void SystemScreensaver::pickRandomCustomImage(std::string& path)
{
2020-11-10 21:18:20 +00:00
if (mImageCustomFiles.size() == 0)
return;
if (mVideoFiles.size() == 1) {
mPreviousCustomImage = mImageCustomFiles.front();
path = mImageCustomFiles.front();
return;
}
2020-11-10 21:18:20 +00:00
unsigned int index;
do {
// Get a random number in range.
std::random_device randDev;
// Mersenne Twister pseudorandom number generator.
std::mt19937 engine{randDev()};
std::uniform_int_distribution<int> uniform_dist(0, mImageCustomFiles.size() - 1);
index = uniform_dist(engine);
}
while (mPreviousCustomImage != "" && mImageCustomFiles.at(index) == mPreviousCustomImage);
path = mImageCustomFiles.at(index);
mPreviousCustomImage = path;
mGameName = "";
mSystemName = "";
}