/**************************************************************************** * Copyright (C) 2015 Dimok * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . ****************************************************************************/ #include "MainWindow.h" #include "Application.h" #include "utils/logger.h" #include "utils/StringTools.h" #include "resources/Resources.h" #include "gui/GuiTitleBrowser.h" #include "gui/GuiIconGrid.h" #include #include #include #include "utils/AsyncExecutor.h" #include "GameSplashScreen.h" MainWindow::MainWindow(int32_t w, int32_t h) : width(w), height(h), gameClickSound(Resources::GetSound("game_click.mp3")), mainSwitchButtonFrame(nullptr), currentTvFrame(nullptr), currentDrcFrame(nullptr) { for (int32_t i = 0; i < 4; i++) { std::string filename = StringTools::strfmt("player%i_point.png", i + 1); pointerImgData[i] = Resources::GetImageData(filename.c_str()); pointerImg[i] = new GuiImage(pointerImgData[i]); pointerImg[i]->setScale(1.5f); pointerValid[i] = false; } SetupMainView(); gameList.titleListChanged.connect(this, &MainWindow::OnGameTitleListChanged); gameList.titleUpdated.connect(this, &MainWindow::OnGameTitleUpdated); gameList.titleAdded.connect(this, &MainWindow::OnGameTitleAdded); AsyncExecutor::execute([&] { gameList.load(); }); } MainWindow::~MainWindow() { gameList.titleListChanged.disconnect(this); gameList.titleUpdated.disconnect(this); gameList.titleAdded.disconnect(this); while (!tvElements.empty()) { delete tvElements[0]; remove(tvElements[0]); } while (!drcElements.empty()) { delete drcElements[0]; remove(drcElements[0]); } for (int32_t i = 0; i < 4; i++) { delete pointerImg[i]; Resources::RemoveImageData(pointerImgData[i]); } Resources::RemoveSound(gameClickSound); } void MainWindow::updateEffects() { //! dont read behind the initial elements in case one was added uint32_t tvSize = tvElements.size(); uint32_t drcSize = drcElements.size(); for (uint32_t i = 0; (i < drcSize) && (i < drcElements.size()); ++i) { drcElements[i]->updateEffects(); } //! only update TV elements that are not updated yet because they are on DRC for (uint32_t i = 0; (i < tvSize) && (i < tvElements.size()); ++i) { uint32_t n; for (n = 0; (n < drcSize) && (n < drcElements.size()); n++) { if (tvElements[i] == drcElements[n]) break; } if (n == drcElements.size()) { tvElements[i]->updateEffects(); } } } void MainWindow::process() { //! dont read behind the initial elements in case one was added uint32_t tvSize = tvElements.size(); uint32_t drcSize = drcElements.size(); for (uint32_t i = 0; (i < drcSize) && (i < drcElements.size()); ++i) { drcElements[i]->process(); } //! only update TV elements that are not updated yet because they are on DRC for (uint32_t i = 0; (i < tvSize) && (i < tvElements.size()); ++i) { uint32_t n; for (n = 0; (n < drcSize) && (n < drcElements.size()); n++) { if (tvElements[i] == drcElements[n]) break; } if (n == drcElements.size()) { tvElements[i]->process(); } } if (keyboardInstance != nullptr) { if (keyboardInstance->checkResult()) { std::string result = keyboardInstance->getResult(); currentTvFrame->clearState(GuiElement::STATE_DISABLED); currentDrcFrame->clearState(GuiElement::STATE_DISABLED); mainSwitchButtonFrame->clearState(GuiElement::STATE_DISABLED); } else { } } } void MainWindow::OnGameTitleListChanged(GameList *list) { currentTvFrame->OnGameTitleListUpdated(list); if (currentTvFrame != currentDrcFrame) { currentDrcFrame->OnGameTitleListUpdated(list); } } void MainWindow::OnGameTitleUpdated(gameInfo *info) { currentTvFrame->OnGameTitleUpdated(info); if (currentTvFrame != currentDrcFrame) { currentDrcFrame->OnGameTitleUpdated(info); } } void MainWindow::OnGameTitleAdded(gameInfo *info) { currentTvFrame->OnGameTitleAdded(info); if (currentTvFrame != currentDrcFrame) { currentDrcFrame->OnGameTitleAdded(info); } } void MainWindow::update(GuiController *controller) { //! dont read behind the initial elements in case one was added //uint32_t tvSize = tvElements.size(); if (controller->chan & GuiTrigger::CHANNEL_1) { uint32_t drcSize = drcElements.size(); for (uint32_t i = 0; (i < drcSize) && (i < drcElements.size()); ++i) { drcElements[i]->update(controller); } } else { uint32_t tvSize = tvElements.size(); for (uint32_t i = 0; (i < tvSize) && (i < tvElements.size()); ++i) { tvElements[i]->update(controller); } } // //! only update TV elements that are not updated yet because they are on DRC // for(uint32_t i = 0; (i < tvSize) && (i < tvElements.size()); ++i) // { // uint32_t n; // for(n = 0; (n < drcSize) && (n < drcElements.size()); n++) // { // if(tvElements[i] == drcElements[n]) // break; // } // if(n == drcElements.size()) // { // tvElements[i]->update(controller); // } // } if (controller->chanIdx >= 1 && controller->chanIdx <= 4 && controller->data.validPointer) { int32_t wpadIdx = controller->chanIdx - 1; float posX = controller->data.x; float posY = controller->data.y; pointerImg[wpadIdx]->setPosition(posX, posY); pointerImg[wpadIdx]->setAngle(controller->data.pointerAngle); pointerValid[wpadIdx] = true; } } void MainWindow::drawDrc(CVideo *video) { for (uint32_t i = 0; i < drcElements.size(); ++i) { drcElements[i]->draw(video); } for (int32_t i = 0; i < 4; i++) { if (pointerValid[i]) { pointerImg[i]->setAlpha(0.5f); pointerImg[i]->draw(video); pointerImg[i]->setAlpha(1.0f); } } if (keyboardInstance != nullptr) { keyboardInstance->drawDRC(); } } void MainWindow::drawTv(CVideo *video) { for (uint32_t i = 0; i < tvElements.size(); ++i) { tvElements[i]->draw(video); } for (int32_t i = 0; i < 4; i++) { if (pointerValid[i]) { pointerImg[i]->draw(video); pointerValid[i] = false; } } if (keyboardInstance != nullptr) { keyboardInstance->drawTV(); } } void MainWindow::SetupMainView() { currentTvFrame = new GuiIconGrid(width, height, 0, true); currentTvFrame->setEffect(EFFECT_FADE, 10, 255); currentTvFrame->setState(GuiElement::STATE_DISABLED); currentTvFrame->effectFinished.connect(this, &MainWindow::OnOpenEffectFinish); appendTv(currentTvFrame); currentDrcFrame = new GuiIconGrid(width, height, 0, false); currentDrcFrame->setEffect(EFFECT_FADE, 10, 255); currentDrcFrame->setState(GuiElement::STATE_DISABLED); currentDrcFrame->effectFinished.connect(this, &MainWindow::OnOpenEffectFinish); if (currentTvFrame != currentDrcFrame) { currentDrcFrame->setEffect(EFFECT_FADE, 10, 255); currentDrcFrame->setState(GuiElement::STATE_DISABLED); currentDrcFrame->effectFinished.connect(this, &MainWindow::OnOpenEffectFinish); } //! reconnect only to DRC game selection change currentTvFrame->gameSelectionChanged.disconnect(this); currentDrcFrame->gameSelectionChanged.disconnect(this); currentTvFrame->gameLaunchClicked.disconnect(this); currentDrcFrame->gameLaunchClicked.disconnect(this); if (currentTvFrame != currentDrcFrame) { currentTvFrame->gameSelectionChanged.connect(this, &MainWindow::OnGameSelectionChange); currentTvFrame->gameLaunchClicked.connect(this, &MainWindow::OnGameLaunchSplashScreen); } currentDrcFrame->gameSelectionChanged.connect(this, &MainWindow::OnGameSelectionChange); currentDrcFrame->gameLaunchClicked.connect(this, &MainWindow::OnGameLaunchSplashScreen); mainSwitchButtonFrame = new MainDrcButtonsFrame(width, height); mainSwitchButtonFrame->settingsButtonClicked.connect(this, &MainWindow::OnSettingsButtonClicked); mainSwitchButtonFrame->layoutSwitchClicked.connect(this, &MainWindow::OnLayoutSwitchClicked); mainSwitchButtonFrame->gameListFilterClicked.connect(this, &MainWindow::OnGameListFilterButtonClicked); mainSwitchButtonFrame->setState(GuiElement::STATE_DISABLED); mainSwitchButtonFrame->setEffect(EFFECT_FADE, 10, 255); mainSwitchButtonFrame->setState(GuiElement::STATE_DISABLED); mainSwitchButtonFrame->effectFinished.connect(this, &MainWindow::OnOpenEffectFinish); appendDrc(currentDrcFrame); append(mainSwitchButtonFrame); } void MainWindow::OnLayoutSwitchClicked(GuiElement *element) { if (!currentTvFrame || !currentDrcFrame || !mainSwitchButtonFrame) { return; } if (currentTvFrame == currentDrcFrame) { return; } currentTvFrame->setState(GuiElement::STATE_DISABLED); currentTvFrame->setEffect(EFFECT_FADE, -15, 0); currentTvFrame->effectFinished.connect(this, &MainWindow::OnLayoutSwitchEffectFinish); currentDrcFrame->setState(GuiElement::STATE_DISABLED); currentDrcFrame->setEffect(EFFECT_FADE, -15, 0); mainSwitchButtonFrame->setState(GuiElement::STATE_DISABLED); } void MainWindow::OnGameListFilterButtonClicked(GuiElement *element) { if (!currentTvFrame || !currentDrcFrame || !mainSwitchButtonFrame) { return; } if (keyboardInstance == nullptr) { keyboardInstance = new KeyboardHelper(); } if (keyboardInstance->isReady()) { if (keyboardInstance->openKeyboard()) { currentTvFrame->setState(GuiElement::STATE_DISABLED); currentDrcFrame->setState(GuiElement::STATE_DISABLED); mainSwitchButtonFrame->setState(GuiElement::STATE_DISABLED); } } } void MainWindow::OnLayoutSwitchEffectFinish(GuiElement *element) { if (!currentTvFrame || !currentDrcFrame || !mainSwitchButtonFrame) return; element->effectFinished.disconnect(this); remove(currentDrcFrame); remove(currentTvFrame); GuiTitleBrowser *tmpElement = currentDrcFrame; currentDrcFrame = currentTvFrame; currentTvFrame = tmpElement; appendTv(currentTvFrame); appendDrc(currentDrcFrame); //! re-append on top append(mainSwitchButtonFrame); currentTvFrame->resetState(); currentTvFrame->setEffect(EFFECT_FADE, 15, 255); currentDrcFrame->resetState(); currentDrcFrame->setEffect(EFFECT_FADE, 15, 255); mainSwitchButtonFrame->clearState(GuiElement::STATE_DISABLED); //! reconnect only to DRC game selection change currentTvFrame->gameSelectionChanged.disconnect(this); currentDrcFrame->gameSelectionChanged.disconnect(this); currentTvFrame->gameLaunchClicked.disconnect(this); currentDrcFrame->gameLaunchClicked.disconnect(this); currentTvFrame->gameSelectionChanged.connect(this, &MainWindow::OnGameSelectionChange); currentTvFrame->gameLaunchClicked.connect(this, &MainWindow::OnGameLaunchSplashScreen); currentDrcFrame->gameSelectionChanged.connect(this, &MainWindow::OnGameSelectionChange); currentDrcFrame->gameLaunchClicked.connect(this, &MainWindow::OnGameLaunchSplashScreen); } void MainWindow::OnOpenEffectFinish(GuiElement *element) { //! once the menu is open reset its state and allow it to be "clicked/hold" element->effectFinished.disconnect(this); element->clearState(GuiElement::STATE_DISABLED); } void MainWindow::OnCloseEffectFinish(GuiElement *element) { //! remove element from draw list and push to delete queue remove(element); AsyncExecutor::pushForDelete(element); } void MainWindow::OnSettingsButtonClicked(GuiElement *element) { } void MainWindow::OnGameSelectionChange(GuiTitleBrowser *element, uint64_t selectedIdx) { if (!currentDrcFrame || !currentTvFrame) return; if (element == currentDrcFrame && currentDrcFrame != currentTvFrame) { currentTvFrame->setSelectedGame(selectedIdx); } else if (element == currentTvFrame && currentDrcFrame != currentTvFrame) { currentDrcFrame->setSelectedGame(selectedIdx); } } #define HBL_TITLE_ID (0x0005000013374842) #define MII_MAKER_JPN_TITLE_ID (0x000500101004A000) #define MII_MAKER_USA_TITLE_ID (0x000500101004A100) #define MII_MAKER_EUR_TITLE_ID (0x000500101004A200) extern "C" void _SYSLaunchTitleByPathFromLauncher(const char *path, int len, int); void MainWindow::OnGameLaunchSplashScreen(GuiTitleBrowser *element, uint64_t titleID) { gameInfo *info = gameList.getGameInfo(titleID); if (info != nullptr) { uint64_t ownTitleId = OSGetTitleID(); if (ownTitleId == HBL_TITLE_ID || ownTitleId == MII_MAKER_JPN_TITLE_ID || ownTitleId == MII_MAKER_USA_TITLE_ID || ownTitleId == MII_MAKER_EUR_TITLE_ID) { OnGameLaunch(titleID); } else { GameSplashScreen *gameSettingsDRC = new GameSplashScreen(width, height, info, false); gameSettingsDRC->setEffect(EFFECT_FADE, 15, 255); gameSettingsDRC->setState(GuiElement::STATE_DISABLED); gameSettingsDRC->effectFinished.connect(this, &MainWindow::OnOpenEffectFinish); gameSettingsDRC->gameGameSplashScreenFinished.connect(this, &MainWindow::OnGameLaunchSplashScreenFinished); appendDrc(gameSettingsDRC); GameSplashScreen *gameSettingsTV = new GameSplashScreen(width, height, info, true); gameSettingsTV->setEffect(EFFECT_FADE, 15, 255); gameSettingsTV->setState(GuiElement::STATE_DISABLED); gameSettingsTV->effectFinished.connect(this, &MainWindow::OnOpenEffectFinish); gameSettingsTV->gameGameSplashScreenFinished.connect(this, &MainWindow::OnGameLaunchSplashScreenFinished); appendTv(gameSettingsTV); } } else { DEBUG_FUNCTION_LINE("Failed to find gameInfo for titleId %016llX\n", titleID); } } void MainWindow::OnGameLaunchSplashScreenFinished(GuiElement *element, gameInfo *info, bool launchGame) { if (info == nullptr) { return; } if (launchGame) { OnGameLaunch(info->titleId); } if (element) { element->setState(GuiElement::STATE_DISABLED); element->setEffect(EFFECT_FADE, 15, 255); element->effectFinished.connect(this, &MainWindow::OnCloseEffectFinish); } } void MainWindow::OnGameLaunch(uint64_t titleID) { gameInfo *info = gameList.getGameInfo(titleID); if (info != nullptr) { uint64_t titleID = OSGetTitleID(); if (titleID == HBL_TITLE_ID || titleID == MII_MAKER_JPN_TITLE_ID || titleID == MII_MAKER_USA_TITLE_ID || titleID == MII_MAKER_EUR_TITLE_ID) { SYSLaunchTitle(info->titleId); } else { const char *path = info->gamePath.c_str(); _SYSLaunchTitleByPathFromLauncher(path, strlen(path), 0); } } else { DEBUG_FUNCTION_LINE("Failed to find gameInfo for titleId %016llX\n", titleID); } }