2021-12-13 12:12:54 +00:00
/* PCSX2 - PS2 Emulator for PCs
* Copyright ( C ) 2002 - 2022 PCSX2 Dev Team
*
* PCSX2 is free software : you can redistribute it and / or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found -
* ation , either version 3 of the License , or ( at your option ) any later version .
*
* PCSX2 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 PCSX2 .
* If not , see < http : //www.gnu.org/licenses/>.
*/
# include "PrecompiledHeader.h"
# include <QtCore/QDateTime>
2022-03-04 10:40:03 +00:00
# include <QtGui/QCloseEvent>
2021-12-13 12:12:54 +00:00
# include <QtWidgets/QFileDialog>
# include <QtWidgets/QMessageBox>
# include <QtWidgets/QProgressBar>
# include <QtWidgets/QStyle>
# include <QtWidgets/QStyleFactory>
2022-05-18 13:27:23 +00:00
# include "common/Assertions.h"
2022-05-25 22:32:10 +00:00
# include "common/CocoaTools.h"
2021-12-13 12:12:54 +00:00
# include "common/FileSystem.h"
# include "pcsx2/CDVD/CDVDaccess.h"
# include "pcsx2/Frontend/GameList.h"
2022-06-18 09:35:17 +00:00
# include "pcsx2/Frontend/LogSink.h"
2022-03-25 10:14:23 +00:00
# include "pcsx2/GSDumpReplayer.h"
2021-12-13 12:12:54 +00:00
# include "pcsx2/HostDisplay.h"
2022-05-24 12:37:44 +00:00
# include "pcsx2/HostSettings.h"
2022-04-03 13:46:05 +00:00
# include "pcsx2/PerformanceMetrics.h"
2022-04-04 00:06:59 +00:00
# include "pcsx2/Recording/InputRecording.h"
2021-12-13 12:12:54 +00:00
# include "AboutDialog.h"
2022-04-04 11:27:23 +00:00
# include "AutoUpdaterDialog.h"
2021-12-13 12:12:54 +00:00
# include "DisplayWidget.h"
# include "EmuThread.h"
# include "GameList/GameListRefreshThread.h"
# include "GameList/GameListWidget.h"
# include "MainWindow.h"
# include "QtHost.h"
# include "QtUtils.h"
# include "Settings/ControllerSettingsDialog.h"
# include "Settings/GameListSettingsWidget.h"
# include "Settings/InterfaceSettingsWidget.h"
2022-03-04 09:46:47 +00:00
# include "SettingWidgetBinder.h"
2022-04-04 00:06:59 +00:00
# include "svnrev.h"
2022-04-04 21:35:08 +00:00
# include "Tools/InputRecording/NewInputRecordingDlg.h"
2022-04-04 00:06:59 +00:00
2021-12-13 12:12:54 +00:00
2022-02-15 14:29:18 +00:00
static constexpr char DISC_IMAGE_FILTER [ ] =
2022-05-23 16:19:14 +00:00
QT_TRANSLATE_NOOP ( " MainWindow " , " All File Types (*.bin *.iso *.cue *.chd *.cso *.gz *.elf *.irx *.m3u *.gs *.gs.xz *.gs.zst *.dump);; "
2022-03-12 13:20:23 +00:00
" Single-Track Raw Images (*.bin *.iso);; "
" Cue Sheets (*.cue);; "
" MAME CHD Images (*.chd);; "
" CSO Images (*.cso);; "
2022-04-08 02:00:08 +00:00
" GZ Images (*.gz);; "
2022-03-12 13:20:23 +00:00
" ELF Executables (*.elf);; "
" IRX Executables (*.irx);; "
" Playlists (*.m3u);; "
2022-05-23 16:19:14 +00:00
" GS Dumps (*.gs *.gs.xz *.gs.zst);; "
" Block Dumps (*.dump) " ) ;
2021-12-13 12:12:54 +00:00
2022-05-25 20:52:07 +00:00
# ifdef __APPLE__
const char * MainWindow : : DEFAULT_THEME_NAME = " " ;
# else
2021-12-13 12:12:54 +00:00
const char * MainWindow : : DEFAULT_THEME_NAME = " darkfusion " ;
2022-05-25 20:52:07 +00:00
# endif
2021-12-13 12:12:54 +00:00
MainWindow * g_main_window = nullptr ;
2022-06-28 12:53:26 +00:00
// UI thread VM validity.
static bool s_vm_valid = false ;
static bool s_vm_paused = false ;
2021-12-13 12:12:54 +00:00
MainWindow : : MainWindow ( const QString & unthemed_style_name )
: m_unthemed_style_name ( unthemed_style_name )
{
pxAssert ( ! g_main_window ) ;
g_main_window = this ;
}
MainWindow : : ~ MainWindow ( )
{
// we compare here, since recreate destroys the window later
if ( g_main_window = = this )
g_main_window = nullptr ;
2022-05-25 22:32:10 +00:00
# ifdef __APPLE__
CocoaTools : : RemoveThemeChangeHandler ( this ) ;
# endif
2021-12-13 12:12:54 +00:00
}
void MainWindow : : initialize ( )
{
2022-05-25 21:20:34 +00:00
setStyleFromSettings ( ) ;
setIconThemeFromStyle ( ) ;
2022-05-25 22:32:10 +00:00
# ifdef __APPLE__
CocoaTools : : AddThemeChangeHandler ( this , [ ] ( void * ctx ) {
// This handler is called *before* the style change has propagated far enough for Qt to see it
// Use RunOnUIThread to delay until it has
QtHost : : RunOnUIThread ( [ ctx = static_cast < MainWindow * > ( ctx ) ] {
ctx - > setStyleFromSettings ( ) ; // Qt won't notice the style change without us touching the palette in some way
ctx - > setIconThemeFromStyle ( ) ;
} ) ;
} ) ;
# endif
2021-12-13 12:12:54 +00:00
m_ui . setupUi ( this ) ;
setupAdditionalUi ( ) ;
connectSignals ( ) ;
2022-05-24 08:04:26 +00:00
connectVMThreadSignals ( g_emu_thread ) ;
2021-12-13 12:12:54 +00:00
restoreStateFromConfig ( ) ;
switchToGameListView ( ) ;
updateWindowTitle ( ) ;
updateSaveStateMenus ( QString ( ) , QString ( ) , 0 ) ;
}
2022-05-25 20:52:07 +00:00
// TODO: Figure out how to set this in the .ui file
/// Marks the icons for all actions in the given menu as mask icons
/// This means macOS's menubar renderer will ignore color values and use only the alpha in the image.
/// The color value will instead be taken from the system theme.
/// Since the menubar follows the OS's dark/light mode and not our current theme's, this prevents problems where a theme mismatch puts white icons in light mode or dark icons in dark mode.
static void makeIconsMasks ( QWidget * menu )
{
for ( QAction * action : menu - > actions ( ) )
{
if ( ! action - > icon ( ) . isNull ( ) )
{
QIcon icon = action - > icon ( ) ;
icon . setIsMask ( true ) ;
action - > setIcon ( icon ) ;
}
if ( action - > menu ( ) )
makeIconsMasks ( action - > menu ( ) ) ;
}
}
2021-12-13 12:12:54 +00:00
void MainWindow : : setupAdditionalUi ( )
{
2022-06-04 05:53:31 +00:00
setWindowIcon ( QIcon ( QStringLiteral ( " %1/icons/AppIconLarge.png " ) . arg ( QtHost : : GetResourcesBasePath ( ) ) ) ) ;
2022-05-25 20:52:07 +00:00
makeIconsMasks ( menuBar ( ) ) ;
2022-05-24 12:37:44 +00:00
const bool toolbar_visible = Host : : GetBaseBoolSettingValue ( " UI " , " ShowToolbar " , false ) ;
2021-12-13 12:12:54 +00:00
m_ui . actionViewToolbar - > setChecked ( toolbar_visible ) ;
m_ui . toolBar - > setVisible ( toolbar_visible ) ;
2022-05-24 12:37:44 +00:00
const bool toolbars_locked = Host : : GetBaseBoolSettingValue ( " UI " , " LockToolbar " , false ) ;
2021-12-13 12:12:54 +00:00
m_ui . actionViewLockToolbar - > setChecked ( toolbars_locked ) ;
m_ui . toolBar - > setMovable ( ! toolbars_locked ) ;
m_ui . toolBar - > setContextMenuPolicy ( Qt : : PreventContextMenu ) ;
2022-05-24 12:37:44 +00:00
const bool status_bar_visible = Host : : GetBaseBoolSettingValue ( " UI " , " ShowStatusBar " , true ) ;
2021-12-13 12:12:54 +00:00
m_ui . actionViewStatusBar - > setChecked ( status_bar_visible ) ;
m_ui . statusBar - > setVisible ( status_bar_visible ) ;
2022-05-06 11:03:16 +00:00
m_game_list_widget = new GameListWidget ( this ) ;
2021-12-13 12:12:54 +00:00
m_game_list_widget - > initialize ( ) ;
m_ui . actionGridViewShowTitles - > setChecked ( m_game_list_widget - > getShowGridCoverTitles ( ) ) ;
2022-05-06 11:03:16 +00:00
setCentralWidget ( m_game_list_widget ) ;
2021-12-13 12:12:54 +00:00
m_status_progress_widget = new QProgressBar ( m_ui . statusBar ) ;
m_status_progress_widget - > setSizePolicy ( QSizePolicy : : Preferred , QSizePolicy : : Fixed ) ;
m_status_progress_widget - > setFixedSize ( 140 , 16 ) ;
m_status_progress_widget - > hide ( ) ;
2022-04-03 13:46:05 +00:00
m_status_gs_widget = new QLabel ( m_ui . statusBar ) ;
m_status_gs_widget - > setSizePolicy ( QSizePolicy : : Expanding , QSizePolicy : : Fixed ) ;
m_status_gs_widget - > setFixedHeight ( 16 ) ;
m_status_gs_widget - > hide ( ) ;
m_status_fps_widget = new QLabel ( m_ui . statusBar ) ;
m_status_fps_widget - > setAlignment ( Qt : : AlignRight ) ;
m_status_fps_widget - > setFixedHeight ( 16 ) ;
m_status_fps_widget - > hide ( ) ;
2021-12-13 12:12:54 +00:00
for ( u32 scale = 0 ; scale < = 10 ; scale + + )
{
2022-02-15 14:29:18 +00:00
QAction * action = m_ui . menuWindowSize - > addAction ( ( scale = = 0 ) ? tr ( " Internal Resolution " ) : tr ( " %1x Scale " ) . arg ( scale ) ) ;
2021-12-13 12:12:54 +00:00
connect ( action , & QAction : : triggered , [ scale ] ( ) { g_emu_thread - > requestDisplaySize ( static_cast < float > ( scale ) ) ; } ) ;
}
updateEmulationActions ( false , false ) ;
}
void MainWindow : : connectSignals ( )
{
connect ( m_ui . actionStartFile , & QAction : : triggered , this , & MainWindow : : onStartFileActionTriggered ) ;
connect ( m_ui . actionStartBios , & QAction : : triggered , this , & MainWindow : : onStartBIOSActionTriggered ) ;
connect ( m_ui . actionChangeDisc , & QAction : : triggered , [ this ] { m_ui . menuChangeDisc - > exec ( QCursor : : pos ( ) ) ; } ) ;
connect ( m_ui . actionChangeDiscFromFile , & QAction : : triggered , this , & MainWindow : : onChangeDiscFromFileActionTriggered ) ;
2022-02-15 14:29:18 +00:00
connect ( m_ui . actionChangeDiscFromDevice , & QAction : : triggered , this , & MainWindow : : onChangeDiscFromDeviceActionTriggered ) ;
connect ( m_ui . actionChangeDiscFromGameList , & QAction : : triggered , this , & MainWindow : : onChangeDiscFromGameListActionTriggered ) ;
2021-12-13 12:12:54 +00:00
connect ( m_ui . menuChangeDisc , & QMenu : : aboutToShow , this , & MainWindow : : onChangeDiscMenuAboutToShow ) ;
connect ( m_ui . menuChangeDisc , & QMenu : : aboutToHide , this , & MainWindow : : onChangeDiscMenuAboutToHide ) ;
2022-05-07 12:56:44 +00:00
connect ( m_ui . actionPowerOff , & QAction : : triggered , this , [ this ] ( ) { requestShutdown ( true , true ) ; } ) ;
connect ( m_ui . actionPowerOffWithoutSaving , & QAction : : triggered , this , [ this ] ( ) { requestShutdown ( false , false ) ; } ) ;
2021-12-13 12:12:54 +00:00
connect ( m_ui . actionLoadState , & QAction : : triggered , this , [ this ] ( ) { m_ui . menuLoadState - > exec ( QCursor : : pos ( ) ) ; } ) ;
connect ( m_ui . actionSaveState , & QAction : : triggered , this , [ this ] ( ) { m_ui . menuSaveState - > exec ( QCursor : : pos ( ) ) ; } ) ;
connect ( m_ui . actionExit , & QAction : : triggered , this , & MainWindow : : close ) ;
2022-05-12 21:53:40 +00:00
connect ( m_ui . actionScreenshot , & QAction : : triggered , this , & MainWindow : : onScreenshotActionTriggered ) ;
2021-12-13 12:12:54 +00:00
connect ( m_ui . menuLoadState , & QMenu : : aboutToShow , this , & MainWindow : : onLoadStateMenuAboutToShow ) ;
connect ( m_ui . menuSaveState , & QMenu : : aboutToShow , this , & MainWindow : : onSaveStateMenuAboutToShow ) ;
2022-02-15 14:29:18 +00:00
connect ( m_ui . actionSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( ) ; } ) ;
connect ( m_ui . actionInterfaceSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " Interface " ) ; } ) ;
connect ( m_ui . actionGameListSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " Game List " ) ; } ) ;
connect ( m_ui . actionEmulationSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " Emulation " ) ; } ) ;
connect ( m_ui . actionBIOSSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " BIOS " ) ; } ) ;
connect ( m_ui . actionSystemSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " System " ) ; } ) ;
connect ( m_ui . actionGraphicsSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " Graphics " ) ; } ) ;
connect ( m_ui . actionAudioSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " Audio " ) ; } ) ;
2022-03-25 09:24:41 +00:00
connect ( m_ui . actionMemoryCardSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " Memory Cards " ) ; } ) ;
2022-03-13 17:58:39 +00:00
connect ( m_ui . actionDEV9Settings , & QAction : : triggered , [ this ] ( ) { doSettings ( " Network & HDD " ) ; } ) ;
2022-06-18 10:20:24 +00:00
connect ( m_ui . actionFolderSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " Folders " ) ; } ) ;
2022-02-15 14:29:18 +00:00
connect (
m_ui . actionControllerSettings , & QAction : : triggered , [ this ] ( ) { doControllerSettings ( ControllerSettingsDialog : : Category : : GlobalSettings ) ; } ) ;
connect ( m_ui . actionHotkeySettings , & QAction : : triggered , [ this ] ( ) { doControllerSettings ( ControllerSettingsDialog : : Category : : HotkeySettings ) ; } ) ;
connect (
m_ui . actionAddGameDirectory , & QAction : : triggered , [ this ] ( ) { getSettingsDialog ( ) - > getGameListSettingsWidget ( ) - > addSearchDirectory ( this ) ; } ) ;
2021-12-13 12:12:54 +00:00
connect ( m_ui . actionScanForNewGames , & QAction : : triggered , [ this ] ( ) { refreshGameList ( false ) ; } ) ;
connect ( m_ui . actionRescanAllGames , & QAction : : triggered , [ this ] ( ) { refreshGameList ( true ) ; } ) ;
connect ( m_ui . actionViewToolbar , & QAction : : toggled , this , & MainWindow : : onViewToolbarActionToggled ) ;
connect ( m_ui . actionViewLockToolbar , & QAction : : toggled , this , & MainWindow : : onViewLockToolbarActionToggled ) ;
connect ( m_ui . actionViewStatusBar , & QAction : : toggled , this , & MainWindow : : onViewStatusBarActionToggled ) ;
connect ( m_ui . actionViewGameList , & QAction : : triggered , this , & MainWindow : : onViewGameListActionTriggered ) ;
connect ( m_ui . actionViewGameGrid , & QAction : : triggered , this , & MainWindow : : onViewGameGridActionTriggered ) ;
connect ( m_ui . actionViewSystemDisplay , & QAction : : triggered , this , & MainWindow : : onViewSystemDisplayTriggered ) ;
connect ( m_ui . actionViewGameProperties , & QAction : : triggered , this , & MainWindow : : onViewGamePropertiesActionTriggered ) ;
connect ( m_ui . actionGitHubRepository , & QAction : : triggered , this , & MainWindow : : onGitHubRepositoryActionTriggered ) ;
connect ( m_ui . actionSupportForums , & QAction : : triggered , this , & MainWindow : : onSupportForumsActionTriggered ) ;
connect ( m_ui . actionDiscordServer , & QAction : : triggered , this , & MainWindow : : onDiscordServerActionTriggered ) ;
connect ( m_ui . actionAboutQt , & QAction : : triggered , qApp , & QApplication : : aboutQt ) ;
connect ( m_ui . actionAbout , & QAction : : triggered , this , & MainWindow : : onAboutActionTriggered ) ;
connect ( m_ui . actionCheckForUpdates , & QAction : : triggered , this , & MainWindow : : onCheckForUpdatesActionTriggered ) ;
connect ( m_ui . actionOpenDataDirectory , & QAction : : triggered , this , & MainWindow : : onToolsOpenDataDirectoryTriggered ) ;
connect ( m_ui . actionGridViewShowTitles , & QAction : : triggered , m_game_list_widget , & GameListWidget : : setShowCoverTitles ) ;
connect ( m_ui . actionGridViewZoomIn , & QAction : : triggered , m_game_list_widget , [ this ] ( ) {
if ( isShowingGameList ( ) )
m_game_list_widget - > gridZoomIn ( ) ;
} ) ;
connect ( m_ui . actionGridViewZoomOut , & QAction : : triggered , m_game_list_widget , [ this ] ( ) {
if ( isShowingGameList ( ) )
m_game_list_widget - > gridZoomOut ( ) ;
} ) ;
2022-02-15 14:29:18 +00:00
connect ( m_ui . actionGridViewRefreshCovers , & QAction : : triggered , m_game_list_widget , & GameListWidget : : refreshGridCovers ) ;
2021-12-13 12:12:54 +00:00
2022-04-03 13:46:05 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionViewStatusBarVerbose , " UI " , " VerboseStatusBar " , false ) ;
2022-03-04 09:46:47 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionEnableSystemConsole , " Logging " , " EnableSystemConsole " , false ) ;
connect ( m_ui . actionEnableSystemConsole , & QAction : : triggered , this , & MainWindow : : onLoggingOptionChanged ) ;
# ifndef PCSX2_DEVBUILD
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionEnableVerboseLogging , " Logging " , " EnableVerbose " , false ) ;
connect ( m_ui . actionEnableVerboseLogging , & QAction : : triggered , this , & MainWindow : : onLoggingOptionChanged ) ;
# else
// Dev builds always have verbose logging.
m_ui . actionEnableVerboseLogging - > setChecked ( true ) ;
m_ui . actionEnableVerboseLogging - > setEnabled ( false ) ;
# endif
2022-03-19 14:05:46 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionEnableEEConsoleLogging , " Logging " , " EnableEEConsole " , true ) ;
2022-03-04 09:46:47 +00:00
connect ( m_ui . actionEnableEEConsoleLogging , & QAction : : triggered , this , & MainWindow : : onLoggingOptionChanged ) ;
2022-03-19 14:05:46 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionEnableIOPConsoleLogging , " Logging " , " EnableIOPConsole " , true ) ;
2022-03-04 09:46:47 +00:00
connect ( m_ui . actionEnableIOPConsoleLogging , & QAction : : triggered , this , & MainWindow : : onLoggingOptionChanged ) ;
2022-05-24 12:02:05 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionEnableFileLogging , " Logging " , " EnableFileLogging " , false ) ;
connect ( m_ui . actionEnableFileLogging , & QAction : : triggered , this , & MainWindow : : onLoggingOptionChanged ) ;
2022-05-24 14:15:41 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionEnableLogTimestamps , " Logging " , " EnableTimestamps " , true ) ;
connect ( m_ui . actionEnableLogTimestamps , & QAction : : triggered , this , & MainWindow : : onLoggingOptionChanged ) ;
2022-05-24 12:02:05 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionEnableCDVDVerboseReads , " EmuCore " , " CdvdVerboseReads " , false ) ;
2022-05-24 12:22:15 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionSaveBlockDump , " EmuCore " , " CdvdDumpBlocks " , false ) ;
connect ( m_ui . actionSaveBlockDump , & QAction : : toggled , this , & MainWindow : : onBlockDumpActionToggled ) ;
2022-03-04 09:46:47 +00:00
2022-05-16 11:10:34 +00:00
connect ( m_ui . actionSaveGSDump , & QAction : : triggered , this , & MainWindow : : onSaveGSDumpActionTriggered ) ;
2022-04-04 00:06:59 +00:00
// Input Recording
connect ( m_ui . actionInputRecNew , & QAction : : triggered , this , & MainWindow : : onInputRecNewActionTriggered ) ;
connect ( m_ui . actionInputRecPlay , & QAction : : triggered , this , & MainWindow : : onInputRecPlayActionTriggered ) ;
connect ( m_ui . actionInputRecStop , & QAction : : triggered , this , & MainWindow : : onInputRecStopActionTriggered ) ;
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionInputRecConsoleLogs , " Logging " , " EnableInputRecordingLogs " , false ) ;
connect ( m_ui . actionInputRecConsoleLogs , & QAction : : triggered , this , & MainWindow : : onLoggingOptionChanged ) ;
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionInputRecControllerLogs , " Logging " , " EnableControllerLogs " , false ) ;
connect ( m_ui . actionInputRecControllerLogs , & QAction : : triggered , this , & MainWindow : : onLoggingOptionChanged ) ;
2021-12-13 12:12:54 +00:00
// These need to be queued connections to stop crashing due to menus opening/closing and switching focus.
connect ( m_game_list_widget , & GameListWidget : : refreshProgress , this , & MainWindow : : onGameListRefreshProgress ) ;
connect ( m_game_list_widget , & GameListWidget : : refreshComplete , this , & MainWindow : : onGameListRefreshComplete ) ;
2022-02-15 14:29:18 +00:00
connect ( m_game_list_widget , & GameListWidget : : selectionChanged , this , & MainWindow : : onGameListSelectionChanged , Qt : : QueuedConnection ) ;
connect ( m_game_list_widget , & GameListWidget : : entryActivated , this , & MainWindow : : onGameListEntryActivated , Qt : : QueuedConnection ) ;
connect (
m_game_list_widget , & GameListWidget : : entryContextMenuRequested , this , & MainWindow : : onGameListEntryContextMenuRequested , Qt : : QueuedConnection ) ;
2022-05-07 06:32:44 +00:00
connect ( m_game_list_widget , & GameListWidget : : addGameDirectoryRequested , this ,
[ this ] ( ) { getSettingsDialog ( ) - > getGameListSettingsWidget ( ) - > addSearchDirectory ( this ) ; } ) ;
2021-12-13 12:12:54 +00:00
}
void MainWindow : : connectVMThreadSignals ( EmuThread * thread )
{
connect ( thread , & EmuThread : : onCreateDisplayRequested , this , & MainWindow : : createDisplay , Qt : : BlockingQueuedConnection ) ;
connect ( thread , & EmuThread : : onUpdateDisplayRequested , this , & MainWindow : : updateDisplay , Qt : : BlockingQueuedConnection ) ;
2022-02-15 14:29:18 +00:00
connect ( thread , & EmuThread : : onDestroyDisplayRequested , this , & MainWindow : : destroyDisplay , Qt : : BlockingQueuedConnection ) ;
2021-12-13 12:12:54 +00:00
connect ( thread , & EmuThread : : onResizeDisplayRequested , this , & MainWindow : : displayResizeRequested ) ;
connect ( thread , & EmuThread : : onVMStarting , this , & MainWindow : : onVMStarting ) ;
connect ( thread , & EmuThread : : onVMStarted , this , & MainWindow : : onVMStarted ) ;
connect ( thread , & EmuThread : : onVMPaused , this , & MainWindow : : onVMPaused ) ;
connect ( thread , & EmuThread : : onVMResumed , this , & MainWindow : : onVMResumed ) ;
connect ( thread , & EmuThread : : onVMStopped , this , & MainWindow : : onVMStopped ) ;
connect ( thread , & EmuThread : : onGameChanged , this , & MainWindow : : onGameChanged ) ;
2022-04-03 13:46:05 +00:00
connect ( thread , & EmuThread : : onPerformanceMetricsUpdated , this , & MainWindow : : onPerformanceMetricsUpdated ) ;
2021-12-13 12:12:54 +00:00
connect ( m_ui . actionReset , & QAction : : triggered , thread , & EmuThread : : resetVM ) ;
connect ( m_ui . actionPause , & QAction : : toggled , thread , & EmuThread : : setVMPaused ) ;
connect ( m_ui . actionFullscreen , & QAction : : triggered , thread , & EmuThread : : toggleFullscreen ) ;
connect ( m_ui . actionToggleSoftwareRendering , & QAction : : triggered , thread , & EmuThread : : toggleSoftwareRendering ) ;
connect ( m_ui . actionReloadPatches , & QAction : : triggered , thread , & EmuThread : : reloadPatches ) ;
static constexpr GSRendererType renderers [ ] = {
# ifdef _WIN32
2022-03-19 12:19:16 +00:00
GSRendererType : : DX11 , GSRendererType : : DX12 ,
2021-12-13 12:12:54 +00:00
# endif
GSRendererType : : OGL , GSRendererType : : VK , GSRendererType : : SW , GSRendererType : : Null } ;
for ( GSRendererType renderer : renderers )
{
2022-02-15 14:29:18 +00:00
connect ( m_ui . menuDebugSwitchRenderer - > addAction ( QString : : fromUtf8 ( Pcsx2Config : : GSOptions : : GetRendererName ( renderer ) ) ) , & QAction : : triggered ,
[ renderer ] { g_emu_thread - > switchRenderer ( renderer ) ; } ) ;
2021-12-13 12:12:54 +00:00
}
}
void MainWindow : : recreate ( )
{
2022-06-28 12:53:26 +00:00
if ( s_vm_valid )
2022-03-25 10:14:23 +00:00
requestShutdown ( false , true , true ) ;
2021-12-13 12:12:54 +00:00
close ( ) ;
g_main_window = nullptr ;
MainWindow * new_main_window = new MainWindow ( m_unthemed_style_name ) ;
new_main_window - > initialize ( ) ;
new_main_window - > refreshGameList ( false ) ;
new_main_window - > show ( ) ;
deleteLater ( ) ;
}
void MainWindow : : setStyleFromSettings ( )
{
2022-05-24 12:37:44 +00:00
const std : : string theme ( Host : : GetBaseStringSettingValue ( " UI " , " Theme " , DEFAULT_THEME_NAME ) ) ;
2021-12-13 12:12:54 +00:00
if ( theme = = " fusion " )
{
qApp - > setPalette ( QApplication : : style ( ) - > standardPalette ( ) ) ;
qApp - > setStyleSheet ( QString ( ) ) ;
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
}
2022-02-16 15:37:48 +00:00
else if ( theme = = " UntouchedLagoon " )
{
// Custom pallete by RedDevilus, Tame (Light/Washed out) Green as main color and Grayish Blue as complimentary.
// Alternative white theme.
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
const QColor black ( 25 , 25 , 25 ) ;
2022-03-04 10:40:03 +00:00
const QColor teal ( 0 , 128 , 128 ) ;
2022-02-16 15:37:48 +00:00
const QColor tameTeal ( 160 , 190 , 185 ) ;
const QColor grayBlue ( 160 , 180 , 190 ) ;
QPalette standardPalette ;
standardPalette . setColor ( QPalette : : Window , tameTeal ) ;
standardPalette . setColor ( QPalette : : WindowText , black ) ;
standardPalette . setColor ( QPalette : : Base , grayBlue ) ;
standardPalette . setColor ( QPalette : : AlternateBase , tameTeal ) ;
standardPalette . setColor ( QPalette : : ToolTipBase , tameTeal ) ;
standardPalette . setColor ( QPalette : : ToolTipText , grayBlue ) ;
standardPalette . setColor ( QPalette : : Text , black ) ;
standardPalette . setColor ( QPalette : : Button , tameTeal ) ;
standardPalette . setColor ( QPalette : : ButtonText , Qt : : white ) ;
2022-05-23 15:11:29 +00:00
standardPalette . setColor ( QPalette : : Link , black ) ;
2022-02-16 15:37:48 +00:00
standardPalette . setColor ( QPalette : : Highlight , teal ) ;
standardPalette . setColor ( QPalette : : HighlightedText , Qt : : white ) ;
standardPalette . setColor ( QPalette : : Active , QPalette : : Button , tameTeal . darker ( ) ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , Qt : : white ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , Qt : : white ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : Text , Qt : : white ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : Light , tameTeal ) ;
qApp - > setPalette ( standardPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
else if ( theme = = " BabyPastel " )
{
// Custom pallete by RedDevilus, Blue as main color and blue as complimentary.
// Alternative light theme.
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
const QColor gray ( 150 , 150 , 150 ) ;
const QColor black ( 25 , 25 , 25 ) ;
const QColor pink ( 255 , 174 , 201 ) ;
const QColor brightPink ( 255 , 230 , 255 ) ;
const QColor congoPink ( 255 , 127 , 121 ) ;
const QColor blue ( 221 , 225 , 239 ) ;
QPalette standardPalette ;
standardPalette . setColor ( QPalette : : Window , pink ) ;
standardPalette . setColor ( QPalette : : WindowText , black ) ;
standardPalette . setColor ( QPalette : : Base , brightPink ) ;
standardPalette . setColor ( QPalette : : AlternateBase , blue ) ;
standardPalette . setColor ( QPalette : : ToolTipBase , pink ) ;
standardPalette . setColor ( QPalette : : ToolTipText , brightPink ) ;
standardPalette . setColor ( QPalette : : Text , black ) ;
standardPalette . setColor ( QPalette : : Button , pink ) ;
standardPalette . setColor ( QPalette : : ButtonText , black ) ;
2022-05-23 15:11:29 +00:00
standardPalette . setColor ( QPalette : : Link , black ) ;
2022-02-16 15:37:48 +00:00
standardPalette . setColor ( QPalette : : Highlight , congoPink ) ;
standardPalette . setColor ( QPalette : : HighlightedText , black ) ;
standardPalette . setColor ( QPalette : : Active , QPalette : : Button , pink ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , gray ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , congoPink ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : Text , blue ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : Light , gray ) ;
qApp - > setPalette ( standardPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
else if ( theme = = " PCSX2Blue " )
{
// Custom pallete by RedDevilus, White as main color and Blue as complimentary.
// Alternative light theme.
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
const QColor black ( 25 , 25 , 25 ) ;
const QColor darkBlue ( 73 , 97 , 177 ) ;
const QColor blue ( 106 , 156 , 255 ) ;
const QColor lightBlue ( 130 , 155 , 241 ) ;
QPalette standardPalette ;
standardPalette . setColor ( QPalette : : Window , lightBlue ) ;
standardPalette . setColor ( QPalette : : WindowText , black ) ;
standardPalette . setColor ( QPalette : : Base , darkBlue ) ;
standardPalette . setColor ( QPalette : : AlternateBase , lightBlue ) ;
standardPalette . setColor ( QPalette : : ToolTipBase , lightBlue ) ;
standardPalette . setColor ( QPalette : : ToolTipText , Qt : : white ) ;
standardPalette . setColor ( QPalette : : Text , Qt : : white ) ;
standardPalette . setColor ( QPalette : : Button , blue ) ;
standardPalette . setColor ( QPalette : : ButtonText , Qt : : white ) ;
2022-05-23 15:11:29 +00:00
standardPalette . setColor ( QPalette : : Link , darkBlue ) ;
2022-02-16 15:37:48 +00:00
standardPalette . setColor ( QPalette : : Highlight , Qt : : white ) ;
standardPalette . setColor ( QPalette : : HighlightedText , black ) ;
standardPalette . setColor ( QPalette : : Active , QPalette : : Button , blue . darker ( ) ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , darkBlue ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , darkBlue ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : Text , black ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : Light , darkBlue ) ;
qApp - > setPalette ( standardPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
2021-12-13 12:12:54 +00:00
else if ( theme = = " darkfusion " )
{
// adapted from https://gist.github.com/QuantumCD/6245215
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
const QColor lighterGray ( 75 , 75 , 75 ) ;
const QColor darkGray ( 53 , 53 , 53 ) ;
const QColor gray ( 128 , 128 , 128 ) ;
const QColor black ( 25 , 25 , 25 ) ;
const QColor blue ( 198 , 238 , 255 ) ;
QPalette darkPalette ;
darkPalette . setColor ( QPalette : : Window , darkGray ) ;
darkPalette . setColor ( QPalette : : WindowText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Base , black ) ;
darkPalette . setColor ( QPalette : : AlternateBase , darkGray ) ;
darkPalette . setColor ( QPalette : : ToolTipBase , darkGray ) ;
darkPalette . setColor ( QPalette : : ToolTipText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Text , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Button , darkGray ) ;
darkPalette . setColor ( QPalette : : ButtonText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Link , blue ) ;
darkPalette . setColor ( QPalette : : Highlight , lighterGray ) ;
darkPalette . setColor ( QPalette : : HighlightedText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Active , QPalette : : Button , gray . darker ( ) ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Text , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Light , darkGray ) ;
qApp - > setPalette ( darkPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
else if ( theme = = " darkfusionblue " )
{
// adapted from https://gist.github.com/QuantumCD/6245215
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
const QColor darkGray ( 53 , 53 , 53 ) ;
const QColor gray ( 128 , 128 , 128 ) ;
const QColor black ( 25 , 25 , 25 ) ;
const QColor blue ( 198 , 238 , 255 ) ;
const QColor blue2 ( 0 , 88 , 208 ) ;
QPalette darkPalette ;
darkPalette . setColor ( QPalette : : Window , darkGray ) ;
darkPalette . setColor ( QPalette : : WindowText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Base , black ) ;
darkPalette . setColor ( QPalette : : AlternateBase , darkGray ) ;
darkPalette . setColor ( QPalette : : ToolTipBase , blue2 ) ;
darkPalette . setColor ( QPalette : : ToolTipText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Text , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Button , darkGray ) ;
darkPalette . setColor ( QPalette : : ButtonText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Link , blue ) ;
darkPalette . setColor ( QPalette : : Highlight , blue2 ) ;
darkPalette . setColor ( QPalette : : HighlightedText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Active , QPalette : : Button , gray . darker ( ) ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Text , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Light , darkGray ) ;
qApp - > setPalette ( darkPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
2022-02-16 15:37:48 +00:00
else if ( theme = = " ScarletDevilRed " )
{
// Custom pallete by RedDevilus, Red as main color and Purple as complimentary.
// Alternative dark theme.
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
const QColor darkRed ( 80 , 45 , 69 ) ;
const QColor purplishRed ( 120 , 45 , 69 ) ;
const QColor brightRed ( 200 , 45 , 69 ) ;
QPalette darkPalette ;
darkPalette . setColor ( QPalette : : Window , darkRed ) ;
darkPalette . setColor ( QPalette : : WindowText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Base , purplishRed ) ;
darkPalette . setColor ( QPalette : : AlternateBase , darkRed ) ;
darkPalette . setColor ( QPalette : : ToolTipBase , darkRed ) ;
darkPalette . setColor ( QPalette : : ToolTipText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Text , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Button , darkRed ) ;
darkPalette . setColor ( QPalette : : ButtonText , Qt : : white ) ;
2022-05-23 15:11:29 +00:00
darkPalette . setColor ( QPalette : : Link , brightRed ) ;
2022-02-16 15:37:48 +00:00
darkPalette . setColor ( QPalette : : Highlight , brightRed ) ;
darkPalette . setColor ( QPalette : : HighlightedText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Active , QPalette : : Button , purplishRed . darker ( ) ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , brightRed ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , brightRed ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Text , brightRed ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Light , darkRed ) ;
qApp - > setPalette ( darkPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
2021-12-13 12:12:54 +00:00
else
{
qApp - > setPalette ( QApplication : : style ( ) - > standardPalette ( ) ) ;
qApp - > setStyleSheet ( QString ( ) ) ;
qApp - > setStyle ( m_unthemed_style_name ) ;
}
}
2022-05-25 21:20:34 +00:00
void MainWindow : : setIconThemeFromStyle ( )
2021-12-13 12:12:54 +00:00
{
2022-05-25 21:20:34 +00:00
QPalette palette = qApp - > palette ( ) ;
bool dark = palette . windowText ( ) . color ( ) . value ( ) > palette . window ( ) . color ( ) . value ( ) ;
QIcon : : setThemeName ( dark ? QStringLiteral ( " white " ) : QStringLiteral ( " black " ) ) ;
2021-12-13 12:12:54 +00:00
}
2022-05-12 21:53:40 +00:00
void MainWindow : : onScreenshotActionTriggered ( )
{
2022-05-16 11:10:34 +00:00
g_emu_thread - > queueSnapshot ( 0 ) ;
}
void MainWindow : : onSaveGSDumpActionTriggered ( )
{
g_emu_thread - > queueSnapshot ( 1 ) ;
2022-05-12 21:53:40 +00:00
}
2022-05-24 12:22:15 +00:00
void MainWindow : : onBlockDumpActionToggled ( bool checked )
{
if ( ! checked )
return ;
std : : string old_directory ( Host : : GetBaseStringSettingValue ( " EmuCore " , " BlockDumpSaveDirectory " , " " ) ) ;
if ( old_directory . empty ( ) )
old_directory = FileSystem : : GetWorkingDirectory ( ) ;
// prompt for a location to save
const QString new_dir (
QFileDialog : : getExistingDirectory ( this , tr ( " Select location to save block dump: " ) ,
QString : : fromStdString ( old_directory ) ) ) ;
if ( new_dir . isEmpty ( ) )
{
// disable it again
m_ui . actionSaveBlockDump - > setChecked ( false ) ;
return ;
}
QtHost : : SetBaseStringSettingValue ( " EmuCore " , " BlockDumpSaveDirectory " , new_dir . toUtf8 ( ) . constData ( ) ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : saveStateToConfig ( )
{
{
const QByteArray geometry = saveGeometry ( ) ;
const QByteArray geometry_b64 = geometry . toBase64 ( ) ;
2022-05-24 12:37:44 +00:00
const std : : string old_geometry_b64 = Host : : GetBaseStringSettingValue ( " UI " , " MainWindowGeometry " ) ;
2021-12-13 12:12:54 +00:00
if ( old_geometry_b64 ! = geometry_b64 . constData ( ) )
QtHost : : SetBaseStringSettingValue ( " UI " , " MainWindowGeometry " , geometry_b64 . constData ( ) ) ;
}
{
const QByteArray state = saveState ( ) ;
const QByteArray state_b64 = state . toBase64 ( ) ;
2022-05-24 12:37:44 +00:00
const std : : string old_state_b64 = Host : : GetBaseStringSettingValue ( " UI " , " MainWindowState " ) ;
2021-12-13 12:12:54 +00:00
if ( old_state_b64 ! = state_b64 . constData ( ) )
QtHost : : SetBaseStringSettingValue ( " UI " , " MainWindowState " , state_b64 . constData ( ) ) ;
}
}
void MainWindow : : restoreStateFromConfig ( )
{
{
2022-05-24 12:37:44 +00:00
const std : : string geometry_b64 = Host : : GetBaseStringSettingValue ( " UI " , " MainWindowGeometry " ) ;
2021-12-13 12:12:54 +00:00
const QByteArray geometry = QByteArray : : fromBase64 ( QByteArray : : fromStdString ( geometry_b64 ) ) ;
if ( ! geometry . isEmpty ( ) )
restoreGeometry ( geometry ) ;
}
{
2022-05-24 12:37:44 +00:00
const std : : string state_b64 = Host : : GetBaseStringSettingValue ( " UI " , " MainWindowState " ) ;
2021-12-13 12:12:54 +00:00
const QByteArray state = QByteArray : : fromBase64 ( QByteArray : : fromStdString ( state_b64 ) ) ;
if ( ! state . isEmpty ( ) )
restoreState ( state ) ;
{
QSignalBlocker sb ( m_ui . actionViewToolbar ) ;
m_ui . actionViewToolbar - > setChecked ( ! m_ui . toolBar - > isHidden ( ) ) ;
}
{
QSignalBlocker sb ( m_ui . actionViewStatusBar ) ;
m_ui . actionViewStatusBar - > setChecked ( ! m_ui . statusBar - > isHidden ( ) ) ;
}
}
}
void MainWindow : : updateEmulationActions ( bool starting , bool running )
{
const bool starting_or_running = starting | | running ;
m_ui . actionStartFile - > setDisabled ( starting_or_running ) ;
m_ui . actionStartDisc - > setDisabled ( starting_or_running ) ;
m_ui . actionStartBios - > setDisabled ( starting_or_running ) ;
m_ui . actionPowerOff - > setEnabled ( running ) ;
2022-05-07 12:56:44 +00:00
m_ui . actionPowerOffWithoutSaving - > setEnabled ( running ) ;
2021-12-13 12:12:54 +00:00
m_ui . actionReset - > setEnabled ( running ) ;
m_ui . actionPause - > setEnabled ( running ) ;
m_ui . actionChangeDisc - > setEnabled ( running ) ;
m_ui . actionScreenshot - > setEnabled ( running ) ;
m_ui . actionViewSystemDisplay - > setEnabled ( starting_or_running ) ;
m_ui . menuChangeDisc - > setEnabled ( running ) ;
m_ui . actionSaveState - > setEnabled ( running ) ;
m_ui . menuSaveState - > setEnabled ( running ) ;
m_ui . menuWindowSize - > setEnabled ( starting_or_running ) ;
m_ui . actionFullscreen - > setEnabled ( starting_or_running ) ;
m_ui . actionViewGameProperties - > setEnabled ( running ) ;
m_game_list_widget - > setDisabled ( starting & & ! running ) ;
2022-05-06 11:03:16 +00:00
if ( ! starting & & ! running )
m_ui . actionPause - > setChecked ( false ) ;
2022-05-22 14:12:21 +00:00
// scanning needs to be disabled while running
m_ui . actionScanForNewGames - > setDisabled ( starting_or_running ) ;
m_ui . actionRescanAllGames - > setDisabled ( starting_or_running ) ;
2021-12-13 12:12:54 +00:00
}
2022-04-03 13:46:05 +00:00
void MainWindow : : updateStatusBarWidgetVisibility ( )
{
auto Update = [ this ] ( QWidget * widget , bool visible , int stretch )
{
if ( widget - > isVisible ( ) )
{
m_ui . statusBar - > removeWidget ( widget ) ;
widget - > hide ( ) ;
}
if ( visible )
{
m_ui . statusBar - > addPermanentWidget ( widget , stretch ) ;
widget - > show ( ) ;
}
} ;
2022-06-28 12:53:26 +00:00
Update ( m_status_gs_widget , s_vm_valid & & ! s_vm_paused , 1 ) ;
Update ( m_status_fps_widget , s_vm_valid , 0 ) ;
2022-04-03 13:46:05 +00:00
}
2021-12-13 12:12:54 +00:00
void MainWindow : : updateWindowTitle ( )
{
2022-05-11 08:24:56 +00:00
QString suffix ( QtHost : : GetAppConfigSuffix ( ) ) ;
QString main_title ( QtHost : : GetAppNameAndVersion ( ) + suffix ) ;
QString display_title ( m_current_game_name + suffix ) ;
2022-05-06 11:03:16 +00:00
2022-06-28 12:53:26 +00:00
if ( ! s_vm_valid | | m_current_game_name . isEmpty ( ) )
2022-05-06 11:03:16 +00:00
display_title = main_title ;
else if ( isRenderingToMain ( ) )
main_title = display_title ;
if ( windowTitle ( ) ! = main_title )
setWindowTitle ( main_title ) ;
if ( m_display_widget & & ! isRenderingToMain ( ) )
2021-12-13 12:12:54 +00:00
{
2022-05-06 11:03:16 +00:00
QWidget * container = m_display_container ? static_cast < QWidget * > ( m_display_container ) : static_cast < QWidget * > ( m_display_widget ) ;
if ( container - > windowTitle ( ) ! = display_title )
container - > setWindowTitle ( display_title ) ;
2021-12-13 12:12:54 +00:00
}
}
2022-06-28 12:16:45 +00:00
void MainWindow : : updateWindowState ( bool force_visible )
2022-06-28 11:37:30 +00:00
{
const bool hide_window = ! g_emu_thread - > isRenderingToMain ( ) & & Host : : GetBaseBoolSettingValue ( " UI " , " HideMainWindowWhenRunning " , false ) ;
2022-06-28 12:16:45 +00:00
const bool disable_resize = Host : : GetBaseBoolSettingValue ( " UI " , " DisableWindowResize " , false ) ;
2022-06-28 12:53:26 +00:00
const bool has_window = s_vm_valid | | m_display_widget ;
2022-06-28 12:16:45 +00:00
// Need to test both valid and display widget because of startup (vm invalid while window is created).
const bool visible = force_visible | | ! hide_window | | ! has_window ;
if ( isVisible ( ) ! = visible )
setVisible ( visible ) ;
2022-06-28 11:37:30 +00:00
2022-06-28 12:16:45 +00:00
// No point changing realizability if we're not visible.
const bool resizeable = force_visible | | ! disable_resize | | ! has_window ;
if ( visible )
QtUtils : : SetWindowResizeable ( this , resizeable ) ;
// Update the display widget too if rendering separately.
if ( m_display_widget & & ! isRenderingToMain ( ) )
QtUtils : : SetWindowResizeable ( getDisplayContainer ( ) , resizeable ) ;
2022-06-28 11:37:30 +00:00
}
2021-12-13 12:12:54 +00:00
void MainWindow : : setProgressBar ( int current , int total )
{
m_status_progress_widget - > setValue ( current ) ;
m_status_progress_widget - > setMaximum ( total ) ;
if ( m_status_progress_widget - > isVisible ( ) )
return ;
m_status_progress_widget - > show ( ) ;
m_ui . statusBar - > addPermanentWidget ( m_status_progress_widget ) ;
}
void MainWindow : : clearProgressBar ( )
{
if ( ! m_status_progress_widget - > isVisible ( ) )
return ;
m_status_progress_widget - > hide ( ) ;
m_ui . statusBar - > removeWidget ( m_status_progress_widget ) ;
}
2022-03-12 14:52:52 +00:00
bool MainWindow : : isShowingGameList ( ) const
{
2022-05-06 11:03:16 +00:00
return ( centralWidget ( ) = = m_game_list_widget ) ;
}
bool MainWindow : : isRenderingFullscreen ( ) const
{
HostDisplay * display = Host : : GetHostDisplay ( ) ;
if ( ! display | | ! m_display_widget )
return false ;
return ( m_display_widget - > parent ( ) ! = this & & ( m_display_widget - > isFullScreen ( ) | | display - > IsFullscreen ( ) ) ) ;
}
bool MainWindow : : isRenderingToMain ( ) const
{
return ( m_display_widget & & m_display_widget - > parent ( ) = = this ) ;
2022-03-12 14:52:52 +00:00
}
2021-12-13 12:12:54 +00:00
2022-05-15 08:15:27 +00:00
bool MainWindow : : shouldHideMouseCursor ( ) const
{
return isRenderingFullscreen ( ) & & Host : : GetBoolSettingValue ( " UI " , " HideMouseCursor " , false ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : switchToGameListView ( )
{
2022-05-06 11:03:16 +00:00
if ( centralWidget ( ) = = m_game_list_widget )
{
m_game_list_widget - > setFocus ( ) ;
2021-12-13 12:12:54 +00:00
return ;
2022-05-06 11:03:16 +00:00
}
2021-12-13 12:12:54 +00:00
2022-06-28 12:53:26 +00:00
if ( s_vm_valid )
2021-12-13 12:12:54 +00:00
{
2022-06-28 12:53:26 +00:00
m_was_paused_on_surface_loss = s_vm_paused ;
if ( ! s_vm_paused )
2021-12-13 12:12:54 +00:00
g_emu_thread - > setVMPaused ( true ) ;
2022-05-06 11:03:16 +00:00
// switch to surfaceless. we have to wait until the display widget is gone before we swap over.
g_emu_thread - > setSurfaceless ( true ) ;
while ( m_display_widget )
QApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents , 1 ) ;
2021-12-13 12:12:54 +00:00
}
2022-05-06 11:03:16 +00:00
pxAssertMsg ( ! centralWidget ( ) , " Should not have a central widget at game list switch time " ) ;
takeCentralWidget ( ) ;
setCentralWidget ( m_game_list_widget ) ;
m_game_list_widget - > setVisible ( true ) ;
2021-12-13 12:12:54 +00:00
m_game_list_widget - > setFocus ( ) ;
}
void MainWindow : : switchToEmulationView ( )
{
2022-06-28 12:53:26 +00:00
if ( ! s_vm_valid | | ( m_display_widget & & centralWidget ( ) = = m_display_widget ) )
2021-12-13 12:12:54 +00:00
return ;
2022-05-06 11:03:16 +00:00
// we're no longer surfaceless! this will call back to UpdateDisplay(), which will swap the widget out.
g_emu_thread - > setSurfaceless ( false ) ;
2021-12-13 12:12:54 +00:00
2022-05-06 11:03:16 +00:00
// resume if we weren't paused at switch time
2022-06-28 12:53:26 +00:00
if ( s_vm_paused & & ! m_was_paused_on_surface_loss )
2022-05-06 11:03:16 +00:00
g_emu_thread - > setVMPaused ( false ) ;
if ( m_display_widget )
m_display_widget - > setFocus ( ) ;
2021-12-13 12:12:54 +00:00
}
2022-03-12 14:52:52 +00:00
void MainWindow : : refreshGameList ( bool invalidate_cache )
{
2022-05-22 14:12:21 +00:00
// can't do this while the VM is running because of CDVD
2022-06-28 12:53:26 +00:00
if ( s_vm_valid )
2022-05-22 14:12:21 +00:00
return ;
2022-03-12 14:52:52 +00:00
m_game_list_widget - > refresh ( invalidate_cache ) ;
}
2021-12-13 12:12:54 +00:00
2022-05-15 08:16:24 +00:00
void MainWindow : : cancelGameListRefresh ( )
{
m_game_list_widget - > cancelRefresh ( ) ;
}
2022-03-12 14:52:52 +00:00
void MainWindow : : invalidateSaveStateCache ( )
{
m_save_states_invalidated = true ;
}
2021-12-13 12:12:54 +00:00
2022-03-12 14:52:52 +00:00
void MainWindow : : reportError ( const QString & title , const QString & message )
{
QMessageBox : : critical ( this , title , message ) ;
}
2021-12-13 12:12:54 +00:00
2022-04-06 10:49:00 +00:00
void MainWindow : : runOnUIThread ( const std : : function < void ( ) > & func )
{
func ( ) ;
}
2022-03-25 10:14:23 +00:00
bool MainWindow : : requestShutdown ( bool allow_confirm /* = true */ , bool allow_save_to_state /* = true */ , bool block_until_done /* = false */ )
2022-03-04 10:40:03 +00:00
{
2022-06-28 12:53:26 +00:00
if ( ! s_vm_valid )
2022-03-04 10:40:03 +00:00
return true ;
2022-06-20 08:03:38 +00:00
// If we don't have a crc, we can't save state.
2022-05-07 12:56:44 +00:00
allow_save_to_state & = ( m_current_game_crc ! = 0 ) ;
bool save_state = allow_save_to_state & & EmuConfig . SaveStateOnShutdown ;
2022-06-20 08:03:38 +00:00
// Only confirm on UI thread because we need to display a msgbox.
2022-05-24 12:37:44 +00:00
if ( allow_confirm & & ! GSDumpReplayer : : IsReplayingDump ( ) & & Host : : GetBaseBoolSettingValue ( " UI " , " ConfirmShutdown " , true ) )
2022-03-25 10:14:23 +00:00
{
2022-05-07 03:52:13 +00:00
VMLock lock ( pauseAndLockVM ( ) ) ;
2022-05-07 12:56:44 +00:00
QMessageBox msgbox ( lock . getDialogParent ( ) ) ;
msgbox . setIcon ( QMessageBox : : Question ) ;
msgbox . setWindowTitle ( tr ( " Confirm Shutdown " ) ) ;
msgbox . setText ( " Are you sure you want to shut down the virtual machine? " ) ;
QCheckBox * save_cb = new QCheckBox ( tr ( " Save State For Resume " ) , & msgbox ) ;
save_cb - > setChecked ( save_state ) ;
save_cb - > setEnabled ( allow_save_to_state ) ;
msgbox . setCheckBox ( save_cb ) ;
msgbox . addButton ( QMessageBox : : Yes ) ;
msgbox . addButton ( QMessageBox : : No ) ;
msgbox . setDefaultButton ( QMessageBox : : Yes ) ;
if ( msgbox . exec ( ) ! = QMessageBox : : Yes )
2022-03-25 10:14:23 +00:00
return false ;
2022-05-07 12:56:44 +00:00
save_state = save_cb - > isChecked ( ) ;
2022-06-20 08:03:38 +00:00
// Don't switch back to fullscreen when we're shutting down anyway.
lock . cancelResume ( ) ;
2022-03-25 10:14:23 +00:00
}
2022-06-28 11:37:30 +00:00
// This is a little bit annoying. Qt will close everything down if we don't have at least one window visible,
// but we might not be visible because the user is using render-to-separate and hide. We don't want to always
// reshow the main window during display updates, because otherwise fullscreen transitions and renderer switches
// would briefly show and then hide the main window. So instead, we do it on shutdown, here. Except if we're in
// batch mode, when we're going to exit anyway.
if ( ! isRenderingToMain ( ) & & isHidden ( ) & & ! QtHost : : InBatchMode ( ) )
2022-06-28 12:16:45 +00:00
updateWindowState ( true ) ;
2022-06-28 11:37:30 +00:00
// Now we can actually shut down the VM.
2022-05-07 12:56:44 +00:00
g_emu_thread - > shutdownVM ( save_state ) ;
2022-03-25 10:14:23 +00:00
2022-05-03 04:26:49 +00:00
if ( block_until_done | | QtHost : : InBatchMode ( ) )
2022-03-25 10:14:23 +00:00
{
2022-06-20 08:03:38 +00:00
// We need to yield here, since the display gets destroyed.
2022-05-04 04:01:22 +00:00
while ( VMManager : : GetState ( ) ! = VMState : : Shutdown )
2022-03-25 10:14:23 +00:00
QApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents , 1 ) ;
}
2022-03-04 10:40:03 +00:00
2022-05-03 04:26:49 +00:00
if ( QtHost : : InBatchMode ( ) )
{
2022-06-20 08:03:38 +00:00
// Closing the window should shut down everything.
2022-05-03 04:26:49 +00:00
close ( ) ;
}
2022-03-25 10:14:23 +00:00
return true ;
2022-03-04 10:40:03 +00:00
}
void MainWindow : : requestExit ( )
{
2022-03-25 10:14:23 +00:00
// this is block, because otherwise closeEvent() will also prompt
if ( ! requestShutdown ( true , true , true ) )
2022-03-04 10:40:03 +00:00
return ;
close ( ) ;
}
2022-05-15 08:15:27 +00:00
void MainWindow : : checkForSettingChanges ( )
{
if ( m_display_widget )
2022-06-28 12:53:26 +00:00
m_display_widget - > updateRelativeMode ( s_vm_valid & & ! s_vm_paused ) ;
2022-06-28 11:37:30 +00:00
2022-06-28 12:16:45 +00:00
updateWindowState ( ) ;
2022-05-15 08:15:27 +00:00
}
2022-03-12 14:52:52 +00:00
void Host : : InvalidateSaveStateCache ( )
{
QMetaObject : : invokeMethod ( g_main_window , & MainWindow : : invalidateSaveStateCache , Qt : : QueuedConnection ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : onGameListRefreshProgress ( const QString & status , int current , int total )
{
m_ui . statusBar - > showMessage ( status ) ;
setProgressBar ( current , total ) ;
}
2022-03-12 14:52:52 +00:00
void MainWindow : : onGameListRefreshComplete ( )
{
clearProgressBar ( ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : onGameListSelectionChanged ( )
{
auto lock = GameList : : GetLock ( ) ;
const GameList : : Entry * entry = m_game_list_widget - > getSelectedEntry ( ) ;
if ( ! entry )
return ;
m_ui . statusBar - > showMessage ( QString : : fromStdString ( entry - > path ) ) ;
}
void MainWindow : : onGameListEntryActivated ( )
{
auto lock = GameList : : GetLock ( ) ;
const GameList : : Entry * entry = m_game_list_widget - > getSelectedEntry ( ) ;
if ( ! entry )
return ;
2022-06-28 12:53:26 +00:00
if ( s_vm_valid )
2021-12-13 12:12:54 +00:00
{
// change disc on double click
2022-05-06 13:34:07 +00:00
doDiscChange ( QString : : fromStdString ( entry - > path ) ) ;
2021-12-13 12:12:54 +00:00
return ;
}
2022-05-08 06:45:38 +00:00
// we might still be saving a resume state...
VMManager : : WaitForSaveStateFlush ( ) ;
2022-05-07 12:56:44 +00:00
const std : : optional < bool > resume = promptForResumeState (
QString : : fromStdString ( VMManager : : GetSaveStateFileName ( entry - > serial . c_str ( ) , entry - > crc , - 1 ) ) ) ;
if ( ! resume . has_value ( ) )
{
// cancelled
return ;
}
2021-12-13 12:12:54 +00:00
// only resume if the option is enabled, and we have one for this game
2022-05-07 12:56:44 +00:00
startGameListEntry ( entry , resume . value ( ) ? std : : optional < s32 > ( - 1 ) : std : : optional < s32 > ( ) , std : : nullopt ) ;
2021-12-13 12:12:54 +00:00
}
void MainWindow : : onGameListEntryContextMenuRequested ( const QPoint & point )
{
auto lock = GameList : : GetLock ( ) ;
const GameList : : Entry * entry = m_game_list_widget - > getSelectedEntry ( ) ;
QMenu menu ;
if ( entry )
{
QAction * action = menu . addAction ( tr ( " Properties... " ) ) ;
2022-02-15 14:29:18 +00:00
action - > setEnabled ( ! entry - > serial . empty ( ) ) ;
if ( action - > isEnabled ( ) )
2022-05-23 14:35:21 +00:00
connect ( action , & QAction : : triggered , [ entry ] ( ) { SettingsDialog : : openGamePropertiesDialog ( entry , entry - > serial , entry - > crc ) ; } ) ;
2021-12-13 12:12:54 +00:00
action = menu . addAction ( tr ( " Open Containing Directory... " ) ) ;
connect ( action , & QAction : : triggered , [ this , entry ] ( ) {
const QFileInfo fi ( QString : : fromStdString ( entry - > path ) ) ;
QtUtils : : OpenURL ( this , QUrl : : fromLocalFile ( fi . absolutePath ( ) ) ) ;
} ) ;
action = menu . addAction ( tr ( " Set Cover Image... " ) ) ;
connect ( action , & QAction : : triggered , [ this , entry ] ( ) { setGameListEntryCoverImage ( entry ) ; } ) ;
connect ( menu . addAction ( tr ( " Exclude From List " ) ) , & QAction : : triggered ,
[ this , entry ] ( ) { getSettingsDialog ( ) - > getGameListSettingsWidget ( ) - > addExcludedPath ( entry - > path ) ; } ) ;
menu . addSeparator ( ) ;
2022-06-28 12:53:26 +00:00
if ( ! s_vm_valid )
2021-12-13 12:12:54 +00:00
{
action = menu . addAction ( tr ( " Default Boot " ) ) ;
connect ( action , & QAction : : triggered , [ this , entry ] ( ) { startGameListEntry ( entry ) ; } ) ;
// Make bold to indicate it's the default choice when double-clicking
2022-05-07 12:56:44 +00:00
if ( ! VMManager : : HasSaveStateInSlot ( entry - > serial . c_str ( ) , entry - > crc , - 1 ) )
2021-12-13 12:12:54 +00:00
QtUtils : : MarkActionAsDefault ( action ) ;
action = menu . addAction ( tr ( " Fast Boot " ) ) ;
connect ( action , & QAction : : triggered , [ this , entry ] ( ) { startGameListEntry ( entry , std : : nullopt , true ) ; } ) ;
action = menu . addAction ( tr ( " Full Boot " ) ) ;
connect ( action , & QAction : : triggered , [ this , entry ] ( ) { startGameListEntry ( entry , std : : nullopt , false ) ; } ) ;
if ( m_ui . menuDebug - > menuAction ( ) - > isVisible ( ) )
{
// TODO: Hook this up once it's implemented.
action = menu . addAction ( tr ( " Boot and Debug " ) ) ;
}
menu . addSeparator ( ) ;
2022-02-15 14:29:18 +00:00
populateLoadStateMenu ( & menu , QString : : fromStdString ( entry - > path ) , QString : : fromStdString ( entry - > serial ) , entry - > crc ) ;
2021-12-13 12:12:54 +00:00
}
else
{
action = menu . addAction ( tr ( " Change Disc " ) ) ;
connect ( action , & QAction : : triggered , [ this , entry ] ( ) {
g_emu_thread - > changeDisc ( QString : : fromStdString ( entry - > path ) ) ;
switchToEmulationView ( ) ;
} ) ;
QtUtils : : MarkActionAsDefault ( action ) ;
}
menu . addSeparator ( ) ;
}
connect ( menu . addAction ( tr ( " Add Search Directory... " ) ) , & QAction : : triggered ,
[ this ] ( ) { getSettingsDialog ( ) - > getGameListSettingsWidget ( ) - > addSearchDirectory ( this ) ; } ) ;
menu . exec ( point ) ;
}
void MainWindow : : onStartFileActionTriggered ( )
{
2022-05-26 07:49:40 +00:00
QString path =
2022-02-15 14:29:18 +00:00
QDir : : toNativeSeparators ( QFileDialog : : getOpenFileName ( this , tr ( " Select Disc Image " ) , QString ( ) , tr ( DISC_IMAGE_FILTER ) , nullptr ) ) ;
2022-05-26 07:49:40 +00:00
if ( path . isEmpty ( ) )
2021-12-13 12:12:54 +00:00
return ;
2022-05-26 07:49:40 +00:00
doStartDisc ( path ) ;
2021-12-13 12:12:54 +00:00
}
void MainWindow : : onStartBIOSActionTriggered ( )
{
std : : shared_ptr < VMBootParameters > params = std : : make_shared < VMBootParameters > ( ) ;
g_emu_thread - > startVM ( std : : move ( params ) ) ;
}
void MainWindow : : onChangeDiscFromFileActionTriggered ( )
{
2022-05-07 03:52:13 +00:00
VMLock lock ( pauseAndLockVM ( ) ) ;
QString filename = QFileDialog : : getOpenFileName ( lock . getDialogParent ( ) , tr ( " Select Disc Image " ) , QString ( ) , tr ( DISC_IMAGE_FILTER ) , nullptr ) ;
2021-12-13 12:12:54 +00:00
if ( filename . isEmpty ( ) )
return ;
g_emu_thread - > changeDisc ( filename ) ;
}
2022-03-12 14:52:52 +00:00
void MainWindow : : onChangeDiscFromGameListActionTriggered ( )
{
2022-05-06 13:34:07 +00:00
m_was_disc_change_request = true ;
2022-03-12 14:52:52 +00:00
switchToGameListView ( ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : onChangeDiscFromDeviceActionTriggered ( )
{
// TODO
}
void MainWindow : : onChangeDiscMenuAboutToShow ( )
{
// TODO: This is where we would populate the playlist if there is one.
}
2022-02-15 14:29:18 +00:00
void MainWindow : : onChangeDiscMenuAboutToHide ( ) { }
2021-12-13 12:12:54 +00:00
void MainWindow : : onLoadStateMenuAboutToShow ( )
{
if ( m_save_states_invalidated )
updateSaveStateMenus ( m_current_disc_path , m_current_game_serial , m_current_game_crc ) ;
}
void MainWindow : : onSaveStateMenuAboutToShow ( )
{
if ( m_save_states_invalidated )
updateSaveStateMenus ( m_current_disc_path , m_current_game_serial , m_current_game_crc ) ;
}
void MainWindow : : onViewToolbarActionToggled ( bool checked )
{
QtHost : : SetBaseBoolSettingValue ( " UI " , " ShowToolbar " , checked ) ;
m_ui . toolBar - > setVisible ( checked ) ;
}
void MainWindow : : onViewLockToolbarActionToggled ( bool checked )
{
QtHost : : SetBaseBoolSettingValue ( " UI " , " LockToolbar " , checked ) ;
m_ui . toolBar - > setMovable ( ! checked ) ;
}
void MainWindow : : onViewStatusBarActionToggled ( bool checked )
{
QtHost : : SetBaseBoolSettingValue ( " UI " , " ShowStatusBar " , checked ) ;
m_ui . statusBar - > setVisible ( checked ) ;
}
void MainWindow : : onViewGameListActionTriggered ( )
{
switchToGameListView ( ) ;
m_game_list_widget - > showGameList ( ) ;
}
void MainWindow : : onViewGameGridActionTriggered ( )
{
switchToGameListView ( ) ;
m_game_list_widget - > showGameGrid ( ) ;
}
void MainWindow : : onViewSystemDisplayTriggered ( )
{
2022-06-28 12:53:26 +00:00
if ( s_vm_valid )
2021-12-13 12:12:54 +00:00
switchToEmulationView ( ) ;
}
void MainWindow : : onViewGamePropertiesActionTriggered ( )
{
2022-06-28 12:53:26 +00:00
if ( ! s_vm_valid )
2021-12-13 12:12:54 +00:00
return ;
2022-02-15 14:29:18 +00:00
// prefer to use a game list entry, if we have one, that way the summary is populated
if ( ! m_current_disc_path . isEmpty ( ) )
{
auto lock = GameList : : GetLock ( ) ;
const GameList : : Entry * entry = GameList : : GetEntryForPath ( m_current_disc_path . toUtf8 ( ) . constData ( ) ) ;
if ( entry )
{
2022-05-23 14:35:21 +00:00
SettingsDialog : : openGamePropertiesDialog ( entry , entry - > serial , entry - > crc ) ;
2022-02-15 14:29:18 +00:00
return ;
}
}
2021-12-13 12:12:54 +00:00
2022-02-15 14:29:18 +00:00
// open properties for the current running file (isn't in the game list)
if ( m_current_game_crc ! = 0 )
2022-05-23 14:35:21 +00:00
SettingsDialog : : openGamePropertiesDialog ( nullptr , m_current_game_serial . toStdString ( ) , m_current_game_crc ) ;
2021-12-13 12:12:54 +00:00
}
2022-03-12 14:52:52 +00:00
void MainWindow : : onGitHubRepositoryActionTriggered ( )
{
QtUtils : : OpenURL ( this , AboutDialog : : getGitHubRepositoryUrl ( ) ) ;
}
2022-02-15 14:29:18 +00:00
2022-03-12 14:52:52 +00:00
void MainWindow : : onSupportForumsActionTriggered ( )
{
QtUtils : : OpenURL ( this , AboutDialog : : getSupportForumsUrl ( ) ) ;
}
2022-02-15 14:29:18 +00:00
2022-03-12 14:52:52 +00:00
void MainWindow : : onDiscordServerActionTriggered ( )
{
QtUtils : : OpenURL ( this , AboutDialog : : getDiscordServerUrl ( ) ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : onAboutActionTriggered ( )
{
AboutDialog about ( this ) ;
about . exec ( ) ;
}
2022-04-04 11:27:23 +00:00
void MainWindow : : onCheckForUpdatesActionTriggered ( )
{
// Wipe out the last version, that way it displays the update if we've previously skipped it.
QtHost : : RemoveBaseSettingValue ( " AutoUpdater " , " LastVersion " ) ;
checkForUpdates ( true ) ;
}
void MainWindow : : checkForUpdates ( bool display_message )
{
if ( ! AutoUpdaterDialog : : isSupported ( ) )
{
if ( display_message )
{
QMessageBox mbox ( this ) ;
mbox . setWindowTitle ( tr ( " Updater Error " ) ) ;
mbox . setTextFormat ( Qt : : RichText ) ;
QString message ;
# ifdef _WIN32
message =
tr ( " <p>Sorry, you are trying to update a PCSX2 version which is not an official GitHub release. To "
" prevent incompatibilities, the auto-updater is only enabled on official builds.</p> "
" <p>To obtain an official build, please download from the link below:</p> "
" <p><a href= \" https://pcsx2.net/downloads/ \" >https://pcsx2.net/downloads/</a></p> " ) ;
# else
message = tr ( " Automatic updating is not supported on the current platform. " ) ;
# endif
mbox . setText ( message ) ;
mbox . setIcon ( QMessageBox : : Critical ) ;
mbox . exec ( ) ;
}
return ;
}
if ( m_auto_updater_dialog )
return ;
m_auto_updater_dialog = new AutoUpdaterDialog ( this ) ;
connect ( m_auto_updater_dialog , & AutoUpdaterDialog : : updateCheckCompleted , this , & MainWindow : : onUpdateCheckComplete ) ;
m_auto_updater_dialog - > queueUpdateCheck ( display_message ) ;
}
void MainWindow : : onUpdateCheckComplete ( )
{
if ( ! m_auto_updater_dialog )
return ;
m_auto_updater_dialog - > deleteLater ( ) ;
m_auto_updater_dialog = nullptr ;
}
void MainWindow : : startupUpdateCheck ( )
{
2022-05-24 12:37:44 +00:00
if ( ! Host : : GetBaseBoolSettingValue ( " AutoUpdater " , " CheckAtStartup " , true ) )
2022-04-04 11:27:23 +00:00
return ;
checkForUpdates ( false ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : onToolsOpenDataDirectoryTriggered ( )
{
2022-05-19 14:46:33 +00:00
const QString path ( QString : : fromStdString ( EmuFolders : : DataRoot ) ) ;
2021-12-13 12:12:54 +00:00
QtUtils : : OpenURL ( this , QUrl : : fromLocalFile ( path ) ) ;
}
void MainWindow : : onThemeChanged ( )
{
setStyleFromSettings ( ) ;
2022-05-25 21:20:34 +00:00
setIconThemeFromStyle ( ) ;
2021-12-13 12:12:54 +00:00
recreate ( ) ;
}
void MainWindow : : onThemeChangedFromSettings ( )
{
// reopen the settings dialog after recreating
onThemeChanged ( ) ;
2022-02-15 14:29:18 +00:00
g_main_window - > doSettings ( ) ;
2021-12-13 12:12:54 +00:00
}
2022-03-04 09:46:47 +00:00
void MainWindow : : onLoggingOptionChanged ( )
{
2022-06-18 09:35:17 +00:00
Host : : UpdateLogging ( ) ;
2022-03-04 09:46:47 +00:00
}
2022-04-04 00:06:59 +00:00
void MainWindow : : onInputRecNewActionTriggered ( )
{
2022-06-28 12:53:26 +00:00
const bool wasPaused = s_vm_paused ;
const bool wasRunning = s_vm_valid ;
2022-04-04 00:06:59 +00:00
if ( wasRunning & & ! wasPaused )
{
VMManager : : SetPaused ( true ) ;
}
NewInputRecordingDlg dlg ( this ) ;
const auto result = dlg . exec ( ) ;
if ( result = = QDialog : : Accepted )
{
if ( g_InputRecording . Create (
dlg . getFilePath ( ) ,
dlg . getInputRecType ( ) = = InputRecording : : Type : : FROM_SAVESTATE ,
dlg . getAuthorName ( ) ) )
{
return ;
}
}
if ( wasRunning & & ! wasPaused )
{
VMManager : : SetPaused ( false ) ;
}
}
# include "pcsx2/Recording/InputRecordingControls.h"
void MainWindow : : onInputRecPlayActionTriggered ( )
{
2022-06-28 12:53:26 +00:00
const bool wasPaused = s_vm_paused ;
2022-04-04 00:06:59 +00:00
if ( ! wasPaused )
g_InputRecordingControls . PauseImmediately ( ) ;
QFileDialog dialog ( this ) ;
dialog . setFileMode ( QFileDialog : : ExistingFile ) ;
dialog . setWindowTitle ( " Select a File " ) ;
dialog . setNameFilter ( tr ( " Input Recording Files (*.p2m2) " ) ) ;
QStringList fileNames ;
if ( dialog . exec ( ) )
{
fileNames = dialog . selectedFiles ( ) ;
}
if ( fileNames . length ( ) > 0 )
{
if ( g_InputRecording . IsActive ( ) )
{
g_InputRecording . Stop ( ) ;
}
2022-04-04 21:35:08 +00:00
if ( g_InputRecording . Play ( fileNames . first ( ) . toStdString ( ) ) )
2022-04-04 00:06:59 +00:00
{
return ;
}
}
if ( ! wasPaused )
{
g_InputRecordingControls . Resume ( ) ;
}
}
void MainWindow : : onInputRecStopActionTriggered ( )
{
if ( g_InputRecording . IsActive ( ) )
{
g_InputRecording . Stop ( ) ;
}
}
void MainWindow : : onInputRecOpenSettingsTriggered ( )
{
// TODO - Vaser - Implement
}
2021-12-13 12:12:54 +00:00
void MainWindow : : onVMStarting ( )
{
2022-06-28 12:53:26 +00:00
s_vm_valid = true ;
2021-12-13 12:12:54 +00:00
updateEmulationActions ( true , false ) ;
updateWindowTitle ( ) ;
// prevent loading state until we're fully initialized
updateSaveStateMenus ( QString ( ) , QString ( ) , 0 ) ;
}
void MainWindow : : onVMStarted ( )
{
2022-06-28 12:53:26 +00:00
s_vm_valid = true ;
2022-05-06 13:34:07 +00:00
m_was_disc_change_request = false ;
2021-12-13 12:12:54 +00:00
updateEmulationActions ( true , true ) ;
updateWindowTitle ( ) ;
2022-04-03 13:46:05 +00:00
updateStatusBarWidgetVisibility ( ) ;
2021-12-13 12:12:54 +00:00
}
void MainWindow : : onVMPaused ( )
{
// update UI
{
QSignalBlocker sb ( m_ui . actionPause ) ;
m_ui . actionPause - > setChecked ( true ) ;
}
2022-06-28 12:53:26 +00:00
s_vm_paused = true ;
2021-12-13 12:12:54 +00:00
updateWindowTitle ( ) ;
2022-04-03 13:46:05 +00:00
updateStatusBarWidgetVisibility ( ) ;
m_status_fps_widget - > setText ( tr ( " Paused " ) ) ;
2022-06-20 08:01:40 +00:00
if ( m_display_widget )
{
m_display_widget - > updateRelativeMode ( false ) ;
m_display_widget - > updateCursor ( false ) ;
}
2021-12-13 12:12:54 +00:00
}
void MainWindow : : onVMResumed ( )
{
// update UI
{
QSignalBlocker sb ( m_ui . actionPause ) ;
m_ui . actionPause - > setChecked ( false ) ;
}
2022-06-28 12:53:26 +00:00
s_vm_paused = false ;
2022-05-06 13:34:07 +00:00
m_was_disc_change_request = false ;
2021-12-13 12:12:54 +00:00
updateWindowTitle ( ) ;
2022-04-03 13:46:05 +00:00
updateStatusBarWidgetVisibility ( ) ;
m_status_fps_widget - > setText ( m_last_fps_status ) ;
2022-05-07 03:52:13 +00:00
if ( m_display_widget )
2022-06-20 08:01:40 +00:00
{
m_display_widget - > updateRelativeMode ( true ) ;
m_display_widget - > updateCursor ( true ) ;
2022-05-07 03:52:13 +00:00
m_display_widget - > setFocus ( ) ;
2022-06-20 08:01:40 +00:00
}
2021-12-13 12:12:54 +00:00
}
void MainWindow : : onVMStopped ( )
{
2022-06-28 12:53:26 +00:00
s_vm_valid = false ;
s_vm_paused = false ;
2022-04-03 13:46:05 +00:00
m_last_fps_status = QString ( ) ;
2021-12-13 12:12:54 +00:00
updateEmulationActions ( false , false ) ;
updateWindowTitle ( ) ;
2022-06-28 12:16:45 +00:00
updateWindowState ( ) ;
2022-04-03 13:46:05 +00:00
updateStatusBarWidgetVisibility ( ) ;
2022-05-15 08:15:27 +00:00
if ( m_display_widget )
{
m_display_widget - > updateRelativeMode ( false ) ;
m_display_widget - > updateCursor ( false ) ;
}
else
{
switchToGameListView ( ) ;
}
2021-12-13 12:12:54 +00:00
}
void MainWindow : : onGameChanged ( const QString & path , const QString & serial , const QString & name , quint32 crc )
{
m_current_disc_path = path ;
m_current_game_serial = serial ;
m_current_game_name = name ;
m_current_game_crc = crc ;
updateWindowTitle ( ) ;
updateSaveStateMenus ( path , serial , crc ) ;
}
2022-04-03 13:46:05 +00:00
void MainWindow : : onPerformanceMetricsUpdated ( const QString & fps_stat , const QString & gs_stat )
{
m_last_fps_status = fps_stat ;
m_status_fps_widget - > setText ( m_last_fps_status ) ;
m_status_gs_widget - > setText ( gs_stat ) ;
}
2022-06-04 16:13:05 +00:00
void MainWindow : : showEvent ( QShowEvent * event )
{
QMainWindow : : showEvent ( event ) ;
// This is a bit silly, but for some reason resizing *before* the window is shown
// gives the incorrect sizes for columns, if you set the style before setting up
// the rest of the window... so, instead, let's just force it to be resized on show.
if ( isShowingGameList ( ) )
m_game_list_widget - > resizeTableViewColumnsToFit ( ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : closeEvent ( QCloseEvent * event )
{
2022-03-25 10:14:23 +00:00
if ( ! requestShutdown ( true , true , true ) )
2022-03-04 10:40:03 +00:00
{
event - > ignore ( ) ;
return ;
}
2021-12-13 12:12:54 +00:00
saveStateToConfig ( ) ;
QMainWindow : : closeEvent ( event ) ;
}
2022-05-26 07:49:40 +00:00
static QString getFilenameFromMimeData ( const QMimeData * md )
{
QString filename ;
if ( md - > hasUrls ( ) )
{
// only one url accepted
const QList < QUrl > urls ( md - > urls ( ) ) ;
if ( urls . size ( ) = = 1 )
filename = urls . front ( ) . toLocalFile ( ) ;
}
return filename ;
}
void MainWindow : : dragEnterEvent ( QDragEnterEvent * event )
{
const std : : string filename ( getFilenameFromMimeData ( event - > mimeData ( ) ) . toStdString ( ) ) ;
// allow save states being dragged in
if ( ! VMManager : : IsLoadableFileName ( filename ) & & ! VMManager : : IsSaveStateFileName ( filename ) )
return ;
event - > acceptProposedAction ( ) ;
}
void MainWindow : : dropEvent ( QDropEvent * event )
{
const QString filename ( getFilenameFromMimeData ( event - > mimeData ( ) ) ) ;
const std : : string filename_str ( filename . toStdString ( ) ) ;
if ( VMManager : : IsSaveStateFileName ( filename_str ) )
{
// can't load a save state without a current VM
2022-06-28 12:53:26 +00:00
if ( s_vm_valid )
2022-05-26 07:49:40 +00:00
{
event - > acceptProposedAction ( ) ;
g_emu_thread - > loadState ( filename ) ;
}
else
{
QMessageBox : : critical ( this , tr ( " Load State Failed " ) , tr ( " Cannot load a save state without a running VM. " ) ) ;
}
}
else if ( VMManager : : IsLoadableFileName ( filename_str ) )
{
// if we're already running, do a disc change, otherwise start
event - > acceptProposedAction ( ) ;
2022-06-28 12:53:26 +00:00
if ( s_vm_valid )
2022-05-26 07:49:40 +00:00
doDiscChange ( filename ) ;
else
doStartDisc ( filename ) ;
}
}
2021-12-13 12:12:54 +00:00
DisplayWidget * MainWindow : : createDisplay ( bool fullscreen , bool render_to_main )
{
2022-05-06 11:03:16 +00:00
DevCon . WriteLn ( " createDisplay(%u, %u) " , static_cast < u32 > ( fullscreen ) , static_cast < u32 > ( render_to_main ) ) ;
2021-12-13 12:12:54 +00:00
HostDisplay * host_display = Host : : GetHostDisplay ( ) ;
if ( ! host_display )
return nullptr ;
2022-05-24 12:37:44 +00:00
const std : : string fullscreen_mode ( Host : : GetBaseStringSettingValue ( " EmuCore/GS " , " FullscreenMode " , " " ) ) ;
2021-12-13 12:12:54 +00:00
const bool is_exclusive_fullscreen = ( fullscreen & & ! fullscreen_mode . empty ( ) & & host_display - > SupportsFullscreen ( ) ) ;
QWidget * container ;
if ( DisplayContainer : : IsNeeded ( fullscreen , render_to_main ) )
{
m_display_container = new DisplayContainer ( ) ;
m_display_widget = new DisplayWidget ( m_display_container ) ;
m_display_container - > setDisplayWidget ( m_display_widget ) ;
container = m_display_container ;
}
else
{
2022-05-06 11:03:16 +00:00
m_display_widget = new DisplayWidget ( ( ! fullscreen & & render_to_main ) ? this : nullptr ) ;
2021-12-13 12:12:54 +00:00
container = m_display_widget ;
}
2022-05-06 11:03:16 +00:00
if ( fullscreen | | ! render_to_main )
{
container - > setWindowTitle ( windowTitle ( ) ) ;
container - > setWindowIcon ( windowIcon ( ) ) ;
}
2021-12-13 12:12:54 +00:00
if ( fullscreen )
{
if ( ! is_exclusive_fullscreen )
container - > showFullScreen ( ) ;
else
container - > showNormal ( ) ;
}
else if ( ! render_to_main )
{
restoreDisplayWindowGeometryFromConfig ( ) ;
container - > showNormal ( ) ;
}
else
{
2022-05-06 11:03:16 +00:00
m_game_list_widget - > setVisible ( false ) ;
takeCentralWidget ( ) ;
setCentralWidget ( m_display_widget ) ;
2021-12-13 12:12:54 +00:00
}
// we need the surface visible.. this might be able to be replaced with something else
QCoreApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents ) ;
std : : optional < WindowInfo > wi = m_display_widget - > getWindowInfo ( ) ;
if ( ! wi . has_value ( ) )
{
QMessageBox : : critical ( this , tr ( " Error " ) , tr ( " Failed to get window info from widget " ) ) ;
destroyDisplayWidget ( ) ;
return nullptr ;
}
2022-04-15 08:45:41 +00:00
g_emu_thread - > connectDisplaySignals ( m_display_widget ) ;
2022-02-15 14:29:18 +00:00
if ( ! host_display - > CreateRenderDevice ( wi . value ( ) , Host : : GetStringSettingValue ( " EmuCore/GS " , " Adapter " , " " ) , EmuConfig . GetEffectiveVsyncMode ( ) ,
Host : : GetBoolSettingValue ( " EmuCore/GS " , " ThreadedPresentation " , false ) , Host : : GetBoolSettingValue ( " EmuCore/GS " , " UseDebugDevice " , false ) ) )
2021-12-13 12:12:54 +00:00
{
QMessageBox : : critical ( this , tr ( " Error " ) , tr ( " Failed to create host display device context. " ) ) ;
destroyDisplayWidget ( ) ;
return nullptr ;
}
if ( is_exclusive_fullscreen )
setDisplayFullscreen ( fullscreen_mode ) ;
2022-05-06 11:03:16 +00:00
updateWindowTitle ( ) ;
2022-06-28 12:16:45 +00:00
updateWindowState ( ) ;
2022-05-06 11:03:16 +00:00
2022-06-28 11:37:30 +00:00
m_display_widget - > setFocus ( ) ;
2022-05-15 08:15:27 +00:00
m_display_widget - > setShouldHideCursor ( shouldHideMouseCursor ( ) ) ;
2022-06-28 12:53:26 +00:00
m_display_widget - > updateRelativeMode ( s_vm_valid & & ! s_vm_paused ) ;
m_display_widget - > updateCursor ( s_vm_valid & & ! s_vm_paused ) ;
2022-05-15 08:15:27 +00:00
2021-12-13 12:12:54 +00:00
host_display - > DoneRenderContextCurrent ( ) ;
return m_display_widget ;
}
2022-05-06 11:03:16 +00:00
DisplayWidget * MainWindow : : updateDisplay ( bool fullscreen , bool render_to_main , bool surfaceless )
2021-12-13 12:12:54 +00:00
{
2022-05-07 03:52:13 +00:00
DevCon . WriteLn ( " updateDisplay() fullscreen=%s render_to_main=%s surfaceless=%s " ,
fullscreen ? " true " : " false " , render_to_main ? " true " : " false " , surfaceless ? " true " : " false " ) ;
2022-05-06 11:03:16 +00:00
2021-12-13 12:12:54 +00:00
HostDisplay * host_display = Host : : GetHostDisplay ( ) ;
2022-04-15 08:45:41 +00:00
QWidget * container = m_display_container ? static_cast < QWidget * > ( m_display_container ) : static_cast < QWidget * > ( m_display_widget ) ;
2022-05-07 03:52:13 +00:00
const bool is_fullscreen = isRenderingFullscreen ( ) ;
const bool is_rendering_to_main = isRenderingToMain ( ) ;
2022-05-24 12:37:44 +00:00
const std : : string fullscreen_mode ( Host : : GetBaseStringSettingValue ( " EmuCore/GS " , " FullscreenMode " , " " ) ) ;
2021-12-13 12:12:54 +00:00
const bool is_exclusive_fullscreen = ( fullscreen & & ! fullscreen_mode . empty ( ) & & host_display - > SupportsFullscreen ( ) ) ;
2022-05-06 11:03:16 +00:00
const bool changing_surfaceless = ( ! m_display_widget ! = surfaceless ) ;
if ( fullscreen = = is_fullscreen & & is_rendering_to_main = = render_to_main & & ! changing_surfaceless )
2021-12-13 12:12:54 +00:00
return m_display_widget ;
// Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off.
2022-04-15 08:45:41 +00:00
// .. except on Wayland, where everything tends to break if you don't recreate.
2021-12-13 12:12:54 +00:00
const bool has_container = ( m_display_container ! = nullptr ) ;
const bool needs_container = DisplayContainer : : IsNeeded ( fullscreen , render_to_main ) ;
2022-05-06 11:03:16 +00:00
if ( ! is_rendering_to_main & & ! render_to_main & & ! is_exclusive_fullscreen & & has_container = = needs_container & & ! needs_container & & ! changing_surfaceless )
2021-12-13 12:12:54 +00:00
{
2022-05-07 03:52:13 +00:00
DevCon . WriteLn ( " Toggling to %s without recreating surface " , ( fullscreen ? " fullscreen " : " windowed " ) ) ;
2021-12-13 12:12:54 +00:00
if ( host_display - > IsFullscreen ( ) )
host_display - > SetFullscreen ( false , 0 , 0 , 0.0f ) ;
2022-05-07 03:52:13 +00:00
// since we don't destroy the display widget, we need to save it here
if ( ! is_fullscreen & & ! is_rendering_to_main )
saveDisplayWindowGeometryToConfig ( ) ;
2021-12-13 12:12:54 +00:00
if ( fullscreen )
{
2022-04-15 08:45:41 +00:00
container - > showFullScreen ( ) ;
2021-12-13 12:12:54 +00:00
}
else
{
restoreDisplayWindowGeometryFromConfig ( ) ;
2022-04-15 08:45:41 +00:00
container - > showNormal ( ) ;
2021-12-13 12:12:54 +00:00
}
2022-06-20 11:50:01 +00:00
m_display_widget - > setFocus ( ) ;
m_display_widget - > setShouldHideCursor ( shouldHideMouseCursor ( ) ) ;
2022-06-28 12:53:26 +00:00
m_display_widget - > updateRelativeMode ( s_vm_valid & & ! s_vm_paused ) ;
m_display_widget - > updateCursor ( s_vm_valid & & ! s_vm_paused ) ;
2022-06-20 11:50:01 +00:00
2021-12-13 12:12:54 +00:00
QCoreApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents ) ;
return m_display_widget ;
}
host_display - > DestroyRenderSurface ( ) ;
destroyDisplayWidget ( ) ;
2022-05-06 11:03:16 +00:00
// if we're going to surfaceless, we're done here
if ( surfaceless )
return nullptr ;
2021-12-13 12:12:54 +00:00
if ( DisplayContainer : : IsNeeded ( fullscreen , render_to_main ) )
{
m_display_container = new DisplayContainer ( ) ;
m_display_widget = new DisplayWidget ( m_display_container ) ;
m_display_container - > setDisplayWidget ( m_display_widget ) ;
container = m_display_container ;
}
else
{
2022-05-06 11:03:16 +00:00
m_display_widget = new DisplayWidget ( ( ! fullscreen & & render_to_main ) ? this : nullptr ) ;
2021-12-13 12:12:54 +00:00
container = m_display_widget ;
}
2022-05-06 11:03:16 +00:00
if ( fullscreen | | ! render_to_main )
{
container - > setWindowTitle ( windowTitle ( ) ) ;
container - > setWindowIcon ( windowIcon ( ) ) ;
// make sure the game list widget is still visible
if ( centralWidget ( ) ! = m_game_list_widget & & ! fullscreen )
{
setCentralWidget ( m_game_list_widget ) ;
m_game_list_widget - > setVisible ( true ) ;
}
}
2021-12-13 12:12:54 +00:00
if ( fullscreen )
{
if ( ! is_exclusive_fullscreen )
container - > showFullScreen ( ) ;
else
container - > showNormal ( ) ;
}
else if ( ! render_to_main )
{
restoreDisplayWindowGeometryFromConfig ( ) ;
container - > showNormal ( ) ;
}
else
{
2022-05-06 11:03:16 +00:00
m_game_list_widget - > setVisible ( false ) ;
takeCentralWidget ( ) ;
setCentralWidget ( m_display_widget ) ;
m_display_widget - > setFocus ( ) ;
2021-12-13 12:12:54 +00:00
}
// we need the surface visible.. this might be able to be replaced with something else
QCoreApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents ) ;
std : : optional < WindowInfo > wi = m_display_widget - > getWindowInfo ( ) ;
if ( ! wi . has_value ( ) )
{
QMessageBox : : critical ( this , tr ( " Error " ) , tr ( " Failed to get new window info from widget " ) ) ;
destroyDisplayWidget ( ) ;
return nullptr ;
}
2022-04-15 08:45:41 +00:00
g_emu_thread - > connectDisplaySignals ( m_display_widget ) ;
2021-12-13 12:12:54 +00:00
if ( ! host_display - > ChangeRenderWindow ( wi . value ( ) ) )
pxFailRel ( " Failed to recreate surface on new widget. " ) ;
if ( is_exclusive_fullscreen )
setDisplayFullscreen ( fullscreen_mode ) ;
2022-05-06 11:03:16 +00:00
updateWindowTitle ( ) ;
2022-06-28 12:16:45 +00:00
updateWindowState ( ) ;
2022-06-28 11:37:30 +00:00
2021-12-13 12:12:54 +00:00
m_display_widget - > setFocus ( ) ;
2022-05-15 08:15:27 +00:00
m_display_widget - > setShouldHideCursor ( shouldHideMouseCursor ( ) ) ;
2022-06-28 12:53:26 +00:00
m_display_widget - > updateRelativeMode ( s_vm_valid & & ! s_vm_paused ) ;
m_display_widget - > updateCursor ( s_vm_valid & & ! s_vm_paused ) ;
2021-12-13 12:12:54 +00:00
QSignalBlocker blocker ( m_ui . actionFullscreen ) ;
m_ui . actionFullscreen - > setChecked ( fullscreen ) ;
return m_display_widget ;
}
void MainWindow : : displayResizeRequested ( qint32 width , qint32 height )
{
if ( ! m_display_widget )
return ;
// unapply the pixel scaling factor for hidpi
const float dpr = devicePixelRatioF ( ) ;
width = static_cast < qint32 > ( std : : max ( static_cast < int > ( std : : lroundf ( static_cast < float > ( width ) / dpr ) ) , 1 ) ) ;
height = static_cast < qint32 > ( std : : max ( static_cast < int > ( std : : lroundf ( static_cast < float > ( height ) / dpr ) ) , 1 ) ) ;
if ( m_display_container | | ! m_display_widget - > parent ( ) )
{
// no parent - rendering to separate window. easy.
2022-06-28 12:16:45 +00:00
QtUtils : : ResizePotentiallyFixedSizeWindow ( getDisplayContainer ( ) , width , height ) ;
2021-12-13 12:12:54 +00:00
return ;
}
// we are rendering to the main window. we have to add in the extra height from the toolbar/status bar.
const s32 extra_height = this - > height ( ) - m_display_widget - > height ( ) ;
2022-06-28 12:16:45 +00:00
QtUtils : : ResizePotentiallyFixedSizeWindow ( this , width , height + extra_height ) ;
2021-12-13 12:12:54 +00:00
}
2022-03-12 14:52:52 +00:00
void MainWindow : : destroyDisplay ( )
{
2022-06-28 11:37:30 +00:00
// Now we can safely destroy the display window.
2022-03-12 14:52:52 +00:00
destroyDisplayWidget ( ) ;
2022-05-06 11:03:16 +00:00
2022-06-28 11:37:30 +00:00
// Switch back to game list view, we're not going back to display, so we can't use switchToGameListView().
2022-05-06 11:03:16 +00:00
if ( centralWidget ( ) ! = m_game_list_widget )
{
takeCentralWidget ( ) ;
setCentralWidget ( m_game_list_widget ) ;
m_game_list_widget - > setVisible ( true ) ;
m_game_list_widget - > setFocus ( ) ;
}
2022-03-12 14:52:52 +00:00
}
2021-12-13 12:12:54 +00:00
void MainWindow : : focusDisplayWidget ( )
{
2022-05-06 11:03:16 +00:00
if ( ! m_display_widget | | centralWidget ( ) ! = m_display_widget )
2021-12-13 12:12:54 +00:00
return ;
m_display_widget - > setFocus ( ) ;
}
QWidget * MainWindow : : getDisplayContainer ( ) const
{
return ( m_display_container ? static_cast < QWidget * > ( m_display_container ) : static_cast < QWidget * > ( m_display_widget ) ) ;
}
void MainWindow : : saveDisplayWindowGeometryToConfig ( )
{
2022-05-07 03:52:13 +00:00
QWidget * container = getDisplayContainer ( ) ;
if ( container - > windowState ( ) & Qt : : WindowFullScreen )
{
// if we somehow ended up here, don't save the fullscreen state to the config
return ;
}
2021-12-13 12:12:54 +00:00
const QByteArray geometry = getDisplayContainer ( ) - > saveGeometry ( ) ;
const QByteArray geometry_b64 = geometry . toBase64 ( ) ;
2022-05-24 12:37:44 +00:00
const std : : string old_geometry_b64 = Host : : GetBaseStringSettingValue ( " UI " , " DisplayWindowGeometry " ) ;
2021-12-13 12:12:54 +00:00
if ( old_geometry_b64 ! = geometry_b64 . constData ( ) )
QtHost : : SetBaseStringSettingValue ( " UI " , " DisplayWindowGeometry " , geometry_b64 . constData ( ) ) ;
}
void MainWindow : : restoreDisplayWindowGeometryFromConfig ( )
{
2022-05-24 12:37:44 +00:00
const std : : string geometry_b64 = Host : : GetBaseStringSettingValue ( " UI " , " DisplayWindowGeometry " ) ;
2021-12-13 12:12:54 +00:00
const QByteArray geometry = QByteArray : : fromBase64 ( QByteArray : : fromStdString ( geometry_b64 ) ) ;
QWidget * container = getDisplayContainer ( ) ;
if ( ! geometry . isEmpty ( ) )
2022-05-07 03:52:13 +00:00
{
2021-12-13 12:12:54 +00:00
container - > restoreGeometry ( geometry ) ;
2022-05-07 03:52:13 +00:00
// make sure we're not loading a dodgy config which had fullscreen set...
container - > setWindowState ( container - > windowState ( ) & ~ ( Qt : : WindowFullScreen | Qt : : WindowActive ) ) ;
}
2021-12-13 12:12:54 +00:00
else
2022-05-07 03:52:13 +00:00
{
// default size
2021-12-13 12:12:54 +00:00
container - > resize ( 640 , 480 ) ;
2022-05-07 03:52:13 +00:00
}
2021-12-13 12:12:54 +00:00
}
void MainWindow : : destroyDisplayWidget ( )
{
if ( ! m_display_widget )
return ;
2022-05-07 03:52:13 +00:00
if ( ! isRenderingFullscreen ( ) & & ! isRenderingToMain ( ) )
2021-12-13 12:12:54 +00:00
saveDisplayWindowGeometryToConfig ( ) ;
if ( m_display_container )
m_display_container - > removeDisplayWidget ( ) ;
2022-05-06 11:03:16 +00:00
if ( m_display_widget = = centralWidget ( ) )
takeCentralWidget ( ) ;
if ( m_display_widget )
2021-12-13 12:12:54 +00:00
{
2022-05-06 11:03:16 +00:00
m_display_widget - > deleteLater ( ) ;
m_display_widget = nullptr ;
2021-12-13 12:12:54 +00:00
}
2022-05-06 11:03:16 +00:00
if ( m_display_container )
{
m_display_container - > deleteLater ( ) ;
m_display_container = nullptr ;
}
2021-12-13 12:12:54 +00:00
}
void MainWindow : : setDisplayFullscreen ( const std : : string & fullscreen_mode )
{
u32 width , height ;
float refresh_rate ;
if ( HostDisplay : : ParseFullscreenMode ( fullscreen_mode , & width , & height , & refresh_rate ) )
{
if ( Host : : GetHostDisplay ( ) - > SetFullscreen ( true , width , height , refresh_rate ) )
{
Host : : AddOSDMessage ( " Acquired exclusive fullscreen. " , 10.0f ) ;
}
else
{
Host : : AddOSDMessage ( " Failed to acquire exclusive fullscreen. " , 10.0f ) ;
}
}
}
SettingsDialog * MainWindow : : getSettingsDialog ( )
{
if ( ! m_settings_dialog )
{
m_settings_dialog = new SettingsDialog ( this ) ;
2022-02-15 14:29:18 +00:00
connect (
m_settings_dialog - > getInterfaceSettingsWidget ( ) , & InterfaceSettingsWidget : : themeChanged , this , & MainWindow : : onThemeChangedFromSettings ) ;
2021-12-13 12:12:54 +00:00
}
return m_settings_dialog ;
}
2022-02-15 14:29:18 +00:00
void MainWindow : : doSettings ( const char * category /* = nullptr */ )
2021-12-13 12:12:54 +00:00
{
SettingsDialog * dlg = getSettingsDialog ( ) ;
if ( ! dlg - > isVisible ( ) )
{
dlg - > setModal ( false ) ;
dlg - > show ( ) ;
}
2022-02-15 14:29:18 +00:00
if ( category )
2021-12-13 12:12:54 +00:00
dlg - > setCategory ( category ) ;
}
ControllerSettingsDialog * MainWindow : : getControllerSettingsDialog ( )
{
if ( ! m_controller_settings_dialog )
m_controller_settings_dialog = new ControllerSettingsDialog ( this ) ;
return m_controller_settings_dialog ;
}
void MainWindow : : doControllerSettings ( ControllerSettingsDialog : : Category category )
{
ControllerSettingsDialog * dlg = getControllerSettingsDialog ( ) ;
if ( ! dlg - > isVisible ( ) )
{
dlg - > setModal ( false ) ;
dlg - > show ( ) ;
}
if ( category ! = ControllerSettingsDialog : : Category : : Count )
dlg - > setCategory ( category ) ;
}
2022-02-15 14:29:18 +00:00
void MainWindow : : startGameListEntry ( const GameList : : Entry * entry , std : : optional < s32 > save_slot , std : : optional < bool > fast_boot )
2021-12-13 12:12:54 +00:00
{
std : : shared_ptr < VMBootParameters > params = std : : make_shared < VMBootParameters > ( ) ;
params - > fast_boot = fast_boot ;
GameList : : FillBootParametersForEntry ( params . get ( ) , entry ) ;
if ( save_slot . has_value ( ) & & ! entry - > serial . empty ( ) )
{
std : : string state_filename = VMManager : : GetSaveStateFileName ( entry - > serial . c_str ( ) , entry - > crc , save_slot . value ( ) ) ;
if ( ! FileSystem : : FileExists ( state_filename . c_str ( ) ) )
{
QMessageBox : : critical ( this , tr ( " Error " ) , tr ( " This save state does not exist. " ) ) ;
return ;
}
params - > save_state = std : : move ( state_filename ) ;
}
g_emu_thread - > startVM ( std : : move ( params ) ) ;
}
void MainWindow : : setGameListEntryCoverImage ( const GameList : : Entry * entry )
{
2022-02-15 14:29:18 +00:00
const QString filename ( QFileDialog : : getOpenFileName ( this , tr ( " Select Cover Image " ) , QString ( ) , tr ( " All Cover Image Types (*.jpg *.jpeg *.png) " ) ) ) ;
2021-12-13 12:12:54 +00:00
if ( filename . isEmpty ( ) )
return ;
if ( ! GameList : : GetCoverImagePathForEntry ( entry ) . empty ( ) )
{
2022-02-15 14:29:18 +00:00
if ( QMessageBox : : question ( this , tr ( " Cover Already Exists " ) , tr ( " A cover image for this game already exists, do you wish to replace it? " ) ,
2021-12-13 12:12:54 +00:00
QMessageBox : : Yes , QMessageBox : : No ) ! = QMessageBox : : Yes )
{
return ;
}
}
const QString new_filename ( QString : : fromStdString ( GameList : : GetNewCoverImagePathForEntry ( entry , filename . toUtf8 ( ) . constData ( ) ) ) ) ;
if ( new_filename . isEmpty ( ) )
return ;
if ( QFile : : exists ( new_filename ) & & ! QFile : : remove ( new_filename ) )
{
QMessageBox : : critical ( this , tr ( " Copy Error " ) , tr ( " Failed to remove existing cover '%1' " ) . arg ( new_filename ) ) ;
return ;
}
if ( ! QFile : : copy ( filename , new_filename ) )
{
QMessageBox : : critical ( this , tr ( " Copy Error " ) , tr ( " Failed to copy '%1' to '%2' " ) . arg ( filename ) . arg ( new_filename ) ) ;
return ;
}
m_game_list_widget - > refreshGridCovers ( ) ;
}
2022-05-07 12:56:44 +00:00
std : : optional < bool > MainWindow : : promptForResumeState ( const QString & save_state_path )
{
if ( save_state_path . isEmpty ( ) )
return false ;
QFileInfo fi ( save_state_path ) ;
if ( ! fi . exists ( ) )
return false ;
QMessageBox msgbox ( this ) ;
msgbox . setIcon ( QMessageBox : : Question ) ;
msgbox . setWindowTitle ( tr ( " Load Resume State " ) ) ;
msgbox . setText (
tr ( " A resume save state was found for this game, saved at: \n \n %1. \n \n Do you want to load this state, or start from a fresh boot? " )
. arg ( fi . lastModified ( ) . toLocalTime ( ) . toString ( ) ) ) ;
QPushButton * load = msgbox . addButton ( tr ( " Load State " ) , QMessageBox : : AcceptRole ) ;
QPushButton * boot = msgbox . addButton ( tr ( " Fresh Boot " ) , QMessageBox : : RejectRole ) ;
QPushButton * delboot = msgbox . addButton ( tr ( " Delete And Boot " ) , QMessageBox : : RejectRole ) ;
2022-05-24 15:12:56 +00:00
msgbox . addButton ( QMessageBox : : Cancel ) ;
2022-05-07 12:56:44 +00:00
msgbox . setDefaultButton ( load ) ;
msgbox . exec ( ) ;
QAbstractButton * clicked = msgbox . clickedButton ( ) ;
if ( load = = clicked )
{
return true ;
}
else if ( boot = = clicked )
{
return false ;
}
else if ( delboot = = clicked )
{
if ( ! QFile : : remove ( save_state_path ) )
QMessageBox : : critical ( this , tr ( " Error " ) , tr ( " Failed to delete save state file '%1'. " ) . arg ( save_state_path ) ) ;
return false ;
}
return std : : nullopt ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : loadSaveStateSlot ( s32 slot )
{
2022-06-28 12:53:26 +00:00
if ( s_vm_valid )
2021-12-13 12:12:54 +00:00
{
// easy when we're running
g_emu_thread - > loadStateFromSlot ( slot ) ;
return ;
}
else
{
// we're not currently running, therefore we must've right clicked in the game list
const GameList : : Entry * entry = m_game_list_widget - > getSelectedEntry ( ) ;
if ( ! entry )
return ;
startGameListEntry ( entry , slot , std : : nullopt ) ;
}
}
void MainWindow : : loadSaveStateFile ( const QString & filename , const QString & state_filename )
{
2022-06-28 12:53:26 +00:00
if ( s_vm_valid )
2021-12-13 12:12:54 +00:00
{
2022-05-26 07:36:05 +00:00
if ( ! filename . isEmpty ( ) & & m_current_disc_path ! = filename )
g_emu_thread - > changeDisc ( m_current_disc_path ) ;
g_emu_thread - > loadState ( state_filename ) ;
2021-12-13 12:12:54 +00:00
}
else
{
std : : shared_ptr < VMBootParameters > params = std : : make_shared < VMBootParameters > ( ) ;
2022-03-12 14:52:52 +00:00
params - > filename = filename . toStdString ( ) ;
2021-12-13 12:12:54 +00:00
params - > save_state = state_filename . toStdString ( ) ;
g_emu_thread - > startVM ( std : : move ( params ) ) ;
}
}
static QString formatTimestampForSaveStateMenu ( time_t timestamp )
{
const QDateTime qtime ( QDateTime : : fromSecsSinceEpoch ( static_cast < qint64 > ( timestamp ) ) ) ;
return qtime . toString ( QLocale : : system ( ) . dateTimeFormat ( QLocale : : ShortFormat ) ) ;
}
void MainWindow : : populateLoadStateMenu ( QMenu * menu , const QString & filename , const QString & serial , quint32 crc )
{
if ( serial . isEmpty ( ) )
return ;
const bool is_right_click_menu = ( menu ! = m_ui . menuLoadState ) ;
QAction * action = menu - > addAction ( is_right_click_menu ? tr ( " Load State File... " ) : tr ( " Load From File... " ) ) ;
connect ( action , & QAction : : triggered , [ this , filename ] ( ) {
2022-02-15 14:29:18 +00:00
const QString path ( QFileDialog : : getOpenFileName ( this , tr ( " Select Save State File " ) , QString ( ) , tr ( " Save States (*.p2s) " ) ) ) ;
2021-12-13 12:12:54 +00:00
if ( path . isEmpty ( ) )
return ;
loadSaveStateFile ( filename , path ) ;
} ) ;
// don't include undo in the right click menu
if ( ! is_right_click_menu )
{
QAction * load_undo_state = menu - > addAction ( tr ( " Undo Load State " ) ) ;
load_undo_state - > setEnabled ( false ) ; // CanUndoLoadState()
// connect(load_undo_state, &QAction::triggered, this, &QtHostInterface::undoLoadState);
menu - > addSeparator ( ) ;
}
const QByteArray game_serial_utf8 ( serial . toUtf8 ( ) ) ;
std : : string state_filename ;
FILESYSTEM_STAT_DATA sd ;
if ( is_right_click_menu )
{
state_filename = VMManager : : GetSaveStateFileName ( game_serial_utf8 . constData ( ) , crc , - 1 ) ;
if ( FileSystem : : StatFile ( state_filename . c_str ( ) , & sd ) )
{
action = menu - > addAction ( tr ( " Resume (%2) " ) . arg ( formatTimestampForSaveStateMenu ( sd . ModificationTime ) ) ) ;
connect ( action , & QAction : : triggered , [ this ] ( ) { loadSaveStateSlot ( - 1 ) ; } ) ;
// Make bold to indicate it's the default choice when double-clicking
2022-05-07 12:56:44 +00:00
QtUtils : : MarkActionAsDefault ( action ) ;
2021-12-13 12:12:54 +00:00
}
}
for ( s32 i = 1 ; i < = NUM_SAVE_STATE_SLOTS ; i + + )
{
FILESYSTEM_STAT_DATA sd ;
state_filename = VMManager : : GetSaveStateFileName ( game_serial_utf8 . constData ( ) , crc , i ) ;
if ( ! FileSystem : : StatFile ( state_filename . c_str ( ) , & sd ) )
continue ;
2022-06-15 18:09:31 +00:00
action = menu - > addAction ( tr ( " Load Slot %1 (%2) " ) . arg ( i ) . arg ( formatTimestampForSaveStateMenu ( sd . ModificationTime ) ) ) ;
2021-12-13 12:12:54 +00:00
connect ( action , & QAction : : triggered , [ this , i ] ( ) { loadSaveStateSlot ( i ) ; } ) ;
}
}
void MainWindow : : populateSaveStateMenu ( QMenu * menu , const QString & serial , quint32 crc )
{
if ( serial . isEmpty ( ) )
return ;
connect ( menu - > addAction ( tr ( " Save To File... " ) ) , & QAction : : triggered , [ this ] ( ) {
2022-02-15 14:29:18 +00:00
const QString path ( QFileDialog : : getSaveFileName ( this , tr ( " Select Save State File " ) , QString ( ) , tr ( " Save States (*.p2s) " ) ) ) ;
2021-12-13 12:12:54 +00:00
if ( path . isEmpty ( ) )
return ;
g_emu_thread - > saveState ( path ) ;
} ) ;
menu - > addSeparator ( ) ;
const QByteArray game_serial_utf8 ( serial . toUtf8 ( ) ) ;
for ( s32 i = 1 ; i < = NUM_SAVE_STATE_SLOTS ; i + + )
{
std : : string filename ( VMManager : : GetSaveStateFileName ( game_serial_utf8 . constData ( ) , crc , i ) ) ;
FILESYSTEM_STAT_DATA sd ;
QString timestamp ;
if ( FileSystem : : StatFile ( filename . c_str ( ) , & sd ) )
timestamp = formatTimestampForSaveStateMenu ( sd . ModificationTime ) ;
else
timestamp = tr ( " Empty " ) ;
QString title ( tr ( " Save Slot %1 (%2) " ).arg(i).arg(timestamp)) ;
2022-03-04 08:04:05 +00:00
connect ( menu - > addAction ( title ) , & QAction : : triggered , [ i ] ( ) { g_emu_thread - > saveStateToSlot ( i ) ; } ) ;
2021-12-13 12:12:54 +00:00
}
}
void MainWindow : : updateSaveStateMenus ( const QString & filename , const QString & serial , quint32 crc )
{
const bool load_enabled = ! serial . isEmpty ( ) ;
2022-06-28 12:53:26 +00:00
const bool save_enabled = ! serial . isEmpty ( ) & & s_vm_valid ;
2021-12-13 12:12:54 +00:00
m_ui . menuLoadState - > clear ( ) ;
m_ui . menuLoadState - > setEnabled ( load_enabled ) ;
m_ui . actionLoadState - > setEnabled ( load_enabled ) ;
m_ui . menuSaveState - > clear ( ) ;
m_ui . menuSaveState - > setEnabled ( save_enabled ) ;
m_ui . actionSaveState - > setEnabled ( save_enabled ) ;
m_save_states_invalidated = false ;
if ( load_enabled )
populateLoadStateMenu ( m_ui . menuLoadState , filename , serial , crc ) ;
if ( save_enabled )
populateSaveStateMenu ( m_ui . menuSaveState , serial , crc ) ;
}
2022-05-06 13:34:07 +00:00
2022-05-26 07:49:40 +00:00
void MainWindow : : doStartDisc ( const QString & path )
{
2022-06-28 12:53:26 +00:00
if ( s_vm_valid )
2022-05-26 07:49:40 +00:00
return ;
std : : shared_ptr < VMBootParameters > params = std : : make_shared < VMBootParameters > ( ) ;
params - > filename = path . toStdString ( ) ;
// we might still be saving a resume state...
VMManager : : WaitForSaveStateFlush ( ) ;
const std : : optional < bool > resume (
promptForResumeState (
QString : : fromStdString ( VMManager : : GetSaveStateFileName ( params - > filename . c_str ( ) , - 1 ) ) ) ) ;
if ( ! resume . has_value ( ) )
return ;
else if ( resume . value ( ) )
params - > state_index = - 1 ;
g_emu_thread - > startVM ( std : : move ( params ) ) ;
}
2022-05-06 13:34:07 +00:00
void MainWindow : : doDiscChange ( const QString & path )
{
bool reset_system = false ;
if ( ! m_was_disc_change_request )
{
const int choice = QMessageBox : : question ( this , tr ( " Confirm Disc Change " ) , tr ( " Do you want to swap discs or boot the new image (via system reset)? " ) ,
tr ( " Swap Disc " ) , tr ( " Reset " ) , tr ( " Cancel " ) , 0 , 2 ) ;
if ( choice = = 2 )
return ;
reset_system = ( choice ! = 0 ) ;
}
switchToEmulationView ( ) ;
g_emu_thread - > changeDisc ( path ) ;
if ( reset_system )
g_emu_thread - > resetVM ( ) ;
}
2022-05-07 03:52:13 +00:00
MainWindow : : VMLock MainWindow : : pauseAndLockVM ( )
{
const bool was_fullscreen = isRenderingFullscreen ( ) ;
2022-06-28 12:53:26 +00:00
const bool was_paused = s_vm_paused ;
2022-05-07 03:52:13 +00:00
// We use surfaceless rather than switching out of fullscreen, because
// we're paused, so we're not going to be rendering anyway.
if ( was_fullscreen )
g_emu_thread - > setSurfaceless ( true ) ;
if ( ! was_paused )
g_emu_thread - > setVMPaused ( true ) ;
// We want to parent dialogs to the display widget, except if we were fullscreen,
// since it's going to get destroyed by the surfaceless call above.
QWidget * dialog_parent = was_fullscreen ? static_cast < QWidget * > ( this ) : getDisplayContainer ( ) ;
return VMLock ( dialog_parent , was_paused , was_fullscreen ) ;
}
MainWindow : : VMLock : : VMLock ( QWidget * dialog_parent , bool was_paused , bool was_fullscreen )
: m_dialog_parent ( dialog_parent )
, m_was_paused ( was_paused )
, m_was_fullscreen ( was_fullscreen )
{
}
MainWindow : : VMLock : : VMLock ( VMLock & & lock )
: m_dialog_parent ( lock . m_dialog_parent )
, m_was_paused ( lock . m_was_paused )
, m_was_fullscreen ( lock . m_was_fullscreen )
{
lock . m_dialog_parent = nullptr ;
2022-06-20 08:03:38 +00:00
lock . m_was_paused = true ;
2022-05-07 03:52:13 +00:00
lock . m_was_fullscreen = false ;
}
MainWindow : : VMLock : : ~ VMLock ( )
{
if ( m_was_fullscreen )
g_emu_thread - > setSurfaceless ( false ) ;
if ( ! m_was_paused )
g_emu_thread - > setVMPaused ( false ) ;
}
2022-06-20 08:03:38 +00:00
void MainWindow : : VMLock : : cancelResume ( )
{
m_was_paused = true ;
m_was_fullscreen = false ;
}
2022-06-28 12:53:26 +00:00
bool QtHost : : IsVMValid ( )
{
return s_vm_valid ;
}
bool QtHost : : IsVMPaused ( )
{
return s_vm_paused ;
}