2021-12-13 12:12:54 +00:00
/* PCSX2 - PS2 Emulator for PCs
2023-01-12 09:30:39 +00:00
* Copyright ( C ) 2002 - 2023 PCSX2 Dev Team
2021-12-13 12:12:54 +00:00
*
* 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 "AboutDialog.h"
2022-04-04 11:27:23 +00:00
# include "AutoUpdaterDialog.h"
2022-09-03 11:29:02 +00:00
# include "CoverDownloadDialog.h"
2021-12-13 12:12:54 +00:00
# include "DisplayWidget.h"
# include "GameList/GameListRefreshThread.h"
# include "GameList/GameListWidget.h"
# include "MainWindow.h"
# include "QtHost.h"
# include "QtUtils.h"
2023-05-13 04:30:41 +00:00
# include "SettingWidgetBinder.h"
2023-07-27 09:24:55 +00:00
# include "Settings/AchievementLoginDialog.h"
2021-12-13 12:12:54 +00:00
# include "Settings/ControllerSettingsDialog.h"
# include "Settings/GameListSettingsWidget.h"
# include "Settings/InterfaceSettingsWidget.h"
2022-06-16 00:44:14 +00:00
# include "Tools/InputRecording/InputRecordingViewer.h"
2022-04-04 21:35:08 +00:00
# include "Tools/InputRecording/NewInputRecordingDlg.h"
2023-05-13 04:30:41 +00:00
# include "svnrev.h"
# include "pcsx2/Achievements.h"
# include "pcsx2/CDVD/CDVDcommon.h"
# include "pcsx2/CDVD/CDVDdiscReader.h"
# include "pcsx2/GS.h"
# include "pcsx2/GS/GS.h"
# include "pcsx2/GSDumpReplayer.h"
# include "pcsx2/GameList.h"
# include "pcsx2/Host.h"
# include "pcsx2/LogSink.h"
2023-06-24 07:46:36 +00:00
# include "pcsx2/MTGS.h"
2023-05-13 04:30:41 +00:00
# include "pcsx2/PerformanceMetrics.h"
# include "pcsx2/Recording/InputRecording.h"
# include "pcsx2/Recording/InputRecordingControls.h"
# include "common/Assertions.h"
# include "common/CocoaTools.h"
# include "common/FileSystem.h"
# include <QtCore/QDateTime>
# include <QtGui/QCloseEvent>
# include <QtWidgets/QFileDialog>
# include <QtWidgets/QInputDialog>
# include <QtWidgets/QMessageBox>
# include <QtWidgets/QProgressBar>
# include <QtWidgets/QStyle>
# include <QtWidgets/QStyleFactory>
2022-04-04 00:06:59 +00:00
2022-10-15 11:20:05 +00:00
# ifdef _WIN32
# include "common/RedtapeWindows.h"
# include <Dbt.h>
# endif
2022-12-04 08:03:30 +00:00
const char * MainWindow : : OPEN_FILE_FILTER =
2023-05-13 03:31:24 +00:00
QT_TRANSLATE_NOOP ( " MainWindow " , " All File Types (*.bin *.iso *.cue *.mdf *.chd *.cso *.gz *.elf *.irx *.gs *.gs.xz *.gs.zst *.dump);; "
2022-03-12 13:20:23 +00:00
" Single-Track Raw Images (*.bin *.iso);; "
" Cue Sheets (*.cue);; "
2023-05-13 03:31:24 +00:00
" Media Descriptor File (*.mdf);; "
2022-03-12 13:20:23 +00:00
" 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);; "
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
2023-05-13 03:31:24 +00:00
const char * MainWindow : : DISC_IMAGE_FILTER = QT_TRANSLATE_NOOP ( " MainWindow " , " All File Types (*.bin *.iso *.cue *.mdf *.chd *.cso *.gz *.dump);; "
2022-12-04 08:03:30 +00:00
" Single-Track Raw Images (*.bin *.iso);; "
" Cue Sheets (*.cue);; "
2023-05-13 03:31:24 +00:00
" Media Descriptor File (*.mdf);; "
2022-12-04 08:03:30 +00:00
" MAME CHD Images (*.chd);; "
" CSO Images (*.cso);; "
" GZ Images (*.gz);; "
" Block Dumps (*.dump) " ) ;
2022-06-28 13:34:45 +00:00
2021-12-13 12:12:54 +00:00
MainWindow * g_main_window = nullptr ;
2022-07-09 06:59:13 +00:00
# if defined(_WIN32) || defined(__APPLE__)
static const bool s_use_central_widget = false ;
# else
// Qt Wayland is broken. Any sort of stacked widget usage fails to update,
// leading to broken window resizes, no display rendering, etc. So, we mess
// with the central widget instead. Which we can't do on xorg, because it
// breaks window resizing there...
static bool s_use_central_widget = false ;
# endif
2022-06-28 12:53:26 +00:00
// UI thread VM validity.
static bool s_vm_valid = false ;
static bool s_vm_paused = false ;
2022-11-20 04:26:23 +00:00
MainWindow : : MainWindow ( )
2021-12-13 12:12:54 +00:00
{
pxAssert ( ! g_main_window ) ;
g_main_window = this ;
2022-07-09 06:59:13 +00:00
# if !defined(_WIN32) && !defined(__APPLE__)
s_use_central_widget = DisplayContainer : : isRunningOnWayland ( ) ;
# endif
2021-12-13 12:12:54 +00:00
}
MainWindow : : ~ MainWindow ( )
{
2022-12-20 13:47:09 +00:00
// make sure the game list isn't refreshing, because it's on a separate thread
cancelGameListRefresh ( ) ;
2021-12-13 12:12:54 +00:00
// we compare here, since recreate destroys the window later
if ( g_main_window = = this )
g_main_window = nullptr ;
2022-10-15 11:20:05 +00:00
# ifdef _WIN32
unregisterForDeviceNotifications ( ) ;
# endif
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 22:32:10 +00:00
# ifdef __APPLE__
2022-06-28 13:34:45 +00:00
CocoaTools : : AddThemeChangeHandler ( this , [ ] ( void * ctx ) {
2022-05-25 22:32:10 +00:00
// This handler is called *before* the style change has propagated far enough for Qt to see it
// Use RunOnUIThread to delay until it has
2022-04-18 13:35:14 +00:00
QtHost : : RunOnUIThread ( [ ctx = static_cast < MainWindow * > ( ctx ) ] {
ctx - > updateTheme ( ) ; // Qt won't notice the style change without us touching the palette in some way
2022-05-25 22:32:10 +00:00
} ) ;
} ) ;
# 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 ( ) ;
2023-01-24 21:13:29 +00:00
updateSaveStateMenusEnableState ( false ) ;
2022-10-15 11:20:05 +00:00
# ifdef _WIN32
registerForDeviceNotifications ( ) ;
# endif
2021-12-13 12:12:54 +00:00
}
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 ( ) ) ;
}
}
2022-07-09 06:59:13 +00:00
QWidget * MainWindow : : getContentParent ( )
{
return s_use_central_widget ? static_cast < QWidget * > ( this ) : static_cast < QWidget * > ( m_ui . mainContainer ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : setupAdditionalUi ( )
{
2022-11-23 13:20:49 +00:00
const bool show_advanced_settings = QtHost : : ShouldShowAdvancedSettings ( ) ;
2022-05-25 20:52:07 +00:00
makeIconsMasks ( menuBar ( ) ) ;
2022-11-23 13:20:49 +00:00
m_ui . menuDebug - > menuAction ( ) - > setVisible ( show_advanced_settings ) ;
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-07-09 06:59:13 +00:00
m_game_list_widget = new GameListWidget ( getContentParent ( ) ) ;
2021-12-13 12:12:54 +00:00
m_game_list_widget - > initialize ( ) ;
m_ui . actionGridViewShowTitles - > setChecked ( m_game_list_widget - > getShowGridCoverTitles ( ) ) ;
2022-07-09 06:59:13 +00:00
if ( s_use_central_widget )
{
m_ui . mainContainer = nullptr ; // setCentralWidget() will delete this
setCentralWidget ( m_game_list_widget ) ;
}
else
{
m_ui . mainContainer - > addWidget ( 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 ) ;
2022-07-09 10:41:52 +00:00
m_status_progress_widget - > setMinimum ( 0 ) ;
m_status_progress_widget - > setMaximum ( 100 ) ;
2021-12-13 12:12:54 +00:00
m_status_progress_widget - > hide ( ) ;
2022-07-03 13:30:03 +00:00
m_status_verbose_widget = new QLabel ( m_ui . statusBar ) ;
m_status_verbose_widget - > setSizePolicy ( QSizePolicy : : Expanding , QSizePolicy : : Fixed ) ;
m_status_verbose_widget - > setFixedHeight ( 16 ) ;
m_status_verbose_widget - > hide ( ) ;
m_status_renderer_widget = new QLabel ( m_ui . statusBar ) ;
m_status_renderer_widget - > setFixedHeight ( 16 ) ;
m_status_renderer_widget - > setFixedSize ( 65 , 16 ) ;
m_status_renderer_widget - > hide ( ) ;
m_status_resolution_widget = new QLabel ( m_ui . statusBar ) ;
m_status_resolution_widget - > setFixedHeight ( 16 ) ;
m_status_resolution_widget - > setFixedSize ( 70 , 16 ) ;
m_status_resolution_widget - > hide ( ) ;
2022-04-03 13:46:05 +00:00
m_status_fps_widget = new QLabel ( m_ui . statusBar ) ;
2022-07-03 13:30:03 +00:00
m_status_fps_widget - > setFixedSize ( 85 , 16 ) ;
2022-04-03 13:46:05 +00:00
m_status_fps_widget - > hide ( ) ;
2022-07-03 13:30:03 +00:00
m_status_vps_widget = new QLabel ( m_ui . statusBar ) ;
m_status_vps_widget - > setFixedSize ( 125 , 16 ) ;
m_status_vps_widget - > hide ( ) ;
2023-02-28 18:29:41 +00:00
m_settings_toolbar_menu = new QMenu ( m_ui . toolBar ) ;
m_settings_toolbar_menu - > addAction ( m_ui . actionSettings ) ;
m_settings_toolbar_menu - > addAction ( m_ui . actionViewGameProperties ) ;
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 ) ) ; } ) ;
}
2023-03-03 11:30:40 +00:00
updateEmulationActions ( false , false , false ) ;
2022-09-15 09:43:21 +00:00
updateDisplayRelatedActions ( false , false , false ) ;
2022-04-18 13:35:14 +00:00
# ifdef ENABLE_RAINTEGRATION
if ( Achievements : : IsUsingRAIntegration ( ) )
{
2022-11-23 13:20:49 +00:00
QMenu * raMenu = new QMenu ( QStringLiteral ( " RAIntegration " ) , m_ui . menu_Tools ) ;
2022-04-18 13:35:14 +00:00
connect ( raMenu , & QMenu : : aboutToShow , this , [ this , raMenu ] ( ) {
raMenu - > clear ( ) ;
const auto items = Achievements : : RAIntegration : : GetMenuItems ( ) ;
for ( const auto & [ id , title , checked ] : items )
{
if ( id = = 0 )
{
raMenu - > addSeparator ( ) ;
continue ;
}
QAction * raAction = raMenu - > addAction ( QString : : fromUtf8 ( title ) ) ;
if ( checked )
{
raAction - > setCheckable ( true ) ;
raAction - > setChecked ( checked ) ;
}
2022-12-04 08:03:30 +00:00
connect ( raAction , & QAction : : triggered , this ,
[ id = id ] ( ) { Host : : RunOnCPUThread ( [ id ] ( ) { Achievements : : RAIntegration : : ActivateMenuItem ( id ) ; } , false ) ; } ) ;
2022-04-18 13:35:14 +00:00
}
} ) ;
2022-11-23 13:20:49 +00:00
m_ui . menu_Tools - > insertMenu ( m_ui . menuInput_Recording - > menuAction ( ) , raMenu ) ;
2022-04-18 13:35:14 +00:00
}
# endif
2021-12-13 12:12:54 +00:00
}
void MainWindow : : connectSignals ( )
{
connect ( m_ui . actionStartFile , & QAction : : triggered , this , & MainWindow : : onStartFileActionTriggered ) ;
2022-06-28 13:34:45 +00:00
connect ( m_ui . actionStartDisc , & QAction : : triggered , this , & MainWindow : : onStartDiscActionTriggered ) ;
2021-12-13 12:12:54 +00:00
connect ( m_ui . actionStartBios , & QAction : : triggered , this , & MainWindow : : onStartBIOSActionTriggered ) ;
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 ) ;
2022-06-28 14:00:51 +00:00
connect ( m_ui . actionRemoveDisc , & QAction : : triggered , this , & MainWindow : : onRemoveDiscActionTriggered ) ;
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-15 08:20:21 +00:00
connect ( m_ui . actionPowerOff , & QAction : : triggered , this , [ this ] ( ) { requestShutdown ( true , true , EmuConfig . SaveStateOnShutdown ) ; } ) ;
connect ( m_ui . actionPowerOffWithoutSaving , & QAction : : triggered , this , [ this ] ( ) { requestShutdown ( false , false , false ) ; } ) ;
2023-09-04 11:34:57 +00:00
connect ( m_ui . actionStartFullscreenUI , & QAction : : triggered , this , & MainWindow : : onStartFullscreenUITriggered ) ;
connect ( m_ui . actionToolbarStartFullscreenUI , & QAction : : triggered , this , & MainWindow : : onStartFullscreenUITriggered ) ;
2023-06-30 12:19:09 +00:00
connect ( m_ui . actionToolbarStartFile , & QAction : : triggered , this , & MainWindow : : onStartFileActionTriggered ) ;
connect ( m_ui . actionToolbarStartDisc , & QAction : : triggered , this , & MainWindow : : onStartDiscActionTriggered ) ;
connect ( m_ui . actionToolbarStartBios , & QAction : : triggered , this , & MainWindow : : onStartBIOSActionTriggered ) ;
connect ( m_ui . actionToolbarChangeDisc , & QAction : : triggered , [ this ] { m_ui . menuChangeDisc - > exec ( QCursor : : pos ( ) ) ; } ) ;
connect ( m_ui . actionToolbarPowerOff , & QAction : : triggered , this , [ this ] ( ) { requestShutdown ( true , true , EmuConfig . SaveStateOnShutdown ) ; } ) ;
connect ( m_ui . actionToolbarLoadState , & QAction : : triggered , this , [ this ] ( ) { m_ui . menuLoadState - > exec ( QCursor : : pos ( ) ) ; } ) ;
connect ( m_ui . actionToolbarSaveState , & QAction : : triggered , this , [ this ] ( ) { m_ui . menuSaveState - > exec ( QCursor : : pos ( ) ) ; } ) ;
connect ( m_ui . actionToolbarSettings , & QAction : : triggered , this , & MainWindow : : onSettingsTriggeredFromToolbar ) ;
connect ( m_ui . actionToolbarControllerSettings , & QAction : : triggered ,
[ this ] ( ) { doControllerSettings ( ControllerSettingsDialog : : Category : : GlobalSettings ) ; } ) ;
connect ( m_ui . actionToolbarScreenshot , & QAction : : triggered , this , & MainWindow : : onScreenshotActionTriggered ) ;
2021-12-13 12:12:54 +00:00
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 . 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-04-18 13:35:14 +00:00
connect ( m_ui . actionAchievementSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " Achievements " ) ; } ) ;
2022-12-04 08:03:30 +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 ) ;
2023-01-26 15:25:37 +00:00
connect ( m_ui . actionCheckForUpdates , & QAction : : triggered , this , [ this ] ( ) { checkForUpdates ( true , true ) ; } ) ;
2021-12-13 12:12:54 +00:00
connect ( m_ui . actionOpenDataDirectory , & QAction : : triggered , this , & MainWindow : : onToolsOpenDataDirectoryTriggered ) ;
2022-09-03 11:29:02 +00:00
connect ( m_ui . actionCoverDownloader , & QAction : : triggered , this , & MainWindow : : onToolsCoverDownloaderTriggered ) ;
2021-12-13 12:12:54 +00:00
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 ) ;
2022-07-09 08:52:33 +00:00
connect ( m_game_list_widget , & GameListWidget : : layoutChange , this , [ this ] ( ) {
QSignalBlocker sb ( m_ui . actionGridViewShowTitles ) ;
m_ui . actionGridViewShowTitles - > setChecked ( m_game_list_widget - > getShowGridCoverTitles ( ) ) ;
} ) ;
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 ) ;
# ifndef PCSX2_DEVBUILD
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionEnableVerboseLogging , " Logging " , " EnableVerbose " , false ) ;
# 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 ) ;
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionEnableIOPConsoleLogging , " Logging " , " EnableIOPConsole " , true ) ;
2022-05-24 12:02:05 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionEnableFileLogging , " Logging " , " EnableFileLogging " , false ) ;
2022-05-24 14:15:41 +00:00
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionEnableLogTimestamps , " Logging " , " EnableTimestamps " , true ) ;
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 ) ;
2022-11-23 13:20:49 +00:00
m_ui . actionShowAdvancedSettings - > setChecked ( QtHost : : ShouldShowAdvancedSettings ( ) ) ;
2022-05-24 12:22:15 +00:00
connect ( m_ui . actionSaveBlockDump , & QAction : : toggled , this , & MainWindow : : onBlockDumpActionToggled ) ;
2022-11-23 13:20:49 +00:00
connect ( m_ui . actionShowAdvancedSettings , & QAction : : toggled , this , & MainWindow : : onShowAdvancedSettingsToggled ) ;
2022-05-16 11:10:34 +00:00
connect ( m_ui . actionSaveGSDump , & QAction : : triggered , this , & MainWindow : : onSaveGSDumpActionTriggered ) ;
2022-12-18 13:05:00 +00:00
connect ( m_ui . actionToolsVideoCapture , & QAction : : toggled , this , & MainWindow : : onToolsVideoCaptureToggled ) ;
2022-05-16 11:10:34 +00:00
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 ) ;
SettingWidgetBinder : : BindWidgetToBoolSetting ( nullptr , m_ui . actionInputRecControllerLogs , " Logging " , " EnableControllerLogs " , false ) ;
2022-06-16 00:44:14 +00:00
connect ( m_ui . actionInputRecOpenViewer , & QAction : : triggered , this , & MainWindow : : onInputRecOpenViewer ) ;
2022-04-04 00:06:59 +00:00
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 ) ;
2022-12-04 08:03:30 +00:00
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 )
{
2022-08-20 10:20:36 +00:00
connect ( thread , & EmuThread : : messageConfirmed , this , & MainWindow : : confirmMessage , Qt : : BlockingQueuedConnection ) ;
2023-04-25 12:52:41 +00:00
connect ( thread , & EmuThread : : onAcquireRenderWindowRequested , this , & MainWindow : : acquireRenderWindow , Qt : : BlockingQueuedConnection ) ;
connect ( thread , & EmuThread : : onReleaseRenderWindowRequested , this , & MainWindow : : releaseRenderWindow , Qt : : BlockingQueuedConnection ) ;
connect ( thread , & EmuThread : : onResizeRenderWindowRequested , this , & MainWindow : : displayResizeRequested ) ;
2023-07-23 05:07:46 +00:00
connect ( thread , & EmuThread : : onMouseModeRequested , this , & MainWindow : : mouseModeRequested ) ;
2023-09-04 11:34:57 +00:00
connect ( thread , & EmuThread : : onFullscreenUIStateChange , this , & MainWindow : : onFullscreenUIStateChange ) ;
2021-12-13 12:12:54 +00:00
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 ) ;
2023-07-06 13:18:30 +00:00
connect ( thread , & EmuThread : : onCaptureStarted , this , & MainWindow : : onCaptureStarted ) ;
connect ( thread , & EmuThread : : onCaptureStopped , this , & MainWindow : : onCaptureStopped ) ;
2023-07-27 09:24:55 +00:00
connect ( thread , & EmuThread : : onAchievementsLoginRequested , this , & MainWindow : : onAchievementsLoginRequested ) ;
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 ) ;
2023-06-30 12:19:09 +00:00
connect ( m_ui . actionToolbarReset , & QAction : : triggered , thread , & EmuThread : : resetVM ) ;
connect ( m_ui . actionToolbarPause , & QAction : : toggled , thread , & EmuThread : : setVMPaused ) ;
connect ( m_ui . actionToolbarFullscreen , & QAction : : triggered , thread , & EmuThread : : toggleFullscreen ) ;
2021-12-13 12:12:54 +00:00
connect ( m_ui . actionToggleSoftwareRendering , & QAction : : triggered , thread , & EmuThread : : toggleSoftwareRendering ) ;
2022-12-24 06:51:44 +00:00
connect ( m_ui . actionDebugger , & QAction : : triggered , this , & MainWindow : : openDebugger ) ;
2021-12-13 12:12:54 +00:00
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-12-04 08:03:30 +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-05-15 08:20:21 +00:00
requestShutdown ( false , true , EmuConfig . SaveStateOnShutdown ) ;
2021-12-13 12:12:54 +00:00
2022-10-15 11:20:05 +00:00
// We need to close input sources, because e.g. DInput uses our window handle.
g_emu_thread - > closeInputSources ( ) ;
2021-12-13 12:12:54 +00:00
close ( ) ;
g_main_window = nullptr ;
2022-11-20 04:26:23 +00:00
MainWindow * new_main_window = new MainWindow ( ) ;
2021-12-13 12:12:54 +00:00
new_main_window - > initialize ( ) ;
new_main_window - > refreshGameList ( false ) ;
new_main_window - > show ( ) ;
deleteLater ( ) ;
2022-10-15 11:20:05 +00:00
// Reload the sources we just closed.
g_emu_thread - > reloadInputSources ( ) ;
2021-12-13 12:12:54 +00:00
}
2022-09-07 07:44:10 +00:00
void MainWindow : : recreateSettings ( )
{
QString current_category ;
if ( m_settings_dialog )
{
2022-11-23 13:20:49 +00:00
const bool was_visible = m_settings_dialog - > isVisible ( ) ;
2022-09-07 07:44:10 +00:00
current_category = m_settings_dialog - > getCategory ( ) ;
m_settings_dialog - > hide ( ) ;
m_settings_dialog - > deleteLater ( ) ;
m_settings_dialog = nullptr ;
2022-11-23 13:20:49 +00:00
if ( ! was_visible )
return ;
2022-09-07 07:44:10 +00:00
}
doSettings ( current_category . toUtf8 ( ) . constData ( ) ) ;
}
void MainWindow : : resetSettings ( bool ui )
{
Host : : RequestResetSettings ( false , true , false , false , ui ) ;
if ( ui )
{
// UI reset includes theme (and eventually language).
// Just updating the theme here, when there's no change, causes Qt to get very confused..
// So, we'll just tear down everything and recreate. We'll need to do that for language
// resets eventaully anyway.
recreate ( ) ;
}
// g_main_window here for recreate() case above.
g_main_window - > recreateSettings ( ) ;
}
2022-07-20 05:40:53 +00:00
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 (
2022-12-04 08:03:30 +00:00
QFileDialog : : getExistingDirectory ( this , tr ( " Select location to save block dump: " ) , QString : : fromStdString ( old_directory ) ) ) ;
2022-05-24 12:22:15 +00:00
if ( new_dir . isEmpty ( ) )
{
// disable it again
m_ui . actionSaveBlockDump - > setChecked ( false ) ;
return ;
}
2022-09-07 07:44:10 +00:00
Host : : SetBaseStringSettingValue ( " EmuCore " , " BlockDumpSaveDirectory " , new_dir . toUtf8 ( ) . constData ( ) ) ;
Host : : CommitBaseSettingChanges ( ) ;
2022-10-19 18:36:23 +00:00
g_emu_thread - > applySettings ( ) ;
2022-05-24 12:22:15 +00:00
}
2022-11-23 13:20:49 +00:00
void MainWindow : : onShowAdvancedSettingsToggled ( bool checked )
{
if ( checked & & ! Host : : GetBaseBoolSettingValue ( " UI " , " AdvancedSettingsWarningShown " , false ) )
{
QCheckBox * cb = new QCheckBox ( tr ( " Do not show again " ) ) ;
QMessageBox mb ( this ) ;
mb . setWindowTitle ( tr ( " Show Advanced Settings " ) ) ;
2022-12-04 08:03:30 +00:00
mb . setText ( tr ( " Changing advanced settings can have unpredictable effects on games, including graphical glitches, lock-ups, and "
" even corrupted save files. "
" We do not recommend changing advanced settings unless you know what you are doing, and the implications of changing "
" each setting. \n \n "
" The PCSX2 team will not provide any support for configurations that modify these settings, you are on your own. \n \n "
" Are you sure you want to continue? " ) ) ;
2022-11-23 13:20:49 +00:00
mb . setIcon ( QMessageBox : : Warning ) ;
mb . addButton ( QMessageBox : : Yes ) ;
mb . addButton ( QMessageBox : : No ) ;
mb . setDefaultButton ( QMessageBox : : No ) ;
mb . setCheckBox ( cb ) ;
if ( mb . exec ( ) = = QMessageBox : : No )
{
QSignalBlocker sb ( m_ui . actionShowAdvancedSettings ) ;
m_ui . actionShowAdvancedSettings - > setChecked ( false ) ;
return ;
}
if ( cb - > isChecked ( ) )
{
Host : : SetBaseBoolSettingValue ( " UI " , " AdvancedSettingsWarningShown " , true ) ;
Host : : CommitBaseSettingChanges ( ) ;
}
}
Host : : SetBaseBoolSettingValue ( " UI " , " ShowAdvancedSettings " , checked ) ;
Host : : CommitBaseSettingChanges ( ) ;
m_ui . menuDebug - > menuAction ( ) - > setVisible ( checked ) ;
// just recreate the entire settings window, it's easier.
if ( m_settings_dialog )
recreateSettings ( ) ;
}
2022-12-18 13:05:00 +00:00
void MainWindow : : onToolsVideoCaptureToggled ( bool checked )
{
if ( ! s_vm_valid )
return ;
2023-07-06 13:18:30 +00:00
// Reset the checked state, we'll get updated by the GS thread.
QSignalBlocker sb ( m_ui . actionToolsVideoCapture ) ;
m_ui . actionToolsVideoCapture - > setChecked ( ! checked ) ;
2022-12-18 13:05:00 +00:00
if ( ! checked )
{
g_emu_thread - > endCapture ( ) ;
return ;
}
const QString container ( QString : : fromStdString (
2023-01-19 14:01:54 +00:00
Host : : GetStringSettingValue ( " EmuCore/GS " , " CaptureContainer " , Pcsx2Config : : GSOptions : : DEFAULT_CAPTURE_CONTAINER ) ) ) ;
2022-12-18 13:05:00 +00:00
const QString filter ( tr ( " %1 Files (*.%2) " ).arg(container.toUpper()).arg(container)) ;
2023-01-12 09:30:39 +00:00
QString path ( QStringLiteral ( " %1.%2 " ) . arg ( QString : : fromStdString ( GSGetBaseVideoFilename ( ) ) ) . arg ( container ) ) ;
2022-12-18 13:05:00 +00:00
path = QFileDialog : : getSaveFileName ( this , tr ( " Video Capture " ) , path , filter ) ;
if ( path . isEmpty ( ) )
return ;
g_emu_thread - > beginCapture ( path ) ;
}
2023-07-06 13:18:30 +00:00
void MainWindow : : onCaptureStarted ( const QString & filename )
{
if ( ! s_vm_valid )
return ;
QSignalBlocker sb ( m_ui . actionToolsVideoCapture ) ;
m_ui . actionToolsVideoCapture - > setChecked ( true ) ;
}
void MainWindow : : onCaptureStopped ( )
{
if ( ! s_vm_valid )
return ;
QSignalBlocker sb ( m_ui . actionToolsVideoCapture ) ;
m_ui . actionToolsVideoCapture - > setChecked ( false ) ;
}
2023-07-27 09:24:55 +00:00
void MainWindow : : onAchievementsLoginRequested ( Achievements : : LoginRequestReason reason )
{
auto lock = pauseAndLockVM ( ) ;
AchievementLoginDialog dlg ( this , reason ) ;
dlg . exec ( ) ;
}
2023-02-28 18:29:41 +00:00
void MainWindow : : onSettingsTriggeredFromToolbar ( )
{
if ( s_vm_valid )
{
m_settings_toolbar_menu - > exec ( QCursor : : pos ( ) ) ;
}
else
{
doSettings ( ) ;
}
}
2021-12-13 12:12:54 +00:00
void MainWindow : : saveStateToConfig ( )
{
2022-06-29 02:44:10 +00:00
if ( ! isVisible ( ) )
return ;
2023-02-18 03:19:25 +00:00
bool changed = false ;
const QByteArray geometry ( saveGeometry ( ) ) ;
const QByteArray geometry_b64 ( geometry . toBase64 ( ) ) ;
const std : : string old_geometry_b64 ( Host : : GetBaseStringSettingValue ( " UI " , " MainWindowGeometry " ) ) ;
if ( old_geometry_b64 ! = geometry_b64 . constData ( ) )
2021-12-13 12:12:54 +00:00
{
2023-02-18 03:19:25 +00:00
Host : : SetBaseStringSettingValue ( " UI " , " MainWindowGeometry " , geometry_b64 . constData ( ) ) ;
changed = true ;
2021-12-13 12:12:54 +00:00
}
2023-02-18 03:19:25 +00:00
const QByteArray state ( saveState ( ) ) ;
const QByteArray state_b64 ( state . toBase64 ( ) ) ;
const std : : string old_state_b64 ( Host : : GetBaseStringSettingValue ( " UI " , " MainWindowState " ) ) ;
if ( old_state_b64 ! = state_b64 . constData ( ) )
2021-12-13 12:12:54 +00:00
{
2023-02-18 03:19:25 +00:00
Host : : SetBaseStringSettingValue ( " UI " , " MainWindowState " , state_b64 . constData ( ) ) ;
changed = true ;
2021-12-13 12:12:54 +00:00
}
2023-02-18 03:19:25 +00:00
if ( changed )
Host : : CommitBaseSettingChanges ( ) ;
2021-12-13 12:12:54 +00:00
}
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 ( ) ) ;
}
}
}
2023-03-03 11:30:40 +00:00
void MainWindow : : updateEmulationActions ( bool starting , bool running , bool stopping )
2021-12-13 12:12:54 +00:00
{
const bool starting_or_running = starting | | running ;
2023-03-03 11:30:40 +00:00
m_ui . actionStartFile - > setDisabled ( starting_or_running | | stopping ) ;
m_ui . actionStartDisc - > setDisabled ( starting_or_running | | stopping ) ;
m_ui . actionStartBios - > setDisabled ( starting_or_running | | stopping ) ;
2023-06-30 12:19:09 +00:00
m_ui . actionToolbarStartFile - > setDisabled ( starting_or_running | | stopping ) ;
m_ui . actionToolbarStartDisc - > setDisabled ( starting_or_running | | stopping ) ;
m_ui . actionToolbarStartBios - > setDisabled ( starting_or_running | | stopping ) ;
2023-09-04 11:34:57 +00:00
m_ui . actionStartFullscreenUI - > setDisabled ( starting_or_running | | stopping ) ;
m_ui . actionToolbarStartFullscreenUI - > setDisabled ( starting_or_running | | stopping ) ;
2021-12-13 12:12:54 +00:00
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 . actionScreenshot - > setEnabled ( running ) ;
m_ui . menuChangeDisc - > setEnabled ( running ) ;
m_ui . menuSaveState - > setEnabled ( running ) ;
2023-06-30 12:19:09 +00:00
m_ui . actionToolbarPowerOff - > setEnabled ( running ) ;
m_ui . actionToolbarReset - > setEnabled ( running ) ;
m_ui . actionToolbarPause - > setEnabled ( running ) ;
m_ui . actionToolbarScreenshot - > setEnabled ( running ) ;
m_ui . actionToolbarChangeDisc - > setEnabled ( running ) ;
m_ui . actionToolbarSaveState - > setEnabled ( running ) ;
2021-12-13 12:12:54 +00:00
m_ui . actionViewGameProperties - > setEnabled ( running ) ;
2022-12-18 13:05:00 +00:00
m_ui . actionToolsVideoCapture - > setEnabled ( running ) ;
if ( ! running & & m_ui . actionToolsVideoCapture - > isChecked ( ) )
2023-04-01 03:13:21 +00:00
{
QSignalBlocker sb ( m_ui . actionToolsVideoCapture ) ;
2022-12-18 13:05:00 +00:00
m_ui . actionToolsVideoCapture - > setChecked ( false ) ;
2023-04-01 03:13:21 +00:00
}
2022-12-18 13:05:00 +00:00
2021-12-13 12:12:54 +00:00
m_game_list_widget - > setDisabled ( starting & & ! running ) ;
2022-05-06 11:03:16 +00:00
if ( ! starting & & ! running )
2023-04-01 03:13:21 +00:00
{
2023-06-30 12:19:09 +00:00
{
QSignalBlocker sb ( m_ui . actionPause ) ;
m_ui . actionPause - > setChecked ( false ) ;
}
{
QSignalBlocker sb ( m_ui . actionToolbarPause ) ;
m_ui . actionToolbarPause - > setChecked ( false ) ;
}
2023-04-01 03:13:21 +00:00
}
2022-05-22 14:12:21 +00:00
// scanning needs to be disabled while running
2023-03-03 11:30:40 +00:00
m_ui . actionScanForNewGames - > setDisabled ( starting_or_running | | stopping ) ;
m_ui . actionRescanAllGames - > setDisabled ( starting_or_running | | stopping ) ;
2021-12-13 12:12:54 +00:00
}
2022-09-15 09:43:21 +00:00
void MainWindow : : updateDisplayRelatedActions ( bool has_surface , bool render_to_main , bool fullscreen )
{
// rendering to main, or switched to gamelist/grid
2023-06-24 07:46:36 +00:00
m_ui . actionViewSystemDisplay - > setEnabled ( ( has_surface & & render_to_main ) | | ( ! has_surface & & MTGS : : IsOpen ( ) ) ) ;
2022-09-15 09:43:21 +00:00
m_ui . menuWindowSize - > setEnabled ( has_surface & & ! fullscreen ) ;
m_ui . actionFullscreen - > setEnabled ( has_surface ) ;
2023-06-30 12:19:09 +00:00
m_ui . actionToolbarFullscreen - > setEnabled ( has_surface ) ;
2022-09-15 09:43:21 +00:00
{
QSignalBlocker blocker ( m_ui . actionFullscreen ) ;
m_ui . actionFullscreen - > setChecked ( fullscreen ) ;
}
2023-06-30 12:19:09 +00:00
{
QSignalBlocker blocker ( m_ui . actionToolbarFullscreen ) ;
m_ui . actionToolbarFullscreen - > setChecked ( fullscreen ) ;
}
2022-09-15 09:43:21 +00:00
}
2022-04-03 13:46:05 +00:00
void MainWindow : : updateStatusBarWidgetVisibility ( )
{
2022-06-28 13:34:45 +00:00
auto Update = [ this ] ( QWidget * widget , bool visible , int stretch ) {
2022-04-03 13:46:05 +00:00
if ( widget - > isVisible ( ) )
{
m_ui . statusBar - > removeWidget ( widget ) ;
widget - > hide ( ) ;
}
if ( visible )
{
m_ui . statusBar - > addPermanentWidget ( widget , stretch ) ;
widget - > show ( ) ;
}
} ;
2022-07-03 13:30:03 +00:00
Update ( m_status_verbose_widget , s_vm_valid , 1 ) ;
Update ( m_status_renderer_widget , s_vm_valid , 0 ) ;
Update ( m_status_resolution_widget , s_vm_valid , 0 ) ;
2022-06-28 12:53:26 +00:00
Update ( m_status_fps_widget , s_vm_valid , 0 ) ;
2022-07-03 13:30:03 +00:00
Update ( m_status_vps_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 ) ;
2023-06-13 12:43:11 +00:00
QString display_title ( m_current_title + suffix ) ;
2022-05-06 11:03:16 +00:00
2023-06-13 12:43:11 +00:00
if ( ! s_vm_valid | | m_current_title . 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
{
2022-06-29 02:44:10 +00:00
// Skip all of this when we're closing, since we don't want to make ourselves visible and cancel it.
if ( m_is_closing )
return ;
2022-07-12 03:11:56 +00:00
const bool hide_window = ! isRenderingToMain ( ) & & shouldHideMainWindow ( ) ;
2022-10-22 03:43:12 +00:00
const bool disable_resize = Host : : GetBoolSettingValue ( " 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 )
{
2022-07-18 07:47:57 +00:00
const int value = ( total ! = 0 ) ? ( ( current * 100 ) / total ) : 0 ;
2022-07-09 10:41:52 +00:00
if ( m_status_progress_widget - > value ( ) ! = value )
m_status_progress_widget - > setValue ( value ) ;
2021-12-13 12:12:54 +00:00
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-07-09 06:59:13 +00:00
if ( s_use_central_widget )
return ( centralWidget ( ) = = m_game_list_widget ) ;
else
return ( m_ui . mainContainer - > currentIndex ( ) = = 0 ) ;
2022-05-06 11:03:16 +00:00
}
bool MainWindow : : isRenderingFullscreen ( ) const
{
2023-06-24 07:46:36 +00:00
if ( ! MTGS : : IsOpen ( ) | | ! m_display_widget )
2022-05-06 11:03:16 +00:00
return false ;
2023-04-01 05:54:04 +00:00
return getDisplayContainer ( ) - > isFullScreen ( ) ;
2022-05-06 11:03:16 +00:00
}
bool MainWindow : : isRenderingToMain ( ) const
{
2022-07-09 06:59:13 +00:00
if ( s_use_central_widget )
return ( m_display_widget & & centralWidget ( ) = = m_display_widget ) ;
else
return ( m_display_widget & & m_ui . mainContainer - > indexOf ( m_display_widget ) = = 1 ) ;
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
{
2023-07-23 05:07:46 +00:00
return ( ( isRenderingFullscreen ( ) & & Host : : GetBoolSettingValue ( " UI " , " HideMouseCursor " , false ) ) | |
m_relative_mouse_mode | | m_hide_mouse_cursor ) ;
2022-05-15 08:15:27 +00:00
}
2022-07-09 09:14:48 +00:00
bool MainWindow : : shouldHideMainWindow ( ) const
{
2022-10-01 15:14:39 +00:00
// NOTE: We can't use isRenderingToMain() here, because this happens post-fullscreen-switch.
2022-10-22 03:43:12 +00:00
return Host : : GetBoolSettingValue ( " UI " , " HideMainWindowWhenRunning " , false ) | |
2022-12-04 08:03:30 +00:00
( g_emu_thread - > shouldRenderToMain ( ) & & isRenderingFullscreen ( ) ) | | QtHost : : InNoGUIMode ( ) ;
2022-07-09 09:14:48 +00:00
}
2021-12-13 12:12:54 +00:00
void MainWindow : : switchToGameListView ( )
{
2022-07-09 06:59:13 +00:00
if ( isShowingGameList ( ) )
2022-05-06 11:03:16 +00:00
{
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-05-15 08:20:21 +00:00
if ( m_display_created )
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
}
}
void MainWindow : : switchToEmulationView ( )
{
2022-05-15 08:20:21 +00:00
if ( ! m_display_created | | ! isShowingGameList ( ) )
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 : : reportError ( const QString & title , const QString & message )
{
QMessageBox : : critical ( this , title , message ) ;
}
2021-12-13 12:12:54 +00:00
2022-08-20 10:20:36 +00:00
bool MainWindow : : confirmMessage ( const QString & title , const QString & message )
{
VMLock lock ( pauseAndLockVM ( ) ) ;
return ( QMessageBox : : question ( this , title , message ) = = QMessageBox : : Yes ) ;
}
2022-04-06 10:49:00 +00:00
void MainWindow : : runOnUIThread ( const std : : function < void ( ) > & func )
{
func ( ) ;
}
2023-01-28 03:33:51 +00:00
bool MainWindow : : requestShutdown ( bool allow_confirm , bool allow_save_to_state , bool default_save_to_state )
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.
2023-06-13 12:43:11 +00:00
allow_save_to_state & = ( m_current_disc_crc ! = 0 ) ;
2022-05-15 08:20:21 +00:00
bool save_state = allow_save_to_state & & default_save_to_state ;
2022-05-07 12:56:44 +00:00
2022-06-20 08:03:38 +00:00
// Only confirm on UI thread because we need to display a msgbox.
2022-10-22 03:43:12 +00:00
if ( ! m_is_closing & & allow_confirm & & ! GSDumpReplayer : : IsReplayingDump ( ) & & Host : : GetBoolSettingValue ( " 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 " ) ) ;
2022-10-31 12:05:47 +00:00
msgbox . setText ( tr ( " Are you sure you want to shut down the virtual machine? " ) ) ;
2022-05-07 12:56:44 +00:00
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.
2022-10-22 10:31:18 +00:00
if ( ! isRenderingToMain ( ) & & isHidden ( ) & & ! QtHost : : InBatchMode ( ) & & ! g_emu_thread - > isRunningFullscreenUI ( ) )
2022-06-28 12:16:45 +00:00
updateWindowState ( true ) ;
2022-06-28 11:37:30 +00:00
2023-03-03 11:30:40 +00:00
// Clear the VM valid state early. That way we can't do anything in the UI if we take a while to shut down.
if ( s_vm_valid )
{
s_vm_valid = false ;
updateEmulationActions ( false , false , true ) ;
updateDisplayRelatedActions ( false , false , false ) ;
}
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
return true ;
2022-03-04 10:40:03 +00:00
}
2023-01-28 03:33:51 +00:00
void MainWindow : : requestExit ( bool allow_confirm )
2022-03-04 10:40:03 +00:00
{
2023-04-02 09:26:25 +00:00
// requestShutdown() clears this flag.
const bool vm_was_valid = QtHost : : IsVMValid ( ) ;
2022-03-25 10:14:23 +00:00
// this is block, because otherwise closeEvent() will also prompt
2023-01-28 03:33:51 +00:00
if ( ! requestShutdown ( allow_confirm , true , EmuConfig . SaveStateOnShutdown ) )
2022-03-04 10:40:03 +00:00
return ;
2023-01-28 03:33:51 +00:00
// VM stopped signal won't have fired yet, so queue an exit if we still have one.
// Otherwise, immediately exit, because there's no VM to exit us later.
2023-04-02 09:26:25 +00:00
if ( vm_was_valid )
2023-01-28 03:33:51 +00:00
m_is_closing = true ;
else
QGuiApplication : : quit ( ) ;
2022-03-04 10:40:03 +00:00
}
2022-05-15 08:15:27 +00:00
void MainWindow : : checkForSettingChanges ( )
{
if ( m_display_widget )
2022-12-04 15:00:06 +00:00
updateDisplayWidgetCursor ( ) ;
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-10-15 09:03:52 +00:00
std : : optional < WindowInfo > MainWindow : : getWindowInfo ( )
{
2023-01-02 02:41:49 +00:00
if ( ! m_display_widget | | isRenderingToMain ( ) )
2022-12-30 04:51:40 +00:00
return QtUtils : : GetWindowInfoForWidget ( this ) ;
else if ( QWidget * widget = getDisplayContainer ( ) )
return QtUtils : : GetWindowInfoForWidget ( widget ) ;
else
return std : : nullopt ;
2022-10-15 09:03:52 +00:00
}
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 ( )
{
2023-01-31 20:54:29 +00:00
m_ui . statusBar - > clearMessage ( ) ;
2022-03-12 14:52:52 +00:00
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-06-28 13:34:45 +00:00
if ( ! entry - > IsDisc ( ) )
{
QMessageBox : : critical ( this , tr ( " Error " ) , tr ( " You must select a disc to change discs. " ) ) ;
return ;
}
doDiscChange ( CDVD_SourceType : : Iso , 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-12-04 08:03:30 +00:00
const std : : optional < bool > resume =
promptForResumeState ( QString : : fromStdString ( VMManager : : GetSaveStateFileName ( entry - > serial . c_str ( ) , entry - > crc , - 1 ) ) ) ;
2022-05-07 12:56:44 +00:00
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-12-04 08:03:30 +00:00
action - > setEnabled ( ! entry - > serial . empty ( ) | | entry - > type = = GameList : : EntryType : : ELF ) ;
2022-02-15 14:29:18 +00:00
if ( action - > isEnabled ( ) )
2022-12-04 08:03:30 +00:00
{
connect ( action , & QAction : : triggered , [ entry ] ( ) {
2023-07-20 09:12:17 +00:00
SettingsDialog : : openGamePropertiesDialog ( entry , entry - > title ,
( entry - > type ! = GameList : : EntryType : : ELF ) ? entry - > serial : std : : string ( ) ,
entry - > crc ) ;
2022-12-04 08:03:30 +00:00
} ) ;
}
2021-12-13 12:12:54 +00:00
2023-03-18 21:14:55 +00:00
//: Refers to the directory where a game is contained.
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 ) ; } ) ;
2022-12-04 08:03:30 +00:00
connect ( menu . addAction ( tr ( " Reset Play Time " ) ) , & QAction : : triggered , [ this , entry ] ( ) { clearGameListEntryPlayTime ( entry ) ; } ) ;
2022-11-22 15:03:36 +00:00
2021-12-13 12:12:54 +00:00
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 ( ) )
{
action = menu . addAction ( tr ( " Boot and Debug " ) ) ;
2023-04-25 10:24:22 +00:00
connect ( action , & QAction : : triggered , [ this , entry ] ( ) {
DebugInterface : : setPauseOnEntry ( true ) ;
startGameListEntry ( entry ) ;
getDebuggerWindow ( ) - > show ( ) ;
} ) ;
2021-12-13 12:12:54 +00:00
}
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
}
2022-06-28 13:34:45 +00:00
else if ( entry - > IsDisc ( ) )
2021-12-13 12:12:54 +00:00
{
action = menu . addAction ( tr ( " Change Disc " ) ) ;
connect ( action , & QAction : : triggered , [ this , entry ] ( ) {
2022-06-28 13:34:45 +00:00
g_emu_thread - > changeDisc ( CDVD_SourceType : : Iso , QString : : fromStdString ( entry - > path ) ) ;
2021-12-13 12:12:54 +00:00
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-12-04 08:03:30 +00:00
const QString path (
QDir : : toNativeSeparators ( QFileDialog : : getOpenFileName ( this , tr ( " Start File " ) , QString ( ) , tr ( OPEN_FILE_FILTER ) , nullptr ) ) ) ;
2022-06-28 13:34:45 +00:00
if ( path . isEmpty ( ) )
return ;
doStartFile ( std : : nullopt , path ) ;
}
void MainWindow : : onStartDiscActionTriggered ( )
{
QString path ( getDiscDevicePath ( tr ( " Start Disc " ) ) ) ;
2022-05-26 07:49:40 +00:00
if ( path . isEmpty ( ) )
2021-12-13 12:12:54 +00:00
return ;
2022-06-28 13:34:45 +00:00
doStartFile ( CDVD_SourceType : : Disc , 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 ( ) ) ;
2022-12-04 08:03:30 +00:00
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 ;
2022-06-28 13:34:45 +00:00
g_emu_thread - > changeDisc ( CDVD_SourceType : : Iso , filename ) ;
2021-12-13 12:12:54 +00:00
}
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 ( )
{
2022-06-28 13:34:45 +00:00
QString path ( getDiscDevicePath ( tr ( " Change Disc " ) ) ) ;
if ( path . isEmpty ( ) )
return ;
g_emu_thread - > changeDisc ( CDVD_SourceType : : Disc , path ) ;
2021-12-13 12:12:54 +00:00
}
2022-06-28 14:00:51 +00:00
void MainWindow : : onRemoveDiscActionTriggered ( )
{
g_emu_thread - > changeDisc ( CDVD_SourceType : : NoDisc , QString ( ) ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : onChangeDiscMenuAboutToShow ( )
{
// TODO: This is where we would populate the playlist if there is one.
}
2022-12-04 08:03:30 +00:00
void MainWindow : : onChangeDiscMenuAboutToHide ( )
{
}
2021-12-13 12:12:54 +00:00
void MainWindow : : onLoadStateMenuAboutToShow ( )
{
2023-01-27 17:31:30 +00:00
m_ui . menuLoadState - > clear ( ) ;
2023-06-13 12:43:11 +00:00
updateSaveStateMenusEnableState ( ! m_current_disc_serial . isEmpty ( ) ) ;
populateLoadStateMenu ( m_ui . menuLoadState , m_current_disc_path , m_current_disc_serial , m_current_disc_crc ) ;
2021-12-13 12:12:54 +00:00
}
void MainWindow : : onSaveStateMenuAboutToShow ( )
{
2023-01-27 17:31:30 +00:00
m_ui . menuSaveState - > clear ( ) ;
2023-06-13 12:43:11 +00:00
updateSaveStateMenusEnableState ( ! m_current_disc_serial . isEmpty ( ) ) ;
populateSaveStateMenu ( m_ui . menuSaveState , m_current_disc_serial , m_current_disc_crc ) ;
2021-12-13 12:12:54 +00:00
}
2023-09-04 11:34:57 +00:00
void MainWindow : : onStartFullscreenUITriggered ( )
{
if ( m_display_widget )
g_emu_thread - > stopFullscreenUI ( ) ;
else
g_emu_thread - > startFullscreenUI ( Host : : GetBaseBoolSettingValue ( " UI " , " StartFullscreen " , false ) ) ;
}
void MainWindow : : onFullscreenUIStateChange ( bool running )
{
m_ui . actionStartFullscreenUI - > setText ( running ? tr ( " Stop Big Picture Mode " ) : tr ( " Start Big Picture Mode " ) ) ;
m_ui . actionToolbarStartFullscreenUI - > setText ( running ? tr ( " Exit Big Picture " , " In Toolbar " ) : tr ( " Big Picture " , " In Toolbar " ) ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : onViewToolbarActionToggled ( bool checked )
{
2022-09-07 07:44:10 +00:00
Host : : SetBaseBoolSettingValue ( " UI " , " ShowToolbar " , checked ) ;
Host : : CommitBaseSettingChanges ( ) ;
2021-12-13 12:12:54 +00:00
m_ui . toolBar - > setVisible ( checked ) ;
}
void MainWindow : : onViewLockToolbarActionToggled ( bool checked )
{
2022-09-07 07:44:10 +00:00
Host : : SetBaseBoolSettingValue ( " UI " , " LockToolbar " , checked ) ;
Host : : CommitBaseSettingChanges ( ) ;
2021-12-13 12:12:54 +00:00
m_ui . toolBar - > setMovable ( ! checked ) ;
}
void MainWindow : : onViewStatusBarActionToggled ( bool checked )
{
2022-09-07 07:44:10 +00:00
Host : : SetBaseBoolSettingValue ( " UI " , " ShowStatusBar " , checked ) ;
Host : : CommitBaseSettingChanges ( ) ;
2021-12-13 12:12:54 +00:00
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-05-15 08:20:21 +00:00
if ( m_display_created )
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
2023-07-20 09:12:17 +00:00
if ( ! m_current_disc_path . isEmpty ( ) | | ! m_current_elf_override . isEmpty ( ) )
2022-02-15 14:29:18 +00:00
{
auto lock = GameList : : GetLock ( ) ;
2023-07-20 09:12:17 +00:00
const QString & path = ( m_current_elf_override . isEmpty ( ) ? m_current_disc_path : m_current_elf_override ) ;
const GameList : : Entry * entry = GameList : : GetEntryForPath ( path . toUtf8 ( ) . constData ( ) ) ;
2022-02-15 14:29:18 +00:00
if ( entry )
{
2023-07-20 09:12:17 +00:00
SettingsDialog : : openGamePropertiesDialog (
entry , entry - > title , m_current_elf_override . isEmpty ( ) ? entry - > serial : std : : string ( ) , 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)
2023-06-13 12:43:11 +00:00
if ( m_current_disc_crc = = 0 )
{
QMessageBox : : critical ( this , tr ( " Game Properties " ) , tr ( " Game properties is unavailable for the current game. " ) ) ;
return ;
}
// can't use serial for ELFs, because they might have a disc set
if ( m_current_elf_override . isEmpty ( ) )
2023-07-20 09:12:17 +00:00
{
SettingsDialog : : openGamePropertiesDialog (
nullptr , m_current_title . toStdString ( ) , m_current_disc_serial . toStdString ( ) , m_current_disc_crc ) ;
}
2023-06-13 12:43:11 +00:00
else
2023-07-20 09:12:17 +00:00
{
SettingsDialog : : openGamePropertiesDialog (
nullptr , m_current_title . toStdString ( ) , std : : string ( ) , m_current_disc_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 ( ) ;
}
2023-01-26 15:25:37 +00:00
void MainWindow : : checkForUpdates ( bool display_message , bool force_check )
2022-04-04 11:27:23 +00:00
{
if ( ! AutoUpdaterDialog : : isSupported ( ) )
{
if ( display_message )
{
QMessageBox mbox ( this ) ;
mbox . setWindowTitle ( tr ( " Updater Error " ) ) ;
mbox . setTextFormat ( Qt : : RichText ) ;
QString message ;
# ifdef _WIN32
2022-12-04 08:03:30 +00:00
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> " ) ;
2022-04-04 11:27:23 +00:00
# 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 ;
2023-01-26 15:25:37 +00:00
if ( force_check )
{
// Wipe out the last version, that way it displays the update if we've previously skipped it.
Host : : RemoveBaseSettingValue ( " AutoUpdater " , " LastVersion " ) ;
Host : : CommitBaseSettingChanges ( ) ;
}
2022-04-04 11:27:23 +00:00
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 ;
2023-01-26 15:25:37 +00:00
checkForUpdates ( false , false ) ;
2022-04-04 11:27:23 +00:00
}
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 ) ) ;
}
2022-09-03 11:29:02 +00:00
void MainWindow : : onToolsCoverDownloaderTriggered ( )
{
CoverDownloadDialog dlg ( this ) ;
connect ( & dlg , & CoverDownloadDialog : : coverRefreshRequested , m_game_list_widget , & GameListWidget : : refreshGridCovers ) ;
dlg . exec ( ) ;
}
2022-07-24 13:52:22 +00:00
void MainWindow : : updateTheme ( )
2021-12-13 12:12:54 +00:00
{
2023-04-25 10:24:22 +00:00
QtHost : : UpdateApplicationTheme ( ) ;
2023-09-09 03:30:53 +00:00
m_game_list_widget - > reloadThemeSpecificImages ( ) ;
2021-12-13 12:12:54 +00:00
}
2023-06-19 12:03:10 +00:00
void MainWindow : : updateLanguage ( )
{
QtHost : : InstallTranslator ( ) ;
recreate ( ) ;
}
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 )
{
2022-06-23 21:29:45 +00:00
g_emu_thread - > setVMPaused ( true ) ;
2022-04-04 00:06:59 +00:00
}
NewInputRecordingDlg dlg ( this ) ;
const auto result = dlg . exec ( ) ;
if ( result = = QDialog : : Accepted )
{
2022-12-04 08:03:30 +00:00
Host : : RunOnCPUThread (
[ & , filePath = dlg . getFilePath ( ) , fromSavestate = dlg . getInputRecType ( ) = = InputRecording : : Type : : FROM_SAVESTATE ,
authorName = dlg . getAuthorName ( ) ] ( ) {
if ( g_InputRecording . create ( filePath , fromSavestate , authorName ) )
{
QtHost : : RunOnUIThread ( [ & ] ( ) {
m_ui . actionInputRecNew - > setEnabled ( false ) ;
m_ui . actionInputRecStop - > setEnabled ( true ) ;
m_ui . actionReset - > setEnabled ( ! g_InputRecording . isTypeSavestate ( ) ) ;
2023-06-30 12:19:09 +00:00
m_ui . actionToolbarReset - > setEnabled ( ! g_InputRecording . isTypeSavestate ( ) ) ;
2022-12-04 08:03:30 +00:00
} ) ;
}
} ) ;
2022-04-04 00:06:59 +00:00
}
if ( wasRunning & & ! wasPaused )
{
2022-06-23 21:29:45 +00:00
g_emu_thread - > setVMPaused ( false ) ;
2022-04-04 00:06:59 +00:00
}
}
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 )
2022-06-16 00:44:14 +00:00
{
2022-06-23 21:29:45 +00:00
g_emu_thread - > setVMPaused ( true ) ;
2022-06-16 00:44:14 +00:00
}
2022-04-04 00:06:59 +00:00
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 ( ) ;
}
2022-06-16 00:44:14 +00:00
else
{
if ( ! wasPaused )
{
2022-06-23 21:29:45 +00:00
g_emu_thread - > setVMPaused ( false ) ;
2022-06-16 00:44:14 +00:00
return ;
}
}
2022-04-04 00:06:59 +00:00
if ( fileNames . length ( ) > 0 )
{
2022-06-16 00:44:14 +00:00
if ( g_InputRecording . isActive ( ) )
2022-04-04 00:06:59 +00:00
{
2022-12-04 08:03:30 +00:00
Host : : RunOnCPUThread ( [ ] ( ) { g_InputRecording . stop ( ) ; } ) ;
2022-06-16 00:44:14 +00:00
m_ui . actionInputRecStop - > setEnabled ( false ) ;
2022-04-04 00:06:59 +00:00
}
2022-06-23 21:29:45 +00:00
Host : : RunOnCPUThread ( [ & , filename = fileNames . first ( ) . toStdString ( ) ] ( ) {
if ( g_InputRecording . play ( filename ) )
{
QtHost : : RunOnUIThread ( [ & ] ( ) {
2022-09-23 18:06:38 +00:00
m_ui . actionInputRecNew - > setEnabled ( false ) ;
2022-06-23 21:29:45 +00:00
m_ui . actionInputRecStop - > setEnabled ( true ) ;
2022-09-23 18:06:38 +00:00
m_ui . actionReset - > setEnabled ( ! g_InputRecording . isTypeSavestate ( ) ) ;
2023-06-30 12:19:09 +00:00
m_ui . actionToolbarReset - > setEnabled ( ! g_InputRecording . isTypeSavestate ( ) ) ;
2022-06-23 21:29:45 +00:00
} ) ;
}
} ) ;
2022-04-04 00:06:59 +00:00
}
}
void MainWindow : : onInputRecStopActionTriggered ( )
{
2022-06-16 00:44:14 +00:00
if ( g_InputRecording . isActive ( ) )
2022-04-04 00:06:59 +00:00
{
2022-06-23 21:29:45 +00:00
Host : : RunOnCPUThread ( [ & ] ( ) {
g_InputRecording . stop ( ) ;
QtHost : : RunOnUIThread ( [ & ] ( ) {
m_ui . actionInputRecNew - > setEnabled ( true ) ;
m_ui . actionInputRecStop - > setEnabled ( false ) ;
2022-09-23 18:06:38 +00:00
m_ui . actionReset - > setEnabled ( true ) ;
2023-06-30 12:19:09 +00:00
m_ui . actionToolbarReset - > setEnabled ( true ) ;
2022-06-23 21:29:45 +00:00
} ) ;
} ) ;
2022-04-04 00:06:59 +00:00
}
}
void MainWindow : : onInputRecOpenSettingsTriggered ( )
{
// TODO - Vaser - Implement
}
2022-06-16 00:44:14 +00:00
InputRecordingViewer * MainWindow : : getInputRecordingViewer ( )
{
if ( ! m_input_recording_viewer )
{
m_input_recording_viewer = new InputRecordingViewer ( this ) ;
}
return m_input_recording_viewer ;
}
void MainWindow : : updateInputRecordingActions ( bool started )
{
m_ui . actionInputRecNew - > setEnabled ( started ) ;
m_ui . actionInputRecPlay - > setEnabled ( started ) ;
}
void MainWindow : : onInputRecOpenViewer ( )
{
InputRecordingViewer * viewer = getInputRecordingViewer ( ) ;
if ( ! viewer - > isVisible ( ) )
{
viewer - > show ( ) ;
}
}
2021-12-13 12:12:54 +00:00
void MainWindow : : onVMStarting ( )
{
2022-06-28 12:53:26 +00:00
s_vm_valid = true ;
2023-03-03 11:30:40 +00:00
updateEmulationActions ( true , false , false ) ;
2021-12-13 12:12:54 +00:00
updateWindowTitle ( ) ;
// prevent loading state until we're fully initialized
2023-01-24 21:13:29 +00:00
updateSaveStateMenusEnableState ( false ) ;
2021-12-13 12:12:54 +00:00
}
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 ;
2023-03-03 11:30:40 +00:00
updateEmulationActions ( true , true , false ) ;
2021-12-13 12:12:54 +00:00
updateWindowTitle ( ) ;
2022-04-03 13:46:05 +00:00
updateStatusBarWidgetVisibility ( ) ;
2022-06-16 00:44:14 +00:00
updateInputRecordingActions ( true ) ;
2021-12-13 12:12:54 +00:00
}
void MainWindow : : onVMPaused ( )
{
// update UI
{
QSignalBlocker sb ( m_ui . actionPause ) ;
m_ui . actionPause - > setChecked ( true ) ;
}
2023-06-30 12:19:09 +00:00
{
QSignalBlocker sb ( m_ui . actionToolbarPause ) ;
m_ui . actionToolbarPause - > setChecked ( true ) ;
}
2021-12-13 12:12:54 +00:00
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 ( ) ;
2022-07-03 13:30:03 +00:00
m_last_fps_status = m_status_verbose_widget - > text ( ) ;
m_status_verbose_widget - > setText ( tr ( " Paused " ) ) ;
2022-06-20 08:01:40 +00:00
if ( m_display_widget )
2022-12-04 15:00:06 +00:00
updateDisplayWidgetCursor ( ) ;
2021-12-13 12:12:54 +00:00
}
void MainWindow : : onVMResumed ( )
{
// update UI
{
QSignalBlocker sb ( m_ui . actionPause ) ;
m_ui . actionPause - > setChecked ( false ) ;
}
2023-06-30 12:19:09 +00:00
{
QSignalBlocker sb ( m_ui . actionToolbarPause ) ;
m_ui . actionToolbarPause - > setChecked ( false ) ;
}
2021-12-13 12:12:54 +00:00
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 ( ) ;
2022-07-03 13:30:03 +00:00
m_status_verbose_widget - > setText ( m_last_fps_status ) ;
m_last_fps_status = QString ( ) ;
2022-05-07 03:52:13 +00:00
if ( m_display_widget )
2022-06-20 08:01:40 +00:00
{
2022-12-04 15:00:06 +00:00
updateDisplayWidgetCursor ( ) ;
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 ( ) ;
2023-03-03 11:30:40 +00:00
updateEmulationActions ( false , false , false ) ;
2021-12-13 12:12:54 +00:00
updateWindowTitle ( ) ;
2022-06-28 12:16:45 +00:00
updateWindowState ( ) ;
2022-04-03 13:46:05 +00:00
updateStatusBarWidgetVisibility ( ) ;
2022-06-16 00:44:14 +00:00
updateInputRecordingActions ( false ) ;
2022-05-15 08:15:27 +00:00
2023-01-28 03:33:51 +00:00
// If we're closing or in batch mode, quit the whole application now.
if ( m_is_closing | | QtHost : : InBatchMode ( ) )
{
2023-02-04 08:35:05 +00:00
QApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents , 1 ) ;
2023-01-28 03:33:51 +00:00
QCoreApplication : : quit ( ) ;
return ;
}
2022-05-15 08:15:27 +00:00
if ( m_display_widget )
2022-12-04 15:00:06 +00:00
updateDisplayWidgetCursor ( ) ;
2022-05-15 08:15:27 +00:00
else
switchToGameListView ( ) ;
2022-10-22 04:52:01 +00:00
// reload played time
if ( m_game_list_widget - > isShowingGameList ( ) )
m_game_list_widget - > refresh ( false ) ;
2021-12-13 12:12:54 +00:00
}
2023-06-13 12:43:11 +00:00
void MainWindow : : onGameChanged ( const QString & title , const QString & elf_override , const QString & disc_path ,
const QString & serial , quint32 disc_crc , quint32 crc )
2021-12-13 12:12:54 +00:00
{
2023-06-13 12:43:11 +00:00
m_current_title = title ;
2022-12-04 08:03:30 +00:00
m_current_elf_override = elf_override ;
2023-06-13 12:43:11 +00:00
m_current_disc_path = disc_path ;
m_current_disc_serial = serial ;
m_current_disc_crc = disc_crc ;
m_current_running_crc = crc ;
2021-12-13 12:12:54 +00:00
updateWindowTitle ( ) ;
2023-01-24 21:13:29 +00:00
updateSaveStateMenusEnableState ( ! serial . isEmpty ( ) ) ;
2021-12-13 12:12:54 +00:00
}
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 ( ) ;
2022-04-18 13:35:14 +00:00
# ifdef ENABLE_RAINTEGRATION
if ( Achievements : : IsUsingRAIntegration ( ) )
Achievements : : RAIntegration : : MainWindowChanged ( ( void * ) winId ( ) ) ;
# endif
2022-06-04 16:13:05 +00:00
}
2021-12-13 12:12:54 +00:00
void MainWindow : : closeEvent ( QCloseEvent * event )
{
2023-02-04 08:35:05 +00:00
// If there's no VM, we can just exit as normal.
if ( ! s_vm_valid )
2022-03-04 10:40:03 +00:00
{
2023-02-18 03:19:25 +00:00
saveStateToConfig ( ) ;
2023-09-04 11:18:37 +00:00
if ( m_display_widget )
g_emu_thread - > stopFullscreenUI ( ) ;
2023-02-04 08:35:05 +00:00
QMainWindow : : closeEvent ( event ) ;
2022-03-04 10:40:03 +00:00
return ;
}
2023-02-04 08:35:05 +00:00
// But if there is, we have to cancel the action, regardless of whether we ended exiting
// or not. The window still needs to be visible while GS is shutting down.
event - > ignore ( ) ;
// Exit cancelled?
if ( ! requestShutdown ( true , true , EmuConfig . SaveStateOnShutdown ) )
return ;
// Application will be exited in VM stopped handler.
2022-06-29 02:44:10 +00:00
m_is_closing = true ;
2021-12-13 12:12:54 +00:00
}
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 )
2023-03-03 11:31:10 +00:00
filename = QDir : : toNativeSeparators ( urls . front ( ) . toLocalFile ( ) ) ;
2022-05-26 07:49:40 +00:00
}
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 ) )
{
2022-06-28 13:34:45 +00:00
// 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-06-28 13:34:45 +00:00
doDiscChange ( CDVD_SourceType : : Iso , filename ) ;
2022-05-26 07:49:40 +00:00
else
2022-06-29 09:31:19 +00:00
doStartFile ( std : : nullopt , filename ) ;
2022-06-28 13:34:45 +00:00
}
2022-05-26 07:49:40 +00:00
}
2022-10-15 11:20:05 +00:00
void MainWindow : : registerForDeviceNotifications ( )
{
# ifdef _WIN32
// We use these notifications to detect when a controller is connected or disconnected.
DEV_BROADCAST_DEVICEINTERFACE_W filter = { sizeof ( DEV_BROADCAST_DEVICEINTERFACE_W ) , DBT_DEVTYP_DEVICEINTERFACE } ;
2022-12-04 08:03:30 +00:00
m_device_notification_handle =
RegisterDeviceNotificationW ( ( HANDLE ) winId ( ) , & filter , DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES ) ;
2022-10-15 11:20:05 +00:00
# endif
}
void MainWindow : : unregisterForDeviceNotifications ( )
{
# ifdef _WIN32
if ( ! m_device_notification_handle )
return ;
UnregisterDeviceNotification ( static_cast < HDEVNOTIFY > ( m_device_notification_handle ) ) ;
m_device_notification_handle = nullptr ;
# endif
}
# ifdef _WIN32
bool MainWindow : : nativeEvent ( const QByteArray & eventType , void * message , qintptr * result )
{
static constexpr const char win_type [ ] = " windows_generic_MSG " ;
if ( eventType = = QByteArray ( win_type , sizeof ( win_type ) - 1 ) )
{
const MSG * msg = static_cast < const MSG * > ( message ) ;
if ( msg - > message = = WM_DEVICECHANGE & & msg - > wParam = = DBT_DEVNODES_CHANGED )
{
g_emu_thread - > reloadInputDevices ( ) ;
* result = 1 ;
return true ;
}
}
return QMainWindow : : nativeEvent ( eventType , message , result ) ;
}
# endif
2023-04-25 12:52:41 +00:00
std : : optional < WindowInfo > MainWindow : : acquireRenderWindow ( bool recreate_window , bool fullscreen , bool render_to_main , bool surfaceless )
2021-12-13 12:12:54 +00:00
{
2023-04-25 12:52:41 +00:00
DevCon . WriteLn ( " acquireRenderWindow() recreate=%s fullscreen=%s render_to_main=%s surfaceless=%s " , recreate_window ? " true " : " false " ,
2023-04-08 03:20:41 +00:00
fullscreen ? " true " : " false " , render_to_main ? " true " : " false " , surfaceless ? " true " : " false " ) ;
2022-05-06 11:03:16 +00:00
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-06 11:03:16 +00:00
const bool changing_surfaceless = ( ! m_display_widget ! = surfaceless ) ;
2023-04-25 12:52:41 +00:00
if ( m_display_created & & ! recreate_window & & fullscreen = = is_fullscreen & & is_rendering_to_main = = render_to_main & &
! changing_surfaceless )
{
return m_display_widget ? m_display_widget - > getWindowInfo ( ) : WindowInfo ( ) ;
}
2021-12-13 12:12:54 +00:00
// 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 ) ;
2022-07-09 06:59:13 +00:00
const bool needs_container = DisplayContainer : : isNeeded ( fullscreen , render_to_main ) ;
2023-04-25 12:52:41 +00:00
if ( m_display_created & & ! recreate_window & & ! is_rendering_to_main & & ! render_to_main & & 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
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-12-04 15:00:06 +00:00
updateDisplayWidgetCursor ( ) ;
2022-06-20 11:50:01 +00:00
m_display_widget - > setFocus ( ) ;
2022-10-01 15:14:39 +00:00
updateWindowState ( ) ;
2022-06-20 11:50:01 +00:00
2021-12-13 12:12:54 +00:00
QCoreApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents ) ;
2023-04-01 05:54:04 +00:00
return m_display_widget - > getWindowInfo ( ) ;
2021-12-13 12:12:54 +00:00
}
2022-07-09 06:59:13 +00:00
destroyDisplayWidget ( surfaceless ) ;
2023-04-25 12:52:41 +00:00
m_display_created = true ;
2021-12-13 12:12:54 +00:00
2022-05-06 11:03:16 +00:00
// if we're going to surfaceless, we're done here
if ( surfaceless )
2023-04-01 05:54:04 +00:00
return WindowInfo ( ) ;
2022-05-06 11:03:16 +00:00
2023-04-01 05:54:04 +00:00
createDisplayWidget ( fullscreen , render_to_main ) ;
2022-07-09 06:59:13 +00:00
2023-04-25 12:52:41 +00:00
// we need the surface visible.. this might be able to be replaced with something else
QCoreApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents ) ;
2022-07-09 06:59:13 +00:00
std : : optional < WindowInfo > wi = m_display_widget - > getWindowInfo ( ) ;
if ( ! wi . has_value ( ) )
{
2023-04-25 12:52:41 +00:00
QMessageBox : : critical ( this , tr ( " Error " ) , tr ( " Failed to get window info from widget " ) ) ;
2022-07-09 06:59:13 +00:00
destroyDisplayWidget ( true ) ;
2023-04-01 05:54:04 +00:00
return std : : nullopt ;
2022-07-09 06:59:13 +00:00
}
g_emu_thread - > connectDisplaySignals ( m_display_widget ) ;
updateWindowTitle ( ) ;
updateWindowState ( ) ;
2022-12-04 15:00:06 +00:00
updateDisplayWidgetCursor ( ) ;
2022-07-09 06:59:13 +00:00
m_display_widget - > setFocus ( ) ;
2023-04-01 05:54:04 +00:00
return wi ;
2022-07-09 06:59:13 +00:00
}
2023-04-01 05:54:04 +00:00
void MainWindow : : createDisplayWidget ( bool fullscreen , bool render_to_main )
2022-07-09 06:59:13 +00:00
{
// If we're rendering to main and were hidden (e.g. coming back from fullscreen),
// make sure we're visible before trying to add ourselves. Otherwise Wayland breaks.
if ( ! fullscreen & & render_to_main & & ! isVisible ( ) )
{
setVisible ( true ) ;
QGuiApplication : : sync ( ) ;
}
QWidget * container ;
if ( DisplayContainer : : isNeeded ( fullscreen , render_to_main ) )
2021-12-13 12:12:54 +00:00
{
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-07-09 06:59:13 +00:00
m_display_widget = new DisplayWidget ( ( ! fullscreen & & render_to_main ) ? getContentParent ( ) : 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 )
{
2022-07-17 14:49:47 +00:00
// Don't risk doing this on Wayland, it really doesn't like window state changes,
// and positioning has no effect anyway.
if ( ! s_use_central_widget )
2023-02-07 10:21:14 +00:00
{
if ( isVisible ( ) & & g_emu_thread - > shouldRenderToMain ( ) )
container - > move ( pos ( ) ) ;
else
restoreDisplayWindowGeometryFromConfig ( ) ;
}
2022-07-17 14:49:47 +00:00
2023-04-01 05:54:04 +00:00
container - > showFullScreen ( ) ;
2021-12-13 12:12:54 +00:00
}
else if ( ! render_to_main )
{
restoreDisplayWindowGeometryFromConfig ( ) ;
container - > showNormal ( ) ;
}
2022-07-09 06:59:13 +00:00
else if ( s_use_central_widget )
2021-12-13 12:12:54 +00:00
{
2022-05-06 11:03:16 +00:00
m_game_list_widget - > setVisible ( false ) ;
takeCentralWidget ( ) ;
2022-07-09 06:59:13 +00:00
m_game_list_widget - > setParent ( this ) ; // takeCentralWidget() removes parent
2022-05-06 11:03:16 +00:00
setCentralWidget ( m_display_widget ) ;
m_display_widget - > setFocus ( ) ;
2022-07-09 06:59:13 +00:00
update ( ) ;
2021-12-13 12:12:54 +00:00
}
2022-07-09 06:59:13 +00:00
else
2021-12-13 12:12:54 +00:00
{
2022-07-09 06:59:13 +00:00
pxAssertRel ( m_ui . mainContainer - > count ( ) = = 1 , " Has no display widget " ) ;
m_ui . mainContainer - > addWidget ( container ) ;
m_ui . mainContainer - > setCurrentIndex ( 1 ) ;
2021-12-13 12:12:54 +00:00
}
2022-09-15 09:43:21 +00:00
updateDisplayRelatedActions ( true , render_to_main , fullscreen ) ;
2022-07-09 06:59:13 +00:00
// We need the surface visible.
QGuiApplication : : sync ( ) ;
2021-12-13 12:12:54 +00:00
}
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
}
2023-07-23 05:07:46 +00:00
void MainWindow : : mouseModeRequested ( bool relative_mode , bool hide_cursor )
2022-12-04 15:00:06 +00:00
{
2023-07-23 05:07:46 +00:00
if ( m_relative_mouse_mode = = relative_mode & & m_hide_mouse_cursor = = hide_cursor )
2022-12-04 15:00:06 +00:00
return ;
2023-07-23 05:07:46 +00:00
m_relative_mouse_mode = relative_mode ;
m_hide_mouse_cursor = hide_cursor ;
2023-06-19 07:18:11 +00:00
if ( m_display_widget & & ! s_vm_paused )
2022-12-04 15:00:06 +00:00
updateDisplayWidgetCursor ( ) ;
}
2023-04-25 12:52:41 +00:00
void MainWindow : : releaseRenderWindow ( )
2022-03-12 14:52:52 +00:00
{
2022-06-28 11:37:30 +00:00
// Now we can safely destroy the display window.
2022-07-09 06:59:13 +00:00
destroyDisplayWidget ( true ) ;
2022-05-15 08:20:21 +00:00
m_display_created = false ;
2022-05-06 11:03:16 +00:00
2022-07-09 06:59:13 +00:00
m_ui . actionViewSystemDisplay - > setEnabled ( false ) ;
m_ui . actionFullscreen - > setEnabled ( false ) ;
}
void MainWindow : : destroyDisplayWidget ( bool show_game_list )
{
if ( ! m_display_widget )
return ;
if ( ! isRenderingFullscreen ( ) & & ! isRenderingToMain ( ) )
saveDisplayWindowGeometryToConfig ( ) ;
if ( m_display_container )
m_display_container - > removeDisplayWidget ( ) ;
if ( isRenderingToMain ( ) )
2022-05-06 11:03:16 +00:00
{
2022-07-09 06:59:13 +00:00
if ( s_use_central_widget )
{
pxAssertRel ( centralWidget ( ) = = m_display_widget , " Display widget is currently central " ) ;
takeCentralWidget ( ) ;
if ( show_game_list )
{
m_game_list_widget - > setVisible ( true ) ;
setCentralWidget ( m_game_list_widget ) ;
2022-07-24 12:52:04 +00:00
m_game_list_widget - > resizeTableViewColumnsToFit ( ) ;
2022-07-09 06:59:13 +00:00
}
}
else
{
pxAssertRel ( m_ui . mainContainer - > indexOf ( m_display_widget ) = = 1 , " Display widget in stack " ) ;
m_ui . mainContainer - > removeWidget ( m_display_widget ) ;
if ( show_game_list )
2022-07-24 12:52:04 +00:00
{
2022-07-09 06:59:13 +00:00
m_ui . mainContainer - > setCurrentIndex ( 0 ) ;
2022-07-24 12:52:04 +00:00
m_game_list_widget - > resizeTableViewColumnsToFit ( ) ;
}
2022-07-09 06:59:13 +00:00
}
}
if ( m_display_widget )
{
m_display_widget - > deleteLater ( ) ;
m_display_widget = nullptr ;
}
if ( m_display_container )
{
m_display_container - > deleteLater ( ) ;
m_display_container = nullptr ;
2022-06-28 13:34:45 +00:00
}
2022-09-15 09:43:21 +00:00
updateDisplayRelatedActions ( false , false , false ) ;
2022-03-12 14:52:52 +00:00
}
2021-12-13 12:12:54 +00:00
2022-12-04 15:00:06 +00:00
void MainWindow : : updateDisplayWidgetCursor ( )
{
2023-06-19 07:18:11 +00:00
pxAssertRel ( m_display_widget , " Should have a display widget " ) ;
2022-12-04 15:00:06 +00:00
m_display_widget - > updateRelativeMode ( s_vm_valid & & ! s_vm_paused & & m_relative_mouse_mode ) ;
m_display_widget - > updateCursor ( s_vm_valid & & ! s_vm_paused & & shouldHideMouseCursor ( ) ) ;
}
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 ( ) )
2022-09-07 07:44:10 +00:00
{
Host : : SetBaseStringSettingValue ( " UI " , " DisplayWindowGeometry " , geometry_b64 . constData ( ) ) ;
Host : : CommitBaseSettingChanges ( ) ;
}
2021-12-13 12:12:54 +00:00
}
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
}
SettingsDialog * MainWindow : : getSettingsDialog ( )
{
if ( ! m_settings_dialog )
{
m_settings_dialog = new SettingsDialog ( this ) ;
2022-12-04 08:03:30 +00:00
connect ( m_settings_dialog - > getInterfaceSettingsWidget ( ) , & InterfaceSettingsWidget : : themeChanged , this , & MainWindow : : updateTheme ) ;
2023-06-19 12:03:10 +00:00
connect ( m_settings_dialog - > getInterfaceSettingsWidget ( ) , & InterfaceSettingsWidget : : languageChanged , this , [ this ] ( ) {
// reopen settings dialog after it applies
updateLanguage ( ) ;
2023-07-15 22:22:06 +00:00
// If you doSettings now, on macOS, the window will somehow end up underneath the main window that was created above
// Delay it slightly...
QtHost : : RunOnUIThread ( [ ] {
g_main_window - > doSettings ( " Interface " ) ;
} ) ;
2023-06-19 12:03:10 +00:00
} ) ;
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 ( ) ;
2023-07-15 22:22:06 +00:00
if ( dlg - > isVisible ( ) )
{
dlg - > raise ( ) ;
}
else
2021-12-13 12:12:54 +00:00
{
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 ) ;
}
2022-12-24 06:51:44 +00:00
DebuggerWindow * MainWindow : : getDebuggerWindow ( )
{
if ( ! m_debugger_window )
m_debugger_window = new DebuggerWindow ( this ) ;
return m_debugger_window ;
}
void MainWindow : : openDebugger ( )
{
DebuggerWindow * dwnd = getDebuggerWindow ( ) ;
dwnd - > isVisible ( ) ? dwnd - > hide ( ) : dwnd - > show ( ) ;
}
2021-12-13 12:12:54 +00:00
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-06-28 13:34:45 +00:00
QString MainWindow : : getDiscDevicePath ( const QString & title )
{
QString ret ;
const std : : vector < std : : string > devices ( GetOpticalDriveList ( ) ) ;
if ( devices . empty ( ) )
{
QMessageBox : : critical ( this , title ,
tr ( " Could not find any CD/DVD-ROM devices. Please ensure you have a drive connected and "
" sufficient permissions to access it. " ) ) ;
return ret ;
}
// if there's only one, select it automatically
if ( devices . size ( ) = = 1 )
{
ret = QString : : fromStdString ( devices . front ( ) ) ;
return ret ;
}
QStringList input_options ;
for ( const std : : string & name : devices )
input_options . append ( QString : : fromStdString ( name ) ) ;
QInputDialog input_dialog ( this ) ;
input_dialog . setWindowTitle ( title ) ;
input_dialog . setLabelText ( tr ( " Select disc drive: " ) ) ;
input_dialog . setInputMode ( QInputDialog : : TextInput ) ;
input_dialog . setOptions ( QInputDialog : : UseListViewForComboBoxItems ) ;
input_dialog . setComboBoxEditable ( false ) ;
input_dialog . setComboBoxItems ( std : : move ( input_options ) ) ;
if ( input_dialog . exec ( ) = = 0 )
return ret ;
ret = input_dialog . textValue ( ) ;
return ret ;
}
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 )
{
2023-09-09 03:24:31 +00:00
const QString filename = QDir : : toNativeSeparators (
2022-12-04 08:03:30 +00:00
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 ;
2023-07-30 03:35:48 +00:00
const QString old_filename = QString : : fromStdString ( GameList : : GetCoverImagePathForEntry ( entry ) ) ;
2023-09-09 03:24:31 +00:00
const QString new_filename = QString : : fromStdString ( GameList : : GetNewCoverImagePathForEntry ( entry , filename . toUtf8 ( ) . constData ( ) ) ) ;
if ( new_filename . isEmpty ( ) )
return ;
2023-07-30 03:35:48 +00:00
if ( ! old_filename . isEmpty ( ) )
2021-12-13 12:12:54 +00:00
{
2023-09-09 03:24:31 +00:00
if ( QFileInfo ( old_filename ) = = QFileInfo ( filename ) )
{
QMessageBox : : critical ( this , tr ( " Copy Error " ) , tr ( " You must select a different file to the current cover image. " ) ) ;
return ;
}
2022-12-04 08:03:30 +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? " ) , QMessageBox : : Yes ,
QMessageBox : : No ) ! = QMessageBox : : Yes )
2021-12-13 12:12:54 +00:00
{
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 ;
}
2023-07-30 03:35:48 +00:00
if ( ! old_filename . isEmpty ( ) & & old_filename ! = new_filename & & ! QFile : : remove ( old_filename ) )
{
QMessageBox : : critical ( this , tr ( " Copy Error " ) , tr ( " Failed to remove '%1' " ) . arg ( old_filename ) ) ;
return ;
}
2021-12-13 12:12:54 +00:00
m_game_list_widget - > refreshGridCovers ( ) ;
}
2022-11-22 15:03:36 +00:00
void MainWindow : : clearGameListEntryPlayTime ( const GameList : : Entry * entry )
{
if ( QMessageBox : : question ( this , tr ( " Confirm Reset " ) ,
2022-11-23 13:20:49 +00:00
tr ( " Are you sure you want to reset the play time for '%1'? \n \n This action cannot be undone. " )
. arg ( QString : : fromStdString ( entry - > title ) ) ) ! = QMessageBox : : Yes )
2022-11-22 15:03:36 +00:00
{
return ;
}
GameList : : ClearPlayedTimeForSerial ( entry - > serial ) ;
m_game_list_widget - > refresh ( false ) ;
}
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 )
2022-06-28 13:34:45 +00:00
g_emu_thread - > changeDisc ( CDVD_SourceType : : Iso , m_current_disc_path ) ;
2022-05-26 07:36:05 +00:00
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 ) ;
2022-10-01 13:29:29 +00:00
bool has_any_states = false ;
2021-12-13 12:12:54 +00:00
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 ) ;
} ) ;
2022-10-01 13:29:29 +00:00
QAction * delete_save_states_action = menu - > addAction ( tr ( " Delete Save States... " ) ) ;
2021-12-13 12:12:54 +00:00
// 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 ) ;
2022-10-01 13:29:29 +00:00
has_any_states = true ;
2021-12-13 12:12:54 +00:00
}
}
2022-10-01 13:29:29 +00:00
for ( s32 i = 1 ; i < = VMManager : : NUM_SAVE_STATE_SLOTS ; i + + )
2021-12-13 12:12:54 +00:00
{
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 ) ; } ) ;
2022-10-01 13:29:29 +00:00
has_any_states = true ;
}
delete_save_states_action - > setEnabled ( has_any_states ) ;
if ( has_any_states )
{
connect ( delete_save_states_action , & QAction : : triggered , this , [ this , serial , crc ] {
2022-12-04 08:03:30 +00:00
if ( QMessageBox : : warning ( this , tr ( " Delete Save States " ) ,
tr ( " Are you sure you want to delete all save states for %1? \n \n The saves will not be recoverable. " ) . arg ( serial ) ,
2022-10-01 13:29:29 +00:00
QMessageBox : : Yes , QMessageBox : : No ) ! = QMessageBox : : Yes )
{
return ;
}
const u32 deleted = VMManager : : DeleteSaveStates ( serial . toUtf8 ( ) . constData ( ) , crc , true ) ;
QMessageBox : : information ( this , tr ( " Delete Save States " ) , tr ( " %1 save states deleted. " ) . arg ( deleted ) ) ;
} ) ;
2021-12-13 12:12:54 +00:00
}
}
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 ( ) ) ;
2022-10-01 13:29:29 +00:00
for ( s32 i = 1 ; i < = VMManager : : NUM_SAVE_STATE_SLOTS ; i + + )
2021-12-13 12:12:54 +00:00
{
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
}
}
2023-01-24 21:13:29 +00:00
void MainWindow : : updateSaveStateMenusEnableState ( bool enable )
2021-12-13 12:12:54 +00:00
{
2023-01-24 21:13:29 +00:00
const bool load_enabled = enable ;
const bool save_enabled = enable & & s_vm_valid ;
2021-12-13 12:12:54 +00:00
m_ui . menuLoadState - > setEnabled ( load_enabled ) ;
2023-06-30 12:19:09 +00:00
m_ui . actionToolbarLoadState - > setEnabled ( load_enabled ) ;
2021-12-13 12:12:54 +00:00
m_ui . menuSaveState - > setEnabled ( save_enabled ) ;
2023-06-30 12:19:09 +00:00
m_ui . actionToolbarSaveState - > setEnabled ( save_enabled ) ;
2021-12-13 12:12:54 +00:00
}
2022-05-06 13:34:07 +00:00
2022-06-28 13:34:45 +00:00
void MainWindow : : doStartFile ( std : : optional < CDVD_SourceType > source , const QString & path )
2022-05-26 07:49:40 +00:00
{
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 > ( ) ;
2022-06-28 13:34:45 +00:00
params - > source_type = source ;
2022-05-26 07:49:40 +00:00
params - > filename = path . toStdString ( ) ;
// we might still be saving a resume state...
VMManager : : WaitForSaveStateFlush ( ) ;
2023-02-26 14:55:44 +00:00
// GetSaveStateFileName() might temporarily mount the ISO to get the serial.
cancelGameListRefresh ( ) ;
2022-05-26 07:49:40 +00:00
const std : : optional < bool > resume (
2022-12-04 08:03:30 +00:00
promptForResumeState ( QString : : fromStdString ( VMManager : : GetSaveStateFileName ( params - > filename . c_str ( ) , - 1 ) ) ) ) ;
2022-05-26 07:49:40 +00:00
if ( ! resume . has_value ( ) )
return ;
else if ( resume . value ( ) )
params - > state_index = - 1 ;
g_emu_thread - > startVM ( std : : move ( params ) ) ;
}
2022-06-28 13:34:45 +00:00
void MainWindow : : doDiscChange ( CDVD_SourceType source , const QString & path )
2022-05-06 13:34:07 +00:00
{
bool reset_system = false ;
2023-06-19 08:07:00 +00:00
if ( ! m_was_disc_change_request & & ! GSDumpReplayer : : IsReplayingDump ( ) )
2022-05-06 13:34:07 +00:00
{
2023-01-05 02:22:47 +00:00
QMessageBox message ( QMessageBox : : Question , tr ( " Confirm Disc Change " ) ,
2023-03-03 11:31:10 +00:00
tr ( " Do you want to swap discs or boot the new image (via system reset)? " ) , QMessageBox : : NoButton , this ) ;
2023-01-05 02:22:47 +00:00
message . addButton ( tr ( " Swap Disc " ) , QMessageBox : : ActionRole ) ;
QPushButton * reset_button = message . addButton ( tr ( " Reset " ) , QMessageBox : : ActionRole ) ;
QPushButton * cancel_button = message . addButton ( QMessageBox : : Cancel ) ;
message . setDefaultButton ( cancel_button ) ;
message . exec ( ) ;
if ( message . clickedButton ( ) = = cancel_button )
2022-05-06 13:34:07 +00:00
return ;
2023-01-05 02:22:47 +00:00
reset_system = ( message . clickedButton ( ) = = reset_button ) ;
2022-05-06 13:34:07 +00:00
}
switchToEmulationView ( ) ;
2022-06-28 13:34:45 +00:00
g_emu_thread - > changeDisc ( source , path ) ;
2022-05-06 13:34:07 +00:00
if ( reset_system )
g_emu_thread - > resetVM ( ) ;
}
2022-05-07 03:52:13 +00:00
MainWindow : : VMLock MainWindow : : pauseAndLockVM ( )
{
2023-04-25 12:52:41 +00:00
// To switch out of fullscreen when displaying a popup, or not to?
// For Windows, with driver's direct scanout, what renders behind tends to be hit and miss.
// We can't draw anything over exclusive fullscreen, so get out of it in that case.
// Wayland's a pain as usual, we need to recreate the window, which means there'll be a brief
// period when there's no window, and Qt might shut us down. So avoid it there.
// On MacOS, it forces a workspace switch, which is kinda jarring.
# ifndef __APPLE__
const bool was_fullscreen = g_emu_thread - > isFullscreen ( ) & & ! s_use_central_widget ;
# else
const bool was_fullscreen = false ;
# endif
2022-06-28 12:53:26 +00:00
const bool was_paused = s_vm_paused ;
2022-05-07 03:52:13 +00:00
if ( ! was_paused )
g_emu_thread - > setVMPaused ( true ) ;
2023-04-25 12:52:41 +00:00
// We need to switch out of exclusive fullscreen before we can display our popup.
// However, we do not want to switch back to render-to-main, the window might have generated this event.
if ( was_fullscreen )
{
g_emu_thread - > setFullscreen ( false , false ) ;
while ( QtHost : : IsVMValid ( ) & & g_emu_thread - > isFullscreen ( ) )
QApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents , 1 ) ;
}
// Now we'll either have a borderless window, or a regular window (if we were exclusive fullscreen).
QWidget * dialog_parent = getDisplayContainer ( ) ;
2022-05-07 03:52:13 +00:00
return VMLock ( dialog_parent , was_paused , was_fullscreen ) ;
}
2022-12-04 08:03:30 +00:00
void MainWindow : : rescanFile ( const std : : string & path )
{
m_game_list_widget - > rescanFile ( path ) ;
}
2022-05-07 03:52:13 +00:00
MainWindow : : VMLock : : VMLock ( QWidget * dialog_parent , bool was_paused , bool was_fullscreen )
: m_dialog_parent ( dialog_parent )
, m_was_paused ( was_paused )
2022-06-28 13:34:45 +00:00
, m_was_fullscreen ( was_fullscreen )
2022-05-07 03:52:13 +00:00
{
}
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 )
2023-04-25 12:52:41 +00:00
g_emu_thread - > setFullscreen ( true , true ) ;
2023-04-05 08:15:38 +00:00
2022-05-07 03:52:13 +00:00
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 ;
}