dolphin/Source/Core/DolphinQt2/MainWindow.cpp

1050 lines
31 KiB
C++
Raw Normal View History

2017-11-19 10:50:38 -07:00
2015-11-27 01:33:07 -07:00
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <QApplication>
2017-06-24 09:00:37 -06:00
#include <QCloseEvent>
2015-11-27 01:33:07 -07:00
#include <QDir>
2017-06-26 15:22:40 -06:00
#include <QDragEnterEvent>
#include <QDropEvent>
2015-11-27 01:33:07 -07:00
#include <QFileDialog>
2017-06-26 15:22:40 -06:00
#include <QFileInfo>
2015-11-27 01:33:07 -07:00
#include <QIcon>
#include <QMessageBox>
2017-06-26 15:22:40 -06:00
#include <QMimeData>
#include <QProgressDialog>
#include <future>
#include <optional>
2015-11-27 01:33:07 -07:00
#include "Common/Version.h"
Boot: Clean up the boot code * Move out boot parameters to a separate struct, which is not part of SConfig/ConfigManager because there is no reason for it to be there. * Move out file name parsing and constructing the appropriate params from paths to a separate function that does that, and only that. * For every different boot type we support, add a proper struct with only the required parameters, with descriptive names and use std::variant to only store what we need. * Clean up the bHLE_BS2 stuff which made no sense sometimes. Now instead of using bHLE_BS2 for two different things, both for storing the user config setting and as a runtime boot parameter, we simply replace the Disc boot params with BootParameters::IPL. * Const correctness so it's clear what can or cannot update the config. * Drop unused parameters and unneeded checks. * Make a few checks a lot more concise. (Looking at you, extension checks for disc images.) * Remove a mildly terrible workaround where we needed to pass an empty string in order to boot the GC IPL without any game inserted. (Not required anymore thanks to std::variant and std::optional.) The motivation for this are multiple: cleaning up and being able to add support for booting an installed NAND title. Without this change, it'd be pretty much impossible to implement that. Also, using std::visit with std::variant makes the compiler do additional type checks: now we're guaranteed that the boot code will handle all boot types and no invalid boot type will be possible.
2017-05-27 07:43:40 -06:00
#include "Core/Boot/Boot.h"
2015-11-27 01:33:07 -07:00
#include "Core/BootManager.h"
2017-07-06 03:01:32 -06:00
#include "Core/CommonTitles.h"
2017-07-21 14:48:21 -06:00
#include "Core/Config/NetplaySettings.h"
#include "Core/ConfigManager.h"
2015-11-27 01:33:07 -07:00
#include "Core/Core.h"
2017-05-23 14:12:01 -06:00
#include "Core/HW/GCKeyboard.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/ProcessorInterface.h"
2017-05-23 14:12:01 -06:00
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "Core/HotkeyManager.h"
#include "Core/Movie.h"
2017-07-21 14:48:21 -06:00
#include "Core/NetPlayClient.h"
2017-05-09 10:49:10 -06:00
#include "Core/NetPlayProto.h"
2017-07-21 14:48:21 -06:00
#include "Core/NetPlayServer.h"
#include "Core/State.h"
#include "DiscIO/NANDImporter.h"
#include "DolphinQt2/AboutDialog.h"
2017-05-09 10:49:10 -06:00
#include "DolphinQt2/Config/ControllersWindow.h"
#include "DolphinQt2/Config/Graphics/GraphicsWindow.h"
#include "DolphinQt2/Config/LogConfigWidget.h"
#include "DolphinQt2/Config/LogWidget.h"
#include "DolphinQt2/Config/Mapping/MappingWindow.h"
#include "DolphinQt2/Config/SettingsWindow.h"
#include "DolphinQt2/Debugger/BreakpointWidget.h"
#include "DolphinQt2/Debugger/RegisterWidget.h"
2017-09-27 00:53:05 -06:00
#include "DolphinQt2/Debugger/WatchWidget.h"
2017-11-19 10:50:38 -07:00
#include "DolphinQt2/FIFOPlayerWindow.h"
2015-11-27 01:33:07 -07:00
#include "DolphinQt2/Host.h"
#include "DolphinQt2/HotkeyScheduler.h"
2015-11-27 01:33:07 -07:00
#include "DolphinQt2/MainWindow.h"
2017-07-21 14:48:21 -06:00
#include "DolphinQt2/NetPlay/NetPlayDialog.h"
#include "DolphinQt2/NetPlay/NetPlaySetupDialog.h"
#include "DolphinQt2/QtUtils/QueueOnObject.h"
#include "DolphinQt2/QtUtils/RunOnObject.h"
#include "DolphinQt2/QtUtils/WindowActivationEventFilter.h"
2015-11-27 01:33:07 -07:00
#include "DolphinQt2/Resources.h"
#include "DolphinQt2/Settings.h"
#include "DolphinQt2/WiiUpdate.h"
2015-11-27 01:33:07 -07:00
2017-05-23 14:12:01 -06:00
#include "InputCommon/ControllerInterface/ControllerInterface.h"
2017-06-24 09:00:37 -06:00
#include "UICommon/UICommon.h"
#if defined(HAVE_XRANDR) && HAVE_XRANDR
#include <qpa/qplatformnativeinterface.h>
#include "UICommon/X11Utils.h"
#endif
MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters) : QMainWindow(nullptr)
2015-11-27 01:33:07 -07:00
{
setWindowTitle(QString::fromStdString(Common::scm_rev_str));
setWindowIcon(QIcon(Resources::GetMisc(Resources::LOGO_SMALL)));
setUnifiedTitleAndToolBarOnMac(true);
2017-06-26 15:22:40 -06:00
setAcceptDrops(true);
2015-11-27 01:33:07 -07:00
InitControllers();
CreateComponents();
2016-02-09 21:42:06 -07:00
ConnectGameList();
ConnectToolBar();
ConnectRenderWidget();
ConnectStack();
ConnectMenuBar();
2017-05-23 14:12:01 -06:00
InitCoreCallbacks();
2017-07-21 14:48:21 -06:00
NetPlayInit();
if (boot_parameters)
StartGame(std::move(boot_parameters));
2015-11-27 01:33:07 -07:00
}
2016-02-14 18:04:16 -07:00
MainWindow::~MainWindow()
{
m_render_widget->deleteLater();
ShutdownControllers();
Config::Save();
2016-02-14 18:04:16 -07:00
}
2017-05-23 14:12:01 -06:00
void MainWindow::InitControllers()
{
if (g_controller_interface.IsInit())
return;
g_controller_interface.Initialize(reinterpret_cast<void*>(winId()));
Pad::Initialize();
Keyboard::Initialize();
Wiimote::Initialize(Wiimote::InitializeMode::DO_NOT_WAIT_FOR_WIIMOTES);
m_hotkey_scheduler = new HotkeyScheduler();
m_hotkey_scheduler->Start();
ConnectHotkeys();
2017-05-23 14:12:01 -06:00
}
void MainWindow::ShutdownControllers()
{
m_hotkey_scheduler->Stop();
g_controller_interface.Shutdown();
Pad::Shutdown();
Keyboard::Shutdown();
Wiimote::Shutdown();
HotkeyManagerEmu::Shutdown();
m_hotkey_scheduler->deleteLater();
}
void MainWindow::InitCoreCallbacks()
{
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [=](Core::State state) {
if (state == Core::State::Uninitialized)
OnStopComplete();
});
2017-06-24 09:00:37 -06:00
installEventFilter(this);
m_render_widget->installEventFilter(this);
}
static void InstallHotkeyFilter(QWidget* dialog)
{
auto* filter = new WindowActivationEventFilter();
dialog->installEventFilter(filter);
filter->connect(filter, &WindowActivationEventFilter::windowDeactivated,
[] { HotkeyManagerEmu::Enable(true); });
filter->connect(filter, &WindowActivationEventFilter::windowActivated,
[] { HotkeyManagerEmu::Enable(false); });
}
2016-02-09 21:42:06 -07:00
void MainWindow::CreateComponents()
2015-11-27 01:33:07 -07:00
{
m_menu_bar = new MenuBar(this);
m_tool_bar = new ToolBar(this);
m_game_list = new GameList(this);
m_render_widget = new RenderWidget;
m_stack = new QStackedWidget(this);
2017-05-09 10:49:10 -06:00
m_controllers_window = new ControllersWindow(this);
m_settings_window = new SettingsWindow(this);
2017-08-30 08:44:28 -06:00
m_hotkey_window = new MappingWindow(this, MappingWindow::Type::MAPPING_HOTKEYS, 0);
2017-08-30 08:44:28 -06:00
m_log_widget = new LogWidget(this);
m_log_config_widget = new LogConfigWidget(this);
2017-08-30 08:44:28 -06:00
m_fifo_window = new FIFOPlayerWindow(this);
connect(m_fifo_window, &FIFOPlayerWindow::LoadFIFORequested, this,
[this](const QString& path) { StartGame(path); });
m_register_widget = new RegisterWidget(this);
2017-09-27 00:53:05 -06:00
m_watch_widget = new WatchWidget(this);
m_breakpoint_widget = new BreakpointWidget(this);
2017-11-19 10:50:38 -07:00
connect(m_watch_widget, &WatchWidget::RequestMemoryBreakpoint,
[this](u32 addr) { m_breakpoint_widget->AddAddressMBP(addr); });
connect(m_register_widget, &RegisterWidget::RequestMemoryBreakpoint,
[this](u32 addr) { m_breakpoint_widget->AddAddressMBP(addr); });
#if defined(HAVE_XRANDR) && HAVE_XRANDR
m_graphics_window = new GraphicsWindow(
new X11Utils::XRRConfiguration(
static_cast<Display*>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow(
"display", windowHandle())),
winId()),
this);
#else
m_graphics_window = new GraphicsWindow(nullptr, this);
#endif
InstallHotkeyFilter(m_hotkey_window);
InstallHotkeyFilter(m_controllers_window);
InstallHotkeyFilter(m_settings_window);
InstallHotkeyFilter(m_graphics_window);
2015-11-27 01:33:07 -07:00
}
2016-02-09 21:42:06 -07:00
void MainWindow::ConnectMenuBar()
2015-11-27 01:33:07 -07:00
{
setMenuBar(m_menu_bar);
// File
connect(m_menu_bar, &MenuBar::Open, this, &MainWindow::Open);
connect(m_menu_bar, &MenuBar::Exit, this, &MainWindow::close);
// Emulation
connect(m_menu_bar, &MenuBar::Pause, this, &MainWindow::Pause);
connect(m_menu_bar, &MenuBar::Play, this, [this]() { Play(); });
connect(m_menu_bar, &MenuBar::Stop, this, &MainWindow::RequestStop);
connect(m_menu_bar, &MenuBar::Reset, this, &MainWindow::Reset);
connect(m_menu_bar, &MenuBar::Fullscreen, this, &MainWindow::FullScreen);
connect(m_menu_bar, &MenuBar::FrameAdvance, this, &MainWindow::FrameAdvance);
connect(m_menu_bar, &MenuBar::Screenshot, this, &MainWindow::ScreenShot);
connect(m_menu_bar, &MenuBar::StateLoad, this, &MainWindow::StateLoad);
connect(m_menu_bar, &MenuBar::StateSave, this, &MainWindow::StateSave);
connect(m_menu_bar, &MenuBar::StateLoadSlot, this, &MainWindow::StateLoadSlot);
connect(m_menu_bar, &MenuBar::StateSaveSlot, this, &MainWindow::StateSaveSlot);
connect(m_menu_bar, &MenuBar::StateLoadSlotAt, this, &MainWindow::StateLoadSlotAt);
connect(m_menu_bar, &MenuBar::StateSaveSlotAt, this, &MainWindow::StateSaveSlotAt);
connect(m_menu_bar, &MenuBar::StateLoadUndo, this, &MainWindow::StateLoadUndo);
connect(m_menu_bar, &MenuBar::StateSaveUndo, this, &MainWindow::StateSaveUndo);
connect(m_menu_bar, &MenuBar::StateSaveOldest, this, &MainWindow::StateSaveOldest);
connect(m_menu_bar, &MenuBar::SetStateSlot, this, &MainWindow::SetStateSlot);
// Options
connect(m_menu_bar, &MenuBar::Configure, this, &MainWindow::ShowSettingsWindow);
connect(m_menu_bar, &MenuBar::ConfigureGraphics, this, &MainWindow::ShowGraphicsWindow);
connect(m_menu_bar, &MenuBar::ConfigureAudio, this, &MainWindow::ShowAudioWindow);
connect(m_menu_bar, &MenuBar::ConfigureControllers, this, &MainWindow::ShowControllersWindow);
connect(m_menu_bar, &MenuBar::ConfigureHotkeys, this, &MainWindow::ShowHotkeyDialog);
// Tools
connect(m_menu_bar, &MenuBar::BootGameCubeIPL, this, &MainWindow::OnBootGameCubeIPL);
connect(m_menu_bar, &MenuBar::ImportNANDBackup, this, &MainWindow::OnImportNANDBackup);
connect(m_menu_bar, &MenuBar::PerformOnlineUpdate, this, &MainWindow::PerformOnlineUpdate);
2017-07-06 03:01:32 -06:00
connect(m_menu_bar, &MenuBar::BootWiiSystemMenu, this, &MainWindow::BootWiiSystemMenu);
2017-07-21 14:48:21 -06:00
connect(m_menu_bar, &MenuBar::StartNetPlay, this, &MainWindow::ShowNetPlaySetupDialog);
2017-08-30 08:44:28 -06:00
connect(m_menu_bar, &MenuBar::ShowFIFOPlayer, this, &MainWindow::ShowFIFOPlayer);
2017-08-27 05:55:05 -06:00
// Movie
connect(m_menu_bar, &MenuBar::PlayRecording, this, &MainWindow::OnPlayRecording);
connect(m_menu_bar, &MenuBar::StartRecording, this, &MainWindow::OnStartRecording);
connect(m_menu_bar, &MenuBar::StopRecording, this, &MainWindow::OnStopRecording);
connect(m_menu_bar, &MenuBar::ExportRecording, this, &MainWindow::OnExportRecording);
// View
connect(m_menu_bar, &MenuBar::ShowList, m_game_list, &GameList::SetListView);
connect(m_menu_bar, &MenuBar::ShowGrid, m_game_list, &GameList::SetGridView);
2017-05-08 11:03:59 -06:00
connect(m_menu_bar, &MenuBar::ColumnVisibilityToggled, m_game_list,
&GameList::OnColumnVisibilityToggled);
connect(m_menu_bar, &MenuBar::GameListPlatformVisibilityToggled, m_game_list,
&GameList::OnGameListVisibilityChanged);
connect(m_menu_bar, &MenuBar::GameListRegionVisibilityToggled, m_game_list,
&GameList::OnGameListVisibilityChanged);
connect(m_menu_bar, &MenuBar::ShowAboutDialog, this, &MainWindow::ShowAboutDialog);
2017-08-27 05:55:05 -06:00
connect(m_game_list, &GameList::SelectionChanged, m_menu_bar, &MenuBar::SelectionChanged);
connect(this, &MainWindow::ReadOnlyModeChanged, m_menu_bar, &MenuBar::ReadOnlyModeChanged);
connect(this, &MainWindow::RecordingStatusChanged, m_menu_bar, &MenuBar::RecordingStatusChanged);
2015-11-27 01:33:07 -07:00
}
void MainWindow::ConnectHotkeys()
{
connect(m_hotkey_scheduler, &HotkeyScheduler::ExitHotkey, this, &MainWindow::close);
connect(m_hotkey_scheduler, &HotkeyScheduler::PauseHotkey, this, &MainWindow::Pause);
connect(m_hotkey_scheduler, &HotkeyScheduler::StopHotkey, this, &MainWindow::RequestStop);
connect(m_hotkey_scheduler, &HotkeyScheduler::ScreenShotHotkey, this, &MainWindow::ScreenShot);
connect(m_hotkey_scheduler, &HotkeyScheduler::FullScreenHotkey, this, &MainWindow::FullScreen);
connect(m_hotkey_scheduler, &HotkeyScheduler::StateLoadSlotHotkey, this,
&MainWindow::StateLoadSlot);
connect(m_hotkey_scheduler, &HotkeyScheduler::StateSaveSlotHotkey, this,
&MainWindow::StateSaveSlot);
connect(m_hotkey_scheduler, &HotkeyScheduler::SetStateSlotHotkey, this,
&MainWindow::SetStateSlot);
2017-08-27 05:55:05 -06:00
connect(m_hotkey_scheduler, &HotkeyScheduler::StartRecording, this,
&MainWindow::OnStartRecording);
connect(m_hotkey_scheduler, &HotkeyScheduler::ExportRecording, this,
&MainWindow::OnExportRecording);
connect(m_hotkey_scheduler, &HotkeyScheduler::ToggleReadOnlyMode, [this] {
bool read_only = !Movie::IsReadOnly();
Movie::SetReadOnly(read_only);
emit ReadOnlyModeChanged(read_only);
});
}
2016-02-09 21:42:06 -07:00
void MainWindow::ConnectToolBar()
2015-11-27 01:33:07 -07:00
{
addToolBar(m_tool_bar);
connect(m_tool_bar, &ToolBar::OpenPressed, this, &MainWindow::Open);
connect(m_tool_bar, &ToolBar::PlayPressed, this, [this]() { Play(); });
connect(m_tool_bar, &ToolBar::PausePressed, this, &MainWindow::Pause);
connect(m_tool_bar, &ToolBar::StopPressed, this, &MainWindow::RequestStop);
connect(m_tool_bar, &ToolBar::FullScreenPressed, this, &MainWindow::FullScreen);
connect(m_tool_bar, &ToolBar::ScreenShotPressed, this, &MainWindow::ScreenShot);
connect(m_tool_bar, &ToolBar::SettingsPressed, this, &MainWindow::ShowSettingsWindow);
2017-05-09 10:49:10 -06:00
connect(m_tool_bar, &ToolBar::ControllersPressed, this, &MainWindow::ShowControllersWindow);
connect(m_tool_bar, &ToolBar::GraphicsPressed, this, &MainWindow::ShowGraphicsWindow);
2015-11-27 01:33:07 -07:00
}
2016-02-09 21:42:06 -07:00
void MainWindow::ConnectGameList()
2015-11-27 01:33:07 -07:00
{
connect(m_game_list, &GameList::GameSelected, this, [this]() { Play(); });
2017-07-21 14:48:21 -06:00
connect(m_game_list, &GameList::NetPlayHost, this, &MainWindow::NetPlayHost);
connect(m_game_list, &GameList::OpenGeneralSettings, this, &MainWindow::ShowGeneralWindow);
2015-11-27 01:33:07 -07:00
}
2016-02-09 21:42:06 -07:00
void MainWindow::ConnectRenderWidget()
2015-11-27 01:33:07 -07:00
{
m_rendering_to_main = false;
m_render_widget->hide();
connect(m_render_widget, &RenderWidget::EscapePressed, this, &MainWindow::RequestStop);
connect(m_render_widget, &RenderWidget::Closed, this, &MainWindow::ForceStop);
2015-11-27 01:33:07 -07:00
}
2016-02-09 21:42:06 -07:00
void MainWindow::ConnectStack()
2015-11-27 01:33:07 -07:00
{
m_stack->addWidget(m_game_list);
setCentralWidget(m_stack);
setTabPosition(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea, QTabWidget::North);
addDockWidget(Qt::RightDockWidgetArea, m_log_widget);
addDockWidget(Qt::RightDockWidgetArea, m_log_config_widget);
addDockWidget(Qt::RightDockWidgetArea, m_register_widget);
2017-09-27 00:53:05 -06:00
addDockWidget(Qt::RightDockWidgetArea, m_watch_widget);
addDockWidget(Qt::RightDockWidgetArea, m_breakpoint_widget);
tabifyDockWidget(m_log_widget, m_log_config_widget);
tabifyDockWidget(m_log_widget, m_register_widget);
2017-09-27 00:53:05 -06:00
tabifyDockWidget(m_log_widget, m_watch_widget);
tabifyDockWidget(m_log_widget, m_breakpoint_widget);
2015-11-27 01:33:07 -07:00
}
void MainWindow::Open()
{
QString file = QFileDialog::getOpenFileName(
this, tr("Select a File"), QDir::currentPath(),
2016-12-17 06:48:49 -07:00
tr("All GC/Wii files (*.elf *.dol *.gcm *.iso *.tgc *.wbfs *.ciso *.gcz *.wad);;"
"All Files (*)"));
if (!file.isEmpty())
StartGame(file);
2015-11-27 01:33:07 -07:00
}
void MainWindow::Play(const std::optional<std::string>& savestate_path)
2015-11-27 01:33:07 -07:00
{
// If we're in a paused game, start it up again.
// Otherwise, play the selected game, if there is one.
// Otherwise, play the default game.
// Otherwise, play the last played game, if there is one.
// Otherwise, prompt for a new game.
if (Core::GetState() == Core::State::Paused)
{
Core::SetState(Core::State::Running);
2018-01-01 14:15:26 -07:00
EnableScreenSaver(false);
}
else
{
QSharedPointer<GameFile> selection = m_game_list->GetSelectedGame();
if (selection)
{
StartGame(selection->GetFilePath(), savestate_path);
2018-01-01 14:15:26 -07:00
EnableScreenSaver(false);
}
else
{
auto default_path = QString::fromStdString(SConfig::GetInstance().m_strDefaultISO);
if (!default_path.isEmpty() && QFile::exists(default_path))
{
StartGame(default_path, savestate_path);
2018-01-01 14:15:26 -07:00
EnableScreenSaver(false);
}
else
{
2017-06-04 14:43:41 -06:00
Open();
}
}
}
2015-11-27 01:33:07 -07:00
}
void MainWindow::Pause()
{
Core::SetState(Core::State::Paused);
2018-01-01 14:15:26 -07:00
EnableScreenSaver(true);
2015-11-27 01:33:07 -07:00
}
void MainWindow::OnStopComplete()
{
m_stop_requested = false;
HideRenderWidget();
if (m_exit_requested)
QGuiApplication::instance()->quit();
// If the current emulation prevented the booting of another, do that now
if (m_pending_boot != nullptr)
{
StartGame(std::move(m_pending_boot));
m_pending_boot.reset();
}
}
bool MainWindow::RequestStop()
2015-11-27 01:33:07 -07:00
{
2017-06-24 09:00:37 -06:00
if (!Core::IsRunning())
{
Core::QueueHostJob([this] { OnStopComplete(); }, true);
2017-06-24 09:00:37 -06:00
return true;
}
2017-06-24 09:00:37 -06:00
if (SConfig::GetInstance().bConfirmStop)
{
2017-06-24 09:00:37 -06:00
const Core::State state = Core::GetState();
2017-07-21 14:48:21 -06:00
// Only pause the game, if NetPlay is not running
bool pause = Settings::Instance().GetNetPlayClient() != nullptr;
2017-06-24 09:00:37 -06:00
if (pause)
Core::SetState(Core::State::Paused);
QMessageBox::StandardButton confirm;
2017-06-24 09:00:37 -06:00
confirm = QMessageBox::question(m_render_widget, tr("Confirm"),
m_stop_requested ?
tr("A shutdown is already in progress. Unsaved data "
"may be lost if you stop the current emulation "
"before it completes. Force stop?") :
tr("Do you want to stop the current emulation?"));
2017-06-24 09:00:37 -06:00
if (pause)
Core::SetState(state);
if (confirm != QMessageBox::Yes)
return false;
}
2015-11-27 01:33:07 -07:00
2017-06-24 09:00:37 -06:00
// TODO: Add Movie shutdown
// TODO: Add Debugger shutdown
if (!m_stop_requested && UICommon::TriggerSTMPowerEvent())
{
2017-06-24 09:00:37 -06:00
m_stop_requested = true;
// Unpause because gracefully shutting down needs the game to actually request a shutdown.
// TODO: Do not unpause in debug mode to allow debugging until the complete shutdown.
2017-06-24 09:00:37 -06:00
if (Core::GetState() == Core::State::Paused)
Core::SetState(Core::State::Running);
return true;
2017-06-24 09:00:37 -06:00
}
ForceStop();
#ifdef Q_OS_WIN
2017-06-24 09:00:37 -06:00
// Allow windows to idle or turn off display again
SetThreadExecutionState(ES_CONTINUOUS);
#endif
2017-06-24 09:00:37 -06:00
return true;
2015-11-27 01:33:07 -07:00
}
void MainWindow::ForceStop()
{
BootManager::Stop();
2018-01-01 14:15:26 -07:00
EnableScreenSaver(true);
2015-11-27 01:33:07 -07:00
}
void MainWindow::Reset()
{
if (Movie::IsRecordingInput())
2016-08-04 10:54:45 -06:00
Movie::SetReset(true);
ProcessorInterface::ResetButton_Tap();
}
void MainWindow::FrameAdvance()
{
Core::DoFrameStep();
}
2015-11-27 01:33:07 -07:00
void MainWindow::FullScreen()
{
// If the render widget is fullscreen we want to reset it to whatever is in
// settings. If it's set to be fullscreen then it just remakes the window,
// which probably isn't ideal.
bool was_fullscreen = m_render_widget->isFullScreen();
HideRenderWidget();
if (was_fullscreen)
ShowRenderWidget();
else
m_render_widget->showFullScreen();
2015-11-27 01:33:07 -07:00
}
void MainWindow::ScreenShot()
{
Core::SaveScreenShot();
2015-11-27 01:33:07 -07:00
}
void MainWindow::StartGame(const QString& path, const std::optional<std::string>& savestate_path)
{
StartGame(BootParameters::GenerateFromFile(path.toStdString(), savestate_path));
}
void MainWindow::StartGame(std::unique_ptr<BootParameters>&& parameters)
2015-11-27 01:33:07 -07:00
{
// If we're running, only start a new game once we've stopped the last.
if (Core::GetState() != Core::State::Uninitialized)
{
if (!RequestStop())
return;
// As long as the shutdown isn't complete, we can't boot, so let's boot later
m_pending_boot = std::move(parameters);
return;
}
// Boot up, show an error if it fails to load the game.
if (!BootManager::BootCore(std::move(parameters)))
{
QMessageBox::critical(this, tr("Error"), tr("Failed to init core"), QMessageBox::Ok);
return;
}
ShowRenderWidget();
#ifdef Q_OS_WIN
// Prevents Windows from sleeping, turning off the display, or idling
EXECUTION_STATE shouldScreenSave =
SConfig::GetInstance().bDisableScreenSaver ? ES_DISPLAY_REQUIRED : 0;
SetThreadExecutionState(ES_CONTINUOUS | shouldScreenSave | ES_SYSTEM_REQUIRED);
#endif
2015-11-27 01:33:07 -07:00
}
void MainWindow::ShowRenderWidget()
{
if (SConfig::GetInstance().bRenderToMain)
{
// If we're rendering to main, add it to the stack and update our title when necessary.
m_rendering_to_main = true;
m_stack->setCurrentIndex(m_stack->addWidget(m_render_widget));
connect(Host::GetInstance(), &Host::RequestTitle, this, &MainWindow::setWindowTitle);
}
else
{
// Otherwise, just show it.
m_rendering_to_main = false;
if (SConfig::GetInstance().bFullscreen)
{
m_render_widget->showFullScreen();
}
else
{
m_render_widget->showNormal();
m_render_widget->resize(640, 480);
}
}
2015-11-27 01:33:07 -07:00
}
void MainWindow::HideRenderWidget()
{
if (m_rendering_to_main)
{
// Remove the widget from the stack and reparent it to nullptr, so that it can draw
// itself in a new window if it wants. Disconnect the title updates.
m_stack->removeWidget(m_render_widget);
m_render_widget->setParent(nullptr);
m_rendering_to_main = false;
disconnect(Host::GetInstance(), &Host::RequestTitle, this, &MainWindow::setWindowTitle);
setWindowTitle(QString::fromStdString(Common::scm_rev_str));
}
m_render_widget->hide();
2015-11-27 01:33:07 -07:00
}
2016-02-09 21:42:06 -07:00
2017-05-09 10:49:10 -06:00
void MainWindow::ShowControllersWindow()
{
m_controllers_window->show();
m_controllers_window->raise();
m_controllers_window->activateWindow();
}
void MainWindow::ShowSettingsWindow()
{
m_settings_window->show();
m_settings_window->raise();
m_settings_window->activateWindow();
}
void MainWindow::ShowAudioWindow()
{
m_settings_window->SelectAudioPane();
ShowSettingsWindow();
}
void MainWindow::ShowGeneralWindow()
{
m_settings_window->SelectGeneralPane();
ShowSettingsWindow();
}
void MainWindow::ShowAboutDialog()
{
AboutDialog about{this};
about.exec();
}
void MainWindow::ShowHotkeyDialog()
{
m_hotkey_window->show();
m_hotkey_window->raise();
m_hotkey_window->activateWindow();
}
void MainWindow::ShowGraphicsWindow()
{
m_graphics_window->show();
m_graphics_window->raise();
m_graphics_window->activateWindow();
}
2017-07-21 14:48:21 -06:00
void MainWindow::ShowNetPlaySetupDialog()
{
m_netplay_setup_dialog->show();
m_netplay_setup_dialog->raise();
m_netplay_setup_dialog->activateWindow();
}
2017-08-30 08:44:28 -06:00
void MainWindow::ShowFIFOPlayer()
{
m_fifo_window->show();
m_fifo_window->raise();
m_fifo_window->activateWindow();
}
void MainWindow::StateLoad()
{
QString path = QFileDialog::getOpenFileName(this, tr("Select a File"), QDir::currentPath(),
tr("All Save States (*.sav *.s##);; All Files (*)"));
State::LoadAs(path.toStdString());
}
void MainWindow::StateSave()
{
QString path = QFileDialog::getSaveFileName(this, tr("Select a File"), QDir::currentPath(),
tr("All Save States (*.sav *.s##);; All Files (*)"));
State::SaveAs(path.toStdString());
}
void MainWindow::StateLoadSlot()
{
State::Load(m_state_slot);
}
void MainWindow::StateSaveSlot()
{
State::Save(m_state_slot, true);
m_menu_bar->UpdateStateSlotMenu();
}
void MainWindow::StateLoadSlotAt(int slot)
{
State::Load(slot);
}
void MainWindow::StateSaveSlotAt(int slot)
{
State::Save(slot, true);
m_menu_bar->UpdateStateSlotMenu();
}
void MainWindow::StateLoadUndo()
{
State::UndoLoadState();
}
void MainWindow::StateSaveUndo()
{
State::UndoSaveState();
}
void MainWindow::StateSaveOldest()
{
State::SaveFirstSaved();
}
void MainWindow::SetStateSlot(int slot)
{
Settings::Instance().SetStateSlot(slot);
m_state_slot = slot;
}
2017-06-24 09:00:37 -06:00
void MainWindow::PerformOnlineUpdate(const std::string& region)
{
WiiUpdate::PerformOnlineUpdate(region, this);
// Since the update may have installed a newer system menu, refresh the tools menu.
m_menu_bar->UpdateToolsMenu(false);
}
2017-07-06 03:01:32 -06:00
void MainWindow::BootWiiSystemMenu()
{
StartGame(std::make_unique<BootParameters>(BootParameters::NANDTitle{Titles::SYSTEM_MENU}));
2017-07-06 03:01:32 -06:00
}
2017-07-21 14:48:21 -06:00
void MainWindow::NetPlayInit()
{
m_netplay_setup_dialog = new NetPlaySetupDialog(this);
m_netplay_dialog = new NetPlayDialog(this);
connect(m_netplay_dialog, &NetPlayDialog::Boot, this,
[this](const QString& path) { StartGame(path); });
2017-07-21 14:48:21 -06:00
connect(m_netplay_dialog, &NetPlayDialog::Stop, this, &MainWindow::RequestStop);
connect(m_netplay_dialog, &NetPlayDialog::rejected, this, &MainWindow::NetPlayQuit);
connect(m_netplay_setup_dialog, &NetPlaySetupDialog::Join, this, &MainWindow::NetPlayJoin);
connect(m_netplay_setup_dialog, &NetPlaySetupDialog::Host, this, &MainWindow::NetPlayHost);
}
bool MainWindow::NetPlayJoin()
{
if (Core::IsRunning())
{
QMessageBox::critical(
nullptr, QObject::tr("Error"),
QObject::tr("Can't start a NetPlay Session while a game is still running!"));
return false;
}
if (m_netplay_dialog->isVisible())
{
QMessageBox::critical(nullptr, QObject::tr("Error"),
QObject::tr("A NetPlay Session is already in progress!"));
return false;
}
// Settings
std::string host_ip;
u16 host_port;
2017-07-21 14:48:21 -06:00
if (Settings::Instance().GetNetPlayServer() != nullptr)
{
host_ip = "127.0.0.1";
host_port = Settings::Instance().GetNetPlayServer()->GetPort();
}
else
{
host_ip = Config::Get(Config::NETPLAY_HOST_CODE);
host_port = Config::Get(Config::NETPLAY_HOST_PORT);
}
const std::string traversal_choice = Config::Get(Config::NETPLAY_TRAVERSAL_CHOICE);
const bool is_traversal = traversal_choice == "traversal";
2017-07-21 14:48:21 -06:00
const std::string traversal_host = Config::Get(Config::NETPLAY_TRAVERSAL_SERVER);
const u16 traversal_port = Config::Get(Config::NETPLAY_TRAVERSAL_PORT);
const std::string nickname = Config::Get(Config::NETPLAY_NICKNAME);
2017-07-21 14:48:21 -06:00
// Create Client
Settings::Instance().ResetNetPlayClient(new NetPlayClient(
host_ip, host_port, m_netplay_dialog, nickname,
NetTraversalConfig{Settings::Instance().GetNetPlayServer() != nullptr ? false : is_traversal,
traversal_host, traversal_port}));
2017-07-21 14:48:21 -06:00
if (!Settings::Instance().GetNetPlayClient()->IsConnected())
{
QMessageBox::critical(nullptr, QObject::tr("Error"),
QObject::tr("Failed to connect to server"));
return false;
}
m_netplay_setup_dialog->close();
m_netplay_dialog->show(nickname, is_traversal);
return true;
}
bool MainWindow::NetPlayHost(const QString& game_id)
{
if (Core::IsRunning())
{
QMessageBox::critical(
nullptr, QObject::tr("Error"),
QObject::tr("Can't start a NetPlay Session while a game is still running!"));
return false;
}
if (m_netplay_dialog->isVisible())
{
QMessageBox::critical(nullptr, QObject::tr("Error"),
QObject::tr("A NetPlay Session is already in progress!"));
return false;
}
// Settings
u16 host_port = Config::Get(Config::NETPLAY_HOST_PORT);
const std::string traversal_choice = Config::Get(Config::NETPLAY_TRAVERSAL_CHOICE);
const bool is_traversal = traversal_choice == "traversal";
const bool use_upnp = Config::Get(Config::NETPLAY_USE_UPNP);
const std::string traversal_host = Config::Get(Config::NETPLAY_TRAVERSAL_SERVER);
const u16 traversal_port = Config::Get(Config::NETPLAY_TRAVERSAL_PORT);
const std::string nickname = Config::Get(Config::NETPLAY_NICKNAME);
2017-07-21 14:48:21 -06:00
if (is_traversal)
host_port = Config::Get(Config::NETPLAY_LISTEN_PORT);
// Create Server
Settings::Instance().ResetNetPlayServer(new NetPlayServer(
host_port, use_upnp, NetTraversalConfig{is_traversal, traversal_host, traversal_port}));
2017-07-21 14:48:21 -06:00
if (!Settings::Instance().GetNetPlayServer()->is_connected)
{
QMessageBox::critical(
nullptr, QObject::tr("Failed to open server"),
QObject::tr(
"Failed to listen on port %1. Is another instance of the NetPlay server running?")
.arg(host_port));
return false;
}
Settings::Instance().GetNetPlayServer()->ChangeGame(game_id.toStdString());
// Join our local server
return NetPlayJoin();
}
void MainWindow::NetPlayQuit()
{
Settings::Instance().ResetNetPlayClient();
Settings::Instance().ResetNetPlayServer();
}
2018-01-01 14:15:26 -07:00
void MainWindow::EnableScreenSaver(bool enable)
{
#if defined(HAVE_XRANDR) && HAVE_XRANDR
UICommon::EnableScreenSaver(
static_cast<Display*>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow(
"display", windowHandle())),
winId(), enable);
#else
UICommon::EnableScreenSaver(enable);
#endif
}
2017-06-24 09:00:37 -06:00
bool MainWindow::eventFilter(QObject* object, QEvent* event)
{
if (event->type() == QEvent::Close)
2017-06-24 09:00:37 -06:00
{
if (RequestStop() && object == this)
m_exit_requested = true;
2017-06-24 09:00:37 -06:00
static_cast<QCloseEvent*>(event)->ignore();
return true;
}
return false;
}
2017-06-26 15:22:40 -06:00
void MainWindow::dragEnterEvent(QDragEnterEvent* event)
{
if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() == 1)
event->acceptProposedAction();
}
void MainWindow::dropEvent(QDropEvent* event)
{
const auto& urls = event->mimeData()->urls();
if (urls.empty())
return;
const auto& url = urls[0];
QFileInfo file_info(url.toLocalFile());
auto path = file_info.filePath();
if (!file_info.exists() || !file_info.isReadable())
{
QMessageBox::critical(this, tr("Error"), tr("Failed to open '%1'").arg(path));
return;
}
if (file_info.isFile())
{
StartGame(path);
}
else
{
auto& settings = Settings::Instance();
if (settings.GetPaths().size() != 0)
{
if (QMessageBox::question(
this, tr("Confirm"),
tr("Do you want to add \"%1\" to the list of Game Paths?").arg(path)) !=
QMessageBox::Yes)
return;
}
settings.AddPath(path);
}
}
QSize MainWindow::sizeHint() const
{
return QSize(800, 600);
}
void MainWindow::OnBootGameCubeIPL(DiscIO::Region region)
{
StartGame(std::make_unique<BootParameters>(BootParameters::IPL{region}));
}
void MainWindow::OnImportNANDBackup()
{
auto response = QMessageBox::question(
this, tr("Question"),
tr("Merging a new NAND over your currently selected NAND will overwrite any channels "
"and savegames that already exist. This process is not reversible, so it is "
"recommended that you keep backups of both NANDs. Are you sure you want to "
"continue?"));
if (response == QMessageBox::No)
return;
QString file = QFileDialog::getOpenFileName(this, tr("Select the save file"), QDir::currentPath(),
tr("BootMii NAND backup file (*.bin);;"
"All Files (*)"));
if (file.isEmpty())
return;
QProgressDialog* dialog = new QProgressDialog(this);
dialog->setMinimum(0);
dialog->setMaximum(0);
dialog->setLabelText(tr("Importing NAND backup"));
dialog->setCancelButton(nullptr);
2017-09-01 19:20:20 -06:00
auto beginning = QDateTime::currentDateTime().toMSecsSinceEpoch();
auto result = std::async(std::launch::async, [&] {
DiscIO::NANDImporter().ImportNANDBin(
file.toStdString(),
[&dialog, beginning] {
QueueOnObject(dialog, [&dialog, beginning] {
dialog->setLabelText(
tr("Importing NAND backup\n Time elapsed: %1s")
.arg((QDateTime::currentDateTime().toMSecsSinceEpoch() - beginning) / 1000));
});
},
[this] {
return RunOnObject(this, [this] {
return QFileDialog::getOpenFileName(this, tr("Select the keys file (OTP/SEEPROM dump)"),
QDir::currentPath(),
tr("BootMii keys file (*.bin);;"
"All Files (*)"))
.toStdString();
});
});
QueueOnObject(dialog, &QProgressDialog::close);
});
dialog->exec();
result.wait();
m_menu_bar->UpdateToolsMenu(Core::IsRunning());
}
2017-08-27 05:55:05 -06:00
void MainWindow::OnPlayRecording()
{
QString dtm_file = QFileDialog::getOpenFileName(this, tr("Select the Recording File"), QString(),
2017-08-27 05:55:05 -06:00
tr("Dolphin TAS Movies (*.dtm)"));
if (dtm_file.isEmpty())
return;
if (!Movie::IsReadOnly())
{
// let's make the read-only flag consistent at the start of a movie.
Movie::SetReadOnly(true);
emit ReadOnlyModeChanged(true);
}
std::optional<std::string> savestate_path;
if (Movie::PlayInput(dtm_file.toStdString(), &savestate_path))
2017-08-27 05:55:05 -06:00
{
emit RecordingStatusChanged(true);
Play(savestate_path);
2017-08-27 05:55:05 -06:00
}
}
void MainWindow::OnStartRecording()
{
if ((!Core::IsRunningAndStarted() && Core::IsRunning()) || Movie::IsRecordingInput() ||
Movie::IsPlayingInput())
return;
if (Movie::IsReadOnly())
{
// The user just chose to record a movie, so that should take precedence
Movie::SetReadOnly(false);
emit ReadOnlyModeChanged(true);
}
int controllers = 0;
for (int i = 0; i < 4; i++)
{
if (SerialInterface::SIDevice_IsGCController(SConfig::GetInstance().m_SIDevice[i]))
controllers |= (1 << i);
if (g_wiimote_sources[i] != WIIMOTE_SRC_NONE)
controllers |= (1 << (i + 4));
}
if (Movie::BeginRecordingInput(controllers))
{
emit RecordingStatusChanged(true);
if (!Core::IsRunning())
Play();
}
}
void MainWindow::OnStopRecording()
{
if (Movie::IsRecordingInput())
OnExportRecording();
Movie::EndPlayInput(false);
emit RecordingStatusChanged(true);
}
void MainWindow::OnExportRecording()
{
bool was_paused = Core::GetState() == Core::State::Paused;
if (was_paused)
Core::SetState(Core::State::Paused);
QString dtm_file = QFileDialog::getSaveFileName(this, tr("Select the Recording File"), QString(),
2017-08-27 05:55:05 -06:00
tr("Dolphin TAS Movies (*.dtm)"));
if (was_paused)
Core::SetState(Core::State::Running);
if (dtm_file.isEmpty())
return;
Core::SetState(Core::State::Running);
Movie::SaveRecording(dtm_file.toStdString());
}