diff --git a/emulationstation.sh b/emulationstation.sh new file mode 100755 index 0000000000..cc48aed670 --- /dev/null +++ b/emulationstation.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +while true; do + rm -f /tmp/es-restart /tmp/es-sysrestart /tmp/es-shutdown + ./emulationstation "$@" + [ -f /tmp/es-restart ] && continue + if [ -f /tmp/es-sysrestart ]; then + rm -f /tmp/es-sysrestart + sudo reboot + break + fi + if [ -f /tmp/es-shutdown ]; then + rm -f /tmp/es-shutdown + sudo poweroff + break + fi + break +done diff --git a/es-app/CMakeLists.txt b/es-app/CMakeLists.txt index ee7f829909..334300ff71 100644 --- a/es-app/CMakeLists.txt +++ b/es-app/CMakeLists.txt @@ -35,6 +35,7 @@ set(ES_HEADERS # Views ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/BasicGameListView.h ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/DetailedGameListView.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/FanartGameListView.h ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/IGameListView.h ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/ISimpleGameListView.h ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/GridGameListView.h @@ -81,6 +82,7 @@ set(ES_SOURCES # Views ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/BasicGameListView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/DetailedGameListView.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/FanartGameListView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/IGameListView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/ISimpleGameListView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/GridGameListView.cpp diff --git a/es-app/src/FileData.cpp b/es-app/src/FileData.cpp index a1b625d8a5..d4279105bd 100644 --- a/es-app/src/FileData.cpp +++ b/es-app/src/FileData.cpp @@ -66,7 +66,7 @@ std::string FileData::getCleanName() const if(mSystem && mSystem->hasPlatformId(PlatformIds::ARCADE) || mSystem->hasPlatformId(PlatformIds::NEOGEO)) stem = PlatformIds::getCleanMameName(stem.c_str()); - return removeParenthesis(stem); + return stem; } const std::string& FileData::getThumbnailPath() const diff --git a/es-app/src/MetaData.cpp b/es-app/src/MetaData.cpp index 2d5340bd24..aafbcb610c 100644 --- a/es-app/src/MetaData.cpp +++ b/es-app/src/MetaData.cpp @@ -10,6 +10,7 @@ MetaDataDecl gameDecls[] = { {"name", MD_STRING, "", false, "name", "enter game name"}, {"desc", MD_MULTILINE_STRING, "", false, "description", "enter description"}, {"image", MD_IMAGE_PATH, "", false, "image", "enter path to image"}, + {"fanart", MD_IMAGE_PATH, "", false, "fanart", "enter path to fanart"}, {"thumbnail", MD_IMAGE_PATH, "", false, "thumbnail", "enter path to thumbnail"}, {"rating", MD_RATING, "0.000000", false, "rating", "enter rating"}, {"releasedate", MD_DATE, "not-a-date-time", false, "release date", "enter release date"}, diff --git a/es-app/src/SystemData.cpp b/es-app/src/SystemData.cpp index 5e2b9b4c2e..b7d3122968 100644 --- a/es-app/src/SystemData.cpp +++ b/es-app/src/SystemData.cpp @@ -17,12 +17,14 @@ std::vector SystemData::sSystemVector; namespace fs = boost::filesystem; -SystemData::SystemData(const std::string& name, const std::string& fullName, const std::string& startPath, const std::vector& extensions, +SystemData::SystemData(const std::string& name, const std::string& fullName, const std::string& input, const std::string& startPath, const std::vector& extensions, const std::string& command, const std::vector& platformIds, const std::string& themeFolder) { mName = name; mFullName = fullName; mStartPath = startPath; + mInput = input; + updateVisibility(); //expand home symbol if the startpath contains ~ if(mStartPath[0] == '~') @@ -45,11 +47,18 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, con if(!Settings::getInstance()->getBool("IgnoreGamelist")) parseGamelist(this); - mRootFolder->sort(FileSorts::SortTypes.at(0)); + int sortId = (int)(Settings::getInstance()->getInt("SortTypeDefault")); + + mRootFolder->sort(FileSorts::SortTypes.at(sortId)); loadTheme(); } +void SystemData::updateVisibility() +{ + mVisible = InputManager::getInstance()->isInputFound(mInput); +} + SystemData::~SystemData() { //save changed game data back to xml @@ -104,6 +113,15 @@ std::string escapePath(const boost::filesystem::path& path) #endif } +void SystemData::updateSystems() +{ + for(unsigned int i = 0; i < sSystemVector.size(); i++) + { + SystemData *v = sSystemVector.at(i); + v->updateVisibility(); + } +} + void SystemData::launchGame(Window* window, FileData* game) { LOG(LogInfo) << "Attempting to launch game..."; @@ -262,12 +280,13 @@ bool SystemData::loadConfig() for(pugi::xml_node system = systemList.child("system"); system; system = system.next_sibling("system")) { - std::string name, fullname, path, cmd, themeFolder; + std::string name, fullname, path, cmd, themeFolder, input; PlatformIds::PlatformId platformId = PlatformIds::PLATFORM_UNKNOWN; name = system.child("name").text().get(); fullname = system.child("fullname").text().get(); path = system.child("path").text().get(); + input = system.child("input").text().get(); // convert extensions list from a string into a vector of strings std::vector extensions = readList(system.child("extension").text().get()); @@ -312,7 +331,7 @@ bool SystemData::loadConfig() boost::filesystem::path genericPath(path); path = genericPath.generic_string(); - SystemData* newSys = new SystemData(name, fullname, path, extensions, cmd, platformIds, themeFolder); + SystemData* newSys = new SystemData(name, fullname, input, path, extensions, cmd, platformIds, themeFolder); if(newSys->getRootFolder()->getChildren().size() == 0) { LOG(LogWarning) << "System \"" << name << "\" has no games! Ignoring it."; diff --git a/es-app/src/SystemData.h b/es-app/src/SystemData.h index 2a0ac81886..e4a395fa2e 100644 --- a/es-app/src/SystemData.h +++ b/es-app/src/SystemData.h @@ -11,13 +11,15 @@ class SystemData { public: - SystemData(const std::string& name, const std::string& fullName, const std::string& startPath, const std::vector& extensions, + SystemData(const std::string& name, const std::string& fullName, const std::string& input, const std::string& startPath, const std::vector& extensions, const std::string& command, const std::vector& platformIds, const std::string& themeFolder); ~SystemData(); inline FileData* getRootFolder() const { return mRootFolder; }; inline const std::string& getName() const { return mName; } inline const std::string& getFullName() const { return mFullName; } + inline const bool& isVisible() const { return mVisible; } + inline const std::string& getInput() const { return mInput; } inline const std::string& getStartPath() const { return mStartPath; } inline const std::vector& getExtensions() const { return mSearchExtensions; } inline const std::string& getThemeFolder() const { return mThemeFolder; } @@ -63,17 +65,23 @@ class SystemData // Load or re-load theme. void loadTheme(); + void updateVisibility(); + + static void updateSystems(); private: std::string mName; std::string mFullName; std::string mStartPath; + std::string mInput; std::vector mSearchExtensions; std::string mLaunchCommand; std::vector mPlatformIds; std::string mThemeFolder; std::shared_ptr mTheme; + bool mVisible; + void populateFolder(FileData* folder); FileData* mRootFolder; diff --git a/es-app/src/components/ScraperSearchComponent.cpp b/es-app/src/components/ScraperSearchComponent.cpp index 9cfda3de62..fb15008e25 100644 --- a/es-app/src/components/ScraperSearchComponent.cpp +++ b/es-app/src/components/ScraperSearchComponent.cpp @@ -32,43 +32,18 @@ ScraperSearchComponent::ScraperSearchComponent(Window* window, SearchType type) // selected result thumbnail mResultThumbnail = std::make_shared(mWindow); - mGrid.setEntry(mResultThumbnail, Vector2i(1, 1), false, false, Vector2i(1, 1)); + mGrid.setEntry(mResultThumbnail, Vector2i(1, 1), false, false, Vector2i(2, 3)); + + // selected result fanart + mResultFanart = std::make_shared(mWindow); + if(mSearchType != ALWAYS_ACCEPT_FIRST_RESULT) + mGrid.setEntry(mResultFanart, Vector2i(3, 2), false, false, Vector2i(1, 1)); // selected result desc + container mDescContainer = std::make_shared(mWindow); mResultDesc = std::make_shared(mWindow, "Result desc", Font::get(FONT_SIZE_SMALL), 0x777777FF); mDescContainer->addChild(mResultDesc.get()); mDescContainer->setAutoScroll(true); - - // metadata - auto font = Font::get(FONT_SIZE_SMALL); // this gets replaced in onSizeChanged() so its just a placeholder - const unsigned int mdColor = 0x777777FF; - const unsigned int mdLblColor = 0x666666FF; - mMD_Rating = std::make_shared(mWindow); - mMD_ReleaseDate = std::make_shared(mWindow); - mMD_ReleaseDate->setColor(mdColor); - mMD_Developer = std::make_shared(mWindow, "", font, mdColor); - mMD_Publisher = std::make_shared(mWindow, "", font, mdColor); - mMD_Genre = std::make_shared(mWindow, "", font, mdColor); - mMD_Players = std::make_shared(mWindow, "", font, mdColor); - - mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "RATING:", font, mdLblColor), mMD_Rating, false)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "RELEASED:", font, mdLblColor), mMD_ReleaseDate)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "DEVELOPER:", font, mdLblColor), mMD_Developer)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "PUBLISHER:", font, mdLblColor), mMD_Publisher)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "GENRE:", font, mdLblColor), mMD_Genre)); - mMD_Pairs.push_back(MetaDataPair(std::make_shared(mWindow, "PLAYERS:", font, mdLblColor), mMD_Players)); - - mMD_Grid = std::make_shared(mWindow, Vector2i(2, mMD_Pairs.size()*2 - 1)); - unsigned int i = 0; - for(auto it = mMD_Pairs.begin(); it != mMD_Pairs.end(); it++) - { - mMD_Grid->setEntry(it->first, Vector2i(0, i), false, true); - mMD_Grid->setEntry(it->second, Vector2i(1, i), false, it->resize); - i += 2; - } - - mGrid.setEntry(mMD_Grid, Vector2i(2, 1), false, false); // result list mResultList = std::make_shared(mWindow); @@ -110,7 +85,8 @@ void ScraperSearchComponent::onSizeChanged() // limit thumbnail size using setMaxHeight - we do this instead of letting mGrid call setSize because it maintains the aspect ratio // we also pad a little so it doesn't rub up against the metadata labels - mResultThumbnail->setMaxSize(mGrid.getColWidth(1) * boxartCellScale, mGrid.getRowHeight(1)); + mResultThumbnail->setMaxSize((mGrid.getColWidth(1)+mGrid.getColWidth(2)) * boxartCellScale, (mGrid.getRowHeight(1)+mGrid.getRowHeight(2))); + mResultFanart->setMaxSize(mGrid.getColWidth(2), mGrid.getRowHeight(2) * boxartCellScale); // metadata resizeMetadata(); @@ -129,44 +105,6 @@ void ScraperSearchComponent::onSizeChanged() void ScraperSearchComponent::resizeMetadata() { - mMD_Grid->setSize(mGrid.getColWidth(2), mGrid.getRowHeight(1)); - if(mMD_Grid->getSize().y() > mMD_Pairs.size()) - { - const int fontHeight = (int)(mMD_Grid->getSize().y() / mMD_Pairs.size() * 0.8f); - auto fontLbl = Font::get(fontHeight, FONT_PATH_REGULAR); - auto fontComp = Font::get(fontHeight, FONT_PATH_LIGHT); - - // update label fonts - float maxLblWidth = 0; - for(auto it = mMD_Pairs.begin(); it != mMD_Pairs.end(); it++) - { - it->first->setFont(fontLbl); - it->first->setSize(0, 0); - if(it->first->getSize().x() > maxLblWidth) - maxLblWidth = it->first->getSize().x() + 6; - } - - for(unsigned int i = 0; i < mMD_Pairs.size(); i++) - { - mMD_Grid->setRowHeightPerc(i*2, (fontLbl->getLetterHeight() + 2) / mMD_Grid->getSize().y()); - } - - // update component fonts - mMD_ReleaseDate->setFont(fontComp); - mMD_Developer->setFont(fontComp); - mMD_Publisher->setFont(fontComp); - mMD_Genre->setFont(fontComp); - mMD_Players->setFont(fontComp); - - mMD_Grid->setColWidthPerc(0, maxLblWidth / mMD_Grid->getSize().x()); - - // rating is manually sized - mMD_Rating->setSize(mMD_Grid->getColWidth(1), fontLbl->getHeight() * 0.65f); - mMD_Grid->onSizeChanged(); - - // make result font follow label font - mResultDesc->setFont(Font::get(fontHeight, FONT_PATH_REGULAR)); - } } void ScraperSearchComponent::updateViewStyle() @@ -191,15 +129,8 @@ void ScraperSearchComponent::updateViewStyle() mGrid.setEntry(mDescContainer, Vector2i(3, 0), false, false, Vector2i(1, 3), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); mResultDesc->setSize(mDescContainer->getSize().x(), 0); // make desc text wrap at edge of container }else{ - // fake row where name would be - mGrid.setEntry(std::make_shared(mWindow), Vector2i(1, 0), false, true, Vector2i(2, 1), GridFlags::BORDER_TOP); - // show result list on the right - mGrid.setEntry(mResultList, Vector2i(3, 0), true, true, Vector2i(1, 3), GridFlags::BORDER_LEFT | GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); - - // show description under image/info - mGrid.setEntry(mDescContainer, Vector2i(1, 2), false, false, Vector2i(2, 1), GridFlags::BORDER_BOTTOM); - mResultDesc->setSize(mDescContainer->getSize().x(), 0); // make desc text wrap at edge of container + mGrid.setEntry(mResultList, Vector2i(3, 0), true, true, Vector2i(1, 2), GridFlags::BORDER_LEFT | GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); } } @@ -311,26 +242,24 @@ void ScraperSearchComponent::updateInfoPane() mThumbnailReq.reset(); } + if(mSearchType != ALWAYS_ACCEPT_FIRST_RESULT) { + mResultFanart->setImage(""); + const std::string& fanarturl = res.fanartUrl; + if(!fanarturl.empty()) + { + mFanartReq = std::unique_ptr(new HttpReq(fanarturl)); + }else{ + mFanartReq.reset(); + } + } + // metadata - mMD_Rating->setValue(strToUpper(res.mdl.get("rating"))); - mMD_ReleaseDate->setValue(strToUpper(res.mdl.get("releasedate"))); - mMD_Developer->setText(strToUpper(res.mdl.get("developer"))); - mMD_Publisher->setText(strToUpper(res.mdl.get("publisher"))); - mMD_Genre->setText(strToUpper(res.mdl.get("genre"))); - mMD_Players->setText(strToUpper(res.mdl.get("players"))); mGrid.onSizeChanged(); }else{ mResultName->setText(""); mResultDesc->setText(""); mResultThumbnail->setImage(""); - - // metadata - mMD_Rating->setValue(""); - mMD_ReleaseDate->setValue(""); - mMD_Developer->setText(""); - mMD_Publisher->setText(""); - mMD_Genre->setText(""); - mMD_Players->setText(""); + mResultFanart->setImage(""); } } @@ -390,6 +319,11 @@ void ScraperSearchComponent::update(int deltaTime) updateThumbnail(); } + if(mFanartReq && mFanartReq->status() != HttpReq::REQ_IN_PROGRESS) + { + updateFanart(); + } + if(mSearchHandle && mSearchHandle->status() != ASYNC_IN_PROGRESS) { auto status = mSearchHandle->status(); @@ -441,6 +375,21 @@ void ScraperSearchComponent::updateThumbnail() mThumbnailReq.reset(); } +void ScraperSearchComponent::updateFanart() +{ + if(mFanartReq && mFanartReq->status() == HttpReq::REQ_SUCCESS) + { + std::string content = mFanartReq->getContent(); + mResultFanart->setImage(content.data(), content.length()); + mGrid.onSizeChanged(); // a hack to fix the thumbnail position since its size changed + }else{ + LOG(LogWarning) << "fanart req failed: " << mFanartReq->getErrorMsg(); + mResultFanart->setImage(""); + } + + mFanartReq.reset(); +} + void ScraperSearchComponent::openInputScreen(ScraperSearchParams& params) { auto searchForFunc = [&](const std::string& name) diff --git a/es-app/src/components/ScraperSearchComponent.h b/es-app/src/components/ScraperSearchComponent.h index f4fb43f518..271de58403 100644 --- a/es-app/src/components/ScraperSearchComponent.h +++ b/es-app/src/components/ScraperSearchComponent.h @@ -48,6 +48,7 @@ class ScraperSearchComponent : public GuiComponent private: void updateViewStyle(); void updateThumbnail(); + void updateFanart(); void updateInfoPane(); void resizeMetadata(); @@ -66,15 +67,9 @@ class ScraperSearchComponent : public GuiComponent std::shared_ptr mDescContainer; std::shared_ptr mResultDesc; std::shared_ptr mResultThumbnail; + std::shared_ptr mResultFanart; std::shared_ptr mResultList; - std::shared_ptr mMD_Grid; - std::shared_ptr mMD_Rating; - std::shared_ptr mMD_ReleaseDate; - std::shared_ptr mMD_Developer; - std::shared_ptr mMD_Publisher; - std::shared_ptr mMD_Genre; - std::shared_ptr mMD_Players; // label-component pair struct MetaDataPair @@ -99,6 +94,7 @@ class ScraperSearchComponent : public GuiComponent std::unique_ptr mMDResolveHandle; std::vector mScraperResults; std::unique_ptr mThumbnailReq; + std::unique_ptr mFanartReq; BusyComponent mBusyAnim; }; diff --git a/es-app/src/guis/GuiFastSelect.cpp b/es-app/src/guis/GuiFastSelect.cpp index 31db350414..8200b3e570 100644 --- a/es-app/src/guis/GuiFastSelect.cpp +++ b/es-app/src/guis/GuiFastSelect.cpp @@ -2,6 +2,7 @@ #include "ThemeData.h" #include "FileSorts.h" #include "SystemData.h" +#include "Settings.h" static const std::string LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -31,7 +32,7 @@ GuiFastSelect::GuiFastSelect(Window* window, IGameListView* gamelist) : GuiCompo // TODO - set font size addChild(&mSortText); - mSortId = 0; // TODO + mSortId = (int)(Settings::getInstance()->getInt("SortTypeDefault")); // TODO updateSortText(); mLetterId = LETTERS.find(mGameList->getCursor()->getName()[0]); diff --git a/es-app/src/guis/GuiGamelistOptions.cpp b/es-app/src/guis/GuiGamelistOptions.cpp index e019cd7e6f..47586e0ceb 100644 --- a/es-app/src/guis/GuiGamelistOptions.cpp +++ b/es-app/src/guis/GuiGamelistOptions.cpp @@ -2,6 +2,8 @@ #include "GuiMetaDataEd.h" #include "views/gamelist/IGameListView.h" #include "views/ViewController.h" +#include "Settings.h" + GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : GuiComponent(window), mSystem(system), @@ -36,11 +38,12 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : Gui mMenu.addRow(row); // sort list by + int defaultSortId = (int)(Settings::getInstance()->getInt("SortTypeDefault")); mListSort = std::make_shared(mWindow, "SORT GAMES BY", false); for(unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) { const FileData::SortType& sort = FileSorts::SortTypes.at(i); - mListSort->add(sort.description, &sort, i == 0); // TODO - actually make the sort type persistent + mListSort->add(sort.description, &sort, i == defaultSortId); // TODO - actually make the sort type persistent } mMenu.addWithLabel("SORT GAMES BY", mListSort); diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 417d38224c..8478d98eb0 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -177,10 +177,21 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN Window* window = mWindow; ComponentListRow row; + row.makeAcceptInputHandler([window] { + window->pushGui(new GuiMsgBox(window, "REALLY RESTART?", "YES", + [] { + if(quitES("/tmp/es-restart") != 0) + LOG(LogWarning) << "Restart terminated with non-zero result!"; + }, "NO", nullptr)); + }); + row.addElement(std::make_shared(window, "RESTART EMULATIONSTATION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + s->addRow(row); + + row.elements.clear(); row.makeAcceptInputHandler([window] { window->pushGui(new GuiMsgBox(window, "REALLY RESTART?", "YES", [] { - if(runRestartCommand() != 0) + if(quitES("/tmp/es-sysrestart") != 0) LOG(LogWarning) << "Restart terminated with non-zero result!"; }, "NO", nullptr)); }); @@ -191,7 +202,7 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN row.makeAcceptInputHandler([window] { window->pushGui(new GuiMsgBox(window, "REALLY SHUTDOWN?", "YES", [] { - if(runShutdownCommand() != 0) + if(quitES("/tmp/es-shutdown") != 0) LOG(LogWarning) << "Shutdown terminated with non-zero result!"; }, "NO", nullptr)); }); diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index 393f199fd3..058c94d19f 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -284,6 +284,7 @@ int main(int argc, char* argv[]) while(running) { SDL_Event event; + bool isEventValid = true; while(SDL_PollEvent(&event)) { switch(event.type) @@ -298,13 +299,24 @@ int main(int argc, char* argv[]) case SDL_TEXTEDITING: case SDL_JOYDEVICEADDED: case SDL_JOYDEVICEREMOVED: - InputManager::getInstance()->parseEvent(event, &window); + isEventValid = InputManager::getInstance()->parseEvent(event, &window); break; case SDL_QUIT: running = false; break; } + switch(event.type) + { + case SDL_JOYDEVICEADDED: + case SDL_JOYDEVICEREMOVED: + SystemData::updateSystems(); + ViewController::get()->reloadAll(); + break; + default: + break; + } } + if (isEventValid == false) continue; if(window.isSleeping()) { diff --git a/es-app/src/scrapers/GamesDBScraper.cpp b/es-app/src/scrapers/GamesDBScraper.cpp index ea78e374e1..d4adefa525 100644 --- a/es-app/src/scrapers/GamesDBScraper.cpp +++ b/es-app/src/scrapers/GamesDBScraper.cpp @@ -149,6 +149,14 @@ void TheGamesDBRequest::process(const std::unique_ptr& req, std::vector if(images) { + pugi::xml_node fanart = images.child("fanart"); + if (fanart) { + pugi::xml_node fanartUrl = fanart.child("original"); + if (fanartUrl) { + result.fanartUrl = baseImageUrl + fanartUrl.text().get(); + } + } + pugi::xml_node art = images.find_child_by_attribute("boxart", "side", "front"); if(art) diff --git a/es-app/src/scrapers/Scraper.cpp b/es-app/src/scrapers/Scraper.cpp index c827a4755f..52d24012d8 100644 --- a/es-app/src/scrapers/Scraper.cpp +++ b/es-app/src/scrapers/Scraper.cpp @@ -132,6 +132,15 @@ MDResolveHandle::MDResolveHandle(const ScraperSearchResult& result, const Scrape mResult.imageUrl = ""; })); } + if(!result.fanartUrl.empty()) + { + std::string imgPath = getSaveAsPath(search, "fanart", result.fanartUrl); + mFuncs.push_back(ResolvePair(downloadImageAsync(result.fanartUrl, imgPath, Renderer::getScreenWidth(), Renderer::getScreenHeight()), [this, imgPath] + { + mResult.mdl.set("fanart", imgPath); + mResult.fanartUrl = ""; + })); + } } void MDResolveHandle::update() @@ -159,10 +168,16 @@ void MDResolveHandle::update() setStatus(ASYNC_DONE); } -std::unique_ptr downloadImageAsync(const std::string& url, const std::string& saveAs) +std::unique_ptr downloadImageAsync(const std::string& url, const std::string& saveAs, int width, int height) { return std::unique_ptr(new ImageDownloadHandle(url, saveAs, - Settings::getInstance()->getInt("ScraperResizeWidth"), Settings::getInstance()->getInt("ScraperResizeHeight"))); + width, height)); +} + +std::unique_ptr downloadImageAsync(const std::string& url, const std::string& saveAs) +{ + + return downloadImageAsync(url, saveAs, Settings::getInstance()->getInt("ScraperResizeWidth"), Settings::getInstance()->getInt("ScraperResizeHeight")); } ImageDownloadHandle::ImageDownloadHandle(const std::string& url, const std::string& path, int maxWidth, int maxHeight) : diff --git a/es-app/src/scrapers/Scraper.h b/es-app/src/scrapers/Scraper.h index 87ab2502a7..dd8d616d5a 100644 --- a/es-app/src/scrapers/Scraper.h +++ b/es-app/src/scrapers/Scraper.h @@ -4,6 +4,7 @@ #include "SystemData.h" #include "HttpReq.h" #include "AsyncHandle.h" +#include "Renderer.h" #include #include #include @@ -25,6 +26,7 @@ struct ScraperSearchResult MetaDataList mdl; std::string imageUrl; std::string thumbnailUrl; + std::string fanartUrl; }; // So let me explain why I've abstracted this so heavily. @@ -146,6 +148,7 @@ std::string getSaveAsPath(const ScraperSearchParams& params, const std::string& //Will resize according to Settings::getInt("ScraperResizeWidth") and Settings::getInt("ScraperResizeHeight"). std::unique_ptr downloadImageAsync(const std::string& url, const std::string& saveAs); +std::unique_ptr downloadImageAsync(const std::string& url, const std::string& saveAs, int width, int height); // Resolves all metadata assets that need to be downloaded. std::unique_ptr resolveMetaDataAssets(const ScraperSearchResult& result, const ScraperSearchParams& search); diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index 54987fdf87..303fede735 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -34,6 +34,7 @@ void SystemView::populate() for(auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); it++) { + if (!(*it)->isVisible()) continue; const std::shared_ptr& theme = (*it)->getTheme(); Entry e; diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index 5a94d97c2b..5e2b16c592 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -3,8 +3,14 @@ #include "SystemData.h" #include "Settings.h" +#define USE_FANART + #include "views/gamelist/BasicGameListView.h" +#ifdef USE_FANART +#include "views/gamelist/FanartGameListView.h" +#else #include "views/gamelist/DetailedGameListView.h" +#endif #include "views/gamelist/GridGameListView.h" #include "guis/GuiMenu.h" #include "guis/GuiMsgBox.h" @@ -221,7 +227,11 @@ std::shared_ptr ViewController::getGameListView(SystemData* syste } if(detailed) +#ifdef USE_FANART + view = std::shared_ptr(new FanartGameListView(mWindow, system->getRootFolder())); +#else view = std::shared_ptr(new DetailedGameListView(mWindow, system->getRootFolder())); +#endif else view = std::shared_ptr(new BasicGameListView(mWindow, system->getRootFolder())); @@ -259,7 +269,7 @@ bool ViewController::input(InputConfig* config, Input input) return true; // open menu - if(config->isMappedTo("start", input) && input.value != 0) + if(config->isMappedTo("start", input) && input.value != 0 && !Settings::getInstance()->getBool("HideMainMenu")) { // open menu mWindow->pushGui(new GuiMenu(mWindow)); diff --git a/es-app/src/views/gamelist/FanartGameListView.cpp b/es-app/src/views/gamelist/FanartGameListView.cpp new file mode 100644 index 0000000000..b5988fdd87 --- /dev/null +++ b/es-app/src/views/gamelist/FanartGameListView.cpp @@ -0,0 +1,201 @@ +#include "views/gamelist/FanartGameListView.h" +#include "views/ViewController.h" +#include "Window.h" +#include "animations/LambdaAnimation.h" + +#define COL_SIZE 0.25f +#define IMAGE_SIZE 0.25f +#define LIST_SIZE 0.4f + +FanartGameListView::FanartGameListView(Window* window, FileData* root) : + BasicGameListView(window, root), + mImage(window), + + mLblGenre(window), mLblPlayers(window), + + mGenre(window), mPlayers(window) +{ + const float padding = 0.01f; + + mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y()); + mList.setSize(mSize.x() * (0.50f - padding), mList.getSize().y()); + mList.setAlignment(TextListComponent::ALIGN_LEFT); + mList.setCursorChangedCallback([&](const CursorState& state) { updateInfoPanel(); }); + + // image + mImage.setOrigin(0.0f, 0.0f); + mImage.setPosition(0.0f, 0.0f); + mImage.setMaxSize(mSize.x(), mSize.y()); + addChild(&mImage); + + // metadata labels + values + mLblGenre.setText("Genre: "); + addChild(&mLblGenre); + addChild(&mGenre); + mLblPlayers.setText("Players: "); + addChild(&mLblPlayers); + addChild(&mPlayers); + + initMDLabels(); + initMDValues(); + updateInfoPanel(); +} + +void FanartGameListView::onThemeChanged(const std::shared_ptr& theme) +{ + mTheme = theme; + BasicGameListView::onThemeChanged(theme); + + using namespace ThemeFlags; + mImage.applyTheme(theme, getName(), "md_image", POSITION | ThemeFlags::SIZE); + mFanart.applyTheme(theme, getName(), "md_fanart", POSITION | COLOR | ThemeFlags::SIZE | ThemeFlags::PATH); + + initMDLabels(); + std::vector labels = getMDLabels(); + assert(labels.size() == 2); + const char* lblElements[2] = { + "md_lbl_genre", "md_lbl_players" + }; + + for(unsigned int i = 0; i < labels.size(); i++) + { + labels[i]->applyTheme(theme, getName(), lblElements[i], ALL); + } + + + initMDValues(); + std::vector values = getMDValues(); + assert(values.size() == 2); + const char* valElements[2] = { + "md_genre", "md_players" + }; + + for(unsigned int i = 0; i < values.size(); i++) + { + values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT); + } +} + +void FanartGameListView::initMDLabels() +{ + using namespace Eigen; + + std::vector components = getMDLabels(); + + Vector3f start(mSize.x() * 0.01f, mSize.y() * 0.625f, 0.0f); + + const float colSize = (mSize.x() * COL_SIZE); + + for(unsigned int i = 0; i < components.size(); i++) + { + Vector3f pos(0.0f, 0.0f, 0.0f); + + pos = start + Vector3f(colSize * i, 0, 0); + + components[i]->setFont(Font::get(FONT_SIZE_SMALL)); + components[i]->setPosition(pos); + } +} + +void FanartGameListView::initMDValues() +{ + using namespace Eigen; + + std::vector labels = getMDLabels(); + std::vector values = getMDValues(); + + std::shared_ptr defaultFont = Font::get(FONT_SIZE_SMALL); + mGenre.setFont(defaultFont); + mPlayers.setFont(defaultFont); + + float bottom = 0.0f; + + const float colSize = (mSize.x() * COL_SIZE); + for(unsigned int i = 0; i < labels.size(); i++) + { + const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2; + values[i]->setPosition(labels[i]->getPosition() + Vector3f(labels[i]->getSize().x(), heightDiff, 0)); + values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y()); + + float testBot = values[i]->getPosition().y() + values[i]->getSize().y(); + if(testBot > bottom) + bottom = testBot; + } +} + +void FanartGameListView::updateInfoPanel() +{ + FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected(); + + bool fadingOut; + if(file == NULL) + { + fadingOut = true; + }else{ + mImage.setImage(file->metadata.get("image")); + if (!file->metadata.get("fanart").empty()) + mFanart.setImage(file->metadata.get("fanart")); + else + if (mTheme != NULL) mFanart.applyTheme(mTheme, getName(), "md_fanart", ThemeFlags::PATH); + if (mFanart.hasImage()) + mBackground.setOpacity(0x00); + else + mBackground.setOpacity(0xFF); + + if(file->getType() == GAME) + { + mGenre.setValue(file->metadata.get("genre")); + mPlayers.setValue(file->metadata.get("players")); + } + + fadingOut = false; + } + + std::vector comps = getMDValues(); + comps.push_back(&mImage); + std::vector labels = getMDLabels(); + comps.insert(comps.end(), labels.begin(), labels.end()); + + for(auto it = comps.begin(); it != comps.end(); it++) + { + GuiComponent* comp = *it; + // an animation is playing + // then animate if reverse != fadingOut + // an animation is not playing + // then animate if opacity != our target opacity + if((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || + (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) + { + auto func = [comp](float t) + { + comp->setOpacity((unsigned char)(lerp(0.0f, 1.0f, t)*255)); + }; + comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); + } + } +} + +void FanartGameListView::launch(FileData* game) +{ + Eigen::Vector3f target(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f, 0); + if(mImage.hasImage()) + target << mImage.getCenter().x(), mImage.getCenter().y(), 0; + + ViewController::get()->launch(game, target); +} + +std::vector FanartGameListView::getMDLabels() +{ + std::vector ret; + ret.push_back(&mLblGenre); + ret.push_back(&mLblPlayers); + return ret; +} + +std::vector FanartGameListView::getMDValues() +{ + std::vector ret; + ret.push_back(&mGenre); + ret.push_back(&mPlayers); + return ret; +} diff --git a/es-app/src/views/gamelist/FanartGameListView.h b/es-app/src/views/gamelist/FanartGameListView.h new file mode 100644 index 0000000000..6332a97e08 --- /dev/null +++ b/es-app/src/views/gamelist/FanartGameListView.h @@ -0,0 +1,37 @@ +#pragma once + +#include "views/gamelist/BasicGameListView.h" +#include "components/ScrollableContainer.h" +#include "components/RatingComponent.h" +#include "components/DateTimeComponent.h" + +class FanartGameListView : public BasicGameListView +{ +public: + FanartGameListView(Window* window, FileData* root); + + virtual void onThemeChanged(const std::shared_ptr& theme) override; + + virtual const char* getName() const override { return "fanart"; } + +protected: + virtual void launch(FileData* game) override; + +private: + void updateInfoPanel(); + + void initMDLabels(); + void initMDValues(); + + ImageComponent mImage; + + TextComponent mLblGenre, mLblPlayers; + + TextComponent mGenre; + TextComponent mPlayers; + + std::vector getMDLabels(); + std::vector getMDValues(); + + std::shared_ptr mTheme; +}; diff --git a/es-app/src/views/gamelist/IGameListView.cpp b/es-app/src/views/gamelist/IGameListView.cpp index 69799cdd90..e3964dc706 100644 --- a/es-app/src/views/gamelist/IGameListView.cpp +++ b/es-app/src/views/gamelist/IGameListView.cpp @@ -11,7 +11,7 @@ bool IGameListView::input(InputConfig* config, Input input) { // select to open GuiGamelistOptions - if(config->isMappedTo("select", input) && input.value) + if(config->isMappedTo("select", input) && input.value && !Settings::getInstance()->getBool("HideMainMenu")) { Sound::getFromTheme(mTheme, getName(), "menuOpen")->play(); mWindow->pushGui(new GuiGamelistOptions(mWindow, this->mRoot->getSystem())); diff --git a/es-app/src/views/gamelist/ISimpleGameListView.cpp b/es-app/src/views/gamelist/ISimpleGameListView.cpp index dd6df5b634..9bfffe0057 100644 --- a/es-app/src/views/gamelist/ISimpleGameListView.cpp +++ b/es-app/src/views/gamelist/ISimpleGameListView.cpp @@ -6,7 +6,7 @@ #include "Settings.h" ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root) : IGameListView(window, root), - mHeaderText(window), mHeaderImage(window), mBackground(window), mThemeExtras(window) + mHeaderText(window), mHeaderImage(window), mBackground(window), mThemeExtras(window), mFanart(window) { mHeaderText.setText("Logo Text"); mHeaderText.setSize(mSize.x(), 0); @@ -19,8 +19,13 @@ ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root) : IGame mBackground.setResize(mSize.x(), mSize.y()); + mFanart.setOrigin(0.0f, 0.0f); + mFanart.setPosition(0.0f, 0.0f); + mFanart.setMaxSize(mSize.x(), mSize.y()); + addChild(&mHeaderText); addChild(&mBackground); + addChild(&mFanart); addChild(&mThemeExtras); } diff --git a/es-app/src/views/gamelist/ISimpleGameListView.h b/es-app/src/views/gamelist/ISimpleGameListView.h index 9022cde73e..26b823c34b 100644 --- a/es-app/src/views/gamelist/ISimpleGameListView.h +++ b/es-app/src/views/gamelist/ISimpleGameListView.h @@ -31,6 +31,7 @@ class ISimpleGameListView : public IGameListView TextComponent mHeaderText; ImageComponent mHeaderImage; ImageComponent mBackground; + ImageComponent mFanart; ThemeExtras mThemeExtras; diff --git a/es-core/src/InputManager.cpp b/es-core/src/InputManager.cpp index 3bd26b8975..3e2e28e587 100644 --- a/es-core/src/InputManager.cpp +++ b/es-core/src/InputManager.cpp @@ -62,6 +62,19 @@ void InputManager::init() loadInputConfig(mKeyboardInputConfig); } +bool InputManager::isInputFound(const std::string& name) { + if (name.empty()) return true; + int numJoysticks = SDL_NumJoysticks(); + for(int i = 0; i < numJoysticks; i++) + { + std::string s(SDL_JoystickNameForIndex(i)); + if (s.find(name, 0) != std::string::npos) { + return true; + } + } + return false; +} + void InputManager::addJoystickByDeviceIndex(int id) { assert(id >= 0 && id < SDL_NumJoysticks()); @@ -170,10 +183,15 @@ InputConfig* InputManager::getInputConfigByDevice(int device) bool InputManager::parseEvent(const SDL_Event& ev, Window* window) { bool causedEvent = false; + std::string PS3("PLAYSTATION(R)3"); switch(ev.type) { case SDL_JOYAXISMOTION: + //Do not handle JOYAXISMOTION for PS3 controller //if it switched boundaries + if (PS3.compare(0,15,SDL_JoystickNameForIndex(ev.jaxis.which),0,15) == 0) { + return false; + } if((abs(ev.jaxis.value) > DEADZONE) != (abs(mPrevAxisValues[ev.jaxis.which][ev.jaxis.axis]) > DEADZONE)) { int normValue; @@ -208,14 +226,14 @@ bool InputManager::parseEvent(const SDL_Event& ev, Window* window) } if(ev.key.repeat) - return false; + return true; if(ev.key.keysym.sym == SDLK_F4) { SDL_Event* quit = new SDL_Event(); quit->type = SDL_QUIT; SDL_PushEvent(quit); - return false; + return true; } window->input(getInputConfigByDevice(DEVICE_KEYBOARD), Input(DEVICE_KEYBOARD, TYPE_KEY, ev.key.keysym.sym, 1, false)); @@ -235,7 +253,7 @@ bool InputManager::parseEvent(const SDL_Event& ev, Window* window) case SDL_JOYDEVICEREMOVED: removeJoystickByJoystickID(ev.jdevice.which); // ev.jdevice.which is an SDL_JoystickID (instance ID) - return false; + return true; } return false; diff --git a/es-core/src/InputManager.h b/es-core/src/InputManager.h index 5ec8d8848a..e813944675 100644 --- a/es-core/src/InputManager.h +++ b/es-core/src/InputManager.h @@ -53,6 +53,8 @@ class InputManager InputConfig* getInputConfigByDevice(int deviceId); bool parseEvent(const SDL_Event& ev, Window* window); + + bool isInputFound(const std::string& name); }; #endif diff --git a/es-core/src/Log.cpp b/es-core/src/Log.cpp index 643aef16ae..ef4d4b9b59 100644 --- a/es-core/src/Log.cpp +++ b/es-core/src/Log.cpp @@ -4,7 +4,7 @@ #include #include "platform.h" -LogLevel Log::reportingLevel = LogInfo; +LogLevel Log::reportingLevel = LogError; FILE* Log::file = NULL; //fopen(getLogPath().c_str(), "w"); LogLevel Log::getReportingLevel() diff --git a/es-core/src/Renderer_init_sdlgl.cpp b/es-core/src/Renderer_init_sdlgl.cpp index 00175f3fc8..f689d8638f 100644 --- a/es-core/src/Renderer_init_sdlgl.cpp +++ b/es-core/src/Renderer_init_sdlgl.cpp @@ -36,6 +36,9 @@ namespace Renderer return false; } + //hide mouse cursor early + initialCursorState = SDL_ShowCursor(0) == 1; + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); @@ -108,9 +111,6 @@ namespace Renderer LOG(LogWarning) << "Tried to enable vsync, but failed! (" << SDL_GetError() << ")"; } - //hide mouse cursor - initialCursorState = SDL_ShowCursor(0) == 1; - return true; } @@ -151,7 +151,7 @@ namespace Renderer glMatrixMode(GL_PROJECTION); glOrtho(0, display_width, display_height, 0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); - glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); return true; } diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index 779ee71420..c8e1179b12 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -59,6 +59,7 @@ void Settings::setDefaults() mBoolMap["IgnoreGamelist"] = false; mBoolMap["HideConsole"] = true; mBoolMap["QuickSystemSelect"] = true; + mBoolMap["HideMainMenu"] = false; mBoolMap["Debug"] = false; mBoolMap["DebugGrid"] = false; @@ -67,6 +68,8 @@ void Settings::setDefaults() mIntMap["ScreenSaverTime"] = 5*60*1000; // 5 minutes mIntMap["ScraperResizeWidth"] = 400; mIntMap["ScraperResizeHeight"] = 0; + mIntMap["SortTypeDefault"] = 0; + mIntMap["DimValue"] = 0x90; mStringMap["TransitionStyle"] = "fade"; mStringMap["ThemeSet"] = ""; diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index fce86628f3..e99b3f5f44 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -31,6 +31,17 @@ std::map< std::string, ElementMapType > ThemeData::sElementMap = boost::assign:: ("origin", NORMALIZED_PAIR) ("path", PATH) ("tile", BOOLEAN) + ("centered", BOOLEAN) + ("fullscreen", BOOLEAN) + ("color", COLOR))) + ("fanart", makeMap(boost::assign::map_list_of + ("pos", NORMALIZED_PAIR) + ("size", NORMALIZED_PAIR) + ("maxSize", NORMALIZED_PAIR) + ("origin", NORMALIZED_PAIR) + ("path", PATH) + ("tile", BOOLEAN) + ("centered", BOOLEAN) ("color", COLOR))) ("text", makeMap(boost::assign::map_list_of ("pos", NORMALIZED_PAIR) @@ -394,7 +405,7 @@ std::vector ThemeData::makeExtras(const std::shared_ptrsetImage(":/scroll_gradient.png"); + //mBackgroundOverlay = new ImageComponent(this); } Window::~Window() { - delete mBackgroundOverlay; + //delete mBackgroundOverlay; // delete all our GUIs while(peekGui()) @@ -65,6 +64,8 @@ bool Window::init(unsigned int width, unsigned int height) return false; } + //mBackgroundOverlay->setImage(":/scroll_gradient.png"); + InputManager::getInstance()->init(); ResourceManager::getInstance()->reloadAll(); @@ -77,7 +78,7 @@ bool Window::init(unsigned int width, unsigned int height) mDefaultFonts.push_back(Font::get(FONT_SIZE_LARGE)); } - mBackgroundOverlay->setResize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); + //mBackgroundOverlay->setResize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); // update our help because font sizes probably changed if(peekGui()) @@ -106,6 +107,7 @@ void Window::input(InputConfig* config, Input input) // wake up mTimeSinceLastInput = 0; mSleeping = false; + mDeepSleeping = false; onWake(); return; } @@ -186,7 +188,7 @@ void Window::render() bottom->render(transform); if(bottom != top) { - mBackgroundOverlay->render(transform); + //mBackgroundOverlay->render(transform); top->render(transform); } } @@ -205,6 +207,8 @@ void Window::render() { // go to sleep mSleeping = true; + if(mTimeSinceLastInput >= 3*screensaverTime && screensaverTime != 0 && mAllowSleep) + mDeepSleeping = true; onSleep(); } } @@ -326,11 +330,15 @@ void Window::setHelpPrompts(const std::vector& prompts, const HelpSt void Window::onSleep() { 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); + if (Settings::getInstance()->getString("ScreenSaverBehavior") != "dim") { + mDeepSleeping = true; + Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0x00000000 | 0xFF); + } else { + unsigned char opacity = mDeepSleeping ? 0xFF : (unsigned int)Settings::getInstance()->getInt("DimValue"); + Renderer::drawRect(0, 0, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0x00000000 | opacity); + } } void Window::onWake() { - } diff --git a/es-core/src/Window.h b/es-core/src/Window.h index 6576cb04a3..f15f66a3ea 100644 --- a/es-core/src/Window.h +++ b/es-core/src/Window.h @@ -28,7 +28,7 @@ class Window void normalizeNextUpdate(); - inline bool isSleeping() const { return mSleeping; } + inline bool isSleeping() const { return (mSleeping && mDeepSleeping); } bool getAllowSleep(); void setAllowSleep(bool sleep); @@ -58,6 +58,7 @@ class Window bool mAllowSleep; bool mSleeping; + bool mDeepSleeping; unsigned int mTimeSinceLastInput; bool mRenderedHelpPrompts; diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index 2898c6f732..b673604a6e 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -23,7 +23,7 @@ Eigen::Vector2f ImageComponent::getCenter() const } ImageComponent::ImageComponent(Window* window) : GuiComponent(window), - mTargetIsMax(false), mFlipX(false), mFlipY(false), mOrigin(0.0, 0.0), mTargetSize(0, 0), mColorShift(0xFFFFFFFF) + mTargetIsMax(false), mFlipX(false), mFlipY(false), mOrigin(0.0, 0.0), mTargetSize(0, 0), mColorShift(0xFFFFFFFF), mCentered(false), mFullscreen(false) { updateColors(); } @@ -43,6 +43,10 @@ void ImageComponent::resize() if(textureSize.isZero()) return; + if (mCentered) { + setPosition((mTargetSize.x() - mSize.x())/2.0f + mOrigPosition.x(), (mTargetSize.y() - mSize.y())/2.0f+ mOrigPosition.y(), mOrigPosition.z()); + } + if(mTexture->isTiled()) { mSize = mTargetSize; @@ -58,20 +62,21 @@ void ImageComponent::resize() mSize = textureSize; Eigen::Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y())); - - if(resizeScale.x() < resizeScale.y()) - { - mSize[0] *= resizeScale.x(); - mSize[1] *= resizeScale.x(); - }else{ - mSize[0] *= resizeScale.y(); - mSize[1] *= resizeScale.y(); + if (!mFullscreen) + { + if(resizeScale.x() < resizeScale.y()) + { + mSize[0] *= resizeScale.x(); + mSize[1] *= resizeScale.x(); + }else{ + mSize[0] *= resizeScale.y(); + mSize[1] *= resizeScale.y(); + } + // for SVG rasterization, always calculate width from rounded height (see comment above) + mSize[1] = round(mSize[1]); + mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x(); } - // for SVG rasterization, always calculate width from rounded height (see comment above) - mSize[1] = round(mSize[1]); - mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x(); - }else{ // if both components are set, we just stretch // if no components are set, we don't resize at all @@ -186,6 +191,11 @@ void ImageComponent::updateVertices() Eigen::Vector2f topLeft(-mSize.x() * mOrigin.x(), -mSize.y() * mOrigin.y()); Eigen::Vector2f bottomRight(mSize.x() * (1 -mOrigin.x()), mSize.y() * (1 - mOrigin.y())); + if (mFullscreen) { + topLeft << -mPosition.x(), -mPosition.y(); + bottomRight << (float)Renderer::getScreenWidth() - mPosition.x(), (float)Renderer::getScreenHeight() - mPosition.y(); + } + const float width = round(bottomRight.x() - topLeft.x()); const float height = round(bottomRight.y() - topLeft.y()); @@ -202,7 +212,9 @@ void ImageComponent::updateVertices() mVertices[4].pos << topLeft.x(), bottomRight.y(); mVertices[5].pos << bottomRight.x(), bottomRight.y(); - float px, py; + float px, py, ox, oy; + ox = 0; + oy = 0; if(mTexture->isTiled()) { px = mSize.x() / getTextureSize().x(); @@ -210,25 +222,38 @@ void ImageComponent::updateVertices() }else{ px = 1; py = 1; + if (mFullscreen) { + Eigen::Vector2f aspectRatio((width / height), (mSize.x() / mSize.y())); + if(aspectRatio.x() < aspectRatio.y()) + { + float l = aspectRatio.x()/(aspectRatio.y() * 2.0f); + ox = 0.5f - l; + px = 0.5f + l; + }else{ + float l = aspectRatio.y()/(aspectRatio.x() * 2.0f); + oy = 0.5f - l; + py = 0.5f + l; + } + } } - mVertices[0].tex << 0, py; - mVertices[1].tex << 0, 0; + mVertices[0].tex << ox, py; + mVertices[1].tex << ox, oy; mVertices[2].tex << px, py; mVertices[3].tex << px, py; - mVertices[4].tex << 0, 0; - mVertices[5].tex << px, 0; + mVertices[4].tex << ox, oy; + mVertices[5].tex << px, oy; if(mFlipX) { for(int i = 0; i < 6; i++) - mVertices[i].tex[0] = mVertices[i].tex[0] == px ? 0 : px; + mVertices[i].tex[0] = mVertices[i].tex[0] == px ? ox : px; } if(mFlipY) { for(int i = 1; i < 6; i++) - mVertices[i].tex[1] = mVertices[i].tex[1] == py ? 0 : py; + mVertices[i].tex[1] = mVertices[i].tex[1] == py ? oy : py; } } @@ -299,6 +324,17 @@ void ImageComponent::applyTheme(const std::shared_ptr& theme, const s { Eigen::Vector2f denormalized = elem->get("pos").cwiseProduct(scale); setPosition(Eigen::Vector3f(denormalized.x(), denormalized.y(), 0)); + mOrigPosition << mPosition; + } + + if (elem->has("centered") && elem->get("centered")) + { + mCentered = true; + } + + if (elem->has("fullscreen") && elem->get("fullscreen")) + { + mFullscreen = true; } if(properties & ThemeFlags::SIZE) @@ -321,6 +357,7 @@ void ImageComponent::applyTheme(const std::shared_ptr& theme, const s if(properties & COLOR && elem->has("color")) setColorShift(elem->get("color")); + resize(); } std::vector ImageComponent::getHelpPrompts() diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index 939c6c7cd2..dc63788d92 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -64,8 +64,9 @@ class ImageComponent : public GuiComponent private: Eigen::Vector2f mTargetSize; Eigen::Vector2f mOrigin; + Eigen::Vector3f mOrigPosition; - bool mFlipX, mFlipY, mTargetIsMax; + bool mFlipX, mFlipY, mTargetIsMax, mCentered, mFullscreen; // Calculates the correct mSize from our resizing information (set by setResize/setMaxSize). // Used internally whenever the resizing parameters or texture change. diff --git a/es-core/src/platform.cpp b/es-core/src/platform.cpp index e1663a5f97..ff20dda4b1 100644 --- a/es-core/src/platform.cpp +++ b/es-core/src/platform.cpp @@ -1,7 +1,9 @@ #include "platform.h" #include #include +#include #include +#include #ifdef WIN32 #include @@ -12,11 +14,17 @@ std::string getHomePath() std::string homePath; // this should give you something like "/home/YOUR_USERNAME" on Linux and "C:\Users\YOUR_USERNAME\" on Windows - const char * envHome = getenv("HOME"); + const char * envHome = getenv("EMULATIONSTATION_HOME"); if(envHome != nullptr) { homePath = envHome; - } + } else { + envHome = getenv("EMULATIONSTATION_HOME"); + if(envHome != nullptr) + { + homePath = envHome; + } + } #ifdef WIN32 // but does not seem to work for Windows XP or Vista, so try something else @@ -69,4 +77,20 @@ int runSystemCommand(const std::string& cmd_utf8) #else return system(cmd_utf8.c_str()); #endif +} + +int quitES(const std::string& filename) +{ + touch(filename); + SDL_Event* quit = new SDL_Event(); + quit->type = SDL_QUIT; + SDL_PushEvent(quit); + return 0; +} + +void touch(const std::string& filename) +{ + int fd = open(filename.c_str(), O_CREAT|O_WRONLY, 0644); + if (fd >= 0) + close(fd); } \ No newline at end of file diff --git a/es-core/src/platform.h b/es-core/src/platform.h index a0571b32ae..0760b565c1 100644 --- a/es-core/src/platform.h +++ b/es-core/src/platform.h @@ -21,4 +21,6 @@ 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) \ No newline at end of file +int runSystemCommand(const std::string& cmd_utf8); // run a utf-8 encoded in the shell (requires wstring conversion on Windows) +int quitES(const std::string& filename); +void touch(const std::string& filename); diff --git a/es-core/src/resources/TextureResource.cpp b/es-core/src/resources/TextureResource.cpp index e6657dcc20..1fe355b4fc 100644 --- a/es-core/src/resources/TextureResource.cpp +++ b/es-core/src/resources/TextureResource.cpp @@ -47,7 +47,7 @@ void TextureResource::initFromPixels(const unsigned char* dataRGBA, size_t width glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dataRGBA); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); const GLint wrapMode = mTile ? GL_REPEAT : GL_CLAMP_TO_EDGE; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);