src/platform/qt/Window.cpp (view raw)
1#include "Window.h"
2
3#include <QFileDialog>
4#include <QKeyEvent>
5#include <QKeySequence>
6#include <QMenuBar>
7
8#include "GameController.h"
9#include "GDBWindow.h"
10#include "GDBController.h"
11#include "LoadSaveState.h"
12#include "LogView.h"
13
14using namespace QGBA;
15
16Window::Window(QWidget* parent)
17 : QMainWindow(parent)
18#ifdef USE_GDB_STUB
19 , m_gdbController(nullptr)
20#endif
21{
22 m_controller = new GameController(this);
23 m_logView = new LogView();
24
25 QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer));
26 format.setSwapInterval(1);
27 m_display = new Display(format);
28 setCentralWidget(m_display);
29 connect(m_controller, SIGNAL(gameStarted(GBAThread*)), this, SLOT(gameStarted(GBAThread*)));
30 connect(m_controller, SIGNAL(gameStopped(GBAThread*)), m_display, SLOT(stopDrawing()));
31 connect(m_controller, SIGNAL(gameStopped(GBAThread*)), this, SLOT(gameStopped()));
32 connect(m_controller, SIGNAL(stateLoaded(GBAThread*)), m_display, SLOT(forceDraw()));
33 connect(m_controller, SIGNAL(postLog(int, const QString&)), m_logView, SLOT(postLog(int, const QString&)));
34 connect(this, SIGNAL(startDrawing(const uint32_t*, GBAThread*)), m_display, SLOT(startDrawing(const uint32_t*, GBAThread*)), Qt::QueuedConnection);
35 connect(this, SIGNAL(shutdown()), m_display, SLOT(stopDrawing()));
36 connect(this, SIGNAL(shutdown()), m_controller, SLOT(closeGame()));
37 connect(this, SIGNAL(shutdown()), m_logView, SLOT(hide()));
38 connect(this, SIGNAL(audioBufferSamplesChanged(int)), m_controller, SLOT(setAudioBufferSamples(int)));
39 connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float)));
40
41 setupMenu(menuBar());
42}
43
44Window::~Window() {
45 delete m_logView;
46}
47
48GBAKey Window::mapKey(int qtKey) {
49 switch (qtKey) {
50 case Qt::Key_Z:
51 return GBA_KEY_A;
52 break;
53 case Qt::Key_X:
54 return GBA_KEY_B;
55 break;
56 case Qt::Key_A:
57 return GBA_KEY_L;
58 break;
59 case Qt::Key_S:
60 return GBA_KEY_R;
61 break;
62 case Qt::Key_Return:
63 return GBA_KEY_START;
64 break;
65 case Qt::Key_Backspace:
66 return GBA_KEY_SELECT;
67 break;
68 case Qt::Key_Up:
69 return GBA_KEY_UP;
70 break;
71 case Qt::Key_Down:
72 return GBA_KEY_DOWN;
73 break;
74 case Qt::Key_Left:
75 return GBA_KEY_LEFT;
76 break;
77 case Qt::Key_Right:
78 return GBA_KEY_RIGHT;
79 break;
80 default:
81 return GBA_KEY_NONE;
82 }
83}
84
85void Window::selectROM() {
86 QString filename = QFileDialog::getOpenFileName(this, tr("Select ROM"));
87 if (!filename.isEmpty()) {
88 m_controller->loadGame(filename);
89 }
90}
91
92#ifdef USE_GDB_STUB
93void Window::gdbOpen() {
94 if (!m_gdbController) {
95 m_gdbController = new GDBController(m_controller, this);
96 }
97 GDBWindow* window = new GDBWindow(m_gdbController);
98 window->show();
99}
100#endif
101
102void Window::keyPressEvent(QKeyEvent* event) {
103 if (event->isAutoRepeat()) {
104 QWidget::keyPressEvent(event);
105 return;
106 }
107 GBAKey key = mapKey(event->key());
108 if (key == GBA_KEY_NONE) {
109 QWidget::keyPressEvent(event);
110 return;
111 }
112 m_controller->keyPressed(key);
113 event->accept();
114}
115
116void Window::keyReleaseEvent(QKeyEvent* event) {
117 if (event->isAutoRepeat()) {
118 QWidget::keyReleaseEvent(event);
119 return;
120 }
121 GBAKey key = mapKey(event->key());
122 if (key == GBA_KEY_NONE) {
123 QWidget::keyPressEvent(event);
124 return;
125 }
126 m_controller->keyReleased(key);
127 event->accept();
128}
129
130void Window::closeEvent(QCloseEvent* event) {
131 emit shutdown();
132 QMainWindow::closeEvent(event);
133}
134
135void Window::gameStarted(GBAThread* context) {
136 emit startDrawing(m_controller->drawContext(), context);
137 foreach (QAction* action, m_gameActions) {
138 action->setDisabled(false);
139 }
140}
141
142void Window::gameStopped() {
143 foreach (QAction* action, m_gameActions) {
144 action->setDisabled(true);
145 }
146}
147
148void Window::openStateWindow(LoadSave ls) {
149 bool wasPaused = m_controller->isPaused();
150 LoadSaveState* window = new LoadSaveState(m_controller);
151 connect(this, SIGNAL(shutdown()), window, SLOT(hide()));
152 if (!wasPaused) {
153 m_controller->setPaused(true);
154 connect(window, &LoadSaveState::closed, [this]() { m_controller->setPaused(false); });
155 }
156 window->setAttribute(Qt::WA_DeleteOnClose);
157 window->setMode(ls);
158 window->show();
159}
160
161void Window::setupMenu(QMenuBar* menubar) {
162 menubar->clear();
163 QMenu* fileMenu = menubar->addMenu(tr("&File"));
164 fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open);
165
166 QMenu* emulationMenu = menubar->addMenu(tr("&Emulation"));
167 QAction* reset = new QAction(tr("&Reset"), emulationMenu);
168 reset->setShortcut(tr("Ctrl+R"));
169 connect(reset, SIGNAL(triggered()), m_controller, SLOT(reset()));
170 m_gameActions.append(reset);
171 emulationMenu->addAction(reset);
172
173 QAction* shutdown = new QAction(tr("Sh&utdown"), emulationMenu);
174 connect(shutdown, SIGNAL(triggered()), m_controller, SLOT(closeGame()));
175 m_gameActions.append(shutdown);
176 emulationMenu->addAction(shutdown);
177 emulationMenu->addSeparator();
178
179 QAction* loadState = new QAction(tr("&Load state"), emulationMenu);
180 loadState->setShortcut(tr("Ctrl+L"));
181 connect(loadState, &QAction::triggered, [this]() { this->openStateWindow(LoadSave::LOAD); });
182 m_gameActions.append(loadState);
183 emulationMenu->addAction(loadState);
184
185 QAction* saveState = new QAction(tr("&Save state"), emulationMenu);
186 saveState->setShortcut(tr("Ctrl+S"));
187 connect(saveState, &QAction::triggered, [this]() { this->openStateWindow(LoadSave::SAVE); });
188 m_gameActions.append(saveState);
189 emulationMenu->addAction(saveState);
190
191 QMenu* quickLoadMenu = emulationMenu->addMenu(tr("Quick load"));
192 QMenu* quickSaveMenu = emulationMenu->addMenu(tr("Quick save"));
193 int i;
194 for (i = 1; i < 10; ++i) {
195 QAction* quickLoad = new QAction(tr("State &%1").arg(i), quickLoadMenu);
196 quickLoad->setShortcut(tr("F%1").arg(i));
197 connect(quickLoad, &QAction::triggered, [this, i]() { m_controller->loadState(i); });
198 m_gameActions.append(quickLoad);
199 quickLoadMenu->addAction(quickLoad);
200
201 QAction* quickSave = new QAction(tr("State &%1").arg(i), quickSaveMenu);
202 quickSave->setShortcut(tr("Shift+F%1").arg(i));
203 connect(quickSave, &QAction::triggered, [this, i]() { m_controller->saveState(i); });
204 m_gameActions.append(quickSave);
205 quickSaveMenu->addAction(quickSave);
206 }
207
208 emulationMenu->addSeparator();
209
210 QAction* pause = new QAction(tr("&Pause"), emulationMenu);
211 pause->setChecked(false);
212 pause->setCheckable(true);
213 pause->setShortcut(tr("Ctrl+P"));
214 connect(pause, SIGNAL(triggered(bool)), m_controller, SLOT(setPaused(bool)));
215 connect(m_controller, &GameController::gamePaused, [pause]() { pause->setChecked(true); });
216 connect(m_controller, &GameController::gameUnpaused, [pause]() { pause->setChecked(false); });
217 m_gameActions.append(pause);
218 emulationMenu->addAction(pause);
219
220 QAction* frameAdvance = new QAction(tr("&Next frame"), emulationMenu);
221 frameAdvance->setShortcut(tr("Ctrl+N"));
222 connect(frameAdvance, SIGNAL(triggered()), m_controller, SLOT(frameAdvance()));
223 m_gameActions.append(frameAdvance);
224 emulationMenu->addAction(frameAdvance);
225
226 QMenu* target = emulationMenu->addMenu("FPS target");
227 QAction* setTarget = new QAction(tr("15"), emulationMenu);
228 connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(15); });
229 target->addAction(setTarget);
230 setTarget = new QAction(tr("30"), emulationMenu);
231 connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(30); });
232 target->addAction(setTarget);
233 setTarget = new QAction(tr("45"), emulationMenu);
234 connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(45); });
235 target->addAction(setTarget);
236 setTarget = new QAction(tr("60"), emulationMenu);
237 connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(60); });
238 target->addAction(setTarget);
239 setTarget = new QAction(tr("90"), emulationMenu);
240 connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(90); });
241 target->addAction(setTarget);
242 setTarget = new QAction(tr("120"), emulationMenu);
243 connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(120); });
244 target->addAction(setTarget);
245 setTarget = new QAction(tr("240"), emulationMenu);
246 connect(setTarget, &QAction::triggered, [this]() { emit fpsTargetChanged(240); });
247 target->addAction(setTarget);
248
249 QMenu* videoMenu = menubar->addMenu(tr("&Video"));
250 QMenu* frameMenu = videoMenu->addMenu(tr("Frame &size"));
251 QAction* setSize = new QAction(tr("1x"), videoMenu);
252 connect(setSize, &QAction::triggered, [this]() {
253 showNormal();
254 resize(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
255 });
256 frameMenu->addAction(setSize);
257 setSize = new QAction(tr("2x"), videoMenu);
258 connect(setSize, &QAction::triggered, [this]() {
259 showNormal();
260 resize(VIDEO_HORIZONTAL_PIXELS * 2, VIDEO_VERTICAL_PIXELS * 2);
261 });
262 frameMenu->addAction(setSize);
263 setSize = new QAction(tr("3x"), videoMenu);
264 connect(setSize, &QAction::triggered, [this]() {
265 showNormal();
266 resize(VIDEO_HORIZONTAL_PIXELS * 3, VIDEO_VERTICAL_PIXELS * 3);
267 });
268 frameMenu->addAction(setSize);
269 setSize = new QAction(tr("4x"), videoMenu);
270 connect(setSize, &QAction::triggered, [this]() {
271 showNormal();
272 resize(VIDEO_HORIZONTAL_PIXELS * 4, VIDEO_VERTICAL_PIXELS * 4);
273 });
274 frameMenu->addAction(setSize);
275 frameMenu->addAction(tr("Fullscreen"), this, SLOT(showFullScreen()), QKeySequence("Ctrl+F"));
276
277 QMenu* soundMenu = menubar->addMenu(tr("&Sound"));
278 QMenu* buffersMenu = soundMenu->addMenu(tr("Buffer &size"));
279 QAction* setBuffer = new QAction(tr("512"), buffersMenu);
280 connect(setBuffer, &QAction::triggered, [this]() { emit audioBufferSamplesChanged(512); });
281 buffersMenu->addAction(setBuffer);
282 setBuffer = new QAction(tr("1024"), buffersMenu);
283 connect(setBuffer, &QAction::triggered, [this]() { emit audioBufferSamplesChanged(1024); });
284 buffersMenu->addAction(setBuffer);
285 setBuffer = new QAction(tr("2048"), buffersMenu);
286 connect(setBuffer, &QAction::triggered, [this]() { emit audioBufferSamplesChanged(2048); });
287 buffersMenu->addAction(setBuffer);
288
289 QMenu* debuggingMenu = menubar->addMenu(tr("&Debugging"));
290 QAction* viewLogs = new QAction(tr("View &logs..."), debuggingMenu);
291 connect(viewLogs, SIGNAL(triggered()), m_logView, SLOT(show()));
292 debuggingMenu->addAction(viewLogs);
293#ifdef USE_GDB_STUB
294 QAction* gdbWindow = new QAction(tr("Start &GDB server..."), debuggingMenu);
295 connect(gdbWindow, SIGNAL(triggered()), this, SLOT(gdbOpen()));
296 debuggingMenu->addAction(gdbWindow);
297#endif
298
299 foreach (QAction* action, m_gameActions) {
300 action->setDisabled(true);
301 }
302}