2024-05-20 14:01:20 +02:00
# include "stdafx.h"
2020-12-05 13:08:24 +01:00
# include "gui_application.h"
2019-06-18 04:02:06 +02:00
# include "qt_utils.h"
# include "welcome_dialog.h"
2020-02-22 20:42:49 +01:00
# include "main_window.h"
# include "emu_settings.h"
# include "gui_settings.h"
# include "persistent_settings.h"
# include "gs_frame.h"
# include "gl_gs_frame.h"
2020-09-02 16:20:29 +02:00
# include "localized_emu.h"
2021-10-13 20:18:18 +02:00
# include "qt_camera_handler.h"
2022-02-20 19:01:21 +01:00
# include "qt_music_handler.h"
2023-04-24 22:59:56 +02:00
# include "rpcs3_version.h"
2019-06-18 04:02:06 +02:00
# ifdef WITH_DISCORD_RPC
# include "_discord_utils.h"
# endif
2021-10-13 20:18:18 +02:00
# include "Emu/Io/Null/null_camera_handler.h"
2022-02-20 19:01:21 +01:00
# include "Emu/Io/Null/null_music_handler.h"
2021-09-23 20:12:06 +02:00
# include "Emu/vfs_config.h"
2024-03-24 19:05:10 +01:00
# include "util/init_mutex.hpp"
2024-04-20 12:38:14 +02:00
# include "util/console.h"
2019-06-18 04:02:06 +02:00
# include "trophy_notification_helper.h"
# include "save_data_dialog.h"
# include "msg_dialog_frame.h"
# include "osk_dialog_frame.h"
2020-11-17 02:19:17 +01:00
# include "recvmessage_dialog_frame.h"
# include "sendmessage_dialog_frame.h"
2019-08-14 20:30:53 +02:00
# include "stylesheets.h"
2024-03-24 11:08:42 +01:00
# include "progress_dialog.h"
2019-06-18 04:02:06 +02:00
2019-08-25 11:51:13 +02:00
# include <QScreen>
2020-02-22 20:42:49 +01:00
# include <QFontDatabase>
2023-04-24 22:59:56 +02:00
# include <QLayout>
2020-03-13 18:34:08 +01:00
# include <QLibraryInfo>
# include <QDirIterator>
2021-04-06 22:16:58 +02:00
# include <QFileInfo>
2022-01-10 14:15:04 +01:00
# include <QMessageBox>
2023-04-24 22:59:56 +02:00
# include <QTextDocument>
2023-12-19 23:20:25 +01:00
# include <QStyleFactory>
2023-12-22 03:25:19 +01:00
# include <QStyleHints>
2019-08-25 11:51:13 +02:00
2019-09-20 00:14:09 +02:00
# include <clocale>
2020-07-30 13:23:55 +02:00
# include "Emu/RSX/Null/NullGSRender.h"
# include "Emu/RSX/GL/GLGSRender.h"
2021-11-13 16:11:49 +01:00
# if defined(HAVE_VULKAN)
2020-07-30 13:23:55 +02:00
# include "Emu/RSX/VK/VKGSRender.h"
# endif
2024-04-11 18:25:32 +02:00
# ifdef _WIN32
# include "Windows.h"
# endif
2020-02-01 08:43:43 +01:00
LOG_CHANNEL ( gui_log , " GUI " ) ;
2020-02-01 05:15:50 +01:00
2024-05-20 14:01:20 +02:00
std : : unique_ptr < raw_mouse_handler > g_raw_mouse_handler ;
2023-06-16 20:05:35 +02:00
[[noreturn]] void report_fatal_error ( std : : string_view text , bool is_html = false , bool include_help_text = true ) ;
2019-06-18 04:02:06 +02:00
gui_application : : gui_application ( int & argc , char * * argv ) : QApplication ( argc , argv )
{
2024-06-23 10:59:08 +02:00
std : : setlocale ( LC_NUMERIC , " C " ) ; // On linux Qt changes to system locale while initializing QCoreApplication
2019-06-18 04:02:06 +02:00
}
2019-08-25 11:51:13 +02:00
gui_application : : ~ gui_application ( )
{
# ifdef WITH_DISCORD_RPC
discord : : shutdown ( ) ;
# endif
}
2021-02-05 15:36:57 +01:00
bool gui_application : : Init ( )
2019-06-18 04:02:06 +02:00
{
2022-02-26 15:50:03 +01:00
# ifndef __APPLE__
2019-06-18 04:02:06 +02:00
setWindowIcon ( QIcon ( " :/rpcs3.ico " ) ) ;
2022-02-26 15:50:03 +01:00
# endif
2019-06-18 04:02:06 +02:00
2023-04-24 22:59:56 +02:00
if ( ! rpcs3 : : is_release_build ( ) & & ! rpcs3 : : is_local_build ( ) )
{
const std : : string_view branch_name = rpcs3 : : get_full_branch ( ) ;
gui_log . warning ( " Experimental Build Warning! Build origin: %s " , branch_name ) ;
QMessageBox msg ;
2023-08-05 11:19:52 +02:00
msg . setWindowModality ( Qt : : WindowModal ) ;
2023-04-24 22:59:56 +02:00
msg . setWindowTitle ( tr ( " Experimental Build Warning " ) ) ;
msg . setIcon ( QMessageBox : : Critical ) ;
msg . setTextFormat ( Qt : : RichText ) ;
msg . setStandardButtons ( QMessageBox : : Yes | QMessageBox : : No ) ;
msg . setDefaultButton ( QMessageBox : : No ) ;
2023-06-01 01:06:30 +02:00
msg . setText ( tr (
2023-04-24 22:59:56 +02:00
R " (
< p style = " white-space: nowrap; " >
Please understand that this build is not an official RPCS3 release . < br >
This build contains changes that may break games , or even < b > damage < / b > your data . < br >
2023-06-01 01:06:30 +02:00
We recommend to download and use the official build from the < a % 0 href = ' https : //rpcs3.net/download'>RPCS3 website</a>.<br><br>
2023-04-24 22:59:56 +02:00
Build origin : % 1 < br >
Do you wish to use this build anyway ?
< / p >
) "
2023-06-01 01:06:30 +02:00
) . arg ( gui : : utils : : get_link_style ( ) ) . arg ( Qt : : convertFromPlainText ( branch_name . data ( ) ) ) ) ;
2023-04-24 22:59:56 +02:00
msg . layout ( ) - > setSizeConstraint ( QLayout : : SetFixedSize ) ;
if ( msg . exec ( ) = = QMessageBox : : No )
{
return false ;
}
}
2024-11-15 01:55:01 +01:00
m_emu_settings = std : : make_shared < emu_settings > ( ) ;
m_gui_settings = std : : make_shared < gui_settings > ( ) ;
m_persistent_settings = std : : make_shared < persistent_settings > ( ) ;
2019-06-18 04:02:06 +02:00
2021-02-05 15:36:57 +01:00
if ( ! m_emu_settings - > Init ( ) )
{
return false ;
}
2024-04-11 18:25:32 +02:00
if ( m_gui_settings - > GetValue ( gui : : m_attachCommandLine ) . toBool ( ) )
{
2024-04-20 12:38:14 +02:00
utils : : attach_console ( utils : : console_stream : : std_err , true ) ;
2024-04-11 18:25:32 +02:00
}
else
{
m_gui_settings - > SetValue ( gui : : m_attachCommandLine , false ) ;
}
2021-04-02 00:54:32 +02:00
// The user might be set by cli arg. If not, set another user.
if ( m_active_user . empty ( ) )
{
2021-04-10 11:11:08 +02:00
// Get active user with standard user as fallback
m_active_user = m_persistent_settings - > GetCurrentUser ( " 00000001 " ) . toStdString ( ) ;
2021-04-02 00:54:32 +02:00
}
2020-08-02 16:03:30 +02:00
2019-06-18 04:02:06 +02:00
// Force init the emulator
2021-04-02 00:54:32 +02:00
InitializeEmulator ( m_active_user , m_show_gui ) ;
2019-06-18 04:02:06 +02:00
// Create the main window
2019-08-25 11:51:13 +02:00
if ( m_show_gui )
{
2020-01-12 22:17:01 +01:00
m_main_window = new main_window ( m_gui_settings , m_emu_settings , m_persistent_settings , nullptr ) ;
2020-02-05 11:07:34 +01:00
const auto codes = GetAvailableLanguageCodes ( ) ;
const auto language = m_gui_settings - > GetValue ( gui : : loc_language ) . toString ( ) ;
const auto index = codes . indexOf ( language ) ;
2022-09-19 14:57:51 +02:00
LoadLanguage ( index < 0 ? QLocale ( QLocale : : English ) . bcp47Name ( ) : : : at32 ( codes , index ) ) ;
2019-08-25 11:51:13 +02:00
}
2019-06-18 04:02:06 +02:00
// Create callbacks from the emulator, which reference the handlers.
InitializeCallbacks ( ) ;
// Create connects to propagate events throughout Gui.
InitializeConnects ( ) ;
if ( m_gui_settings - > GetValue ( gui : : ib_show_welcome ) . toBool ( ) )
{
2023-06-23 18:46:00 +02:00
welcome_dialog * welcome = new welcome_dialog ( m_gui_settings , false ) ;
2024-11-02 07:55:58 +01:00
bool use_dark_theme = false ;
connect ( welcome , & QDialog : : accepted , this , [ & ] ( )
{
use_dark_theme = welcome - > does_user_want_dark_theme ( ) ;
} ) ;
2019-06-18 04:02:06 +02:00
welcome - > exec ( ) ;
2023-09-16 21:27:21 +02:00
2024-11-02 07:55:58 +01:00
if ( use_dark_theme )
2023-09-16 21:27:21 +02:00
{
m_gui_settings - > SetValue ( gui : : m_currentStylesheet , " Darker Style by TheMitoSan " ) ;
}
2019-06-18 04:02:06 +02:00
}
2019-08-25 11:51:13 +02:00
2022-01-10 14:15:04 +01:00
// Check maxfiles
if ( utils : : get_maxfiles ( ) < 4096 )
{
QMessageBox : : warning ( nullptr ,
tr ( " Warning " ) ,
tr ( " The current limit of maximum file descriptors is too low. \n "
" Some games will crash. \n "
" \n "
" Please increase the limit before running RPCS3. " ) ) ;
}
2021-03-25 22:55:06 +01:00
if ( m_main_window & & ! m_main_window - > Init ( m_with_cli_boot ) )
2020-03-22 14:49:49 +01:00
{
2021-02-05 15:36:57 +01:00
return false ;
2020-03-22 14:49:49 +01:00
}
2019-06-18 04:02:06 +02:00
# ifdef WITH_DISCORD_RPC
// Discord Rich Presence Integration
if ( m_gui_settings - > GetValue ( gui : : m_richPresence ) . toBool ( ) )
{
discord : : initialize ( ) ;
}
# endif
2021-02-05 15:36:57 +01:00
2024-02-05 23:49:00 +01:00
// Install native event filter
# ifdef _WIN32 // Currently only needed for raw mouse input on windows
installNativeEventFilter ( & m_native_event_filter ) ;
# endif
2021-02-05 15:36:57 +01:00
return true ;
2019-06-18 04:02:06 +02:00
}
2020-02-05 11:07:34 +01:00
void gui_application : : SwitchTranslator ( QTranslator & translator , const QString & filename , const QString & language_code )
2020-02-03 23:43:11 +01:00
{
// remove the old translator
removeTranslator ( & translator ) ;
2021-05-22 10:42:05 +02:00
const QString lang_path = QLibraryInfo : : path ( QLibraryInfo : : TranslationsPath ) + QStringLiteral ( " / " ) ;
2020-02-04 13:40:02 +01:00
const QString file_path = lang_path + filename ;
if ( QFileInfo ( file_path ) . isFile ( ) )
2020-02-03 23:43:11 +01:00
{
2020-02-04 13:40:02 +01:00
// load the new translator
if ( translator . load ( file_path ) )
{
installTranslator ( & translator ) ;
}
}
2020-02-05 11:07:34 +01:00
else if ( const QString default_code = QLocale ( QLocale : : English ) . bcp47Name ( ) ; language_code ! = default_code )
2020-02-04 13:40:02 +01:00
{
2020-02-05 11:07:34 +01:00
// show error, but ignore default case "en", since it is handled in source code
2023-06-12 03:45:37 +02:00
gui_log . error ( " No translation file found in: %s " , file_path ) ;
2020-02-05 11:07:34 +01:00
// reset current language to default "en"
m_language_code = default_code ;
2020-02-03 23:43:11 +01:00
}
}
2020-02-05 11:07:34 +01:00
void gui_application : : LoadLanguage ( const QString & language_code )
2020-02-03 23:43:11 +01:00
{
2020-02-05 11:07:34 +01:00
if ( m_language_code = = language_code )
2020-02-03 23:43:11 +01:00
{
return ;
}
2020-02-05 11:07:34 +01:00
m_language_code = language_code ;
2020-02-03 23:43:11 +01:00
2020-02-05 11:07:34 +01:00
const QLocale locale = QLocale ( language_code ) ;
2020-02-04 13:40:02 +01:00
const QString locale_name = QLocale : : languageToString ( locale . language ( ) ) ;
2020-02-03 23:43:11 +01:00
QLocale : : setDefault ( locale ) ;
// Idk if this is overruled by the QLocale default, so I'll change it here just to be sure.
// As per QT recommendations to avoid conflicts for POSIX functions
std : : setlocale ( LC_NUMERIC , " C " ) ;
2020-02-05 11:07:34 +01:00
SwitchTranslator ( m_translator , QStringLiteral ( " rpcs3_%1.qm " ) . arg ( language_code ) , language_code ) ;
2020-02-03 23:43:11 +01:00
if ( m_main_window )
{
2020-02-05 11:07:34 +01:00
const QString default_code = QLocale ( QLocale : : English ) . bcp47Name ( ) ;
QStringList language_codes = GetAvailableLanguageCodes ( ) ;
if ( ! language_codes . contains ( default_code ) )
{
language_codes . prepend ( default_code ) ;
}
m_main_window - > RetranslateUI ( language_codes , m_language_code ) ;
2020-02-04 13:40:02 +01:00
}
2020-02-05 11:07:34 +01:00
m_gui_settings - > SetValue ( gui : : loc_language , m_language_code ) ;
2023-06-12 03:45:37 +02:00
gui_log . notice ( " Current language changed to %s (%s) " , locale_name , language_code ) ;
2020-02-04 13:40:02 +01:00
}
2020-02-05 11:07:34 +01:00
QStringList gui_application : : GetAvailableLanguageCodes ( )
2020-02-04 13:40:02 +01:00
{
2020-02-05 11:07:34 +01:00
QStringList language_codes ;
2020-02-04 13:40:02 +01:00
2021-05-22 10:42:05 +02:00
const QString language_path = QLibraryInfo : : path ( QLibraryInfo : : TranslationsPath ) ;
2020-02-04 13:40:02 +01:00
if ( QFileInfo ( language_path ) . isDir ( ) )
{
const QDir dir ( language_path ) ;
2023-05-06 08:42:14 +02:00
const QStringList filenames = dir . entryList ( QStringList ( " rpcs3_*.qm " ) ) ;
2020-02-04 13:40:02 +01:00
2023-05-06 08:42:14 +02:00
for ( const QString & filename : filenames )
2020-02-04 13:40:02 +01:00
{
2020-02-05 11:07:34 +01:00
QString language_code = filename ; // "rpcs3_en.qm"
language_code . truncate ( language_code . lastIndexOf ( ' . ' ) ) ; // "rpcs3_en"
language_code . remove ( 0 , language_code . indexOf ( ' _ ' ) + 1 ) ; // "en"
2020-02-04 13:40:02 +01:00
2023-05-06 08:42:14 +02:00
if ( language_codes . contains ( language_code ) )
{
2023-06-12 03:45:37 +02:00
gui_log . error ( " Found duplicate language '%s' (%s) " , language_code , filename ) ;
2023-05-06 08:42:14 +02:00
}
else
{
language_codes < < language_code ;
}
2020-02-04 13:40:02 +01:00
}
2020-02-03 23:43:11 +01:00
}
2020-02-05 11:07:34 +01:00
return language_codes ;
2020-02-03 23:43:11 +01:00
}
2019-06-18 04:02:06 +02:00
void gui_application : : InitializeConnects ( )
{
2020-04-28 19:12:36 +02:00
connect ( & m_timer , & QTimer : : timeout , this , & gui_application : : UpdatePlaytime ) ;
2019-10-31 07:59:37 +01:00
connect ( this , & gui_application : : OnEmulatorRun , this , & gui_application : : StartPlaytime ) ;
connect ( this , & gui_application : : OnEmulatorStop , this , & gui_application : : StopPlaytime ) ;
connect ( this , & gui_application : : OnEmulatorPause , this , & gui_application : : StopPlaytime ) ;
connect ( this , & gui_application : : OnEmulatorResume , this , & gui_application : : StartPlaytime ) ;
2023-07-07 09:31:35 +02:00
connect ( this , & QGuiApplication : : applicationStateChanged , this , & gui_application : : OnAppStateChanged ) ;
2019-10-31 07:59:37 +01:00
2019-08-25 11:51:13 +02:00
if ( m_main_window )
{
2020-02-05 11:07:34 +01:00
connect ( m_main_window , & main_window : : RequestLanguageChange , this , & gui_application : : LoadLanguage ) ;
2019-08-25 11:51:13 +02:00
connect ( m_main_window , & main_window : : RequestGlobalStylesheetChange , this , & gui_application : : OnChangeStyleSheetRequest ) ;
2023-01-21 00:53:49 +01:00
connect ( m_main_window , & main_window : : NotifyEmuSettingsChange , this , [ this ] ( ) { OnEmuSettingsChange ( ) ; } ) ;
2023-11-25 01:33:53 +01:00
connect ( m_main_window , & main_window : : NotifyShortcutHandlers , this , & gui_application : : OnShortcutChange ) ;
2019-06-18 04:02:06 +02:00
2019-08-25 11:51:13 +02:00
connect ( this , & gui_application : : OnEmulatorRun , m_main_window , & main_window : : OnEmuRun ) ;
connect ( this , & gui_application : : OnEmulatorStop , m_main_window , & main_window : : OnEmuStop ) ;
connect ( this , & gui_application : : OnEmulatorPause , m_main_window , & main_window : : OnEmuPause ) ;
connect ( this , & gui_application : : OnEmulatorResume , m_main_window , & main_window : : OnEmuResume ) ;
connect ( this , & gui_application : : OnEmulatorReady , m_main_window , & main_window : : OnEmuReady ) ;
2022-06-21 22:13:22 +02:00
connect ( this , & gui_application : : OnEnableDiscEject , m_main_window , & main_window : : OnEnableDiscEject ) ;
connect ( this , & gui_application : : OnEnableDiscInsert , m_main_window , & main_window : : OnEnableDiscInsert ) ;
2023-12-22 03:25:19 +01:00
connect ( QGuiApplication : : styleHints ( ) , & QStyleHints : : colorSchemeChanged , this , [ this ] ( ) { OnChangeStyleSheetRequest ( ) ; } ) ;
2019-08-25 11:51:13 +02:00
}
# ifdef WITH_DISCORD_RPC
2020-02-07 21:55:29 +01:00
connect ( this , & gui_application : : OnEmulatorRun , [ this ] ( bool /*start_playtime*/ )
2019-08-25 11:51:13 +02:00
{
// Discord Rich Presence Integration
if ( m_gui_settings - > GetValue ( gui : : m_richPresence ) . toBool ( ) )
{
discord : : update_presence ( Emu . GetTitleID ( ) , Emu . GetTitle ( ) ) ;
}
} ) ;
connect ( this , & gui_application : : OnEmulatorStop , [ this ] ( )
{
// Discord Rich Presence Integration
if ( m_gui_settings - > GetValue ( gui : : m_richPresence ) . toBool ( ) )
{
discord : : update_presence ( m_gui_settings - > GetValue ( gui : : m_discordState ) . toString ( ) . toStdString ( ) ) ;
}
} ) ;
# endif
2019-06-18 04:02:06 +02:00
qRegisterMetaType < std : : function < void ( ) > > ( " std::function<void()> " ) ;
2022-01-20 18:44:49 +01:00
connect ( this , & gui_application : : RequestCallFromMainThread , this , & gui_application : : CallFromMainThread ) ;
2019-06-18 04:02:06 +02:00
}
std : : unique_ptr < gs_frame > gui_application : : get_gs_frame ( )
{
extern const std : : unordered_map < video_resolution , std : : pair < int , int > , value_hash < video_resolution > > g_video_out_resolution_map ;
2022-09-19 14:57:51 +02:00
auto [ w , h ] = : : at32 ( g_video_out_resolution_map , g_cfg . video . resolution ) ;
2019-06-18 04:02:06 +02:00
2023-10-06 00:26:54 +02:00
const bool resize_game_window = m_gui_settings - > GetValue ( gui : : gs_resize ) . toBool ( ) ;
if ( resize_game_window )
2019-06-18 04:02:06 +02:00
{
2022-11-17 19:37:12 +01:00
if ( m_gui_settings - > GetValue ( gui : : gs_resize_manual ) . toBool ( ) )
{
w = m_gui_settings - > GetValue ( gui : : gs_width ) . toInt ( ) ;
h = m_gui_settings - > GetValue ( gui : : gs_height ) . toInt ( ) ;
}
else
{
const qreal device_pixel_ratio = devicePixelRatio ( ) ;
w / = device_pixel_ratio ;
h / = device_pixel_ratio ;
}
2019-06-18 04:02:06 +02:00
}
2023-02-13 21:12:15 +01:00
QScreen * screen = nullptr ;
QRect base_geometry { } ;
2023-02-13 22:13:31 +01:00
// Use screen index set by CLI argument
int screen_index = m_game_screen_index ;
2023-10-06 00:26:54 +02:00
const int last_screen_index = m_gui_settings - > GetValue ( gui : : gs_screen ) . toInt ( ) ;
2023-02-13 22:13:31 +01:00
2023-10-06 00:26:54 +02:00
// Use last used screen if no CLI index was set
if ( screen_index < 0 )
2023-02-13 22:13:31 +01:00
{
2023-10-06 00:26:54 +02:00
screen_index = last_screen_index ;
2023-02-13 22:13:31 +01:00
}
// Try to find the specified screen
if ( screen_index > = 0 )
2023-02-13 21:12:15 +01:00
{
const QList < QScreen * > available_screens = screens ( ) ;
2023-02-13 22:13:31 +01:00
if ( screen_index < available_screens . count ( ) )
2023-02-13 21:12:15 +01:00
{
2023-02-13 22:13:31 +01:00
screen = : : at32 ( available_screens , screen_index ) ;
2023-02-13 21:12:15 +01:00
if ( screen )
{
base_geometry = screen - > geometry ( ) ;
}
}
if ( ! screen )
{
2023-02-13 22:13:31 +01:00
gui_log . error ( " The selected game screen with index %d is not available (available screens: %d) " , screen_index , available_screens . count ( ) ) ;
2023-02-13 21:12:15 +01:00
}
}
2023-02-13 22:13:31 +01:00
// Fallback to the screen of the main window. Use the primary screen as last resort.
2023-02-13 21:12:15 +01:00
if ( ! screen )
{
screen = m_main_window ? m_main_window - > screen ( ) : primaryScreen ( ) ;
base_geometry = m_main_window ? m_main_window - > frameGeometry ( ) : primaryScreen ( ) - > geometry ( ) ;
}
2023-10-06 00:26:54 +02:00
// Use saved geometry if possible. Ignore this if the last used screen is different than the requested screen.
QRect frame_geometry = screen_index ! = last_screen_index ? QRect { } : m_gui_settings - > GetValue ( gui : : gs_geometry ) . value < QRect > ( ) ;
if ( frame_geometry . isNull ( ) | | frame_geometry . isEmpty ( ) )
{
// Center above main window or inside screen if the saved geometry is invalid
frame_geometry = gui : : utils : : create_centered_window_geometry ( screen , base_geometry , w , h ) ;
}
else if ( resize_game_window )
{
// Apply size override to our saved geometry if needed
frame_geometry . setSize ( QSize ( w , h ) ) ;
}
// Load AppIcon
2023-02-13 21:12:15 +01:00
const QIcon app_icon = m_main_window ? m_main_window - > GetAppIcon ( ) : gui : : utils : : get_app_icon_from_path ( Emu . GetBoot ( ) , Emu . GetTitleID ( ) ) ;
2019-06-18 04:02:06 +02:00
2021-04-07 23:05:18 +02:00
gs_frame * frame = nullptr ;
2019-06-18 04:02:06 +02:00
2021-04-07 23:05:18 +02:00
switch ( g_cfg . video . renderer . get ( ) )
2019-06-18 04:02:06 +02:00
{
case video_renderer : : opengl :
{
2023-02-13 20:57:25 +01:00
frame = new gl_gs_frame ( screen , frame_geometry , app_icon , m_gui_settings , m_start_games_fullscreen ) ;
2019-06-18 04:02:06 +02:00
break ;
}
2020-02-12 21:52:11 +01:00
case video_renderer : : null :
2019-06-18 04:02:06 +02:00
case video_renderer : : vulkan :
{
2023-02-13 20:57:25 +01:00
frame = new gs_frame ( screen , frame_geometry , app_icon , m_gui_settings , m_start_games_fullscreen ) ;
2019-06-18 04:02:06 +02:00
break ;
}
}
m_game_window = frame ;
2019-08-25 11:51:13 +02:00
2019-06-18 04:02:06 +02:00
return std : : unique_ptr < gs_frame > ( frame ) ;
}
/** RPCS3 emulator has functions it desires to call from the GUI at times. Initialize them in here. */
void gui_application : : InitializeCallbacks ( )
{
EmuCallbacks callbacks = CreateCallbacks ( ) ;
2020-12-18 12:40:25 +01:00
callbacks . try_to_quit = [ this ] ( bool force_quit , std : : function < void ( ) > on_exit ) - > bool
2019-06-18 04:02:06 +02:00
{
2019-08-25 11:51:13 +02:00
// Close rpcs3 if closed in no-gui mode
if ( force_quit | | ! m_main_window )
{
2020-12-18 12:40:25 +01:00
if ( on_exit )
{
on_exit ( ) ;
}
2020-07-03 17:11:28 +02:00
if ( m_main_window )
{
// Close main window in order to save its window state
m_main_window - > close ( ) ;
}
2019-08-25 11:51:13 +02:00
quit ( ) ;
2020-07-03 17:11:28 +02:00
return true ;
2019-08-25 11:51:13 +02:00
}
2020-07-03 17:11:28 +02:00
return false ;
2019-06-18 04:02:06 +02:00
} ;
2023-07-31 22:57:26 +02:00
callbacks . call_from_main_thread = [ this ] ( std : : function < void ( ) > func , atomic_t < u32 > * wake_up )
2019-06-18 04:02:06 +02:00
{
2022-06-24 19:58:26 +02:00
RequestCallFromMainThread ( std : : move ( func ) , wake_up ) ;
2019-06-18 04:02:06 +02:00
} ;
2022-07-04 15:02:17 +02:00
callbacks . init_gs_render = [ ] ( utils : : serial * ar )
2020-07-30 13:23:55 +02:00
{
2021-04-07 23:05:18 +02:00
switch ( g_cfg . video . renderer . get ( ) )
2020-07-30 13:23:55 +02:00
{
case video_renderer : : null :
{
2022-07-04 15:02:17 +02:00
g_fxo - > init < rsx : : thread , named_thread < NullGSRender > > ( ar ) ;
2020-07-30 13:23:55 +02:00
break ;
}
case video_renderer : : opengl :
{
2022-02-25 07:35:43 +01:00
# if not defined(__APPLE__)
2022-07-04 15:02:17 +02:00
g_fxo - > init < rsx : : thread , named_thread < GLGSRender > > ( ar ) ;
2022-02-25 07:35:43 +01:00
# endif
2020-07-30 13:23:55 +02:00
break ;
}
case video_renderer : : vulkan :
{
2022-02-25 07:35:43 +01:00
# if defined(HAVE_VULKAN)
2022-07-04 15:02:17 +02:00
g_fxo - > init < rsx : : thread , named_thread < VKGSRender > > ( ar ) ;
2022-02-25 07:35:43 +01:00
# endif
2020-07-30 13:23:55 +02:00
break ;
}
}
} ;
2021-10-13 20:18:18 +02:00
callbacks . get_camera_handler = [ ] ( ) - > std : : shared_ptr < camera_handler_base >
{
switch ( g_cfg . io . camera . get ( ) )
{
case camera_handler : : null :
case camera_handler : : fake :
{
return std : : make_shared < null_camera_handler > ( ) ;
}
case camera_handler : : qt :
{
return std : : make_shared < qt_camera_handler > ( ) ;
}
}
2021-10-22 01:56:03 +02:00
return nullptr ;
2021-10-13 20:18:18 +02:00
} ;
2022-02-20 19:01:21 +01:00
callbacks . get_music_handler = [ ] ( ) - > std : : shared_ptr < music_handler_base >
{
switch ( g_cfg . audio . music . get ( ) )
{
case music_handler : : null :
{
return std : : make_shared < null_music_handler > ( ) ;
}
case music_handler : : qt :
{
return std : : make_shared < qt_music_handler > ( ) ;
}
}
return nullptr ;
} ;
2019-06-18 04:02:06 +02:00
callbacks . get_gs_frame = [ this ] ( ) - > std : : unique_ptr < GSFrameBase > { return get_gs_frame ( ) ; } ;
2019-12-03 00:55:07 +01:00
callbacks . get_msg_dialog = [ this ] ( ) - > std : : shared_ptr < MsgDialogBase > { return m_show_gui ? std : : make_shared < msg_dialog_frame > ( ) : nullptr ; } ;
callbacks . get_osk_dialog = [ this ] ( ) - > std : : shared_ptr < OskDialogBase > { return m_show_gui ? std : : make_shared < osk_dialog_frame > ( ) : nullptr ; } ;
2019-12-03 08:32:28 +01:00
callbacks . get_save_dialog = [ ] ( ) - > std : : unique_ptr < SaveDialogBase > { return std : : make_unique < save_data_dialog > ( ) ; } ;
2020-11-17 02:19:17 +01:00
callbacks . get_sendmessage_dialog = [ this ] ( ) - > std : : shared_ptr < SendMessageDialogBase > { return std : : make_shared < sendmessage_dialog_frame > ( ) ; } ;
callbacks . get_recvmessage_dialog = [ this ] ( ) - > std : : shared_ptr < RecvMessageDialogBase > { return std : : make_shared < recvmessage_dialog_frame > ( ) ; } ;
2019-12-03 08:32:28 +01:00
callbacks . get_trophy_notification_dialog = [ this ] ( ) - > std : : unique_ptr < TrophyNotificationBase > { return std : : make_unique < trophy_notification_helper > ( m_game_window ) ; } ;
2019-06-18 04:02:06 +02:00
2020-02-10 09:55:53 +01:00
callbacks . on_run = [ this ] ( bool start_playtime ) { OnEmulatorRun ( start_playtime ) ; } ;
callbacks . on_pause = [ this ] ( ) { OnEmulatorPause ( ) ; } ;
callbacks . on_resume = [ this ] ( ) { OnEmulatorResume ( true ) ; } ;
callbacks . on_stop = [ this ] ( ) { OnEmulatorStop ( ) ; } ;
callbacks . on_ready = [ this ] ( ) { OnEmulatorReady ( ) ; } ;
2019-06-18 04:02:06 +02:00
2022-06-21 22:13:22 +02:00
callbacks . enable_disc_eject = [ this ] ( bool enabled )
{
Emu . CallFromMainThread ( [ this , enabled ] ( )
{
OnEnableDiscEject ( enabled ) ;
} ) ;
} ;
callbacks . enable_disc_insert = [ this ] ( bool enabled )
{
Emu . CallFromMainThread ( [ this , enabled ] ( )
{
OnEnableDiscInsert ( enabled ) ;
} ) ;
} ;
2020-12-07 18:10:34 +01:00
callbacks . on_missing_fw = [ this ] ( )
{
2021-02-22 18:22:55 +01:00
if ( ! m_main_window ) return false ;
2021-01-27 17:22:06 +01:00
return m_main_window - > OnMissingFw ( ) ;
2020-12-07 18:10:34 +01:00
} ;
2020-02-10 09:55:53 +01:00
callbacks . handle_taskbar_progress = [ this ] ( s32 type , s32 value )
2019-06-18 04:02:06 +02:00
{
if ( m_game_window )
{
switch ( type )
{
2019-12-04 21:56:19 +01:00
case 0 : static_cast < gs_frame * > ( m_game_window ) - > progress_reset ( value ) ; break ;
case 1 : static_cast < gs_frame * > ( m_game_window ) - > progress_increment ( value ) ; break ;
case 2 : static_cast < gs_frame * > ( m_game_window ) - > progress_set_limit ( value ) ; break ;
2021-01-31 04:06:00 +01:00
case 3 : static_cast < gs_frame * > ( m_game_window ) - > progress_set_value ( value ) ; break ;
2020-02-01 05:15:50 +01:00
default : gui_log . fatal ( " Unknown type in handle_taskbar_progress(type=%d, value=%d) " , type , value ) ; break ;
2019-06-18 04:02:06 +02:00
}
}
} ;
2020-09-03 18:30:46 +02:00
callbacks . get_localized_string = [ ] ( localized_string_id id , const char * args ) - > std : : string
2020-09-02 16:20:29 +02:00
{
2020-09-03 18:30:46 +02:00
return localized_emu : : get_string ( id , args ) ;
2020-09-02 16:20:29 +02:00
} ;
2020-09-03 18:30:46 +02:00
callbacks . get_localized_u32string = [ ] ( localized_string_id id , const char * args ) - > std : : u32string
2020-09-02 16:20:29 +02:00
{
2020-09-03 18:30:46 +02:00
return localized_emu : : get_u32string ( id , args ) ;
2020-09-02 16:20:29 +02:00
} ;
2024-11-14 23:48:51 +01:00
callbacks . get_localized_setting = [ this ] ( const cfg : : _base * node , u32 enum_index ) - > std : : string
{
ensure ( ! ! m_emu_settings ) ;
return m_emu_settings - > GetLocalizedSetting ( node , enum_index ) ;
} ;
2021-05-22 10:42:05 +02:00
callbacks . play_sound = [ this ] ( const std : : string & path )
2021-10-29 22:04:49 +02:00
{
2021-05-22 10:42:05 +02:00
Emu . CallFromMainThread ( [ this , path ] ( )
2021-10-29 22:04:49 +02:00
{
if ( fs : : is_file ( path ) )
{
2024-04-19 03:13:46 +02:00
// Allow to play 3 sound effects at the same time
while ( m_sound_effects . size ( ) > = 3 )
{
m_sound_effects . pop_front ( ) ;
}
// Create a new sound effect. Re-using the same object seems to be broken for some users starting with Qt 6.6.3.
std : : unique_ptr < QSoundEffect > sound_effect = std : : make_unique < QSoundEffect > ( ) ;
2024-11-15 01:55:01 +01:00
sound_effect - > setSource ( QUrl : : fromLocalFile ( QString : : fromStdString ( path ) ) ) ;
2024-04-19 03:13:46 +02:00
sound_effect - > setVolume ( g_cfg . audio . volume * 0.01f ) ;
sound_effect - > play ( ) ;
m_sound_effects . push_back ( std : : move ( sound_effect ) ) ;
2021-10-29 22:04:49 +02:00
}
} ) ;
} ;
2023-06-01 02:08:09 +02:00
if ( m_show_gui ) // If this is false, we already have a fallback in the main_application.
{
callbacks . on_install_pkgs = [ this ] ( const std : : vector < std : : string > & pkgs )
{
ensure ( m_main_window ) ;
ensure ( ! pkgs . empty ( ) ) ;
QStringList pkg_list ;
for ( const std : : string & pkg : pkgs )
{
pkg_list < < QString : : fromStdString ( pkg ) ;
}
return m_main_window - > InstallPackages ( pkg_list , true ) ;
} ;
}
2023-06-16 20:05:35 +02:00
callbacks . on_emulation_stop_no_response = [ this ] ( std : : shared_ptr < atomic_t < bool > > closed_successfully , int seconds_waiting_already )
{
const std : : string terminate_message = tr ( " Stopping emulator took too long. "
" \n Some thread has probably deadlocked. Aborting. " ) . toStdString ( ) ;
if ( ! closed_successfully )
{
report_fatal_error ( terminate_message ) ;
}
Emu . CallFromMainThread ( [ this , closed_successfully , seconds_waiting_already , terminate_message ]
{
const auto seconds = std : : make_shared < int > ( seconds_waiting_already ) ;
QMessageBox * mb = new QMessageBox ( ) ;
mb - > setWindowTitle ( tr ( " PS3 Game/Application Is Unresponsive " ) ) ;
mb - > setIcon ( QMessageBox : : Critical ) ;
mb - > setStandardButtons ( QMessageBox : : Yes | QMessageBox : : No ) ;
mb - > setDefaultButton ( QMessageBox : : No ) ;
mb - > button ( QMessageBox : : Yes ) - > setText ( tr ( " Terminate RPCS3 " ) ) ;
mb - > button ( QMessageBox : : No ) - > setText ( tr ( " Keep Waiting " ) ) ;
QString text_base = tr ( " Waiting for %0 second(s) already to stop emulation without success. "
" \n Keep waiting or terminate RPCS3 unsafely at your own risk? " ) ;
mb - > setText ( text_base . arg ( 10 ) ) ;
mb - > layout ( ) - > setSizeConstraint ( QLayout : : SetFixedSize ) ;
mb - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
QTimer * update_timer = new QTimer ( mb ) ;
connect ( update_timer , & QTimer : : timeout , [ mb , seconds , text_base , closed_successfully ] ( )
{
* seconds + = 1 ;
mb - > setText ( text_base . arg ( * seconds ) ) ;
if ( * closed_successfully )
{
mb - > reject ( ) ;
}
} ) ;
connect ( mb , & QDialog : : accepted , mb , [ closed_successfully , terminate_message ]
{
if ( ! * closed_successfully )
{
report_fatal_error ( terminate_message ) ;
}
} ) ;
mb - > open ( ) ;
update_timer - > start ( 1000 ) ;
} ) ;
} ;
2024-03-06 16:28:07 +01:00
callbacks . on_save_state_progress = [ this ] ( std : : shared_ptr < atomic_t < bool > > closed_successfully , stx : : shared_ptr < utils : : serial > ar_ptr , stx : : atomic_ptr < std : : string > * code_location , std : : shared_ptr < void > init_mtx )
2024-03-24 11:08:42 +01:00
{
2024-03-06 16:28:07 +01:00
Emu . CallFromMainThread ( [ this , closed_successfully , ar_ptr , code_location , init_mtx ]
2024-03-24 11:08:42 +01:00
{
const auto half_seconds = std : : make_shared < int > ( 1 ) ;
progress_dialog * pdlg = new progress_dialog ( tr ( " Creating Save-State / Do Not Close RPCS3 " ) , tr ( " Please wait... " ) , tr ( " Hide Progress " ) , 0 , 100 , true , m_main_window ) ;
pdlg - > setAutoReset ( false ) ;
pdlg - > setAutoClose ( true ) ;
pdlg - > show ( ) ;
2024-03-06 16:28:07 +01:00
QString text_base = tr ( " %0 written, %1 second(s) passed%2 " ) ;
2024-03-24 11:08:42 +01:00
2024-03-06 16:28:07 +01:00
pdlg - > setLabelText ( text_base . arg ( " 0B " ) . arg ( 1 ) . arg ( " " ) ) ;
2024-03-24 11:08:42 +01:00
pdlg - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
QTimer * update_timer = new QTimer ( pdlg ) ;
2024-03-06 16:28:07 +01:00
connect ( update_timer , & QTimer : : timeout , [ pdlg , ar_ptr , half_seconds , text_base , closed_successfully
, code_location , init_mtx , old_written = usz { 0 } , repeat_count = u32 { 0 } ] ( ) mutable
2024-03-24 11:08:42 +01:00
{
2024-03-06 16:28:07 +01:00
std : : string verbose_message ;
usz bytes_written = 0 ;
2024-03-24 19:05:10 +01:00
2024-04-13 21:43:25 +02:00
while ( true )
2024-03-24 19:05:10 +01:00
{
2024-04-13 21:43:25 +02:00
auto mtx = static_cast < stx : : init_mutex * > ( init_mtx . get ( ) ) ;
auto init = mtx - > access ( ) ;
2024-03-06 16:28:07 +01:00
if ( ! init )
{
2024-04-13 21:43:25 +02:00
// Try to wait for the abort process to complete
auto fake_reset = mtx - > reset ( ) ;
if ( ! fake_reset )
{
// End of emulation termination
pdlg - > reject ( ) ;
return ;
}
fake_reset . set_init ( ) ;
// Now ar_ptr contains a null file descriptor
continue ;
2024-03-06 16:28:07 +01:00
}
if ( auto str_ptr = code_location - > load ( ) )
{
verbose_message = " \n " + * str_ptr ;
}
2024-05-05 03:37:49 +02:00
bytes_written = ar_ptr - > is_writing ( ) ? std : : max < usz > ( ar_ptr - > get_size ( ) , old_written ) : old_written ;
2024-04-13 21:43:25 +02:00
break ;
2024-03-24 19:05:10 +01:00
}
2024-04-13 21:43:25 +02:00
* half_seconds + = 1 ;
2024-03-06 16:28:07 +01:00
if ( old_written = = bytes_written )
{
if ( repeat_count = = 60 )
{
if ( verbose_message . empty ( ) )
{
verbose_message + = " \n " ;
}
else
{
verbose_message + = " . " ;
}
verbose_message + = " If Stuck, Report To Developers " ;
}
else
{
repeat_count + + ;
}
}
else
{
repeat_count = 0 ;
}
2024-03-24 11:08:42 +01:00
2024-03-06 16:28:07 +01:00
old_written = bytes_written ;
2024-11-15 01:55:01 +01:00
pdlg - > setLabelText ( text_base . arg ( gui : : utils : : format_byte_size ( bytes_written ) ) . arg ( * half_seconds / 2 ) . arg ( QString : : fromStdString ( verbose_message ) ) ) ;
2024-03-24 11:08:42 +01:00
// 300MB -> 50%, 600MB -> 75%, 1200MB -> 87.5% etc
2024-03-06 16:28:07 +01:00
const int percent = std : : clamp ( static_cast < int > ( 100. - 100. / std : : pow ( 2. , std : : fmax ( 0.01 , bytes_written * 1. / ( 300 * 1024 * 1024 ) ) ) ) , 2 , 100 ) ;
// Add a third of the remaining progress when the keyword is found
pdlg - > setValue ( verbose_message . find ( " Finalizing " ) ! = umax ? 100 - ( ( 100 - percent ) * 2 / 3 ) : percent ) ;
2024-03-24 11:08:42 +01:00
if ( * closed_successfully )
{
pdlg - > reject ( ) ;
}
} ) ;
pdlg - > open ( ) ;
update_timer - > start ( 500 ) ;
} ) ;
} ;
2023-09-25 17:32:50 +02:00
callbacks . add_breakpoint = [ this ] ( u32 addr )
{
Emu . BlockingCallFromMainThread ( [ this , addr ] ( )
{
m_main_window - > OnAddBreakpoint ( addr ) ;
} ) ;
} ;
2019-06-18 04:02:06 +02:00
Emu . SetCallbacks ( std : : move ( callbacks ) ) ;
}
2020-02-07 21:55:29 +01:00
void gui_application : : StartPlaytime ( bool start_playtime = true )
2019-10-31 07:59:37 +01:00
{
2020-02-07 21:55:29 +01:00
if ( ! start_playtime )
{
return ;
}
2024-11-15 01:55:01 +01:00
const QString serial = QString : : fromStdString ( Emu . GetTitleID ( ) ) ;
2019-11-17 10:57:21 +01:00
if ( serial . isEmpty ( ) )
{
return ;
}
2024-08-24 20:00:07 +02:00
m_persistent_settings - > SetLastPlayed ( serial , QDateTime : : currentDateTime ( ) . toString ( gui : : persistent : : last_played_date_format ) , true ) ;
2019-10-31 07:59:37 +01:00
m_timer_playtime . start ( ) ;
2020-04-28 19:12:36 +02:00
m_timer . start ( 10000 ) ; // Update every 10 seconds in case the emulation crashes
}
void gui_application : : UpdatePlaytime ( )
{
if ( ! m_timer_playtime . isValid ( ) )
{
m_timer . stop ( ) ;
return ;
}
2024-11-15 01:55:01 +01:00
const QString serial = QString : : fromStdString ( Emu . GetTitleID ( ) ) ;
2020-04-28 19:12:36 +02:00
if ( serial . isEmpty ( ) )
{
m_timer_playtime . invalidate ( ) ;
m_timer . stop ( ) ;
return ;
}
2024-08-24 20:00:07 +02:00
m_persistent_settings - > AddPlaytime ( serial , m_timer_playtime . restart ( ) , false ) ;
m_persistent_settings - > SetLastPlayed ( serial , QDateTime : : currentDateTime ( ) . toString ( gui : : persistent : : last_played_date_format ) , true ) ;
2019-10-31 07:59:37 +01:00
}
void gui_application : : StopPlaytime ( )
{
2020-04-28 19:12:36 +02:00
m_timer . stop ( ) ;
2019-10-31 07:59:37 +01:00
if ( ! m_timer_playtime . isValid ( ) )
return ;
2024-11-15 01:55:01 +01:00
const QString serial = QString : : fromStdString ( Emu . GetTitleID ( ) ) ;
2019-11-17 10:57:21 +01:00
if ( serial . isEmpty ( ) )
{
m_timer_playtime . invalidate ( ) ;
return ;
}
2024-08-24 20:00:07 +02:00
m_persistent_settings - > AddPlaytime ( serial , m_timer_playtime . restart ( ) , false ) ;
m_persistent_settings - > SetLastPlayed ( serial , QDateTime : : currentDateTime ( ) . toString ( gui : : persistent : : last_played_date_format ) , true ) ;
2019-10-31 07:59:37 +01:00
m_timer_playtime . invalidate ( ) ;
}
2019-06-18 04:02:06 +02:00
/*
2021-02-07 13:14:36 +01:00
* Handle a request to change the stylesheet based on the current entry in the settings .
2019-06-18 04:02:06 +02:00
*/
2021-02-07 13:14:36 +01:00
void gui_application : : OnChangeStyleSheetRequest ( )
2019-06-18 04:02:06 +02:00
{
2019-09-08 18:27:36 +02:00
// skip stylesheets on first repaint if a style was set from command line
if ( m_use_cli_style & & gui : : stylesheet . isEmpty ( ) )
{
gui : : stylesheet = styleSheet ( ) . isEmpty ( ) ? " /* style set by command line arg */ " : styleSheet ( ) ;
if ( m_main_window )
{
m_main_window - > RepaintGui ( ) ;
}
return ;
}
2021-02-08 20:15:46 +01:00
// Remove old fonts
QFontDatabase : : removeAllApplicationFonts ( ) ;
2021-02-07 16:34:53 +01:00
const QString stylesheet_name = m_gui_settings - > GetValue ( gui : : m_currentStylesheet ) . toString ( ) ;
2019-06-18 04:02:06 +02:00
2023-12-22 00:33:36 +01:00
// Determine default style
if ( m_default_style . isEmpty ( ) )
2023-12-19 23:20:25 +01:00
{
2024-04-17 23:40:45 +02:00
# ifdef _WIN32
// On windows, the custom stylesheets don't seem to work properly unless we use the windowsvista style as default
if ( QStyleFactory : : keys ( ) . contains ( " windowsvista " ) )
{
m_default_style = " windowsvista " ;
gui_log . notice ( " Using '%s' as default style " , m_default_style ) ;
}
# endif
2023-12-22 00:33:36 +01:00
// Use the initial style as default style
2024-04-17 23:40:45 +02:00
if ( const QStyle * style = m_default_style . isEmpty ( ) ? QApplication : : style ( ) : nullptr )
2023-12-19 23:20:25 +01:00
{
2023-12-22 00:33:36 +01:00
m_default_style = style - > name ( ) ;
gui_log . notice ( " Determined '%s' as default style " , m_default_style ) ;
}
// Fallback to the first style, which is supposed to be the default style according to the Qt docs.
if ( m_default_style . isEmpty ( ) )
{
if ( const QStringList styles = QStyleFactory : : keys ( ) ; ! styles . empty ( ) )
{
m_default_style = styles . front ( ) ;
gui_log . notice ( " Determined '%s' as default style (first style available) " , m_default_style ) ;
}
2023-12-19 23:20:25 +01:00
}
}
2023-12-22 00:33:36 +01:00
// Reset style to default before doing anything else, or we will get unexpected effects in custom stylesheets.
if ( QStyle * style = QStyleFactory : : create ( m_default_style ) )
{
setStyle ( style ) ;
}
2023-12-19 23:20:25 +01:00
const auto match_native_style = [ & stylesheet_name ] ( ) - > QString
{
// Search for "native (<style>)"
static const QRegularExpression expr ( gui : : NativeStylesheet + " \\ ((?<style>.*) \ \ ) " ) ;
const QRegularExpressionMatch match = expr . match ( stylesheet_name ) ;
if ( match . hasMatch ( ) )
{
return match . captured ( " style " ) ;
}
return { } ;
} ;
gui_log . notice ( " Changing stylesheet to '%s' " , stylesheet_name ) ;
2023-12-22 03:25:19 +01:00
gui : : custom_stylesheet_active = false ;
2023-12-19 23:20:25 +01:00
2021-02-07 16:34:53 +01:00
if ( stylesheet_name . isEmpty ( ) | | stylesheet_name = = gui : : DefaultStylesheet )
2019-06-18 04:02:06 +02:00
{
2023-12-19 23:20:25 +01:00
gui_log . notice ( " Using default stylesheet " ) ;
2019-08-14 20:30:53 +02:00
setStyleSheet ( gui : : stylesheets : : default_style_sheet ) ;
2023-12-22 03:25:19 +01:00
gui : : custom_stylesheet_active = true ;
2019-06-18 04:02:06 +02:00
}
2021-02-07 16:34:53 +01:00
else if ( stylesheet_name = = gui : : NoStylesheet )
2019-09-08 18:27:36 +02:00
{
2023-12-19 23:20:25 +01:00
gui_log . notice ( " Using empty style " ) ;
2019-09-08 18:27:36 +02:00
setStyleSheet ( " /* none */ " ) ;
}
2023-12-19 23:20:25 +01:00
else if ( const QString native_style = match_native_style ( ) ; ! native_style . isEmpty ( ) )
{
if ( QStyle * style = QStyleFactory : : create ( native_style ) )
{
gui_log . notice ( " Using native style '%s' " , native_style ) ;
setStyleSheet ( " /* none */ " ) ;
setStyle ( style ) ;
}
else
{
gui_log . error ( " Failed to set stylesheet: Native style '%s' not available " , native_style ) ;
}
}
2021-02-07 13:14:36 +01:00
else
2019-06-18 04:02:06 +02:00
{
2021-02-07 13:14:36 +01:00
QString stylesheet_path ;
QString stylesheet_dir ;
2024-11-12 20:21:44 +01:00
std : : vector < QDir > locs ;
locs . push_back ( m_gui_settings - > GetSettingsDir ( ) ) ;
2021-02-07 13:14:36 +01:00
# if !defined(_WIN32)
2021-11-24 07:40:04 +01:00
# ifdef __APPLE__
2024-11-12 20:21:44 +01:00
locs . push_back ( QCoreApplication : : applicationDirPath ( ) + " /../Resources/GuiConfigs/ " ) ;
2021-02-07 13:14:36 +01:00
# else
2021-11-24 07:40:04 +01:00
# ifdef DATADIR
const QString data_dir = ( DATADIR ) ;
2024-11-12 20:21:44 +01:00
locs . push_back ( data_dir + " /GuiConfigs/ " ) ;
2021-11-24 07:40:04 +01:00
# endif
2024-11-12 20:21:44 +01:00
locs . push_back ( QCoreApplication : : applicationDirPath ( ) + " /../share/rpcs3/GuiConfigs/ " ) ;
2021-02-07 13:14:36 +01:00
# endif
2024-11-12 20:21:44 +01:00
locs . push_back ( QCoreApplication : : applicationDirPath ( ) + " /GuiConfigs/ " ) ;
2021-02-07 13:14:36 +01:00
# endif
2024-11-12 20:21:44 +01:00
for ( QDir & loc : locs )
2021-02-07 13:14:36 +01:00
{
2021-02-07 16:34:53 +01:00
QFileInfo file_info ( loc . absoluteFilePath ( stylesheet_name + QStringLiteral ( " .qss " ) ) ) ;
2021-02-07 13:14:36 +01:00
if ( file_info . exists ( ) )
{
loc . cdUp ( ) ;
stylesheet_dir = loc . absolutePath ( ) ;
stylesheet_path = file_info . absoluteFilePath ( ) ;
break ;
}
}
2019-06-18 04:02:06 +02:00
2021-02-07 13:14:36 +01:00
if ( QFile file ( stylesheet_path ) ; ! stylesheet_path . isEmpty ( ) & & file . open ( QIODevice : : ReadOnly | QIODevice : : Text ) )
{
2024-11-15 01:55:01 +01:00
const QString config_dir = QString : : fromStdString ( fs : : get_config_dir ( ) ) ;
2019-08-14 20:30:53 +02:00
2021-02-07 13:14:36 +01:00
// Add PS3 fonts
2024-11-15 01:55:01 +01:00
QDirIterator ps3_font_it ( QString : : fromStdString ( g_cfg_vfs . get_dev_flash ( ) + " data/font/ " ) , QStringList ( ) < < " *.ttf " , QDir : : Files , QDirIterator : : Subdirectories ) ;
2021-02-07 13:14:36 +01:00
while ( ps3_font_it . hasNext ( ) )
QFontDatabase : : addApplicationFont ( ps3_font_it . next ( ) ) ;
2019-06-18 04:02:06 +02:00
2021-02-07 13:14:36 +01:00
// Add custom fonts
QDirIterator custom_font_it ( config_dir + " fonts/ " , QStringList ( ) < < " *.ttf " , QDir : : Files , QDirIterator : : Subdirectories ) ;
while ( custom_font_it . hasNext ( ) )
QFontDatabase : : addApplicationFont ( custom_font_it . next ( ) ) ;
2021-02-07 16:34:53 +01:00
// Replace relative paths with absolute paths. Since relative paths should always be the same, we can just use simple string replacement.
// Another option would be to use QDir::setCurrent, but that changes current working directory for the whole process (We don't want that).
QString stylesheet = file . readAll ( ) ;
stylesheet . replace ( QStringLiteral ( " url( \" GuiConfigs/ " ) , QStringLiteral ( " url( \" " ) + stylesheet_dir + QStringLiteral ( " /GuiConfigs/ " ) ) ;
setStyleSheet ( stylesheet ) ;
2021-02-07 13:14:36 +01:00
file . close ( ) ;
}
else
{
2023-06-12 03:45:37 +02:00
gui_log . error ( " Could not find stylesheet '%s'. Using default. " , stylesheet_name ) ;
2021-02-07 13:14:36 +01:00
setStyleSheet ( gui : : stylesheets : : default_style_sheet ) ;
}
2023-12-22 03:25:19 +01:00
gui : : custom_stylesheet_active = true ;
2019-06-18 04:02:06 +02:00
}
gui : : stylesheet = styleSheet ( ) ;
2019-08-25 11:51:13 +02:00
if ( m_main_window )
{
m_main_window - > RepaintGui ( ) ;
}
2019-06-18 04:02:06 +02:00
}
2023-11-25 01:33:53 +01:00
void gui_application : : OnShortcutChange ( )
{
if ( m_game_window )
{
static_cast < gs_frame * > ( m_game_window ) - > update_shortcuts ( ) ;
}
}
2019-06-18 04:02:06 +02:00
/**
* Using connects avoids timers being unable to be used in a non - qt thread . So , even if this looks stupid to just call func , it ' s succinct .
*/
2023-07-31 22:57:26 +02:00
void gui_application : : CallFromMainThread ( const std : : function < void ( ) > & func , atomic_t < u32 > * wake_up )
2019-06-18 04:02:06 +02:00
{
func ( ) ;
2022-06-24 19:58:26 +02:00
if ( wake_up )
{
* wake_up = true ;
wake_up - > notify_one ( ) ;
}
2019-06-18 04:02:06 +02:00
}
2023-07-07 09:31:35 +02:00
void gui_application : : OnAppStateChanged ( Qt : : ApplicationState state )
{
// Invalidate previous delayed pause call (even when the setting is off because it is dynamic)
m_pause_delayed_tag + + ;
if ( ! g_cfg . misc . autopause )
{
return ;
}
const auto emu_state = Emu . GetStatus ( ) ;
const bool is_active = state = = Qt : : ApplicationActive ;
if ( emu_state ! = system_state : : paused & & emu_state ! = system_state : : running )
{
return ;
}
const bool is_paused = emu_state = = system_state : : paused ;
if ( is_active ! = is_paused )
{
// Nothing to do (either paused and this is focus-out event or running and this is a focus-in event)
// Invalidate data
m_is_pause_on_focus_loss_active = false ;
m_emu_focus_out_emulation_id = Emulator : : stop_counter_t { } ;
return ;
}
if ( is_paused )
{
// Check if Emu.Resume() or Emu.Kill() has not been called since
if ( m_is_pause_on_focus_loss_active & & m_pause_amend_time_on_focus_loss = = Emu . GetPauseTime ( ) & & m_emu_focus_out_emulation_id = = Emu . GetEmulationIdentifier ( ) )
{
m_is_pause_on_focus_loss_active = false ;
Emu . Resume ( ) ;
}
return ;
}
// Gather validation data
m_emu_focus_out_emulation_id = Emu . GetEmulationIdentifier ( ) ;
auto pause_callback = [ this , delayed_tag = m_pause_delayed_tag ] ( )
{
// Check if Emu.Kill() has not been called since
if ( applicationState ( ) ! = Qt : : ApplicationActive & & Emu . IsRunning ( ) & &
m_emu_focus_out_emulation_id = = Emu . GetEmulationIdentifier ( ) & &
delayed_tag = = m_pause_delayed_tag & &
! m_is_pause_on_focus_loss_active )
{
if ( Emu . Pause ( ) )
{
// Gather validation data
m_pause_amend_time_on_focus_loss = Emu . GetPauseTime ( ) ;
m_emu_focus_out_emulation_id = Emu . GetEmulationIdentifier ( ) ;
m_is_pause_on_focus_loss_active = true ;
}
}
} ;
if ( state = = Qt : : ApplicationSuspended )
{
// Must be invoked now (otherwise it may not happen later)
pause_callback ( ) ;
return ;
}
// Delay pause so it won't immediately pause the emulated application
QTimer : : singleShot ( 1000 , this , pause_callback ) ;
}
2024-02-05 23:49:00 +01:00
bool gui_application : : native_event_filter : : nativeEventFilter ( [[maybe_unused]] const QByteArray& eventType, [[maybe_unused]] void* message, [[maybe_unused]] qintptr * result )
{
# ifdef _WIN32
2024-05-20 14:43:17 +02:00
if ( ! Emu . IsRunning ( ) & & ! g_raw_mouse_handler )
2024-02-05 23:49:00 +01:00
{
return false ;
}
if ( eventType = = " windows_generic_MSG " )
{
if ( MSG * msg = static_cast < MSG * > ( message ) ; msg & & msg - > message = = WM_INPUT )
{
if ( auto * handler = g_fxo - > try_get < MouseHandlerBase > ( ) ; handler & & handler - > type = = mouse_handler : : raw )
{
static_cast < raw_mouse_handler * > ( handler ) - > handle_native_event ( * msg ) ;
}
2024-05-20 14:01:20 +02:00
if ( g_raw_mouse_handler )
{
g_raw_mouse_handler - > handle_native_event ( * msg ) ;
}
2024-02-05 23:49:00 +01:00
}
}
# endif
return false ;
}