2015-01-04 23:06:57 +05:30
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
2016-03-06 18:34:47 +05:30
# include <clocale>
2016-04-05 17:59:55 +05:30
# include <memory>
2014-10-28 13:06:00 +05:30
# include <thread>
2016-03-19 07:01:01 +05:30
# include <glad/glad.h>
# define QT_NO_OPENGL
2014-04-01 07:56:50 +05:30
# include <QDesktopWidget>
2015-09-11 09:53:00 +05:30
# include <QtGui>
2014-04-01 07:56:50 +05:30
# include <QFileDialog>
2015-07-28 22:13:18 +05:30
# include <QMessageBox>
2014-04-01 07:56:50 +05:30
# include "qhexedit.h"
2015-09-11 09:53:00 +05:30
# include "citra_qt/bootmanager.h"
# include "citra_qt/config.h"
2016-01-24 23:04:05 +05:30
# include "citra_qt/configure_dialog.h"
2015-09-01 10:05:33 +05:30
# include "citra_qt/game_list.h"
2015-09-11 09:53:00 +05:30
# include "citra_qt/hotkeys.h"
# include "citra_qt/main.h"
2016-01-25 01:53:55 +05:30
# include "citra_qt/ui_settings.h"
2015-09-11 09:53:00 +05:30
// 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-08-18 02:55:21 +05:30
# include "common/microprofile.h"
2014-10-28 13:06:00 +05:30
# include "common/platform.h"
2015-06-21 19:28:59 +05:30
# include "common/scm_rev.h"
2014-10-28 13:06:00 +05:30
# include "common/scope_exit.h"
2015-09-11 09:53:00 +05:30
# 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 13:06:00 +05:30
2015-09-11 09:53:00 +05:30
# include "core/core.h"
2014-10-28 02:48:28 +05:30
# include "core/settings.h"
2014-04-11 06:20:10 +05:30
# include "core/system.h"
2014-05-01 09:16:57 +05:30
# include "core/arm/disassembler/load_symbol_map.h"
2015-10-22 07:49:55 +05:30
# include "core/gdbstub/gdbstub.h"
2015-09-11 09:53:00 +05:30
# include "core/loader/loader.h"
2014-06-17 03:33:13 +05:30
2015-05-19 09:51:33 +05:30
# include "video_core/video_core.h"
2016-01-25 01:53:55 +05:30
GMainWindow : : GMainWindow ( ) : config ( new Config ( ) ) , emu_thread ( nullptr )
2014-04-01 07:56:50 +05:30
{
2014-10-25 21:32:26 +05:30
Pica : : g_debug_context = Pica : : DebugContext : : Construct ( ) ;
2014-04-01 07:56:50 +05:30
ui . setupUi ( this ) ;
statusBar ( ) - > hide ( ) ;
2015-04-29 09:31:41 +05:30
render_window = new GRenderWindow ( this , emu_thread . get ( ) ) ;
2014-04-22 08:45:17 +05:30
render_window - > hide ( ) ;
2014-04-01 07:56:50 +05:30
2015-09-01 10:05:33 +05:30
game_list = new GameList ( ) ;
ui . horizontalLayout - > addWidget ( game_list ) ;
2015-02-05 22:23:25 +05:30
profilerWidget = new ProfilerWidget ( this ) ;
addDockWidget ( Qt : : BottomDockWidgetArea , profilerWidget ) ;
profilerWidget - > hide ( ) ;
2016-04-29 05:47:31 +05:30
# if MICROPROFILE_ENABLED
2015-08-18 02:55:21 +05:30
microProfileDialog = new MicroProfileDialog ( this ) ;
microProfileDialog - > hide ( ) ;
2016-04-29 05:47:31 +05:30
# endif
2015-08-18 02:55:21 +05:30
2015-04-29 09:31:41 +05:30
disasmWidget = new DisassemblerWidget ( this , emu_thread . get ( ) ) ;
2014-04-19 04:00:53 +05:30
addDockWidget ( Qt : : BottomDockWidgetArea , disasmWidget ) ;
disasmWidget - > hide ( ) ;
2014-04-01 07:56:50 +05:30
2014-04-19 04:00:53 +05:30
registersWidget = new RegistersWidget ( this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , registersWidget ) ;
registersWidget - > hide ( ) ;
callstackWidget = new CallstackWidget ( this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , callstackWidget ) ;
callstackWidget - > hide ( ) ;
2014-04-01 07:56:50 +05:30
2014-05-18 02:08:10 +05:30
graphicsWidget = new GPUCommandStreamWidget ( this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , graphicsWidget ) ;
2014-08-14 22:51:55 +05:30
graphicsWidget - > hide ( ) ;
2014-05-18 02:08:10 +05:30
2014-05-18 21:22:22 +05:30
graphicsCommandsWidget = new GPUCommandListWidget ( this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , graphicsCommandsWidget ) ;
2014-08-14 22:51:55 +05:30
graphicsCommandsWidget - > hide ( ) ;
2014-05-18 21:22:22 +05:30
2014-10-25 23:58:24 +05:30
auto graphicsBreakpointsWidget = new GraphicsBreakPointsWidget ( Pica : : g_debug_context , this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , graphicsBreakpointsWidget ) ;
graphicsBreakpointsWidget - > hide ( ) ;
2014-10-26 21:08:40 +05:30
auto graphicsFramebufferWidget = new GraphicsFramebufferWidget ( Pica : : g_debug_context , this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , graphicsFramebufferWidget ) ;
graphicsFramebufferWidget - > hide ( ) ;
2014-12-10 23:54:56 +05:30
auto graphicsVertexShaderWidget = new GraphicsVertexShaderWidget ( Pica : : g_debug_context , this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , graphicsVertexShaderWidget ) ;
graphicsVertexShaderWidget - > hide ( ) ;
2015-04-04 16:27:31 +05:30
auto graphicsTracingWidget = new GraphicsTracingWidget ( Pica : : g_debug_context , this ) ;
addDockWidget ( Qt : : RightDockWidgetArea , graphicsTracingWidget ) ;
graphicsTracingWidget - > hide ( ) ;
2014-04-01 07:56:50 +05:30
QMenu * debug_menu = ui . menu_View - > addMenu ( tr ( " Debugging " ) ) ;
2015-02-05 22:23:25 +05:30
debug_menu - > addAction ( profilerWidget - > toggleViewAction ( ) ) ;
2016-04-29 05:47:31 +05:30
# if MICROPROFILE_ENABLED
2015-08-18 02:55:21 +05:30
debug_menu - > addAction ( microProfileDialog - > toggleViewAction ( ) ) ;
2016-04-29 05:47:31 +05:30
# endif
2014-04-19 04:00:53 +05:30
debug_menu - > addAction ( disasmWidget - > toggleViewAction ( ) ) ;
debug_menu - > addAction ( registersWidget - > toggleViewAction ( ) ) ;
debug_menu - > addAction ( callstackWidget - > toggleViewAction ( ) ) ;
2014-05-18 02:08:10 +05:30
debug_menu - > addAction ( graphicsWidget - > toggleViewAction ( ) ) ;
2014-05-18 21:22:22 +05:30
debug_menu - > addAction ( graphicsCommandsWidget - > toggleViewAction ( ) ) ;
2014-10-25 23:58:24 +05:30
debug_menu - > addAction ( graphicsBreakpointsWidget - > toggleViewAction ( ) ) ;
2014-10-26 21:08:40 +05:30
debug_menu - > addAction ( graphicsFramebufferWidget - > toggleViewAction ( ) ) ;
2014-12-10 23:54:56 +05:30
debug_menu - > addAction ( graphicsVertexShaderWidget - > toggleViewAction ( ) ) ;
2015-04-04 16:27:31 +05:30
debug_menu - > addAction ( graphicsTracingWidget - > toggleViewAction ( ) ) ;
2014-04-01 07:56:50 +05:30
// 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
2016-01-25 01:53:55 +05:30
restoreGeometry ( UISettings : : values . geometry ) ;
restoreState ( UISettings : : values . state ) ;
render_window - > restoreGeometry ( UISettings : : values . renderwindow_geometry ) ;
2016-04-29 05:47:31 +05:30
# if MICROPROFILE_ENABLED
2016-01-25 01:53:55 +05:30
microProfileDialog - > restoreGeometry ( UISettings : : values . microprofile_geometry ) ;
microProfileDialog - > setVisible ( UISettings : : values . microprofile_visible ) ;
2016-04-29 05:47:31 +05:30
# endif
2015-09-07 12:21:57 +05:30
2016-01-25 01:53:55 +05:30
game_list - > LoadInterfaceLayout ( ) ;
2015-09-02 18:26:38 +05:30
2016-01-25 01:53:55 +05:30
ui . action_Single_Window_Mode - > setChecked ( UISettings : : values . single_window_mode ) ;
2014-04-22 08:45:17 +05:30
ToggleWindowMode ( ) ;
2014-04-01 07:56:50 +05:30
2016-01-25 01:53:55 +05:30
ui . actionDisplay_widget_title_bars - > setChecked ( UISettings : : values . display_titlebar ) ;
2015-01-06 20:39:30 +05:30
OnDisplayTitleBars ( ui . actionDisplay_widget_title_bars - > isChecked ( ) ) ;
2015-07-28 22:13:18 +05:30
// 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 ( ) ;
2014-04-01 07:56:50 +05:30
// Setup connections
2016-01-25 01:53:55 +05:30
connect ( game_list , SIGNAL ( GameChosen ( QString ) ) , this , SLOT ( OnGameListLoadFile ( QString ) ) , Qt : : DirectConnection ) ;
2016-01-24 23:04:05 +05:30
connect ( ui . action_Configure , SIGNAL ( triggered ( ) ) , this , SLOT ( OnConfigure ( ) ) ) ;
2016-01-25 01:53:55 +05:30
connect ( ui . action_Load_File , SIGNAL ( triggered ( ) ) , this , SLOT ( OnMenuLoadFile ( ) ) , Qt : : DirectConnection ) ;
2014-05-01 09:16:57 +05:30
connect ( ui . action_Load_Symbol_Map , SIGNAL ( triggered ( ) ) , this , SLOT ( OnMenuLoadSymbolMap ( ) ) ) ;
2015-09-07 04:03:57 +05:30
connect ( ui . action_Select_Game_List_Root , SIGNAL ( triggered ( ) ) , this , SLOT ( OnMenuSelectGameListRoot ( ) ) ) ;
2014-04-22 08:45:17 +05:30
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 ( ) ) ) ;
2015-01-01 02:56:11 +05:30
connect ( ui . action_Single_Window_Mode , SIGNAL ( triggered ( bool ) ) , this , SLOT ( ToggleWindowMode ( ) ) ) ;
2014-04-01 07:56:50 +05:30
2015-05-01 05:16:50 +05:30
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 05:42:59 +05:30
connect ( this , SIGNAL ( EmulationStarting ( EmuThread * ) ) , graphicsTracingWidget , SLOT ( OnEmulationStarting ( EmuThread * ) ) ) ;
connect ( this , SIGNAL ( EmulationStopping ( ) ) , graphicsTracingWidget , SLOT ( OnEmulationStopping ( ) ) ) ;
2014-04-01 07:56:50 +05:30
// Setup hotkeys
2014-04-22 08:45:17 +05:30
RegisterHotkey ( " Main Window " , " Load File " , QKeySequence : : Open ) ;
2014-04-01 07:56:50 +05:30
RegisterHotkey ( " Main Window " , " Start Emulation " ) ;
2016-01-25 01:53:55 +05:30
LoadHotkeys ( ) ;
2014-04-01 07:56:50 +05:30
2014-04-22 08:45:17 +05:30
connect ( GetHotkey ( " Main Window " , " Load File " , this ) , SIGNAL ( activated ( ) ) , this , SLOT ( OnMenuLoadFile ( ) ) ) ;
2014-04-01 07:56:50 +05:30
connect ( GetHotkey ( " Main Window " , " Start Emulation " , this ) , SIGNAL ( activated ( ) ) , this , SLOT ( OnStartGame ( ) ) ) ;
2014-11-13 22:47:39 +05:30
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 08:19:55 +05:30
show ( ) ;
2016-01-25 02:24:04 +05:30
game_list - > PopulateAsync ( UISettings : : values . gamedir , UISettings : : values . gamedir_deepscan ) ;
2015-09-01 10:05:33 +05:30
2014-10-31 11:14:51 +05:30
QStringList args = QApplication : : arguments ( ) ;
if ( args . length ( ) > = 2 ) {
BootGame ( args [ 1 ] . toStdString ( ) ) ;
}
2014-04-01 07:56:50 +05:30
}
GMainWindow : : ~ GMainWindow ( )
{
// will get automatically deleted otherwise
2014-12-04 00:27:57 +05:30
if ( render_window - > parent ( ) = = nullptr )
2014-04-01 07:56:50 +05:30
delete render_window ;
2014-10-25 21:32:26 +05:30
Pica : : g_debug_context . reset ( ) ;
2014-04-01 07:56:50 +05:30
}
2015-01-06 20:39:30 +05:30
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-08 01:03:54 +05:30
bool GMainWindow : : InitializeSystem ( ) {
2015-07-28 22:13:18 +05:30
// Shutdown previous session if the emu thread is still active...
if ( emu_thread ! = nullptr )
ShutdownGame ( ) ;
2016-03-19 07:01:01 +05:30
render_window - > MakeCurrent ( ) ;
if ( ! gladLoadGL ( ) ) {
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. " ) ) ;
return false ;
}
2015-04-29 09:31:41 +05:30
// Initialize the core emulation
2016-01-08 01:03:54 +05:30
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 06:54:07 +05:30
2016-01-08 01:03:54 +05:30
bool GMainWindow : : LoadROM ( const std : : string & filename ) {
2016-05-18 03:36:33 +05:30
std : : unique_ptr < Loader : : AppLoader > app_loader = Loader : : GetFileLoader ( filename ) ;
if ( ! app_loader ) {
LOG_CRITICAL ( Frontend , " Failed to obtain loader for %s! " , filename . c_str ( ) ) ;
QMessageBox : : critical ( this , tr ( " Error while loading ROM! " ) ,
tr ( " The ROM format is not supported. " ) ) ;
return false ;
}
Loader : : ResultStatus result = app_loader - > Load ( ) ;
2016-01-07 23:06:10 +05:30
if ( Loader : : ResultStatus : : Success ! = result ) {
2014-12-06 07:23:49 +05:30
LOG_CRITICAL ( Frontend , " Failed to load ROM! " ) ;
2015-04-29 09:31:41 +05:30
System : : Shutdown ( ) ;
2016-01-07 23:06:10 +05:30
switch ( result ) {
case Loader : : ResultStatus : : ErrorEncrypted : {
// Build the MessageBox ourselves to have clickable link
QMessageBox popup_error ;
popup_error . setTextFormat ( Qt : : RichText ) ;
2016-01-08 01:03:54 +05:30
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 23:06:10 +05:30
popup_error . setIcon ( QMessageBox : : Critical ) ;
popup_error . exec ( ) ;
break ;
}
case Loader : : ResultStatus : : ErrorInvalidFormat :
2016-01-08 01:03:54 +05:30
QMessageBox : : critical ( this , tr ( " Error while loading ROM! " ) ,
2016-01-07 23:06:10 +05:30
tr ( " The ROM format is not supported. " ) ) ;
break ;
case Loader : : ResultStatus : : Error :
default :
2016-01-08 01:03:54 +05:30
QMessageBox : : critical ( this , tr ( " Error while loading ROM! " ) ,
tr ( " Unknown error! " ) ) ;
2016-01-07 23:06:10 +05:30
break ;
}
2016-01-08 01:03:54 +05:30
return false ;
2014-04-04 06:54:07 +05:30
}
2016-01-08 01:03:54 +05:30
return true ;
}
void GMainWindow : : BootGame ( const std : : string & filename ) {
LOG_INFO ( Frontend , " Citra starting... " ) ;
2016-03-06 15:52:45 +05:30
StoreRecentFile ( filename ) ; // Put the filename on top of the list
2016-01-08 01:03:54 +05:30
if ( ! InitializeSystem ( ) )
return ;
if ( ! LoadROM ( filename ) )
return ;
2014-04-04 06:54:07 +05:30
2015-04-29 09:31:41 +05:30
// Create and start the emulation thread
2016-04-05 17:59:55 +05:30
emu_thread = std : : make_unique < EmuThread > ( render_window ) ;
2015-05-01 05:16:50 +05:30
emit EmulationStarting ( emu_thread . get ( ) ) ;
2015-05-19 09:54:43 +05:30
render_window - > moveContext ( ) ;
2015-04-17 04:05:09 +05:30
emu_thread - > start ( ) ;
2014-04-22 08:45:17 +05:30
2015-09-05 15:59:44 +05:30
connect ( render_window , SIGNAL ( Closed ( ) ) , this , SLOT ( OnStopGame ( ) ) ) ;
2015-04-29 09:31:41 +05:30
// 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 10:05:33 +05:30
if ( ui . action_Single_Window_Mode - > isChecked ( ) ) {
game_list - > hide ( ) ;
}
2014-04-22 08:45:17 +05:30
render_window - > show ( ) ;
2015-04-29 09:31:41 +05:30
2015-09-01 07:00:06 +05:30
emulation_running = true ;
2014-10-31 11:14:51 +05:30
OnStartGame ( ) ;
2014-04-01 07:56:50 +05:30
}
2015-04-28 08:43:57 +05:30
void GMainWindow : : ShutdownGame ( ) {
2015-05-01 05:16:50 +05:30
emu_thread - > RequestStop ( ) ;
2015-04-28 08:43:57 +05:30
2015-04-29 04:33:01 +05:30
// Release emu threads from any breakpoints
2015-05-01 05:16:50 +05:30
// 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 09:31:41 +05:30
// 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-29 04:33:01 +05:30
Pica : : g_debug_context - > ClearBreakpoints ( ) ;
2015-05-01 05:16:50 +05:30
emit EmulationStopping ( ) ;
2015-04-29 09:31:41 +05:30
// Wait for emulation thread to complete and delete it
emu_thread - > wait ( ) ;
emu_thread = nullptr ;
2015-09-05 15:59:44 +05:30
// The emulation is stopped, so closing the window or not does not matter anymore
disconnect ( render_window , SIGNAL ( Closed ( ) ) , this , SLOT ( OnStopGame ( ) ) ) ;
2015-04-29 04:33:01 +05:30
// Update the GUI
2015-05-01 05:28:26 +05:30
ui . action_Start - > setEnabled ( false ) ;
2015-07-26 20:08:51 +05:30
ui . action_Start - > setText ( tr ( " Start " ) ) ;
2015-04-28 08:43:57 +05:30
ui . action_Pause - > setEnabled ( false ) ;
ui . action_Stop - > setEnabled ( false ) ;
render_window - > hide ( ) ;
2015-09-01 10:05:33 +05:30
game_list - > show ( ) ;
2015-09-01 07:00:06 +05:30
emulation_running = false ;
2015-04-28 08:43:57 +05:30
}
2016-01-25 01:53:55 +05:30
void GMainWindow : : StoreRecentFile ( const std : : string & filename ) {
UISettings : : values . recent_files . prepend ( QString : : fromStdString ( filename ) ) ;
UISettings : : values . recent_files . removeDuplicates ( ) ;
while ( UISettings : : values . recent_files . size ( ) > max_recent_files_item ) {
UISettings : : values . recent_files . removeLast ( ) ;
2015-09-08 06:30:08 +05:30
}
2015-08-18 02:20:52 +05:30
UpdateRecentFiles ( ) ;
}
2015-07-28 22:13:18 +05:30
void GMainWindow : : UpdateRecentFiles ( ) {
2016-01-25 01:53:55 +05:30
unsigned int num_recent_files = std : : min ( UISettings : : values . recent_files . size ( ) , static_cast < int > ( max_recent_files_item ) ) ;
2015-07-28 22:13:18 +05:30
for ( unsigned int i = 0 ; i < num_recent_files ; i + + ) {
2016-01-25 01:53:55 +05:30
QString text = QString ( " &%1. %2 " ) . arg ( i + 1 ) . arg ( QFileInfo ( UISettings : : values . recent_files [ i ] ) . fileName ( ) ) ;
2015-07-28 22:13:18 +05:30
actions_recent_files [ i ] - > setText ( text ) ;
2016-01-25 01:53:55 +05:30
actions_recent_files [ i ] - > setData ( UISettings : : values . recent_files [ i ] ) ;
actions_recent_files [ i ] - > setToolTip ( UISettings : : values . recent_files [ i ] ) ;
2015-07-28 22:13:18 +05:30
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 10:05:33 +05:30
void GMainWindow : : OnGameListLoadFile ( QString game_path ) {
2016-03-31 16:28:37 +05:30
BootGame ( game_path . toStdString ( ) ) ;
2015-09-01 10:05:33 +05:30
}
2015-07-28 22:13:18 +05:30
void GMainWindow : : OnMenuLoadFile ( ) {
2016-01-25 01:53:55 +05:30
QString filename = QFileDialog : : getOpenFileName ( this , tr ( " Load File " ) , UISettings : : values . roms_path , tr ( " 3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi) " ) ) ;
2015-09-01 10:05:33 +05:30
if ( ! filename . isEmpty ( ) ) {
2016-01-25 01:53:55 +05:30
UISettings : : values . roms_path = QFileInfo ( filename ) . path ( ) ;
2015-04-29 04:33:01 +05:30
2016-03-31 16:28:37 +05:30
BootGame ( filename . toStdString ( ) ) ;
2015-04-29 04:33:01 +05:30
}
2014-04-01 07:56:50 +05:30
}
2014-05-01 09:16:57 +05:30
void GMainWindow : : OnMenuLoadSymbolMap ( ) {
2016-01-25 01:53:55 +05:30
QString filename = QFileDialog : : getOpenFileName ( this , tr ( " Load Symbol Map " ) , UISettings : : values . symbols_path , tr ( " Symbol map (*) " ) ) ;
2015-09-01 10:05:33 +05:30
if ( ! filename . isEmpty ( ) ) {
2016-01-25 01:53:55 +05:30
UISettings : : values . symbols_path = QFileInfo ( filename ) . path ( ) ;
2015-07-26 20:43:02 +05:30
2016-03-31 16:28:37 +05:30
LoadSymbolMap ( filename . toStdString ( ) ) ;
2015-07-26 20:43:02 +05:30
}
2014-05-01 09:16:57 +05:30
}
2015-09-07 04:03:57 +05:30
void GMainWindow : : OnMenuSelectGameListRoot ( ) {
QString dir_path = QFileDialog : : getExistingDirectory ( this , tr ( " Select Directory " ) ) ;
if ( ! dir_path . isEmpty ( ) ) {
2016-01-25 02:24:04 +05:30
UISettings : : values . gamedir = dir_path ;
2016-01-25 01:53:55 +05:30
game_list - > PopulateAsync ( dir_path , UISettings : : values . gamedir_deepscan ) ;
2015-09-07 04:03:57 +05:30
}
}
2015-07-28 22:13:18 +05:30
void GMainWindow : : OnMenuRecentFile ( ) {
QAction * action = qobject_cast < QAction * > ( sender ( ) ) ;
assert ( action ) ;
QString filename = action - > data ( ) . toString ( ) ;
QFileInfo file_info ( filename ) ;
if ( file_info . exists ( ) ) {
2016-03-31 16:28:37 +05:30
BootGame ( filename . toStdString ( ) ) ;
2015-07-28 22:13:18 +05:30
} 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 ) ) ;
2016-01-25 01:53:55 +05:30
UISettings : : values . recent_files . removeOne ( filename ) ;
2015-08-18 02:20:52 +05:30
UpdateRecentFiles ( ) ;
2015-07-28 22:13:18 +05:30
}
}
void GMainWindow : : OnStartGame ( ) {
2015-04-29 04:33:01 +05:30
emu_thread - > SetRunning ( true ) ;
2014-04-04 06:54:07 +05:30
ui . action_Start - > setEnabled ( false ) ;
2015-07-26 20:08:51 +05:30
ui . action_Start - > setText ( tr ( " Continue " ) ) ;
2014-04-04 06:54:07 +05:30
ui . action_Pause - > setEnabled ( true ) ;
ui . action_Stop - > setEnabled ( true ) ;
2014-04-01 07:56:50 +05:30
}
2015-07-28 22:13:18 +05:30
void GMainWindow : : OnPauseGame ( ) {
2015-04-29 04:33:01 +05:30
emu_thread - > SetRunning ( false ) ;
2014-04-04 06:54:07 +05:30
ui . action_Start - > setEnabled ( true ) ;
ui . action_Pause - > setEnabled ( false ) ;
ui . action_Stop - > setEnabled ( true ) ;
2014-04-01 07:56:50 +05:30
}
2015-04-17 09:01:14 +05:30
void GMainWindow : : OnStopGame ( ) {
2015-04-28 08:43:57 +05:30
ShutdownGame ( ) ;
2014-04-01 07:56:50 +05:30
}
2015-04-17 04:05:09 +05:30
void GMainWindow : : ToggleWindowMode ( ) {
if ( ui . action_Single_Window_Mode - > isChecked ( ) ) {
// Render in the main window...
2014-04-22 08:45:17 +05:30
render_window - > BackupGeometry ( ) ;
ui . horizontalLayout - > addWidget ( render_window ) ;
2014-12-27 00:12:27 +05:30
render_window - > setFocusPolicy ( Qt : : ClickFocus ) ;
2015-09-01 07:00:06 +05:30
if ( emulation_running ) {
render_window - > setVisible ( true ) ;
render_window - > setFocus ( ) ;
2015-10-07 00:50:26 +05:30
game_list - > hide ( ) ;
2015-09-01 07:00:06 +05:30
}
2015-04-17 04:05:09 +05:30
} else {
// Render in a separate window...
ui . horizontalLayout - > removeWidget ( render_window ) ;
render_window - > setParent ( nullptr ) ;
render_window - > setFocusPolicy ( Qt : : NoFocus ) ;
2015-09-01 07:00:06 +05:30
if ( emulation_running ) {
render_window - > setVisible ( true ) ;
render_window - > RestoreGeometry ( ) ;
2015-09-01 10:05:33 +05:30
game_list - > show ( ) ;
2015-09-01 07:00:06 +05:30
}
2014-04-01 07:56:50 +05:30
}
}
2015-07-28 22:13:18 +05:30
void GMainWindow : : OnConfigure ( ) {
2016-01-24 23:04:05 +05:30
ConfigureDialog configureDialog ( this ) ;
auto result = configureDialog . exec ( ) ;
2016-01-25 02:24:04 +05:30
if ( result = = QDialog : : Accepted )
2016-01-24 23:04:05 +05:30
{
configureDialog . applyConfiguration ( ) ;
2016-01-25 02:24:04 +05:30
config - > Save ( ) ;
2016-01-24 23:04:05 +05:30
}
2014-04-01 07:56:50 +05:30
}
2016-01-10 18:01:20 +05:30
bool GMainWindow : : ConfirmClose ( ) {
2016-01-25 02:24:04 +05:30
if ( emu_thread = = nullptr | | ! UISettings : : values . confirm_before_closing )
2016-01-13 23:10:41 +05:30
return true ;
2016-01-10 18:01:20 +05:30
2016-01-13 23:10:41 +05:30
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 18:01:20 +05:30
}
2015-07-28 22:13:18 +05:30
void GMainWindow : : closeEvent ( QCloseEvent * event ) {
2016-01-10 18:01:20 +05:30
if ( ! ConfirmClose ( ) ) {
event - > ignore ( ) ;
return ;
}
2016-01-25 01:53:55 +05:30
UISettings : : values . geometry = saveGeometry ( ) ;
UISettings : : values . state = saveState ( ) ;
UISettings : : values . renderwindow_geometry = render_window - > saveGeometry ( ) ;
2016-04-29 05:47:31 +05:30
# if MICROPROFILE_ENABLED
2016-01-25 01:53:55 +05:30
UISettings : : values . microprofile_geometry = microProfileDialog - > saveGeometry ( ) ;
UISettings : : values . microprofile_visible = microProfileDialog - > isVisible ( ) ;
2016-04-29 05:47:31 +05:30
# endif
2016-01-25 01:53:55 +05:30
UISettings : : values . single_window_mode = ui . action_Single_Window_Mode - > isChecked ( ) ;
UISettings : : values . display_titlebar = ui . actionDisplay_widget_title_bars - > isChecked ( ) ;
UISettings : : values . first_start = false ;
2015-09-08 06:41:21 +05:30
2016-01-25 01:53:55 +05:30
game_list - > SaveInterfaceLayout ( ) ;
SaveHotkeys ( ) ;
2014-04-01 07:56:50 +05:30
2015-05-13 08:44:24 +05:30
// Shutdown session if the emu thread is active...
if ( emu_thread ! = nullptr )
ShutdownGame ( ) ;
2015-05-02 02:23:16 +05:30
2014-04-01 07:56:50 +05:30
render_window - > close ( ) ;
QWidget : : closeEvent ( event ) ;
}
# ifdef main
# undef main
# endif
2015-07-28 22:13:18 +05:30
int main ( int argc , char * argv [ ] ) {
2014-12-07 03:30:08 +05:30
Log : : Filter log_filter ( Log : : Level : : Info ) ;
2015-03-06 23:45:02 +05:30
Log : : SetFilter ( & log_filter ) ;
2014-10-28 13:06:00 +05:30
2015-08-18 02:55:21 +05:30
MicroProfileOnThreadCreate ( " Frontend " ) ;
SCOPE_EXIT ( {
MicroProfileShutdown ( ) ;
} ) ;
2015-07-26 20:43:02 +05:30
// Init settings params
QCoreApplication : : setOrganizationName ( " Citra team " ) ;
QCoreApplication : : setApplicationName ( " Citra " ) ;
2014-04-01 07:56:50 +05:30
QApplication : : setAttribute ( Qt : : AA_X11InitThreads ) ;
QApplication app ( argc , argv ) ;
2014-12-07 03:30:08 +05:30
2016-03-06 18:34:47 +05:30
// Qt changes the locale and causes issues in float conversion using std::to_string() when generating shaders
setlocale ( LC_ALL , " C " ) ;
2014-04-01 07:56:50 +05:30
GMainWindow main_window ;
2014-12-07 03:30:08 +05:30
// After settings have been loaded by GMainWindow, apply the filter
log_filter . ParseFilterString ( Settings : : values . log_filter ) ;
2014-04-01 07:56:50 +05:30
main_window . show ( ) ;
return app . exec ( ) ;
}