mirror of https://github.com/mgba-emu/mgba.git
Qt: Add option to lock the maximum frame size (closes #1493)
This commit is contained in:
parent
be85200b3e
commit
d1a6e6b747
1
CHANGES
1
CHANGES
|
@ -1,5 +1,6 @@
|
|||
0.11.0: (Future)
|
||||
Features:
|
||||
- New option to lock the maximum frame size
|
||||
- Scripting: New `input` API for getting raw keyboard/mouse/controller state
|
||||
- Scripting: New `storage` API for saving data for a script, e.g. settings
|
||||
- Scripting: Debugger integration to allow for breakpoints and watchpoints
|
||||
|
|
|
@ -38,6 +38,8 @@ union mVideoBackendCommandData {
|
|||
struct {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned maxW;
|
||||
unsigned maxH;
|
||||
} u;
|
||||
const void* image;
|
||||
};
|
||||
|
|
|
@ -48,7 +48,7 @@ struct VideoBackend {
|
|||
void (*layerDimensions)(const struct VideoBackend*, enum VideoLayer, struct mRectangle*);
|
||||
void (*swap)(struct VideoBackend*);
|
||||
void (*clear)(struct VideoBackend*);
|
||||
void (*contextResized)(struct VideoBackend*, unsigned w, unsigned h);
|
||||
void (*contextResized)(struct VideoBackend*, unsigned w, unsigned h, unsigned maxW, unsigned maxH);
|
||||
void (*setImageSize)(struct VideoBackend*, enum VideoLayer, int w, int h);
|
||||
void (*imageSize)(struct VideoBackend*, enum VideoLayer, int* w, int* h);
|
||||
void (*setImage)(struct VideoBackend*, enum VideoLayer, const void* frame);
|
||||
|
|
|
@ -61,7 +61,7 @@ static void _mVideoProxyBackendClear(struct VideoBackend* v) {
|
|||
mVideoProxyBackendSubmit(proxy, &cmd, NULL);
|
||||
}
|
||||
|
||||
static void _mVideoProxyBackendContextResized(struct VideoBackend* v, unsigned w, unsigned h) {
|
||||
static void _mVideoProxyBackendContextResized(struct VideoBackend* v, unsigned w, unsigned h, unsigned maxW, unsigned maxH) {
|
||||
struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v;
|
||||
struct mVideoBackendCommand cmd = {
|
||||
.cmd = mVB_CMD_CONTEXT_RESIZED,
|
||||
|
@ -69,6 +69,8 @@ static void _mVideoProxyBackendContextResized(struct VideoBackend* v, unsigned w
|
|||
.u = {
|
||||
.width = w,
|
||||
.height = h,
|
||||
.maxW = maxW,
|
||||
.maxH = maxH,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -139,8 +141,8 @@ void mVideoProxyBackendInit(struct mVideoProxyBackend* proxy, struct VideoBacken
|
|||
proxy->d.drawFrame = _mVideoProxyBackendDrawFrame;
|
||||
proxy->backend = backend;
|
||||
|
||||
RingFIFOInit(&proxy->in, 0x400);
|
||||
RingFIFOInit(&proxy->out, 0x400);
|
||||
RingFIFOInit(&proxy->in, sizeof(union mVideoBackendCommandData) * 0x80);
|
||||
RingFIFOInit(&proxy->out, sizeof(union mVideoBackendCommandData) * 0x80);
|
||||
MutexInit(&proxy->inLock);
|
||||
MutexInit(&proxy->outLock);
|
||||
ConditionInit(&proxy->inWait);
|
||||
|
@ -209,7 +211,7 @@ bool mVideoProxyBackendRun(struct mVideoProxyBackend* proxy, bool block) {
|
|||
proxy->backend->clear(proxy->backend);
|
||||
break;
|
||||
case mVB_CMD_CONTEXT_RESIZED:
|
||||
proxy->backend->contextResized(proxy->backend, cmd.data.u.width, cmd.data.u.height);
|
||||
proxy->backend->contextResized(proxy->backend, cmd.data.u.width, cmd.data.u.height, cmd.data.u.maxW, cmd.data.u.maxH);
|
||||
break;
|
||||
case mVB_CMD_SET_IMAGE_SIZE:
|
||||
proxy->backend->setImageSize(proxy->backend, cmd.layer, cmd.data.s.width, cmd.data.s.height);
|
||||
|
|
|
@ -103,20 +103,29 @@ static void mGLContextDeinit(struct VideoBackend* v) {
|
|||
glDeleteTextures(VIDEO_LAYER_MAX, context->layers);
|
||||
}
|
||||
|
||||
static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h) {
|
||||
static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h, unsigned maxW, unsigned maxH) {
|
||||
unsigned drawW = w;
|
||||
unsigned drawH = h;
|
||||
|
||||
unsigned maxW;
|
||||
unsigned maxH;
|
||||
VideoBackendGetFrameSize(v, &maxW, &maxH);
|
||||
|
||||
if (maxW && drawW > maxW) {
|
||||
drawW = maxW;
|
||||
}
|
||||
|
||||
if (maxH && drawH > maxH) {
|
||||
drawH = maxH;
|
||||
}
|
||||
|
||||
unsigned lockW;
|
||||
unsigned lockH;
|
||||
VideoBackendGetFrameSize(v, &lockW, &lockH);
|
||||
|
||||
if (v->lockAspectRatio) {
|
||||
lockAspectRatioUInt(maxW, maxH, &drawW, &drawH);
|
||||
lockAspectRatioUInt(lockW, lockH, &drawW, &drawH);
|
||||
}
|
||||
if (v->lockIntegerScaling) {
|
||||
lockIntegerRatioUInt(maxW, &drawW);
|
||||
lockIntegerRatioUInt(maxH, &drawH);
|
||||
lockIntegerRatioUInt(lockW, &drawW);
|
||||
lockIntegerRatioUInt(lockH, &drawH);
|
||||
}
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
|
|
@ -271,20 +271,28 @@ static void mGLES2ContextDeinit(struct VideoBackend* v) {
|
|||
free(context->initialShader.uniforms);
|
||||
}
|
||||
|
||||
static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h) {
|
||||
static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h, unsigned maxW, unsigned maxH) {
|
||||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||
unsigned drawW = w;
|
||||
unsigned drawH = h;
|
||||
|
||||
unsigned maxW = context->width;
|
||||
unsigned maxH = context->height;
|
||||
if (maxW && drawW > maxW) {
|
||||
drawW = maxW;
|
||||
}
|
||||
|
||||
if (maxH && drawH > maxH) {
|
||||
drawH = maxH;
|
||||
}
|
||||
|
||||
unsigned lockW = context->width;
|
||||
unsigned lockH = context->height;
|
||||
|
||||
if (v->lockAspectRatio) {
|
||||
lockAspectRatioUInt(maxW, maxH, &drawW, &drawH);
|
||||
lockAspectRatioUInt(lockW, lockH, &drawW, &drawH);
|
||||
}
|
||||
if (v->lockIntegerScaling) {
|
||||
lockIntegerRatioUInt(maxW, &drawW);
|
||||
lockIntegerRatioUInt(maxH, &drawH);
|
||||
lockIntegerRatioUInt(lockW, &drawW);
|
||||
lockIntegerRatioUInt(lockH, &drawH);
|
||||
}
|
||||
size_t n;
|
||||
for (n = 0; n < context->nShaders; ++n) {
|
||||
|
|
|
@ -57,6 +57,7 @@ public:
|
|||
virtual void setVideoScale(int) {}
|
||||
virtual void setBackgroundImage(const QImage&) = 0;
|
||||
virtual QSize contentSize() const = 0;
|
||||
virtual void setMaximumSize(const QSize& size) = 0;
|
||||
|
||||
virtual void setVideoProxy(std::shared_ptr<VideoProxy> proxy) { m_videoProxy = std::move(proxy); }
|
||||
std::shared_ptr<VideoProxy> videoProxy() { return m_videoProxy; }
|
||||
|
@ -105,6 +106,7 @@ private:
|
|||
bool m_filter = false;
|
||||
QTimer m_mouseTimer;
|
||||
std::shared_ptr<VideoProxy> m_videoProxy;
|
||||
QSize m_maxSize;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -524,6 +524,10 @@ int DisplayGL::framebufferHandle() {
|
|||
return m_painter->glTex();
|
||||
}
|
||||
|
||||
void DisplayGL::setMaximumSize(const QSize& size) {
|
||||
QMetaObject::invokeMethod(m_painter.get(), "setMaximumSize", Q_ARG(const QSize&, size));
|
||||
}
|
||||
|
||||
PainterGL::PainterGL(QWindow* window, mGLWidget* widget, const QSurfaceFormat& format)
|
||||
: m_window(window)
|
||||
, m_format(format)
|
||||
|
@ -720,6 +724,11 @@ void PainterGL::resize(const QSize& size) {
|
|||
}
|
||||
}
|
||||
|
||||
void PainterGL::setMaximumSize(const QSize& size) {
|
||||
m_maxSize = size;
|
||||
resizeContext();
|
||||
}
|
||||
|
||||
void PainterGL::lockAspectRatio(bool lock) {
|
||||
m_backend->lockAspectRatio = lock;
|
||||
resize(m_size);
|
||||
|
@ -911,7 +920,11 @@ void PainterGL::unpause() {
|
|||
|
||||
void PainterGL::performDraw() {
|
||||
float r = m_window->devicePixelRatio();
|
||||
m_backend->contextResized(m_backend, m_size.width() * r, m_size.height() * r);
|
||||
QSize maxSize = m_maxSize;
|
||||
if (!maxSize.isValid()) {
|
||||
maxSize = QSize(0, 0);
|
||||
}
|
||||
m_backend->contextResized(m_backend, m_size.width() * r, m_size.height() * r, maxSize.width() * r, maxSize.height() * r);
|
||||
if (m_buffer) {
|
||||
m_backend->setImage(m_backend, VIDEO_LAYER_IMAGE, m_buffer);
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ public:
|
|||
void setVideoProxy(std::shared_ptr<VideoProxy>) override;
|
||||
int framebufferHandle() override;
|
||||
QSize contentSize() const override { return m_cachedContentSize; }
|
||||
void setMaximumSize(const QSize& size) override;
|
||||
|
||||
static bool highestCompatible(QSurfaceFormat&);
|
||||
static bool supportsFormat(const QSurfaceFormat&);
|
||||
|
@ -172,6 +173,7 @@ public slots:
|
|||
void pause();
|
||||
void unpause();
|
||||
void resize(const QSize& size);
|
||||
void setMaximumSize(const QSize& size);
|
||||
void lockAspectRatio(bool lock);
|
||||
void lockIntegerScaling(bool lock);
|
||||
void interframeBlending(bool enable);
|
||||
|
@ -230,6 +232,7 @@ private:
|
|||
VideoBackend* m_backend = nullptr;
|
||||
QSize m_size;
|
||||
QSize m_dims;
|
||||
QSize m_maxSize;
|
||||
MessagePainter* m_messagePainter = nullptr;
|
||||
QElapsedTimer m_delayTimer;
|
||||
std::shared_ptr<VideoProxy> m_videoProxy;
|
||||
|
|
|
@ -134,7 +134,20 @@ void DisplayQt::paintEvent(QPaintEvent*) {
|
|||
if (!drawSize.isValid() || drawSize.width() < 1 || drawSize.height() < 1) {
|
||||
return;
|
||||
}
|
||||
QRect full(clampSize(contentSize(), size(), isAspectRatioLocked(), isIntegerScalingLocked()));
|
||||
QSize usedSize = size();
|
||||
QPoint screenOrigin(0, 0);
|
||||
if (m_maxSize.isValid()) {
|
||||
if (m_maxSize.width() < usedSize.width()) {
|
||||
screenOrigin.setX((usedSize.width() - m_maxSize.width()) / 2);
|
||||
usedSize.setWidth(m_maxSize.width());
|
||||
}
|
||||
if (m_maxSize.height() < usedSize.height()) {
|
||||
screenOrigin.setY((usedSize.height() - m_maxSize.height()) / 2);
|
||||
usedSize.setHeight(m_maxSize.height());
|
||||
}
|
||||
}
|
||||
QRect full(clampSize(contentSize(), usedSize, isAspectRatioLocked(), isIntegerScalingLocked()));
|
||||
full.translate(screenOrigin);
|
||||
painter.save();
|
||||
painter.translate(full.topLeft());
|
||||
painter.scale(full.width() / static_cast<qreal>(frame.width), full.height() / static_cast<qreal>(frame.height));
|
||||
|
@ -220,7 +233,7 @@ void DisplayQt::swap(struct VideoBackend*) {
|
|||
void DisplayQt::clear(struct VideoBackend*) {
|
||||
}
|
||||
|
||||
void DisplayQt::contextResized(struct VideoBackend*, unsigned, unsigned) {
|
||||
void DisplayQt::contextResized(struct VideoBackend*, unsigned, unsigned, unsigned, unsigned) {
|
||||
}
|
||||
|
||||
void DisplayQt::setImageSize(struct VideoBackend* v, enum VideoLayer layer, int w, int h) {
|
||||
|
|
|
@ -27,6 +27,8 @@ public:
|
|||
VideoShader* shaders() override { return nullptr; }
|
||||
QSize contentSize() const override;
|
||||
VideoBackend* videoBackend() override { return &m_backend; }
|
||||
void setMaximumSize(const QSize& size) override { m_maxSize = size; }
|
||||
|
||||
|
||||
public slots:
|
||||
void stopDrawing() override;
|
||||
|
@ -56,7 +58,7 @@ private:
|
|||
static void layerDimensions(const struct VideoBackend*, enum VideoLayer, struct mRectangle*);
|
||||
static void swap(struct VideoBackend*);
|
||||
static void clear(struct VideoBackend*);
|
||||
static void contextResized(struct VideoBackend*, unsigned w, unsigned h);
|
||||
static void contextResized(struct VideoBackend*, unsigned w, unsigned h, unsigned maxW, unsigned maxH);
|
||||
static void setImageSize(struct VideoBackend*, enum VideoLayer, int w, int h);
|
||||
static void imageSize(struct VideoBackend*, enum VideoLayer, int* w, int* h);
|
||||
static void setImage(struct VideoBackend*, enum VideoLayer, const void* frame);
|
||||
|
@ -69,6 +71,7 @@ private:
|
|||
int m_width = -1;
|
||||
int m_height = -1;
|
||||
QImage m_oldBacking{nullptr};
|
||||
QSize m_maxSize;
|
||||
std::shared_ptr<CoreController> m_context = nullptr;
|
||||
};
|
||||
|
||||
|
|
|
@ -252,6 +252,9 @@ void Window::argumentsPassed() {
|
|||
|
||||
void Window::resizeFrame(const QSize& size) {
|
||||
QSize newSize(size);
|
||||
if (!m_config->getOption("lockFrameSize").toInt()) {
|
||||
m_savedSize = size;
|
||||
}
|
||||
if (windowHandle()) {
|
||||
QRect geom = windowHandle()->screen()->availableGeometry();
|
||||
if (newSize.width() > geom.width()) {
|
||||
|
@ -1522,7 +1525,10 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
for (int i = 1; i <= 8; ++i) {
|
||||
auto setSize = m_actions.addAction(tr("%1×").arg(QString::number(i)), QString("frame.%1x").arg(QString::number(i)), [this, i]() {
|
||||
auto setSize = m_frameSizes[i];
|
||||
bool lockFrameSize = m_config->getOption("lockFrameSize").toInt();
|
||||
if (!lockFrameSize) {
|
||||
showNormal();
|
||||
}
|
||||
#if defined(M_CORE_GBA)
|
||||
QSize minimumSize = QSize(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
|
||||
#elif defined(M_CORE_GB)
|
||||
|
@ -1538,7 +1544,11 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
size *= i;
|
||||
m_savedScale = i;
|
||||
m_config->setOption("scaleMultiplier", i); // TODO: Port to other
|
||||
m_savedSize = size;
|
||||
resizeFrame(size);
|
||||
if (lockFrameSize) {
|
||||
m_display->setMaximumSize(size);
|
||||
}
|
||||
setSize->setActive(true);
|
||||
}, "frame");
|
||||
setSize->setExclusive(true);
|
||||
|
@ -1553,8 +1563,22 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
#else
|
||||
fullscreenKeys = QKeySequence("Ctrl+F");
|
||||
#endif
|
||||
m_actions.addSeparator("frame");
|
||||
m_actions.addAction(tr("Toggle fullscreen"), "fullscreen", this, &Window::toggleFullScreen, "frame", fullscreenKeys);
|
||||
|
||||
ConfigOption* lockFrameSize = m_config->addOption("lockFrameSize");
|
||||
lockFrameSize->addBoolean(tr("&Lock frame size"), &m_actions, "frame");
|
||||
lockFrameSize->connect([this](const QVariant& value) {
|
||||
if (m_display) {
|
||||
if (value.toBool()) {
|
||||
m_display->setMaximumSize(m_display->size());
|
||||
} else {
|
||||
m_display->setMaximumSize({});
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
m_config->updateOption("lockFrameSize");
|
||||
|
||||
ConfigOption* lockAspectRatio = m_config->addOption("lockAspectRatio");
|
||||
lockAspectRatio->addBoolean(tr("Lock aspect ratio"), &m_actions, "av");
|
||||
lockAspectRatio->connect([this](const QVariant& value) {
|
||||
|
@ -2216,6 +2240,11 @@ void Window::setController(CoreController* controller, const QString& fname) {
|
|||
void Window::attachDisplay() {
|
||||
m_display->attach(m_controller);
|
||||
connect(m_display.get(), &QGBA::Display::drawingStarted, this, &Window::changeRenderer);
|
||||
if (m_config->getOption("lockFrameSize").toInt()) {
|
||||
m_display->setMaximumSize(m_savedSize);
|
||||
} else {
|
||||
m_display->setMaximumSize({});
|
||||
}
|
||||
m_display->startDrawing(m_controller);
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
|
|
@ -194,6 +194,7 @@ private:
|
|||
|
||||
std::unique_ptr<QGBA::Display> m_display;
|
||||
QSize m_initialSize;
|
||||
QSize m_savedSize;
|
||||
int m_savedScale;
|
||||
|
||||
// TODO: Move these to a new class
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#endif
|
||||
|
||||
void mSDLGLDoViewport(int w, int h, struct VideoBackend* v) {
|
||||
v->contextResized(v, w, h);
|
||||
v->contextResized(v, w, h, 0, 0);
|
||||
v->clear(v);
|
||||
v->swap(v);
|
||||
v->clear(v);
|
||||
|
|
Loading…
Reference in New Issue