// Copyright 2014 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #include #include #include #include #include #include #include #include "ui_MainWindow.h" #include "Core/BootManager.h" #include "Core/ConfigManager.h" #include "Core/HW/ProcessorInterface.h" #include "DolphinQt/AboutDialog.h" #include "DolphinQt/Host.h" #include "DolphinQt/MainWindow.h" #include "DolphinQt/SystemInfo.h" #include "DolphinQt/Utils/Resources.h" #include "DolphinQt/Utils/Utils.h" #include "VideoCommon/VideoConfig.h" // The "g_main_window" object as defined in MainWindow.h DMainWindow* g_main_window = nullptr; DMainWindow::DMainWindow(QWidget* parent_widget) : QMainWindow(parent_widget) { m_ui = std::make_unique(); m_ui->setupUi(this); Resources::Init(); UpdateIcons(); setWindowIcon(Resources::GetIcon(Resources::DOLPHIN_LOGO)); // Create the GameList m_game_tracker = new DGameTracker(this); m_ui->centralWidget->addWidget(m_game_tracker); m_game_tracker->ScanForGames(); m_game_tracker->SelectLastBootedGame(); // Setup the GameList style switching actions QActionGroup* gamelistGroup = new QActionGroup(this); gamelistGroup->addAction(m_ui->actionListView); gamelistGroup->addAction(m_ui->actionTreeView); gamelistGroup->addAction(m_ui->actionGridView); gamelistGroup->addAction(m_ui->actionIconView); // TODO: save/load this from user prefs! m_ui->actionListView->setChecked(true); OnGameListStyleChanged(); // Connect all the signals/slots connect(this, &DMainWindow::CoreStateChanged, this, &DMainWindow::OnCoreStateChanged); connect(m_ui->actionOpen, &QAction::triggered, this, [&]() { QString filename = ShowFileDialog(); if (!filename.isNull()) StartGame(filename); }); connect(m_ui->actionBrowse, &QAction::triggered, this, &DMainWindow::OnBrowse); connect(m_ui->actionExit, &QAction::triggered, this, [&]() { close(); }); connect(m_ui->actionListView, &QAction::triggered, this, &DMainWindow::OnGameListStyleChanged); connect(m_ui->actionTreeView, &QAction::triggered, this, &DMainWindow::OnGameListStyleChanged); connect(m_ui->actionGridView, &QAction::triggered, this, &DMainWindow::OnGameListStyleChanged); connect(m_ui->actionIconView, &QAction::triggered, this, &DMainWindow::OnGameListStyleChanged); connect(m_ui->actionPlay, &QAction::triggered, this, &DMainWindow::OnPlay); connect(m_game_tracker, &DGameTracker::StartGame, this, &DMainWindow::OnPlay); connect(m_ui->actionStop, &QAction::triggered, this, &DMainWindow::OnStop); connect(m_ui->actionReset, &QAction::triggered, this, &DMainWindow::OnReset); connect(m_ui->actionScreenshot, &QAction::triggered, this, []() { Core::SaveScreenShot(); }); connect(m_ui->actionWebsite, &QAction::triggered, this, []() { QDesktopServices::openUrl(QUrl(SL("https://dolphin-emu.org/"))); }); connect(m_ui->actionOnlineDocs, &QAction::triggered, this, []() { QDesktopServices::openUrl(QUrl(SL("https://dolphin-emu.org/docs/guides/"))); }); connect(m_ui->actionGitHub, &QAction::triggered, this, []() { QDesktopServices::openUrl(QUrl(SL("https://github.com/dolphin-emu/dolphin"))); }); connect(m_ui->actionSystemInfo, &QAction::triggered, this, [&]() { DSystemInfo* dlg = new DSystemInfo(this); dlg->open(); }); connect(m_ui->actionAbout, &QAction::triggered, this, [&]() { DAboutDialog* dlg = new DAboutDialog(this); dlg->open(); }); connect(m_ui->actionAboutQt, &QAction::triggered, this, [&]() { QApplication::aboutQt(); }); // Update GUI items emit CoreStateChanged(Core::CORE_UNINITIALIZED); // Platform-specific stuff #ifdef Q_OS_MACX m_ui->toolbar->setMovable(false); #endif } DMainWindow::~DMainWindow() { } bool DMainWindow::event(QEvent* e) { if (e->type() == HostEvent::TitleEvent) { HostTitleEvent* htev = (HostTitleEvent*)e; m_ui->statusbar->showMessage(QString::fromStdString(htev->m_title), 1500); return true; } return QMainWindow::event(e); } void DMainWindow::closeEvent(QCloseEvent* ce) { if (!OnStop()) ce->ignore(); else QMainWindow::closeEvent(ce); } // Emulation void DMainWindow::StartGame(const QString filename) { m_render_widget = std::make_unique(); m_render_widget->setWindowTitle(tr("Dolphin")); // TODO m_render_widget->setWindowIcon(windowIcon()); if (SConfig::GetInstance().bFullscreen) { m_render_widget->setWindowFlags(m_render_widget->windowFlags() | Qt::BypassWindowManagerHint); g_Config.bFullscreen = !g_Config.bBorderlessFullscreen; m_render_widget->showFullScreen(); } else { m_ui->centralWidget->addWidget(m_render_widget.get()); m_ui->centralWidget->setCurrentWidget(m_render_widget.get()); if (SConfig::GetInstance().bRenderWindowAutoSize) { // Resize main window to fit render m_render_widget->setMinimumSize(SConfig::GetInstance().iRenderWindowWidth, SConfig::GetInstance().iRenderWindowHeight); qApp->processEvents(); // Force a redraw so the window has time to resize m_render_widget->setMinimumSize(0, 0); // Allow the widget to scale down } m_render_widget->adjustSize(); } if (!BootManager::BootCore(filename.toStdString())) { QMessageBox::critical(this, tr("Fatal error"), tr("Failed to init Core"), QMessageBox::Ok); if (SConfig::GetInstance().bFullscreen) m_render_widget->close(); else m_ui->centralWidget->removeWidget(m_render_widget.get()); m_render_widget.reset(); } else { DisableScreensaver(); emit CoreStateChanged(Core::CORE_RUN); } } void DMainWindow::DisableScreensaver() { #ifdef Q_OS_WIN // Prevents Windows from sleeping or turning off the display SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); #endif } void DMainWindow::EnableScreensaver() { #ifdef Q_OS_WIN // Allows Windows to sleep and turn off the display SetThreadExecutionState(ES_CONTINUOUS); #endif } QString DMainWindow::RequestBootFilename() { // If a game is already selected, just return the filename if (m_game_tracker->SelectedGame() != nullptr) return m_game_tracker->SelectedGame()->GetFileName(); return ShowFileDialog(); } QString DMainWindow::ShowFileDialog() { return QFileDialog::getOpenFileName(this, tr("Open File"), QString(), tr("All supported ROMs (%1);;All files (*)") .arg(SL("*.gcm *.iso *.ciso *.gcz *.wbfs *.elf *.dol *.dff *.tmd *.wad"))); } QString DMainWindow::ShowFolderDialog() { return QFileDialog::getExistingDirectory(this, tr("Browse for a directory to add"), QDir::homePath(), QFileDialog::ShowDirsOnly); } void DMainWindow::DoStartPause() { if (Core::GetState() == Core::CORE_RUN) { Core::SetState(Core::CORE_PAUSE); emit CoreStateChanged(Core::CORE_PAUSE); } else { Core::SetState(Core::CORE_RUN); emit CoreStateChanged(Core::CORE_RUN); } if (SConfig::GetInstance().bHideCursor) m_render_widget->setCursor(Qt::BlankCursor); } void DMainWindow::OnBrowse() { std::string path = ShowFolderDialog().toStdString(); std::vector& iso_folder = SConfig::GetInstance().m_ISOFolder; if (!path.empty()) { auto itResult = std::find(iso_folder.begin(), iso_folder.end(), path); if (itResult == iso_folder.end()) { iso_folder.push_back(path); SConfig::GetInstance().SaveSettings(); } } m_game_tracker->ScanForGames(); } void DMainWindow::OnPlay() { if (Core::GetState() != Core::CORE_UNINITIALIZED) { DoStartPause(); } else { // initialize Core and boot the game QString filename = RequestBootFilename(); if (!filename.isNull()) StartGame(filename); } } bool DMainWindow::OnStop() { if (Core::GetState() == Core::CORE_UNINITIALIZED || m_isStopping) return true; // Ask for confirmation in case the user accidentally clicked Stop / Escape if (SConfig::GetInstance().bConfirmStop) { // Pause emulation if it isn't already bool wasPaused = false; if (Core::GetState() == Core::CORE_PAUSE) { wasPaused = true; } else { Core::SetState(Core::CORE_PAUSE); emit CoreStateChanged(Core::CORE_PAUSE); } QMessageBox::StandardButton ret = QMessageBox::question(m_render_widget.get(), tr("Please confirm..."), tr("Do you want to stop the current emulation?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (ret == QMessageBox::No) { if (!wasPaused) DoStartPause(); return false; } } return Stop(); } bool DMainWindow::Stop() { m_isStopping = true; // TODO: Movie stuff // TODO: Show the author/description dialog here BootManager::Stop(); EnableScreensaver(); // TODO: Restore original window title // TODO: // If batch mode was specified on the command-line, exit now. //if (m_bBatchMode) // Close(true); if (SConfig::GetInstance().bFullscreen) m_render_widget->close(); else m_ui->centralWidget->removeWidget(m_render_widget.get()); m_render_widget.reset(); emit CoreStateChanged(Core::CORE_UNINITIALIZED); m_isStopping = false; return true; } void DMainWindow::OnReset() { // TODO: Movie needs to be reset here ProcessorInterface::ResetButton_Tap(); } void DMainWindow::OnGameListStyleChanged() { if (m_ui->actionListView->isChecked()) m_game_tracker->SetViewStyle(STYLE_LIST); else if (m_ui->actionTreeView->isChecked()) m_game_tracker->SetViewStyle(STYLE_TREE); else if (m_ui->actionGridView->isChecked()) m_game_tracker->SetViewStyle(STYLE_GRID); else if (m_ui->actionIconView->isChecked()) m_game_tracker->SetViewStyle(STYLE_ICON); } void DMainWindow::OnCoreStateChanged(Core::EState state) { bool is_not_initialized = (state == Core::CORE_UNINITIALIZED); bool is_running = (state == Core::CORE_RUN); bool is_paused = (state == Core::CORE_PAUSE); // Update the toolbar m_ui->actionPlay->setEnabled(is_not_initialized || is_running || is_paused); if (is_running) { m_ui->actionPlay->setIcon(Resources::GetIcon(Resources::TOOLBAR_PAUSE)); m_ui->actionPlay->setText(tr("Pause")); } else if (is_paused || is_not_initialized) { m_ui->actionPlay->setIcon(Resources::GetIcon(Resources::TOOLBAR_PLAY)); m_ui->actionPlay->setText(tr("Play")); } m_ui->actionStop->setEnabled(!is_not_initialized); m_ui->actionOpen->setEnabled(is_not_initialized); m_game_tracker->setEnabled(is_not_initialized); } // Update all the icons used in DMainWindow with fresh ones from // "Resources". Call this function after changing the icon theme. void DMainWindow::UpdateIcons() { // Play/Pause is handled in OnCoreStateChanged(). m_ui->actionStop->setIcon(Resources::GetIcon(Resources::TOOLBAR_STOP)); m_ui->actionScreenshot->setIcon(Resources::GetIcon(Resources::TOOLBAR_SCREENSHOT)); }