diff --git a/Core/ELF/PBPReader.h b/Core/ELF/PBPReader.h index 791365af12..ab68ea73ba 100644 --- a/Core/ELF/PBPReader.h +++ b/Core/ELF/PBPReader.h @@ -24,7 +24,7 @@ enum PBPSubFile { PBP_PARAM_SFO, PBP_ICON0_PNG, PBP_ICON1_PMF, - PBP_UNKNOWN_PNG, // PIC0? + PBP_PIC0_PNG, PBP_PIC1_PNG, PBP_SND0_AT3, PBP_EXECUTABLE_PSP, diff --git a/GPU/GLES/DepalettizeShader.cpp b/GPU/GLES/DepalettizeShader.cpp index 48840c58e2..df409876e1 100644 --- a/GPU/GLES/DepalettizeShader.cpp +++ b/GPU/GLES/DepalettizeShader.cpp @@ -240,7 +240,7 @@ void GenerateDepalShader100(char *buffer, GEBufferFormat pixelFormat) { case GE_FORMAT_565: if ((mask & (mask + 1)) == 0 && shift < 16) { const u8 shifts[16] = {0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4}; - const u32 multipliers[16] = {31, 31, 31, 31, 31, 63, 63, 63, 63, 63, 63, 31, 31, 31, 31, 31}; + const int multipliers[16] = {31, 31, 31, 31, 31, 63, 63, 63, 63, 63, 63, 31, 31, 31, 31, 31}; const char *rgba = "rrrrrggggggbbbbb"; const u8 rgba_shift = shifts[shift]; if (rgba_shift == 0 && mask == multipliers[shift]) { diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 01d2c9fdb8..34cd18e2a0 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -293,12 +293,26 @@ public: } delete [] contents; } + info_->iconDataLoaded = true; } - if (info_->wantBG) { + if (info_->wantFlags & GAMEINFO_WANTBG) { + if (pbp.GetSubFileSize(PBP_PIC0_PNG) > 0) { + lock_guard lock(info_->lock); + pbp.GetSubFileAsString(PBP_PIC0_PNG, &info_->pic0TextureData); + info_->pic0DataLoaded = true; + } if (pbp.GetSubFileSize(PBP_PIC1_PNG) > 0) { lock_guard lock(info_->lock); pbp.GetSubFileAsString(PBP_PIC1_PNG, &info_->pic1TextureData); + info_->pic1DataLoaded = true; + } + } + if (info_->wantFlags & GAMEINFO_WANTSND) { + if (pbp.GetSubFileSize(PBP_SND0_AT3) > 0) { + lock_guard lock(info_->lock); + pbp.GetSubFileAsString(PBP_SND0_AT3, &info_->sndFileData); + info_->sndDataLoaded = true; } } } @@ -319,6 +333,7 @@ handleELF: if (contents) { lock_guard lock(info_->lock); info_->iconTextureData = std::string((const char *)contents, sz); + info_->iconDataLoaded = true; } delete [] contents; } @@ -339,10 +354,17 @@ handleELF: } ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->iconTextureData, &info_->lock); - if (info_->wantBG) { + info_->iconDataLoaded = true; + if (info_->wantFlags & GAMEINFO_WANTBG) { ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0TextureData, &info_->lock); + info_->pic0DataLoaded = true; + ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1TextureData, &info_->lock); + info_->pic1DataLoaded = true; + } + if (info_->wantFlags & GAMEINFO_WANTSND) { + ReadFileToString(&umd, "/PSP_GAME/SND0.AT3", &info_->sndFileData, &info_->lock); + info_->pic1DataLoaded = true; } - ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1TextureData, &info_->lock); break; } case FILETYPE_PSP_ISO: @@ -365,10 +387,16 @@ handleELF: info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); info_->ParseParamSFO(); - if (info_->wantBG) { + if (info_->wantFlags & GAMEINFO_WANTBG) { ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0TextureData, &info_->lock); + info_->pic0DataLoaded = true; + ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1TextureData, &info_->lock); + info_->pic1DataLoaded = true; + } + if (info_->wantFlags & GAMEINFO_WANTSND) { + ReadFileToString(&umd, "/PSP_GAME/SND0.AT3", &info_->sndFileData, &info_->lock); + info_->pic1DataLoaded = true; } - ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1TextureData, &info_->lock); } // Fall back to unknown icon if ISO is broken/is a homebrew ISO, override is allowed though @@ -382,12 +410,12 @@ handleELF: } delete [] contents; } + info_->iconDataLoaded = true; break; } case FILETYPE_ARCHIVE_ZIP: info_->paramSFOLoaded = true; - info_->wantBG = false; { // Read standard icon size_t sz; @@ -395,6 +423,7 @@ handleELF: if (contents) { lock_guard lock(info_->lock); info_->iconTextureData = std::string((const char *)contents, sz); + info_->iconDataLoaded = true; } delete [] contents; } @@ -402,7 +431,6 @@ handleELF: case FILETYPE_ARCHIVE_RAR: info_->paramSFOLoaded = true; - info_->wantBG = false; { // Read standard icon size_t sz; @@ -410,6 +438,7 @@ handleELF: if (contents) { lock_guard lock(info_->lock); info_->iconTextureData = std::string((const char *)contents, sz); + info_->iconDataLoaded = true; } delete [] contents; } @@ -418,12 +447,10 @@ handleELF: case FILETYPE_NORMAL_DIRECTORY: default: info_->paramSFOLoaded = true; - info_->wantBG = false; break; } - // probably only want these when we ask for the background image... - // should maybe flip the flag to "onlyIcon" - if (info_->wantBG) { + + if (info_->wantFlags & GAMEINFO_WANTSIZE) { info_->gameSize = info_->GetGameSizeInBytes(); info_->saveDataSize = info_->GetSaveDataSizeInBytes(); info_->installDataSize = info_->GetInstallDataSizeInBytes(); @@ -455,8 +482,7 @@ void GameInfoCache::Shutdown() { StopProcessingWorkQueue(gameInfoWQ_); } -void GameInfoCache::Save() -{ +void GameInfoCache::Save() { // TODO } @@ -515,61 +541,31 @@ void GameInfoCache::FlushBGs() { delete iter->second->pic1Texture; iter->second->pic1Texture = 0; } - iter->second->wantBG = false; + iter->second->wantFlags &= ~GAMEINFO_WANTBG; } } // This may run off-main-thread and we thus can't use the global // pspFileSystem (well, we could with synchronization but there might not // even be a game running). -GameInfo *GameInfoCache::GetInfo(const std::string &gamePath, bool wantBG) { +GameInfo *GameInfoCache::GetInfo(const std::string &gamePath, int wantFlags) { + GameInfo *info = 0; + auto iter = info_.find(gamePath); if (iter != info_.end()) { - GameInfo *info = iter->second; - if (!info->wantBG && wantBG) { + info = iter->second; + if ((info->wantFlags & wantFlags) != wantFlags) { // Need to start over. We'll just add a new work item. - delete info; // Hm, how dangerous is this? There might be a race condition here. goto again; } - { - lock_guard lock(info->lock); - if (info->iconTextureData.size()) { - // We'd have to split up Texture->LoadPNG though, creating some intermediate Image class maybe. - info->iconTexture = new Texture(); - if (info->iconTexture->LoadPNG((const u8 *)info->iconTextureData.data(), info->iconTextureData.size(), false)) { - info->timeIconWasLoaded = time_now_d(); - } else { - delete info->iconTexture; - info->iconTexture = 0; - } - info->iconTextureData.clear(); - } + if (info->iconDataLoaded) { + SetupTexture(info, info->iconTextureData, info->iconTexture, info->timeIconWasLoaded); } - { - lock_guard lock(info->lock); - if (info->pic0TextureData.size()) { - info->pic0Texture = new Texture(); - if (info->pic0Texture->LoadPNG((const u8 *)info->pic0TextureData.data(), info->pic0TextureData.size(), false)) { - info->timePic0WasLoaded = time_now_d(); - } else { - delete info->pic0Texture; - info->pic0Texture = 0; - } - info->pic0TextureData.clear(); - } + if (info->pic0DataLoaded) { + SetupTexture(info, info->pic0TextureData, info->pic0Texture, info->timePic0WasLoaded); } - { - lock_guard lock(info->lock); - if (info->pic1TextureData.size()) { - info->pic1Texture = new Texture(); - if (info->pic1Texture->LoadPNG((const u8 *)info->pic1TextureData.data(), info->pic1TextureData.size(), false)) { - info->timePic1WasLoaded = time_now_d(); - } else { - delete info->pic1Texture; - info->pic1Texture = 0; - } - info->pic1TextureData.clear(); - } + if (info->pic1DataLoaded) { + SetupTexture(info, info->pic1TextureData, info->pic1Texture, info->timePic1WasLoaded); } iter->second->lastAccessedTime = time_now_d(); return iter->second; @@ -577,8 +573,13 @@ GameInfo *GameInfoCache::GetInfo(const std::string &gamePath, bool wantBG) { again: - GameInfo *info = new GameInfo(); - info->wantBG = wantBG; + if (!info) { + info = new GameInfo(); + } + { + lock_guard lock(info->lock); + info->wantFlags |= wantFlags; + } GameInfoWorkItem *item = new GameInfoWorkItem(gamePath, info); gameInfoWQ_->Add(item); @@ -586,3 +587,18 @@ again: info_[gamePath] = info; return info; } + +void GameInfoCache::SetupTexture(GameInfo *info, std::string &textureData, Texture *&tex, double &loadTime) { + if (textureData.size()) { + if (!tex) { + tex = new Texture(); + if (tex->LoadPNG((const u8 *)textureData.data(), textureData.size(), false)) { + loadTime = time_now_d(); + } else { + delete tex; + tex = 0; + } + } + textureData.clear(); + } +} diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index 8a79830fef..47ca650c91 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -42,11 +42,58 @@ enum GameRegion { GAMEREGION_MAX, }; +enum GameInfoWantFlags { + GAMEINFO_WANTBG = 0x01, + GAMEINFO_WANTSIZE = 0x02, + GAMEINFO_WANTSND = 0x04, +}; + +// TODO: Need to fix c++11 still on Symbian and use std::atomic instead. +class CompletionFlag { +public: + CompletionFlag() : pending(1) { + } + + void SetDone() { +#if defined(_WIN32) + _WriteBarrier(); + pending = 0; +#else + __sync_lock_release(&pending); +#endif + } + + bool IsDone() { + const bool done = pending == 0; +#if defined(_WIN32) + _ReadBarrier(); +#else + __sync_synchronize(); +#endif + return done; + } + + CompletionFlag &operator =(const bool &v) { + pending = v ? 0 : 1; + return *this; + } + + operator bool() { + return IsDone(); + } + +private: + volatile u32 pending; + + DISALLOW_COPY_AND_ASSIGN(CompletionFlag); +}; + class GameInfo { public: GameInfo() : disc_total(0), disc_number(0), region(-1), fileType(FILETYPE_UNKNOWN), paramSFOLoaded(false), - iconTexture(NULL), pic0Texture(NULL), pic1Texture(NULL), wantBG(false), + iconTexture(NULL), pic0Texture(NULL), pic1Texture(NULL), wantFlags(0), + timeIconWasLoaded(0.0), timePic0WasLoaded(0.0), timePic1WasLoaded(0.0), gameSize(0), saveDataSize(0), installDataSize(0) {} bool DeleteGame(); // Better be sure what you're doing when calling this. @@ -87,7 +134,9 @@ public: std::string pic1TextureData; Texture *pic1Texture; - bool wantBG; + std::string sndFileData; + + int wantFlags; double lastAccessedTime; @@ -97,6 +146,11 @@ public: double timePic0WasLoaded; double timePic1WasLoaded; + CompletionFlag iconDataLoaded; + CompletionFlag pic0DataLoaded; + CompletionFlag pic1DataLoaded; + CompletionFlag sndDataLoaded; + u64 gameSize; u64 saveDataSize; u64 installDataSize; @@ -116,7 +170,7 @@ public: // but filled in later asynchronously in the background. So keep calling this, // redrawing the UI often. Only set wantBG if you really want it because // it's big. bgTextures may be discarded over time as well. - GameInfo *GetInfo(const std::string &gamePath, bool wantBG); + GameInfo *GetInfo(const std::string &gamePath, int wantFlags); void Decimate(); // Deletes old info. void FlushBGs(); // Gets rid of all BG textures. @@ -125,6 +179,8 @@ public: void Load(); private: + void SetupTexture(GameInfo *info, std::string &textureData, Texture *&tex, double &loadTime); + // Maps ISO path to info. std::map info_; diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 6f4f547cfb..df36195c7e 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include "base/colorutil.h" #include "base/timeutil.h" #include "gfx_es2/draw_buffer.h" @@ -34,7 +35,7 @@ #include "Core/Config.h" void GameScreen::CreateViews() { - GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, true); + GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); I18NCategory *d = GetI18NCategory("Dialog"); I18NCategory *ga = GetI18NCategory("Game"); @@ -91,13 +92,21 @@ void GameScreen::update(InputState &input) { UIScreen::update(input); I18NCategory *ga = GetI18NCategory("Game"); - GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, true); + GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); if (tvTitle_) tvTitle_->SetText(info->title + " (" + info->id + ")"); if (info->iconTexture && texvGameIcon_) { texvGameIcon_->SetTexture(info->iconTexture); - uint32_t color = whiteAlpha(ease((time_now_d() - info->timeIconWasLoaded) * 3)); + // Fade the icon with the background. + double loadTime = info->timeIconWasLoaded; + if (info->pic1Texture) { + loadTime = std::max(loadTime, info->timePic1WasLoaded); + } + if (info->pic0Texture) { + loadTime = std::max(loadTime, info->timePic0WasLoaded); + } + uint32_t color = whiteAlpha(ease((time_now_d() - loadTime) * 3)); texvGameIcon_->SetColor(color); } @@ -144,7 +153,7 @@ UI::EventReturn GameScreen::OnPlay(UI::EventParams &e) { } UI::EventReturn GameScreen::OnGameSettings(UI::EventParams &e) { - GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, true); + GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); if (info && info->paramSFOLoaded) { std::string discID = info->paramSFO.GetValueString("DISC_ID"); screenManager()->push(new GameSettingsScreen(gamePath_, discID)); @@ -155,7 +164,7 @@ UI::EventReturn GameScreen::OnGameSettings(UI::EventParams &e) { UI::EventReturn GameScreen::OnDeleteSaveData(UI::EventParams &e) { I18NCategory *d = GetI18NCategory("Dialog"); I18NCategory *ga = GetI18NCategory("Game"); - GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, true); + GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); if (info) { screenManager()->push( new PromptScreen(d->T("DeleteConfirmAll", "Do you really want to delete all\nyour save data for this game?"), ga->T("ConfirmDelete"), d->T("Cancel"), @@ -167,7 +176,7 @@ UI::EventReturn GameScreen::OnDeleteSaveData(UI::EventParams &e) { } void GameScreen::CallbackDeleteSaveData(bool yes) { - GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, false); + GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, 0); if (yes) { info->DeleteAllSaveData(); info->saveDataSize = 0; @@ -178,7 +187,7 @@ void GameScreen::CallbackDeleteSaveData(bool yes) { UI::EventReturn GameScreen::OnDeleteGame(UI::EventParams &e) { I18NCategory *d = GetI18NCategory("Dialog"); I18NCategory *ga = GetI18NCategory("Game"); - GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, true); + GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); if (info) { screenManager()->push( new PromptScreen(d->T("DeleteConfirmGame", "Do you really want to delete this game\nfrom your device? You can't undo this."), ga->T("ConfirmDelete"), d->T("Cancel"), @@ -189,7 +198,7 @@ UI::EventReturn GameScreen::OnDeleteGame(UI::EventParams &e) { } void GameScreen::CallbackDeleteGame(bool yes) { - GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, false); + GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, 0); if (yes) { info->DeleteGame(); g_gameInfoCache.Clear(); @@ -198,7 +207,7 @@ void GameScreen::CallbackDeleteGame(bool yes) { } UI::EventReturn GameScreen::OnCreateShortcut(UI::EventParams &e) { - GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, false); + GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, 0); if (info) { host->CreateDesktopShortcut(gamePath_, info->title); } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 56b172bbb9..42cbc0997f 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -54,7 +54,7 @@ extern bool iosCanUseJit; #endif void GameSettingsScreen::CreateViews() { - GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, true); + GameInfo *info = g_gameInfoCache.GetInfo(gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); cap60FPS_ = g_Config.iForceMaxEmulatedFPS == 60; diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index c1b1bacc6c..286bc76175 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -138,7 +138,13 @@ public: } } + virtual void FocusChanged(int focusFlags) { + UI::Clickable::FocusChanged(focusFlags); + TriggerOnHighlight(focusFlags); + } + UI::Event OnHoldClick; + UI::Event OnHighlight; private: void TriggerOnHoldClick() { @@ -149,6 +155,13 @@ private: down_ = false; OnHoldClick.Trigger(e); } + void TriggerOnHighlight(int focusFlags) { + UI::EventParams e; + e.v = this; + e.s = gamePath_; + e.a = focusFlags; + OnHighlight.Trigger(e); + } bool gridStyle_; std::string gamePath_; @@ -160,7 +173,7 @@ private: }; void GameButton::Draw(UIContext &dc) { - GameInfo *ginfo = g_gameInfoCache.GetInfo(gamePath_, false); + GameInfo *ginfo = g_gameInfoCache.GetInfo(gamePath_, 0); Texture *texture = 0; u32 color = 0, shadowColor = 0; @@ -376,6 +389,7 @@ public: UI::Event OnChoice; UI::Event OnHoldChoice; + UI::Event OnHighlight; UI::Choice *HomebrewStoreButton() { return homebrewStoreButton_; } private: @@ -386,6 +400,7 @@ private: UI::EventReturn GameButtonClick(UI::EventParams &e); UI::EventReturn GameButtonHoldClick(UI::EventParams &e); + UI::EventReturn GameButtonHighlight(UI::EventParams &e); UI::EventReturn NavigateClick(UI::EventParams &e); UI::EventReturn LayoutChange(UI::EventParams &e); UI::EventReturn LastClick(UI::EventParams &e); @@ -568,6 +583,7 @@ void GameBrowser::Refresh() { GameButton *b = gameList_->Add(gameButtons[i]); b->OnClick.Handle(this, &GameBrowser::GameButtonClick); b->OnHoldClick.Handle(this, &GameBrowser::GameButtonHoldClick); + b->OnHighlight.Handle(this, &GameBrowser::GameButtonHighlight); } // Show a button to toggle pinning at the very end. @@ -667,6 +683,12 @@ UI::EventReturn GameBrowser::GameButtonHoldClick(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn GameBrowser::GameButtonHighlight(UI::EventParams &e) { + // Insta-update - here we know we are already on the right thread. + OnHighlight.Trigger(e); + return UI::EVENT_DONE; +} + UI::EventReturn GameBrowser::NavigateClick(UI::EventParams &e) { DirButton *button = static_cast(e.v); std::string text = button->GetPath(); @@ -680,7 +702,7 @@ UI::EventReturn GameBrowser::NavigateClick(UI::EventParams &e) { return UI::EVENT_DONE; } -MainScreen::MainScreen() : backFromStore_(false) { +MainScreen::MainScreen() : highlightProgress_(0.0f), prevHighlightProgress_(0.0f), backFromStore_(false) { System_SendMessage("event", "mainscreen"); } @@ -736,6 +758,9 @@ void MainScreen::CreateViews() { tabRecentGames->OnHoldChoice.Handle(this, &MainScreen::OnGameSelected); tabAllGames->OnHoldChoice.Handle(this, &MainScreen::OnGameSelected); tabHomebrew->OnHoldChoice.Handle(this, &MainScreen::OnGameSelected); + tabRecentGames->OnHighlight.Handle(this, &MainScreen::OnGameHighlight); + tabAllGames->OnHighlight.Handle(this, &MainScreen::OnGameHighlight); + tabHomebrew->OnHighlight.Handle(this, &MainScreen::OnGameHighlight); if (g_Config.recentIsos.size() > 0) { leftColumn->SetCurrentTab(0); @@ -884,6 +909,59 @@ UI::EventReturn MainScreen::OnLoadFile(UI::EventParams &e) { return UI::EVENT_DONE; } +extern void DrawBackground(UIContext &dc, float alpha); + +void MainScreen::DrawBackground(UIContext &dc) { + UIScreenWithBackground::DrawBackground(dc); + if (highlightedGamePath_.empty() && prevHighlightedGamePath_.empty()) { + return; + } + + if (DrawBackgroundFor(dc, prevHighlightedGamePath_, 1.0f - prevHighlightProgress_)) { + if (prevHighlightProgress_ < 1.0f) { + prevHighlightProgress_ += 1.0f / 20.0f; + } + } + if (!highlightedGamePath_.empty()) { + if (DrawBackgroundFor(dc, highlightedGamePath_, highlightProgress_)) { + if (highlightProgress_ < 1.0f) { + highlightProgress_ += 1.0f / 20.0f; + } + } + } +} + +bool MainScreen::DrawBackgroundFor(UIContext &dc, const std::string &gamePath, float progress) { + dc.Flush(); + + GameInfo *ginfo = 0; + if (!gamePath.empty()) { + ginfo = g_gameInfoCache.GetInfo(gamePath, GAMEINFO_WANTBG); + // Loading texture data may bind a texture. + dc.RebindTexture(); + + // Let's not bother if there's no picture. + if (!ginfo || (!ginfo->pic1Texture && !ginfo->pic0Texture)) { + return false; + } + } else { + return false; + } + + if (ginfo->pic1Texture) { + ginfo->pic1Texture->Bind(0); + } else if (ginfo->pic0Texture) { + ginfo->pic0Texture->Bind(0); + } + + uint32_t color = whiteAlpha(ease(progress)) & 0xFFc0c0c0; + dc.Draw()->DrawTexRect(dc.GetBounds(), 0,0,1,1, color); + dc.Flush(); + dc.RebindTexture(); + + return true; +} + UI::EventReturn MainScreen::OnGameSelected(UI::EventParams &e) { #ifdef _WIN32 std::string path = ReplaceAll(e.s, "\\", "/"); @@ -894,6 +972,27 @@ UI::EventReturn MainScreen::OnGameSelected(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn MainScreen::OnGameHighlight(UI::EventParams &e) { + #ifdef _WIN32 + std::string path = ReplaceAll(e.s, "\\", "/"); +#else + std::string path = e.s; +#endif + + if (!highlightedGamePath_.empty() || (e.a == FF_LOSTFOCUS && highlightedGamePath_ == path)) { + if (prevHighlightedGamePath_.empty() || prevHighlightProgress_ >= 0.75f) { + prevHighlightedGamePath_ = highlightedGamePath_; + prevHighlightProgress_ = 1.0 - highlightProgress_; + } + highlightedGamePath_.clear(); + } + if (e.a == FF_GOTFOCUS) { + highlightedGamePath_ = path; + highlightProgress_ = 0.0f; + } + return UI::EVENT_DONE; +} + UI::EventReturn MainScreen::OnGameSelectedInstant(UI::EventParams &e) { #ifdef _WIN32 std::string path = ReplaceAll(e.s, "\\", "/"); diff --git a/UI/MainScreen.h b/UI/MainScreen.h index 791aa28e78..093cbf5a73 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -37,13 +37,17 @@ public: protected: virtual void CreateViews(); + virtual void DrawBackground(UIContext &dc); virtual void update(InputState &input); virtual void sendMessage(const char *message, const char *value); virtual void dialogFinished(const Screen *dialog, DialogResult result); private: + bool DrawBackgroundFor(UIContext &dc, const std::string &gamePath, float progress); + UI::EventReturn OnGameSelected(UI::EventParams &e); UI::EventReturn OnGameSelectedInstant(UI::EventParams &e); + UI::EventReturn OnGameHighlight(UI::EventParams &e); // Event handlers UI::EventReturn OnLoadFile(UI::EventParams &e); UI::EventReturn OnGameSettings(UI::EventParams &e); @@ -60,6 +64,10 @@ private: UI::LinearLayout *upgradeBar_; UI::TabHolder *tabHolder_; + std::string highlightedGamePath_; + std::string prevHighlightedGamePath_; + float highlightProgress_; + float prevHighlightProgress_; bool backFromStore_; }; diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index b9c78bca5f..e0bccc56ae 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -105,20 +105,23 @@ void DrawBackground(UIContext &dc, float alpha = 1.0f) { } void DrawGameBackground(UIContext &dc, const std::string &gamePath) { - GameInfo *ginfo = g_gameInfoCache.GetInfo(gamePath, true); + GameInfo *ginfo = g_gameInfoCache.GetInfo(gamePath, GAMEINFO_WANTBG); dc.Flush(); if (ginfo) { bool hasPic = false; + double loadTime; if (ginfo->pic1Texture) { ginfo->pic1Texture->Bind(0); + loadTime = ginfo->timePic1WasLoaded; hasPic = true; } else if (ginfo->pic0Texture) { ginfo->pic0Texture->Bind(0); + loadTime = ginfo->timePic0WasLoaded; hasPic = true; } if (hasPic) { - uint32_t color = whiteAlpha(ease((time_now_d() - ginfo->timePic1WasLoaded) * 3)) & 0xFFc0c0c0; + uint32_t color = whiteAlpha(ease((time_now_d() - loadTime) * 3)) & 0xFFc0c0c0; dc.Draw()->DrawTexRect(dc.GetBounds(), 0,0,1,1, color); dc.Flush(); dc.RebindTexture();