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

457 lines
16 KiB
C++
Raw Normal View History

//
// SystemScreenSaver.cpp
//
// Screensaver, supporting the following modes:
// Dim, black, slideshow, video.
//
#include "SystemScreenSaver.h"
2017-11-01 22:21:10 +00:00
#ifdef _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 "FileFilterIndex.h"
#include "Log.h"
#include "PowerSaver.h"
2017-11-01 22:21:10 +00:00
#include "Sound.h"
#include "SystemData.h"
2017-11-01 22:21:10 +00:00
#include <unordered_map>
#include <time.h>
#ifdef _WIN64
#include <cstring>
#endif
#define FADE_TIME 300
SystemScreenSaver::SystemScreenSaver(
Window* window)
: mVideoScreensaver(nullptr),
mImageScreensaver(nullptr),
mWindow(window),
mVideosCounted(false),
mVideoCount(0),
mImagesCounted(false),
mImageCount(0),
mState(STATE_INACTIVE),
mOpacity(0.0f),
mTimer(0),
mSystemName(""),
mGameName(""),
mCurrentGame(nullptr),
mStopBackgroundAudio(true)
{
mWindow->setScreenSaver(this);
std::string path = getTitleFolder();
if (!Utils::FileSystem::exists(path))
Utils::FileSystem::createDirectory(path);
srand((unsigned int)time(nullptr));
mVideoChangeTime = 30000;
}
SystemScreenSaver::~SystemScreenSaver()
{
// Delete subtitle file, if it exists.
remove(getTitlePath().c_str());
mCurrentGame = nullptr;
delete mVideoScreensaver;
delete mImageScreensaver;
}
bool SystemScreenSaver::allowSleep()
{
return ((mVideoScreensaver == nullptr) && (mImageScreensaver == nullptr));
}
bool SystemScreenSaver::isScreenSaverActive()
{
return (mState != STATE_INACTIVE);
}
void SystemScreenSaver::startScreenSaver()
{
std::string screensaver_behavior = Settings::getInstance()->getString("ScreenSaverBehavior");
if (!mVideoScreensaver && (screensaver_behavior == "random video")) {
// Configure to fade out the windows, skip fading if mode is set to Instant.
mState = PowerSaver::getMode() == PowerSaver::INSTANT
? STATE_SCREENSAVER_ACTIVE
: STATE_FADE_OUT_WINDOW;
mVideoChangeTime = Settings::getInstance()->getInt("ScreenSaverSwapVideoTimeout");
mOpacity = 0.0f;
// Load a random video.
std::string path = "";
pickRandomVideo(path);
int retry = 200;
while (retry > 0 && ((path.empty() || !Utils::FileSystem::exists(path)) ||
mCurrentGame == nullptr)) {
retry--;
pickRandomVideo(path);
}
if (!path.empty() && Utils::FileSystem::exists(path)) {
#ifdef _RPI_
// Create the correct type of video component
if (Settings::getInstance()->getBool("ScreenSaverOmxPlayer"))
mVideoScreensaver = new VideoPlayerComponent(mWindow, getTitlePath());
else
mVideoScreensaver = new VideoVlcComponent(mWindow, getTitlePath());
#else
mVideoScreensaver = new VideoVlcComponent(mWindow, getTitlePath());
#endif
mVideoScreensaver->topWindow(true);
mVideoScreensaver->setOrigin(0.5f, 0.5f);
mVideoScreensaver->setPosition(Renderer::getScreenWidth() / 2.0f,
Renderer::getScreenHeight() / 2.0f);
2020-07-27 18:38:22 +00:00
if (Settings::getInstance()->getBool("ScreenSaverStretchVideos"))
mVideoScreensaver->setResize((float)Renderer::getScreenWidth(),
(float)Renderer::getScreenHeight());
else
mVideoScreensaver->setMaxSize((float)Renderer::getScreenWidth(),
(float)Renderer::getScreenHeight());
mVideoScreensaver->setVideo(path);
mVideoScreensaver->setScreensaverMode(true);
mVideoScreensaver->onShow();
PowerSaver::runningScreenSaver(true);
mTimer = 0;
return;
}
}
else if (screensaver_behavior == "slideshow") {
// Configure to fade out the windows, skip fading if mode is set to Instant.
mState = PowerSaver::getMode() == PowerSaver::INSTANT
? STATE_SCREENSAVER_ACTIVE
: STATE_FADE_OUT_WINDOW;
mVideoChangeTime = Settings::getInstance()->getInt("ScreenSaverSwapImageTimeout");
mOpacity = 0.0f;
// Load a random image.
std::string path = "";
if (Settings::getInstance()->getBool("SlideshowScreenSaverCustomImageSource")) {
pickRandomCustomImage(path);
// Custom images are not tied to the game list.
mCurrentGame = nullptr;
}
else {
pickRandomGameListImage(path);
}
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);
2020-07-27 18:38:22 +00:00
if (Settings::getInstance()->getBool("ScreenSaverStretchImages"))
mImageScreensaver->setResize((float)Renderer::getScreenWidth(),
(float)Renderer::getScreenHeight());
else
mImageScreensaver->setMaxSize((float)Renderer::getScreenWidth(),
(float)Renderer::getScreenHeight());
std::string bg_audio_file = Settings::getInstance()->
getString("SlideshowScreenSaverBackgroundAudioFile");
if ((!mBackgroundAudio) && (bg_audio_file != "")) {
if (Utils::FileSystem::exists(bg_audio_file)) {
// Pause PowerSaver so that the background audio keeps playing.
PowerSaver::pause();
mBackgroundAudio = Sound::get(bg_audio_file);
mBackgroundAudio->play();
}
}
PowerSaver::runningScreenSaver(true);
mTimer = 0;
return;
}
// No videos. Just use a standard screensaver.
mState = STATE_SCREENSAVER_ACTIVE;
mCurrentGame = nullptr;
}
void SystemScreenSaver::stopScreenSaver()
{
if ((mBackgroundAudio) && (mStopBackgroundAudio)) {
mBackgroundAudio->stop();
mBackgroundAudio.reset();
// If we were playing audio, we paused PowerSaver.
PowerSaver::resume();
}
// So that we stop the background audio next time, unless we're restarting the screensaver.
mStopBackgroundAudio = true;
delete mVideoScreensaver;
mVideoScreensaver = nullptr;
delete mImageScreensaver;
mImageScreensaver = nullptr;
// We need this to loop through different videos.
mState = STATE_INACTIVE;
PowerSaver::runningScreenSaver(false);
}
void SystemScreenSaver::renderScreenSaver()
{
std::string screensaver_behavior = Settings::getInstance()->getString("ScreenSaverBehavior");
if (mVideoScreensaver && screensaver_behavior == "random video") {
// Render black background.
Renderer::setMatrix(Transform4x4f::Identity());
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
Renderer::getScreenHeight(), 0x000000FF, 0x000000FF);
// Only render the video if the state requires it.
if ((int)mState >= STATE_FADE_IN_VIDEO) {
Transform4x4f transform = Transform4x4f::Identity();
mVideoScreensaver->render(transform);
}
}
else if (mImageScreensaver && screensaver_behavior == "slideshow") {
// Render black background.
Renderer::setMatrix(Transform4x4f::Identity());
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
Renderer::getScreenHeight(), 0x000000FF, 0x000000FF);
// Only render the video if the state requires it.
if ((int)mState >= STATE_FADE_IN_VIDEO) {
if (mImageScreensaver->hasImage()) {
mImageScreensaver->setOpacity(255- (unsigned char) (mOpacity * 255));
Transform4x4f transform = Transform4x4f::Identity();
mImageScreensaver->render(transform);
}
}
// Check if we need to restart the background audio.
if ((mBackgroundAudio) && (Settings::getInstance()->
getString("SlideshowScreenSaverBackgroundAudioFile") != "")) {
if (!mBackgroundAudio->isPlaying())
mBackgroundAudio->play();
}
}
else if (mState != STATE_INACTIVE) {
Renderer::setMatrix(Transform4x4f::Identity());
unsigned char color = screensaver_behavior == "dim" ? 0x000000A0 : 0x000000FF;
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
Renderer::getScreenHeight(), color, color);
}
}
unsigned long SystemScreenSaver::countGameListNodes(const char *nodeName)
{
unsigned long nodeCount = 0;
std::vector<SystemData*>::const_iterator it;
for (it = SystemData::sSystemVector.cbegin();
it != SystemData::sSystemVector.cend(); ++it) {
// We only want nodes from game systems that are not collections.
if (!(*it)->isGameSystem() || (*it)->isCollection())
continue;
FileData* rootFileData = (*it)->getRootFolder();
FileType type = GAME;
std::vector<FileData*> allFiles = rootFileData->getFilesRecursive(type, true);
std::vector<FileData*>::const_iterator itf; // Declare an iterator to a vector of strings.
for (itf=allFiles.cbegin() ; itf < allFiles.cend(); itf++) {
if ((strcmp(nodeName, "video") == 0 && (*itf)->getVideoPath() != "") ||
(strcmp(nodeName, "image") == 0 && (*itf)->getImagePath() != ""))
nodeCount++;
}
}
return nodeCount;
}
void SystemScreenSaver::countVideos()
{
if (!mVideosCounted) {
mVideoCount = countGameListNodes("video");
mVideosCounted = true;
}
}
void SystemScreenSaver::countImages()
{
if (!mImagesCounted) {
mImageCount = countGameListNodes("image");
mImagesCounted = true;
}
}
void SystemScreenSaver::pickGameListNode(unsigned long index,
const char *nodeName, std::string& path)
{
std::vector<SystemData*>::const_iterator it;
for (it = SystemData::sSystemVector.cbegin();
it != SystemData::sSystemVector.cend(); ++it) {
// We only want nodes from game systems that are not collections.
if (!(*it)->isGameSystem() || (*it)->isCollection())
continue;
FileData* rootFileData = (*it)->getRootFolder();
FileType type = GAME;
std::vector<FileData*> allFiles = rootFileData->getFilesRecursive(type, true);
std::vector<FileData*>::const_iterator itf; // Declare an iterator to a vector of strings.
for (itf=allFiles.cbegin() ; itf < allFiles.cend(); itf++) {
if ((strcmp(nodeName, "video") == 0 && (*itf)->getVideoPath() != "") ||
(strcmp(nodeName, "image") == 0 && (*itf)->getImagePath() != "")) {
if (index-- == 0) {
// We have it.
path = "";
if (strcmp(nodeName, "video") == 0)
path = (*itf)->getVideoPath();
else if (strcmp(nodeName, "image") == 0)
path = (*itf)->getImagePath();
mSystemName = (*it)->getFullName();
mGameName = (*itf)->getName();
mCurrentGame = (*itf);
// End of getting FileData.
#ifdef _RPI_
if (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never")
writeSubtitle(mGameName.c_str(), mSystemName.c_str(),
(Settings::getInstance()->getString("ScreenSaverGameInfo") ==
"always"));
#endif
return;
}
}
}
}
}
void SystemScreenSaver::pickRandomVideo(std::string& path)
{
countVideos();
mCurrentGame = nullptr;
if (mVideoCount > 0) {
int video = (int)(((float)rand() / float(RAND_MAX)) * (float)mVideoCount);
pickGameListNode(video, "video", path);
}
}
void SystemScreenSaver::pickRandomGameListImage(std::string& path)
{
countImages();
mCurrentGame = nullptr;
if (mImageCount > 0) {
int image = (int)(((float)rand() / float(RAND_MAX)) * (float)mImageCount);
pickGameListNode(image, "image", path);
}
}
void SystemScreenSaver::pickRandomCustomImage(std::string& path)
{
std::string imageDir = Settings::getInstance()->getString("SlideshowScreenSaverImageDir");
if ((imageDir != "") && (Utils::FileSystem::exists(imageDir))) {
std::string imageFilter = Settings::getInstance()->
getString("SlideshowScreenSaverImageFilter");
std::vector<std::string> matchingFiles;
Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(
imageDir, Settings::getInstance()->getBool("SlideshowScreenSaverRecurse"));
for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin();
it != dirContent.cend(); ++it) {
if (Utils::FileSystem::isRegularFile(*it)) {
// If the image filter is empty, or the file extension is in the filter
// string, add it to the matching files list.
if ((imageFilter.length() <= 0) || (imageFilter.find(
Utils::FileSystem::getExtension(*it)) != std::string::npos))
matchingFiles.push_back(*it);
}
}
int fileCount = (int)matchingFiles.size();
if (fileCount > 0) {
// Get a random index in the range 0 to fileCount (exclusive).
int randomIndex = rand() % fileCount;
path = matchingFiles[randomIndex];
}
else {
LOG(LogError) << "Slideshow Screensaver - No image files found\n";
}
}
else {
LOG(LogError) << "Slideshow Screensaver - Image directory does not exist: " <<
imageDir << "\n";
}
}
void SystemScreenSaver::update(int deltaTime)
{
// Use this to update the fade value for the current fade stage.
if (mState == STATE_FADE_OUT_WINDOW) {
mOpacity += (float)deltaTime / FADE_TIME;
if (mOpacity >= 1.0f) {
mOpacity = 1.0f;
// Update to the next state.
mState = STATE_FADE_IN_VIDEO;
}
}
else if (mState == STATE_FADE_IN_VIDEO) {
mOpacity -= (float)deltaTime / FADE_TIME;
if (mOpacity <= 0.0f) {
mOpacity = 0.0f;
// Update to the next state.
mState = STATE_SCREENSAVER_ACTIVE;
}
}
else if (mState == STATE_SCREENSAVER_ACTIVE) {
// Update the timer that swaps the videos.
mTimer += deltaTime;
if (mTimer > mVideoChangeTime)
nextVideo();
}
// If we have a loaded video then update it.
if (mVideoScreensaver)
mVideoScreensaver->update(deltaTime);
if (mImageScreensaver)
mImageScreensaver->update(deltaTime);
}
void SystemScreenSaver::nextVideo() {
mStopBackgroundAudio = false;
stopScreenSaver();
startScreenSaver();
mState = STATE_SCREENSAVER_ACTIVE;
}
FileData* SystemScreenSaver::getCurrentGame()
{
return mCurrentGame;
}
void SystemScreenSaver::launchGame()
{
if (mCurrentGame != nullptr) {
// Launching game
ViewController::get()->goToGameList(mCurrentGame->getSystem());
IGameListView* view = ViewController::get()->
getGameListView(mCurrentGame->getSystem()).get();
view->setCursor(mCurrentGame);
if (Settings::getInstance()->getBool("ScreenSaverControls"))
view->launch(mCurrentGame);
}
}