2015-01-04 17:36:57 +00:00
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
2016-03-06 13:04:47 +00:00
# include <clocale>
2014-10-28 07:36:00 +00:00
# include <thread>
2014-04-01 02:26:50 +00:00
# include <QDesktopWidget>
2015-09-11 04:23:00 +00:00
# include <QtGui>
2014-04-01 02:26:50 +00:00
# include <QFileDialog>
2015-07-28 16:43:18 +00:00
# include <QMessageBox>
2014-04-01 02:26:50 +00:00
# include "qhexedit.h"
2015-09-11 04:23:00 +00:00
# include "citra_qt/bootmanager.h"
# include "citra_qt/config.h"
2016-01-24 17:34:05 +00:00
# include "citra_qt/configure_dialog.h"
2015-09-01 04:35:33 +00:00
# include "citra_qt/game_list.h"
2015-09-11 04:23:00 +00:00
# include "citra_qt/hotkeys.h"
# include "citra_qt/main.h"
// Debugger
# include "citra_qt/debugger/callstack.h"
# include "citra_qt/debugger/disassembler.h"
# include "citra_qt/debugger/graphics.h"
# include "citra_qt/debugger/graphics_breakpoints.h"
# include "citra_qt/debugger/graphics_cmdlists.h"
# include "citra_qt/debugger/graphics_framebuffer.h"
# include "citra_qt/debugger/graphics_tracing.h"
# include "citra_qt/debugger/graphics_vertex_shader.h"
# include "citra_qt/debugger/profiler.h"
# include "citra_qt/debugger/ramview.h"
# include "citra_qt/debugger/registers.h"
2015-04-29 04:01:41 +00:00
# include "common/make_unique.h"
2015-08-17 21:25:21 +00:00
# include "common/microprofile.h"
2014-10-28 07:36:00 +00:00
# include "common/platform.h"
2015-06-21 13:58:59 +00:00
# include "common/scm_rev.h"
2014-10-28 07:36:00 +00:00
# include "common/scope_exit.h"
2015-09-11 04:23:00 +00:00
# include "common/string_util.h"
# include "common/logging/backend.h"
# include "common/logging/filter.h"
# include "common/logging/log.h"
# include "common/logging/text_formatter.h"
2014-10-28 07:36:00 +00:00
2015-09-11 04:23:00 +00:00
# include "core/core.h"
2014-10-27 21:18:28 +00:00
# include "core/settings.h"
2014-04-11 00:50:10 +00:00
# include "core/system.h"
2014-05-01 03:46:57 +00:00
# include "core/arm/disassembler/load_symbol_map.h"
2015-10-22 02:19:55 +00:00
# include "core/gdbstub/gdbstub.h"
2015-09-11 04:23:00 +00:00
# include "core/loader/loader.h"
2014-06-16 22:03:13 +00:00
2015-05-19 04:21:33 +00:00
# include "video_core/video_core.h"
2015-04-16 22:35:09 +00:00
GMainWindow : : GMainWindow ( ) : emu_thread ( nullptr )
2014-04-01 02:26:50 +00:00
{
2014-10-25 16:02:26 +00:00
Pica : : g_debug_context = Pica : : DebugContext : : Construct ( ) ;
2014-09-13 00:06:13 +00:00
Config config ;
2014-04-01 02:26:50 +00:00
ui . setupUi ( this ) ;
statusBar ( ) - > hide ( ) ;
2015-04-29 04:01:41 +00:00
render_window = new GRenderWindow ( this , emu_thread . get ( ) ) ;
2014-04-22 03:15:17 +00:00
render_window - > hide ( ) ;
2014-04-01 02:26:50 +00:00
2015-09-01 04:35:33 +00:00
game_list = new GameList ( ) ;
ui . horizontalLayout - > addWidget ( game_list ) ;
2015-02-05 16:53:25 +00:00
profilerWidget = new ProfilerWidget ( this ) ;
addDockWidget ( Qt : : BottomDockWidgetArea , profilerWidget ) ;
profilerWidget - > hide ( ) ;
2015-08-17 21:25:21 +00:00
microProfileDialog = new MicroProfileDialog ( this ) ;
microProfileDialog - > hide ( ) ;
2015-04-29 04:01:41 +00:00
disasmWidget = new DisassemblerWidget ( this , emu_thread . get ( ) ) ;
2014-04-18 22:30:53 +00:00
addDockWidget ( Qt : : BottomDockWidgetArea , disasmWidget ) ;
disasmWidget - > hide ( ) ;
2014-04-01 02:26:50 +00:00
2014-04-18 22:30:53 +00:00
registersWidget = new RegistersWidget ( this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , registersWidget ) ;
registersWidget - > hide ( ) ;
callstackWidget = new CallstackWidget ( this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , callstackWidget ) ;
callstackWidget - > hide ( ) ;
2014-04-01 02:26:50 +00:00
2014-05-17 20:38:10 +00:00
graphicsWidget = new GPUCommandStreamWidget ( this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , graphicsWidget ) ;
2014-08-14 17:21:55 +00:00
graphicsWidget - > hide ( ) ;
2014-05-17 20:38:10 +00:00
2014-05-18 15:52:22 +00:00
graphicsCommandsWidget = new GPUCommandListWidget ( this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , graphicsCommandsWidget ) ;
2014-08-14 17:21:55 +00:00
graphicsCommandsWidget - > hide ( ) ;
2014-05-18 15:52:22 +00:00
2014-10-25 18:28:24 +00:00
auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget ( Pica : : g_debug_context , this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , graphicsBreakpointsWidget ) ;
graphicsBreakpointsWidget - > hide ( ) ;
2014-10-26 15:38:40 +00:00
auto graphicsFramebufferWidget = new GraphicsFramebufferWidget ( Pica : : g_debug_context , this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , graphicsFramebufferWidget ) ;
graphicsFramebufferWidget - > hide ( ) ;
2014-12-10 18:24:56 +00:00
auto graphicsVertexShaderWidget = new GraphicsVertexShaderWidget ( Pica : : g_debug_context , this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , graphicsVertexShaderWidget ) ;
graphicsVertexShaderWidget - > hide ( ) ;
2015-04-04 10:57:31 +00:00
auto graphicsTracingWidget = new GraphicsTracingWidget ( Pica : : g_debug_context , this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , graphicsTracingWidget ) ;
graphicsTracingWidget - > hide ( ) ;
2014-04-01 02:26:50 +00:00
QMenu * debug_menu = ui . menu_View - > addMenu ( tr ( " Debugging " ) ) ;
2015-02-05 16:53:25 +00:00
debug_menu - > addAction ( profilerWidget - > toggleViewAction ( ) ) ;
2015-08-17 21:25:21 +00:00
debug_menu - > addAction ( microProfileDialog - > toggleViewAction ( ) ) ;
2014-04-18 22:30:53 +00:00
debug_menu - > addAction ( disasmWidget - > toggleViewAction ( ) ) ;
debug_menu - > addAction ( registersWidget - > toggleViewAction ( ) ) ;
debug_menu - > addAction ( callstackWidget - > toggleViewAction ( ) ) ;
2014-05-17 20:38:10 +00:00
debug_menu - > addAction ( graphicsWidget - > toggleViewAction ( ) ) ;
2014-05-18 15:52:22 +00:00
debug_menu - > addAction ( graphicsCommandsWidget - > toggleViewAction ( ) ) ;
2014-10-25 18:28:24 +00:00
debug_menu - > addAction ( graphicsBreakpointsWidget - > toggleViewAction ( ) ) ;
2014-10-26 15:38:40 +00:00
debug_menu - > addAction ( graphicsFramebufferWidget - > toggleViewAction ( ) ) ;
2014-12-10 18:24:56 +00:00
debug_menu - > addAction ( graphicsVertexShaderWidget - > toggleViewAction ( ) ) ;
2015-04-04 10:57:31 +00:00
debug_menu - > addAction ( graphicsTracingWidget - > toggleViewAction ( ) ) ;
2014-04-01 02:26:50 +00:00
// Set default UI state
// geometry: 55% of the window contents are in the upper screen half, 45% in the lower half
QDesktopWidget * desktop = ( ( QApplication * ) QApplication : : instance ( ) ) - > desktop ( ) ;
QRect screenRect = desktop - > screenGeometry ( this ) ;
int x , y , w , h ;
w = screenRect . width ( ) * 2 / 3 ;
h = screenRect . height ( ) / 2 ;
x = ( screenRect . x ( ) + screenRect . width ( ) ) / 2 - w / 2 ;
y = ( screenRect . y ( ) + screenRect . height ( ) ) / 2 - h * 55 / 100 ;
setGeometry ( x , y , w , h ) ;
// Restore UI state
2015-07-26 15:13:02 +00:00
QSettings settings ;
2015-09-08 01:11:21 +00:00
settings . beginGroup ( " UILayout " ) ;
2014-04-01 02:26:50 +00:00
restoreGeometry ( settings . value ( " geometry " ) . toByteArray ( ) ) ;
restoreState ( settings . value ( " state " ) . toByteArray ( ) ) ;
render_window - > restoreGeometry ( settings . value ( " geometryRenderWindow " ) . toByteArray ( ) ) ;
2015-08-17 21:25:21 +00:00
microProfileDialog - > restoreGeometry ( settings . value ( " microProfileDialogGeometry " ) . toByteArray ( ) ) ;
microProfileDialog - > setVisible ( settings . value ( " microProfileDialogVisible " ) . toBool ( ) ) ;
2015-09-08 01:11:21 +00:00
settings . endGroup ( ) ;
2014-04-01 02:26:50 +00:00
2015-09-07 06:51:57 +00:00
game_list - > LoadInterfaceLayout ( settings ) ;
2016-01-24 17:34:05 +00:00
GDBStub : : ToggleServer ( Settings : : values . use_gdbstub ) ;
2015-09-02 12:56:38 +00:00
GDBStub : : SetServerPort ( static_cast < u32 > ( Settings : : values . gdbstub_port ) ) ;
2014-12-31 21:26:11 +00:00
ui . action_Single_Window_Mode - > setChecked ( settings . value ( " singleWindowMode " , true ) . toBool ( ) ) ;
2014-04-22 03:15:17 +00:00
ToggleWindowMode ( ) ;
2014-04-01 02:26:50 +00:00
2015-01-06 15:09:30 +00:00
ui . actionDisplay_widget_title_bars - > setChecked ( settings . value ( " displayTitleBars " , true ) . toBool ( ) ) ;
OnDisplayTitleBars ( ui . actionDisplay_widget_title_bars - > isChecked ( ) ) ;
2015-07-28 16:43:18 +00:00
// Prepare actions for recent files
for ( int i = 0 ; i < max_recent_files_item ; + + i ) {
actions_recent_files [ i ] = new QAction ( this ) ;
actions_recent_files [ i ] - > setVisible ( false ) ;
connect ( actions_recent_files [ i ] , SIGNAL ( triggered ( ) ) , this , SLOT ( OnMenuRecentFile ( ) ) ) ;
ui . menu_recent_files - > addAction ( actions_recent_files [ i ] ) ;
}
UpdateRecentFiles ( ) ;
2016-01-13 17:40:41 +00:00
confirm_before_closing = settings . value ( " confirmClose " , true ) . toBool ( ) ;
2014-04-01 02:26:50 +00:00
// Setup connections
2015-09-01 04:35:33 +00:00
connect ( game_list , SIGNAL ( GameChosen ( QString ) ) , this , SLOT ( OnGameListLoadFile ( QString ) ) ) ;
2016-01-24 17:34:05 +00:00
connect ( ui . action_Configure , SIGNAL ( triggered ( ) ) , this , SLOT ( OnConfigure ( ) ) ) ;
2014-04-22 03:15:17 +00:00
connect ( ui . action_Load_File , SIGNAL ( triggered ( ) ) , this , SLOT ( OnMenuLoadFile ( ) ) ) ;
2014-05-01 03:46:57 +00:00
connect ( ui . action_Load_Symbol_Map , SIGNAL ( triggered ( ) ) , this , SLOT ( OnMenuLoadSymbolMap ( ) ) ) ;
2015-09-06 22:33:57 +00:00
connect ( ui . action_Select_Game_List_Root , SIGNAL ( triggered ( ) ) , this , SLOT ( OnMenuSelectGameListRoot ( ) ) ) ;
2014-04-22 03:15:17 +00:00
connect ( ui . action_Start , SIGNAL ( triggered ( ) ) , this , SLOT ( OnStartGame ( ) ) ) ;
connect ( ui . action_Pause , SIGNAL ( triggered ( ) ) , this , SLOT ( OnPauseGame ( ) ) ) ;
connect ( ui . action_Stop , SIGNAL ( triggered ( ) ) , this , SLOT ( OnStopGame ( ) ) ) ;
2014-12-31 21:26:11 +00:00
connect ( ui . action_Single_Window_Mode , SIGNAL ( triggered ( bool ) ) , this , SLOT ( ToggleWindowMode ( ) ) ) ;
2014-04-01 02:26:50 +00:00
2015-04-30 23:46:50 +00:00
connect ( this , SIGNAL ( EmulationStarting ( EmuThread * ) ) , disasmWidget , SLOT ( OnEmulationStarting ( EmuThread * ) ) ) ;
connect ( this , SIGNAL ( EmulationStopping ( ) ) , disasmWidget , SLOT ( OnEmulationStopping ( ) ) ) ;
connect ( this , SIGNAL ( EmulationStarting ( EmuThread * ) ) , registersWidget , SLOT ( OnEmulationStarting ( EmuThread * ) ) ) ;
connect ( this , SIGNAL ( EmulationStopping ( ) ) , registersWidget , SLOT ( OnEmulationStopping ( ) ) ) ;
connect ( this , SIGNAL ( EmulationStarting ( EmuThread * ) ) , render_window , SLOT ( OnEmulationStarting ( EmuThread * ) ) ) ;
connect ( this , SIGNAL ( EmulationStopping ( ) ) , render_window , SLOT ( OnEmulationStopping ( ) ) ) ;
2015-05-21 00:12:59 +00:00
connect ( this , SIGNAL ( EmulationStarting ( EmuThread * ) ) , graphicsTracingWidget , SLOT ( OnEmulationStarting ( EmuThread * ) ) ) ;
connect ( this , SIGNAL ( EmulationStopping ( ) ) , graphicsTracingWidget , SLOT ( OnEmulationStopping ( ) ) ) ;
2014-04-01 02:26:50 +00:00
// Setup hotkeys
2014-04-22 03:15:17 +00:00
RegisterHotkey ( " Main Window " , " Load File " , QKeySequence : : Open ) ;
2014-04-01 02:26:50 +00:00
RegisterHotkey ( " Main Window " , " Start Emulation " ) ;
LoadHotkeys ( settings ) ;
2014-04-22 03:15:17 +00:00
connect ( GetHotkey ( " Main Window " , " Load File " , this ) , SIGNAL ( activated ( ) ) , this , SLOT ( OnMenuLoadFile ( ) ) ) ;
2014-04-01 02:26:50 +00:00
connect ( GetHotkey ( " Main Window " , " Start Emulation " , this ) , SIGNAL ( activated ( ) ) , this , SLOT ( OnStartGame ( ) ) ) ;
2014-11-13 17:17:39 +00:00
std : : string window_title = Common : : StringFromFormat ( " Citra | %s-%s " , Common : : g_scm_branch , Common : : g_scm_desc ) ;
setWindowTitle ( window_title . c_str ( ) ) ;
2014-04-24 02:49:55 +00:00
show ( ) ;
2016-01-07 00:54:14 +00:00
game_list - > PopulateAsync ( settings . value ( " gameListRootDir " , " . " ) . toString ( ) , settings . value ( " gameListDeepScan " , false ) . toBool ( ) ) ;
2015-09-01 04:35:33 +00:00
2014-10-31 05:44:51 +00:00
QStringList args = QApplication : : arguments ( ) ;
if ( args . length ( ) > = 2 ) {
BootGame ( args [ 1 ] . toStdString ( ) ) ;
}
2014-04-01 02:26:50 +00:00
}
GMainWindow : : ~ GMainWindow ( )
{
// will get automatically deleted otherwise
2014-12-03 18:57:57 +00:00
if ( render_window - > parent ( ) = = nullptr )
2014-04-01 02:26:50 +00:00
delete render_window ;
2014-10-25 16:02:26 +00:00
Pica : : g_debug_context . reset ( ) ;
2014-04-01 02:26:50 +00:00
}
2015-01-06 15:09:30 +00:00
void GMainWindow : : OnDisplayTitleBars ( bool show )
{
QList < QDockWidget * > widgets = findChildren < QDockWidget * > ( ) ;
if ( show ) {
for ( QDockWidget * widget : widgets ) {
QWidget * old = widget - > titleBarWidget ( ) ;
widget - > setTitleBarWidget ( nullptr ) ;
if ( old ! = nullptr )
delete old ;
}
} else {
for ( QDockWidget * widget : widgets ) {
QWidget * old = widget - > titleBarWidget ( ) ;
widget - > setTitleBarWidget ( new QWidget ( ) ) ;
if ( old ! = nullptr )
delete old ;
}
}
}
2016-01-07 19:33:54 +00:00
bool GMainWindow : : InitializeSystem ( ) {
2015-07-28 16:43:18 +00:00
// Shutdown previous session if the emu thread is still active...
if ( emu_thread ! = nullptr )
ShutdownGame ( ) ;
2015-04-29 04:01:41 +00:00
// Initialize the core emulation
2016-01-07 19:33:54 +00:00
System : : Result system_result = System : : Init ( render_window ) ;
if ( System : : Result : : Success ! = system_result ) {
switch ( system_result ) {
case System : : Result : : ErrorInitVideoCore :
QMessageBox : : critical ( this , tr ( " Error while starting Citra! " ) ,
tr ( " Failed to initialize the video core! \n \n "
" Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver. " ) ) ;
break ;
default :
QMessageBox : : critical ( this , tr ( " Error while starting Citra! " ) ,
tr ( " Unknown error (please check the log)! " ) ) ;
break ;
}
return false ;
}
return true ;
}
2014-04-04 01:24:07 +00:00
2016-01-07 19:33:54 +00:00
bool GMainWindow : : LoadROM ( const std : : string & filename ) {
2016-01-07 17:36:10 +00:00
Loader : : ResultStatus result = Loader : : LoadFile ( filename ) ;
if ( Loader : : ResultStatus : : Success ! = result ) {
2014-12-06 01:53:49 +00:00
LOG_CRITICAL ( Frontend , " Failed to load ROM! " ) ;
2015-04-29 04:01:41 +00:00
System : : Shutdown ( ) ;
2016-01-07 17:36:10 +00:00
switch ( result ) {
case Loader : : ResultStatus : : ErrorEncrypted : {
// Build the MessageBox ourselves to have clickable link
QMessageBox popup_error ;
popup_error . setTextFormat ( Qt : : RichText ) ;
2016-01-07 19:33:54 +00:00
popup_error . setWindowTitle ( tr ( " Error while loading ROM! " ) ) ;
popup_error . setText ( tr ( " The game that you are trying to load must be decrypted before being used with Citra.<br/><br/> "
" For more information on dumping and decrypting games, please see: <a href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://citra-emu.org/wiki/Dumping-Game-Cartridges</a> " ) ) ;
2016-01-07 17:36:10 +00:00
popup_error . setIcon ( QMessageBox : : Critical ) ;
popup_error . exec ( ) ;
break ;
}
case Loader : : ResultStatus : : ErrorInvalidFormat :
2016-01-07 19:33:54 +00:00
QMessageBox : : critical ( this , tr ( " Error while loading ROM! " ) ,
2016-01-07 17:36:10 +00:00
tr ( " The ROM format is not supported. " ) ) ;
break ;
case Loader : : ResultStatus : : Error :
default :
2016-01-07 19:33:54 +00:00
QMessageBox : : critical ( this , tr ( " Error while loading ROM! " ) ,
tr ( " Unknown error! " ) ) ;
2016-01-07 17:36:10 +00:00
break ;
}
2016-01-07 19:33:54 +00:00
return false ;
2014-04-04 01:24:07 +00:00
}
2016-01-07 19:33:54 +00:00
return true ;
}
void GMainWindow : : BootGame ( const std : : string & filename ) {
LOG_INFO ( Frontend , " Citra starting... " ) ;
2016-03-06 10:22:45 +00:00
StoreRecentFile ( filename ) ; // Put the filename on top of the list
2016-01-07 19:33:54 +00:00
if ( ! InitializeSystem ( ) )
return ;
if ( ! LoadROM ( filename ) )
return ;
2014-04-04 01:24:07 +00:00
2015-04-29 04:01:41 +00:00
// Create and start the emulation thread
emu_thread = Common : : make_unique < EmuThread > ( render_window ) ;
2015-04-30 23:46:50 +00:00
emit EmulationStarting ( emu_thread . get ( ) ) ;
2015-05-19 04:24:43 +00:00
render_window - > moveContext ( ) ;
2015-04-16 22:35:09 +00:00
emu_thread - > start ( ) ;
2014-04-22 03:15:17 +00:00
2015-09-05 10:29:44 +00:00
connect ( render_window , SIGNAL ( Closed ( ) ) , this , SLOT ( OnStopGame ( ) ) ) ;
2015-04-29 04:01:41 +00:00
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues
connect ( emu_thread . get ( ) , SIGNAL ( DebugModeEntered ( ) ) , disasmWidget , SLOT ( OnDebugModeEntered ( ) ) , Qt : : BlockingQueuedConnection ) ;
connect ( emu_thread . get ( ) , SIGNAL ( DebugModeEntered ( ) ) , registersWidget , SLOT ( OnDebugModeEntered ( ) ) , Qt : : BlockingQueuedConnection ) ;
connect ( emu_thread . get ( ) , SIGNAL ( DebugModeEntered ( ) ) , callstackWidget , SLOT ( OnDebugModeEntered ( ) ) , Qt : : BlockingQueuedConnection ) ;
connect ( emu_thread . get ( ) , SIGNAL ( DebugModeLeft ( ) ) , disasmWidget , SLOT ( OnDebugModeLeft ( ) ) , Qt : : BlockingQueuedConnection ) ;
connect ( emu_thread . get ( ) , SIGNAL ( DebugModeLeft ( ) ) , registersWidget , SLOT ( OnDebugModeLeft ( ) ) , Qt : : BlockingQueuedConnection ) ;
connect ( emu_thread . get ( ) , SIGNAL ( DebugModeLeft ( ) ) , callstackWidget , SLOT ( OnDebugModeLeft ( ) ) , Qt : : BlockingQueuedConnection ) ;
// Update the GUI
registersWidget - > OnDebugModeEntered ( ) ;
callstackWidget - > OnDebugModeEntered ( ) ;
2015-09-01 04:35:33 +00:00
if ( ui . action_Single_Window_Mode - > isChecked ( ) ) {
game_list - > hide ( ) ;
}
2014-04-22 03:15:17 +00:00
render_window - > show ( ) ;
2015-04-29 04:01:41 +00:00
2015-09-01 01:30:06 +00:00
emulation_running = true ;
2014-10-31 05:44:51 +00:00
OnStartGame ( ) ;
2014-04-01 02:26:50 +00:00
}
2015-04-28 03:13:57 +00:00
void GMainWindow : : ShutdownGame ( ) {
2015-04-30 23:46:50 +00:00
emu_thread - > RequestStop ( ) ;
2015-04-28 03:13:57 +00:00
2015-04-28 23:03:01 +00:00
// Release emu threads from any breakpoints
2015-04-30 23:46:50 +00:00
// This belongs after RequestStop() and before wait() because if emulation stops on a GPU
// breakpoint after (or before) RequestStop() is called, the emulation would never be able
2015-04-29 04:01:41 +00:00
// to continue out to the main loop and terminate. Thus wait() would hang forever.
// TODO(bunnei): This function is not thread safe, but it's being used as if it were
2015-04-28 23:03:01 +00:00
Pica : : g_debug_context - > ClearBreakpoints ( ) ;
2015-04-30 23:46:50 +00:00
emit EmulationStopping ( ) ;
2015-04-29 04:01:41 +00:00
// Wait for emulation thread to complete and delete it
emu_thread - > wait ( ) ;
emu_thread = nullptr ;
2015-09-05 10:29:44 +00:00
// The emulation is stopped, so closing the window or not does not matter anymore
disconnect ( render_window , SIGNAL ( Closed ( ) ) , this , SLOT ( OnStopGame ( ) ) ) ;
2015-04-28 23:03:01 +00:00
// Update the GUI
2015-04-30 23:58:26 +00:00
ui . action_Start - > setEnabled ( false ) ;
2015-07-26 14:38:51 +00:00
ui . action_Start - > setText ( tr ( " Start " ) ) ;
2015-04-28 03:13:57 +00:00
ui . action_Pause - > setEnabled ( false ) ;
ui . action_Stop - > setEnabled ( false ) ;
render_window - > hide ( ) ;
2015-09-01 04:35:33 +00:00
game_list - > show ( ) ;
2015-09-01 01:30:06 +00:00
emulation_running = false ;
2015-04-28 03:13:57 +00:00
}
2016-03-06 10:22:45 +00:00
void GMainWindow : : StoreRecentFile ( const std : : string & filename )
2015-08-17 20:50:52 +00:00
{
QSettings settings ;
QStringList recent_files = settings . value ( " recentFiles " ) . toStringList ( ) ;
2016-03-06 10:22:45 +00:00
recent_files . prepend ( QString : : fromStdString ( filename ) ) ;
2015-08-17 20:50:52 +00:00
recent_files . removeDuplicates ( ) ;
2015-09-08 01:00:08 +00:00
while ( recent_files . size ( ) > max_recent_files_item ) {
recent_files . removeLast ( ) ;
}
2015-08-17 20:50:52 +00:00
settings . setValue ( " recentFiles " , recent_files ) ;
UpdateRecentFiles ( ) ;
}
2015-07-28 16:43:18 +00:00
void GMainWindow : : UpdateRecentFiles ( ) {
QSettings settings ;
QStringList recent_files = settings . value ( " recentFiles " ) . toStringList ( ) ;
unsigned int num_recent_files = std : : min ( recent_files . size ( ) , static_cast < int > ( max_recent_files_item ) ) ;
for ( unsigned int i = 0 ; i < num_recent_files ; i + + ) {
QString text = QString ( " &%1. %2 " ) . arg ( i + 1 ) . arg ( QFileInfo ( recent_files [ i ] ) . fileName ( ) ) ;
actions_recent_files [ i ] - > setText ( text ) ;
actions_recent_files [ i ] - > setData ( recent_files [ i ] ) ;
2015-08-17 20:50:52 +00:00
actions_recent_files [ i ] - > setToolTip ( recent_files [ i ] ) ;
2015-07-28 16:43:18 +00:00
actions_recent_files [ i ] - > setVisible ( true ) ;
}
for ( int j = num_recent_files ; j < max_recent_files_item ; + + j ) {
actions_recent_files [ j ] - > setVisible ( false ) ;
}
// Grey out the recent files menu if the list is empty
if ( num_recent_files = = 0 ) {
ui . menu_recent_files - > setEnabled ( false ) ;
} else {
ui . menu_recent_files - > setEnabled ( true ) ;
}
}
2015-09-01 04:35:33 +00:00
void GMainWindow : : OnGameListLoadFile ( QString game_path ) {
2015-10-20 17:58:23 +00:00
BootGame ( game_path . toLocal8Bit ( ) . data ( ) ) ;
2015-09-01 04:35:33 +00:00
}
2015-07-28 16:43:18 +00:00
void GMainWindow : : OnMenuLoadFile ( ) {
2015-07-26 15:13:02 +00:00
QSettings settings ;
QString rom_path = settings . value ( " romsPath " , QString ( ) ) . toString ( ) ;
QString filename = QFileDialog : : getOpenFileName ( this , tr ( " Load File " ) , rom_path , tr ( " 3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi) " ) ) ;
2015-09-01 04:35:33 +00:00
if ( ! filename . isEmpty ( ) ) {
2015-07-26 15:13:02 +00:00
settings . setValue ( " romsPath " , QFileInfo ( filename ) . path ( ) ) ;
2015-04-28 23:03:01 +00:00
2015-10-20 17:58:23 +00:00
BootGame ( filename . toLocal8Bit ( ) . data ( ) ) ;
2015-04-28 23:03:01 +00:00
}
2014-04-01 02:26:50 +00:00
}
2014-05-01 03:46:57 +00:00
void GMainWindow : : OnMenuLoadSymbolMap ( ) {
2015-07-26 15:13:02 +00:00
QSettings settings ;
QString symbol_path = settings . value ( " symbolsPath " , QString ( ) ) . toString ( ) ;
QString filename = QFileDialog : : getOpenFileName ( this , tr ( " Load Symbol Map " ) , symbol_path , tr ( " Symbol map (*) " ) ) ;
2015-09-01 04:35:33 +00:00
if ( ! filename . isEmpty ( ) ) {
2015-07-26 15:13:02 +00:00
settings . setValue ( " symbolsPath " , QFileInfo ( filename ) . path ( ) ) ;
2015-10-20 17:58:23 +00:00
LoadSymbolMap ( filename . toLocal8Bit ( ) . data ( ) ) ;
2015-07-26 15:13:02 +00:00
}
2014-05-01 03:46:57 +00:00
}
2015-09-06 22:33:57 +00:00
void GMainWindow : : OnMenuSelectGameListRoot ( ) {
QSettings settings ;
QString dir_path = QFileDialog : : getExistingDirectory ( this , tr ( " Select Directory " ) ) ;
if ( ! dir_path . isEmpty ( ) ) {
settings . setValue ( " gameListRootDir " , dir_path ) ;
game_list - > PopulateAsync ( dir_path , settings . value ( " gameListDeepScan " ) . toBool ( ) ) ;
}
}
2015-07-28 16:43:18 +00:00
void GMainWindow : : OnMenuRecentFile ( ) {
QAction * action = qobject_cast < QAction * > ( sender ( ) ) ;
assert ( action ) ;
QString filename = action - > data ( ) . toString ( ) ;
QFileInfo file_info ( filename ) ;
if ( file_info . exists ( ) ) {
2015-10-20 17:58:23 +00:00
BootGame ( filename . toLocal8Bit ( ) . data ( ) ) ;
2015-07-28 16:43:18 +00:00
} else {
// Display an error message and remove the file from the list.
QMessageBox : : information ( this , tr ( " File not found " ) , tr ( " File \" %1 \" not found " ) . arg ( filename ) ) ;
QSettings settings ;
QStringList recent_files = settings . value ( " recentFiles " ) . toStringList ( ) ;
recent_files . removeOne ( filename ) ;
settings . setValue ( " recentFiles " , recent_files ) ;
2015-08-17 20:50:52 +00:00
UpdateRecentFiles ( ) ;
2015-07-28 16:43:18 +00:00
}
}
void GMainWindow : : OnStartGame ( ) {
2015-04-28 23:03:01 +00:00
emu_thread - > SetRunning ( true ) ;
2014-04-04 01:24:07 +00:00
ui . action_Start - > setEnabled ( false ) ;
2015-07-26 14:38:51 +00:00
ui . action_Start - > setText ( tr ( " Continue " ) ) ;
2014-04-04 01:24:07 +00:00
ui . action_Pause - > setEnabled ( true ) ;
ui . action_Stop - > setEnabled ( true ) ;
2014-04-01 02:26:50 +00:00
}
2015-07-28 16:43:18 +00:00
void GMainWindow : : OnPauseGame ( ) {
2015-04-28 23:03:01 +00:00
emu_thread - > SetRunning ( false ) ;
2014-04-04 01:24:07 +00:00
ui . action_Start - > setEnabled ( true ) ;
ui . action_Pause - > setEnabled ( false ) ;
ui . action_Stop - > setEnabled ( true ) ;
2014-04-01 02:26:50 +00:00
}
2015-04-17 03:31:14 +00:00
void GMainWindow : : OnStopGame ( ) {
2015-04-28 03:13:57 +00:00
ShutdownGame ( ) ;
2014-04-01 02:26:50 +00:00
}
2015-04-16 22:35:09 +00:00
void GMainWindow : : ToggleWindowMode ( ) {
if ( ui . action_Single_Window_Mode - > isChecked ( ) ) {
// Render in the main window...
2014-04-22 03:15:17 +00:00
render_window - > BackupGeometry ( ) ;
ui . horizontalLayout - > addWidget ( render_window ) ;
2014-12-26 18:42:27 +00:00
render_window - > setFocusPolicy ( Qt : : ClickFocus ) ;
2015-09-01 01:30:06 +00:00
if ( emulation_running ) {
render_window - > setVisible ( true ) ;
render_window - > setFocus ( ) ;
2015-10-06 19:20:26 +00:00
game_list - > hide ( ) ;
2015-09-01 01:30:06 +00:00
}
2015-04-16 22:35:09 +00:00
} else {
// Render in a separate window...
ui . horizontalLayout - > removeWidget ( render_window ) ;
render_window - > setParent ( nullptr ) ;
render_window - > setFocusPolicy ( Qt : : NoFocus ) ;
2015-09-01 01:30:06 +00:00
if ( emulation_running ) {
render_window - > setVisible ( true ) ;
render_window - > RestoreGeometry ( ) ;
2015-09-01 04:35:33 +00:00
game_list - > show ( ) ;
2015-09-01 01:30:06 +00:00
}
2014-04-01 02:26:50 +00:00
}
}
2015-07-28 16:43:18 +00:00
void GMainWindow : : OnConfigure ( ) {
2016-01-24 17:34:05 +00:00
ConfigureDialog configureDialog ( this ) ;
auto result = configureDialog . exec ( ) ;
if ( result = = QDialog : : Accepted )
{
configureDialog . applyConfiguration ( ) ;
}
2014-04-01 02:26:50 +00:00
}
2016-01-10 12:31:20 +00:00
bool GMainWindow : : ConfirmClose ( ) {
2016-01-13 17:40:41 +00:00
if ( emu_thread = = nullptr | | ! confirm_before_closing )
return true ;
2016-01-10 12:31:20 +00:00
2016-01-13 17:40:41 +00:00
auto answer = QMessageBox : : question ( this , tr ( " Citra " ) ,
tr ( " Are you sure you want to close Citra? " ) ,
QMessageBox : : Yes | QMessageBox : : No , QMessageBox : : No ) ;
return answer ! = QMessageBox : : No ;
2016-01-10 12:31:20 +00:00
}
2015-07-28 16:43:18 +00:00
void GMainWindow : : closeEvent ( QCloseEvent * event ) {
2016-01-10 12:31:20 +00:00
if ( ! ConfirmClose ( ) ) {
event - > ignore ( ) ;
return ;
}
2014-04-01 02:26:50 +00:00
// Save window layout
QSettings settings ( QSettings : : IniFormat , QSettings : : UserScope , " Citra team " , " Citra " ) ;
2015-09-08 01:11:21 +00:00
settings . beginGroup ( " UILayout " ) ;
2014-04-01 02:26:50 +00:00
settings . setValue ( " geometry " , saveGeometry ( ) ) ;
settings . setValue ( " state " , saveState ( ) ) ;
settings . setValue ( " geometryRenderWindow " , render_window - > saveGeometry ( ) ) ;
2015-08-17 21:25:21 +00:00
settings . setValue ( " microProfileDialogGeometry " , microProfileDialog - > saveGeometry ( ) ) ;
settings . setValue ( " microProfileDialogVisible " , microProfileDialog - > isVisible ( ) ) ;
2015-09-08 01:11:21 +00:00
settings . endGroup ( ) ;
2014-12-31 21:26:11 +00:00
settings . setValue ( " singleWindowMode " , ui . action_Single_Window_Mode - > isChecked ( ) ) ;
2015-01-06 15:09:30 +00:00
settings . setValue ( " displayTitleBars " , ui . actionDisplay_widget_title_bars - > isChecked ( ) ) ;
2014-04-01 02:26:50 +00:00
settings . setValue ( " firstStart " , false ) ;
2016-01-13 17:40:41 +00:00
settings . setValue ( " confirmClose " , confirm_before_closing ) ;
2015-09-07 06:51:57 +00:00
game_list - > SaveInterfaceLayout ( settings ) ;
2014-04-01 02:26:50 +00:00
SaveHotkeys ( settings ) ;
2015-05-13 03:14:24 +00:00
// Shutdown session if the emu thread is active...
if ( emu_thread ! = nullptr )
ShutdownGame ( ) ;
2015-05-01 20:53:16 +00:00
2014-04-01 02:26:50 +00:00
render_window - > close ( ) ;
QWidget : : closeEvent ( event ) ;
}
# ifdef main
# undef main
# endif
2015-07-28 16:43:18 +00:00
int main ( int argc , char * argv [ ] ) {
2014-12-06 22:00:08 +00:00
Log : : Filter log_filter ( Log : : Level : : Info ) ;
2015-03-06 18:15:02 +00:00
Log : : SetFilter ( & log_filter ) ;
2014-10-28 07:36:00 +00:00
2015-08-17 21:25:21 +00:00
MicroProfileOnThreadCreate ( " Frontend " ) ;
SCOPE_EXIT ( {
MicroProfileShutdown ( ) ;
} ) ;
2015-07-26 15:13:02 +00:00
// Init settings params
QSettings : : setDefaultFormat ( QSettings : : IniFormat ) ;
QCoreApplication : : setOrganizationName ( " Citra team " ) ;
QCoreApplication : : setApplicationName ( " Citra " ) ;
2014-04-01 02:26:50 +00:00
QApplication : : setAttribute ( Qt : : AA_X11InitThreads ) ;
QApplication app ( argc , argv ) ;
2014-12-06 22:00:08 +00:00
2016-03-06 13:04:47 +00:00
// Qt changes the locale and causes issues in float conversion using std::to_string() when generating shaders
setlocale ( LC_ALL , " C " ) ;
2014-04-01 02:26:50 +00:00
GMainWindow main_window ;
2014-12-06 22:00:08 +00:00
// After settings have been loaded by GMainWindow, apply the filter
log_filter . ParseFilterString ( Settings : : values . log_filter ) ;
2014-04-01 02:26:50 +00:00
main_window . show ( ) ;
return app . exec ( ) ;
}