From 0d046339549a5ce097a18f45ff6d6437e57992df Mon Sep 17 00:00:00 2001 From: fieldofcows Date: Wed, 14 Dec 2016 08:30:54 +0000 Subject: [PATCH 1/2] Changes from fieldofcows to separate screensaver into separate module, squashed by pjft. --- es-app/CMakeLists.txt | 2 + es-app/src/SystemScreenSaver.cpp | 217 ++++++++++++++++++ es-app/src/SystemScreenSaver.h | 39 ++++ es-app/src/guis/GuiMenu.cpp | 3 +- es-app/src/main.cpp | 2 + es-core/src/GuiComponent.h | 1 + es-core/src/Window.cpp | 65 ++++-- es-core/src/Window.h | 15 ++ .../src/components/VideoPlayerComponent.cpp | 1 + es-core/src/components/VideoVlcComponent.cpp | 1 + 10 files changed, 327 insertions(+), 19 deletions(-) create mode 100644 es-app/src/SystemScreenSaver.cpp create mode 100644 es-app/src/SystemScreenSaver.h diff --git a/es-app/CMakeLists.txt b/es-app/CMakeLists.txt index 1b062b692..2378348c4 100644 --- a/es-app/CMakeLists.txt +++ b/es-app/CMakeLists.txt @@ -11,6 +11,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/VolumeControl.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Gamelist.h ${CMAKE_CURRENT_SOURCE_DIR}/src/FileFilterIndex.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/SystemScreenSaver.h # GuiComponents ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AsyncReqComponent.h @@ -60,6 +61,7 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/VolumeControl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Gamelist.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/FileFilterIndex.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/SystemScreenSaver.cpp # GuiComponents ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AsyncReqComponent.cpp diff --git a/es-app/src/SystemScreenSaver.cpp b/es-app/src/SystemScreenSaver.cpp new file mode 100644 index 000000000..3d3c46a62 --- /dev/null +++ b/es-app/src/SystemScreenSaver.cpp @@ -0,0 +1,217 @@ +#include "SystemScreenSaver.h" +#ifdef _RPI_ +#include "components/VideoPlayerComponent.h" +#endif +#include "components/VideoVlcComponent.h" +#include "Renderer.h" +#include "Settings.h" +#include "SystemData.h" +#include "Util.h" + +#define FADE_TIME 3000 +#define SWAP_VIDEO_TIMEOUT 30000 + +SystemScreenSaver::SystemScreenSaver(Window* window) : + mVideoScreensaver(NULL), + mWindow(window), + mCounted(false), + mVideoCount(0), + mState(STATE_INACTIVE), + mOpacity(0.0f), + mTimer(0) +{ + mWindow->setScreenSaver(this); +} + +SystemScreenSaver::~SystemScreenSaver() +{ + delete mVideoScreensaver; +} + +bool SystemScreenSaver::allowSleep() +{ + return false; +} + +void SystemScreenSaver::startScreenSaver() +{ + if (!mVideoScreensaver && (Settings::getInstance()->getString("ScreenSaverBehavior") == "random video")) + { + // Configure to fade out the windows + mState = STATE_FADE_OUT_WINDOW; + mOpacity = 0.0f; + + // Load a random video + std::string path; + pickRandomVideo(path); + if (!path.empty()) + { + // Create the correct type of video component +#ifdef _RPI_ + if (Settings::getInstance()->getBool("VideoOmxPlayer")) + mVideoScreensaver = new VideoPlayerComponent(mWindow); + else + mVideoScreensaver = new VideoVlcComponent(mWindow); +#else + mVideoScreensaver = new VideoVlcComponent(mWindow); +#endif + + + mVideoScreensaver->setOrigin(0.0f, 0.0f); + mVideoScreensaver->setPosition(0.0f, 0.0f); + mVideoScreensaver->setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); + mVideoScreensaver->setVideo(path); + mVideoScreensaver->onShow(); + mTimer = 0; + } + else + { + // No videos. Just use a standard screensaver + mState = STATE_SCREENSAVER_ACTIVE; + } + } +} + +void SystemScreenSaver::stopScreenSaver() +{ + delete mVideoScreensaver; + mVideoScreensaver = NULL; + mState = STATE_INACTIVE; +} + +void SystemScreenSaver::renderScreenSaver() +{ + if (mVideoScreensaver) + { + // Only render the video if the state requires it + if ((int)mState >= STATE_FADE_IN_VIDEO) + { + Eigen::Affine3f transform = Eigen::Affine3f::Identity(); + mVideoScreensaver->render(transform); + } + // Handle any fade + Renderer::setMatrix(Eigen::Affine3f::Identity()); + Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), (unsigned char)(mOpacity * 255)); + } + else if (mState != STATE_INACTIVE) + { + Renderer::setMatrix(Eigen::Affine3f::Identity()); + unsigned char opacity = Settings::getInstance()->getString("ScreenSaverBehavior") == "dim" ? 0xA0 : 0xFF; + Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0x00000000 | opacity); + } +} + +void SystemScreenSaver::countVideos() +{ + if (!mCounted) + { + mVideoCount = 0; + mCounted = true; + std::vector:: iterator it; + for (it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); ++it) + { + pugi::xml_document doc; + pugi::xml_node root; + std::string xmlReadPath = (*it)->getGamelistPath(false); + + if(boost::filesystem::exists(xmlReadPath)) + { + pugi::xml_parse_result result = doc.load_file(xmlReadPath.c_str()); + if (!result) + continue; + root = doc.child("gameList"); + if (!root) + continue; + for(pugi::xml_node fileNode = root.child("game"); fileNode; fileNode = fileNode.next_sibling("game")) + { + pugi::xml_node videoNode = fileNode.child("video"); + if (videoNode) + ++mVideoCount; + } + } + } + } +} + +void SystemScreenSaver::pickRandomVideo(std::string& path) +{ + countVideos(); + if (mVideoCount > 0) + { + srand((unsigned int)time(NULL)); + int video = (int)(((float)rand() / float(RAND_MAX)) * (float)mVideoCount); + + std::vector:: iterator it; + for (it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); ++it) + { + pugi::xml_document doc; + pugi::xml_node root; + std::string xmlReadPath = (*it)->getGamelistPath(false); + + if(boost::filesystem::exists(xmlReadPath)) + { + pugi::xml_parse_result result = doc.load_file(xmlReadPath.c_str()); + if (!result) + continue; + root = doc.child("gameList"); + if (!root) + continue; + for(pugi::xml_node fileNode = root.child("game"); fileNode; fileNode = fileNode.next_sibling("game")) + { + pugi::xml_node videoNode = fileNode.child("video"); + if (videoNode) + { + // See if this is the randomly selected video + if (video-- == 0) + { + // Yes. Resolve to a full path + path = resolvePath(videoNode.text().get(), (*it)->getStartPath(), true).generic_string(); + return; + } + } + } + } + } + } +} + +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 > SWAP_VIDEO_TIMEOUT) + { + stopScreenSaver(); + startScreenSaver(); + mState = STATE_SCREENSAVER_ACTIVE; + } + } + + // If we have a loaded video then update it + if (mVideoScreensaver) + mVideoScreensaver->update(deltaTime); +} diff --git a/es-app/src/SystemScreenSaver.h b/es-app/src/SystemScreenSaver.h new file mode 100644 index 000000000..2e693e596 --- /dev/null +++ b/es-app/src/SystemScreenSaver.h @@ -0,0 +1,39 @@ +#pragma once + +#include "Window.h" + +class VideoComponent; + +// Screensaver implementation for main window +class SystemScreenSaver : public Window::ScreenSaver +{ +public: + SystemScreenSaver(Window* window); + virtual ~SystemScreenSaver(); + + virtual void startScreenSaver(); + virtual void stopScreenSaver(); + virtual void renderScreenSaver(); + virtual bool allowSleep(); + virtual void update(int deltaTime); + +private: + void countVideos(); + void pickRandomVideo(std::string& path); + + enum STATE { + STATE_INACTIVE, + STATE_FADE_OUT_WINDOW, + STATE_FADE_IN_VIDEO, + STATE_SCREENSAVER_ACTIVE + }; + +private: + bool mCounted; + unsigned long mVideoCount; + VideoComponent* mVideoScreensaver; + Window* mWindow; + STATE mState; + float mOpacity; + int mTimer; +}; diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 9735b0f60..3d3e8d3a2 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -112,10 +112,11 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN s->addSaveFunc([screensaver_time] { Settings::getInstance()->setInt("ScreenSaverTime", (int)round(screensaver_time->getValue()) * (1000 * 60)); }); // screensaver behavior - auto screensaver_behavior = std::make_shared< OptionListComponent >(mWindow, "TRANSITION STYLE", false); + auto screensaver_behavior = std::make_shared< OptionListComponent >(mWindow, "STYLE", false); std::vector screensavers; screensavers.push_back("dim"); screensavers.push_back("black"); + screensavers.push_back("random video"); for(auto it = screensavers.begin(); it != screensavers.end(); it++) screensaver_behavior->add(*it, *it, Settings::getInstance()->getString("ScreenSaverBehavior") == *it); s->addWithLabel("SCREENSAVER BEHAVIOR", screensaver_behavior); diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index dec399bde..0128089be 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -14,6 +14,7 @@ #include "platform.h" #include "Log.h" #include "Window.h" +#include "SystemScreenSaver.h" #include "EmulationStation.h" #include "Settings.h" #include "ScraperCmdLine.h" @@ -222,6 +223,7 @@ int main(int argc, char* argv[]) atexit(&onExit); Window window; + SystemScreenSaver screensaver(&window); ViewController::init(&window); window.pushGui(ViewController::get()); diff --git a/es-core/src/GuiComponent.h b/es-core/src/GuiComponent.h index 66b921b88..6172cd528 100644 --- a/es-core/src/GuiComponent.h +++ b/es-core/src/GuiComponent.h @@ -87,6 +87,7 @@ public: virtual void onShow(); virtual void onHide(); + virtual void onScreenSaverActivate(); virtual void onScreenSaverDeactivate(); virtual void topWindow(bool isTop); diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index b8c6bc144..8bf64fd73 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -10,7 +10,7 @@ #include "components/ImageComponent.h" Window::Window() : mNormalizeNextUpdate(false), mFrameTimeElapsed(0), mFrameCountElapsed(0), mAverageDeltaTime(10), - mAllowSleep(true), mSleeping(false), mTimeSinceLastInput(0) + mAllowSleep(true), mSleeping(false), mTimeSinceLastInput(0), mScreenSaver(NULL), mRenderScreenSaver(false) { mHelp = new HelpComponent(this); mBackgroundOverlay = new ImageComponent(this); @@ -128,13 +128,14 @@ void Window::input(InputConfig* config, Input input) { // wake up mTimeSinceLastInput = 0; - + cancelScreenSaver(); mSleeping = false; onWake(); return; } mTimeSinceLastInput = 0; + cancelScreenSaver(); if(config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_g && SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { @@ -194,6 +195,10 @@ void Window::update(int deltaTime) if(peekGui()) peekGui()->update(deltaTime); + + // Update the screensaver + if (mScreenSaver) + mScreenSaver->update(deltaTime); } void Window::render() @@ -226,18 +231,16 @@ void Window::render() } unsigned int screensaverTime = (unsigned int)Settings::getInstance()->getInt("ScreenSaverTime"); + if(mTimeSinceLastInput >= screensaverTime && screensaverTime != 0) + startScreenSaver(); + + // Always call the screensaver render function regardless of whether the screensaver is active + // or not because it may perform a fade on transition + renderScreenSaver(); + if(mTimeSinceLastInput >= screensaverTime && screensaverTime != 0) { - if (!mRenderScreenSaver) - { - for(auto i = mGuiStack.begin(); i != mGuiStack.end(); i++) - (*i)->onScreenSaverActivate(); - mRenderScreenSaver = true; - } - - renderScreenSaver(); - - if (!isProcessing() && mAllowSleep) + if (!isProcessing() && mAllowSleep && (!mScreenSaver || mScreenSaver->allowSleep())) { // go to sleep mSleeping = true; @@ -374,9 +377,35 @@ bool Window::isProcessing() return count_if(mGuiStack.begin(), mGuiStack.end(), [](GuiComponent* c) { return c->isProcessing(); }) > 0; } -void Window::renderScreenSaver() -{ - Renderer::setMatrix(Eigen::Affine3f::Identity()); - unsigned char opacity = Settings::getInstance()->getString("ScreenSaverBehavior") == "dim" ? 0xA0 : 0xFF; - Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0x00000000 | opacity); -} +void Window::startScreenSaver() + { + if (mScreenSaver && !mRenderScreenSaver) + { + // Tell the GUI components the screensaver is starting + for(auto i = mGuiStack.begin(); i != mGuiStack.end(); i++) + (*i)->onScreenSaverActivate(); + + mScreenSaver->startScreenSaver(); + mRenderScreenSaver = true; + } + } + + void Window::cancelScreenSaver() + { + if (mScreenSaver && mRenderScreenSaver) + { + mScreenSaver->stopScreenSaver(); + mRenderScreenSaver = false; + + // Tell the GUI components the screensaver has stopped + for(auto i = mGuiStack.begin(); i != mGuiStack.end(); i++) + (*i)->onScreenSaverDeactivate(); + } + } + + void Window::renderScreenSaver() + { + if (mScreenSaver) + mScreenSaver->renderScreenSaver(); + } + diff --git a/es-core/src/Window.h b/es-core/src/Window.h index 6e2c93d3a..d1e6bdd39 100644 --- a/es-core/src/Window.h +++ b/es-core/src/Window.h @@ -11,6 +11,15 @@ class ImageComponent; class Window { public: + class ScreenSaver { + public: + virtual void startScreenSaver() = 0; + virtual void stopScreenSaver() = 0; + virtual void renderScreenSaver() = 0; + virtual bool allowSleep() = 0; + virtual void update(int deltaTime) = 0; + }; + Window(); ~Window(); @@ -38,6 +47,8 @@ public: void renderHelpPromptsEarly(); // used to render HelpPrompts before a fade void setHelpPrompts(const std::vector& prompts, const HelpStyle& style); + void setScreenSaver(ScreenSaver* screenSaver) { mScreenSaver = screenSaver; } + private: void onSleep(); void onWake(); @@ -45,9 +56,13 @@ private: // Returns true if at least one component on the stack is processing bool isProcessing(); void renderScreenSaver(); + void startScreenSaver(); + void cancelScreenSaver(); HelpComponent* mHelp; ImageComponent* mBackgroundOverlay; + ScreenSaver* mScreenSaver; + bool mRenderScreenSaver; std::vector mGuiStack; diff --git a/es-core/src/components/VideoPlayerComponent.cpp b/es-core/src/components/VideoPlayerComponent.cpp index 3096688fa..bd9b68087 100644 --- a/es-core/src/components/VideoPlayerComponent.cpp +++ b/es-core/src/components/VideoPlayerComponent.cpp @@ -19,6 +19,7 @@ VideoPlayerComponent::VideoPlayerComponent(Window* window) : VideoPlayerComponent::~VideoPlayerComponent() { + stopVideo(); } void VideoPlayerComponent::render(const Eigen::Affine3f& parentTrans) diff --git a/es-core/src/components/VideoVlcComponent.cpp b/es-core/src/components/VideoVlcComponent.cpp index 5e1be839c..8adedf628 100644 --- a/es-core/src/components/VideoVlcComponent.cpp +++ b/es-core/src/components/VideoVlcComponent.cpp @@ -45,6 +45,7 @@ VideoVlcComponent::VideoVlcComponent(Window* window) : VideoVlcComponent::~VideoVlcComponent() { + stopVideo(); } void VideoVlcComponent::setResize(float width, float height) From 59d7516a1618604644067101aed87a4cc13f3fe0 Mon Sep 17 00:00:00 2001 From: pjft Date: Thu, 1 Jun 2017 21:08:44 +0100 Subject: [PATCH 2/2] Random Game Screensaver, Game Name and Controls - Final changes for VLC screensaver support as well - ALSA de-init/re-init only when needed - Adding screensaver options menu inside UI settings - Slightly moved options (Show Frameskip to "Other Settings", sorting within same menu) - Adding info popups on random video screensaver and OMX + Game Info setting --- es-app/CMakeLists.txt | 2 + es-app/src/SystemScreenSaver.cpp | 153 +++++++++++++++--- es-app/src/SystemScreenSaver.h | 10 ++ es-app/src/guis/GuiMenu.cpp | 136 +++++++++------- es-app/src/guis/GuiMenu.h | 2 +- es-app/src/guis/GuiScreensaverOptions.cpp | 125 ++++++++++++++ es-app/src/guis/GuiScreensaverOptions.h | 24 +++ es-app/src/views/SystemView.cpp | 10 ++ es-app/src/views/gamelist/BasicGameListView.h | 2 +- .../src/views/gamelist/DetailedGameListView.h | 1 - es-app/src/views/gamelist/GridGameListView.h | 2 +- es-app/src/views/gamelist/IGameListView.h | 1 + .../src/views/gamelist/ISimpleGameListView.h | 2 +- .../src/views/gamelist/VideoGameListView.cpp | 8 +- es-app/src/views/gamelist/VideoGameListView.h | 3 +- es-core/src/Settings.cpp | 12 ++ es-core/src/Window.cpp | 97 ++++++----- es-core/src/Window.h | 15 +- es-core/src/components/VideoComponent.cpp | 45 ++++++ es-core/src/components/VideoComponent.h | 8 + .../src/components/VideoPlayerComponent.cpp | 60 +++++-- es-core/src/components/VideoPlayerComponent.h | 3 +- es-core/src/components/VideoVlcComponent.cpp | 61 +++++-- es-core/src/components/VideoVlcComponent.h | 5 +- es-core/src/platform.h | 1 + 25 files changed, 624 insertions(+), 164 deletions(-) create mode 100644 es-app/src/guis/GuiScreensaverOptions.cpp create mode 100644 es-app/src/guis/GuiScreensaverOptions.h diff --git a/es-app/CMakeLists.txt b/es-app/CMakeLists.txt index 2378348c4..d44c16ac1 100644 --- a/es-app/CMakeLists.txt +++ b/es-app/CMakeLists.txt @@ -24,6 +24,7 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMetaDataEd.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGameScraper.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGamelistOptions.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScreensaverOptions.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMenu.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiSettings.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperMulti.h @@ -73,6 +74,7 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMetaDataEd.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGameScraper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiGamelistOptions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScreensaverOptions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiSettings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiScraperMulti.cpp diff --git a/es-app/src/SystemScreenSaver.cpp b/es-app/src/SystemScreenSaver.cpp index 3d3c46a62..242d3ef9c 100644 --- a/es-app/src/SystemScreenSaver.cpp +++ b/es-app/src/SystemScreenSaver.cpp @@ -3,12 +3,17 @@ #include "components/VideoPlayerComponent.h" #endif #include "components/VideoVlcComponent.h" +#include "platform.h" #include "Renderer.h" #include "Settings.h" #include "SystemData.h" #include "Util.h" +#include "Log.h" +#include "views/ViewController.h" +#include "views/gamelist/IGameListView.h" +#include -#define FADE_TIME 3000 +#define FADE_TIME 300 #define SWAP_VIDEO_TIMEOUT 30000 SystemScreenSaver::SystemScreenSaver(Window* window) : @@ -18,19 +23,35 @@ SystemScreenSaver::SystemScreenSaver(Window* window) : mVideoCount(0), mState(STATE_INACTIVE), mOpacity(0.0f), - mTimer(0) + mTimer(0), + mSystemName(""), + mGameName(""), + mCurrentGame(NULL) { mWindow->setScreenSaver(this); + std::string path = getTitleFolder(); + if(!boost::filesystem::exists(path)) + boost::filesystem::create_directory(path); + srand((unsigned int)time(NULL)); } SystemScreenSaver::~SystemScreenSaver() { + // Delete subtitle file, if existing + remove(getTitlePath().c_str()); + mCurrentGame = NULL; delete mVideoScreensaver; } bool SystemScreenSaver::allowSleep() { - return false; + //return false; + return (mVideoScreensaver == NULL); +} + +bool SystemScreenSaver::isScreenSaverActive() +{ + return (mState != STATE_INACTIVE); } void SystemScreenSaver::startScreenSaver() @@ -42,34 +63,50 @@ void SystemScreenSaver::startScreenSaver() mOpacity = 0.0f; // Load a random video - std::string path; + std::string path = ""; pickRandomVideo(path); - if (!path.empty()) + + int retry = 200; + while(retry > 0 && ((path.empty() || !boost::filesystem::exists(path)) || mCurrentGame == NULL)) { - // Create the correct type of video component + retry--; + pickRandomVideo(path); + } + + if (!path.empty() && boost::filesystem::exists(path)) + { + // Create the correct type of video component + #ifdef _RPI_ - if (Settings::getInstance()->getBool("VideoOmxPlayer")) - mVideoScreensaver = new VideoPlayerComponent(mWindow); + if (Settings::getInstance()->getBool("ScreenSaverOmxPlayer")) + mVideoScreensaver = new VideoPlayerComponent(mWindow, getTitlePath()); else - mVideoScreensaver = new VideoVlcComponent(mWindow); + mVideoScreensaver = new VideoVlcComponent(mWindow, getTitlePath()); #else - mVideoScreensaver = new VideoVlcComponent(mWindow); + mVideoScreensaver = new VideoVlcComponent(mWindow, getTitlePath()); #endif + mVideoScreensaver->setOrigin(0.5f, 0.5f); + mVideoScreensaver->setPosition(Renderer::getScreenWidth()/2, Renderer::getScreenHeight()/2); - mVideoScreensaver->setOrigin(0.0f, 0.0f); - mVideoScreensaver->setPosition(0.0f, 0.0f); - mVideoScreensaver->setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); + if (Settings::getInstance()->getBool("StretchVideoOnScreenSaver")) + { + mVideoScreensaver->setResize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); + } + else + { + mVideoScreensaver->setMaxSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); + } mVideoScreensaver->setVideo(path); + mVideoScreensaver->setScreensaverMode(true); mVideoScreensaver->onShow(); mTimer = 0; - } - else - { - // No videos. Just use a standard screensaver - mState = STATE_SCREENSAVER_ACTIVE; + return; } } + // No videos. Just use a standard screensaver + mState = STATE_SCREENSAVER_ACTIVE; + mCurrentGame = NULL; } void SystemScreenSaver::stopScreenSaver() @@ -81,17 +118,18 @@ void SystemScreenSaver::stopScreenSaver() void SystemScreenSaver::renderScreenSaver() { - if (mVideoScreensaver) + if (mVideoScreensaver && Settings::getInstance()->getString("ScreenSaverBehavior") == "random video") { + // Render black background + Renderer::setMatrix(Eigen::Affine3f::Identity()); + Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), (unsigned char)(255)); + // Only render the video if the state requires it if ((int)mState >= STATE_FADE_IN_VIDEO) { Eigen::Affine3f transform = Eigen::Affine3f::Identity(); mVideoScreensaver->render(transform); } - // Handle any fade - Renderer::setMatrix(Eigen::Affine3f::Identity()); - Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), (unsigned char)(mOpacity * 255)); } else if (mState != STATE_INACTIVE) { @@ -136,9 +174,9 @@ void SystemScreenSaver::countVideos() void SystemScreenSaver::pickRandomVideo(std::string& path) { countVideos(); + mCurrentGame = NULL; if (mVideoCount > 0) { - srand((unsigned int)time(NULL)); int video = (int)(((float)rand() / float(RAND_MAX)) * (float)mVideoCount); std::vector:: iterator it; @@ -166,6 +204,48 @@ void SystemScreenSaver::pickRandomVideo(std::string& path) { // Yes. Resolve to a full path path = resolvePath(videoNode.text().get(), (*it)->getStartPath(), true).generic_string(); + mSystemName = (*it)->getFullName(); + mGameName = fileNode.child("name").text().get(); + + // getting corresponding FileData + + // try the easy way. Should work for the majority of cases, unless in subfolders + FileData* rootFileData = (*it)->getRootFolder(); + std::string gamePath = resolvePath(fileNode.child("path").text().get(), (*it)->getStartPath(), false).string(); + + std::string shortPath = gamePath; + shortPath = shortPath.replace(0, (*it)->getStartPath().length()+1, ""); + + const std::unordered_map& children = rootFileData->getChildrenByFilename(); + std::unordered_map::const_iterator screenSaverGame = children.find(shortPath); + + if (screenSaverGame != children.end()) + { + // Found the corresponding FileData + mCurrentGame = screenSaverGame->second; + } + else + { + // Couldn't find FileData. Going for the full iteration. + // iterate on children + FileType type = GAME; + std::vector allFiles = rootFileData->getFilesRecursive(type); + std::vector::iterator itf; // declare an iterator to a vector of strings + + int i = 0; + for(itf=allFiles.begin() ; itf < allFiles.end(); itf++,i++ ) { + if ((*itf)->getPath() == gamePath) + { + mCurrentGame = (*itf); + break; + } + } + } + + // end of getting FileData + if (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never") + writeSubtitle(mGameName.c_str(), mSystemName.c_str(), + (Settings::getInstance()->getString("ScreenSaverGameInfo") == "always")); return; } } @@ -205,9 +285,7 @@ void SystemScreenSaver::update(int deltaTime) mTimer += deltaTime; if (mTimer > SWAP_VIDEO_TIMEOUT) { - stopScreenSaver(); - startScreenSaver(); - mState = STATE_SCREENSAVER_ACTIVE; + nextVideo(); } } @@ -215,3 +293,26 @@ void SystemScreenSaver::update(int deltaTime) if (mVideoScreensaver) mVideoScreensaver->update(deltaTime); } + +void SystemScreenSaver::nextVideo() { + stopScreenSaver(); + startScreenSaver(); + mState = STATE_SCREENSAVER_ACTIVE; +} + +FileData* SystemScreenSaver::getCurrentGame() +{ + return mCurrentGame; +} + +void SystemScreenSaver::launchGame() +{ + // 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); + } +} \ No newline at end of file diff --git a/es-app/src/SystemScreenSaver.h b/es-app/src/SystemScreenSaver.h index 2e693e596..55d99c161 100644 --- a/es-app/src/SystemScreenSaver.h +++ b/es-app/src/SystemScreenSaver.h @@ -13,14 +13,21 @@ public: virtual void startScreenSaver(); virtual void stopScreenSaver(); + virtual void nextVideo(); virtual void renderScreenSaver(); virtual bool allowSleep(); virtual void update(int deltaTime); + virtual bool isScreenSaverActive(); + + virtual FileData* getCurrentGame(); + virtual void launchGame(); private: void countVideos(); void pickRandomVideo(std::string& path); + void input(InputConfig* config, Input input); + enum STATE { STATE_INACTIVE, STATE_FADE_OUT_WINDOW, @@ -36,4 +43,7 @@ private: STATE mState; float mOpacity; int mTimer; + FileData* mCurrentGame; + std::string mGameName; + std::string mSystemName; }; diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 3d3e8d3a2..43ced8330 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -6,6 +6,7 @@ #include "Settings.h" #include "guis/GuiMsgBox.h" #include "guis/GuiSettings.h" +#include "guis/GuiScreensaverOptions.h" #include "guis/GuiScraperStart.h" #include "guis/GuiDetectDevice.h" #include "views/ViewController.h" @@ -98,6 +99,30 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN s->addWithLabel("ENABLE NAVIGATION SOUNDS", sounds_enabled); s->addSaveFunc([sounds_enabled] { Settings::getInstance()->setBool("EnableSounds", sounds_enabled->getState()); }); + auto video_audio = std::make_shared(mWindow); + video_audio->setState(Settings::getInstance()->getBool("VideoAudio")); + s->addWithLabel("ENABLE VIDEO AUDIO", video_audio); + s->addSaveFunc([video_audio] { Settings::getInstance()->setBool("VideoAudio", video_audio->getState()); }); + +#ifdef _RPI_ + // OMX player Audio Device + auto omx_audio_dev = std::make_shared< OptionListComponent >(mWindow, "OMX PLAYER AUDIO DEVICE", false); + std::vector devices; + devices.push_back("local"); + devices.push_back("hdmi"); + devices.push_back("both"); + // USB audio + devices.push_back("alsa:hw:0,0"); + devices.push_back("alsa:hw:1,0"); + for (auto it = devices.begin(); it != devices.end(); it++) + omx_audio_dev->add(*it, *it, Settings::getInstance()->getString("OMXAudioDev") == *it); + s->addWithLabel("OMX PLAYER AUDIO DEVICE", omx_audio_dev); + s->addSaveFunc([omx_audio_dev] { + if (Settings::getInstance()->getString("OMXAudioDev") != omx_audio_dev->getSelected()) + Settings::getInstance()->setString("OMXAudioDev", omx_audio_dev->getSelected()); + }); +#endif + mWindow->pushGui(s); }); @@ -112,7 +137,7 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN s->addSaveFunc([screensaver_time] { Settings::getInstance()->setInt("ScreenSaverTime", (int)round(screensaver_time->getValue()) * (1000 * 60)); }); // screensaver behavior - auto screensaver_behavior = std::make_shared< OptionListComponent >(mWindow, "STYLE", false); + auto screensaver_behavior = std::make_shared< OptionListComponent >(mWindow, "SCREENSAVER BEHAVIOR", false); std::vector screensavers; screensavers.push_back("dim"); screensavers.push_back("black"); @@ -120,19 +145,24 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN for(auto it = screensavers.begin(); it != screensavers.end(); it++) screensaver_behavior->add(*it, *it, Settings::getInstance()->getString("ScreenSaverBehavior") == *it); s->addWithLabel("SCREENSAVER BEHAVIOR", screensaver_behavior); - s->addSaveFunc([screensaver_behavior] { Settings::getInstance()->setString("ScreenSaverBehavior", screensaver_behavior->getSelected()); }); + s->addSaveFunc([this, screensaver_behavior] { + if (Settings::getInstance()->getString("ScreenSaverBehavior") != "random video" && screensaver_behavior->getSelected() == "random video") { + // if before it wasn't risky but now there's a risk of problems, show warning + mWindow->pushGui(new GuiMsgBox(mWindow, + "The \"Random Video\" screensaver shows videos from your gamelist.\n\nIf you do not have videos, or if in several consecutive attempts the games it selects don't have videos it will default to black.\n\nMore options in the \"UI Settings\" > \"Video Screensaver\" menu.", + "OK", [] { return; })); + } + Settings::getInstance()->setString("ScreenSaverBehavior", screensaver_behavior->getSelected()); + }); - // framerate - auto framerate = std::make_shared(mWindow); - framerate->setState(Settings::getInstance()->getBool("DrawFramerate")); - s->addWithLabel("SHOW FRAMERATE", framerate); - s->addSaveFunc([framerate] { Settings::getInstance()->setBool("DrawFramerate", framerate->getState()); }); + ComponentListRow row; - // show help - auto show_help = std::make_shared(mWindow); - show_help->setState(Settings::getInstance()->getBool("ShowHelpPrompts")); - s->addWithLabel("ON-SCREEN HELP", show_help); - s->addSaveFunc([show_help] { Settings::getInstance()->setBool("ShowHelpPrompts", show_help->getState()); }); + // show filtered menu + row.elements.clear(); + row.addElement(std::make_shared(mWindow, "VIDEO SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(makeArrow(mWindow), false); + row.makeAcceptInputHandler(std::bind(&GuiMenu::openScreensaverOptions, this)); + s->addRow(row); // quick system select (left/right in game list view) auto quick_sys_select = std::make_shared(mWindow); @@ -198,52 +228,12 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN if (needReload) ViewController::get()->reloadAll(); }); - mWindow->pushGui(s); - }); - addEntry("VIDEO PLAYER SETTINGS", 0x777777FF, true, - [this] { - auto s = new GuiSettings(mWindow, "VIDEO PLAYER SETTINGS"); - -#ifdef _RPI_ - // Video Player - VideoOmxPlayer - auto omx_player = std::make_shared(mWindow); - omx_player->setState(Settings::getInstance()->getBool("VideoOmxPlayer")); - s->addWithLabel("USE OMX VIDEO PLAYER (HW ACCELERATED)", omx_player); - s->addSaveFunc([omx_player] - { - // need to reload all views to re-create the right video components - bool needReload = false; - if(Settings::getInstance()->getBool("VideoOmxPlayer") != omx_player->getState()) - needReload = true; - - Settings::getInstance()->setBool("VideoOmxPlayer", omx_player->getState()); - - if(needReload) - ViewController::get()->reloadAll(); - }); - - // OMX player Audio Device - auto omx_audio_dev = std::make_shared< OptionListComponent >(mWindow, "OMX PLAYER AUDIO DEVICE", false); - std::vector devices; - devices.push_back("local"); - devices.push_back("hdmi"); - devices.push_back("both"); - // USB audio - devices.push_back("alsa:hw:0,0"); - devices.push_back("alsa:hw:1,0"); - for (auto it = devices.begin(); it != devices.end(); it++) - omx_audio_dev->add(*it, *it, Settings::getInstance()->getString("OMXAudioDev") == *it); - s->addWithLabel("OMX PLAYER AUDIO DEVICE", omx_audio_dev); - s->addSaveFunc([omx_audio_dev] { - if (Settings::getInstance()->getString("OMXAudioDev") != omx_audio_dev->getSelected()) - Settings::getInstance()->setString("OMXAudioDev", omx_audio_dev->getSelected()); - }); -#endif - auto video_audio = std::make_shared(mWindow); - video_audio->setState(Settings::getInstance()->getBool("VideoAudio")); - s->addWithLabel("ENABLE VIDEO AUDIO", video_audio); - s->addSaveFunc([video_audio] { Settings::getInstance()->setBool("VideoAudio", video_audio->getState()); }); + // show help + auto show_help = std::make_shared(mWindow); + show_help->setState(Settings::getInstance()->getBool("ShowHelpPrompts")); + s->addWithLabel("ON-SCREEN HELP", show_help); + s->addSaveFunc([show_help] { Settings::getInstance()->setBool("ShowHelpPrompts", show_help->getState()); }); mWindow->pushGui(s); }); @@ -263,12 +253,39 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN s->addWithLabel("PARSE GAMESLISTS ONLY", parse_gamelists); s->addSaveFunc([parse_gamelists] { Settings::getInstance()->setBool("ParseGamelistOnly", parse_gamelists->getState()); }); +#ifdef _RPI_ + // Video Player - VideoOmxPlayer + auto omx_player = std::make_shared(mWindow); + omx_player->setState(Settings::getInstance()->getBool("VideoOmxPlayer")); + s->addWithLabel("USE OMX PLAYER (HW ACCELERATED)", omx_player); + s->addSaveFunc([omx_player] + { + // need to reload all views to re-create the right video components + bool needReload = false; + if(Settings::getInstance()->getBool("VideoOmxPlayer") != omx_player->getState()) + needReload = true; + + Settings::getInstance()->setBool("VideoOmxPlayer", omx_player->getState()); + + if(needReload) + ViewController::get()->reloadAll(); + }); + +#endif + // maximum vram auto max_vram = std::make_shared(mWindow, 0.f, 1000.f, 10.f, "Mb"); max_vram->setValue((float)(Settings::getInstance()->getInt("MaxVRAM"))); s->addWithLabel("VRAM LIMIT", max_vram); s->addSaveFunc([max_vram] { Settings::getInstance()->setInt("MaxVRAM", (int)round(max_vram->getValue())); }); + // framerate + auto framerate = std::make_shared(mWindow); + framerate->setState(Settings::getInstance()->getBool("DrawFramerate")); + s->addWithLabel("SHOW FRAMERATE", framerate); + s->addSaveFunc([framerate] { Settings::getInstance()->setBool("DrawFramerate", framerate->getState()); }); + + mWindow->pushGui(s); }); @@ -351,6 +368,11 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, Renderer::getScreenHeight() * 0.15f); } +void GuiMenu::openScreensaverOptions() { + GuiScreensaverOptions* ggf = new GuiScreensaverOptions(mWindow, "VIDEO SCREENSAVER"); + mWindow->pushGui(ggf); +} + void GuiMenu::onSizeChanged() { mVersion.setSize(mSize.x(), 0); diff --git a/es-app/src/guis/GuiMenu.h b/es-app/src/guis/GuiMenu.h index 19fa1d334..42862d24a 100644 --- a/es-app/src/guis/GuiMenu.h +++ b/es-app/src/guis/GuiMenu.h @@ -16,7 +16,7 @@ public: private: void addEntry(const char* name, unsigned int color, bool add_arrow, const std::function& func); - + void openScreensaverOptions(); MenuComponent mMenu; TextComponent mVersion; }; diff --git a/es-app/src/guis/GuiScreensaverOptions.cpp b/es-app/src/guis/GuiScreensaverOptions.cpp new file mode 100644 index 000000000..51eb8fc50 --- /dev/null +++ b/es-app/src/guis/GuiScreensaverOptions.cpp @@ -0,0 +1,125 @@ +#include "guis/GuiScreensaverOptions.h" +#include "Window.h" +#include "Settings.h" +#include "views/ViewController.h" + +#include "components/ButtonComponent.h" +#include "components/SwitchComponent.h" +#include "components/SliderComponent.h" +#include "components/TextComponent.h" +#include "components/OptionListComponent.h" +#include "components/MenuComponent.h" +#include "guis/GuiMsgBox.h" + +GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const char* title) : GuiComponent(window), mMenu(window, title) +{ + addChild(&mMenu); + +#ifdef _RPI_ + auto ss_omx = std::make_shared(mWindow); + ss_omx->setState(Settings::getInstance()->getBool("ScreenSaverOmxPlayer")); + addWithLabel("USE OMX PLAYER FOR SCREENSAVER", ss_omx); + addSaveFunc([ss_omx, this] { Settings::getInstance()->setBool("ScreenSaverOmxPlayer", ss_omx->getState()); }); +#endif + + // Allow ScreenSaver Controls - ScreenSaverControls + auto ss_controls = std::make_shared(mWindow); + ss_controls->setState(Settings::getInstance()->getBool("ScreenSaverControls")); + addWithLabel("SCREENSAVER CONTROLS", ss_controls); + addSaveFunc([ss_controls] { Settings::getInstance()->setBool("ScreenSaverControls", ss_controls->getState()); }); + + // Render Video Game Name as subtitles + auto ss_info = std::make_shared< OptionListComponent >(mWindow, "SHOW GAME INFO", false); + std::vector info_type; + info_type.push_back("always"); + info_type.push_back("start & end"); + info_type.push_back("never"); + for(auto it = info_type.begin(); it != info_type.end(); it++) + ss_info->add(*it, *it, Settings::getInstance()->getString("ScreenSaverGameInfo") == *it); + addWithLabel("SHOW GAME INFO ON SCREENSAVER", ss_info); + addSaveFunc([ss_info, this] { Settings::getInstance()->setString("ScreenSaverGameInfo", ss_info->getSelected()); }); + +#ifndef _RPI_ + auto captions_compatibility = std::make_shared(mWindow); + captions_compatibility->setState(Settings::getInstance()->getBool("CaptionsCompatibility")); + addWithLabel("USE COMPATIBLE LOW RESOLUTION FOR CAPTIONS", captions_compatibility); + addSaveFunc([captions_compatibility] { Settings::getInstance()->setBool("CaptionsCompatibility", captions_compatibility->getState()); }); +#endif + + auto stretch_screensaver = std::make_shared(mWindow); + stretch_screensaver->setState(Settings::getInstance()->getBool("StretchVideoOnScreenSaver")); + addWithLabel("STRETCH VIDEO ON SCREENSAVER", stretch_screensaver); + addSaveFunc([stretch_screensaver] { Settings::getInstance()->setBool("StretchVideoOnScreenSaver", stretch_screensaver->getState()); }); + + mMenu.addButton("BACK", "go back", [this] { delete this; }); + + setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); + mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f); +} + +GuiScreensaverOptions::~GuiScreensaverOptions() +{ + save(); +} + +void GuiScreensaverOptions::save() +{ + if(!mSaveFuncs.size()) + return; + +#ifdef _RPI_ + bool startingStatusNotRisky = (Settings::getInstance()->getString("ScreenSaverGameInfo") == "never" || !Settings::getInstance()->getBool("ScreenSaverOmxPlayer")); +#endif + + for(auto it = mSaveFuncs.begin(); it != mSaveFuncs.end(); it++) + (*it)(); + + Settings::getInstance()->saveFile(); + +#ifdef _RPI_ + bool endStatusRisky = (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never" && Settings::getInstance()->getBool("ScreenSaverOmxPlayer")); + if (startingStatusNotRisky && endStatusRisky) { + // if before it wasn't risky but now there's a risk of problems, show warning + mWindow->pushGui(new GuiMsgBox(mWindow, + "Using OMX Player and displaying Game Info may result in the video flickering in some TV modes. If that happens, consider:\n\n• Disabling the \"Show Game Info\" option;\n• Disabling \"Overscan\" on the Pi configuration menu might help:\nRetroPie > Raspi-Config > Advanced Options > Overscan > \"No\".\n• Disabling the use of OMX Player for the screensaver.", + "GOT IT!", [] { return; })); + } +#endif +} + +bool GuiScreensaverOptions::input(InputConfig* config, Input input) +{ + if(config->isMappedTo("b", input) && input.value != 0) + { + delete this; + return true; + } + + if(config->isMappedTo("start", input) && input.value != 0) + { + // close everything + Window* window = mWindow; + while(window->peekGui() && window->peekGui() != ViewController::get()) + delete window->peekGui(); + return true; + } + + return GuiComponent::input(config, input); +} + +HelpStyle GuiScreensaverOptions::getHelpStyle() +{ + HelpStyle style = HelpStyle(); + style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system"); + return style; +} + +std::vector GuiScreensaverOptions::getHelpPrompts() +{ + std::vector prompts = mMenu.getHelpPrompts(); + + prompts.push_back(HelpPrompt("b", "back")); + prompts.push_back(HelpPrompt("start", "close")); + + return prompts; +} diff --git a/es-app/src/guis/GuiScreensaverOptions.h b/es-app/src/guis/GuiScreensaverOptions.h new file mode 100644 index 000000000..e90dceee5 --- /dev/null +++ b/es-app/src/guis/GuiScreensaverOptions.h @@ -0,0 +1,24 @@ +#include "GuiComponent.h" +#include "components/MenuComponent.h" +#include "SystemData.h" + +// This is just a really simple template for a GUI that calls some save functions when closed. +class GuiScreensaverOptions : public GuiComponent +{ +public: + GuiScreensaverOptions(Window* window, const char* title); + virtual ~GuiScreensaverOptions(); // just calls save(); + + void save(); + inline void addRow(const ComponentListRow& row) { mMenu.addRow(row); }; + inline void addWithLabel(const std::string& label, const std::shared_ptr& comp) { mMenu.addWithLabel(label, comp); }; + inline void addSaveFunc(const std::function& func) { mSaveFuncs.push_back(func); }; + + bool input(InputConfig* config, Input input) override; + std::vector getHelpPrompts() override; + HelpStyle getHelpStyle() override; + +private: + MenuComponent mMenu; + std::vector< std::function > mSaveFuncs; +}; \ No newline at end of file diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index d31df73ef..3252c8a5f 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -158,6 +158,12 @@ bool SystemView::input(InputConfig* config, Input input) config->isMappedTo("up", input) || config->isMappedTo("down", input)) listInput(0); + if(config->isMappedTo("select", input) && Settings::getInstance()->getBool("ScreenSaverControls")) + { + mWindow->startScreenSaver(); + mWindow->renderScreenSaver(); + return true; + } } return GuiComponent::input(config, input); @@ -348,6 +354,10 @@ std::vector SystemView::getHelpPrompts() prompts.push_back(HelpPrompt("left/right", "choose")); prompts.push_back(HelpPrompt("a", "select")); prompts.push_back(HelpPrompt("x", "random")); + + if (Settings::getInstance()->getBool("ScreenSaverControls")) + prompts.push_back(HelpPrompt("select", "launch screensaver")); + return prompts; } diff --git a/es-app/src/views/gamelist/BasicGameListView.h b/es-app/src/views/gamelist/BasicGameListView.h index a1bf0b5d6..00998cd8c 100644 --- a/es-app/src/views/gamelist/BasicGameListView.h +++ b/es-app/src/views/gamelist/BasicGameListView.h @@ -19,10 +19,10 @@ public: virtual const char* getName() const override { return "basic"; } virtual std::vector getHelpPrompts() override; + virtual void launch(FileData* game) override; protected: virtual void populateList(const std::vector& files) override; - virtual void launch(FileData* game) override; virtual void remove(FileData* game) override; TextListComponent mList; diff --git a/es-app/src/views/gamelist/DetailedGameListView.h b/es-app/src/views/gamelist/DetailedGameListView.h index 30396e04c..3b470d34e 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.h +++ b/es-app/src/views/gamelist/DetailedGameListView.h @@ -14,7 +14,6 @@ public: virtual const char* getName() const override { return "detailed"; } -protected: virtual void launch(FileData* game) override; private: diff --git a/es-app/src/views/gamelist/GridGameListView.h b/es-app/src/views/gamelist/GridGameListView.h index 81bf45585..5451410bd 100644 --- a/es-app/src/views/gamelist/GridGameListView.h +++ b/es-app/src/views/gamelist/GridGameListView.h @@ -20,10 +20,10 @@ public: virtual const char* getName() const override { return "grid"; } virtual std::vector getHelpPrompts() override; + virtual void launch(FileData* game) override; protected: virtual void populateList(const std::vector& files) override; - virtual void launch(FileData* game) override; ImageGridComponent mGrid; }; diff --git a/es-app/src/views/gamelist/IGameListView.h b/es-app/src/views/gamelist/IGameListView.h index 5a44fbbee..eb7610790 100644 --- a/es-app/src/views/gamelist/IGameListView.h +++ b/es-app/src/views/gamelist/IGameListView.h @@ -35,6 +35,7 @@ public: virtual void remove(FileData* game) = 0; virtual const char* getName() const = 0; + virtual void launch(FileData* game) = 0; virtual HelpStyle getHelpStyle() override; diff --git a/es-app/src/views/gamelist/ISimpleGameListView.h b/es-app/src/views/gamelist/ISimpleGameListView.h index ff405b42a..c629fc960 100644 --- a/es-app/src/views/gamelist/ISimpleGameListView.h +++ b/es-app/src/views/gamelist/ISimpleGameListView.h @@ -23,10 +23,10 @@ public: virtual void setCursor(FileData*) = 0; virtual bool input(InputConfig* config, Input input) override; + virtual void launch(FileData* game) = 0; protected: virtual void populateList(const std::vector& files) = 0; - virtual void launch(FileData* game) = 0; TextComponent mHeaderText; ImageComponent mHeaderImage; diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index 9967497aa..09c6bbe7c 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -29,11 +29,11 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) : // Create the correct type of video window #ifdef _RPI_ if (Settings::getInstance()->getBool("VideoOmxPlayer")) - mVideo = new VideoPlayerComponent(window); + mVideo = new VideoPlayerComponent(window, ""); else - mVideo = new VideoVlcComponent(window); + mVideo = new VideoVlcComponent(window, getTitlePath()); #else - mVideo = new VideoVlcComponent(window); + mVideo = new VideoVlcComponent(window, getTitlePath()); #endif mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y()); @@ -227,6 +227,8 @@ void VideoGameListView::updateInfoPanel() { FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected(); + boost::filesystem::remove(getTitlePath().c_str()); + bool fadingOut; if(file == NULL) { diff --git a/es-app/src/views/gamelist/VideoGameListView.h b/es-app/src/views/gamelist/VideoGameListView.h index 09b12f602..6a14b1570 100644 --- a/es-app/src/views/gamelist/VideoGameListView.h +++ b/es-app/src/views/gamelist/VideoGameListView.h @@ -17,10 +17,9 @@ public: virtual void onThemeChanged(const std::shared_ptr& theme) override; virtual const char* getName() const override { return "video"; } + virtual void launch(FileData* game) override; protected: - virtual void launch(FileData* game) override; - virtual void update(int deltaTime) override; private: diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index ac74ad68e..3f88c8758 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -77,10 +77,22 @@ void Settings::setDefaults() mStringMap["Scraper"] = "TheGamesDB"; mStringMap["GamelistViewStyle"] = "automatic"; + mBoolMap["ScreenSaverControls"] = true; + mStringMap["ScreenSaverGameInfo"] = "never"; + mBoolMap["StretchVideoOnScreenSaver"] = false; + // This setting only applies to raspberry pi but set it for all platforms so // we don't get a warning if we encounter it on a different platform mBoolMap["VideoOmxPlayer"] = false; + #ifdef _RPI_ + // we're defaulting to OMX Player for full screen video on the Pi + mBoolMap["ScreenSaverOmxPlayer"] = true; + #else + mBoolMap["ScreenSaverOmxPlayer"] = false; + #endif + mBoolMap["VideoAudio"] = true; + mBoolMap["CaptionsCompatibility"] = true; // Audio out device for Video playback using OMX player. mStringMap["OMXAudioDev"] = "both"; diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index 8bf64fd73..4101df0df 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -115,13 +115,34 @@ void Window::textInput(const char* text) void Window::input(InputConfig* config, Input input) { - if (mRenderScreenSaver) - { - mRenderScreenSaver = false; - - // Tell the GUI components the screensaver has stopped - for(auto i = mGuiStack.begin(); i != mGuiStack.end(); i++) - (*i)->onScreenSaverDeactivate(); + if (mScreenSaver) { + if(mScreenSaver->isScreenSaverActive() && Settings::getInstance()->getBool("ScreenSaverControls") && + (Settings::getInstance()->getString("ScreenSaverBehavior") == "random video")) + { + if(mScreenSaver->getCurrentGame() != NULL && (config->isMappedTo("right", input) || config->isMappedTo("start", input) || config->isMappedTo("select", input))) + { + if(config->isMappedTo("right", input) || config->isMappedTo("select", input)) + { + if (input.value != 0) { + // handle screensaver control + mScreenSaver->nextVideo(); + } + return; + } + else if(config->isMappedTo("start", input)) + { + // launch game! + cancelScreenSaver(); + mScreenSaver->launchGame(); + // to force handling the wake up process + mSleeping = true; + } + } + else if(input.value != 0) + { + return; + } + } } if(mSleeping) @@ -377,35 +398,35 @@ bool Window::isProcessing() return count_if(mGuiStack.begin(), mGuiStack.end(), [](GuiComponent* c) { return c->isProcessing(); }) > 0; } -void Window::startScreenSaver() - { - if (mScreenSaver && !mRenderScreenSaver) - { - // Tell the GUI components the screensaver is starting - for(auto i = mGuiStack.begin(); i != mGuiStack.end(); i++) - (*i)->onScreenSaverActivate(); - - mScreenSaver->startScreenSaver(); - mRenderScreenSaver = true; - } - } - - void Window::cancelScreenSaver() - { - if (mScreenSaver && mRenderScreenSaver) - { - mScreenSaver->stopScreenSaver(); - mRenderScreenSaver = false; - - // Tell the GUI components the screensaver has stopped - for(auto i = mGuiStack.begin(); i != mGuiStack.end(); i++) - (*i)->onScreenSaverDeactivate(); - } - } - - void Window::renderScreenSaver() - { - if (mScreenSaver) - mScreenSaver->renderScreenSaver(); +void Window::startScreenSaver() + { + if (mScreenSaver && !mRenderScreenSaver) + { + // Tell the GUI components the screensaver is starting + for(auto i = mGuiStack.begin(); i != mGuiStack.end(); i++) + (*i)->onScreenSaverActivate(); + + mScreenSaver->startScreenSaver(); + mRenderScreenSaver = true; + } } - + + void Window::cancelScreenSaver() + { + if (mScreenSaver && mRenderScreenSaver) + { + mScreenSaver->stopScreenSaver(); + mRenderScreenSaver = false; + + // Tell the GUI components the screensaver has stopped + for(auto i = mGuiStack.begin(); i != mGuiStack.end(); i++) + (*i)->onScreenSaverDeactivate(); + } + } + + void Window::renderScreenSaver() + { + if (mScreenSaver) + mScreenSaver->renderScreenSaver(); + } + diff --git a/es-core/src/Window.h b/es-core/src/Window.h index d1e6bdd39..7b92605e2 100644 --- a/es-core/src/Window.h +++ b/es-core/src/Window.h @@ -5,6 +5,7 @@ #include "resources/Font.h" #include "InputManager.h" +class FileData; class HelpComponent; class ImageComponent; @@ -15,9 +16,13 @@ public: public: virtual void startScreenSaver() = 0; virtual void stopScreenSaver() = 0; + virtual void nextVideo() = 0; virtual void renderScreenSaver() = 0; virtual bool allowSleep() = 0; virtual void update(int deltaTime) = 0; + virtual bool isScreenSaverActive() = 0; + virtual FileData* getCurrentGame() = 0; + virtual void launchGame() = 0; }; Window(); @@ -49,16 +54,17 @@ public: void setScreenSaver(ScreenSaver* screenSaver) { mScreenSaver = screenSaver; } + void startScreenSaver(); + void cancelScreenSaver(); + void renderScreenSaver(); + private: void onSleep(); void onWake(); // Returns true if at least one component on the stack is processing bool isProcessing(); - void renderScreenSaver(); - void startScreenSaver(); - void cancelScreenSaver(); - + HelpComponent* mHelp; ImageComponent* mBackgroundOverlay; ScreenSaver* mScreenSaver; @@ -71,7 +77,6 @@ private: int mFrameTimeElapsed; int mFrameCountElapsed; int mAverageDeltaTime; - bool mRenderScreenSaver; std::unique_ptr mFrameDataText; diff --git a/es-core/src/components/VideoComponent.cpp b/es-core/src/components/VideoComponent.cpp index 0edae8deb..e015d34ce 100644 --- a/es-core/src/components/VideoComponent.cpp +++ b/es-core/src/components/VideoComponent.cpp @@ -9,6 +9,45 @@ #define FADE_TIME_MS 200 +std::string getTitlePath() { + std::string titleFolder = getTitleFolder(); + return titleFolder + "last_title.srt"; +} + +std::string getTitleFolder() { + std::string home = getHomePath(); + return home + "/.emulationstation/tmp/"; +} + +void writeSubtitle(const char* gameName, const char* systemName, bool always) +{ + FILE* file = fopen(getTitlePath().c_str(), "w"); + if (always) { + fprintf(file, "1\n00:00:01,000 --> 00:00:30,000\n"); + } + else + { + fprintf(file, "1\n00:00:01,000 --> 00:00:08,000\n"); + } + fprintf(file, "%s\n", gameName); + fprintf(file, "%s\n\n", systemName); + + if (!always) { + fprintf(file, "2\n00:00:26,000 --> 00:00:30,000\n"); + fprintf(file, "%s\n", gameName); + fprintf(file, "%s\n", systemName); + } + + fflush(file); + fclose(file); + file = NULL; +} + +void VideoComponent::setScreensaverMode(bool isScreensaver) +{ + mScreensaverMode = isScreensaver; +} + VideoComponent::VideoComponent(Window* window) : GuiComponent(window), mStaticImage(window), @@ -19,6 +58,7 @@ VideoComponent::VideoComponent(Window* window) : mShowing(false), mScreensaverActive(false), mDisable(false), + mScreensaverMode(false), mTargetIsMax(false), mOrigin(0, 0), mTargetSize(0, 0) @@ -31,12 +71,17 @@ VideoComponent::VideoComponent(Window* window) : topWindow(false); } + std::string path = getTitleFolder(); + if(!boost::filesystem::exists(path)) + boost::filesystem::create_directory(path); } VideoComponent::~VideoComponent() { // Stop any currently running video stopVideo(); + // Delete subtitle file, if existing + remove(getTitlePath().c_str()); } void VideoComponent::setOrigin(float originX, float originY) diff --git a/es-core/src/components/VideoComponent.h b/es-core/src/components/VideoComponent.h index e172a1172..414c51144 100644 --- a/es-core/src/components/VideoComponent.h +++ b/es-core/src/components/VideoComponent.h @@ -12,6 +12,10 @@ #include #include +std::string getTitlePath(); +std::string getTitleFolder(); +void writeSubtitle(const char* gameName, const char* systemName, bool always); + class VideoComponent : public GuiComponent { // Structure that groups together the configuration of the video component @@ -35,6 +39,9 @@ public: // Configures the component to show the default video void setDefaultVideo(); + // sets whether it's going to render in screensaver mode + void setScreensaverMode(bool isScreensaver); + virtual void onShow() override; virtual void onHide() override; virtual void onScreenSaverActivate() override; @@ -107,6 +114,7 @@ protected: bool mShowing; bool mDisable; bool mScreensaverActive; + bool mScreensaverMode; bool mTargetIsMax; Configuration mConfig; diff --git a/es-core/src/components/VideoPlayerComponent.cpp b/es-core/src/components/VideoPlayerComponent.cpp index bd9b68087..33599b906 100644 --- a/es-core/src/components/VideoPlayerComponent.cpp +++ b/es-core/src/components/VideoPlayerComponent.cpp @@ -1,5 +1,6 @@ #ifdef _RPI_ #include "components/VideoPlayerComponent.h" +#include #include "AudioManager.h" #include "Renderer.h" #include "ThemeData.h" @@ -11,9 +12,10 @@ #include #include -VideoPlayerComponent::VideoPlayerComponent(Window* window) : +VideoPlayerComponent::VideoPlayerComponent(Window* window, std::string path) : VideoComponent(window), - mPlayerPid(-1) + mPlayerPid(-1), + subtitlePath(path) { } @@ -47,7 +49,8 @@ void VideoPlayerComponent::setMaxSize(float width, float height) void VideoPlayerComponent::startVideo() { - if (!mIsPlaying) { + if (!mIsPlaying) + { mVideoWidth = 0; mVideoHeight = 0; @@ -59,8 +62,11 @@ void VideoPlayerComponent::startVideo() // Set the video that we are going to be playing so we don't attempt to restart it mPlayingVideoPath = mVideoPath; - // Disable AudioManager so video can play - AudioManager::getInstance()->deinit(); + // Disable AudioManager so video can play, in case we're requesting ALSA + if (boost::starts_with(Settings::getInstance()->getString("OMXAudioDev").c_str(), "alsa")) + { + AudioManager::getInstance()->deinit(); + } // Start the player process pid_t pid = fork(); @@ -89,7 +95,7 @@ void VideoPlayerComponent::startVideo() // We need to specify the layer of 10000 or above to ensure the video is displayed on top // of our SDL display - const char* argv[] = { "", "--layer", "10010", "--loop", "--no-osd", "--aspect-mode", "letterbox", "--vol", "0", "-o", "both","--win", buf, "-b", "", "", "", "", NULL }; + const char* argv[] = { "", "--layer", "10010", "--loop", "--no-osd", "--aspect-mode", "letterbox", "--vol", "0", "-o", "both","--win", buf, "--no-ghost-box", "", "", "", "", NULL }; // check if we want to mute the audio if (!Settings::getInstance()->getBool("VideoAudio")) @@ -97,16 +103,43 @@ void VideoPlayerComponent::startVideo() argv[8] = "-1000000"; } - // if we are rendering a video gamelist - if (!mTargetIsMax) + // test if there's a path for possible subtitles, meaning we're a screensaver video + if (!subtitlePath.empty()) { - argv[6] = "stretch"; + // if we are rendering a screensaver + + // check if we want to stretch the image + if (Settings::getInstance()->getBool("StretchVideoOnScreenSaver")) + { + argv[6] = "stretch"; + } + + if (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never") + { + // if we have chosen to render subtitles + argv[13] = "--subtitles"; + argv[14] = subtitlePath.c_str(); + argv[15] = mPlayingVideoPath.c_str(); + } + else + { + // if we have chosen NOT to render subtitles in the screensaver + argv[13] = mPlayingVideoPath.c_str(); + } + } + else + { + // if we are rendering a video gamelist + if (!mTargetIsMax) + { + argv[6] = "stretch"; + } + argv[13] = mPlayingVideoPath.c_str(); } argv[10] = Settings::getInstance()->getString("OMXAudioDev").c_str(); - argv[13] = mPlayingVideoPath.c_str(); - + //const char* argv[] = args; const char* env[] = { "LD_LIBRARY_PATH=/opt/vc/libs:/usr/lib/omxplayer", NULL }; // Redirect stdout @@ -142,7 +175,10 @@ void VideoPlayerComponent::stopVideo() kill(mPlayerPid, SIGKILL); waitpid(mPlayerPid, &status, WNOHANG); // Restart AudioManager - AudioManager::getInstance()->init(); + if (boost::starts_with(Settings::getInstance()->getString("OMXAudioDev").c_str(), "alsa")) + { + AudioManager::getInstance()->init(); + } mPlayerPid = -1; } } diff --git a/es-core/src/components/VideoPlayerComponent.h b/es-core/src/components/VideoPlayerComponent.h index 27a098115..effb5942b 100644 --- a/es-core/src/components/VideoPlayerComponent.h +++ b/es-core/src/components/VideoPlayerComponent.h @@ -12,7 +12,7 @@ void catch_child(int sig_num); class VideoPlayerComponent : public VideoComponent { public: - VideoPlayerComponent(Window* window); + VideoPlayerComponent(Window* window, std::string path); virtual ~VideoPlayerComponent(); void render(const Eigen::Affine3f& parentTrans) override; @@ -36,6 +36,7 @@ private: private: pid_t mPlayerPid; + std::string subtitlePath; }; #endif diff --git a/es-core/src/components/VideoVlcComponent.cpp b/es-core/src/components/VideoVlcComponent.cpp index 8adedf628..10c2118e5 100644 --- a/es-core/src/components/VideoVlcComponent.cpp +++ b/es-core/src/components/VideoVlcComponent.cpp @@ -11,26 +11,26 @@ libvlc_instance_t* VideoVlcComponent::mVLC = NULL; // VLC prepares to render a video frame. static void *lock(void *data, void **p_pixels) { - struct VideoContext *c = (struct VideoContext *)data; - SDL_LockMutex(c->mutex); - SDL_LockSurface(c->surface); + struct VideoContext *c = (struct VideoContext *)data; + SDL_LockMutex(c->mutex); + SDL_LockSurface(c->surface); *p_pixels = c->surface->pixels; - return NULL; // Picture identifier, not needed here. + return NULL; // Picture identifier, not needed here. } // VLC just rendered a video frame. static void unlock(void *data, void *id, void *const *p_pixels) { - struct VideoContext *c = (struct VideoContext *)data; - SDL_UnlockSurface(c->surface); - SDL_UnlockMutex(c->mutex); + struct VideoContext *c = (struct VideoContext *)data; + SDL_UnlockSurface(c->surface); + SDL_UnlockMutex(c->mutex); } // VLC wants to display a video frame. static void display(void *data, void *id) { - //Data to be displayed + //Data to be displayed } -VideoVlcComponent::VideoVlcComponent(Window* window) : +VideoVlcComponent::VideoVlcComponent(Window* window, std::string subtitles) : VideoComponent(window), mMediaPlayer(nullptr) { @@ -40,7 +40,7 @@ VideoVlcComponent::VideoVlcComponent(Window* window) : mTexture = TextureResource::get(""); // Make sure VLC has been initialised - setupVLC(); + setupVLC(subtitles); } VideoVlcComponent::~VideoVlcComponent() @@ -227,13 +227,27 @@ void VideoVlcComponent::freeContext() } } -void VideoVlcComponent::setupVLC() +void VideoVlcComponent::setupVLC(std::string subtitles) { // If VLC hasn't been initialised yet then do it now if (!mVLC) { - const char* args[] = { "--quiet" }; - mVLC = libvlc_new(sizeof(args) / sizeof(args[0]), args); + const char** args; + const char* newargs[] = { "--quiet", "--sub-file", subtitles.c_str() }; + const char* singleargs[] = { "--quiet" }; + int argslen = 0; + + if (!subtitles.empty()) + { + argslen = sizeof(newargs) / sizeof(newargs[0]); + args = newargs; + } + else + { + argslen = sizeof(singleargs) / sizeof(singleargs[0]); + args = singleargs; + } + mVLC = libvlc_new(argslen, args); } } @@ -292,10 +306,31 @@ void VideoVlcComponent::startVideo() // Make sure we found a valid video track if ((mVideoWidth > 0) && (mVideoHeight > 0)) { +#ifndef _RPI_ + if (mScreensaverMode) + { + if(!Settings::getInstance()->getBool("CaptionsCompatibility")) { + + Eigen::Vector2f resizeScale((Renderer::getScreenWidth() / mVideoWidth), (Renderer::getScreenHeight() / mVideoHeight)); + + if(resizeScale.x() < resizeScale.y()) + { + mVideoWidth *= resizeScale.x(); + mVideoHeight *= resizeScale.x(); + }else{ + mVideoWidth *= resizeScale.y(); + mVideoHeight *= resizeScale.y(); + } + mVideoHeight = round(mVideoHeight); + mVideoWidth = round(mVideoWidth); + } + } +#endif setupContext(); // Setup the media player mMediaPlayer = libvlc_media_player_new_from_media(mMedia); + if (!Settings::getInstance()->getBool("VideoAudio")) { libvlc_audio_set_mute(mMediaPlayer, 1); diff --git a/es-core/src/components/VideoVlcComponent.h b/es-core/src/components/VideoVlcComponent.h index c3c6ad549..796c970da 100644 --- a/es-core/src/components/VideoVlcComponent.h +++ b/es-core/src/components/VideoVlcComponent.h @@ -6,6 +6,7 @@ #include "VideoComponent.h" #include +#include #include "resources/TextureResource.h" struct VideoContext { @@ -26,9 +27,9 @@ class VideoVlcComponent : public VideoComponent }; public: - static void setupVLC(); + static void setupVLC(std::string subtitles); - VideoVlcComponent(Window* window); + VideoVlcComponent(Window* window, std::string subtitles); virtual ~VideoVlcComponent(); void render(const Eigen::Affine3f& parentTrans) override; diff --git a/es-core/src/platform.h b/es-core/src/platform.h index 0760b565c..0b15ab25d 100644 --- a/es-core/src/platform.h +++ b/es-core/src/platform.h @@ -19,6 +19,7 @@ std::string getHomePath(); + int runShutdownCommand(); // shut down the system (returns 0 if successful) int runRestartCommand(); // restart the system (returns 0 if successful) int runSystemCommand(const std::string& cmd_utf8); // run a utf-8 encoded in the shell (requires wstring conversion on Windows)