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