Play-/Source/ui_qt/mainwindow.cpp

887 lines
26 KiB
C++
Raw Normal View History

2016-09-06 00:12:56 +01:00
#include "mainwindow.h"
#include "QStringUtils.h"
2016-08-23 00:54:10 +01:00
#include "settingsdialog.h"
2016-08-10 14:56:16 +01:00
#include "memorycardmanagerdialog.h"
#include "S3FileBrowser.h"
2019-01-16 20:00:10 +00:00
#include "ui_shared/BootablesProcesses.h"
2019-07-25 12:43:24 -04:00
#include "ui_shared/StatsManager.h"
#include "openglwindow.h"
#include "GSH_OpenGLQt.h"
2019-12-15 12:38:16 -05:00
#ifdef HAS_GSH_VULKAN
#include "vulkanwindow.h"
#include "GSH_VulkanQt.h"
#include "gs/GSH_Vulkan/GSH_VulkanDeviceInfo.h"
#endif
2016-07-26 15:06:37 +01:00
#include <QDateTime>
#include <QFileDialog>
2016-08-23 00:54:10 +01:00
#include <QTimer>
2016-07-01 05:21:58 +03:00
#include <QWindow>
2016-07-26 15:06:37 +01:00
#include <QMessageBox>
2016-09-06 00:03:02 +01:00
#include <QStorageInfo>
2019-01-16 20:00:10 +00:00
#include <ctime>
#include <QtGlobal>
2019-03-25 12:41:48 -04:00
#include "StdStreamUtils.h"
#include "string_format.h"
2018-10-18 08:20:35 -04:00
#ifdef _WIN32
#include "../../tools/PsfPlayer/Source/win32_ui/SH_WaveOut.h"
#ifdef DEBUGGER_INCLUDED
2019-06-04 22:56:44 -04:00
#include "win32/DebugSupport/Debugger.h"
#include "win32/DebugSupport/FrameDebugger/FrameDebugger.h"
#include "ui_debugmenu.h"
#endif
2018-10-18 08:20:35 -04:00
#else
2016-08-23 00:54:10 +01:00
#include "tools/PsfPlayer/Source/SH_OpenAL.h"
2018-10-18 08:20:35 -04:00
#endif
#include "input/PH_GenericInput.h"
2016-07-26 15:06:37 +01:00
#include "DiskUtils.h"
#include "PathUtils.h"
2016-08-10 16:29:40 +01:00
#include <zlib.h>
2016-07-26 15:06:37 +01:00
2019-01-16 20:00:10 +00:00
#include "CoverUtils.h"
2016-08-23 00:54:10 +01:00
#include "PreferenceDefs.h"
2019-03-04 22:19:20 -05:00
#include "PS2VM_Preferences.h"
2017-04-09 06:50:07 +01:00
#include "ScreenShotUtils.h"
2016-09-06 00:12:56 +01:00
#include "ui_mainwindow.h"
2016-09-06 00:03:02 +01:00
#include "vfsmanagerdialog.h"
2019-01-16 20:00:10 +00:00
#include "bootablelistdialog.h"
#include "ControllerConfig/controllerconfigdialog.h"
2016-09-06 00:12:56 +01:00
#ifdef __APPLE__
2018-11-19 19:18:37 -05:00
#include "macos/InputProviderMacOsHid.h"
#endif
2018-11-27 13:25:34 -05:00
#ifdef HAS_LIBEVDEV
#include "unix/InputProviderEvDev.h"
#endif
#ifdef WIN32
#include "win32/InputProviderDirectInput.h"
2019-06-19 19:05:45 -04:00
#include "win32/InputProviderXInput.h"
#endif
2018-04-30 21:01:23 +01:00
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
2016-09-06 00:12:56 +01:00
{
2018-04-30 21:01:23 +01:00
ui->setupUi(this);
2016-07-01 05:21:58 +03:00
RegisterPreferences();
m_continuationChecker = new CContinuationChecker(this);
#ifdef PROFILE
{
m_profileStatsLabel = new QLabel(this);
QFont courierFont("Courier");
m_profileStatsLabel->setFont(courierFont);
m_profileStatsLabel->setAlignment(Qt::AlignTop);
ui->gridLayout->addWidget(m_profileStatsLabel, 0, 1);
}
#endif
2019-08-01 12:44:10 -04:00
2018-04-30 21:01:23 +01:00
m_pauseFocusLost = CAppConfig::GetInstance().GetPreferenceBoolean(PREF_UI_PAUSEWHENFOCUSLOST);
auto lastPath = CAppConfig::GetInstance().GetPreferencePath(PREF_PS2_CDROM0_PATH);
std::error_code lastPathExistsErrorCode;
if(fs::exists(lastPath, lastPathExistsErrorCode))
2018-04-30 21:01:23 +01:00
{
m_lastPath = lastPath.parent_path();
}
else
{
m_lastPath = QStringToPath(QDir::homePath());
2018-04-30 21:01:23 +01:00
}
2016-07-29 18:31:09 +01:00
2018-04-30 21:01:23 +01:00
CreateStatusBar();
UpdateUI();
ui->actionBoot_DiscImage_S3->setVisible(S3FileBrowser::IsAvailable());
InitVirtualMachine();
SetupGsHandler();
#ifdef DEBUGGER_INCLUDED
m_debugger = std::make_unique<CDebugger>(*m_virtualMachine);
m_frameDebugger = std::make_unique<CFrameDebugger>();
auto debugMenu = new QMenu(this);
debugMenuUi = new Ui::DebugMenu();
debugMenuUi->setupUi(debugMenu);
ui->menuBar->insertMenu(ui->menuHelp->menuAction(), debugMenu);
connect(debugMenuUi->actionShowDebugger, &QAction::triggered, this, std::bind(&MainWindow::ShowDebugger, this));
connect(debugMenuUi->actionShowFrameDebugger, &QAction::triggered, this, std::bind(&MainWindow::ShowFrameDebugger, this));
2019-03-25 12:41:48 -04:00
connect(debugMenuUi->actionDumpNextFrame, &QAction::triggered, this, std::bind(&MainWindow::DumpNextFrame, this));
connect(debugMenuUi->actionGsDrawEnabled, &QAction::triggered, this, std::bind(&MainWindow::ToggleGsDraw, this));
#endif
2016-09-06 00:12:56 +01:00
}
MainWindow::~MainWindow()
{
#ifdef DEBUGGER_INCLUDED
m_debugger.reset();
m_frameDebugger.reset();
delete debugMenuUi;
#endif
2018-04-30 21:01:23 +01:00
CAppConfig::GetInstance().Save();
2018-08-01 12:59:11 -04:00
if(m_virtualMachine != nullptr)
2018-04-30 21:01:23 +01:00
{
2018-08-01 12:59:11 -04:00
m_virtualMachine->Pause();
m_qtKeyInputProvider.reset();
2018-08-01 12:59:11 -04:00
m_virtualMachine->DestroyPadHandler();
m_virtualMachine->DestroyGSHandler();
m_virtualMachine->DestroySoundHandler();
m_virtualMachine->Destroy();
delete m_virtualMachine;
m_virtualMachine = nullptr;
2018-04-30 21:01:23 +01:00
}
delete ui;
}
void MainWindow::InitVirtualMachine()
2016-08-02 02:50:26 +01:00
{
assert(!m_virtualMachine);
2018-08-01 12:59:11 -04:00
m_virtualMachine = new CPS2VM();
m_virtualMachine->Initialize();
2018-04-30 21:01:23 +01:00
SetupSoundHandler();
2016-07-26 00:52:31 +01:00
{
m_virtualMachine->CreatePadHandler(CPH_GenericInput::GetFactoryFunction());
auto padHandler = static_cast<CPH_GenericInput*>(m_virtualMachine->GetPadHandler());
2020-02-25 18:47:50 +00:00
auto profile = CAppConfig::GetInstance().GetPreferenceString(PREF_INPUT_PAD1_PROFILE);
auto& bindingManager = padHandler->GetBindingManager();
2020-02-25 18:47:50 +00:00
bindingManager.Load(profile);
//Create QtKeyInputProvider
m_qtKeyInputProvider = std::make_shared<CInputProviderQtKey>();
bindingManager.RegisterInputProvider(m_qtKeyInputProvider);
#ifdef __APPLE__
bindingManager.RegisterInputProvider(std::make_shared<CInputProviderMacOsHid>());
#endif
2018-11-27 13:25:34 -05:00
#ifdef HAS_LIBEVDEV
bindingManager.RegisterInputProvider(std::make_shared<CInputProviderEvDev>());
#endif
#ifdef WIN32
bindingManager.RegisterInputProvider(std::make_shared<CInputProviderDirectInput>());
2019-06-20 18:21:46 -04:00
bindingManager.RegisterInputProvider(std::make_shared<CInputProviderXInput>());
2018-11-27 13:25:34 -05:00
#endif
if(!bindingManager.HasBindings())
2018-10-23 01:40:35 +01:00
{
2018-11-19 19:11:39 -05:00
ControllerConfigDialog::AutoConfigureKeyboard(&bindingManager);
2018-10-23 01:40:35 +01:00
}
}
2018-11-28 22:32:27 -05:00
#ifdef PROFILE
m_profileFrameDoneConnection = m_virtualMachine->ProfileFrameDone.Connect(std::bind(&CStatsManager::OnProfileFrameDone, &CStatsManager::GetInstance(), m_virtualMachine, std::placeholders::_1));
#endif
2019-08-01 12:44:10 -04:00
//OnExecutableChange might be called from another thread, we need to wrap it around a Qt signal
2019-07-01 13:11:23 +01:00
m_OnExecutableChangeConnection = m_virtualMachine->m_ee->m_os->OnExecutableChange.Connect(std::bind(&MainWindow::EmitOnExecutableChange, this));
connect(this, SIGNAL(onExecutableChange()), this, SLOT(HandleOnExecutableChange()));
}
void MainWindow::SetOutputWindowSize()
2016-09-06 00:12:56 +01:00
{
outputWindow_resized();
}
void MainWindow::SetupGsHandler()
{
assert(m_virtualMachine);
2020-02-11 07:53:35 +00:00
switch(CAppConfig::GetInstance().GetPreferenceInteger(PREF_VIDEO_GS_HANDLER))
{
2019-12-15 12:38:16 -05:00
#ifdef HAS_GSH_VULKAN
2020-02-11 07:53:35 +00:00
case SettingsDialog::GS_HANDLERS::VULKAN:
2020-02-09 23:05:24 +00:00
{
assert(GSH_Vulkan::CDeviceInfo::GetInstance().HasAvailableDevices());
2020-02-10 22:11:18 +00:00
m_outputwindow = new VulkanWindow;
QWidget* container = QWidget::createWindowContainer(m_outputwindow);
m_outputwindow->create();
ui->gridLayout->addWidget(container, 0, 0);
2020-02-09 23:05:24 +00:00
m_virtualMachine->CreateGSHandler(CGSH_VulkanQt::GetFactoryFunction(m_outputwindow));
}
2020-02-11 07:53:35 +00:00
break;
case SettingsDialog::GS_HANDLERS::OPENGL:
#endif
2020-02-11 07:53:35 +00:00
default:
2020-02-09 23:05:24 +00:00
{
2020-02-10 22:11:18 +00:00
m_outputwindow = new OpenGLWindow;
QWidget* container = QWidget::createWindowContainer(m_outputwindow);
m_outputwindow->create();
ui->gridLayout->addWidget(container, 0, 0);
2020-02-09 23:05:24 +00:00
m_virtualMachine->CreateGSHandler(CGSH_OpenGLQt::GetFactoryFunction(m_outputwindow));
}
2020-02-11 07:53:35 +00:00
}
2020-02-10 22:11:18 +00:00
connect(m_outputwindow, SIGNAL(heightChanged(int)), this, SLOT(outputWindow_resized()));
connect(m_outputwindow, SIGNAL(widthChanged(int)), this, SLOT(outputWindow_resized()));
connect(m_outputwindow, SIGNAL(keyUp(QKeyEvent*)), this, SLOT(keyReleaseEvent(QKeyEvent*)));
connect(m_outputwindow, SIGNAL(keyDown(QKeyEvent*)), this, SLOT(keyPressEvent(QKeyEvent*)));
connect(m_outputwindow, SIGNAL(focusOut(QFocusEvent*)), this, SLOT(focusOutEvent(QFocusEvent*)));
connect(m_outputwindow, SIGNAL(focusIn(QFocusEvent*)), this, SLOT(focusInEvent(QFocusEvent*)));
connect(m_outputwindow, SIGNAL(doubleClick(QMouseEvent*)), this, SLOT(doubleClickEvent(QMouseEvent*)));
2020-02-09 23:05:24 +00:00
m_OnNewFrameConnection = m_virtualMachine->m_ee->m_gs->OnNewFrame.Connect(std::bind(&CStatsManager::OnNewFrame, &CStatsManager::GetInstance(), std::placeholders::_1));
}
2016-08-10 16:42:37 +01:00
void MainWindow::SetupSoundHandler()
2016-08-23 00:54:10 +01:00
{
2018-08-01 13:01:48 -04:00
assert(m_virtualMachine);
bool audioEnabled = CAppConfig::GetInstance().GetPreferenceBoolean(PREFERENCE_AUDIO_ENABLEOUTPUT);
if(audioEnabled)
2018-04-30 21:01:23 +01:00
{
2018-10-18 08:20:35 -04:00
#ifdef _WIN32
m_virtualMachine->CreateSoundHandler(&CSH_WaveOut::HandlerFactory);
#else
2018-08-01 13:01:48 -04:00
m_virtualMachine->CreateSoundHandler(&CSH_OpenAL::HandlerFactory);
2018-10-18 08:20:35 -04:00
#endif
2018-08-01 13:01:48 -04:00
}
else
{
m_virtualMachine->DestroySoundHandler();
2018-04-30 21:01:23 +01:00
}
2016-08-23 00:54:10 +01:00
}
void MainWindow::outputWindow_resized()
{
2018-08-01 12:59:11 -04:00
if(m_virtualMachine != nullptr && m_virtualMachine->m_ee != nullptr && m_virtualMachine->m_ee->m_gs != nullptr)
2018-04-30 21:01:23 +01:00
{
uint32 w = m_outputwindow->size().width(), h = m_outputwindow->size().height();
qreal scale = 1.0;
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
scale = devicePixelRatioF();
2020-04-10 15:39:04 -04:00
#endif
2018-04-30 21:01:23 +01:00
CGSHandler::PRESENTATION_PARAMS presentationParams;
2018-08-08 12:21:41 -04:00
presentationParams.mode = static_cast<CGSHandler::PRESENTATION_MODE>(CAppConfig::GetInstance().GetPreferenceInteger(PREF_CGSHANDLER_PRESENTATION_MODE));
2018-05-23 03:12:05 +03:00
presentationParams.windowWidth = w * scale;
presentationParams.windowHeight = h * scale;
2018-08-01 12:59:11 -04:00
m_virtualMachine->m_ee->m_gs->SetPresentationParams(presentationParams);
m_virtualMachine->m_ee->m_gs->Flip(true);
2018-04-30 21:01:23 +01:00
}
}
2018-09-28 08:17:24 -04:00
void MainWindow::on_actionBoot_DiscImage_triggered()
{
2018-04-30 21:01:23 +01:00
QFileDialog dialog(this);
dialog.setDirectory(PathToQString(m_lastPath));
2018-04-30 21:01:23 +01:00
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilter(tr("All supported types(*.iso *.bin *.isz *.cso);;UltraISO Compressed Disk Images (*.isz);;CISO Compressed Disk Images (*.cso);;All files (*.*)"));
if(dialog.exec())
{
auto filePath = QStringToPath(dialog.selectedFiles().first());
m_lastPath = filePath.parent_path();
CAppConfig::GetInstance().SetPreferencePath(PREF_PS2_CDROM0_PATH, filePath);
2018-04-30 21:01:23 +01:00
2018-08-01 12:59:11 -04:00
if(m_virtualMachine != nullptr)
2018-04-30 21:01:23 +01:00
{
try
{
TryRegisteringBootable(filePath);
m_lastOpenCommand = LastOpenCommand(BootType::CD, filePath);
2018-04-30 21:01:23 +01:00
BootCDROM();
}
catch(const std::exception& e)
{
QMessageBox messageBox;
2018-07-31 18:47:14 -04:00
messageBox.critical(nullptr, "Error", e.what());
2018-04-30 21:01:23 +01:00
messageBox.show();
}
}
}
}
void MainWindow::on_actionBoot_DiscImage_S3_triggered()
{
S3FileBrowser browser(this);
if(browser.exec())
{
auto filePath = browser.GetSelectedPath();
CAppConfig::GetInstance().SetPreferencePath(PREF_PS2_CDROM0_PATH, filePath);
if(m_virtualMachine != nullptr)
{
try
{
BootCDROM();
}
catch(const std::exception& e)
{
QMessageBox messageBox;
messageBox.critical(nullptr, "Error", e.what());
messageBox.show();
}
}
}
}
2018-09-28 12:00:02 -04:00
void MainWindow::on_actionBoot_cdrom0_triggered()
{
try
{
BootCDROM();
}
catch(const std::exception& e)
{
QMessageBox messageBox;
messageBox.critical(nullptr, "Error", e.what());
messageBox.show();
}
}
void MainWindow::on_actionBoot_ELF_triggered()
{
2018-04-30 21:01:23 +01:00
QFileDialog dialog(this);
dialog.setDirectory(PathToQString(m_lastPath));
2018-04-30 21:01:23 +01:00
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilter(tr("ELF files (*.elf)"));
if(dialog.exec())
{
auto filePath = QStringToPath(dialog.selectedFiles().first());
m_lastPath = filePath.parent_path();
2018-08-01 12:59:11 -04:00
if(m_virtualMachine != nullptr)
2018-04-30 21:01:23 +01:00
{
try
{
BootElf(filePath);
2018-04-30 21:01:23 +01:00
}
catch(const std::exception& e)
{
QMessageBox messageBox;
2018-07-31 18:47:14 -04:00
messageBox.critical(nullptr, "Error", e.what());
2018-04-30 21:01:23 +01:00
messageBox.show();
}
}
}
}
2019-10-16 20:51:11 -04:00
void MainWindow::BootElf(fs::path filePath)
2016-07-29 19:19:27 +01:00
{
2019-01-16 20:00:10 +00:00
BootablesDb::CClient::GetInstance().SetLastBootedTime(filePath, std::time(nullptr));
2018-10-08 02:44:23 +01:00
m_lastOpenCommand = LastOpenCommand(BootType::ELF, filePath);
2018-08-01 12:59:11 -04:00
m_virtualMachine->Pause();
m_virtualMachine->Reset();
2018-09-22 21:42:03 -04:00
m_virtualMachine->m_ee->m_os->BootFromFile(filePath);
#ifndef DEBUGGER_INCLUDED
2018-08-01 12:59:11 -04:00
m_virtualMachine->Resume();
#endif
m_msgLabel->setText(QString("Loaded executable '%1'.")
2018-08-09 12:28:42 -04:00
.arg(m_virtualMachine->m_ee->m_os->GetExecutableName()));
2016-07-29 19:19:27 +01:00
}
2019-10-16 20:51:11 -04:00
void MainWindow::LoadCDROM(fs::path filePath)
2018-10-08 02:43:23 +01:00
{
2018-10-08 02:44:23 +01:00
m_lastPath = filePath.parent_path();
2018-10-08 02:43:23 +01:00
CAppConfig::GetInstance().SetPreferencePath(PREF_PS2_CDROM0_PATH, filePath);
}
2016-07-29 19:19:27 +01:00
void MainWindow::BootCDROM()
{
auto filePath = CAppConfig::GetInstance().GetPreferencePath(PREF_PS2_CDROM0_PATH);
m_lastOpenCommand = LastOpenCommand(BootType::CD, filePath);
2019-01-16 20:00:10 +00:00
BootablesDb::CClient::GetInstance().SetLastBootedTime(filePath, std::time(nullptr));
2018-08-01 12:59:11 -04:00
m_virtualMachine->Pause();
m_virtualMachine->Reset();
m_virtualMachine->m_ee->m_os->BootFromCDROM();
#ifndef DEBUGGER_INCLUDED
2018-08-01 12:59:11 -04:00
m_virtualMachine->Resume();
#endif
m_msgLabel->setText(QString("Loaded executable '%1' from cdrom0.")
2018-08-09 12:28:42 -04:00
.arg(m_virtualMachine->m_ee->m_os->GetExecutableName()));
2016-07-29 19:19:27 +01:00
}
void MainWindow::on_actionExit_triggered()
{
2018-04-30 21:01:23 +01:00
close();
2016-09-06 00:12:56 +01:00
}
2016-07-26 00:52:31 +01:00
2018-04-30 21:01:23 +01:00
void MainWindow::keyPressEvent(QKeyEvent* event)
2016-07-26 00:52:31 +01:00
{
if((event->key() != Qt::Key_Escape) && m_qtKeyInputProvider)
2018-04-30 21:01:23 +01:00
{
m_qtKeyInputProvider->OnKeyPress(event->key());
2018-04-30 21:01:23 +01:00
}
2016-07-26 00:52:31 +01:00
}
2018-04-30 21:01:23 +01:00
void MainWindow::keyReleaseEvent(QKeyEvent* event)
2016-07-26 00:52:31 +01:00
{
2018-04-30 21:01:23 +01:00
if(event->key() == Qt::Key_Escape)
{
if(isFullScreen())
{
toggleFullscreen();
}
return;
}
if(m_qtKeyInputProvider)
2018-04-30 21:01:23 +01:00
{
m_qtKeyInputProvider->OnKeyRelease(event->key());
2018-04-30 21:01:23 +01:00
}
2016-07-26 00:52:31 +01:00
}
2016-08-23 00:54:10 +01:00
2016-08-10 16:42:37 +01:00
void MainWindow::CreateStatusBar()
2016-08-23 00:54:10 +01:00
{
m_fpsLabel = new QLabel("");
m_fpsLabel->setAlignment(Qt::AlignHCenter);
m_fpsLabel->setMinimumSize(m_fpsLabel->sizeHint());
2018-04-30 21:01:23 +01:00
m_msgLabel = new ElidedLabel();
m_msgLabel->setAlignment(Qt::AlignLeft);
QFontMetrics fm(m_msgLabel->font());
m_msgLabel->setMinimumSize(fm.boundingRect("...").size());
m_msgLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
2017-03-25 21:53:28 +00:00
2018-04-30 21:01:23 +01:00
statusBar()->addWidget(m_msgLabel, 1);
statusBar()->addWidget(m_fpsLabel);
2020-02-10 22:11:18 +00:00
#ifdef HAS_GSH_VULKAN
if(GSH_Vulkan::CDeviceInfo::GetInstance().HasAvailableDevices())
{
m_gsLabel = new QLabel("");
2020-02-10 22:11:18 +00:00
auto gs_index = CAppConfig::GetInstance().GetPreferenceInteger(PREF_VIDEO_GS_HANDLER);
UpdateGSHandlerLabel(gs_index);
m_gsLabel->setAlignment(Qt::AlignHCenter);
m_gsLabel->setMinimumSize(m_gsLabel->sizeHint());
//QLabel have no click event, so we're using ContextMenu event aka rightClick to toggle GS
m_gsLabel->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_gsLabel, &QLabel::customContextMenuRequested, [&]() {
auto gs_index = CAppConfig::GetInstance().GetPreferenceInteger(PREF_VIDEO_GS_HANDLER);
gs_index = (gs_index + 1) % SettingsDialog::GS_HANDLERS::MAX_HANDLER;
CAppConfig::GetInstance().SetPreferenceInteger(PREF_VIDEO_GS_HANDLER, gs_index);
SetupGsHandler();
UpdateGSHandlerLabel(gs_index);
});
statusBar()->addWidget(m_gsLabel);
}
2020-02-10 22:11:18 +00:00
#endif
m_msgLabel->setText(QString("Play! v%1 - %2").arg(PLAY_VERSION).arg(__DATE__));
2018-08-07 19:17:28 -04:00
m_fpsTimer = new QTimer(this);
2019-07-24 19:35:46 -04:00
connect(m_fpsTimer, SIGNAL(timeout()), this, SLOT(updateStats()));
2018-08-07 19:17:28 -04:00
m_fpsTimer->start(1000);
2016-08-23 00:54:10 +01:00
}
2019-07-24 19:35:46 -04:00
void MainWindow::updateStats()
2016-08-23 00:54:10 +01:00
{
2019-07-24 19:35:46 -04:00
uint32 frames = CStatsManager::GetInstance().GetFrames();
uint32 drawCalls = CStatsManager::GetInstance().GetDrawCalls();
uint32 dcpf = (frames != 0) ? (drawCalls / frames) : 0;
#ifdef PROFILE
m_profileStatsLabel->setText(QString::fromStdString(CStatsManager::GetInstance().GetProfilingInfo()));
#endif
m_fpsLabel->setText(QString("%1 f/s, %2 dc/f").arg(frames).arg(dcpf));
2019-07-24 19:35:46 -04:00
CStatsManager::GetInstance().ClearStats();
}
2016-08-23 00:54:10 +01:00
void MainWindow::on_actionSettings_triggered()
{
2020-02-10 22:11:18 +00:00
auto gs_index = CAppConfig::GetInstance().GetPreferenceInteger(PREF_VIDEO_GS_HANDLER);
2018-04-30 21:01:23 +01:00
SettingsDialog sd;
sd.exec();
SetupSoundHandler();
2019-03-30 14:43:25 +00:00
if(m_virtualMachine != nullptr)
{
m_virtualMachine->ReloadSpuBlockCount();
2020-02-10 22:11:18 +00:00
auto new_gs_index = CAppConfig::GetInstance().GetPreferenceInteger(PREF_VIDEO_GS_HANDLER);
if(gs_index != new_gs_index)
{
UpdateGSHandlerLabel(new_gs_index);
2020-02-10 22:11:18 +00:00
SetupGsHandler();
}
else
2019-03-30 14:43:25 +00:00
{
2020-02-10 22:11:18 +00:00
outputWindow_resized();
auto gsHandler = m_virtualMachine->GetGSHandler();
if(gsHandler)
{
gsHandler->NotifyPreferencesChanged();
}
2019-03-30 14:43:25 +00:00
}
}
2016-08-23 00:54:10 +01:00
}
2016-07-26 15:06:37 +01:00
2016-08-10 16:42:37 +01:00
void MainWindow::SetupSaveLoadStateSlots()
2016-08-02 02:50:26 +01:00
{
2018-08-01 12:59:11 -04:00
bool enable = (m_virtualMachine != nullptr ? (m_virtualMachine->m_ee->m_os->GetELF() != nullptr) : false);
2018-04-30 21:01:23 +01:00
ui->menuSave_States->clear();
ui->menuLoad_States->clear();
2019-03-13 12:30:21 -04:00
for(int i = 0; i < 10; i++)
2018-04-30 21:01:23 +01:00
{
2019-03-13 12:30:21 -04:00
QString info = enable ? GetSaveStateInfo(i) : "Empty";
2016-07-26 15:06:37 +01:00
2018-04-30 21:01:23 +01:00
QAction* saveaction = new QAction(this);
saveaction->setText(QString("Save Slot %1 - %2").arg(i).arg(info));
saveaction->setEnabled(enable);
ui->menuSave_States->addAction(saveaction);
2016-07-26 15:06:37 +01:00
2018-04-30 21:01:23 +01:00
QAction* loadaction = new QAction(this);
loadaction->setText(QString("Load Slot %1 - %2").arg(i).arg(info));
loadaction->setEnabled(enable);
ui->menuLoad_States->addAction(loadaction);
2016-07-26 15:06:37 +01:00
2018-04-30 21:01:23 +01:00
if(enable)
{
connect(saveaction, &QAction::triggered, std::bind(&MainWindow::saveState, this, i));
connect(loadaction, &QAction::triggered, std::bind(&MainWindow::loadState, this, i));
}
}
2016-07-26 15:06:37 +01:00
}
2017-02-27 22:09:05 +00:00
void MainWindow::saveState(int stateSlot)
2016-08-02 02:50:26 +01:00
{
2018-08-01 12:59:11 -04:00
auto stateFilePath = m_virtualMachine->GenerateStatePath(stateSlot);
auto future = m_virtualMachine->SaveState(stateFilePath);
m_continuationChecker->GetContinuationManager().Register(std::move(future),
2018-10-11 12:54:56 -04:00
[this, stateSlot = stateSlot](const bool& succeeded) {
if(succeeded)
{
m_msgLabel->setText(QString("Saved state to slot %1.").arg(stateSlot));
QString datetime = GetSaveStateInfo(stateSlot);
2019-03-13 12:30:21 -04:00
ui->menuSave_States->actions().at(stateSlot)->setText(QString("Save Slot %1 - %2").arg(stateSlot).arg(datetime));
ui->menuLoad_States->actions().at(stateSlot)->setText(QString("Load Slot %1 - %2").arg(stateSlot).arg(datetime));
2018-10-11 12:54:56 -04:00
}
else
{
m_msgLabel->setText(QString("Error saving state to slot %1.").arg(stateSlot));
}
});
2016-07-26 15:06:37 +01:00
}
2018-04-30 21:01:23 +01:00
void MainWindow::loadState(int stateSlot)
{
2018-08-01 12:59:11 -04:00
auto stateFilePath = m_virtualMachine->GenerateStatePath(stateSlot);
auto future = m_virtualMachine->LoadState(stateFilePath);
m_continuationChecker->GetContinuationManager().Register(std::move(future),
2018-10-11 12:54:56 -04:00
[this, stateSlot = stateSlot](const bool& succeeded) {
if(succeeded)
{
m_msgLabel->setText(QString("Loaded state from slot %1.").arg(stateSlot));
#ifndef DEBUGGER_INCLUDED
2018-10-11 12:54:56 -04:00
m_virtualMachine->Resume();
#endif
2018-10-11 12:54:56 -04:00
}
else
{
m_msgLabel->setText(QString("Error loading state from slot %1.").arg(stateSlot));
}
});
2016-07-26 15:06:37 +01:00
}
2016-08-02 02:50:26 +01:00
2019-03-13 12:30:21 -04:00
QString MainWindow::GetSaveStateInfo(int stateSlot)
2016-08-02 02:50:26 +01:00
{
2018-08-01 12:59:11 -04:00
auto stateFilePath = m_virtualMachine->GenerateStatePath(stateSlot);
QFileInfo file(PathToQString(stateFilePath));
2018-04-30 21:01:23 +01:00
if(file.exists() && file.isFile())
{
return file.lastModified().toString("hh:mm dd.MM.yyyy");
2018-04-30 21:01:23 +01:00
}
else
{
return "Empty";
}
2016-07-26 15:06:37 +01:00
}
void MainWindow::on_actionPause_Resume_triggered()
{
2018-08-01 12:59:11 -04:00
if(m_virtualMachine != nullptr)
2018-04-30 21:01:23 +01:00
{
2018-08-01 12:59:11 -04:00
if(m_virtualMachine->GetStatus() == CVirtualMachine::PAUSED)
2018-04-30 21:01:23 +01:00
{
m_msgLabel->setText("Virtual machine resumed.");
2018-08-01 12:59:11 -04:00
m_virtualMachine->Resume();
2018-04-30 21:01:23 +01:00
}
else
{
m_msgLabel->setText("Virtual machine paused.");
2018-08-01 12:59:11 -04:00
m_virtualMachine->Pause();
2018-04-30 21:01:23 +01:00
}
}
}
void MainWindow::closeEvent(QCloseEvent* event)
{
QMessageBox::StandardButton resBtn = QMessageBox::question(this, "Close Confirmation?",
tr("Are you sure you want to exit?\nHave you saved your progress?\n"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes);
if(resBtn != QMessageBox::Yes)
{
event->ignore();
}
else
{
event->accept();
}
2016-07-26 00:22:08 +01:00
}
2016-08-10 16:29:40 +01:00
void MainWindow::on_actionAbout_triggered()
{
2019-10-29 19:50:46 -04:00
auto about = QString("Version %1 (%2)\nQt v%3 - zlib v%4")
.arg(QString(PLAY_VERSION), __DATE__, QT_VERSION_STR, ZLIB_VERSION);
2019-03-04 20:07:38 -05:00
QMessageBox::about(this, this->windowTitle(), about);
2016-08-10 16:29:40 +01:00
}
2016-07-27 13:47:50 +01:00
void MainWindow::EmitOnExecutableChange()
{
emit onExecutableChange();
}
void MainWindow::HandleOnExecutableChange()
2016-07-27 13:47:50 +01:00
{
2018-04-30 21:01:23 +01:00
UpdateUI();
2019-07-22 14:56:37 +01:00
auto titleString = QString("Play! - [ %1 ] - %2").arg(m_virtualMachine->m_ee->m_os->GetExecutableName(), QString(PLAY_VERSION));
setWindowTitle(titleString);
2016-07-27 13:47:50 +01:00
}
void MainWindow::UpdateUI()
{
2018-04-30 21:01:23 +01:00
ui->actionPause_when_focus_is_lost->setChecked(m_pauseFocusLost);
ui->actionReset->setEnabled(!m_lastOpenCommand.path.empty());
SetOutputWindowSize();
2018-04-30 21:01:23 +01:00
SetupSaveLoadStateSlots();
2016-07-27 13:47:50 +01:00
}
void MainWindow::RegisterPreferences()
{
2018-04-30 21:01:23 +01:00
CAppConfig::GetInstance().RegisterPreferenceBoolean(PREFERENCE_AUDIO_ENABLEOUTPUT, true);
CAppConfig::GetInstance().RegisterPreferenceBoolean(PREF_UI_PAUSEWHENFOCUSLOST, true);
2020-02-11 07:53:35 +00:00
CAppConfig::GetInstance().RegisterPreferenceInteger(PREF_VIDEO_GS_HANDLER, SettingsDialog::GS_HANDLERS::OPENGL);
2020-02-25 18:47:50 +00:00
CAppConfig::GetInstance().RegisterPreferenceString(PREF_INPUT_PAD1_PROFILE, "default");
2016-07-29 18:31:09 +01:00
}
2018-04-30 21:01:23 +01:00
void MainWindow::focusOutEvent(QFocusEvent* event)
2016-07-29 18:31:09 +01:00
{
2018-08-01 12:59:11 -04:00
if(m_pauseFocusLost && m_virtualMachine->GetStatus() == CVirtualMachine::RUNNING)
2018-04-30 21:01:23 +01:00
{
if(!isActiveWindow() && !m_outputwindow->isActive())
2018-04-30 21:01:23 +01:00
{
2018-08-01 12:59:11 -04:00
if(m_virtualMachine != nullptr)
2018-04-30 21:01:23 +01:00
{
2018-08-01 12:59:11 -04:00
m_virtualMachine->Pause();
2018-04-30 21:01:23 +01:00
m_deactivatePause = true;
}
}
}
2016-07-29 18:31:09 +01:00
}
2018-04-30 21:01:23 +01:00
void MainWindow::focusInEvent(QFocusEvent* event)
2016-07-29 18:31:09 +01:00
{
2018-08-01 12:59:11 -04:00
if(m_pauseFocusLost && m_virtualMachine->GetStatus() == CVirtualMachine::PAUSED)
2018-04-30 21:01:23 +01:00
{
if(m_deactivatePause && (isActiveWindow() || m_outputwindow->isActive()))
2018-04-30 21:01:23 +01:00
{
2018-08-01 12:59:11 -04:00
if(m_virtualMachine != nullptr)
2018-04-30 21:01:23 +01:00
{
2018-08-01 12:59:11 -04:00
m_virtualMachine->Resume();
2018-04-30 21:01:23 +01:00
m_deactivatePause = false;
}
}
}
2016-07-29 18:31:09 +01:00
}
2018-04-30 21:01:23 +01:00
void MainWindow::doubleClickEvent(QMouseEvent* ev)
2017-04-22 19:46:31 +01:00
{
2018-04-30 21:01:23 +01:00
if(ev->button() == Qt::LeftButton)
{
toggleFullscreen();
}
2017-04-22 19:46:31 +01:00
}
void MainWindow::toggleFullscreen()
{
2018-04-30 21:01:23 +01:00
if(isFullScreen())
{
showNormal();
statusBar()->show();
menuBar()->show();
}
else
{
showFullScreen();
statusBar()->hide();
menuBar()->hide();
}
2017-04-22 19:46:31 +01:00
}
#ifdef DEBUGGER_INCLUDED
bool MainWindow::nativeEventFilter(const QByteArray&, void* message, long* result)
{
auto msg = reinterpret_cast<MSG*>(message);
HWND activeWnd = GetActiveWindow();
if(m_debugger && (activeWnd == m_debugger->m_hWnd) &&
TranslateAccelerator(m_debugger->m_hWnd, m_debugger->GetAccelerators(), msg))
{
return true;
}
if(m_frameDebugger && (activeWnd == m_frameDebugger->m_hWnd) &&
TranslateAccelerator(m_frameDebugger->m_hWnd, m_frameDebugger->GetAccelerators(), msg))
{
return true;
}
return false;
}
void MainWindow::ShowDebugger()
{
m_debugger->Show(SW_SHOWMAXIMIZED);
SetForegroundWindow(*m_debugger);
}
void MainWindow::ShowFrameDebugger()
{
m_frameDebugger->Show(SW_SHOWMAXIMIZED);
SetForegroundWindow(*m_frameDebugger);
}
2019-03-25 12:41:48 -04:00
2019-10-16 20:51:11 -04:00
fs::path MainWindow::GetFrameDumpDirectoryPath()
2019-03-25 12:41:48 -04:00
{
2019-10-16 20:51:11 -04:00
return CAppConfig::GetBasePath() / fs::path("framedumps/");
2019-03-25 12:41:48 -04:00
}
void MainWindow::DumpNextFrame()
{
m_virtualMachine->TriggerFrameDump(
[&](const CFrameDump& frameDump) {
try
{
auto frameDumpDirectoryPath = GetFrameDumpDirectoryPath();
Framework::PathUtils::EnsurePathExists(frameDumpDirectoryPath);
for(unsigned int i = 0; i < UINT_MAX; i++)
{
auto frameDumpFileName = string_format("framedump_%08d.dmp.zip", i);
2019-10-16 20:51:11 -04:00
auto frameDumpPath = frameDumpDirectoryPath / fs::path(frameDumpFileName);
if(!fs::exists(frameDumpPath))
2019-03-25 12:41:48 -04:00
{
auto dumpStream = Framework::CreateOutputStdStream(frameDumpPath.native());
frameDump.Write(dumpStream);
m_msgLabel->setText(QString("Dumped frame to '%1'.").arg(frameDumpFileName.c_str()));
return;
}
}
}
catch(...)
{
}
m_msgLabel->setText(QString("Failed to dump frame."));
});
}
void MainWindow::ToggleGsDraw()
{
auto gs = m_virtualMachine->GetGSHandler();
if(gs == nullptr) return;
bool newState = !gs->GetDrawEnabled();
gs->SetDrawEnabled(newState);
debugMenuUi->actionGsDrawEnabled->setChecked(newState);
m_msgLabel->setText(newState ? QString("GS Draw Enabled") : QString("GS Draw Disabled"));
}
#endif
2016-07-29 18:31:09 +01:00
void MainWindow::on_actionPause_when_focus_is_lost_triggered(bool checked)
{
2018-04-30 21:01:23 +01:00
m_pauseFocusLost = checked;
CAppConfig::GetInstance().SetPreferenceBoolean(PREF_UI_PAUSEWHENFOCUSLOST, m_pauseFocusLost);
}
2016-07-29 19:19:27 +01:00
void MainWindow::on_actionReset_triggered()
{
if(!m_lastOpenCommand.path.empty())
2018-04-30 21:01:23 +01:00
{
if(m_lastOpenCommand.type == BootType::CD)
{
BootCDROM();
}
else if(m_lastOpenCommand.type == BootType::ELF)
{
BootElf(m_lastOpenCommand.path);
2018-04-30 21:01:23 +01:00
}
}
2016-07-29 19:19:27 +01:00
}
2016-08-10 14:56:16 +01:00
void MainWindow::on_actionMemory_Card_Manager_triggered()
{
MemoryCardManagerDialog mcm(this);
2018-04-30 21:01:23 +01:00
mcm.exec();
2016-08-10 14:56:16 +01:00
}
2016-09-06 00:03:02 +01:00
void MainWindow::on_actionVFS_Manager_triggered()
{
2018-04-30 21:01:23 +01:00
VFSManagerDialog vfsmgr;
vfsmgr.exec();
2016-09-06 00:03:02 +01:00
}
2016-09-06 00:23:31 +01:00
void MainWindow::on_actionController_Manager_triggered()
{
auto padHandler = static_cast<CPH_GenericInput*>(m_virtualMachine->GetPadHandler());
if(!padHandler) return;
2018-11-28 22:32:27 -05:00
ControllerConfigDialog ccd(&padHandler->GetBindingManager(), m_qtKeyInputProvider.get(), this);
2018-04-30 21:01:23 +01:00
ccd.exec();
2016-09-06 00:23:31 +01:00
}
2017-04-09 06:50:07 +01:00
void MainWindow::on_actionCapture_Screen_triggered()
{
m_screenShotCompleteConnection = CScreenShotUtils::TriggerGetScreenshot(m_virtualMachine,
2019-08-19 19:05:35 -04:00
[&](int res, const char* msg) -> void {
m_msgLabel->setText(msg);
});
2017-04-09 06:50:07 +01:00
}
2019-01-16 20:00:10 +00:00
void MainWindow::on_actionList_Bootables_triggered()
{
BootableListDialog dialog(this);
if(dialog.exec())
{
try
{
BootablesDb::Bootable bootable = dialog.getResult();
if(IsBootableDiscImagePath(bootable.path))
{
LoadCDROM(bootable.path);
BootCDROM();
}
else if(IsBootableExecutablePath(bootable.path))
{
BootElf(bootable.path);
}
else
{
QMessageBox messageBox;
QString invalid("Invalid File Format.");
messageBox.critical(this, this->windowTitle(), invalid);
messageBox.show();
}
}
catch(const std::exception& e)
{
QMessageBox messageBox;
messageBox.critical(nullptr, "Error", e.what());
messageBox.show();
}
}
}
void MainWindow::UpdateGSHandlerLabel(int gs_index)
{
switch(gs_index)
{
default:
case SettingsDialog::GS_HANDLERS::OPENGL:
m_gsLabel->setText("OpenGL");
break;
#ifdef HAS_GSH_VULKAN
case SettingsDialog::GS_HANDLERS::VULKAN:
m_gsLabel->setText("Vulkan");
break;
#endif
}
}