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