summaryrefslogtreecommitdiff
path: root/src/citra_qt/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/citra_qt/main.cpp')
-rw-r--r--src/citra_qt/main.cpp125
1 files changed, 81 insertions, 44 deletions
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index e5ca04124..dd0e4de8f 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -15,6 +15,7 @@
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/logging/backend.h" 16#include "common/logging/backend.h"
17#include "common/logging/filter.h" 17#include "common/logging/filter.h"
18#include "common/make_unique.h"
18#include "common/platform.h" 19#include "common/platform.h"
19#include "common/scope_exit.h" 20#include "common/scope_exit.h"
20 21
@@ -46,7 +47,7 @@
46 47
47#include "version.h" 48#include "version.h"
48 49
49GMainWindow::GMainWindow() 50GMainWindow::GMainWindow() : emu_thread(nullptr)
50{ 51{
51 Pica::g_debug_context = Pica::DebugContext::Construct(); 52 Pica::g_debug_context = Pica::DebugContext::Construct();
52 53
@@ -55,14 +56,14 @@ GMainWindow::GMainWindow()
55 ui.setupUi(this); 56 ui.setupUi(this);
56 statusBar()->hide(); 57 statusBar()->hide();
57 58
58 render_window = new GRenderWindow; 59 render_window = new GRenderWindow(this, emu_thread.get());
59 render_window->hide(); 60 render_window->hide();
60 61
61 profilerWidget = new ProfilerWidget(this); 62 profilerWidget = new ProfilerWidget(this);
62 addDockWidget(Qt::BottomDockWidgetArea, profilerWidget); 63 addDockWidget(Qt::BottomDockWidgetArea, profilerWidget);
63 profilerWidget->hide(); 64 profilerWidget->hide();
64 65
65 disasmWidget = new DisassemblerWidget(this, render_window->GetEmuThread()); 66 disasmWidget = new DisassemblerWidget(this, emu_thread.get());
66 addDockWidget(Qt::BottomDockWidgetArea, disasmWidget); 67 addDockWidget(Qt::BottomDockWidgetArea, disasmWidget);
67 disasmWidget->hide(); 68 disasmWidget->hide();
68 69
@@ -138,14 +139,12 @@ GMainWindow::GMainWindow()
138 connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode())); 139 connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
139 connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog())); 140 connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog()));
140 141
141 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues 142 connect(this, SIGNAL(EmulationStarting(EmuThread*)), disasmWidget, SLOT(OnEmulationStarting(EmuThread*)));
142 connect(&render_window->GetEmuThread(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); 143 connect(this, SIGNAL(EmulationStopping()), disasmWidget, SLOT(OnEmulationStopping()));
143 connect(&render_window->GetEmuThread(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); 144 connect(this, SIGNAL(EmulationStarting(EmuThread*)), registersWidget, SLOT(OnEmulationStarting(EmuThread*)));
144 connect(&render_window->GetEmuThread(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); 145 connect(this, SIGNAL(EmulationStopping()), registersWidget, SLOT(OnEmulationStopping()));
145 146 connect(this, SIGNAL(EmulationStarting(EmuThread*)), render_window, SLOT(OnEmulationStarting(EmuThread*)));
146 connect(&render_window->GetEmuThread(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection); 147 connect(this, SIGNAL(EmulationStopping()), render_window, SLOT(OnEmulationStopping()));
147 connect(&render_window->GetEmuThread(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
148 connect(&render_window->GetEmuThread(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
149 148
150 // Setup hotkeys 149 // Setup hotkeys
151 RegisterHotkey("Main Window", "Load File", QKeySequence::Open); 150 RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
@@ -196,32 +195,76 @@ void GMainWindow::OnDisplayTitleBars(bool show)
196 } 195 }
197} 196}
198 197
199void GMainWindow::BootGame(std::string filename) 198void GMainWindow::BootGame(std::string filename) {
200{
201 LOG_INFO(Frontend, "Citra starting...\n"); 199 LOG_INFO(Frontend, "Citra starting...\n");
200
201 // Initialize the core emulation
202 System::Init(render_window); 202 System::Init(render_window);
203 203
204 // Load a game or die... 204 // Load the game
205 if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { 205 if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) {
206 LOG_CRITICAL(Frontend, "Failed to load ROM!"); 206 LOG_CRITICAL(Frontend, "Failed to load ROM!");
207 System::Shutdown();
208 return;
207 } 209 }
208 210
209 disasmWidget->Init(); 211 // Create and start the emulation thread
212 emu_thread = Common::make_unique<EmuThread>(render_window);
213 emit EmulationStarting(emu_thread.get());
214 emu_thread->start();
215
216 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues
217 connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
218 connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
219 connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
220 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
221 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
222 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
223
224 // Update the GUI
210 registersWidget->OnDebugModeEntered(); 225 registersWidget->OnDebugModeEntered();
211 callstackWidget->OnDebugModeEntered(); 226 callstackWidget->OnDebugModeEntered();
212
213 render_window->GetEmuThread().SetFilename(filename);
214 render_window->GetEmuThread().start();
215
216 render_window->show(); 227 render_window->show();
228
217 OnStartGame(); 229 OnStartGame();
218} 230}
219 231
232void GMainWindow::ShutdownGame() {
233 emu_thread->RequestStop();
234
235 // Release emu threads from any breakpoints
236 // This belongs after RequestStop() and before wait() because if emulation stops on a GPU
237 // breakpoint after (or before) RequestStop() is called, the emulation would never be able
238 // to continue out to the main loop and terminate. Thus wait() would hang forever.
239 // TODO(bunnei): This function is not thread safe, but it's being used as if it were
240 Pica::g_debug_context->ClearBreakpoints();
241
242 emit EmulationStopping();
243
244 // Wait for emulation thread to complete and delete it
245 emu_thread->wait();
246 emu_thread = nullptr;
247
248 // Shutdown the core emulation
249 System::Shutdown();
250
251 // Update the GUI
252 ui.action_Start->setEnabled(false);
253 ui.action_Pause->setEnabled(false);
254 ui.action_Stop->setEnabled(false);
255 render_window->hide();
256}
257
220void GMainWindow::OnMenuLoadFile() 258void GMainWindow::OnMenuLoadFile()
221{ 259{
222 QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.bin *.cci *.cxi)")); 260 QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.bin *.cci *.cxi)"));
223 if (filename.size()) 261 if (filename.size()) {
224 BootGame(filename.toLatin1().data()); 262 // Shutdown previous session if the emu thread is still active...
263 if (emu_thread != nullptr)
264 ShutdownGame();
265
266 BootGame(filename.toLatin1().data());
267 }
225} 268}
226 269
227void GMainWindow::OnMenuLoadSymbolMap() { 270void GMainWindow::OnMenuLoadSymbolMap() {
@@ -232,7 +275,7 @@ void GMainWindow::OnMenuLoadSymbolMap() {
232 275
233void GMainWindow::OnStartGame() 276void GMainWindow::OnStartGame()
234{ 277{
235 render_window->GetEmuThread().SetCpuRunning(true); 278 emu_thread->SetRunning(true);
236 279
237 ui.action_Start->setEnabled(false); 280 ui.action_Start->setEnabled(false);
238 ui.action_Pause->setEnabled(true); 281 ui.action_Pause->setEnabled(true);
@@ -241,21 +284,15 @@ void GMainWindow::OnStartGame()
241 284
242void GMainWindow::OnPauseGame() 285void GMainWindow::OnPauseGame()
243{ 286{
244 render_window->GetEmuThread().SetCpuRunning(false); 287 emu_thread->SetRunning(false);
245 288
246 ui.action_Start->setEnabled(true); 289 ui.action_Start->setEnabled(true);
247 ui.action_Pause->setEnabled(false); 290 ui.action_Pause->setEnabled(false);
248 ui.action_Stop->setEnabled(true); 291 ui.action_Stop->setEnabled(true);
249} 292}
250 293
251void GMainWindow::OnStopGame() 294void GMainWindow::OnStopGame() {
252{ 295 ShutdownGame();
253 render_window->GetEmuThread().SetCpuRunning(false);
254 // TODO: Shutdown core
255
256 ui.action_Start->setEnabled(true);
257 ui.action_Pause->setEnabled(false);
258 ui.action_Stop->setEnabled(false);
259} 296}
260 297
261void GMainWindow::OnOpenHotkeysDialog() 298void GMainWindow::OnOpenHotkeysDialog()
@@ -265,24 +302,22 @@ void GMainWindow::OnOpenHotkeysDialog()
265} 302}
266 303
267 304
268void GMainWindow::ToggleWindowMode() 305void GMainWindow::ToggleWindowMode() {
269{ 306 if (ui.action_Single_Window_Mode->isChecked()) {
270 bool enable = ui.action_Single_Window_Mode->isChecked(); 307 // Render in the main window...
271 if (!enable && render_window->parent() != nullptr)
272 {
273 ui.horizontalLayout->removeWidget(render_window);
274 render_window->setParent(nullptr);
275 render_window->setVisible(true);
276 render_window->RestoreGeometry();
277 render_window->setFocusPolicy(Qt::NoFocus);
278 }
279 else if (enable && render_window->parent() == nullptr)
280 {
281 render_window->BackupGeometry(); 308 render_window->BackupGeometry();
282 ui.horizontalLayout->addWidget(render_window); 309 ui.horizontalLayout->addWidget(render_window);
283 render_window->setVisible(true); 310 render_window->setVisible(true);
284 render_window->setFocusPolicy(Qt::ClickFocus); 311 render_window->setFocusPolicy(Qt::ClickFocus);
285 render_window->setFocus(); 312 render_window->setFocus();
313
314 } else {
315 // Render in a separate window...
316 ui.horizontalLayout->removeWidget(render_window);
317 render_window->setParent(nullptr);
318 render_window->setVisible(true);
319 render_window->RestoreGeometry();
320 render_window->setFocusPolicy(Qt::NoFocus);
286 } 321 }
287} 322}
288 323
@@ -303,6 +338,8 @@ void GMainWindow::closeEvent(QCloseEvent* event)
303 settings.setValue("firstStart", false); 338 settings.setValue("firstStart", false);
304 SaveHotkeys(settings); 339 SaveHotkeys(settings);
305 340
341 ShutdownGame();
342
306 render_window->close(); 343 render_window->close();
307 344
308 QWidget::closeEvent(event); 345 QWidget::closeEvent(event);