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