summaryrefslogtreecommitdiff
path: root/src/citra_qt
diff options
context:
space:
mode:
Diffstat (limited to 'src/citra_qt')
-rw-r--r--src/citra_qt/bootmanager.cpp103
-rw-r--r--src/citra_qt/bootmanager.h68
-rw-r--r--src/citra_qt/debugger/disassembler.cpp81
-rw-r--r--src/citra_qt/debugger/disassembler.h7
-rw-r--r--src/citra_qt/debugger/graphics_breakpoints_p.h2
-rw-r--r--src/citra_qt/debugger/profiler.cpp4
-rw-r--r--src/citra_qt/debugger/profiler.h2
-rw-r--r--src/citra_qt/debugger/registers.cpp33
-rw-r--r--src/citra_qt/debugger/registers.h4
-rw-r--r--src/citra_qt/main.cpp125
-rw-r--r--src/citra_qt/main.h23
-rw-r--r--src/citra_qt/main.ui3
-rw-r--r--src/citra_qt/util/spinbox.cpp3
13 files changed, 241 insertions, 217 deletions
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index b81bd6167..a7f949411 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -10,6 +10,7 @@
10 10
11#include "common/common.h" 11#include "common/common.h"
12#include "bootmanager.h" 12#include "bootmanager.h"
13#include "main.h"
13 14
14#include "core/core.h" 15#include "core/core.h"
15#include "core/settings.h" 16#include "core/settings.h"
@@ -27,43 +28,33 @@
27#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" 28#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
28 29
29EmuThread::EmuThread(GRenderWindow* render_window) : 30EmuThread::EmuThread(GRenderWindow* render_window) :
30 filename(""), exec_cpu_step(false), cpu_running(false), 31 exec_step(false), running(false), stop_run(false), render_window(render_window) {
31 stop_run(false), render_window(render_window)
32{
33}
34 32
35void EmuThread::SetFilename(std::string filename) 33 connect(this, SIGNAL(started()), render_window, SLOT(moveContext()));
36{
37 this->filename = filename;
38} 34}
39 35
40void EmuThread::run() 36void EmuThread::run() {
41{
42 stop_run = false; 37 stop_run = false;
43 38
44 // holds whether the cpu was running during the last iteration, 39 // holds whether the cpu was running during the last iteration,
45 // so that the DebugModeLeft signal can be emitted before the 40 // so that the DebugModeLeft signal can be emitted before the
46 // next execution step 41 // next execution step
47 bool was_active = false; 42 bool was_active = false;
48 while (!stop_run) 43 while (!stop_run) {
49 { 44 if (running) {
50 if (cpu_running)
51 {
52 if (!was_active) 45 if (!was_active)
53 emit DebugModeLeft(); 46 emit DebugModeLeft();
54 47
55 Core::RunLoop(); 48 Core::RunLoop();
56 49
57 was_active = cpu_running || exec_cpu_step; 50 was_active = running || exec_step;
58 if (!was_active) 51 if (!was_active && !stop_run)
59 emit DebugModeEntered(); 52 emit DebugModeEntered();
60 } 53 } else if (exec_step) {
61 else if (exec_cpu_step)
62 {
63 if (!was_active) 54 if (!was_active)
64 emit DebugModeLeft(); 55 emit DebugModeLeft();
65 56
66 exec_cpu_step = false; 57 exec_step = false;
67 Core::SingleStep(); 58 Core::SingleStep();
68 emit DebugModeEntered(); 59 emit DebugModeEntered();
69 yieldCurrentThread(); 60 yieldCurrentThread();
@@ -71,47 +62,10 @@ void EmuThread::run()
71 was_active = false; 62 was_active = false;
72 } 63 }
73 } 64 }
74 render_window->moveContext();
75
76 Core::Stop();
77}
78
79void EmuThread::Stop()
80{
81 if (!isRunning())
82 {
83 LOG_WARNING(Frontend, "EmuThread::Stop called while emu thread wasn't running, returning...");
84 return;
85 }
86 stop_run = true;
87 65
88 // Release emu threads from any breakpoints, so that this doesn't hang forever. 66 render_window->moveContext();
89 Pica::g_debug_context->ClearBreakpoints();
90
91 //core::g_state = core::SYS_DIE;
92
93 // TODO: Waiting here is just a bad workaround for retarded shutdown logic.
94 wait(1000);
95 if (isRunning())
96 {
97 LOG_WARNING(Frontend, "EmuThread still running, terminating...");
98 quit();
99
100 // TODO: Waiting 50 seconds can be necessary if the logging subsystem has a lot of spam
101 // queued... This should be fixed.
102 wait(50000);
103 if (isRunning())
104 {
105 LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here...");
106 terminate();
107 }
108 }
109 LOG_INFO(Frontend, "EmuThread stopped");
110
111 System::Shutdown();
112} 67}
113 68
114
115// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context. 69// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context.
116// The corresponding functionality is handled in EmuThread instead 70// The corresponding functionality is handled in EmuThread instead
117class GGLWidgetInternal : public QGLWidget 71class GGLWidgetInternal : public QGLWidget
@@ -133,13 +87,9 @@ private:
133 GRenderWindow* parent; 87 GRenderWindow* parent;
134}; 88};
135 89
136EmuThread& GRenderWindow::GetEmuThread() 90GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) :
137{ 91 QWidget(parent), emu_thread(emu_thread), keyboard_id(0) {
138 return emu_thread;
139}
140 92
141GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0)
142{
143 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);
144 setWindowTitle(QString::fromStdString(window_title)); 94 setWindowTitle(QString::fromStdString(window_title));
145 95
@@ -160,7 +110,6 @@ GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this
160 layout->addWidget(child); 110 layout->addWidget(child);
161 layout->setMargin(0); 111 layout->setMargin(0);
162 setLayout(layout); 112 setLayout(layout);
163 connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext()));
164 113
165 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 114 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
166 115
@@ -180,29 +129,17 @@ void GRenderWindow::moveContext()
180 // 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
181#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) 130#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
182 // 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.
183 child->context()->moveToThread((QThread::currentThread() == qApp->thread()) ? &emu_thread : qApp->thread()); 132 auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) ? emu_thread : qApp->thread();
133 child->context()->moveToThread(thread);
184#endif 134#endif
185} 135}
186 136
187GRenderWindow::~GRenderWindow()
188{
189 if (emu_thread.isRunning())
190 emu_thread.Stop();
191}
192
193void GRenderWindow::SwapBuffers() 137void GRenderWindow::SwapBuffers()
194{ 138{
195 // MakeCurrent is already called in renderer_opengl 139 // MakeCurrent is already called in renderer_opengl
196 child->swapBuffers(); 140 child->swapBuffers();
197} 141}
198 142
199void GRenderWindow::closeEvent(QCloseEvent* event)
200{
201 if (emu_thread.isRunning())
202 emu_thread.Stop();
203 QWidget::closeEvent(event);
204}
205
206void GRenderWindow::MakeCurrent() 143void GRenderWindow::MakeCurrent()
207{ 144{
208 child->makeCurrent(); 145 child->makeCurrent();
@@ -288,7 +225,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent *event)
288void GRenderWindow::mouseMoveEvent(QMouseEvent *event) 225void GRenderWindow::mouseMoveEvent(QMouseEvent *event)
289{ 226{
290 auto pos = event->pos(); 227 auto pos = event->pos();
291 this->TouchMoved(static_cast<unsigned>(pos.x()), static_cast<unsigned>(pos.y())); 228 this->TouchMoved(static_cast<unsigned>(std::max(pos.x(), 0)), static_cast<unsigned>(std::max(pos.y(), 0)));
292} 229}
293 230
294void GRenderWindow::mouseReleaseEvent(QMouseEvent *event) 231void GRenderWindow::mouseReleaseEvent(QMouseEvent *event)
@@ -335,3 +272,11 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height)
335void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { 272void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
336 setMinimumSize(minimal_size.first, minimal_size.second); 273 setMinimumSize(minimal_size.first, minimal_size.second);
337} 274}
275
276void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
277 this->emu_thread = emu_thread;
278}
279
280void GRenderWindow::OnEmulationStopping() {
281 emu_thread = nullptr;
282}
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h
index 288da45a1..715faf2d7 100644
--- a/src/citra_qt/bootmanager.h
+++ b/src/citra_qt/bootmanager.h
@@ -9,72 +9,58 @@
9 9
10#include "common/common.h" 10#include "common/common.h"
11#include "common/emu_window.h" 11#include "common/emu_window.h"
12#include "common/thread.h"
12 13
13class QScreen; 14class QScreen;
14class QKeyEvent; 15class QKeyEvent;
15 16
16class GRenderWindow; 17class GRenderWindow;
18class GMainWindow;
17 19
18class EmuThread : public QThread 20class EmuThread : public QThread
19{ 21{
20 Q_OBJECT 22 Q_OBJECT
21 23
22public: 24public:
23 /** 25 EmuThread(GRenderWindow* render_window);
24 * Set image filename
25 *
26 * @param filename
27 * @warning Only call when not running!
28 */
29 void SetFilename(std::string filename);
30 26
31 /** 27 /**
32 * Start emulation (on new thread) 28 * Start emulation (on new thread)
33 *
34 * @warning Only call when not running! 29 * @warning Only call when not running!
35 */ 30 */
36 void run() override; 31 void run() override;
37 32
38 /** 33 /**
39 * Allow the CPU to process a single instruction (if cpu is not running) 34 * Steps the emulation thread by a single CPU instruction (if the CPU is not already running)
40 *
41 * @note This function is thread-safe 35 * @note This function is thread-safe
42 */ 36 */
43 void ExecStep() { exec_cpu_step = true; } 37 void ExecStep() { exec_step = true; }
44 38
45 /** 39 /**
46 * Allow the CPU to continue processing instructions without interruption 40 * Sets whether the emulation thread is running or not
47 * 41 * @param running Boolean value, set the emulation thread to running if true
48 * @note This function is thread-safe 42 * @note This function is thread-safe
49 */ 43 */
50 void SetCpuRunning(bool running) { cpu_running = running; } 44 void SetRunning(bool running) { this->running = running; }
51 45
52 /** 46 /**
53 * Allow the CPU to continue processing instructions without interruption 47 * Check if the emulation thread is running or not
54 * 48 * @return True if the emulation thread is running, otherwise false
55 * @note This function is thread-safe 49 * @note This function is thread-safe
56 */ 50 */
57 bool IsCpuRunning() { return cpu_running; } 51 bool IsRunning() { return running; }
58
59 52
60public slots:
61 /** 53 /**
62 * Stop emulation and wait for the thread to finish. 54 * Requests for the emulation thread to stop running
63 *
64 * @details: This function will wait a second for the thread to finish; if it hasn't finished until then, we'll terminate() it and wait another second, hoping that it will be terminated by then.
65 * @note: This function is thread-safe.
66 */ 55 */
67 void Stop(); 56 void RequestStop() {
57 stop_run = true;
58 running = false;
59 };
68 60
69private: 61private:
70 friend class GRenderWindow; 62 bool exec_step;
71 63 bool running;
72 EmuThread(GRenderWindow* render_window);
73
74 std::string filename;
75
76 bool exec_cpu_step;
77 bool cpu_running;
78 std::atomic<bool> stop_run; 64 std::atomic<bool> stop_run;
79 65
80 GRenderWindow* render_window; 66 GRenderWindow* render_window;
@@ -100,10 +86,7 @@ class GRenderWindow : public QWidget, public EmuWindow
100 Q_OBJECT 86 Q_OBJECT
101 87
102public: 88public:
103 GRenderWindow(QWidget* parent = NULL); 89 GRenderWindow(QWidget* parent, EmuThread* emu_thread);
104 ~GRenderWindow();
105
106 void closeEvent(QCloseEvent*) override;
107 90
108 // EmuWindow implementation 91 // EmuWindow implementation
109 void SwapBuffers() override; 92 void SwapBuffers() override;
@@ -116,8 +99,6 @@ public:
116 void restoreGeometry(const QByteArray& geometry); // overridden 99 void restoreGeometry(const QByteArray& geometry); // overridden
117 QByteArray saveGeometry(); // overridden 100 QByteArray saveGeometry(); // overridden
118 101
119 EmuThread& GetEmuThread();
120
121 void keyPressEvent(QKeyEvent* event) override; 102 void keyPressEvent(QKeyEvent* event) override;
122 void keyReleaseEvent(QKeyEvent* event) override; 103 void keyReleaseEvent(QKeyEvent* event) override;
123 104
@@ -134,15 +115,18 @@ public:
134public slots: 115public slots:
135 void moveContext(); // overridden 116 void moveContext(); // overridden
136 117
118 void OnEmulationStarting(EmuThread* emu_thread);
119 void OnEmulationStopping();
120
137private: 121private:
138 void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; 122 void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override;
139 123
140 QGLWidget* child; 124 QGLWidget* child;
141 125
142 EmuThread emu_thread;
143
144 QByteArray geometry; 126 QByteArray geometry;
145 127
146 /// Device id of keyboard for use with KeyMap 128 /// Device id of keyboard for use with KeyMap
147 int keyboard_id; 129 int keyboard_id;
130
131 EmuThread* emu_thread;
148}; 132};
diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp
index f620687ae..08c6b49bd 100644
--- a/src/citra_qt/debugger/disassembler.cpp
+++ b/src/citra_qt/debugger/disassembler.cpp
@@ -18,8 +18,8 @@
18#include "core/arm/disassembler/arm_disasm.h" 18#include "core/arm/disassembler/arm_disasm.h"
19 19
20 20
21DisassemblerModel::DisassemblerModel(QObject* parent) : QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) { 21DisassemblerModel::DisassemblerModel(QObject* parent) :
22 22 QAbstractListModel(parent), base_address(0), code_size(0), program_counter(0), selection(QModelIndex()) {
23} 23}
24 24
25int DisassemblerModel::columnCount(const QModelIndex& parent) const { 25int DisassemblerModel::columnCount(const QModelIndex& parent) const {
@@ -158,34 +158,28 @@ void DisassemblerModel::SetNextInstruction(unsigned int address) {
158 emit dataChanged(prev_index, prev_index); 158 emit dataChanged(prev_index, prev_index);
159} 159}
160 160
161DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread& emu_thread) : QDockWidget(parent), base_addr(0), emu_thread(emu_thread) 161DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) :
162{ 162 QDockWidget(parent), emu_thread(emu_thread), base_addr(0) {
163 disasm_ui.setupUi(this);
164 163
165 model = new DisassemblerModel(this); 164 disasm_ui.setupUi(this);
166 disasm_ui.treeView->setModel(model);
167 165
168 RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut); 166 RegisterHotkey("Disassembler", "Start/Stop", QKeySequence(Qt::Key_F5), Qt::ApplicationShortcut);
169 RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut); 167 RegisterHotkey("Disassembler", "Step", QKeySequence(Qt::Key_F10), Qt::ApplicationShortcut);
170 RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut); 168 RegisterHotkey("Disassembler", "Step into", QKeySequence(Qt::Key_F11), Qt::ApplicationShortcut);
171 RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), Qt::ApplicationShortcut); 169 RegisterHotkey("Disassembler", "Set Breakpoint", QKeySequence(Qt::Key_F9), Qt::ApplicationShortcut);
172 170
173 connect(disasm_ui.button_breakpoint, SIGNAL(clicked()), model, SLOT(OnSetOrUnsetBreakpoint()));
174 connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep())); 171 connect(disasm_ui.button_step, SIGNAL(clicked()), this, SLOT(OnStep()));
175 connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause())); 172 connect(disasm_ui.button_pause, SIGNAL(clicked()), this, SLOT(OnPause()));
176 connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue())); 173 connect(disasm_ui.button_continue, SIGNAL(clicked()), this, SLOT(OnContinue()));
177 174
178 connect(disasm_ui.treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
179 model, SLOT(OnSelectionChanged(const QModelIndex&)));
180
181 connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, SLOT(OnToggleStartStop())); 175 connect(GetHotkey("Disassembler", "Start/Stop", this), SIGNAL(activated()), this, SLOT(OnToggleStartStop()));
182 connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep())); 176 connect(GetHotkey("Disassembler", "Step", this), SIGNAL(activated()), this, SLOT(OnStep()));
183 connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, SLOT(OnStepInto())); 177 connect(GetHotkey("Disassembler", "Step into", this), SIGNAL(activated()), this, SLOT(OnStepInto()));
184 connect(GetHotkey("Disassembler", "Set Breakpoint", this), SIGNAL(activated()), model, SLOT(OnSetOrUnsetBreakpoint())); 178
179 setEnabled(false);
185} 180}
186 181
187void DisassemblerWidget::Init() 182void DisassemblerWidget::Init() {
188{
189 model->ParseFromAddress(Core::g_app_core->GetPC()); 183 model->ParseFromAddress(Core::g_app_core->GetPC());
190 184
191 disasm_ui.treeView->resizeColumnToContents(0); 185 disasm_ui.treeView->resizeColumnToContents(0);
@@ -197,25 +191,21 @@ void DisassemblerWidget::Init()
197 disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); 191 disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
198} 192}
199 193
200void DisassemblerWidget::OnContinue() 194void DisassemblerWidget::OnContinue() {
201{ 195 emu_thread->SetRunning(true);
202 emu_thread.SetCpuRunning(true);
203} 196}
204 197
205void DisassemblerWidget::OnStep() 198void DisassemblerWidget::OnStep() {
206{
207 OnStepInto(); // change later 199 OnStepInto(); // change later
208} 200}
209 201
210void DisassemblerWidget::OnStepInto() 202void DisassemblerWidget::OnStepInto() {
211{ 203 emu_thread->SetRunning(false);
212 emu_thread.SetCpuRunning(false); 204 emu_thread->ExecStep();
213 emu_thread.ExecStep();
214} 205}
215 206
216void DisassemblerWidget::OnPause() 207void DisassemblerWidget::OnPause() {
217{ 208 emu_thread->SetRunning(false);
218 emu_thread.SetCpuRunning(false);
219 209
220 // TODO: By now, the CPU might not have actually stopped... 210 // TODO: By now, the CPU might not have actually stopped...
221 if (Core::g_app_core) { 211 if (Core::g_app_core) {
@@ -223,17 +213,15 @@ void DisassemblerWidget::OnPause()
223 } 213 }
224} 214}
225 215
226void DisassemblerWidget::OnToggleStartStop() 216void DisassemblerWidget::OnToggleStartStop() {
227{ 217 emu_thread->SetRunning(!emu_thread->IsRunning());
228 emu_thread.SetCpuRunning(!emu_thread.IsCpuRunning());
229} 218}
230 219
231void DisassemblerWidget::OnDebugModeEntered() 220void DisassemblerWidget::OnDebugModeEntered() {
232{
233 ARMword next_instr = Core::g_app_core->GetPC(); 221 ARMword next_instr = Core::g_app_core->GetPC();
234 222
235 if (model->GetBreakPoints().IsAddressBreakPoint(next_instr)) 223 if (model->GetBreakPoints().IsAddressBreakPoint(next_instr))
236 emu_thread.SetCpuRunning(false); 224 emu_thread->SetRunning(false);
237 225
238 model->SetNextInstruction(next_instr); 226 model->SetNextInstruction(next_instr);
239 227
@@ -242,16 +230,35 @@ void DisassemblerWidget::OnDebugModeEntered()
242 disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); 230 disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
243} 231}
244 232
245void DisassemblerWidget::OnDebugModeLeft() 233void DisassemblerWidget::OnDebugModeLeft() {
246{
247
248} 234}
249 235
250int DisassemblerWidget::SelectedRow() 236int DisassemblerWidget::SelectedRow() {
251{
252 QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex(); 237 QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex();
253 if (!index.isValid()) 238 if (!index.isValid())
254 return -1; 239 return -1;
255 240
256 return disasm_ui.treeView->selectionModel()->currentIndex().row(); 241 return disasm_ui.treeView->selectionModel()->currentIndex().row();
257} 242}
243
244void DisassemblerWidget::OnEmulationStarting(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::OnEmulationStopping() {
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 5e19d7c51..45b0a7e08 100644
--- a/src/citra_qt/debugger/disassembler.h
+++ b/src/citra_qt/debugger/disassembler.h
@@ -51,7 +51,7 @@ class DisassemblerWidget : public QDockWidget
51 Q_OBJECT 51 Q_OBJECT
52 52
53public: 53public:
54 DisassemblerWidget(QWidget* parent, EmuThread& emu_thread); 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 OnEmulationStarting(EmuThread* emu_thread);
69 void OnEmulationStopping();
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 EmuThread& emu_thread; 81 EmuThread* emu_thread;
79}; 82};
diff --git a/src/citra_qt/debugger/graphics_breakpoints_p.h b/src/citra_qt/debugger/graphics_breakpoints_p.h
index 232bfc863..34e72e859 100644
--- a/src/citra_qt/debugger/graphics_breakpoints_p.h
+++ b/src/citra_qt/debugger/graphics_breakpoints_p.h
@@ -25,7 +25,7 @@ public:
25 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; 25 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
26 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; 26 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
27 27
28 bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); 28 bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
29 29
30public slots: 30public slots:
31 void OnBreakPointHit(Pica::DebugContext::Event event); 31 void OnBreakPointHit(Pica::DebugContext::Event event);
diff --git a/src/citra_qt/debugger/profiler.cpp b/src/citra_qt/debugger/profiler.cpp
index ae0568b6a..2ac1748b7 100644
--- a/src/citra_qt/debugger/profiler.cpp
+++ b/src/citra_qt/debugger/profiler.cpp
@@ -26,7 +26,7 @@ static QVariant GetDataForColumn(int col, const AggregatedDuration& duration)
26static const TimingCategoryInfo* GetCategoryInfo(int id) 26static const TimingCategoryInfo* GetCategoryInfo(int id)
27{ 27{
28 const auto& categories = GetProfilingManager().GetTimingCategoriesInfo(); 28 const auto& categories = GetProfilingManager().GetTimingCategoriesInfo();
29 if (id >= categories.size()) { 29 if ((size_t)id >= categories.size()) {
30 return nullptr; 30 return nullptr;
31 } else { 31 } else {
32 return &categories[id]; 32 return &categories[id];
@@ -98,7 +98,7 @@ QVariant ProfilerModel::data(const QModelIndex& index, int role) const
98 const TimingCategoryInfo* info = GetCategoryInfo(index.row() - 2); 98 const TimingCategoryInfo* info = GetCategoryInfo(index.row() - 2);
99 return info != nullptr ? QString(info->name) : QVariant(); 99 return info != nullptr ? QString(info->name) : QVariant();
100 } else { 100 } else {
101 if (index.row() - 2 < results.time_per_category.size()) { 101 if (index.row() - 2 < (int)results.time_per_category.size()) {
102 return GetDataForColumn(index.column(), results.time_per_category[index.row() - 2]); 102 return GetDataForColumn(index.column(), results.time_per_category[index.row() - 2]);
103 } else { 103 } else {
104 return QVariant(); 104 return QVariant();
diff --git a/src/citra_qt/debugger/profiler.h b/src/citra_qt/debugger/profiler.h
index a6d87aa0f..fabf279b8 100644
--- a/src/citra_qt/debugger/profiler.h
+++ b/src/citra_qt/debugger/profiler.h
@@ -18,7 +18,7 @@ class ProfilerModel : public QAbstractItemModel
18public: 18public:
19 ProfilerModel(QObject* parent); 19 ProfilerModel(QObject* parent);
20 20
21 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 21 QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
22 QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; 22 QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
23 QModelIndex parent(const QModelIndex& child) const override; 23 QModelIndex parent(const QModelIndex& child) const override;
24 int columnCount(const QModelIndex& parent = QModelIndex()) const override; 24 int columnCount(const QModelIndex& parent = QModelIndex()) const override;
diff --git a/src/citra_qt/debugger/registers.cpp b/src/citra_qt/debugger/registers.cpp
index ab3666156..5527a2afd 100644
--- a/src/citra_qt/debugger/registers.cpp
+++ b/src/citra_qt/debugger/registers.cpp
@@ -7,8 +7,7 @@
7#include "core/core.h" 7#include "core/core.h"
8#include "core/arm/arm_interface.h" 8#include "core/arm/arm_interface.h"
9 9
10RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) 10RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
11{
12 cpu_regs_ui.setupUi(this); 11 cpu_regs_ui.setupUi(this);
13 12
14 tree = cpu_regs_ui.treeWidget; 13 tree = cpu_regs_ui.treeWidget;
@@ -18,8 +17,7 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent)
18 registers->setExpanded(true); 17 registers->setExpanded(true);
19 CSPR->setExpanded(true); 18 CSPR->setExpanded(true);
20 19
21 for (int i = 0; i < 16; ++i) 20 for (int i = 0; i < 16; ++i) {
22 {
23 QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i, 2, 10, QLatin1Char('0')))); 21 QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i, 2, 10, QLatin1Char('0'))));
24 registers->addChild(child); 22 registers->addChild(child);
25 } 23 }
@@ -39,12 +37,16 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent)
39 CSPR->addChild(new QTreeWidgetItem(QStringList("C"))); 37 CSPR->addChild(new QTreeWidgetItem(QStringList("C")));
40 CSPR->addChild(new QTreeWidgetItem(QStringList("Z"))); 38 CSPR->addChild(new QTreeWidgetItem(QStringList("Z")));
41 CSPR->addChild(new QTreeWidgetItem(QStringList("N"))); 39 CSPR->addChild(new QTreeWidgetItem(QStringList("N")));
40
41 setEnabled(false);
42} 42}
43 43
44void RegistersWidget::OnDebugModeEntered() 44void RegistersWidget::OnDebugModeEntered() {
45{
46 ARM_Interface* app_core = Core::g_app_core; 45 ARM_Interface* app_core = Core::g_app_core;
47 46
47 if (app_core == nullptr)
48 return;
49
48 for (int i = 0; i < 16; ++i) 50 for (int i = 0; i < 16; ++i)
49 registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetReg(i), 8, 16, QLatin1Char('0'))); 51 registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetReg(i), 8, 16, QLatin1Char('0')));
50 52
@@ -66,7 +68,22 @@ void RegistersWidget::OnDebugModeEntered()
66 CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than 68 CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than
67} 69}
68 70
69void RegistersWidget::OnDebugModeLeft() 71void RegistersWidget::OnDebugModeLeft() {
70{ 72}
73
74void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) {
75 setEnabled(true);
76}
77
78void RegistersWidget::OnEmulationStopping() {
79 // Reset widget text
80 for (int i = 0; i < 16; ++i)
81 registers->child(i)->setText(1, QString(""));
82
83 for (int i = 0; i < 15; ++i)
84 CSPR->child(i)->setText(1, QString(""));
85
86 CSPR->setText(1, QString(""));
71 87
88 setEnabled(false);
72} 89}
diff --git a/src/citra_qt/debugger/registers.h b/src/citra_qt/debugger/registers.h
index bf8955625..68e3fb908 100644
--- a/src/citra_qt/debugger/registers.h
+++ b/src/citra_qt/debugger/registers.h
@@ -8,6 +8,7 @@
8#include <QTreeWidgetItem> 8#include <QTreeWidgetItem>
9 9
10class QTreeWidget; 10class QTreeWidget;
11class EmuThread;
11 12
12class RegistersWidget : public QDockWidget 13class RegistersWidget : public QDockWidget
13{ 14{
@@ -20,6 +21,9 @@ public slots:
20 void OnDebugModeEntered(); 21 void OnDebugModeEntered();
21 void OnDebugModeLeft(); 22 void OnDebugModeLeft();
22 23
24 void OnEmulationStarting(EmuThread* emu_thread);
25 void OnEmulationStopping();
26
23private: 27private:
24 Ui::ARMRegisters cpu_regs_ui; 28 Ui::ARMRegisters cpu_regs_ui;
25 29
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);
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 9b57c5772..3e29534fb 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -5,12 +5,14 @@
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"
11 12
12class GImageInfo; 13class GImageInfo;
13class GRenderWindow; 14class GRenderWindow;
15class EmuThread;
14class ProfilerWidget; 16class ProfilerWidget;
15class DisassemblerWidget; 17class DisassemblerWidget;
16class RegistersWidget; 18class RegistersWidget;
@@ -34,8 +36,27 @@ public:
34 GMainWindow(); 36 GMainWindow();
35 ~GMainWindow(); 37 ~GMainWindow();
36 38
39signals:
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();
56
37private: 57private:
38 void BootGame(std::string filename); 58 void BootGame(std::string filename);
59 void ShutdownGame();
39 60
40 void closeEvent(QCloseEvent* event) override; 61 void closeEvent(QCloseEvent* event) override;
41 62
@@ -55,6 +76,8 @@ private:
55 76
56 GRenderWindow* render_window; 77 GRenderWindow* render_window;
57 78
79 std::unique_ptr<EmuThread> emu_thread;
80
58 ProfilerWidget* profilerWidget; 81 ProfilerWidget* profilerWidget;
59 DisassemblerWidget* disasmWidget; 82 DisassemblerWidget* disasmWidget;
60 RegistersWidget* registersWidget; 83 RegistersWidget* registersWidget;
diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui
index a3752ac1e..689806465 100644
--- a/src/citra_qt/main.ui
+++ b/src/citra_qt/main.ui
@@ -90,6 +90,9 @@
90 </property> 90 </property>
91 </action> 91 </action>
92 <action name="action_Start"> 92 <action name="action_Start">
93 <property name="enabled">
94 <bool>false</bool>
95 </property>
93 <property name="text"> 96 <property name="text">
94 <string>&amp;Start</string> 97 <string>&amp;Start</string>
95 </property> 98 </property>
diff --git a/src/citra_qt/util/spinbox.cpp b/src/citra_qt/util/spinbox.cpp
index 2e2076a27..de4060116 100644
--- a/src/citra_qt/util/spinbox.cpp
+++ b/src/citra_qt/util/spinbox.cpp
@@ -29,6 +29,7 @@
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 31
32#include <cstdlib>
32#include <QLineEdit> 33#include <QLineEdit>
33#include <QRegExpValidator> 34#include <QRegExpValidator>
34 35
@@ -206,7 +207,7 @@ QString CSpinBox::TextFromValue()
206{ 207{
207 return prefix 208 return prefix
208 + QString(HasSign() ? ((value < 0) ? "-" : "+") : "") 209 + QString(HasSign() ? ((value < 0) ? "-" : "+") : "")
209 + QString("%1").arg(abs(value), num_digits, base, QLatin1Char('0')).toUpper() 210 + QString("%1").arg(std::abs(value), num_digits, base, QLatin1Char('0')).toUpper()
210 + suffix; 211 + suffix;
211} 212}
212 213