2021-12-13 12:12:54 +00:00
/* PCSX2 - PS2 Emulator for PCs
* Copyright ( C ) 2002 - 2022 PCSX2 Dev Team
*
* PCSX2 is free software : you can redistribute it and / or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found -
* ation , either version 3 of the License , or ( at your option ) any later version .
*
* PCSX2 is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE . See the GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along with PCSX2 .
* If not , see < http : //www.gnu.org/licenses/>.
*/
# include "PrecompiledHeader.h"
2022-09-07 09:20:50 +00:00
# include <cmath>
2022-03-04 10:40:03 +00:00
# include <csignal>
2022-09-07 09:20:50 +00:00
# include "common/Assertions.h"
# include "common/Console.h"
# include "common/CrashHandler.h"
# include "common/Exceptions.h"
# include "common/FileSystem.h"
# include "common/Path.h"
# include "common/SettingsWrapper.h"
# include "common/StringUtil.h"
# include "common/Timer.h"
# include "pcsx2/CDVD/CDVD.h"
# include "pcsx2/CDVD/CDVDcommon.h"
# include "pcsx2/Counters.h"
# include "pcsx2/DebugTools/Debug.h"
# include "pcsx2/Frontend/CommonHost.h"
# include "pcsx2/Frontend/FullscreenUI.h"
# include "pcsx2/Frontend/GameList.h"
# include "pcsx2/Frontend/InputManager.h"
# include "pcsx2/Frontend/ImGuiManager.h"
# include "pcsx2/Frontend/LogSink.h"
# include "pcsx2/GS.h"
# include "pcsx2/GS/GS.h"
# include "pcsx2/GSDumpReplayer.h"
# include "pcsx2/HostDisplay.h"
# include "pcsx2/HostSettings.h"
2022-09-14 12:41:50 +00:00
# include "pcsx2/INISettingsInterface.h"
2022-09-07 09:20:50 +00:00
# include "pcsx2/PAD/Host/PAD.h"
# include "pcsx2/PerformanceMetrics.h"
# include "pcsx2/VMManager.h"
2022-03-04 10:40:03 +00:00
# include <QtCore/QTimer>
2022-07-09 09:07:28 +00:00
# include <QtWidgets/QApplication>
2022-03-04 10:40:03 +00:00
# include <QtWidgets/QMessageBox>
2022-08-20 10:20:36 +00:00
# include <QtGui/QClipboard>
2022-09-03 12:29:36 +00:00
# include <QtGui/QInputMethod>
2022-03-04 10:40:03 +00:00
2022-09-17 12:47:35 +00:00
# include "fmt/core.h"
2022-09-07 09:20:50 +00:00
# include "DisplayWidget.h"
# include "GameList/GameListWidget.h"
# include "MainWindow.h"
# include "QtHost.h"
# include "QtUtils.h"
# include "svnrev.h"
2022-04-18 13:35:14 +00:00
# ifdef ENABLE_ACHIEVEMENTS
# include "Frontend/Achievements.h"
# endif
2022-09-07 09:20:50 +00:00
static constexpr u32 SETTINGS_SAVE_DELAY = 1000 ;
EmuThread * g_emu_thread = nullptr ;
//////////////////////////////////////////////////////////////////////////
// Local function declarations
//////////////////////////////////////////////////////////////////////////
namespace QtHost {
static void PrintCommandLineVersion ( ) ;
2022-09-17 12:47:35 +00:00
static void PrintCommandLineHelp ( const std : : string_view & progname ) ;
2022-09-07 09:20:50 +00:00
static std : : shared_ptr < VMBootParameters > & AutoBoot ( std : : shared_ptr < VMBootParameters > & autoboot ) ;
2022-09-17 12:47:35 +00:00
static bool ParseCommandLineOptions ( const QStringList & args , std : : shared_ptr < VMBootParameters > & autoboot ) ;
2022-09-07 09:20:50 +00:00
static bool InitializeConfig ( ) ;
static void SaveSettings ( ) ;
static void HookSignals ( ) ;
}
//////////////////////////////////////////////////////////////////////////
// Local variable declarations
//////////////////////////////////////////////////////////////////////////
const IConsoleWriter * PatchesCon = & Console ;
static std : : unique_ptr < QTimer > s_settings_save_timer ;
static std : : unique_ptr < INISettingsInterface > s_base_settings_interface ;
static bool s_batch_mode = false ;
static bool s_nogui_mode = false ;
static bool s_start_fullscreen_ui = false ;
static bool s_start_fullscreen_ui_fullscreen = false ;
//////////////////////////////////////////////////////////////////////////
// CPU Thread
//////////////////////////////////////////////////////////////////////////
EmuThread : : EmuThread ( QThread * ui_thread )
: QThread ( )
, m_ui_thread ( ui_thread )
{
}
EmuThread : : ~ EmuThread ( ) = default ;
bool EmuThread : : isOnEmuThread ( ) const
{
return QThread : : currentThread ( ) = = this ;
}
void EmuThread : : start ( )
{
pxAssertRel ( ! g_emu_thread , " Emu thread does not exist " ) ;
g_emu_thread = new EmuThread ( QThread : : currentThread ( ) ) ;
g_emu_thread - > QThread : : start ( ) ;
g_emu_thread - > m_started_semaphore . acquire ( ) ;
g_emu_thread - > moveToThread ( g_emu_thread ) ;
}
void EmuThread : : stop ( )
{
pxAssertRel ( g_emu_thread , " Emu thread exists " ) ;
pxAssertRel ( ! g_emu_thread - > isOnEmuThread ( ) , " Not called on the emu thread " ) ;
QMetaObject : : invokeMethod ( g_emu_thread , & EmuThread : : stopInThread , Qt : : QueuedConnection ) ;
while ( g_emu_thread - > isRunning ( ) )
QApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents , 1 ) ;
}
void EmuThread : : stopInThread ( )
{
if ( VMManager : : HasValidVM ( ) )
destroyVM ( ) ;
if ( m_run_fullscreen_ui )
stopFullscreenUI ( ) ;
m_event_loop - > quit ( ) ;
m_shutdown_flag . store ( true ) ;
}
bool EmuThread : : confirmMessage ( const QString & title , const QString & message )
{
if ( ! isOnEmuThread ( ) )
{
// This is definitely deadlock risky, but unlikely to happen (why would GS be confirming?).
bool result = false ;
QMetaObject : : invokeMethod ( g_emu_thread , " confirmMessage " , Qt : : BlockingQueuedConnection ,
Q_RETURN_ARG ( bool , result ) , Q_ARG ( const QString & , title ) , Q_ARG ( const QString & , message ) ) ;
return result ;
}
// Easy if there's no VM.
if ( ! VMManager : : HasValidVM ( ) )
return emit messageConfirmed ( title , message ) ;
// Preemptively pause/set surfaceless on the emu thread, because it can't run while the popup is open.
const bool was_paused = ( VMManager : : GetState ( ) = = VMState : : Paused ) ;
const bool was_fullscreen = isFullscreen ( ) ;
if ( ! was_paused )
VMManager : : SetPaused ( true ) ;
if ( was_fullscreen )
setSurfaceless ( true ) ;
// This won't return until the user confirms one way or another.
const bool result = emit messageConfirmed ( title , message ) ;
// Resume VM after confirming.
if ( was_fullscreen )
setSurfaceless ( false ) ;
if ( ! was_paused )
VMManager : : SetPaused ( false ) ;
return result ;
}
void EmuThread : : startFullscreenUI ( bool fullscreen )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " startFullscreenUI " , Qt : : QueuedConnection , Q_ARG ( bool , fullscreen ) ) ;
return ;
}
if ( VMManager : : HasValidVM ( ) )
return ;
m_run_fullscreen_ui = true ;
if ( fullscreen )
m_is_fullscreen = true ;
if ( ! GetMTGS ( ) . WaitForOpen ( ) )
{
m_run_fullscreen_ui = false ;
return ;
}
// poll more frequently so we don't lose events
stopBackgroundControllerPollTimer ( ) ;
startBackgroundControllerPollTimer ( ) ;
}
void EmuThread : : stopFullscreenUI ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : stopFullscreenUI , Qt : : QueuedConnection ) ;
// wait until the host display is gone
2022-09-07 10:19:40 +00:00
while ( g_host_display )
2022-09-07 09:20:50 +00:00
QApplication : : processEvents ( QEventLoop : : ExcludeUserInputEvents , 1 ) ;
return ;
}
2022-09-07 10:19:40 +00:00
if ( ! g_host_display )
2022-09-07 09:20:50 +00:00
return ;
pxAssertRel ( ! VMManager : : HasValidVM ( ) , " VM is not valid at FSUI shutdown time " ) ;
m_run_fullscreen_ui = false ;
GetMTGS ( ) . WaitForClose ( ) ;
}
void EmuThread : : startVM ( std : : shared_ptr < VMBootParameters > boot_params )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " startVM " , Qt : : QueuedConnection ,
Q_ARG ( std : : shared_ptr < VMBootParameters > , boot_params ) ) ;
return ;
}
pxAssertRel ( ! VMManager : : HasValidVM ( ) , " VM is shut down " ) ;
2022-09-14 12:41:50 +00:00
// Determine whether to start fullscreen or not.
2022-09-07 09:20:50 +00:00
if ( boot_params - > fullscreen . has_value ( ) )
m_is_fullscreen = boot_params - > fullscreen . value ( ) ;
2022-09-14 12:41:50 +00:00
else
m_is_fullscreen = Host : : GetBaseBoolSettingValue ( " UI " , " StartFullscreen " , false ) ;
2022-09-07 09:20:50 +00:00
if ( ! VMManager : : Initialize ( * boot_params ) )
return ;
if ( ! Host : : GetBoolSettingValue ( " UI " , " StartPaused " , false ) )
{
// This will come back and call OnVMResumed().
VMManager : : SetState ( VMState : : Running ) ;
}
else
{
// When starting paused, redraw the window, so there's at least something there.
redrawDisplayWindow ( ) ;
Host : : OnVMPaused ( ) ;
}
m_event_loop - > quit ( ) ;
}
void EmuThread : : resetVM ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : resetVM , Qt : : QueuedConnection ) ;
return ;
}
VMManager : : Reset ( ) ;
}
void EmuThread : : setVMPaused ( bool paused )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " setVMPaused " , Qt : : QueuedConnection , Q_ARG ( bool , paused ) ) ;
return ;
}
VMManager : : SetPaused ( paused ) ;
}
void EmuThread : : shutdownVM ( bool save_state /* = true */ )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " shutdownVM " , Qt : : QueuedConnection , Q_ARG ( bool , save_state ) ) ;
return ;
}
const VMState state = VMManager : : GetState ( ) ;
if ( state = = VMState : : Paused )
m_event_loop - > quit ( ) ;
else if ( state ! = VMState : : Running )
return ;
m_save_state_on_shutdown = save_state ;
VMManager : : SetState ( VMState : : Stopping ) ;
}
void EmuThread : : loadState ( const QString & filename )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " loadState " , Qt : : QueuedConnection , Q_ARG ( const QString & , filename ) ) ;
return ;
}
if ( ! VMManager : : HasValidVM ( ) )
return ;
VMManager : : LoadState ( filename . toUtf8 ( ) . constData ( ) ) ;
}
void EmuThread : : loadStateFromSlot ( qint32 slot )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " loadStateFromSlot " , Qt : : QueuedConnection , Q_ARG ( qint32 , slot ) ) ;
return ;
}
if ( ! VMManager : : HasValidVM ( ) )
return ;
VMManager : : LoadStateFromSlot ( slot ) ;
}
void EmuThread : : saveState ( const QString & filename )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " saveState " , Qt : : QueuedConnection , Q_ARG ( const QString & , filename ) ) ;
return ;
}
if ( ! VMManager : : HasValidVM ( ) )
return ;
if ( ! VMManager : : SaveState ( filename . toUtf8 ( ) . constData ( ) ) )
{
// this one is usually the result of a user-chosen path, so we can display a message box safely here
Console . Error ( " Failed to save state " ) ;
}
}
void EmuThread : : saveStateToSlot ( qint32 slot )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " saveStateToSlot " , Qt : : QueuedConnection , Q_ARG ( qint32 , slot ) ) ;
return ;
}
if ( ! VMManager : : HasValidVM ( ) )
return ;
VMManager : : SaveStateToSlot ( slot ) ;
}
void EmuThread : : run ( )
{
2022-09-14 12:41:50 +00:00
// Qt-specific initialization.
2022-09-07 09:20:50 +00:00
m_event_loop = new QEventLoop ( ) ;
m_started_semaphore . release ( ) ;
2022-09-14 12:41:50 +00:00
connectSignals ( ) ;
2022-09-07 09:20:50 +00:00
2022-09-14 12:41:50 +00:00
// Common host initialization (VM setup, etc).
CommonHost : : CPUThreadInitialize ( ) ;
2022-09-07 09:20:50 +00:00
// Start background polling because the VM won't do it for us.
createBackgroundControllerPollTimer ( ) ;
startBackgroundControllerPollTimer ( ) ;
2022-09-14 12:41:50 +00:00
// Main CPU thread loop.
2022-09-07 09:20:50 +00:00
while ( ! m_shutdown_flag . load ( ) )
{
if ( ! VMManager : : HasValidVM ( ) )
{
m_event_loop - > exec ( ) ;
continue ;
}
executeVM ( ) ;
}
2022-09-14 12:41:50 +00:00
// Teardown in reverse order.
2022-09-07 09:20:50 +00:00
stopBackgroundControllerPollTimer ( ) ;
destroyBackgroundControllerPollTimer ( ) ;
2022-09-14 12:41:50 +00:00
CommonHost : : CPUThreadShutdown ( ) ;
// Move back to the UI thread, since we're no longer running.
2022-09-07 09:20:50 +00:00
moveToThread ( m_ui_thread ) ;
deleteLater ( ) ;
}
void EmuThread : : destroyVM ( )
{
m_last_speed = 0.0f ;
m_last_game_fps = 0.0f ;
m_last_video_fps = 0.0f ;
m_last_internal_width = 0 ;
m_last_internal_height = 0 ;
m_was_paused_by_focus_loss = false ;
VMManager : : Shutdown ( m_save_state_on_shutdown ) ;
2022-09-14 12:41:50 +00:00
m_save_state_on_shutdown = false ;
2022-09-07 09:20:50 +00:00
}
void EmuThread : : executeVM ( )
{
for ( ; ; )
{
switch ( VMManager : : GetState ( ) )
{
case VMState : : Initializing :
pxFailRel ( " Shouldn't be in the starting state state " ) ;
continue ;
case VMState : : Paused :
m_event_loop - > exec ( ) ;
continue ;
case VMState : : Running :
m_event_loop - > processEvents ( QEventLoop : : AllEvents ) ;
VMManager : : Execute ( ) ;
continue ;
case VMState : : Stopping :
destroyVM ( ) ;
m_event_loop - > processEvents ( QEventLoop : : AllEvents ) ;
return ;
default :
continue ;
}
}
}
void EmuThread : : createBackgroundControllerPollTimer ( )
{
pxAssert ( ! m_background_controller_polling_timer ) ;
m_background_controller_polling_timer = new QTimer ( this ) ;
m_background_controller_polling_timer - > setSingleShot ( false ) ;
m_background_controller_polling_timer - > setTimerType ( Qt : : CoarseTimer ) ;
connect ( m_background_controller_polling_timer , & QTimer : : timeout , this , & EmuThread : : doBackgroundControllerPoll ) ;
}
void EmuThread : : destroyBackgroundControllerPollTimer ( )
{
delete m_background_controller_polling_timer ;
m_background_controller_polling_timer = nullptr ;
}
void EmuThread : : startBackgroundControllerPollTimer ( )
{
if ( m_background_controller_polling_timer - > isActive ( ) )
return ;
m_background_controller_polling_timer - > start ( FullscreenUI : : IsInitialized ( ) ?
FULLSCREEN_UI_CONTROLLER_POLLING_INTERVAL :
BACKGROUND_CONTROLLER_POLLING_INTERVAL ) ;
}
void EmuThread : : stopBackgroundControllerPollTimer ( )
{
if ( ! m_background_controller_polling_timer - > isActive ( ) )
return ;
m_background_controller_polling_timer - > stop ( ) ;
}
void EmuThread : : doBackgroundControllerPoll ( )
{
InputManager : : PollSources ( ) ;
}
void EmuThread : : toggleFullscreen ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : toggleFullscreen , Qt : : QueuedConnection ) ;
return ;
}
setFullscreen ( ! m_is_fullscreen ) ;
}
void EmuThread : : setFullscreen ( bool fullscreen )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " setFullscreen " , Qt : : QueuedConnection , Q_ARG ( bool , fullscreen ) ) ;
return ;
}
if ( ! GetMTGS ( ) . IsOpen ( ) | | m_is_fullscreen = = fullscreen )
return ;
// This will call back to us on the MTGS thread.
m_is_fullscreen = fullscreen ;
GetMTGS ( ) . UpdateDisplayWindow ( ) ;
GetMTGS ( ) . WaitGS ( ) ;
// If we're using exclusive fullscreen, the refresh rate may have changed.
UpdateVSyncRate ( ) ;
}
void EmuThread : : setSurfaceless ( bool surfaceless )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " setSurfaceless " , Qt : : QueuedConnection , Q_ARG ( bool , surfaceless ) ) ;
return ;
}
if ( ! GetMTGS ( ) . IsOpen ( ) | | m_is_surfaceless = = surfaceless )
return ;
// If we went surfaceless and were running the fullscreen UI, stop MTGS running idle.
// Otherwise, we'll keep trying to present to nothing.
GetMTGS ( ) . SetRunIdle ( ! surfaceless & & m_run_fullscreen_ui ) ;
// This will call back to us on the MTGS thread.
m_is_surfaceless = surfaceless ;
GetMTGS ( ) . UpdateDisplayWindow ( ) ;
GetMTGS ( ) . WaitGS ( ) ;
}
void EmuThread : : applySettings ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : applySettings , Qt : : QueuedConnection ) ;
return ;
}
VMManager : : ApplySettings ( ) ;
}
void EmuThread : : reloadGameSettings ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : reloadGameSettings , Qt : : QueuedConnection ) ;
return ;
}
// this will skip applying settings when they're not active
2022-09-14 12:41:50 +00:00
VMManager : : ReloadGameSettings ( ) ;
2022-09-07 09:20:50 +00:00
}
void EmuThread : : updateEmuFolders ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : updateEmuFolders , Qt : : QueuedConnection ) ;
return ;
}
Host : : Internal : : UpdateEmuFolders ( ) ;
}
2022-09-14 12:41:50 +00:00
void EmuThread : : connectSignals ( )
2022-09-07 09:20:50 +00:00
{
2022-09-14 12:41:50 +00:00
connect ( qApp , & QGuiApplication : : applicationStateChanged , this , & EmuThread : : onApplicationStateChanged ) ;
2022-09-07 09:20:50 +00:00
}
2022-09-14 12:41:50 +00:00
void EmuThread : : loadSettings ( SettingsInterface & si , std : : unique_lock < std : : mutex > & lock )
2022-09-07 09:20:50 +00:00
{
2022-09-14 12:41:50 +00:00
m_verbose_status = si . GetBoolValue ( " UI " , " VerboseStatusBar " , false ) ;
m_pause_on_focus_loss = si . GetBoolValue ( " UI " , " PauseOnFocusLoss " , false ) ;
2022-09-07 09:20:50 +00:00
}
2022-09-14 12:41:50 +00:00
void Host : : LoadSettings ( SettingsInterface & si , std : : unique_lock < std : : mutex > & lock )
2022-09-07 09:20:50 +00:00
{
2022-09-14 12:41:50 +00:00
CommonHost : : LoadSettings ( si , lock ) ;
g_emu_thread - > loadSettings ( si , lock ) ;
2022-09-07 09:20:50 +00:00
}
2022-09-14 12:41:50 +00:00
void EmuThread : : checkForSettingChanges ( const Pcsx2Config & old_config )
2022-09-07 09:20:50 +00:00
{
QMetaObject : : invokeMethod ( g_main_window , & MainWindow : : checkForSettingChanges , Qt : : QueuedConnection ) ;
2022-09-07 10:19:40 +00:00
if ( g_host_display )
2022-09-07 09:20:50 +00:00
{
const bool render_to_main = shouldRenderToMain ( ) ;
if ( ! m_is_fullscreen & & m_is_rendering_to_main ! = render_to_main )
{
m_is_rendering_to_main = render_to_main ;
GetMTGS ( ) . UpdateDisplayWindow ( ) ;
GetMTGS ( ) . WaitGS ( ) ;
}
}
2022-09-14 12:41:50 +00:00
updatePerformanceMetrics ( true ) ;
}
2022-09-07 09:20:50 +00:00
2022-09-14 12:41:50 +00:00
void Host : : CheckForSettingsChanges ( const Pcsx2Config & old_config )
{
CommonHost : : CheckForSettingsChanges ( old_config ) ;
g_emu_thread - > checkForSettingChanges ( old_config ) ;
2022-09-07 09:20:50 +00:00
}
bool EmuThread : : shouldRenderToMain ( ) const
{
return ! Host : : GetBaseBoolSettingValue ( " UI " , " RenderToSeparateWindow " , false ) & & ! QtHost : : InNoGUIMode ( ) ;
}
void EmuThread : : toggleSoftwareRendering ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : toggleSoftwareRendering , Qt : : QueuedConnection ) ;
return ;
}
if ( ! VMManager : : HasValidVM ( ) )
return ;
GetMTGS ( ) . ToggleSoftwareRendering ( ) ;
}
void EmuThread : : switchRenderer ( GSRendererType renderer )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " switchRenderer " , Qt : : QueuedConnection , Q_ARG ( GSRendererType , renderer ) ) ;
return ;
}
if ( ! VMManager : : HasValidVM ( ) )
return ;
GetMTGS ( ) . SwitchRenderer ( renderer ) ;
}
void EmuThread : : changeDisc ( CDVD_SourceType source , const QString & path )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " changeDisc " , Qt : : QueuedConnection , Q_ARG ( CDVD_SourceType , source ) , Q_ARG ( const QString & , path ) ) ;
return ;
}
if ( ! VMManager : : HasValidVM ( ) )
return ;
VMManager : : ChangeDisc ( source , path . toStdString ( ) ) ;
}
void EmuThread : : reloadPatches ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : reloadPatches , Qt : : QueuedConnection ) ;
return ;
}
if ( ! VMManager : : HasValidVM ( ) )
return ;
VMManager : : ReloadPatches ( true , true ) ;
}
void EmuThread : : reloadInputSources ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : reloadInputSources , Qt : : QueuedConnection ) ;
return ;
}
std : : unique_lock < std : : mutex > lock = Host : : GetSettingsLock ( ) ;
SettingsInterface * si = Host : : GetSettingsInterface ( ) ;
SettingsInterface * bindings_si = Host : : GetSettingsInterfaceForBindings ( ) ;
InputManager : : ReloadSources ( * si , lock ) ;
// skip loading bindings if we're not running, since it'll get done on startup anyway
if ( VMManager : : HasValidVM ( ) )
InputManager : : ReloadBindings ( * si , * bindings_si ) ;
}
void EmuThread : : reloadInputBindings ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : reloadInputBindings , Qt : : QueuedConnection ) ;
return ;
}
// skip loading bindings if we're not running, since it'll get done on startup anyway
if ( ! VMManager : : HasValidVM ( ) )
return ;
auto lock = Host : : GetSettingsLock ( ) ;
SettingsInterface * si = Host : : GetSettingsInterface ( ) ;
SettingsInterface * bindings_si = Host : : GetSettingsInterfaceForBindings ( ) ;
InputManager : : ReloadBindings ( * si , * bindings_si ) ;
}
2022-10-15 11:20:05 +00:00
void EmuThread : : reloadInputDevices ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : reloadInputDevices , Qt : : QueuedConnection ) ;
return ;
}
InputManager : : ReloadDevices ( ) ;
}
void EmuThread : : closeInputSources ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : reloadInputDevices , Qt : : BlockingQueuedConnection ) ;
return ;
}
InputManager : : CloseSources ( ) ;
}
2022-09-07 09:20:50 +00:00
void EmuThread : : requestDisplaySize ( float scale )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " requestDisplaySize " , Qt : : QueuedConnection , Q_ARG ( float , scale ) ) ;
return ;
}
if ( ! VMManager : : HasValidVM ( ) )
return ;
VMManager : : RequestDisplaySize ( scale ) ;
}
void EmuThread : : enumerateInputDevices ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : enumerateInputDevices , Qt : : QueuedConnection ) ;
return ;
}
const std : : vector < std : : pair < std : : string , std : : string > > devs ( InputManager : : EnumerateDevices ( ) ) ;
QList < QPair < QString , QString > > qdevs ;
qdevs . reserve ( devs . size ( ) ) ;
for ( const std : : pair < std : : string , std : : string > & dev : devs )
qdevs . emplace_back ( QString : : fromStdString ( dev . first ) , QString : : fromStdString ( dev . second ) ) ;
onInputDevicesEnumerated ( qdevs ) ;
}
2022-03-04 10:40:03 +00:00
2022-09-07 09:20:50 +00:00
void EmuThread : : enumerateVibrationMotors ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : enumerateVibrationMotors , Qt : : QueuedConnection ) ;
return ;
}
2021-12-13 12:12:54 +00:00
2022-09-07 09:20:50 +00:00
const std : : vector < InputBindingKey > motors ( InputManager : : EnumerateMotors ( ) ) ;
QList < InputBindingKey > qmotors ;
qmotors . reserve ( motors . size ( ) ) ;
for ( InputBindingKey key : motors )
qmotors . push_back ( key ) ;
2021-12-13 12:12:54 +00:00
2022-09-07 09:20:50 +00:00
onVibrationMotorsEnumerated ( qmotors ) ;
}
2021-12-13 12:12:54 +00:00
2022-09-07 09:20:50 +00:00
void EmuThread : : connectDisplaySignals ( DisplayWidget * widget )
{
widget - > disconnect ( this ) ;
2021-12-13 12:12:54 +00:00
2022-09-07 09:20:50 +00:00
connect ( widget , & DisplayWidget : : windowResizedEvent , this , & EmuThread : : onDisplayWindowResized ) ;
connect ( widget , & DisplayWidget : : windowRestoredEvent , this , & EmuThread : : redrawDisplayWindow ) ;
2022-03-04 10:40:03 +00:00
}
2021-12-13 12:12:54 +00:00
2022-09-07 09:20:50 +00:00
void EmuThread : : onDisplayWindowResized ( int width , int height , float scale )
{
2022-09-07 10:19:40 +00:00
if ( ! g_host_display )
2022-09-07 09:20:50 +00:00
return ;
2021-12-13 12:12:54 +00:00
2022-09-07 09:20:50 +00:00
GetMTGS ( ) . ResizeDisplayWindow ( width , height , scale ) ;
}
void EmuThread : : onApplicationStateChanged ( Qt : : ApplicationState state )
{
// NOTE: This is executed on the emu thread, not UI thread.
if ( ! m_pause_on_focus_loss | | ! VMManager : : HasValidVM ( ) )
return ;
const bool focus_loss = ( state ! = Qt : : ApplicationActive ) ;
if ( focus_loss )
{
if ( ! m_was_paused_by_focus_loss & & VMManager : : GetState ( ) = = VMState : : Running )
{
m_was_paused_by_focus_loss = true ;
VMManager : : SetPaused ( true ) ;
}
}
else
{
if ( m_was_paused_by_focus_loss )
{
m_was_paused_by_focus_loss = false ;
if ( VMManager : : GetState ( ) = = VMState : : Paused )
VMManager : : SetPaused ( false ) ;
}
}
}
void EmuThread : : redrawDisplayWindow ( )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , & EmuThread : : redrawDisplayWindow , Qt : : QueuedConnection ) ;
return ;
}
// If we're running, we're going to re-present anyway.
if ( ! VMManager : : HasValidVM ( ) | | VMManager : : GetState ( ) = = VMState : : Running )
return ;
GetMTGS ( ) . RunOnGSThread ( [ ] ( ) { GetMTGS ( ) . PresentCurrentFrame ( ) ; } ) ;
}
void EmuThread : : runOnCPUThread ( const std : : function < void ( ) > & func )
{
func ( ) ;
}
void EmuThread : : queueSnapshot ( quint32 gsdump_frames )
{
if ( ! isOnEmuThread ( ) )
{
QMetaObject : : invokeMethod ( this , " queueSnapshot " , Qt : : QueuedConnection , Q_ARG ( quint32 , gsdump_frames ) ) ;
return ;
}
if ( ! VMManager : : HasValidVM ( ) )
return ;
GetMTGS ( ) . RunOnGSThread ( [ gsdump_frames ] ( ) {
GSQueueSnapshot ( std : : string ( ) , gsdump_frames ) ;
} ) ;
}
void EmuThread : : updateDisplay ( )
{
pxAssertRel ( ! isOnEmuThread ( ) , " Not on emu thread " ) ;
// finished with the display for now
2022-10-10 12:04:56 +00:00
g_host_display - > DoneCurrent ( ) ;
2022-09-07 09:20:50 +00:00
// but we should get it back after this call
onUpdateDisplayRequested ( m_is_fullscreen , ! m_is_fullscreen & & m_is_rendering_to_main , m_is_surfaceless ) ;
2022-10-10 12:04:56 +00:00
if ( ! g_host_display - > MakeCurrent ( ) )
2022-09-07 09:20:50 +00:00
{
pxFailRel ( " Failed to recreate context after updating " ) ;
return ;
}
}
2022-10-10 12:04:56 +00:00
bool EmuThread : : acquireHostDisplay ( RenderAPI api )
2022-09-07 09:20:50 +00:00
{
2022-09-07 10:19:40 +00:00
pxAssertRel ( ! g_host_display , " Host display does not exist on create " ) ;
2022-09-14 12:41:50 +00:00
m_is_rendering_to_main = shouldRenderToMain ( ) ;
m_is_surfaceless = false ;
2022-09-07 10:19:40 +00:00
2022-10-10 12:04:56 +00:00
g_host_display = HostDisplay : : CreateForAPI ( api ) ;
2022-09-07 10:19:40 +00:00
if ( ! g_host_display )
return false ;
2022-09-07 09:20:50 +00:00
DisplayWidget * widget = emit onCreateDisplayRequested ( m_is_fullscreen , m_is_rendering_to_main ) ;
if ( ! widget )
{
2022-09-07 10:19:40 +00:00
g_host_display . reset ( ) ;
return false ;
2022-09-07 09:20:50 +00:00
}
connectDisplaySignals ( widget ) ;
2022-10-10 12:04:56 +00:00
if ( ! g_host_display - > MakeCurrent ( ) )
2022-09-07 09:20:50 +00:00
{
Console . Error ( " Failed to make render context current " ) ;
releaseHostDisplay ( ) ;
2022-09-07 10:19:40 +00:00
return false ;
2022-09-07 09:20:50 +00:00
}
2022-10-10 12:04:56 +00:00
if ( ! g_host_display - > SetupDevice ( ) | | ! ImGuiManager : : Initialize ( ) )
2022-09-07 09:20:50 +00:00
{
Console . Error ( " Failed to initialize device/imgui " ) ;
releaseHostDisplay ( ) ;
2022-09-07 10:19:40 +00:00
return false ;
2022-09-07 09:20:50 +00:00
}
2022-09-07 10:19:40 +00:00
Console . WriteLn ( Color_StrongGreen , " %s Graphics Driver Info: " , HostDisplay : : RenderAPIToString ( g_host_display - > GetRenderAPI ( ) ) ) ;
Console . Indent ( ) . WriteLn ( g_host_display - > GetDriverInfo ( ) ) ;
2022-09-07 09:20:50 +00:00
if ( m_run_fullscreen_ui & & ! FullscreenUI : : Initialize ( ) )
{
Console . Error ( " Failed to initialize fullscreen UI " ) ;
releaseHostDisplay ( ) ;
m_run_fullscreen_ui = false ;
2022-09-07 10:19:40 +00:00
return false ;
2022-09-07 09:20:50 +00:00
}
2022-09-07 10:19:40 +00:00
return true ;
2022-09-07 09:20:50 +00:00
}
void EmuThread : : releaseHostDisplay ( )
{
ImGuiManager : : Shutdown ( ) ;
2022-09-07 10:19:40 +00:00
g_host_display . reset ( ) ;
2022-09-07 09:20:50 +00:00
emit onDestroyDisplayRequested ( ) ;
}
2022-10-10 12:04:56 +00:00
bool Host : : AcquireHostDisplay ( RenderAPI api )
2022-09-07 09:20:50 +00:00
{
return g_emu_thread - > acquireHostDisplay ( api ) ;
}
void Host : : ReleaseHostDisplay ( )
{
g_emu_thread - > releaseHostDisplay ( ) ;
}
2022-10-10 12:04:56 +00:00
VsyncMode Host : : GetEffectiveVSyncMode ( )
{
// Force vsync on when running big picture UI, and paused or no VM.
if ( g_emu_thread - > isRunningFullscreenUI ( ) )
{
const VMState state = VMManager : : GetState ( ) ;
if ( state = = VMState : : Shutdown | | state = = VMState : : Paused )
return VsyncMode : : On ;
}
// Force vsync off when not running at 100% speed.
if ( EmuConfig . GS . LimitScalar ! = 1.0f )
return VsyncMode : : Off ;
// Otherwise use the config setting.
return EmuConfig . GS . VsyncEnable ;
}
2022-09-07 09:20:50 +00:00
bool Host : : BeginPresentFrame ( bool frame_skip )
{
2022-09-07 10:19:40 +00:00
if ( ! g_host_display - > BeginPresent ( frame_skip ) )
2022-09-07 09:20:50 +00:00
{
// if we're skipping a frame, we need to reset imgui's state, since
// we won't be calling EndPresentFrame().
ImGuiManager : : NewFrame ( ) ;
return false ;
}
return true ;
}
void Host : : EndPresentFrame ( )
{
if ( GSDumpReplayer : : IsReplayingDump ( ) )
GSDumpReplayer : : RenderUI ( ) ;
FullscreenUI : : Render ( ) ;
ImGuiManager : : RenderOSD ( ) ;
2022-09-07 10:19:40 +00:00
g_host_display - > EndPresent ( ) ;
2022-09-07 09:20:50 +00:00
ImGuiManager : : NewFrame ( ) ;
}
void Host : : ResizeHostDisplay ( u32 new_window_width , u32 new_window_height , float new_window_scale )
{
2022-10-10 12:04:56 +00:00
g_host_display - > ResizeWindow ( new_window_width , new_window_height , new_window_scale ) ;
2022-09-07 09:20:50 +00:00
ImGuiManager : : WindowResized ( ) ;
// if we're paused, re-present the current frame at the new window size.
if ( VMManager : : GetState ( ) = = VMState : : Paused )
GetMTGS ( ) . PresentCurrentFrame ( ) ;
}
void Host : : RequestResizeHostDisplay ( s32 width , s32 height )
{
g_emu_thread - > onResizeDisplayRequested ( width , height ) ;
}
void Host : : UpdateHostDisplay ( )
{
g_emu_thread - > updateDisplay ( ) ;
ImGuiManager : : WindowResized ( ) ;
// if we're paused, re-present the current frame at the new window size.
if ( VMManager : : GetState ( ) = = VMState : : Paused )
GetMTGS ( ) . PresentCurrentFrame ( ) ;
}
void Host : : OnVMStarting ( )
{
2022-09-14 12:41:50 +00:00
CommonHost : : OnVMStarting ( ) ;
2022-09-07 09:20:50 +00:00
g_emu_thread - > stopBackgroundControllerPollTimer ( ) ;
emit g_emu_thread - > onVMStarting ( ) ;
}
void Host : : OnVMStarted ( )
{
2022-09-14 12:41:50 +00:00
CommonHost : : OnVMStarted ( ) ;
2022-09-07 09:20:50 +00:00
emit g_emu_thread - > onVMStarted ( ) ;
}
void Host : : OnVMDestroyed ( )
{
2022-09-14 12:41:50 +00:00
CommonHost : : OnVMDestroyed ( ) ;
2022-09-07 09:20:50 +00:00
emit g_emu_thread - > onVMStopped ( ) ;
g_emu_thread - > startBackgroundControllerPollTimer ( ) ;
}
void Host : : OnVMPaused ( )
{
2022-09-14 12:41:50 +00:00
CommonHost : : OnVMPaused ( ) ;
2022-09-07 09:20:50 +00:00
g_emu_thread - > startBackgroundControllerPollTimer ( ) ;
emit g_emu_thread - > onVMPaused ( ) ;
}
void Host : : OnVMResumed ( )
{
2022-09-14 12:41:50 +00:00
CommonHost : : OnVMResumed ( ) ;
2022-09-07 09:20:50 +00:00
// exit the event loop when we eventually return
g_emu_thread - > getEventLoop ( ) - > quit ( ) ;
g_emu_thread - > stopBackgroundControllerPollTimer ( ) ;
// if we were surfaceless (view->game list, system->unpause), get our display widget back
if ( g_emu_thread - > isSurfaceless ( ) )
g_emu_thread - > setSurfaceless ( false ) ;
emit g_emu_thread - > onVMResumed ( ) ;
}
2022-09-14 12:41:50 +00:00
void Host : : OnGameChanged ( const std : : string & disc_path , const std : : string & game_serial , const std : : string & game_name , u32 game_crc )
2022-09-07 09:20:50 +00:00
{
2022-09-14 12:41:50 +00:00
CommonHost : : OnGameChanged ( disc_path , game_serial , game_name , game_crc ) ;
emit g_emu_thread - > onGameChanged ( QString : : fromStdString ( disc_path ) , QString : : fromStdString ( game_serial ) , QString : : fromStdString ( game_name ) , game_crc ) ;
2022-09-07 09:20:50 +00:00
}
void EmuThread : : updatePerformanceMetrics ( bool force )
{
if ( m_verbose_status & & VMManager : : HasValidVM ( ) )
{
std : : string gs_stat_str ;
GSgetTitleStats ( gs_stat_str ) ;
QString gs_stat ;
if ( THREAD_VU1 )
{
gs_stat =
QStringLiteral ( " %1 | EE: %2% | VU: %3% | GS: %4% " )
. arg ( gs_stat_str . c_str ( ) )
. arg ( PerformanceMetrics : : GetCPUThreadUsage ( ) , 0 , ' f ' , 0 )
. arg ( PerformanceMetrics : : GetVUThreadUsage ( ) , 0 , ' f ' , 0 )
. arg ( PerformanceMetrics : : GetGSThreadUsage ( ) , 0 , ' f ' , 0 ) ;
}
else
{
gs_stat = QStringLiteral ( " %1 | EE: %2% | GS: %3% " )
. arg ( gs_stat_str . c_str ( ) )
. arg ( PerformanceMetrics : : GetCPUThreadUsage ( ) , 0 , ' f ' , 0 )
. arg ( PerformanceMetrics : : GetGSThreadUsage ( ) , 0 , ' f ' , 0 ) ;
}
QMetaObject : : invokeMethod ( g_main_window - > getStatusVerboseWidget ( ) , " setText " , Qt : : QueuedConnection , Q_ARG ( const QString & , gs_stat ) ) ;
}
const GSRendererType renderer = GSConfig . Renderer ;
const float speed = std : : round ( PerformanceMetrics : : GetSpeed ( ) ) ;
const float gfps = std : : round ( PerformanceMetrics : : GetInternalFPS ( ) ) ;
const float vfps = std : : round ( PerformanceMetrics : : GetFPS ( ) ) ;
int iwidth , iheight ;
GSgetInternalResolution ( & iwidth , & iheight ) ;
if ( iwidth ! = m_last_internal_width | | iheight ! = m_last_internal_height | |
speed ! = m_last_speed | | gfps ! = m_last_game_fps | | vfps ! = m_last_video_fps | |
renderer ! = m_last_renderer | | force )
{
if ( iwidth = = 0 & & iheight = = 0 )
{
// if we don't have width/height yet, we're not going to have fps either.
// and we'll probably be <100% due to compiling. so just leave it blank for now.
QString blank ;
QMetaObject : : invokeMethod ( g_main_window - > getStatusRendererWidget ( ) , " setText " , Qt : : QueuedConnection , Q_ARG ( const QString & , blank ) ) ;
QMetaObject : : invokeMethod ( g_main_window - > getStatusResolutionWidget ( ) , " setText " , Qt : : QueuedConnection , Q_ARG ( const QString & , blank ) ) ;
QMetaObject : : invokeMethod ( g_main_window - > getStatusFPSWidget ( ) , " setText " , Qt : : QueuedConnection , Q_ARG ( const QString & , blank ) ) ;
QMetaObject : : invokeMethod ( g_main_window - > getStatusVPSWidget ( ) , " setText " , Qt : : QueuedConnection , Q_ARG ( const QString & , blank ) ) ;
return ;
}
else
{
if ( renderer ! = m_last_renderer | | force )
{
QMetaObject : : invokeMethod ( g_main_window - > getStatusRendererWidget ( ) , " setText " , Qt : : QueuedConnection ,
Q_ARG ( const QString & , QString : : fromUtf8 ( Pcsx2Config : : GSOptions : : GetRendererName ( renderer ) ) ) ) ;
m_last_renderer = renderer ;
}
if ( iwidth ! = m_last_internal_width | | iheight ! = m_last_internal_height | | force )
{
QMetaObject : : invokeMethod ( g_main_window - > getStatusResolutionWidget ( ) , " setText " , Qt : : QueuedConnection ,
Q_ARG ( const QString & , tr ( " %1x%2 " )
. arg ( iwidth )
. arg ( iheight ) ) ) ;
m_last_internal_width = iwidth ;
m_last_internal_height = iheight ;
}
if ( gfps ! = m_last_game_fps | | force )
{
QMetaObject : : invokeMethod ( g_main_window - > getStatusFPSWidget ( ) , " setText " , Qt : : QueuedConnection ,
Q_ARG ( const QString & , tr ( " Game: %1 FPS " )
. arg ( gfps , 0 , ' f ' , 0 ) ) ) ;
m_last_game_fps = gfps ;
}
if ( speed ! = m_last_speed | | vfps ! = m_last_video_fps | | force )
{
QMetaObject : : invokeMethod ( g_main_window - > getStatusVPSWidget ( ) , " setText " , Qt : : QueuedConnection ,
Q_ARG ( const QString & , tr ( " Video: %1 FPS (%2%) " )
. arg ( vfps , 0 , ' f ' , 0 )
. arg ( speed , 0 , ' f ' , 0 ) ) ) ;
m_last_speed = speed ;
m_last_video_fps = vfps ;
}
}
}
}
void Host : : OnPerformanceMetricsUpdated ( )
{
g_emu_thread - > updatePerformanceMetrics ( false ) ;
}
void Host : : OnSaveStateLoading ( const std : : string_view & filename )
{
emit g_emu_thread - > onSaveStateLoading ( QtUtils : : StringViewToQString ( filename ) ) ;
}
void Host : : OnSaveStateLoaded ( const std : : string_view & filename , bool was_successful )
{
emit g_emu_thread - > onSaveStateLoaded ( QtUtils : : StringViewToQString ( filename ) , was_successful ) ;
}
void Host : : OnSaveStateSaved ( const std : : string_view & filename )
{
emit g_emu_thread - > onSaveStateSaved ( QtUtils : : StringViewToQString ( filename ) ) ;
}
2022-04-18 13:35:14 +00:00
# ifdef ENABLE_ACHIEVEMENTS
void Host : : OnAchievementsRefreshed ( )
{
u32 game_id = 0 ;
u32 achievement_count = 0 ;
u32 max_points = 0 ;
QString game_info ;
if ( Achievements : : HasActiveGame ( ) )
{
game_id = Achievements : : GetGameID ( ) ;
achievement_count = Achievements : : GetAchievementCount ( ) ;
max_points = Achievements : : GetMaximumPointsForGame ( ) ;
game_info = qApp - > translate ( " EmuThread " ,
" Game ID: %1 \n "
" Game Title: %2 \n "
" Achievements: %5 (%6) \n \n " )
. arg ( game_id )
. arg ( QString : : fromStdString ( Achievements : : GetGameTitle ( ) ) )
. arg ( achievement_count )
. arg ( qApp - > translate ( " EmuThread " , " %n points " , " " , max_points ) ) ;
const std : : string rich_presence_string ( Achievements : : GetRichPresenceString ( ) ) ;
if ( ! rich_presence_string . empty ( ) )
game_info . append ( QString : : fromStdString ( rich_presence_string ) ) ;
else
game_info . append ( qApp - > translate ( " EmuThread " , " Rich presence inactive or unsupported. " ) ) ;
}
else
{
game_info = qApp - > translate ( " EmuThread " , " Game not loaded or no RetroAchievements available. " ) ;
}
emit g_emu_thread - > onAchievementsRefreshed ( game_id , game_info , achievement_count , max_points ) ;
}
# endif
2022-09-14 12:41:50 +00:00
void Host : : CPUThreadVSync ( )
2022-09-07 09:20:50 +00:00
{
g_emu_thread - > getEventLoop ( ) - > processEvents ( QEventLoop : : AllEvents ) ;
2022-09-14 12:41:50 +00:00
CommonHost : : CPUThreadVSync ( ) ;
2022-09-07 09:20:50 +00:00
}
void Host : : RunOnCPUThread ( std : : function < void ( ) > function , bool block /* = false */ )
{
if ( g_emu_thread - > isOnEmuThread ( ) )
{
// probably shouldn't ever happen, but just in case..
function ( ) ;
return ;
}
QMetaObject : : invokeMethod ( g_emu_thread , " runOnCPUThread " ,
block ? Qt : : BlockingQueuedConnection : Qt : : QueuedConnection ,
Q_ARG ( const std : : function < void ( ) > & , std : : move ( function ) ) ) ;
}
void Host : : RefreshGameListAsync ( bool invalidate_cache )
{
QMetaObject : : invokeMethod ( g_main_window , " refreshGameList " , Qt : : QueuedConnection ,
Q_ARG ( bool , invalidate_cache ) ) ;
}
void Host : : CancelGameListRefresh ( )
{
QMetaObject : : invokeMethod ( g_main_window , " cancelGameListRefresh " , Qt : : BlockingQueuedConnection ) ;
}
void Host : : RequestExit ( bool save_state_if_running )
{
if ( VMManager : : HasValidVM ( ) )
g_emu_thread - > shutdownVM ( save_state_if_running ) ;
QMetaObject : : invokeMethod ( g_main_window , " requestExit " , Qt : : QueuedConnection ) ;
}
void Host : : RequestVMShutdown ( bool allow_confirm , bool allow_save_state , bool default_save_state )
{
if ( ! VMManager : : HasValidVM ( ) )
return ;
// Run it on the host thread, that way we get the confirm prompt (if enabled).
QMetaObject : : invokeMethod ( g_main_window , " requestShutdown " , Qt : : QueuedConnection ,
Q_ARG ( bool , allow_confirm ) , Q_ARG ( bool , allow_save_state ) ,
Q_ARG ( bool , default_save_state ) , Q_ARG ( bool , false ) ) ;
}
bool Host : : IsFullscreen ( )
{
return g_emu_thread - > isFullscreen ( ) ;
}
void Host : : SetFullscreen ( bool enabled )
{
g_emu_thread - > setFullscreen ( enabled ) ;
}
alignas ( 16 ) static SysMtgsThread s_mtgs_thread ;
SysMtgsThread & GetMTGS ( )
{
return s_mtgs_thread ;
}
2021-12-13 12:12:54 +00:00
2022-09-07 07:44:10 +00:00
bool QtHost : : InitializeConfig ( )
2021-12-13 12:12:54 +00:00
{
2022-09-07 07:44:10 +00:00
if ( ! CommonHost : : InitializeCriticalFolders ( ) )
2021-12-13 12:12:54 +00:00
{
2022-09-07 07:44:10 +00:00
QMessageBox : : critical ( nullptr , QStringLiteral ( " PCSX2 " ) ,
QStringLiteral ( " One or more critical directories are missing, your installation may be incomplete. " ) ) ;
2021-12-13 12:12:54 +00:00
return false ;
}
2022-05-19 14:46:33 +00:00
const std : : string path ( Path : : Combine ( EmuFolders : : Settings , " PCSX2.ini " ) ) ;
2022-05-29 14:22:16 +00:00
Console . WriteLn ( " Loading config from %s. " , path . c_str ( ) ) ;
2022-09-07 07:44:10 +00:00
2021-12-13 12:12:54 +00:00
s_base_settings_interface = std : : make_unique < INISettingsInterface > ( std : : move ( path ) ) ;
Host : : Internal : : SetBaseSettingsLayer ( s_base_settings_interface . get ( ) ) ;
2022-09-07 07:44:10 +00:00
if ( ! s_base_settings_interface - > Load ( ) | | ! CommonHost : : CheckSettingsVersion ( ) )
2021-12-13 12:12:54 +00:00
{
2022-09-07 07:44:10 +00:00
// If the config file doesn't exist, assume this is a new install and don't prompt to overwrite.
if ( FileSystem : : FileExists ( s_base_settings_interface - > GetFileName ( ) . c_str ( ) ) & &
QMessageBox : : question ( nullptr , QStringLiteral ( " PCSX2 " ) ,
QStringLiteral ( " Settings failed to load, or are the incorrect version. Clicking Yes will reset all settings to defaults. Do you want to continue? " ) ) ! = QMessageBox : : Yes )
{
return false ;
}
CommonHost : : SetDefaultSettings ( * s_base_settings_interface , true , true , true , true , true ) ;
SaveSettings ( ) ;
2021-12-13 12:12:54 +00:00
}
2022-09-14 12:41:50 +00:00
CommonHost : : SetBlockSystemConsole ( QtHost : : InNoGUIMode ( ) ) ;
2022-09-07 07:44:10 +00:00
CommonHost : : LoadStartupSettings ( ) ;
2021-12-13 12:12:54 +00:00
return true ;
}
2022-09-07 07:44:10 +00:00
void Host : : SetDefaultUISettings ( SettingsInterface & si )
2021-12-13 12:12:54 +00:00
{
2022-09-07 07:44:10 +00:00
si . SetBoolValue ( " UI " , " InhibitScreensaver " , true ) ;
si . SetBoolValue ( " UI " , " ConfirmShutdown " , true ) ;
si . SetBoolValue ( " UI " , " StartPaused " , false ) ;
si . SetBoolValue ( " UI " , " PauseOnFocusLoss " , false ) ;
si . SetBoolValue ( " UI " , " StartFullscreen " , false ) ;
si . SetBoolValue ( " UI " , " DoubleClickTogglesFullscreen " , true ) ;
si . SetBoolValue ( " UI " , " HideMouseCursor " , false ) ;
si . SetBoolValue ( " UI " , " RenderToSeparateWindow " , false ) ;
si . SetBoolValue ( " UI " , " HideMainWindowWhenRunning " , false ) ;
si . SetBoolValue ( " UI " , " DisableWindowResize " , false ) ;
si . SetStringValue ( " UI " , " Theme " , MainWindow : : DEFAULT_THEME_NAME ) ;
2021-12-13 12:12:54 +00:00
}
2022-03-04 10:40:03 +00:00
void QtHost : : SaveSettings ( )
2021-12-13 12:12:54 +00:00
{
pxAssertRel ( ! g_emu_thread - > isOnEmuThread ( ) , " Saving should happen on the UI thread. " ) ;
{
auto lock = Host : : GetSettingsLock ( ) ;
if ( ! s_base_settings_interface - > Save ( ) )
Console . Error ( " Failed to save settings. " ) ;
}
s_settings_save_timer - > deleteLater ( ) ;
s_settings_save_timer . release ( ) ;
}
2022-09-07 07:44:10 +00:00
void Host : : CommitBaseSettingChanges ( )
2021-12-13 12:12:54 +00:00
{
2022-09-07 07:44:10 +00:00
if ( ! QtHost : : IsOnUIThread ( ) )
{
QtHost : : RunOnUIThread ( & Host : : CommitBaseSettingChanges ) ;
return ;
}
auto lock = Host : : GetSettingsLock ( ) ;
2021-12-13 12:12:54 +00:00
if ( s_settings_save_timer )
return ;
s_settings_save_timer = std : : make_unique < QTimer > ( ) ;
2022-09-07 07:44:10 +00:00
s_settings_save_timer - > connect ( s_settings_save_timer . get ( ) , & QTimer : : timeout , & QtHost : : SaveSettings ) ;
2021-12-13 12:12:54 +00:00
s_settings_save_timer - > setSingleShot ( true ) ;
s_settings_save_timer - > start ( SETTINGS_SAVE_DELAY ) ;
}
2022-05-03 04:26:49 +00:00
bool QtHost : : InBatchMode ( )
{
return s_batch_mode ;
}
2022-07-09 09:14:48 +00:00
bool QtHost : : InNoGUIMode ( )
{
return s_nogui_mode ;
}
2022-09-07 07:44:10 +00:00
bool QtHost : : IsOnUIThread ( )
{
QThread * ui_thread = qApp - > thread ( ) ;
return ( QThread : : currentThread ( ) = = ui_thread ) ;
}
2022-04-06 10:49:00 +00:00
void QtHost : : RunOnUIThread ( const std : : function < void ( ) > & func , bool block /*= false*/ )
{
// main window always exists, so it's fine to attach it to that.
QMetaObject : : invokeMethod ( g_main_window , " runOnUIThread " ,
block ? Qt : : BlockingQueuedConnection : Qt : : QueuedConnection ,
Q_ARG ( const std : : function < void ( ) > & , func ) ) ;
}
2022-09-07 07:44:10 +00:00
bool Host : : RequestResetSettings ( bool folders , bool core , bool controllers , bool hotkeys , bool ui )
{
{
auto lock = Host : : GetSettingsLock ( ) ;
CommonHost : : SetDefaultSettings ( * s_base_settings_interface . get ( ) , folders , core , controllers , hotkeys , ui ) ;
}
Host : : CommitBaseSettingChanges ( ) ;
g_emu_thread - > applySettings ( ) ;
if ( folders )
g_emu_thread - > updateEmuFolders ( ) ;
return true ;
}
2022-05-11 08:24:56 +00:00
QString QtHost : : GetAppNameAndVersion ( )
{
QString ret ;
if constexpr ( ! PCSX2_isReleaseVersion & & GIT_TAGGED_COMMIT )
{
ret = QStringLiteral ( " PCSX2 Nightly - " GIT_TAG ) ;
}
else if constexpr ( PCSX2_isReleaseVersion )
{
# define APPNAME_STRINGIZE(x) #x
ret = QStringLiteral ( " PCSX2 "
APPNAME_STRINGIZE ( PCSX2_VersionHi ) " . "
APPNAME_STRINGIZE ( PCSX2_VersionMid ) " . "
APPNAME_STRINGIZE ( PCSX2_VersionLo ) ) ;
# undef APPNAME_STRINGIZE
}
else
{
return QStringLiteral ( " PCSX2 " GIT_REV ) ;
}
return ret ;
}
QString QtHost : : GetAppConfigSuffix ( )
{
# if defined(PCSX2_DEBUG)
return QStringLiteral ( " [Debug] " ) ;
# elif defined(PCSX2_DEVBUILD)
return QStringLiteral ( " [Devel] " ) ;
# else
return QString ( ) ;
# endif
}
2022-06-04 05:53:31 +00:00
QString QtHost : : GetResourcesBasePath ( )
{
return QString : : fromStdString ( EmuFolders : : Resources ) ;
}
2021-12-13 12:12:54 +00:00
std : : optional < std : : vector < u8 > > Host : : ReadResourceFile ( const char * filename )
{
2022-05-19 14:46:33 +00:00
const std : : string path ( Path : : Combine ( EmuFolders : : Resources , filename ) ) ;
2021-12-13 12:12:54 +00:00
std : : optional < std : : vector < u8 > > ret ( FileSystem : : ReadBinaryFile ( path . c_str ( ) ) ) ;
if ( ! ret . has_value ( ) )
Console . Error ( " Failed to read resource file '%s' " , filename ) ;
return ret ;
}
std : : optional < std : : string > Host : : ReadResourceFileToString ( const char * filename )
{
2022-05-19 14:46:33 +00:00
const std : : string path ( Path : : Combine ( EmuFolders : : Resources , filename ) ) ;
2021-12-13 12:12:54 +00:00
std : : optional < std : : string > ret ( FileSystem : : ReadFileToString ( path . c_str ( ) ) ) ;
if ( ! ret . has_value ( ) )
Console . Error ( " Failed to read resource file to string '%s' " , filename ) ;
return ret ;
}
2022-08-10 11:31:06 +00:00
std : : optional < std : : time_t > Host : : GetResourceFileTimestamp ( const char * filename )
{
const std : : string path ( Path : : Combine ( EmuFolders : : Resources , filename ) ) ;
FILESYSTEM_STAT_DATA sd ;
if ( ! FileSystem : : StatFile ( filename , & sd ) )
return std : : nullopt ;
return sd . ModificationTime ;
}
2021-12-13 12:12:54 +00:00
void Host : : ReportErrorAsync ( const std : : string_view & title , const std : : string_view & message )
{
if ( ! title . empty ( ) & & ! message . empty ( ) )
{
2022-03-20 03:40:47 +00:00
Console . Error ( " ReportErrorAsync: %.*s: %.*s " ,
2021-12-13 12:12:54 +00:00
static_cast < int > ( title . size ( ) ) , title . data ( ) ,
static_cast < int > ( message . size ( ) ) , message . data ( ) ) ;
}
else if ( ! message . empty ( ) )
{
2022-03-20 03:40:47 +00:00
Console . Error ( " ReportErrorAsync: %.*s " ,
2021-12-13 12:12:54 +00:00
static_cast < int > ( message . size ( ) ) , message . data ( ) ) ;
}
QMetaObject : : invokeMethod ( g_main_window , " reportError " , Qt : : QueuedConnection ,
Q_ARG ( const QString & , title . empty ( ) ? QString ( ) : QString : : fromUtf8 ( title . data ( ) , title . size ( ) ) ) ,
Q_ARG ( const QString & , message . empty ( ) ? QString ( ) : QString : : fromUtf8 ( message . data ( ) , message . size ( ) ) ) ) ;
}
2022-08-20 10:20:36 +00:00
bool Host : : ConfirmMessage ( const std : : string_view & title , const std : : string_view & message )
{
const QString qtitle ( QString : : fromUtf8 ( title . data ( ) , title . size ( ) ) ) ;
const QString qmessage ( QString : : fromUtf8 ( message . data ( ) , message . size ( ) ) ) ;
return g_emu_thread - > confirmMessage ( qtitle , qmessage ) ;
}
void Host : : OpenURL ( const std : : string_view & url )
{
QtHost : : RunOnUIThread ( [ url = QtUtils : : StringViewToQString ( url ) ] ( ) {
QtUtils : : OpenURL ( g_main_window , QUrl ( url ) ) ;
} ) ;
}
bool Host : : CopyTextToClipboard ( const std : : string_view & text )
{
QtHost : : RunOnUIThread ( [ text = QtUtils : : StringViewToQString ( text ) ] ( ) {
QClipboard * clipboard = QGuiApplication : : clipboard ( ) ;
if ( clipboard )
clipboard - > setText ( text ) ;
} ) ;
return true ;
}
2022-09-03 12:29:36 +00:00
void Host : : BeginTextInput ( )
{
QInputMethod * method = qApp - > inputMethod ( ) ;
if ( method )
QMetaObject : : invokeMethod ( method , " show " , Qt : : QueuedConnection ) ;
}
void Host : : EndTextInput ( )
{
QInputMethod * method = qApp - > inputMethod ( ) ;
if ( method )
QMetaObject : : invokeMethod ( method , " hide " , Qt : : QueuedConnection ) ;
}
2022-10-15 09:03:52 +00:00
std : : optional < WindowInfo > Host : : GetTopLevelWindowInfo ( )
{
std : : optional < WindowInfo > ret ;
QMetaObject : : invokeMethod ( g_main_window , & MainWindow : : getWindowInfo , Qt : : BlockingQueuedConnection , & ret ) ;
return ret ;
}
2021-12-13 12:12:54 +00:00
void Host : : OnInputDeviceConnected ( const std : : string_view & identifier , const std : : string_view & device_name )
{
emit g_emu_thread - > onInputDeviceConnected (
identifier . empty ( ) ? QString ( ) : QString : : fromUtf8 ( identifier . data ( ) , identifier . size ( ) ) ,
device_name . empty ( ) ? QString ( ) : QString : : fromUtf8 ( device_name . data ( ) , device_name . size ( ) ) ) ;
}
void Host : : OnInputDeviceDisconnected ( const std : : string_view & identifier )
{
emit g_emu_thread - > onInputDeviceDisconnected (
identifier . empty ( ) ? QString ( ) : QString : : fromUtf8 ( identifier . data ( ) , identifier . size ( ) ) ) ;
}
2022-09-07 09:20:50 +00:00
//////////////////////////////////////////////////////////////////////////
// Hotkeys
//////////////////////////////////////////////////////////////////////////
BEGIN_HOTKEY_LIST ( g_host_hotkeys )
END_HOTKEY_LIST ( )
2021-12-13 12:12:54 +00:00
//////////////////////////////////////////////////////////////////////////
// Interface Stuff
//////////////////////////////////////////////////////////////////////////
2022-03-04 10:40:03 +00:00
static void SignalHandler ( int signal )
{
// First try the normal (graceful) shutdown/exit.
static bool graceful_shutdown_attempted = false ;
if ( ! graceful_shutdown_attempted & & g_main_window )
{
std : : fprintf ( stderr , " Received CTRL+C, attempting graceful shutdown. Press CTRL+C again to force. \n " ) ;
graceful_shutdown_attempted = true ;
// This could be a bit risky invoking from a signal handler... hopefully it's okay.
QMetaObject : : invokeMethod ( g_main_window , & MainWindow : : requestExit , Qt : : QueuedConnection ) ;
return ;
}
std : : signal ( signal , SIG_DFL ) ;
// MacOS is missing std::quick_exit() despite it being C++11...
# ifndef __APPLE__
std : : quick_exit ( 1 ) ;
# else
_Exit ( 1 ) ;
# endif
}
void QtHost : : HookSignals ( )
{
std : : signal ( SIGINT , SignalHandler ) ;
std : : signal ( SIGTERM , SignalHandler ) ;
2022-04-18 13:35:14 +00:00
# ifdef __linux__
// Ignore SIGCHLD by default on Linux, since we kick off aplay asynchronously.
struct sigaction sa_chld = { } ;
sigemptyset ( & sa_chld . sa_mask ) ;
sa_chld . sa_flags = SA_SIGINFO | SA_RESTART | SA_NOCLDSTOP | SA_NOCLDWAIT ;
sigaction ( SIGCHLD , & sa_chld , nullptr ) ;
# endif
2022-03-04 10:40:03 +00:00
}
2022-07-09 09:07:28 +00:00
void QtHost : : PrintCommandLineVersion ( )
{
2022-09-14 12:41:50 +00:00
CommonHost : : InitializeEarlyConsole ( ) ;
2022-07-09 09:07:28 +00:00
std : : fprintf ( stderr , " %s \n " , ( GetAppNameAndVersion ( ) + GetAppConfigSuffix ( ) ) . toUtf8 ( ) . constData ( ) ) ;
std : : fprintf ( stderr , " https://pcsx2.net/ \n " ) ;
std : : fprintf ( stderr , " \n " ) ;
}
2022-09-17 12:47:35 +00:00
void QtHost : : PrintCommandLineHelp ( const std : : string_view & progname )
2022-07-09 09:07:28 +00:00
{
PrintCommandLineVersion ( ) ;
2022-09-17 12:47:35 +00:00
fmt : : print ( stderr , " Usage: {} [parameters] [--] [boot filename] \n " , progname ) ;
2022-07-09 09:07:28 +00:00
std : : fprintf ( stderr , " \n " ) ;
std : : fprintf ( stderr , " -help: Displays this information and exits. \n " ) ;
std : : fprintf ( stderr , " -version: Displays version information and exits. \n " ) ;
std : : fprintf ( stderr , " -batch: Enables batch mode (exits after shutting down). \n " ) ;
2022-07-09 09:14:48 +00:00
std : : fprintf ( stderr , " -nogui: Hides main window while running (implies batch mode). \n " ) ;
2022-07-09 09:07:28 +00:00
std : : fprintf ( stderr , " -elf <file>: Overrides the boot ELF with the specified filename. \n " ) ;
std : : fprintf ( stderr , " -disc <path>: Uses the specified host DVD drive as a source. \n " ) ;
std : : fprintf ( stderr , " -bios: Starts the BIOS (System Menu/OSDSYS). \n " ) ;
std : : fprintf ( stderr , " -fastboot: Force fast boot for provided filename. \n " ) ;
std : : fprintf ( stderr , " -slowboot: Force slow boot for provided filename. \n " ) ;
std : : fprintf ( stderr , " -state <index>: Loads specified save state by index. \n " ) ;
std : : fprintf ( stderr , " -statefile <filename>: Loads state from the specified filename. \n " ) ;
std : : fprintf ( stderr , " -fullscreen: Enters fullscreen mode immediately after starting. \n " ) ;
std : : fprintf ( stderr , " -nofullscreen: Prevents fullscreen mode from triggering if enabled. \n " ) ;
std : : fprintf ( stderr , " -earlyconsolelog: Forces logging of early console messages to console. \n " ) ;
2022-04-18 13:35:14 +00:00
# ifdef ENABLE_RAINTEGRATION
std : : fprintf ( stderr , " -raintegration: Use RAIntegration instead of built-in achievement support. \n " ) ;
# endif
2022-07-09 09:07:28 +00:00
std : : fprintf ( stderr , " --: Signals that no more arguments will follow and the remaining \n "
" parameters make up the filename. Use when the filename contains \n "
" spaces or starts with a dash. \n " ) ;
std : : fprintf ( stderr , " \n " ) ;
}
std : : shared_ptr < VMBootParameters > & QtHost : : AutoBoot ( std : : shared_ptr < VMBootParameters > & autoboot )
{
if ( ! autoboot )
autoboot = std : : make_shared < VMBootParameters > ( ) ;
return autoboot ;
}
2022-09-17 12:47:35 +00:00
bool QtHost : : ParseCommandLineOptions ( const QStringList & args , std : : shared_ptr < VMBootParameters > & autoboot )
2022-07-09 09:07:28 +00:00
{
bool no_more_args = false ;
2022-09-17 12:47:35 +00:00
if ( args . empty ( ) )
{
// Nothing to do here.
return true ;
}
for ( auto it = std : : next ( args . begin ( ) ) ; it ! = args . end ( ) ; + + it )
2022-07-09 09:07:28 +00:00
{
if ( ! no_more_args )
{
2022-09-17 12:47:35 +00:00
# define CHECK_ARG(str) (*it == str)
# define CHECK_ARG_PARAM(str) (*it == str && std::next(it) != args.end())
2022-07-09 09:07:28 +00:00
2022-09-17 12:47:35 +00:00
if ( CHECK_ARG ( QStringLiteral ( " -help " ) ) )
2022-07-09 09:07:28 +00:00
{
2022-09-17 12:47:35 +00:00
PrintCommandLineHelp ( args . front ( ) . toStdString ( ) ) ;
2022-07-09 09:07:28 +00:00
return false ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG ( QStringLiteral ( " -version " ) ) )
2022-07-09 09:07:28 +00:00
{
PrintCommandLineVersion ( ) ;
return false ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG ( QStringLiteral ( " -batch " ) ) )
2022-07-09 09:07:28 +00:00
{
s_batch_mode = true ;
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG ( QStringLiteral ( " -nogui " ) ) )
2022-07-09 09:14:48 +00:00
{
s_batch_mode = true ;
s_nogui_mode = true ;
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG ( QStringLiteral ( " -fastboot " ) ) )
2022-07-09 09:07:28 +00:00
{
AutoBoot ( autoboot ) - > fast_boot = true ;
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG ( QStringLiteral ( " -slowboot " ) ) )
2022-07-09 09:07:28 +00:00
{
AutoBoot ( autoboot ) - > fast_boot = false ;
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG_PARAM ( QStringLiteral ( " -state " ) ) )
2022-07-09 09:07:28 +00:00
{
2022-09-17 12:47:35 +00:00
AutoBoot ( autoboot ) - > state_index = ( + + it ) - > toInt ( ) ;
2022-07-09 09:07:28 +00:00
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG_PARAM ( QStringLiteral ( " -statefile " ) ) )
2022-07-09 09:07:28 +00:00
{
2022-09-17 12:47:35 +00:00
AutoBoot ( autoboot ) - > save_state = ( + + it ) - > toStdString ( ) ;
2022-07-09 09:07:28 +00:00
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG_PARAM ( QStringLiteral ( " -elf " ) ) )
2022-07-09 09:07:28 +00:00
{
2022-09-17 12:47:35 +00:00
AutoBoot ( autoboot ) - > elf_override = ( + + it ) - > toStdString ( ) ;
2022-07-09 09:07:28 +00:00
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG_PARAM ( QStringLiteral ( " -disc " ) ) )
2022-07-09 09:07:28 +00:00
{
AutoBoot ( autoboot ) - > source_type = CDVD_SourceType : : Disc ;
2022-09-17 12:47:35 +00:00
AutoBoot ( autoboot ) - > filename = ( + + it ) - > toStdString ( ) ;
2022-07-09 09:07:28 +00:00
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG ( QStringLiteral ( " -bios " ) ) )
2022-07-09 09:07:28 +00:00
{
AutoBoot ( autoboot ) - > source_type = CDVD_SourceType : : NoDisc ;
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG ( QStringLiteral ( " -fullscreen " ) ) )
2022-07-09 09:07:28 +00:00
{
AutoBoot ( autoboot ) - > fullscreen = true ;
2022-05-15 08:20:21 +00:00
s_start_fullscreen_ui_fullscreen = true ;
2022-07-09 09:07:28 +00:00
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG ( QStringLiteral ( " -nofullscreen " ) ) )
2022-07-09 09:07:28 +00:00
{
AutoBoot ( autoboot ) - > fullscreen = false ;
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG ( QStringLiteral ( " -earlyconsolelog " ) ) )
2022-07-09 09:07:28 +00:00
{
2022-09-14 12:41:50 +00:00
CommonHost : : InitializeEarlyConsole ( ) ;
2022-07-09 09:07:28 +00:00
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG ( QStringLiteral ( " -bigpicture " ) ) )
2022-05-15 08:20:21 +00:00
{
s_start_fullscreen_ui = true ;
continue ;
}
2022-04-18 13:35:14 +00:00
# ifdef ENABLE_RAINTEGRATION
else if ( CHECK_ARG ( QStringLiteral ( " -raintegration " ) ) )
{
Achievements : : SwitchToRAIntegration ( ) ;
continue ;
}
# endif
2022-09-17 12:47:35 +00:00
else if ( CHECK_ARG ( QStringLiteral ( " -- " ) ) )
2022-07-09 09:07:28 +00:00
{
no_more_args = true ;
continue ;
}
2022-09-17 12:47:35 +00:00
else if ( ( * it ) [ 0 ] = = ' - ' )
2022-07-09 09:07:28 +00:00
{
2022-09-17 12:47:35 +00:00
QMessageBox : : critical ( nullptr , QStringLiteral ( " Error " ) , QStringLiteral ( " Unknown parameter: '%1' " ) . arg ( * it ) ) ;
2022-07-09 09:07:28 +00:00
return false ;
}
# undef CHECK_ARG
# undef CHECK_ARG_PARAM
}
if ( ! AutoBoot ( autoboot ) - > filename . empty ( ) )
AutoBoot ( autoboot ) - > filename + = ' ' ;
2022-09-17 12:47:35 +00:00
AutoBoot ( autoboot ) - > filename + = it - > toStdString ( ) ;
2022-07-09 09:07:28 +00:00
}
// check autoboot parameters, if we set something like fullscreen without a bios
// or disc, we don't want to actually start.
if ( autoboot & & ! autoboot - > source_type . has_value ( ) & & autoboot - > filename . empty ( ) & & autoboot - > elf_override . empty ( ) )
{
2022-09-14 12:41:50 +00:00
CommonHost : : InitializeEarlyConsole ( ) ;
2022-07-09 09:07:28 +00:00
Console . Warning ( " Skipping autoboot due to no boot parameters. " ) ;
autoboot . reset ( ) ;
}
// if we don't have autoboot, we definitely don't want batch mode (because that'll skip
// scanning the game list).
2022-05-15 08:20:21 +00:00
if ( s_batch_mode & & ! s_start_fullscreen_ui & & ! autoboot )
2022-07-09 09:07:28 +00:00
{
2022-07-09 09:14:48 +00:00
QMessageBox : : critical ( nullptr , QStringLiteral ( " Error " ) , s_nogui_mode ?
QStringLiteral ( " Cannot use no-gui mode, because no boot filename was specified. " ) :
QStringLiteral ( " Cannot use batch mode, because no boot filename was specified. " ) ) ;
2022-07-09 09:07:28 +00:00
return false ;
}
return true ;
}
# ifndef _WIN32
// See note in EarlyHardwareChecks.cpp as to why we don't do this on Windows.
static bool PerformEarlyHardwareChecks ( )
{
// NOTE: No point translating this message, because the configuration isn't loaded yet, so we
// won't know which language to use, and loading the configuration uses float instructions.
const char * error ;
if ( VMManager : : PerformEarlyHardwareChecks ( & error ) )
return true ;
QMessageBox : : critical ( nullptr , QStringLiteral ( " Hardware Check Failed " ) , QString : : fromUtf8 ( error ) ) ;
return false ;
}
# endif
static void RegisterTypes ( )
{
qRegisterMetaType < std : : optional < bool > > ( ) ;
2022-10-15 09:03:52 +00:00
qRegisterMetaType < std : : optional < WindowInfo > > ( " std::optional<WindowInfo>() " ) ;
2022-09-07 09:20:50 +00:00
qRegisterMetaType < std : : function < void ( ) > > ( " std::function<void()> " ) ;
2022-07-09 09:07:28 +00:00
qRegisterMetaType < std : : shared_ptr < VMBootParameters > > ( ) ;
qRegisterMetaType < GSRendererType > ( ) ;
qRegisterMetaType < InputBindingKey > ( ) ;
qRegisterMetaType < CDVD_SourceType > ( ) ;
qRegisterMetaType < const GameList : : Entry * > ( ) ;
}
int main ( int argc , char * argv [ ] )
{
CrashHandler : : Install ( ) ;
QGuiApplication : : setAttribute ( Qt : : AA_EnableHighDpiScaling ) ;
QGuiApplication : : setAttribute ( Qt : : AA_UseHighDpiPixmaps ) ;
QGuiApplication : : setHighDpiScaleFactorRoundingPolicy ( Qt : : HighDpiScaleFactorRoundingPolicy : : PassThrough ) ;
RegisterTypes ( ) ;
QApplication app ( argc , argv ) ;
# ifndef _WIN32
if ( ! PerformEarlyHardwareChecks ( ) )
return EXIT_FAILURE ;
# endif
std : : shared_ptr < VMBootParameters > autoboot ;
2022-09-17 12:47:35 +00:00
if ( ! QtHost : : ParseCommandLineOptions ( app . arguments ( ) , autoboot ) )
2022-07-09 09:07:28 +00:00
return EXIT_FAILURE ;
// Bail out if we can't find any config.
if ( ! QtHost : : InitializeConfig ( ) )
return EXIT_FAILURE ;
2022-07-24 13:52:22 +00:00
// Set theme before creating any windows.
MainWindow : : updateApplicationTheme ( ) ;
2022-07-09 09:07:28 +00:00
MainWindow * main_window = new MainWindow ( QApplication : : style ( ) - > objectName ( ) ) ;
2022-07-24 13:52:22 +00:00
// Start up the CPU thread.
2022-07-09 09:07:28 +00:00
QtHost : : HookSignals ( ) ;
EmuThread : : start ( ) ;
2022-06-23 11:58:14 +00:00
// Create all window objects, the emuthread might still be starting up at this point.
2022-07-09 09:07:28 +00:00
main_window - > initialize ( ) ;
2022-06-23 11:58:14 +00:00
// When running in batch mode, ensure game list is loaded, but don't scan for any new files.
2022-07-09 09:07:28 +00:00
if ( ! s_batch_mode )
main_window - > refreshGameList ( false ) ;
2022-06-23 11:58:14 +00:00
else
GameList : : Refresh ( false , true ) ;
2022-07-09 09:07:28 +00:00
2022-06-23 11:58:14 +00:00
// Don't bother showing the window in no-gui mode.
2022-07-09 09:14:48 +00:00
if ( ! s_nogui_mode )
main_window - > show ( ) ;
2022-07-09 09:07:28 +00:00
2022-05-15 08:20:21 +00:00
// Initialize big picture mode if requested.
if ( s_start_fullscreen_ui )
g_emu_thread - > startFullscreenUI ( s_start_fullscreen_ui_fullscreen ) ;
2022-06-23 11:58:14 +00:00
// Skip the update check if we're booting a game directly.
2022-07-09 09:07:28 +00:00
if ( autoboot )
g_emu_thread - > startVM ( std : : move ( autoboot ) ) ;
2022-05-15 08:20:21 +00:00
else if ( ! s_nogui_mode )
2022-07-09 09:07:28 +00:00
main_window - > startupUpdateCheck ( ) ;
// This doesn't return until we exit.
const int result = app . exec ( ) ;
// Shutting down.
EmuThread : : stop ( ) ;
if ( g_main_window )
{
g_main_window - > close ( ) ;
delete g_main_window ;
}
// Ensure emulog is flushed.
if ( emuLog )
{
std : : fclose ( emuLog ) ;
emuLog = nullptr ;
}
return result ;
}