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 <QtCore/QDateTime>
2022-03-04 10:40:03 +00:00
# include <QtGui/QCloseEvent>
2021-12-13 12:12:54 +00:00
# include <QtWidgets/QFileDialog>
2022-06-28 13:34:45 +00:00
# include <QtWidgets/QInputDialog>
2021-12-13 12:12:54 +00:00
# include <QtWidgets/QMessageBox>
# include <QtWidgets/QProgressBar>
# include <QtWidgets/QStyle>
# include <QtWidgets/QStyleFactory>
2022-05-18 13:27:23 +00:00
# include "common/Assertions.h"
2022-05-25 22:32:10 +00:00
# include "common/CocoaTools.h"
2021-12-13 12:12:54 +00:00
# include "common/FileSystem.h"
2022-07-18 22:13:02 +00:00
# include "pcsx2/CDVD/CDVDcommon.h"
2022-06-28 13:34:45 +00:00
# include "pcsx2/CDVD/CDVDdiscReader.h"
2021-12-13 12:12:54 +00:00
# include "pcsx2/Frontend/GameList.h"
2022-06-18 09:35:17 +00:00
# include "pcsx2/Frontend/LogSink.h"
2022-12-18 13:05:00 +00:00
# include "pcsx2/GS/GS.h"
2022-03-25 10:14:23 +00:00
# include "pcsx2/GSDumpReplayer.h"
2021-12-13 12:12:54 +00:00
# include "pcsx2/HostDisplay.h"
2022-05-24 12:37:44 +00:00
# include "pcsx2/HostSettings.h"
2022-04-03 13:46:05 +00:00
# include "pcsx2/PerformanceMetrics.h"
2022-04-04 00:06:59 +00:00
# include "pcsx2/Recording/InputRecording.h"
2022-06-16 00:44:14 +00:00
# include "pcsx2/Recording/InputRecordingControls.h"
2021-12-13 12:12:54 +00:00
# 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"
# include "Settings/ControllerSettingsDialog.h"
# include "Settings/GameListSettingsWidget.h"
# include "Settings/InterfaceSettingsWidget.h"
2022-03-04 09:46:47 +00:00
# include "SettingWidgetBinder.h"
2022-04-04 00:06:59 +00:00
# include "svnrev.h"
2022-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"
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-04-18 13:35:14 +00:00
# ifdef ENABLE_RAINTEGRATION
# include "pcsx2/Frontend/Achievements.h"
# endif
2021-12-13 12:12:54 +00:00
2022-12-04 08:03:30 +00:00
const char * MainWindow : : OPEN_FILE_FILTER =
2022-10-05 22:58:17 +00:00
QT_TRANSLATE_NOOP ( " MainWindow " , " All File Types (*.bin *.iso *.cue *.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);; "
" 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
2022-12-04 08:03:30 +00:00
const char * MainWindow : : DISC_IMAGE_FILTER = QT_TRANSLATE_NOOP ( " MainWindow " , " All File Types (*.bin *.iso *.cue *.chd *.cso *.gz *.dump);; "
" Single-Track Raw Images (*.bin *.iso);; "
" Cue Sheets (*.cue);; "
" MAME CHD Images (*.chd);; "
" CSO Images (*.cso);; "
" GZ Images (*.gz);; "
" Block Dumps (*.dump) " ) ;
2022-06-28 13:34:45 +00:00
2022-05-25 20:52:07 +00:00
# ifdef __APPLE__
const char * MainWindow : : DEFAULT_THEME_NAME = " " ;
# else
2021-12-13 12:12:54 +00:00
const char * MainWindow : : DEFAULT_THEME_NAME = " darkfusion " ;
2022-05-25 20:52:07 +00:00
# endif
2021-12-13 12:12:54 +00:00
MainWindow * g_main_window = nullptr ;
2022-07-24 13:52:22 +00:00
static QString s_unthemed_style_name ;
2022-11-20 04:26:23 +00:00
static QPalette s_unthemed_palette ;
2022-07-24 13:52:22 +00:00
static bool s_unthemed_style_name_set ;
2021-12-13 12:12:54 +00:00
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-06-04 05:53:31 +00:00
setWindowIcon ( QIcon ( QStringLiteral ( " %1/icons/AppIconLarge.png " ) . arg ( QtHost : : GetResourcesBasePath ( ) ) ) ) ;
2022-05-25 20:52:07 +00:00
makeIconsMasks ( menuBar ( ) ) ;
2022-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 ( ) ;
2021-12-13 12:12:54 +00:00
for ( u32 scale = 0 ; scale < = 10 ; scale + + )
{
2022-02-15 14:29:18 +00:00
QAction * action = m_ui . menuWindowSize - > addAction ( ( scale = = 0 ) ? tr ( " Internal Resolution " ) : tr ( " %1x Scale " ) . arg ( scale ) ) ;
2021-12-13 12:12:54 +00:00
connect ( action , & QAction : : triggered , [ scale ] ( ) { g_emu_thread - > requestDisplaySize ( static_cast < float > ( scale ) ) ; } ) ;
}
updateEmulationActions ( false , false ) ;
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 . actionChangeDisc , & QAction : : triggered , [ this ] { m_ui . menuChangeDisc - > exec ( QCursor : : pos ( ) ) ; } ) ;
connect ( m_ui . actionChangeDiscFromFile , & QAction : : triggered , this , & MainWindow : : onChangeDiscFromFileActionTriggered ) ;
2022-02-15 14:29:18 +00:00
connect ( m_ui . actionChangeDiscFromDevice , & QAction : : triggered , this , & MainWindow : : onChangeDiscFromDeviceActionTriggered ) ;
connect ( m_ui . actionChangeDiscFromGameList , & QAction : : triggered , this , & MainWindow : : onChangeDiscFromGameListActionTriggered ) ;
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 ) ; } ) ;
2021-12-13 12:12:54 +00:00
connect ( m_ui . actionLoadState , & QAction : : triggered , this , [ this ] ( ) { m_ui . menuLoadState - > exec ( QCursor : : pos ( ) ) ; } ) ;
connect ( m_ui . actionSaveState , & QAction : : triggered , this , [ this ] ( ) { m_ui . menuSaveState - > exec ( QCursor : : pos ( ) ) ; } ) ;
connect ( m_ui . actionExit , & QAction : : triggered , this , & MainWindow : : close ) ;
2022-05-12 21:53:40 +00:00
connect ( m_ui . actionScreenshot , & QAction : : triggered , this , & MainWindow : : onScreenshotActionTriggered ) ;
2021-12-13 12:12:54 +00:00
connect ( m_ui . menuLoadState , & QMenu : : aboutToShow , this , & MainWindow : : onLoadStateMenuAboutToShow ) ;
connect ( m_ui . menuSaveState , & QMenu : : aboutToShow , this , & MainWindow : : onSaveStateMenuAboutToShow ) ;
2022-02-15 14:29:18 +00:00
connect ( m_ui . actionSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( ) ; } ) ;
connect ( m_ui . actionInterfaceSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " Interface " ) ; } ) ;
connect ( m_ui . actionGameListSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " Game List " ) ; } ) ;
connect ( m_ui . actionEmulationSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " Emulation " ) ; } ) ;
connect ( m_ui . actionBIOSSettings , & QAction : : triggered , [ this ] ( ) { doSettings ( " BIOS " ) ; } ) ;
connect ( m_ui . 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-05-15 08:20:21 +00:00
connect ( m_ui . actionStartFullscreenUI , & QAction : : triggered , thread , & EmuThread : : startFullscreenUI ) ;
connect ( m_ui . actionStartFullscreenUI2 , & QAction : : triggered , thread , & EmuThread : : startFullscreenUI ) ;
2022-08-20 10:20:36 +00:00
connect ( thread , & EmuThread : : messageConfirmed , this , & MainWindow : : confirmMessage , Qt : : BlockingQueuedConnection ) ;
2021-12-13 12:12:54 +00:00
connect ( thread , & EmuThread : : onCreateDisplayRequested , this , & MainWindow : : createDisplay , Qt : : BlockingQueuedConnection ) ;
connect ( thread , & EmuThread : : onUpdateDisplayRequested , this , & MainWindow : : updateDisplay , Qt : : BlockingQueuedConnection ) ;
2022-02-15 14:29:18 +00:00
connect ( thread , & EmuThread : : onDestroyDisplayRequested , this , & MainWindow : : destroyDisplay , Qt : : BlockingQueuedConnection ) ;
2021-12-13 12:12:54 +00:00
connect ( thread , & EmuThread : : onResizeDisplayRequested , this , & MainWindow : : displayResizeRequested ) ;
2022-12-04 15:00:06 +00:00
connect ( thread , & EmuThread : : onRelativeMouseModeRequested , this , & MainWindow : : relativeMouseModeRequested ) ;
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 ) ;
connect ( m_ui . actionReset , & QAction : : triggered , thread , & EmuThread : : resetVM ) ;
connect ( m_ui . actionPause , & QAction : : toggled , thread , & EmuThread : : setVMPaused ) ;
connect ( m_ui . actionFullscreen , & QAction : : triggered , thread , & EmuThread : : toggleFullscreen ) ;
connect ( m_ui . actionToggleSoftwareRendering , & QAction : : triggered , thread , & EmuThread : : toggleSoftwareRendering ) ;
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-24 13:52:22 +00:00
void MainWindow : : updateApplicationTheme ( )
{
if ( ! s_unthemed_style_name_set )
{
s_unthemed_style_name_set = true ;
s_unthemed_style_name = QApplication : : style ( ) - > objectName ( ) ;
2022-11-20 04:26:23 +00:00
s_unthemed_palette = QApplication : : style ( ) - > standardPalette ( ) ;
2022-07-24 13:52:22 +00:00
}
setStyleFromSettings ( ) ;
setIconThemeFromStyle ( ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : setStyleFromSettings ( )
{
2022-05-24 12:37:44 +00:00
const std : : string theme ( Host : : GetBaseStringSettingValue ( " UI " , " Theme " , DEFAULT_THEME_NAME ) ) ;
2021-12-13 12:12:54 +00:00
if ( theme = = " fusion " )
{
2022-11-20 04:26:23 +00:00
qApp - > setPalette ( s_unthemed_palette ) ;
2021-12-13 12:12:54 +00:00
qApp - > setStyleSheet ( QString ( ) ) ;
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
}
2023-01-05 02:22:47 +00:00
else if ( theme = = " darkfusion " )
2022-12-28 14:05:14 +00:00
{
// adapted from https://gist.github.com/QuantumCD/6245215
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
const QColor lighterGray ( 75 , 75 , 75 ) ;
const QColor darkGray ( 53 , 53 , 53 ) ;
const QColor gray ( 128 , 128 , 128 ) ;
const QColor black ( 25 , 25 , 25 ) ;
const QColor blue ( 198 , 238 , 255 ) ;
QPalette darkPalette ;
darkPalette . setColor ( QPalette : : Window , darkGray ) ;
darkPalette . setColor ( QPalette : : WindowText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Base , black ) ;
darkPalette . setColor ( QPalette : : AlternateBase , darkGray ) ;
darkPalette . setColor ( QPalette : : ToolTipBase , darkGray ) ;
darkPalette . setColor ( QPalette : : ToolTipText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Text , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Button , darkGray ) ;
darkPalette . setColor ( QPalette : : ButtonText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Link , blue ) ;
darkPalette . setColor ( QPalette : : Highlight , lighterGray ) ;
darkPalette . setColor ( QPalette : : HighlightedText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : PlaceholderText , QColor ( Qt : : white ) . darker ( ) ) ;
darkPalette . setColor ( QPalette : : Active , QPalette : : Button , darkGray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Text , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Light , darkGray ) ;
qApp - > setPalette ( darkPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
else if ( theme = = " darkfusionblue " )
{
// adapted from https://gist.github.com/QuantumCD/6245215
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
const QColor darkGray ( 53 , 53 , 53 ) ;
const QColor gray ( 128 , 128 , 128 ) ;
const QColor black ( 25 , 25 , 25 ) ;
const QColor blue ( 198 , 238 , 255 ) ;
const QColor blue2 ( 0 , 88 , 208 ) ;
QPalette darkPalette ;
darkPalette . setColor ( QPalette : : Window , darkGray ) ;
darkPalette . setColor ( QPalette : : WindowText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Base , black ) ;
darkPalette . setColor ( QPalette : : AlternateBase , darkGray ) ;
darkPalette . setColor ( QPalette : : ToolTipBase , blue2 ) ;
darkPalette . setColor ( QPalette : : ToolTipText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Text , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Button , darkGray ) ;
darkPalette . setColor ( QPalette : : ButtonText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Link , blue ) ;
darkPalette . setColor ( QPalette : : Highlight , blue2 ) ;
darkPalette . setColor ( QPalette : : HighlightedText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : PlaceholderText , QColor ( Qt : : white ) . darker ( ) ) ;
darkPalette . setColor ( QPalette : : Active , QPalette : : Button , darkGray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Text , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Light , darkGray ) ;
qApp - > setPalette ( darkPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
2022-02-16 15:37:48 +00:00
else if ( theme = = " UntouchedLagoon " )
{
2022-12-28 14:05:14 +00:00
// Custom palette by RedDevilus, Tame (Light/Washed out) Green as main color and Grayish Blue as complimentary.
2022-02-16 15:37:48 +00:00
// Alternative white theme.
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
const QColor black ( 25 , 25 , 25 ) ;
2023-01-05 15:36:27 +00:00
const QColor darkteal ( 0 , 77 , 77 ) ;
2022-03-04 10:40:03 +00:00
const QColor teal ( 0 , 128 , 128 ) ;
2022-02-16 15:37:48 +00:00
const QColor tameTeal ( 160 , 190 , 185 ) ;
const QColor grayBlue ( 160 , 180 , 190 ) ;
QPalette standardPalette ;
standardPalette . setColor ( QPalette : : Window , tameTeal ) ;
2022-12-28 14:05:14 +00:00
standardPalette . setColor ( QPalette : : WindowText , black . lighter ( ) ) ;
2022-02-16 15:37:48 +00:00
standardPalette . setColor ( QPalette : : Base , grayBlue ) ;
standardPalette . setColor ( QPalette : : AlternateBase , tameTeal ) ;
standardPalette . setColor ( QPalette : : ToolTipBase , tameTeal ) ;
standardPalette . setColor ( QPalette : : ToolTipText , grayBlue ) ;
standardPalette . setColor ( QPalette : : Text , black ) ;
2023-01-05 15:36:27 +00:00
standardPalette . setColor ( QPalette : : Button , tameTeal ) ;
standardPalette . setColor ( QPalette : : ButtonText , black ) ;
standardPalette . setColor ( QPalette : : Link , black . lighter ( ) ) ;
2022-02-16 15:37:48 +00:00
standardPalette . setColor ( QPalette : : Highlight , teal ) ;
2023-01-05 15:36:27 +00:00
standardPalette . setColor ( QPalette : : HighlightedText , grayBlue . lighter ( ) ) ;
2022-02-16 15:37:48 +00:00
2023-01-05 15:36:27 +00:00
standardPalette . setColor ( QPalette : : Active , QPalette : : Button , tameTeal ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , darkteal ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , darkteal . lighter ( ) ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : Text , darkteal . lighter ( ) ) ;
2022-02-16 15:37:48 +00:00
standardPalette . setColor ( QPalette : : Disabled , QPalette : : Light , tameTeal ) ;
qApp - > setPalette ( standardPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
else if ( theme = = " BabyPastel " )
{
2022-12-28 14:05:14 +00:00
// Custom palette by RedDevilus, Blue as main color and blue as complimentary.
2022-02-16 15:37:48 +00:00
// Alternative light theme.
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
const QColor gray ( 150 , 150 , 150 ) ;
const QColor black ( 25 , 25 , 25 ) ;
2023-01-05 15:36:27 +00:00
const QColor redpinkish ( 200 , 75 , 132 ) ;
2022-02-16 15:37:48 +00:00
const QColor pink ( 255 , 174 , 201 ) ;
const QColor brightPink ( 255 , 230 , 255 ) ;
const QColor congoPink ( 255 , 127 , 121 ) ;
const QColor blue ( 221 , 225 , 239 ) ;
QPalette standardPalette ;
standardPalette . setColor ( QPalette : : Window , pink ) ;
standardPalette . setColor ( QPalette : : WindowText , black ) ;
standardPalette . setColor ( QPalette : : Base , brightPink ) ;
standardPalette . setColor ( QPalette : : AlternateBase , blue ) ;
standardPalette . setColor ( QPalette : : ToolTipBase , pink ) ;
standardPalette . setColor ( QPalette : : ToolTipText , brightPink ) ;
standardPalette . setColor ( QPalette : : Text , black ) ;
standardPalette . setColor ( QPalette : : Button , pink ) ;
standardPalette . setColor ( QPalette : : ButtonText , black ) ;
2022-05-23 15:11:29 +00:00
standardPalette . setColor ( QPalette : : Link , black ) ;
2022-02-16 15:37:48 +00:00
standardPalette . setColor ( QPalette : : Highlight , congoPink ) ;
standardPalette . setColor ( QPalette : : HighlightedText , black ) ;
standardPalette . setColor ( QPalette : : Active , QPalette : : Button , pink ) ;
2023-01-05 15:36:27 +00:00
standardPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , redpinkish ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , redpinkish ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : Text , redpinkish ) ;
2022-02-16 15:37:48 +00:00
standardPalette . setColor ( QPalette : : Disabled , QPalette : : Light , gray ) ;
qApp - > setPalette ( standardPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
else if ( theme = = " PCSX2Blue " )
{
2022-12-28 14:05:14 +00:00
// Custom palette by RedDevilus, White as main color and Blue as complimentary.
2022-02-16 15:37:48 +00:00
// Alternative light theme.
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
2023-01-05 15:36:27 +00:00
const QColor blackish ( 35 , 35 , 35 ) ;
2022-02-16 15:37:48 +00:00
const QColor darkBlue ( 73 , 97 , 177 ) ;
2023-01-05 15:36:27 +00:00
const QColor blue2 ( 80 , 120 , 200 ) ;
2022-02-16 15:37:48 +00:00
const QColor blue ( 106 , 156 , 255 ) ;
const QColor lightBlue ( 130 , 155 , 241 ) ;
QPalette standardPalette ;
2023-01-05 15:36:27 +00:00
standardPalette . setColor ( QPalette : : Window , blue2 . lighter ( ) ) ;
standardPalette . setColor ( QPalette : : WindowText , blackish ) ;
standardPalette . setColor ( QPalette : : Base , lightBlue ) ;
standardPalette . setColor ( QPalette : : AlternateBase , blue2 . lighter ( ) ) ;
standardPalette . setColor ( QPalette : : ToolTipBase , blue2 ) ;
2022-02-16 15:37:48 +00:00
standardPalette . setColor ( QPalette : : ToolTipText , Qt : : white ) ;
2023-01-05 15:36:27 +00:00
standardPalette . setColor ( QPalette : : Text , blackish ) ;
standardPalette . setColor ( QPalette : : Button , blue ) ;
standardPalette . setColor ( QPalette : : ButtonText , blackish ) ;
2022-05-23 15:11:29 +00:00
standardPalette . setColor ( QPalette : : Link , darkBlue ) ;
2022-02-16 15:37:48 +00:00
standardPalette . setColor ( QPalette : : Highlight , Qt : : white ) ;
2023-01-05 15:36:27 +00:00
standardPalette . setColor ( QPalette : : HighlightedText , blackish ) ;
2022-02-16 15:37:48 +00:00
2023-01-05 15:36:27 +00:00
standardPalette . setColor ( QPalette : : Active , QPalette : : Button , blue ) ;
2022-02-16 15:37:48 +00:00
standardPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , darkBlue ) ;
standardPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , darkBlue ) ;
2023-01-05 15:36:27 +00:00
standardPalette . setColor ( QPalette : : Disabled , QPalette : : Text , darkBlue ) ;
2022-02-16 15:37:48 +00:00
standardPalette . setColor ( QPalette : : Disabled , QPalette : : Light , darkBlue ) ;
qApp - > setPalette ( standardPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
2022-12-28 14:05:14 +00:00
else if ( theme = = " ScarletDevilRed " )
2021-12-13 12:12:54 +00:00
{
2022-12-28 14:05:14 +00:00
// Custom palette by RedDevilus, Red as main color and Purple as complimentary.
// Alternative dark theme.
2021-12-13 12:12:54 +00:00
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
2022-12-28 14:05:14 +00:00
const QColor darkRed ( 80 , 45 , 69 ) ;
const QColor purplishRed ( 120 , 45 , 69 ) ;
const QColor brightRed ( 200 , 45 , 69 ) ;
2021-12-13 12:12:54 +00:00
QPalette darkPalette ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Window , darkRed ) ;
2021-12-13 12:12:54 +00:00
darkPalette . setColor ( QPalette : : WindowText , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Base , purplishRed ) ;
darkPalette . setColor ( QPalette : : AlternateBase , darkRed ) ;
darkPalette . setColor ( QPalette : : ToolTipBase , darkRed ) ;
2021-12-13 12:12:54 +00:00
darkPalette . setColor ( QPalette : : ToolTipText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Text , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Button , purplishRed . darker ( ) ) ;
2021-12-13 12:12:54 +00:00
darkPalette . setColor ( QPalette : : ButtonText , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Link , brightRed ) ;
darkPalette . setColor ( QPalette : : Highlight , brightRed ) ;
2021-12-13 12:12:54 +00:00
darkPalette . setColor ( QPalette : : HighlightedText , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Active , QPalette : : Button , purplishRed . darker ( ) ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , brightRed ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , brightRed ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Text , brightRed ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Light , darkRed ) ;
2021-12-13 12:12:54 +00:00
qApp - > setPalette ( darkPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
2022-12-28 14:05:14 +00:00
else if ( theme = = " VioletAngelPurple " )
2021-12-13 12:12:54 +00:00
{
2022-12-28 14:05:14 +00:00
// Custom palette by RedDevilus, Blue as main color and Purple as complimentary.
// Alternative dark theme.
2021-12-13 12:12:54 +00:00
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
2023-01-05 02:22:47 +00:00
2022-12-28 14:05:14 +00:00
const QColor blackishblue ( 50 , 25 , 70 ) ;
const QColor darkerPurple ( 90 , 30 , 105 ) ;
const QColor nauticalPurple ( 110 , 30 , 125 ) ;
2021-12-13 12:12:54 +00:00
QPalette darkPalette ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Window , blackishblue ) ;
2021-12-13 12:12:54 +00:00
darkPalette . setColor ( QPalette : : WindowText , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Base , nauticalPurple ) ;
darkPalette . setColor ( QPalette : : AlternateBase , blackishblue ) ;
darkPalette . setColor ( QPalette : : ToolTipBase , nauticalPurple ) ;
2021-12-13 12:12:54 +00:00
darkPalette . setColor ( QPalette : : ToolTipText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Text , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Button , nauticalPurple . darker ( ) ) ;
2021-12-13 12:12:54 +00:00
darkPalette . setColor ( QPalette : : ButtonText , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Link , darkerPurple . lighter ( ) ) ;
darkPalette . setColor ( QPalette : : Highlight , darkerPurple . lighter ( ) ) ;
2021-12-13 12:12:54 +00:00
darkPalette . setColor ( QPalette : : HighlightedText , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Active , QPalette : : Button , nauticalPurple . darker ( ) ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , darkerPurple . lighter ( ) ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , darkerPurple . lighter ( ) ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Text , darkerPurple . darker ( ) ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Light , nauticalPurple ) ;
2021-12-13 12:12:54 +00:00
qApp - > setPalette ( darkPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
2022-12-28 14:05:14 +00:00
else if ( theme = = " Ruby " )
2022-02-16 15:37:48 +00:00
{
2022-12-28 14:05:14 +00:00
// Custom palette by Daisouji, Black as main color and Red as complimentary.
// Alternative dark (black) theme.
2022-02-16 15:37:48 +00:00
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
2022-12-28 14:05:14 +00:00
const QColor gray ( 128 , 128 , 128 ) ;
const QColor slate ( 18 , 18 , 18 ) ;
const QColor rubyish ( 172 , 21 , 31 ) ;
2022-02-16 15:37:48 +00:00
QPalette darkPalette ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Window , slate ) ;
2022-02-16 15:37:48 +00:00
darkPalette . setColor ( QPalette : : WindowText , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Base , slate . lighter ( ) ) ;
darkPalette . setColor ( QPalette : : AlternateBase , slate . lighter ( ) ) ;
darkPalette . setColor ( QPalette : : ToolTipBase , slate ) ;
2022-02-16 15:37:48 +00:00
darkPalette . setColor ( QPalette : : ToolTipText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Text , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Button , slate ) ;
2022-02-16 15:37:48 +00:00
darkPalette . setColor ( QPalette : : ButtonText , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Link , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Highlight , rubyish ) ;
2022-02-16 15:37:48 +00:00
darkPalette . setColor ( QPalette : : HighlightedText , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Active , QPalette : : Button , slate ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Text , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Light , slate . lighter ( ) ) ;
2022-02-16 15:37:48 +00:00
qApp - > setPalette ( darkPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
}
2022-12-28 14:05:14 +00:00
else if ( theme = = " Sapphire " )
2022-07-20 01:12:00 +00:00
{
2022-12-28 14:05:14 +00:00
// Custom palette by RedDevilus, Black as main color and Blue as complimentary.
// Alternative dark (black) theme.
2022-07-20 01:12:00 +00:00
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
const QColor gray ( 128 , 128 , 128 ) ;
const QColor slate ( 18 , 18 , 18 ) ;
2022-12-28 14:05:14 +00:00
const QColor persianBlue ( 32 , 35 , 204 ) ;
2022-07-20 01:12:00 +00:00
QPalette darkPalette ;
darkPalette . setColor ( QPalette : : Window , slate ) ;
darkPalette . setColor ( QPalette : : WindowText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Base , slate . lighter ( ) ) ;
darkPalette . setColor ( QPalette : : AlternateBase , slate . lighter ( ) ) ;
darkPalette . setColor ( QPalette : : ToolTipBase , slate ) ;
darkPalette . setColor ( QPalette : : ToolTipText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Text , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Button , slate ) ;
darkPalette . setColor ( QPalette : : ButtonText , Qt : : white ) ;
darkPalette . setColor ( QPalette : : Link , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Highlight , persianBlue ) ;
2022-07-20 01:12:00 +00:00
darkPalette . setColor ( QPalette : : HighlightedText , Qt : : white ) ;
2022-12-28 14:05:14 +00:00
darkPalette . setColor ( QPalette : : Active , QPalette : : Button , slate ) ;
2022-07-20 01:12:00 +00:00
darkPalette . setColor ( QPalette : : Disabled , QPalette : : ButtonText , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : WindowText , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Text , gray ) ;
darkPalette . setColor ( QPalette : : Disabled , QPalette : : Light , slate . lighter ( ) ) ;
qApp - > setPalette ( darkPalette ) ;
qApp - > setStyleSheet ( " QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; } " ) ;
2022-07-20 05:40:53 +00:00
}
2022-07-20 01:12:00 +00:00
else if ( theme = = " Custom " )
{
2022-07-20 05:40:53 +00:00
2022-07-20 01:12:00 +00:00
//Additional Theme option than loads .qss from main PCSX2 Directory
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
2022-07-20 05:40:53 +00:00
2022-07-20 01:12:00 +00:00
QString sheet_content ;
2022-07-20 05:40:53 +00:00
QFile sheets ( QString : : fromStdString ( Path : : Combine ( EmuFolders : : DataRoot , " custom.qss " ) ) ) ;
if ( sheets . open ( QFile : : ReadOnly ) )
{
QString sheet_content = QString : : fromUtf8 ( sheets . readAll ( ) . data ( ) ) ;
qApp - > setStyleSheet ( sheet_content ) ;
2022-07-20 01:12:00 +00:00
}
else
{
2022-07-20 05:40:53 +00:00
qApp - > setStyle ( QStyleFactory : : create ( " Fusion " ) ) ;
2022-07-20 01:12:00 +00:00
}
}
2022-07-20 05:40:53 +00:00
else
2021-12-13 12:12:54 +00:00
{
2022-11-20 04:26:23 +00:00
qApp - > setPalette ( s_unthemed_palette ) ;
2021-12-13 12:12:54 +00:00
qApp - > setStyleSheet ( QString ( ) ) ;
2022-07-24 13:52:22 +00:00
qApp - > setStyle ( s_unthemed_style_name ) ;
2021-12-13 12:12:54 +00:00
}
}
2022-05-25 21:20:34 +00:00
void MainWindow : : setIconThemeFromStyle ( )
2021-12-13 12:12:54 +00:00
{
2022-05-25 21:20:34 +00:00
QPalette palette = qApp - > palette ( ) ;
bool dark = palette . windowText ( ) . color ( ) . value ( ) > palette . window ( ) . color ( ) . value ( ) ;
QIcon : : setThemeName ( dark ? QStringLiteral ( " white " ) : QStringLiteral ( " black " ) ) ;
2021-12-13 12:12:54 +00:00
}
2022-05-12 21:53:40 +00:00
void MainWindow : : onScreenshotActionTriggered ( )
{
2022-05-16 11:10:34 +00:00
g_emu_thread - > queueSnapshot ( 0 ) ;
}
void MainWindow : : onSaveGSDumpActionTriggered ( )
{
g_emu_thread - > queueSnapshot ( 1 ) ;
2022-05-12 21:53:40 +00:00
}
2022-05-24 12:22:15 +00:00
void MainWindow : : onBlockDumpActionToggled ( bool checked )
{
if ( ! checked )
return ;
std : : string old_directory ( Host : : GetBaseStringSettingValue ( " EmuCore " , " BlockDumpSaveDirectory " , " " ) ) ;
if ( old_directory . empty ( ) )
old_directory = FileSystem : : GetWorkingDirectory ( ) ;
// prompt for a location to save
const QString new_dir (
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 ;
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 ( ) )
{
QSignalBlocker sb ( m_ui . actionToolsVideoCapture ) ;
m_ui . actionToolsVideoCapture - > setChecked ( false ) ;
return ;
}
g_emu_thread - > beginCapture ( path ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : saveStateToConfig ( )
{
2022-06-29 02:44:10 +00:00
if ( ! isVisible ( ) )
return ;
2021-12-13 12:12:54 +00:00
{
const QByteArray geometry = saveGeometry ( ) ;
const QByteArray geometry_b64 = geometry . toBase64 ( ) ;
2022-05-24 12:37:44 +00:00
const std : : string old_geometry_b64 = Host : : GetBaseStringSettingValue ( " UI " , " MainWindowGeometry " ) ;
2021-12-13 12:12:54 +00:00
if ( old_geometry_b64 ! = geometry_b64 . constData ( ) )
2022-09-07 07:44:10 +00:00
{
Host : : SetBaseStringSettingValue ( " UI " , " MainWindowGeometry " , geometry_b64 . constData ( ) ) ;
Host : : CommitBaseSettingChanges ( ) ;
}
2021-12-13 12:12:54 +00:00
}
{
const QByteArray state = saveState ( ) ;
const QByteArray state_b64 = state . toBase64 ( ) ;
2022-05-24 12:37:44 +00:00
const std : : string old_state_b64 = Host : : GetBaseStringSettingValue ( " UI " , " MainWindowState " ) ;
2021-12-13 12:12:54 +00:00
if ( old_state_b64 ! = state_b64 . constData ( ) )
2022-09-07 07:44:10 +00:00
{
Host : : SetBaseStringSettingValue ( " UI " , " MainWindowState " , state_b64 . constData ( ) ) ;
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 ( ) ) ;
}
}
}
void MainWindow : : updateEmulationActions ( bool starting , bool running )
{
const bool starting_or_running = starting | | running ;
m_ui . actionStartFile - > setDisabled ( starting_or_running ) ;
m_ui . actionStartDisc - > setDisabled ( starting_or_running ) ;
m_ui . actionStartBios - > setDisabled ( starting_or_running ) ;
m_ui . actionPowerOff - > setEnabled ( running ) ;
2022-05-07 12:56:44 +00:00
m_ui . actionPowerOffWithoutSaving - > setEnabled ( running ) ;
2021-12-13 12:12:54 +00:00
m_ui . actionReset - > setEnabled ( running ) ;
m_ui . actionPause - > setEnabled ( running ) ;
m_ui . actionChangeDisc - > setEnabled ( running ) ;
m_ui . actionScreenshot - > setEnabled ( running ) ;
m_ui . menuChangeDisc - > setEnabled ( running ) ;
m_ui . actionSaveState - > setEnabled ( running ) ;
m_ui . menuSaveState - > setEnabled ( running ) ;
m_ui . actionViewGameProperties - > setEnabled ( running ) ;
2022-12-18 13:05:00 +00:00
m_ui . actionToolsVideoCapture - > setEnabled ( running ) ;
if ( ! running & & m_ui . actionToolsVideoCapture - > isChecked ( ) )
m_ui . actionToolsVideoCapture - > setChecked ( false ) ;
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 )
m_ui . actionPause - > setChecked ( false ) ;
2022-05-22 14:12:21 +00:00
// scanning needs to be disabled while running
m_ui . actionScanForNewGames - > setDisabled ( starting_or_running ) ;
m_ui . actionRescanAllGames - > setDisabled ( starting_or_running ) ;
2021-12-13 12:12:54 +00:00
}
2022-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
m_ui . actionViewSystemDisplay - > setEnabled ( ( has_surface & & render_to_main ) | | ( ! has_surface & & g_host_display ) ) ;
m_ui . menuWindowSize - > setEnabled ( has_surface & & ! fullscreen ) ;
m_ui . actionFullscreen - > setEnabled ( has_surface ) ;
{
QSignalBlocker blocker ( m_ui . actionFullscreen ) ;
m_ui . actionFullscreen - > setChecked ( fullscreen ) ;
}
}
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 ) ;
QString display_title ( m_current_game_name + suffix ) ;
2022-05-06 11:03:16 +00:00
2022-06-28 12:53:26 +00:00
if ( ! s_vm_valid | | m_current_game_name . isEmpty ( ) )
2022-05-06 11:03:16 +00:00
display_title = main_title ;
else if ( isRenderingToMain ( ) )
main_title = display_title ;
if ( windowTitle ( ) ! = main_title )
setWindowTitle ( main_title ) ;
if ( m_display_widget & & ! isRenderingToMain ( ) )
2021-12-13 12:12:54 +00:00
{
2022-05-06 11:03:16 +00:00
QWidget * container = m_display_container ? static_cast < QWidget * > ( m_display_container ) : static_cast < QWidget * > ( m_display_widget ) ;
if ( container - > windowTitle ( ) ! = display_title )
container - > setWindowTitle ( display_title ) ;
2021-12-13 12:12:54 +00:00
}
}
2022-06-28 12:16:45 +00:00
void MainWindow : : updateWindowState ( bool force_visible )
2022-06-28 11:37:30 +00:00
{
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
{
2022-09-07 10:19:40 +00:00
if ( ! g_host_display | | ! m_display_widget )
2022-05-06 11:03:16 +00:00
return false ;
2022-09-07 10:19:40 +00:00
return getDisplayContainer ( ) - > isFullScreen ( ) | | g_host_display - > 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
{
2022-12-04 15:00:06 +00:00
return ( isRenderingFullscreen ( ) & & Host : : GetBoolSettingValue ( " UI " , " HideMouseCursor " , false ) ) | | m_relative_mouse_mode ;
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.
2022-05-07 12:56:44 +00:00
allow_save_to_state & = ( m_current_game_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
// 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
{
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.
if ( QtHost : : IsVMValid ( ) )
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 ] ( ) {
SettingsDialog : : openGamePropertiesDialog (
entry , ( entry - > type ! = GameList : : EntryType : : ELF ) ? std : : string_view ( entry - > serial ) : std : : string_view ( ) , entry - > crc ) ;
} ) ;
}
2021-12-13 12:12:54 +00:00
action = menu . addAction ( tr ( " Open Containing Directory... " ) ) ;
connect ( action , & QAction : : triggered , [ this , entry ] ( ) {
const QFileInfo fi ( QString : : fromStdString ( entry - > path ) ) ;
QtUtils : : OpenURL ( this , QUrl : : fromLocalFile ( fi . absolutePath ( ) ) ) ;
} ) ;
action = menu . addAction ( tr ( " Set Cover Image... " ) ) ;
connect ( action , & QAction : : triggered , [ this , entry ] ( ) { setGameListEntryCoverImage ( entry ) ; } ) ;
connect ( menu . addAction ( tr ( " Exclude From List " ) ) , & QAction : : triggered ,
[ this , entry ] ( ) { getSettingsDialog ( ) - > getGameListSettingsWidget ( ) - > addExcludedPath ( entry - > path ) ; } ) ;
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-01-15 03:29:42 +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-01-24 21:13:29 +00:00
updateSaveStateMenusEnableState ( ! m_current_game_serial . isEmpty ( ) ) ;
populateLoadStateMenu ( m_ui . menuLoadState , m_current_disc_path , m_current_game_serial , m_current_game_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-01-24 21:13:29 +00:00
updateSaveStateMenusEnableState ( ! m_current_game_serial . isEmpty ( ) ) ;
populateSaveStateMenu ( m_ui . menuSaveState , m_current_game_serial , m_current_game_crc ) ;
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
2022-12-04 08:03:30 +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 ( ) ;
2022-12-04 08:03:30 +00:00
const GameList : : Entry * entry = m_current_elf_override . isEmpty ( ) ?
GameList : : GetEntryForPath ( m_current_disc_path . toUtf8 ( ) . constData ( ) ) :
2023-01-05 02:22:47 +00:00
GameList : : GetEntryForPath ( m_current_elf_override . toUtf8 ( ) . constData ( ) ) ;
2022-02-15 14:29:18 +00:00
if ( entry )
{
2022-12-04 08:03:30 +00:00
SettingsDialog : : openGamePropertiesDialog (
entry , m_current_elf_override . isEmpty ( ) ? std : : string_view ( entry - > serial ) : std : : string_view ( ) , entry - > crc ) ;
2022-02-15 14:29:18 +00:00
return ;
}
}
2021-12-13 12:12:54 +00:00
2022-02-15 14:29:18 +00:00
// open properties for the current running file (isn't in the game list)
if ( m_current_game_crc ! = 0 )
2022-05-23 14:35:21 +00:00
SettingsDialog : : openGamePropertiesDialog ( nullptr , m_current_game_serial . toStdString ( ) , m_current_game_crc ) ;
2021-12-13 12:12:54 +00:00
}
2022-03-12 14:52:52 +00:00
void MainWindow : : onGitHubRepositoryActionTriggered ( )
{
QtUtils : : OpenURL ( this , AboutDialog : : getGitHubRepositoryUrl ( ) ) ;
}
2022-02-15 14:29:18 +00:00
2022-03-12 14:52:52 +00:00
void MainWindow : : onSupportForumsActionTriggered ( )
{
QtUtils : : OpenURL ( this , AboutDialog : : getSupportForumsUrl ( ) ) ;
}
2022-02-15 14:29:18 +00:00
2022-03-12 14:52:52 +00:00
void MainWindow : : onDiscordServerActionTriggered ( )
{
QtUtils : : OpenURL ( this , AboutDialog : : getDiscordServerUrl ( ) ) ;
}
2021-12-13 12:12:54 +00:00
void MainWindow : : onAboutActionTriggered ( )
{
AboutDialog about ( this ) ;
about . exec ( ) ;
}
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
{
2022-07-24 13:52:22 +00:00
updateApplicationTheme ( ) ;
m_game_list_widget - > refreshImages ( ) ;
2021-12-13 12:12:54 +00:00
}
2022-04-04 00:06:59 +00:00
void MainWindow : : onInputRecNewActionTriggered ( )
{
2022-06-28 12:53:26 +00:00
const bool wasPaused = s_vm_paused ;
const bool wasRunning = s_vm_valid ;
2022-04-04 00:06:59 +00:00
if ( wasRunning & & ! wasPaused )
{
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 ( ) ) ;
} ) ;
}
} ) ;
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 ( ) ) ;
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 ) ;
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 ;
2021-12-13 12:12:54 +00:00
updateEmulationActions ( true , false ) ;
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 ;
2021-12-13 12:12:54 +00:00
updateEmulationActions ( true , true ) ;
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 ) ;
}
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 ) ;
}
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 ( ) ;
2021-12-13 12:12:54 +00:00
updateEmulationActions ( false , false ) ;
updateWindowTitle ( ) ;
2022-06-28 12:16:45 +00:00
updateWindowState ( ) ;
2022-04-03 13:46:05 +00:00
updateStatusBarWidgetVisibility ( ) ;
2022-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 ( ) )
{
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
}
2022-12-04 08:03:30 +00:00
void MainWindow : : onGameChanged ( const QString & path , const QString & elf_override , const QString & serial , const QString & name , quint32 crc )
2021-12-13 12:12:54 +00:00
{
m_current_disc_path = path ;
2022-12-04 08:03:30 +00:00
m_current_elf_override = elf_override ;
2021-12-13 12:12:54 +00:00
m_current_game_serial = serial ;
m_current_game_name = name ;
m_current_game_crc = crc ;
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-01-28 03:33:51 +00:00
if ( ! requestShutdown ( true , true , EmuConfig . SaveStateOnShutdown ) )
2022-03-04 10:40:03 +00:00
{
event - > ignore ( ) ;
return ;
}
2021-12-13 12:12:54 +00:00
saveStateToConfig ( ) ;
2022-06-29 02:44:10 +00:00
m_is_closing = true ;
2021-12-13 12:12:54 +00:00
QMainWindow : : closeEvent ( event ) ;
}
2022-05-26 07:49:40 +00:00
static QString getFilenameFromMimeData ( const QMimeData * md )
{
QString filename ;
if ( md - > hasUrls ( ) )
{
// only one url accepted
const QList < QUrl > urls ( md - > urls ( ) ) ;
if ( urls . size ( ) = = 1 )
filename = urls . front ( ) . toLocalFile ( ) ;
}
return filename ;
}
void MainWindow : : dragEnterEvent ( QDragEnterEvent * event )
{
const std : : string filename ( getFilenameFromMimeData ( event - > mimeData ( ) ) . toStdString ( ) ) ;
// allow save states being dragged in
if ( ! VMManager : : IsLoadableFileName ( filename ) & & ! VMManager : : IsSaveStateFileName ( filename ) )
return ;
event - > acceptProposedAction ( ) ;
}
void MainWindow : : dropEvent ( QDropEvent * event )
{
const QString filename ( getFilenameFromMimeData ( event - > mimeData ( ) ) ) ;
const std : : string filename_str ( filename . toStdString ( ) ) ;
if ( VMManager : : IsSaveStateFileName ( filename_str ) )
{
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
2021-12-13 12:12:54 +00:00
DisplayWidget * MainWindow : : createDisplay ( bool fullscreen , bool render_to_main )
{
2022-05-06 11:03:16 +00:00
DevCon . WriteLn ( " createDisplay(%u, %u) " , static_cast < u32 > ( fullscreen ) , static_cast < u32 > ( render_to_main ) ) ;
2022-09-07 10:19:40 +00:00
if ( ! g_host_display )
2021-12-13 12:12:54 +00:00
return nullptr ;
2022-05-24 12:37:44 +00:00
const std : : string fullscreen_mode ( Host : : GetBaseStringSettingValue ( " EmuCore/GS " , " FullscreenMode " , " " ) ) ;
2022-09-07 10:19:40 +00:00
const bool is_exclusive_fullscreen = ( fullscreen & & ! fullscreen_mode . empty ( ) & & g_host_display - > SupportsFullscreen ( ) ) ;
2021-12-13 12:12:54 +00:00
2022-07-09 06:59:13 +00:00
createDisplayWidget ( fullscreen , render_to_main , is_exclusive_fullscreen ) ;
2021-12-13 12:12:54 +00:00
// we need the surface visible.. this might be able to be replaced with something else
QCoreApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents ) ;
std : : optional < WindowInfo > wi = m_display_widget - > getWindowInfo ( ) ;
if ( ! wi . has_value ( ) )
{
QMessageBox : : critical ( this , tr ( " Error " ) , tr ( " Failed to get window info from widget " ) ) ;
2022-07-09 06:59:13 +00:00
destroyDisplayWidget ( true ) ;
2021-12-13 12:12:54 +00:00
return nullptr ;
}
2022-04-15 08:45:41 +00:00
g_emu_thread - > connectDisplaySignals ( m_display_widget ) ;
2022-12-04 05:09:22 +00:00
if ( ! g_host_display - > CreateDevice ( wi . value ( ) , Host : : GetEffectiveVSyncMode ( ) ) )
2021-12-13 12:12:54 +00:00
{
QMessageBox : : critical ( this , tr ( " Error " ) , tr ( " Failed to create host display device context. " ) ) ;
2022-07-09 06:59:13 +00:00
destroyDisplayWidget ( true ) ;
2021-12-13 12:12:54 +00:00
return nullptr ;
}
2022-05-15 08:20:21 +00:00
m_display_created = true ;
2021-12-13 12:12:54 +00:00
if ( is_exclusive_fullscreen )
setDisplayFullscreen ( fullscreen_mode ) ;
2022-05-06 11:03:16 +00:00
updateWindowTitle ( ) ;
2022-06-28 12:16:45 +00:00
updateWindowState ( ) ;
2022-05-06 11:03:16 +00:00
2022-05-15 08:20:21 +00:00
m_ui . actionStartFullscreenUI - > setEnabled ( false ) ;
m_ui . actionStartFullscreenUI2 - > setEnabled ( false ) ;
2022-07-09 06:59:13 +00:00
2022-12-04 15:00:06 +00:00
updateDisplayWidgetCursor ( ) ;
2022-07-09 06:59:13 +00:00
m_display_widget - > setFocus ( ) ;
2022-05-15 08:15:27 +00:00
2022-10-10 12:04:56 +00:00
g_host_display - > DoneCurrent ( ) ;
2021-12-13 12:12:54 +00:00
return m_display_widget ;
}
2022-05-06 11:03:16 +00:00
DisplayWidget * MainWindow : : updateDisplay ( bool fullscreen , bool render_to_main , bool surfaceless )
2021-12-13 12:12:54 +00:00
{
2022-12-04 08:03:30 +00:00
DevCon . WriteLn ( " updateDisplay() fullscreen=%s render_to_main=%s surfaceless=%s " , fullscreen ? " true " : " false " ,
render_to_main ? " true " : " false " , surfaceless ? " true " : " false " ) ;
2022-05-06 11:03:16 +00:00
2022-04-15 08:45:41 +00:00
QWidget * container = m_display_container ? static_cast < QWidget * > ( m_display_container ) : static_cast < QWidget * > ( m_display_widget ) ;
2022-05-07 03:52:13 +00:00
const bool is_fullscreen = isRenderingFullscreen ( ) ;
const bool is_rendering_to_main = isRenderingToMain ( ) ;
2022-05-24 12:37:44 +00:00
const std : : string fullscreen_mode ( Host : : GetBaseStringSettingValue ( " EmuCore/GS " , " FullscreenMode " , " " ) ) ;
2022-09-07 10:19:40 +00:00
const bool is_exclusive_fullscreen = ( fullscreen & & ! fullscreen_mode . empty ( ) & & g_host_display - > SupportsFullscreen ( ) ) ;
2022-05-06 11:03:16 +00:00
const bool changing_surfaceless = ( ! m_display_widget ! = surfaceless ) ;
if ( fullscreen = = is_fullscreen & & is_rendering_to_main = = render_to_main & & ! changing_surfaceless )
2021-12-13 12:12:54 +00:00
return m_display_widget ;
// Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off.
2022-04-15 08:45:41 +00:00
// .. except on Wayland, where everything tends to break if you don't recreate.
2021-12-13 12:12:54 +00:00
const bool has_container = ( m_display_container ! = nullptr ) ;
2022-07-09 06:59:13 +00:00
const bool needs_container = DisplayContainer : : isNeeded ( fullscreen , render_to_main ) ;
2022-12-04 08:03:30 +00:00
if ( ! is_rendering_to_main & & ! render_to_main & & ! is_exclusive_fullscreen & & has_container = = needs_container & & ! needs_container & &
! changing_surfaceless )
2021-12-13 12:12:54 +00:00
{
2022-05-07 03:52:13 +00:00
DevCon . WriteLn ( " Toggling to %s without recreating surface " , ( fullscreen ? " fullscreen " : " windowed " ) ) ;
2022-09-07 10:19:40 +00:00
if ( g_host_display - > IsFullscreen ( ) )
g_host_display - > SetFullscreen ( false , 0 , 0 , 0.0f ) ;
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 ) ;
return m_display_widget ;
}
2022-10-10 12:04:56 +00:00
g_host_display - > DestroySurface ( ) ;
2021-12-13 12:12:54 +00:00
2022-07-09 06:59:13 +00:00
destroyDisplayWidget ( surfaceless ) ;
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 )
return nullptr ;
2022-07-09 06:59:13 +00:00
createDisplayWidget ( fullscreen , render_to_main , is_exclusive_fullscreen ) ;
std : : optional < WindowInfo > wi = m_display_widget - > getWindowInfo ( ) ;
if ( ! wi . has_value ( ) )
{
QMessageBox : : critical ( this , tr ( " Error " ) , tr ( " Failed to get new window info from widget " ) ) ;
destroyDisplayWidget ( true ) ;
return nullptr ;
}
g_emu_thread - > connectDisplaySignals ( m_display_widget ) ;
2022-10-10 12:04:56 +00:00
if ( ! g_host_display - > ChangeWindow ( wi . value ( ) ) )
2022-07-09 06:59:13 +00:00
pxFailRel ( " Failed to recreate surface on new widget. " ) ;
if ( is_exclusive_fullscreen )
setDisplayFullscreen ( fullscreen_mode ) ;
updateWindowTitle ( ) ;
updateWindowState ( ) ;
2022-12-04 15:00:06 +00:00
updateDisplayWidgetCursor ( ) ;
2022-07-09 06:59:13 +00:00
m_display_widget - > setFocus ( ) ;
return m_display_widget ;
}
void MainWindow : : createDisplayWidget ( bool fullscreen , bool render_to_main , bool is_exclusive_fullscreen )
{
// 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 )
restoreDisplayWindowGeometryFromConfig ( ) ;
2021-12-13 12:12:54 +00:00
if ( ! is_exclusive_fullscreen )
container - > showFullScreen ( ) ;
else
container - > showNormal ( ) ;
}
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
}
2022-12-04 15:00:06 +00:00
void MainWindow : : relativeMouseModeRequested ( bool enabled )
{
if ( m_relative_mouse_mode = = enabled )
return ;
m_relative_mouse_mode = enabled ;
if ( s_vm_valid & & ! s_vm_paused )
updateDisplayWidgetCursor ( ) ;
}
2022-03-12 14:52:52 +00:00
void MainWindow : : destroyDisplay ( )
{
2022-06-28 11:37:30 +00:00
// Now we can safely destroy the display window.
2022-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 ) ;
2022-05-15 08:20:21 +00:00
m_ui . actionStartFullscreenUI - > setEnabled ( true ) ;
m_ui . actionStartFullscreenUI2 - > setEnabled ( true ) ;
2022-07-09 06:59:13 +00:00
}
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 ( )
{
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
}
void MainWindow : : setDisplayFullscreen ( const std : : string & fullscreen_mode )
{
u32 width , height ;
float refresh_rate ;
if ( HostDisplay : : ParseFullscreenMode ( fullscreen_mode , & width , & height , & refresh_rate ) )
{
2022-09-07 10:19:40 +00:00
if ( g_host_display - > SetFullscreen ( true , width , height , refresh_rate ) )
2021-12-13 12:12:54 +00:00
{
2022-11-22 14:49:45 +00:00
Host : : AddOSDMessage ( " Acquired exclusive fullscreen. " , Host : : OSD_INFO_DURATION ) ;
2021-12-13 12:12:54 +00:00
}
else
{
2022-11-22 14:49:45 +00:00
Host : : AddOSDMessage ( " Failed to acquire exclusive fullscreen. " , Host : : OSD_WARNING_DURATION ) ;
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 ) ;
2021-12-13 12:12:54 +00:00
}
return m_settings_dialog ;
}
2022-02-15 14:29:18 +00:00
void MainWindow : : doSettings ( const char * category /* = nullptr */ )
2021-12-13 12:12:54 +00:00
{
SettingsDialog * dlg = getSettingsDialog ( ) ;
if ( ! dlg - > isVisible ( ) )
{
dlg - > setModal ( false ) ;
dlg - > show ( ) ;
}
2022-02-15 14:29:18 +00:00
if ( category )
2021-12-13 12:12:54 +00:00
dlg - > setCategory ( category ) ;
}
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 )
{
2022-12-04 08:03:30 +00:00
const QString filename (
QFileDialog : : getOpenFileName ( this , tr ( " Select Cover Image " ) , QString ( ) , tr ( " All Cover Image Types (*.jpg *.jpeg *.png) " ) ) ) ;
2021-12-13 12:12:54 +00:00
if ( filename . isEmpty ( ) )
return ;
if ( ! GameList : : GetCoverImagePathForEntry ( entry ) . empty ( ) )
{
2022-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 ;
}
}
const QString new_filename ( QString : : fromStdString ( GameList : : GetNewCoverImagePathForEntry ( entry , filename . toUtf8 ( ) . constData ( ) ) ) ) ;
if ( new_filename . isEmpty ( ) )
return ;
if ( QFile : : exists ( new_filename ) & & ! QFile : : remove ( new_filename ) )
{
QMessageBox : : critical ( this , tr ( " Copy Error " ) , tr ( " Failed to remove existing cover '%1' " ) . arg ( new_filename ) ) ;
return ;
}
if ( ! QFile : : copy ( filename , new_filename ) )
{
QMessageBox : : critical ( this , tr ( " Copy Error " ) , tr ( " Failed to copy '%1' to '%2' " ) . arg ( filename ) . arg ( new_filename ) ) ;
return ;
}
m_game_list_widget - > refreshGridCovers ( ) ;
}
2022-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 ) ;
m_ui . actionLoadState - > setEnabled ( load_enabled ) ;
m_ui . menuSaveState - > setEnabled ( save_enabled ) ;
m_ui . actionSaveState - > setEnabled ( save_enabled ) ;
}
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 ( ) ;
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 ;
if ( ! m_was_disc_change_request )
{
2023-01-05 02:22:47 +00:00
QMessageBox message ( QMessageBox : : Question , tr ( " Confirm Disc Change " ) ,
tr ( " Do you want to swap discs or boot the new image (via system reset)? " ) ) ;
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 ( )
{
const bool was_fullscreen = isRenderingFullscreen ( ) ;
2022-06-28 12:53:26 +00:00
const bool was_paused = s_vm_paused ;
2022-05-07 03:52:13 +00:00
// We use surfaceless rather than switching out of fullscreen, because
// we're paused, so we're not going to be rendering anyway.
if ( was_fullscreen )
g_emu_thread - > setSurfaceless ( true ) ;
if ( ! was_paused )
g_emu_thread - > setVMPaused ( true ) ;
// We want to parent dialogs to the display widget, except if we were fullscreen,
// since it's going to get destroyed by the surfaceless call above.
QWidget * dialog_parent = was_fullscreen ? static_cast < QWidget * > ( this ) : getDisplayContainer ( ) ;
return VMLock ( dialog_parent , was_paused , was_fullscreen ) ;
}
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 )
g_emu_thread - > setSurfaceless ( false ) ;
if ( ! m_was_paused )
g_emu_thread - > setVMPaused ( false ) ;
}
2022-06-20 08:03:38 +00:00
void MainWindow : : VMLock : : cancelResume ( )
{
m_was_paused = true ;
m_was_fullscreen = false ;
}
2022-06-28 12:53:26 +00:00
bool QtHost : : IsVMValid ( )
{
return s_vm_valid ;
}
bool QtHost : : IsVMPaused ( )
{
return s_vm_paused ;
}