summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2015-04-29 00:01:41 -0400
committerGravatar bunnei2015-05-01 18:34:42 -0400
commit43cf42490730d8a1b980aa1fe9ebbbe1249232ef (patch)
treebdaa812eed455d85517130934e4cf17bd56c6d0b
parentQt: Restructured to remove unnecessary shutdown event and various cleanups. (diff)
downloadyuzu-43cf42490730d8a1b980aa1fe9ebbbe1249232ef.tar.gz
yuzu-43cf42490730d8a1b980aa1fe9ebbbe1249232ef.tar.xz
yuzu-43cf42490730d8a1b980aa1fe9ebbbe1249232ef.zip
Qt: Use signals for emu_thread start/stop and fix disasm widget.
-rw-r--r--src/citra_qt/bootmanager.cpp14
-rw-r--r--src/citra_qt/bootmanager.h21
-rw-r--r--src/citra_qt/debugger/disassembler.cpp81
-rw-r--r--src/citra_qt/debugger/disassembler.h9
-rw-r--r--src/citra_qt/main.cpp68
-rw-r--r--src/citra_qt/main.h24
6 files changed, 138 insertions, 79 deletions
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 1e902a8b6..4be410fe0 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -87,8 +87,8 @@ private:
87 GRenderWindow* parent; 87 GRenderWindow* parent;
88}; 88};
89 89
90GRenderWindow::GRenderWindow(QWidget* parent, GMainWindow& main_window) : 90GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) :
91 QWidget(parent), main_window(main_window), keyboard_id(0) { 91 QWidget(parent), emu_thread(emu_thread), keyboard_id(0) {
92 92
93 std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); 93 std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
94 setWindowTitle(QString::fromStdString(window_title)); 94 setWindowTitle(QString::fromStdString(window_title));
@@ -129,7 +129,7 @@ void GRenderWindow::moveContext()
129 // We need to move GL context to the swapping thread in Qt5 129 // We need to move GL context to the swapping thread in Qt5
130#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) 130#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
131 // If the thread started running, move the GL Context to the new thread. Otherwise, move it back. 131 // If the thread started running, move the GL Context to the new thread. Otherwise, move it back.
132 auto thread = QThread::currentThread() == qApp->thread() ? main_window.GetEmuThread() : qApp->thread(); 132 auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) ? emu_thread : qApp->thread();
133 child->context()->moveToThread(thread); 133 child->context()->moveToThread(thread);
134#endif 134#endif
135} 135}
@@ -272,3 +272,11 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height)
272void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { 272void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
273 setMinimumSize(minimal_size.first, minimal_size.second); 273 setMinimumSize(minimal_size.first, minimal_size.second);
274} 274}
275
276void GRenderWindow::OnEmulationStarted(EmuThread* emu_thread) {
277 this->emu_thread = emu_thread;
278}
279
280void GRenderWindow::OnEmulationStopped() {
281 emu_thread = nullptr;
282}
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h
index e9b3ea664..e57522187 100644
--- a/src/citra_qt/bootmanager.h
+++ b/src/citra_qt/bootmanager.h
@@ -22,6 +22,7 @@ class EmuThread : public QThread
22 Q_OBJECT 22 Q_OBJECT
23 23
24public: 24public:
25 EmuThread(GRenderWindow* render_window);
25 26
26 /** 27 /**
27 * Start emulation (on new thread) 28 * Start emulation (on new thread)
@@ -50,15 +51,14 @@ public:
50 bool IsRunning() { return running; } 51 bool IsRunning() { return running; }
51 52
52 /** 53 /**
53 * Shutdown (permanently stops) the emulation thread 54 * Requests for the emulation thread to stop running and shutdown emulation
54 */ 55 */
55 void Shutdown() { stop_run = true; }; 56 void RequestShutdown() {
57 stop_run = true;
58 running = false;
59 };
56 60
57private: 61private:
58 friend class GMainWindow;
59
60 EmuThread(GRenderWindow* render_window);
61
62 bool exec_step; 62 bool exec_step;
63 bool running; 63 bool running;
64 std::atomic<bool> stop_run; 64 std::atomic<bool> stop_run;
@@ -86,7 +86,7 @@ class GRenderWindow : public QWidget, public EmuWindow
86 Q_OBJECT 86 Q_OBJECT
87 87
88public: 88public:
89 GRenderWindow(QWidget* parent, GMainWindow& main_window); 89 GRenderWindow(QWidget* parent, EmuThread* emu_thread);
90 90
91 // EmuWindow implementation 91 // EmuWindow implementation
92 void SwapBuffers() override; 92 void SwapBuffers() override;
@@ -115,6 +115,9 @@ public:
115public slots: 115public slots:
116 void moveContext(); // overridden 116 void moveContext(); // overridden
117 117
118 void OnEmulationStarted(EmuThread* emu_thread);
119 void OnEmulationStopped();
120
118private: 121private:
119 void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; 122 void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override;
120 123
@@ -122,8 +125,8 @@ private:
122 125
123 QByteArray geometry; 126 QByteArray geometry;
124 127
125 GMainWindow& main_window;
126
127 /// Device id of keyboard for use with KeyMap 128 /// Device id of keyboard for use with KeyMap
128 int keyboard_id; 129 int keyboard_id;
130
131 EmuThread* emu_thread;
129}; 132};
diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp
index f9423e1d6..6400bb856 100644
--- a/src/citra_qt/debugger/disassembler.cpp
+++ b/src/citra_qt/debugger/disassembler.cpp
@@ -4,7 +4,6 @@
4 4
5#include "disassembler.h" 5#include "disassembler.h"
6 6
7#include "../main.h"
8#include "../bootmanager.h" 7#include "../bootmanager.h"
9#include "../hotkeys.h" 8#include "../hotkeys.h"
10 9
@@ -19,8 +18,8 @@
19#include "core/arm/disassembler/arm_disasm.h" 18#include "core/arm/disassembler/arm_disasm.h"
20 19
21 20
22DisassemblerModel::DisassemblerModel(QObject* parent) : QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) { 21DisassemblerModel::DisassemblerModel(QObject* parent) :
23 22 QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) {
24} 23}
25 24
26int DisassemblerModel::columnCount(const QModelIndex& parent) const { 25int DisassemblerModel::columnCount(const QModelIndex& parent) const {
@@ -159,35 +158,28 @@ void DisassemblerModel::SetNextInstruction(unsigned int address) {
159 emit dataChanged(prev_index, prev_index); 158 emit dataChanged(prev_index, prev_index);
160} 159}
161 160
162DisassemblerWidget::DisassemblerWidget(QWidget* parent, GMainWindow& main_window) : 161DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) :
163 QDockWidget(parent), main_window(main_window), base_addr(0) { 162 QDockWidget(parent), emu_thread(emu_thread), base_addr(0) {
164 163
165 disasm_ui.setupUi(this); 164 disasm_ui.setupUi(this);
166 165
167 model = new DisassemblerModel(this);
168 disasm_ui.treeView->setModel(model);
169
170 RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut); 166 RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut);
171 RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut); 167 RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut);
172 RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut); 168 RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut);
173 RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), Qt::ApplicationShortcut); 169 RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), Qt::ApplicationShortcut);
174 170
175 connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint()));
176 connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep())); 171 connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep()));
177 connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause())); 172 connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause()));
178 connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue())); 173 connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue()));
179 174
180 connect(disasm_ui.treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
181 model, SLOT(OnSelectionChanged(const QModelIndex&)));
182
183 connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, SLOT(OnToggleStartStop())); 175 connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, SLOT(OnToggleStartStop()));
184 connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep())); 176 connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep()));
185 connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, SLOT(OnStepInto())); 177 connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, SLOT(OnStepInto()));
186 connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, SLOT(OnSetOrUnsetBreakpoint())); 178
179 setEnabled(false);
187} 180}
188 181
189void DisassemblerWidget::Init() 182void DisassemblerWidget::Init() {
190{
191 model->ParseFromAddress(Core::g_app_core->GetPC()); 183 model->ParseFromAddress(Core::g_app_core->GetPC());
192 184
193 disasm_ui.treeView->resizeColumnToContents(0); 185 disasm_ui.treeView->resizeColumnToContents(0);
@@ -199,25 +191,21 @@ void DisassemblerWidget::Init()
199 disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); 191 disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
200} 192}
201 193
202void DisassemblerWidget::OnContinue() 194void DisassemblerWidget::OnContinue() {
203{ 195 emu_thread->SetRunning(true);
204 main_window.GetEmuThread()->SetRunning(true);
205} 196}
206 197
207void DisassemblerWidget::OnStep() 198void DisassemblerWidget::OnStep() {
208{
209 OnStepInto(); // change later 199 OnStepInto(); // change later
210} 200}
211 201
212void DisassemblerWidget::OnStepInto() 202void DisassemblerWidget::OnStepInto() {
213{ 203 emu_thread->SetRunning(false);
214 main_window.GetEmuThread()->SetRunning(false); 204 emu_thread->ExecStep();
215 main_window.GetEmuThread()->ExecStep();
216} 205}
217 206
218void DisassemblerWidget::OnPause() 207void DisassemblerWidget::OnPause() {
219{ 208 emu_thread->SetRunning(false);
220 main_window.GetEmuThread()->SetRunning(false);
221 209
222 // TODO: By now, the CPU might not have actually stopped... 210 // TODO: By now, the CPU might not have actually stopped...
223 if (Core::g_app_core) { 211 if (Core::g_app_core) {
@@ -225,17 +213,15 @@ void DisassemblerWidget::OnPause()
225 } 213 }
226} 214}
227 215
228void DisassemblerWidget::OnToggleStartStop() 216void DisassemblerWidget::OnToggleStartStop() {
229{ 217 emu_thread->SetRunning(!emu_thread->IsRunning());
230 main_window.GetEmuThread()->SetRunning(!main_window.GetEmuThread()->IsRunning());
231} 218}
232 219
233void DisassemblerWidget::OnDebugModeEntered() 220void DisassemblerWidget::OnDebugModeEntered() {
234{
235 ARMword next_instr = Core::g_app_core->GetPC(); 221 ARMword next_instr = Core::g_app_core->GetPC();
236 222
237 if (model->GetBreakPoints().IsAddressBreakPoint(next_instr)) 223 if (model->GetBreakPoints().IsAddressBreakPoint(next_instr))
238 main_window.GetEmuThread()->SetRunning(false); 224 emu_thread->SetRunning(false);
239 225
240 model->SetNextInstruction(next_instr); 226 model->SetNextInstruction(next_instr);
241 227
@@ -244,16 +230,35 @@ void DisassemblerWidget::OnDebugModeEntered()
244 disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); 230 disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
245} 231}
246 232
247void DisassemblerWidget::OnDebugModeLeft() 233void DisassemblerWidget::OnDebugModeLeft() {
248{
249
250} 234}
251 235
252int DisassemblerWidget::SelectedRow() 236int DisassemblerWidget::SelectedRow() {
253{
254 QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex(); 237 QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex();
255 if (!index.isValid()) 238 if (!index.isValid())
256 return -1; 239 return -1;
257 240
258 return disasm_ui.treeView->selectionModel()->currentIndex().row(); 241 return disasm_ui.treeView->selectionModel()->currentIndex().row();
259} 242}
243
244void DisassemblerWidget::OnEmulationStarted(EmuThread* emu_thread) {
245 this->emu_thread = emu_thread;
246
247 model = new DisassemblerModel(this);
248 disasm_ui.treeView->setModel(model);
249
250 connect(disasm_ui.treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
251 model, SLOT(OnSelectionChanged(const QModelIndex&)));
252 connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint()));
253 connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, SLOT(OnSetOrUnsetBreakpoint()));
254
255 Init();
256 setEnabled(true);
257}
258
259void DisassemblerWidget::OnEmulationStopped() {
260 disasm_ui.treeView->setModel(nullptr);
261 delete model;
262 emu_thread = nullptr;
263 setEnabled(false);
264}
diff --git a/src/citra_qt/debugger/disassembler.h b/src/citra_qt/debugger/disassembler.h
index d9e32dbdf..b771e95b7 100644
--- a/src/citra_qt/debugger/disassembler.h
+++ b/src/citra_qt/debugger/disassembler.h
@@ -13,7 +13,7 @@
13#include "common/break_points.h" 13#include "common/break_points.h"
14 14
15class QAction; 15class QAction;
16class GMainWindow; 16class EmuThread;
17 17
18class DisassemblerModel : public QAbstractListModel 18class DisassemblerModel : public QAbstractListModel
19{ 19{
@@ -51,7 +51,7 @@ class DisassemblerWidget : public QDockWidget
51 Q_OBJECT 51 Q_OBJECT
52 52
53public: 53public:
54 DisassemblerWidget(QWidget* parent, GMainWindow& main_window); 54 DisassemblerWidget(QWidget* parent, EmuThread* emu_thread);
55 55
56 void Init(); 56 void Init();
57 57
@@ -65,6 +65,9 @@ public slots:
65 void OnDebugModeEntered(); 65 void OnDebugModeEntered();
66 void OnDebugModeLeft(); 66 void OnDebugModeLeft();
67 67
68 void OnEmulationStarted(EmuThread* emu_thread);
69 void OnEmulationStopped();
70
68private: 71private:
69 // returns -1 if no row is selected 72 // returns -1 if no row is selected
70 int SelectedRow(); 73 int SelectedRow();
@@ -75,5 +78,5 @@ private:
75 78
76 u32 base_addr; 79 u32 base_addr;
77 80
78 GMainWindow& main_window; 81 EmuThread* emu_thread;
79}; 82};
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index dd180baa4..7de2bf8ba 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
@@ -55,14 +56,14 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
55 ui.setupUi(this); 56 ui.setupUi(this);
56 statusBar()->hide(); 57 statusBar()->hide();
57 58
58 render_window = new GRenderWindow(this, *this); 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, *this); 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,10 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
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(EmulationStarted(EmuThread*)), disasmWidget, SLOT(OnEmulationStarted(EmuThread*)));
142 connect(emu_thread, SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); 143 connect(this, SIGNAL(EmulationStopped()), disasmWidget, SLOT(OnEmulationStopped()));
143 connect(emu_thread, SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); 144 connect(this, SIGNAL(EmulationStarted(EmuThread*)), render_window, SLOT(OnEmulationStarted(EmuThread*)));
144 connect(emu_thread, SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection); 145 connect(this, SIGNAL(EmulationStopped()), render_window, SLOT(OnEmulationStopped()));
145
146 connect(emu_thread, SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
147 connect(emu_thread, SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
148 connect(emu_thread, SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
149 146
150 // Setup hotkeys 147 // Setup hotkeys
151 RegisterHotkey("Main Window", "Load File", QKeySequence::Open); 148 RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
@@ -199,35 +196,62 @@ void GMainWindow::OnDisplayTitleBars(bool show)
199void GMainWindow::BootGame(std::string filename) { 196void GMainWindow::BootGame(std::string filename) {
200 LOG_INFO(Frontend, "Citra starting...\n"); 197 LOG_INFO(Frontend, "Citra starting...\n");
201 198
199 // Initialize the core emulation
202 System::Init(render_window); 200 System::Init(render_window);
203 201
204 // Load a game or die... 202 // Load the game
205 if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) { 203 if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) {
206 LOG_CRITICAL(Frontend, "Failed to load ROM!"); 204 LOG_CRITICAL(Frontend, "Failed to load ROM!");
205 System::Shutdown();
206 return;
207 } 207 }
208 208
209 disasmWidget->Init(); 209 // Create and start the emulation thread
210 registersWidget->OnDebugModeEntered(); 210 emu_thread = Common::make_unique<EmuThread>(render_window);
211 callstackWidget->OnDebugModeEntered(); 211 emit EmulationStarted(emu_thread.get());
212
213 emu_thread = new EmuThread(render_window);
214 emu_thread->start(); 212 emu_thread->start();
215 213
214 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues
215 connect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
216 connect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
217 connect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
218 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
219 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
220 connect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
221
222 // Update the GUI
223 registersWidget->OnDebugModeEntered();
224 callstackWidget->OnDebugModeEntered();
216 render_window->show(); 225 render_window->show();
226
217 OnStartGame(); 227 OnStartGame();
218} 228}
219 229
220void GMainWindow::ShutdownGame() { 230void GMainWindow::ShutdownGame() {
221 // Shutdown the emulation thread and wait for it to complete 231 // Shutdown the emulation thread
222 emu_thread->SetRunning(false); 232 emu_thread->RequestShutdown();
223 emu_thread->Shutdown(); 233
224 emu_thread->wait(); 234 // Disconnect signals that are attached to the current emulation thread
225 delete emu_thread; 235 disconnect(emu_thread.get(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()));
226 emu_thread = nullptr; 236 disconnect(emu_thread.get(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()));
237 disconnect(emu_thread.get(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()));
238 disconnect(emu_thread.get(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()));
239 disconnect(emu_thread.get(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()));
240 disconnect(emu_thread.get(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()));
227 241
228 // Release emu threads from any breakpoints 242 // Release emu threads from any breakpoints
243 // This belongs after RequestShutdown() and before wait() because if emulation stops on a GPU
244 // breakpoint after (or before) RequestShutdown() is called, the emulation would never be able
245 // to continue out to the main loop and terminate. Thus wait() would hang forever.
246 // TODO(bunnei): This function is not thread safe, but it's being used as if it were
229 Pica::g_debug_context->ClearBreakpoints(); 247 Pica::g_debug_context->ClearBreakpoints();
230 248
249 emit EmulationStopped();
250
251 // Wait for emulation thread to complete and delete it
252 emu_thread->wait();
253 emu_thread = nullptr;
254
231 // Shutdown the core emulation 255 // Shutdown the core emulation
232 System::Shutdown(); 256 System::Shutdown();
233 257
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 1821ae35f..3e29534fb 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -5,6 +5,7 @@
5#ifndef _CITRA_QT_MAIN_HXX_ 5#ifndef _CITRA_QT_MAIN_HXX_
6#define _CITRA_QT_MAIN_HXX_ 6#define _CITRA_QT_MAIN_HXX_
7 7
8#include <memory>
8#include <QMainWindow> 9#include <QMainWindow>
9 10
10#include "ui_main.h" 11#include "ui_main.h"
@@ -35,9 +36,23 @@ public:
35 GMainWindow(); 36 GMainWindow();
36 ~GMainWindow(); 37 ~GMainWindow();
37 38
38 EmuThread* GetEmuThread() { 39signals:
39 return emu_thread; 40
40 } 41 /**
42 * Signal that is emitted when a new EmuThread has been created and an emulation session is
43 * about to start. At this time, the core system emulation has been initialized, and all
44 * emulation handles and memory should be valid.
45 *
46 * @param emu_thread Pointer to the newly created EmuThread (to be used by widgets that need to
47 * access/change emulation state).
48 */
49 void EmulationStarting(EmuThread* emu_thread);
50
51 /**
52 * Signal that is emitted when emulation is about to stop. At this time, the EmuThread and core
53 * system emulation handles and memory are still valid, but are about become invalid.
54 */
55 void EmulationStopping();
41 56
42private: 57private:
43 void BootGame(std::string filename); 58 void BootGame(std::string filename);
@@ -60,7 +75,8 @@ private:
60 Ui::MainWindow ui; 75 Ui::MainWindow ui;
61 76
62 GRenderWindow* render_window; 77 GRenderWindow* render_window;
63 EmuThread* emu_thread; 78
79 std::unique_ptr<EmuThread> emu_thread;
64 80
65 ProfilerWidget* profilerWidget; 81 ProfilerWidget* profilerWidget;
66 DisassemblerWidget* disasmWidget; 82 DisassemblerWidget* disasmWidget;