From d1a6e6b747990356754d988e65842eaa02ab9e0c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 14 Apr 2024 20:39:58 -0700 Subject: [PATCH] Qt: Add option to lock the maximum frame size (closes #1493) --- CHANGES | 1 + include/mgba/feature/proxy-backend.h | 2 ++ include/mgba/feature/video-backend.h | 2 +- src/feature/proxy-backend.c | 10 +++++---- src/platform/opengl/gl.c | 23 ++++++++++++++------- src/platform/opengl/gles2.c | 20 ++++++++++++------ src/platform/qt/Display.h | 2 ++ src/platform/qt/DisplayGL.cpp | 15 +++++++++++++- src/platform/qt/DisplayGL.h | 3 +++ src/platform/qt/DisplayQt.cpp | 17 +++++++++++++-- src/platform/qt/DisplayQt.h | 5 ++++- src/platform/qt/Window.cpp | 31 +++++++++++++++++++++++++++- src/platform/qt/Window.h | 1 + src/platform/sdl/gl-common.c | 2 +- 14 files changed, 110 insertions(+), 24 deletions(-) diff --git a/CHANGES b/CHANGES index e08179d72..6249e67d8 100644 --- a/CHANGES +++ b/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 diff --git a/include/mgba/feature/proxy-backend.h b/include/mgba/feature/proxy-backend.h index 8c0b9e427..ffd38eb47 100644 --- a/include/mgba/feature/proxy-backend.h +++ b/include/mgba/feature/proxy-backend.h @@ -38,6 +38,8 @@ union mVideoBackendCommandData { struct { unsigned width; unsigned height; + unsigned maxW; + unsigned maxH; } u; const void* image; }; diff --git a/include/mgba/feature/video-backend.h b/include/mgba/feature/video-backend.h index adf6f8395..f01e656c9 100644 --- a/include/mgba/feature/video-backend.h +++ b/include/mgba/feature/video-backend.h @@ -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); diff --git a/src/feature/proxy-backend.c b/src/feature/proxy-backend.c index 3cfe7c39d..540034227 100644 --- a/src/feature/proxy-backend.c +++ b/src/feature/proxy-backend.c @@ -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); diff --git a/src/platform/opengl/gl.c b/src/platform/opengl/gl.c index fe24d04cf..b0cef1f0d 100644 --- a/src/platform/opengl/gl.c +++ b/src/platform/opengl/gl.c @@ -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(); diff --git a/src/platform/opengl/gles2.c b/src/platform/opengl/gles2.c index 51180813e..6350bd287 100644 --- a/src/platform/opengl/gles2.c +++ b/src/platform/opengl/gles2.c @@ -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) { diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index ff6f0c23b..bb16b268e 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -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 proxy) { m_videoProxy = std::move(proxy); } std::shared_ptr videoProxy() { return m_videoProxy; } @@ -105,6 +106,7 @@ private: bool m_filter = false; QTimer m_mouseTimer; std::shared_ptr m_videoProxy; + QSize m_maxSize; }; } diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 3a4c3ffdd..5b60eb46e 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -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); } diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 71e2de597..5b0aab2cd 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -95,6 +95,7 @@ public: void setVideoProxy(std::shared_ptr) 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 m_videoProxy; diff --git a/src/platform/qt/DisplayQt.cpp b/src/platform/qt/DisplayQt.cpp index db84c2689..3723a8445 100644 --- a/src/platform/qt/DisplayQt.cpp +++ b/src/platform/qt/DisplayQt.cpp @@ -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(frame.width), full.height() / static_cast(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) { diff --git a/src/platform/qt/DisplayQt.h b/src/platform/qt/DisplayQt.h index c1363dede..a6816a18b 100644 --- a/src/platform/qt/DisplayQt.h +++ b/src/platform/qt/DisplayQt.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 m_context = nullptr; }; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index fe2253367..bd52b0862 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -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]; - showNormal(); + 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 diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 4bcb6b7f9..f91e3ff30 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -194,6 +194,7 @@ private: std::unique_ptr m_display; QSize m_initialSize; + QSize m_savedSize; int m_savedScale; // TODO: Move these to a new class diff --git a/src/platform/sdl/gl-common.c b/src/platform/sdl/gl-common.c index 1ce9100e8..f2ccdd7e2 100644 --- a/src/platform/sdl/gl-common.c +++ b/src/platform/sdl/gl-common.c @@ -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);