src/platform/qt/Display.cpp (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "Display.h"
7
8#include <QApplication>
9#include <QResizeEvent>
10
11extern "C" {
12#include "gba-thread.h"
13}
14
15using namespace QGBA;
16
17static const GLint _glVertices[] = {
18 0, 0,
19 256, 0,
20 256, 256,
21 0, 256
22};
23
24static const GLint _glTexCoords[] = {
25 0, 0,
26 1, 0,
27 1, 1,
28 0, 1
29};
30
31Display::Display(QGLFormat format, QWidget* parent)
32 : QGLWidget(format, parent)
33 , m_painter(nullptr)
34 , m_drawThread(nullptr)
35{
36 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
37 setMinimumSize(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
38 setAutoBufferSwap(false);
39 setCursor(Qt::BlankCursor);
40}
41
42void Display::startDrawing(const uint32_t* buffer, GBAThread* thread) {
43 if (m_drawThread) {
44 return;
45 }
46 m_drawThread = new QThread(this);
47 m_painter = new Painter(this);
48 m_painter->setContext(thread);
49 m_painter->setBacking(buffer);
50 m_painter->moveToThread(m_drawThread);
51 m_context = thread;
52 doneCurrent();
53 context()->moveToThread(m_drawThread);
54 connect(m_drawThread, SIGNAL(started()), m_painter, SLOT(start()));
55 m_drawThread->start(QThread::TimeCriticalPriority);
56
57 lockAspectRatio(m_lockAspectRatio);
58}
59
60void Display::stopDrawing() {
61 if (m_drawThread) {
62 if (GBAThreadIsActive(m_context)) {
63 GBAThreadInterrupt(m_context);
64 GBASyncSuspendDrawing(&m_context->sync);
65 }
66 QMetaObject::invokeMethod(m_painter, "stop", Qt::BlockingQueuedConnection);
67 m_drawThread->exit();
68 m_drawThread = nullptr;
69 if (GBAThreadIsActive(m_context)) {
70 GBASyncResumeDrawing(&m_context->sync);
71 GBAThreadContinue(m_context);
72 }
73 }
74}
75
76void Display::pauseDrawing() {
77 if (m_drawThread) {
78 if (GBAThreadIsActive(m_context)) {
79 GBAThreadInterrupt(m_context);
80 GBASyncSuspendDrawing(&m_context->sync);
81 }
82 QMetaObject::invokeMethod(m_painter, "pause", Qt::BlockingQueuedConnection);
83 if (GBAThreadIsActive(m_context)) {
84 GBASyncResumeDrawing(&m_context->sync);
85 GBAThreadContinue(m_context);
86 }
87 }
88}
89
90void Display::unpauseDrawing() {
91 if (m_drawThread) {
92 if (GBAThreadIsActive(m_context)) {
93 GBAThreadInterrupt(m_context);
94 GBASyncSuspendDrawing(&m_context->sync);
95 }
96 QMetaObject::invokeMethod(m_painter, "unpause", Qt::BlockingQueuedConnection);
97 if (GBAThreadIsActive(m_context)) {
98 GBASyncResumeDrawing(&m_context->sync);
99 GBAThreadContinue(m_context);
100 }
101 }
102}
103
104void Display::forceDraw() {
105 if (m_drawThread) {
106 QMetaObject::invokeMethod(m_painter, "forceDraw", Qt::QueuedConnection);
107 }
108}
109
110void Display::lockAspectRatio(bool lock) {
111 m_lockAspectRatio = lock;
112 if (m_drawThread) {
113 QMetaObject::invokeMethod(m_painter, "lockAspectRatio", Qt::QueuedConnection, Q_ARG(bool, lock));
114 }
115}
116
117#ifdef USE_PNG
118void Display::screenshot() {
119 GBAThreadInterrupt(m_context);
120 GBAThreadTakeScreenshot(m_context);
121 GBAThreadContinue(m_context);
122}
123#endif
124
125void Display::initializeGL() {
126 glClearColor(0, 0, 0, 0);
127 glClear(GL_COLOR_BUFFER_BIT);
128 swapBuffers();
129}
130
131void Display::resizeEvent(QResizeEvent* event) {
132 if (m_drawThread) {
133 GBAThreadInterrupt(m_context);
134 GBASyncSuspendDrawing(&m_context->sync);
135 QMetaObject::invokeMethod(m_painter, "resize", Qt::BlockingQueuedConnection, Q_ARG(QSize, event->size()));
136 GBASyncResumeDrawing(&m_context->sync);
137 GBAThreadContinue(m_context);
138 }
139}
140
141Painter::Painter(Display* parent)
142 : m_gl(parent)
143 , m_lockAspectRatio(false)
144{
145 m_size = parent->size();
146}
147
148void Painter::setContext(GBAThread* context) {
149 m_context = context;
150}
151
152void Painter::setBacking(const uint32_t* backing) {
153 m_backing = backing;
154}
155
156void Painter::resize(const QSize& size) {
157 m_size = size;
158 forceDraw();
159}
160
161void Painter::lockAspectRatio(bool lock) {
162 m_lockAspectRatio = lock;
163 forceDraw();
164}
165
166void Painter::start() {
167 m_gl->makeCurrent();
168 glEnable(GL_TEXTURE_2D);
169 glGenTextures(1, &m_tex);
170 glBindTexture(GL_TEXTURE_2D, m_tex);
171 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
172 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
173 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
174 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
175 glEnableClientState(GL_VERTEX_ARRAY);
176 glVertexPointer(2, GL_INT, 0, _glVertices);
177 glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
178 glMatrixMode(GL_PROJECTION);
179 glLoadIdentity();
180 glOrtho(0, 240, 160, 0, 0, 1);
181 glMatrixMode(GL_MODELVIEW);
182 glLoadIdentity();
183 m_gl->doneCurrent();
184
185 m_drawTimer = new QTimer;
186 m_drawTimer->moveToThread(QThread::currentThread());
187 m_drawTimer->setInterval(0);
188 connect(m_drawTimer, SIGNAL(timeout()), this, SLOT(draw()));
189 m_drawTimer->start();
190}
191
192void Painter::draw() {
193 m_gl->makeCurrent();
194 if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip)) {
195 performDraw();
196 }
197 GBASyncWaitFrameEnd(&m_context->sync);
198 m_gl->swapBuffers();
199 m_gl->doneCurrent();
200}
201
202void Painter::forceDraw() {
203 m_gl->makeCurrent();
204 performDraw();
205 m_gl->swapBuffers();
206 m_gl->doneCurrent();
207}
208
209void Painter::stop() {
210 m_drawTimer->stop();
211 delete m_drawTimer;
212 m_gl->makeCurrent();
213 glDeleteTextures(1, &m_tex);
214 glClear(GL_COLOR_BUFFER_BIT);
215 m_gl->swapBuffers();
216 m_gl->doneCurrent();
217 m_gl->context()->moveToThread(QApplication::instance()->thread());
218}
219
220void Painter::pause() {
221 m_drawTimer->stop();
222 // Make sure both buffers are filled
223 forceDraw();
224 forceDraw();
225}
226
227void Painter::unpause() {
228 m_drawTimer->start();
229}
230
231void Painter::performDraw() {
232 glViewport(0, 0, m_size.width() * m_gl->devicePixelRatio(), m_size.height() * m_gl->devicePixelRatio());
233 glClear(GL_COLOR_BUFFER_BIT);
234 int w = m_size.width() * m_gl->devicePixelRatio();
235 int h = m_size.height() * m_gl->devicePixelRatio();
236 int drawW = w;
237 int drawH = h;
238 if (m_lockAspectRatio) {
239 if (w * 2 > h * 3) {
240 drawW = h * 3 / 2;
241 } else if (w * 2 < h * 3) {
242 drawH = w * 2 / 3;
243 }
244 }
245 glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
246 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_backing);
247 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
248 if (m_context->sync.videoFrameWait) {
249 glFlush();
250 }
251}