From c6a0ab9792390fefa816ace846e3a76a85e8b4d5 Mon Sep 17 00:00:00 2001 From: James Rowe Date: Fri, 11 Jan 2019 21:06:34 -0700 Subject: [PATCH 1/6] QT Frontend: Migrate to QOpenGLWindow --- src/core/frontend/emu_window.h | 39 +++++++++++---- src/yuzu/bootmanager.cpp | 91 +++++++++++++++++++++++++++------- src/yuzu/bootmanager.h | 10 +++- src/yuzu/main.cpp | 3 +- 4 files changed, 113 insertions(+), 30 deletions(-) diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 7006a37b3e..75c2be4aec 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -12,6 +12,23 @@ namespace Core::Frontend { +/** + * Represents a graphics context that can be used for background computation or drawing. If the + * graphics backend doesn't require the context, then the implementation of these methods can be + * stubs + */ +class GraphicsContext { +public: + /// Makes the graphics context current for the caller thread + virtual void MakeCurrent() = 0; + + /// Releases (dunno if this is the "right" word) the context from the caller thread + virtual void DoneCurrent() = 0; + + /// Swap buffers to display the next frame + virtual void SwapBuffers() = 0; +}; + /** * Abstraction class used to provide an interface between emulation code and the frontend * (e.g. SDL, QGLWidget, GLFW, etc...). @@ -30,7 +47,7 @@ namespace Core::Frontend { * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please * re-read the upper points again and think about it if you don't see this. */ -class EmuWindow { +class EmuWindow : public GraphicsContext { public: /// Data structure to store emuwindow configuration struct WindowConfig { @@ -40,17 +57,21 @@ public: std::pair min_client_area_size; }; - /// Swap buffers to display the next frame - virtual void SwapBuffers() = 0; - /// Polls window events virtual void PollEvents() = 0; - /// Makes the graphics context current for the caller thread - virtual void MakeCurrent() = 0; - - /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread - virtual void DoneCurrent() = 0; + /** + * Returns a GraphicsContext that the frontend provides that is shared with the emu window. This + * context can be used from other threads for background graphics computation. If the frontend + * is using a graphics backend that doesn't need anything specific to run on a different thread, + * then it can use a stubbed implemenation for GraphicsContext. + * + * If the return value is null, then the core should assume that the frontend cannot provide a + * Shared Context + */ + virtual std::unique_ptr CreateSharedContext() const { + return nullptr; + } /** * Signal that a touch pressed event has occurred (e.g. mouse click pressed) diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index f74cb693a4..087ee8f93b 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -1,6 +1,13 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + #include #include #include +#include +#include +#include #include #include #include @@ -73,13 +80,36 @@ void EmuThread::run() { render_window->moveContext(); } +class GGLContext : public Core::Frontend::GraphicsContext { +public: + explicit GGLContext(QOpenGLContext* shared_context) : surface() { + context = std::make_unique(shared_context); + surface.setFormat(shared_context->format()); + surface.create(); + } + + void MakeCurrent() override { + context->makeCurrent(&surface); + } + + void DoneCurrent() override { + context->doneCurrent(); + } + + void SwapBuffers() override {} + +private: + std::unique_ptr context; + QOffscreenSurface surface; +}; + // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL // context. // The corresponding functionality is handled in EmuThread instead -class GGLWidgetInternal : public QGLWidget { +class GGLWidgetInternal : public QOpenGLWindow { public: - GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) - : QGLWidget(fmt, parent), parent(parent) {} + GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) + : QOpenGLWindow(shared_context), parent(parent) {} void paintEvent(QPaintEvent* ev) override { if (do_painting) { @@ -105,7 +135,7 @@ private: }; GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) - : QWidget(parent), child(nullptr), emu_thread(emu_thread) { + : QWidget(parent), child(nullptr), context(nullptr), emu_thread(emu_thread) { setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); @@ -129,19 +159,19 @@ void GRenderWindow::moveContext() { auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) ? emu_thread : qApp->thread(); - child->context()->moveToThread(thread); + context->moveToThread(thread); } void GRenderWindow::SwapBuffers() { - // In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`, + // In our multi-threaded QWidget use case we shouldn't need to call `makeCurrent`, // since we never call `doneCurrent` in this thread. // However: // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called // since the last time `swapBuffers` was executed; // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. - child->makeCurrent(); + context->makeCurrent(child); - child->swapBuffers(); + context->swapBuffers(child); if (!first_frame) { emit FirstFrameDisplayed(); first_frame = true; @@ -149,11 +179,11 @@ void GRenderWindow::SwapBuffers() { } void GRenderWindow::MakeCurrent() { - child->makeCurrent(); + context->makeCurrent(child); } void GRenderWindow::DoneCurrent() { - child->doneCurrent(); + context->doneCurrent(); } void GRenderWindow::PollEvents() {} @@ -173,7 +203,7 @@ void GRenderWindow::OnFramebufferSizeChanged() { } void GRenderWindow::BackupGeometry() { - geometry = ((QGLWidget*)this)->saveGeometry(); + geometry = ((QWidget*)this)->saveGeometry(); } void GRenderWindow::RestoreGeometry() { @@ -191,7 +221,7 @@ QByteArray GRenderWindow::saveGeometry() { // If we are a top-level widget, store the current geometry // otherwise, store the last backup if (parent() == nullptr) - return ((QGLWidget*)this)->saveGeometry(); + return ((QWidget*)this)->saveGeometry(); else return geometry; } @@ -305,7 +335,19 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { NotifyClientAreaSizeChanged(std::make_pair(width, height)); } +std::unique_ptr GRenderWindow::CreateSharedContext() const { + return std::make_unique(shared_context.get()); +} + void GRenderWindow::InitRenderTarget() { + if (shared_context) { + shared_context.reset(); + } + + if (context) { + context.reset(); + } + if (child) { delete child; } @@ -318,19 +360,28 @@ void GRenderWindow::InitRenderTarget() { // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, // WA_DontShowOnScreen, WA_DeleteOnClose - QGLFormat fmt; + QSurfaceFormat fmt; fmt.setVersion(4, 3); - fmt.setProfile(QGLFormat::CoreProfile); + fmt.setProfile(QSurfaceFormat::CoreProfile); + // TODO: expose a setting for buffer value (ie default/single/double/triple) + fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); + shared_context = std::make_unique(); + shared_context->setFormat(fmt); + shared_context->create(); + context = std::make_unique(); + context->setShareContext(shared_context.get()); + context->setFormat(fmt); + context->create(); fmt.setSwapInterval(false); - // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X - fmt.setOption(QGL::NoDeprecatedFunctions); - - child = new GGLWidgetInternal(fmt, this); + child = new GGLWidgetInternal(this, shared_context.get()); + QWidget* container = QWidget::createWindowContainer(child, this); QBoxLayout* layout = new QHBoxLayout(this); resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); - layout->addWidget(child); + child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); + container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); + layout->addWidget(container); layout->setMargin(0); setLayout(layout); @@ -340,6 +391,8 @@ void GRenderWindow::InitRenderTarget() { NotifyClientAreaSizeChanged(std::pair(child->width(), child->height())); BackupGeometry(); + // show causes the window to actually be created and the gl context as well + show(); } void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) { diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index d1f37e5032..d2a440d0d2 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -7,9 +7,9 @@ #include #include #include -#include #include #include +#include #include "common/thread.h" #include "core/core.h" #include "core/frontend/emu_window.h" @@ -21,6 +21,8 @@ class QTouchEvent; class GGLWidgetInternal; class GMainWindow; class GRenderWindow; +class QSurface; +class QOpenGLContext; class EmuThread : public QThread { Q_OBJECT @@ -115,6 +117,7 @@ public: void MakeCurrent() override; void DoneCurrent() override; void PollEvents() override; + std::unique_ptr CreateSharedContext() const override; void BackupGeometry(); void RestoreGeometry(); @@ -168,6 +171,11 @@ private: QByteArray geometry; EmuThread* emu_thread; + // Context that backs the GGLWidgetInternal (and will be used by core to render) + std::unique_ptr context; + // Context that will be shared between all newly created contexts. This should never be made + // current + std::unique_ptr shared_context; /// Temporary storage of the screenshot taken QImage screenshot_image; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 2c3e27c2e7..c8db7b907a 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2032,7 +2032,8 @@ int main(int argc, char* argv[]) { QCoreApplication::setOrganizationName("yuzu team"); QCoreApplication::setApplicationName("yuzu"); - QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); + // Enables the core to make the qt created contexts current on std::threads + QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); QApplication app(argc, argv); // Qt changes the locale and causes issues in float conversion using std::to_string() when From f2a2f818b6ec0fd4d70dced52d89f209eab9f187 Mon Sep 17 00:00:00 2001 From: James Rowe Date: Fri, 11 Jan 2019 23:03:52 -0700 Subject: [PATCH 2/6] SDL Frontend: Add shared context support --- src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | 37 ++++++++++++++++++++- src/yuzu_cmd/emu_window/emu_window_sdl2.h | 2 ++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index a557f28849..d246389faa 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -19,6 +19,37 @@ #include "input_common/sdl/sdl.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" +class SDLGLContext : public Core::Frontend::GraphicsContext { +public: + explicit SDLGLContext() { + // create a hidden window to make the shared context against + window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position + SDL_WINDOWPOS_UNDEFINED, // y position + Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, + SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + context = SDL_GL_CreateContext(window); + } + + ~SDLGLContext() { + SDL_GL_DeleteContext(context); + SDL_DestroyWindow(window); + } + + void MakeCurrent() override { + SDL_GL_MakeCurrent(window, context); + } + + void DoneCurrent() override { + SDL_GL_MakeCurrent(window, nullptr); + } + + void SwapBuffers() override {} + +private: + SDL_Window* window; + SDL_GLContext context; +}; + void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); InputCommon::GetMotionEmu()->Tilt(x, y); @@ -153,6 +184,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc); @@ -171,7 +203,6 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { if (fullscreen) { Fullscreen(); } - gl_context = SDL_GL_CreateContext(render_window); if (gl_context == nullptr) { @@ -280,3 +311,7 @@ void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); } + +std::unique_ptr EmuWindow_SDL2::CreateSharedContext() const { + return std::make_unique(); +} diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index b0d4116cc7..17e98227f4 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h @@ -27,6 +27,8 @@ public: /// Releases the GL context from the caller thread void DoneCurrent() override; + std::unique_ptr CreateSharedContext() const override; + /// Whether the window is still open, and a close request hasn't yet been sent bool IsOpen() const; From 5f2d9f282aa31c9658f044e7095d3ecbe12105eb Mon Sep 17 00:00:00 2001 From: James Rowe Date: Mon, 21 Jan 2019 16:21:44 -0700 Subject: [PATCH 3/6] QT: Hide GLWidget immediately after showing. With the loading screen merged, we don't want to actually show at this point, but it still needs to be shown to actually create the context. Turns out you can just show and hide it immediately and it'll work. --- src/yuzu/bootmanager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 087ee8f93b..ea55e68bab 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -393,6 +393,8 @@ void GRenderWindow::InitRenderTarget() { BackupGeometry(); // show causes the window to actually be created and the gl context as well show(); + // but we don't want the widget to be shown yet, so immediately hide it + hide(); } void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) { From 9ff72ca9f2147ff41101d60fb806357825d5aa53 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Mon, 25 Mar 2019 16:41:48 -0300 Subject: [PATCH 4/6] bootmanager: Delete container to avoid crash on game restarting While we are at it, remove nullptr checks for deletion, since the C++ standard defines that delete does it by its own --- src/yuzu/bootmanager.cpp | 21 ++++++++------------- src/yuzu/bootmanager.h | 3 ++- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index ea55e68bab..8445d9fe3a 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -340,21 +340,16 @@ std::unique_ptr GRenderWindow::CreateSharedCont } void GRenderWindow::InitRenderTarget() { - if (shared_context) { - shared_context.reset(); - } + shared_context.reset(); + context.reset(); - if (context) { - context.reset(); - } + delete child; + child = nullptr; - if (child) { - delete child; - } + delete container; + container = nullptr; - if (layout()) { - delete layout(); - } + delete layout(); first_frame = false; @@ -375,7 +370,7 @@ void GRenderWindow::InitRenderTarget() { fmt.setSwapInterval(false); child = new GGLWidgetInternal(this, shared_context.get()); - QWidget* container = QWidget::createWindowContainer(child, this); + container = QWidget::createWindowContainer(child, this); QBoxLayout* layout = new QHBoxLayout(this); resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index d2a440d0d2..c2f2fe87e9 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -166,7 +166,8 @@ private: void OnMinimalClientAreaChangeRequest( const std::pair& minimal_size) override; - GGLWidgetInternal* child; + QWidget* container = nullptr; + GGLWidgetInternal* child = nullptr; QByteArray geometry; From bbb396d7f1e4bd28fdd921af91b766d4504e5f4a Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Mon, 25 Mar 2019 16:46:30 -0300 Subject: [PATCH 5/6] bootmanager: Bypass resizing issue --- src/yuzu/bootmanager.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 8445d9fe3a..8b4e59eb39 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -371,14 +371,23 @@ void GRenderWindow::InitRenderTarget() { child = new GGLWidgetInternal(this, shared_context.get()); container = QWidget::createWindowContainer(child, this); + QBoxLayout* layout = new QHBoxLayout(this); + layout->addWidget(container); + layout->setMargin(0); + setLayout(layout); + + // Reset minimum size to avoid unwanted resizes when this function is called for a second time. + setMinimumSize(1, 1); + + // Show causes the window to actually be created and the OpenGL context as well, but we don't + // want the widget to be shown yet, so immediately hide it. + show(); + hide(); resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); - layout->addWidget(container); - layout->setMargin(0); - setLayout(layout); OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); @@ -386,10 +395,6 @@ void GRenderWindow::InitRenderTarget() { NotifyClientAreaSizeChanged(std::pair(child->width(), child->height())); BackupGeometry(); - // show causes the window to actually be created and the gl context as well - show(); - // but we don't want the widget to be shown yet, so immediately hide it - hide(); } void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) { From 9ebc27234d1f79516ea1a785c31551c26118997d Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Mon, 25 Mar 2019 17:01:11 -0300 Subject: [PATCH 6/6] bootmanager: Bypass input focus issues --- src/yuzu/bootmanager.cpp | 101 ++++++++++++++++++++++----------------- src/yuzu/bootmanager.h | 16 ++----- src/yuzu/main.cpp | 12 +++++ src/yuzu/main.h | 4 ++ 4 files changed, 78 insertions(+), 55 deletions(-) diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 8b4e59eb39..e1825e6072 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -122,9 +122,51 @@ public: parent->OnFramebufferSizeChanged(); } + void keyPressEvent(QKeyEvent* event) override { + InputCommon::GetKeyboard()->PressKey(event->key()); + } + + void keyReleaseEvent(QKeyEvent* event) override { + InputCommon::GetKeyboard()->ReleaseKey(event->key()); + } + + void mousePressEvent(QMouseEvent* event) override { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchBeginEvent + + const auto pos{event->pos()}; + if (event->button() == Qt::LeftButton) { + const auto [x, y] = parent->ScaleTouch(pos); + parent->TouchPressed(x, y); + } else if (event->button() == Qt::RightButton) { + InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); + } + } + + void mouseMoveEvent(QMouseEvent* event) override { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchUpdateEvent + + const auto pos{event->pos()}; + const auto [x, y] = parent->ScaleTouch(pos); + parent->TouchMoved(x, y); + InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); + } + + void mouseReleaseEvent(QMouseEvent* event) override { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchEndEvent + + if (event->button() == Qt::LeftButton) + parent->TouchReleased(); + else if (event->button() == Qt::RightButton) + InputCommon::GetMotionEmu()->EndTilt(); + } + void DisablePainting() { do_painting = false; } + void EnablePainting() { do_painting = true; } @@ -196,12 +238,24 @@ void GRenderWindow::PollEvents() {} void GRenderWindow::OnFramebufferSizeChanged() { // Screen changes potentially incur a change in screen DPI, hence we should update the // framebuffer size - qreal pixelRatio = windowPixelRatio(); + qreal pixelRatio = GetWindowPixelRatio(); unsigned width = child->QPaintDevice::width() * pixelRatio; unsigned height = child->QPaintDevice::height() * pixelRatio; UpdateCurrentFramebufferLayout(width, height); } +void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { + if (child) { + child->keyPressEvent(event); + } +} + +void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) { + if (child) { + child->keyReleaseEvent(event); + } +} + void GRenderWindow::BackupGeometry() { geometry = ((QWidget*)this)->saveGeometry(); } @@ -226,13 +280,13 @@ QByteArray GRenderWindow::saveGeometry() { return geometry; } -qreal GRenderWindow::windowPixelRatio() const { +qreal GRenderWindow::GetWindowPixelRatio() const { // windowHandle() might not be accessible until the window is displayed to screen. return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; } std::pair GRenderWindow::ScaleTouch(const QPointF pos) const { - const qreal pixel_ratio = windowPixelRatio(); + const qreal pixel_ratio = GetWindowPixelRatio(); return {static_cast(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), static_cast(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; } @@ -242,47 +296,6 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { QWidget::closeEvent(event); } -void GRenderWindow::keyPressEvent(QKeyEvent* event) { - InputCommon::GetKeyboard()->PressKey(event->key()); -} - -void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { - InputCommon::GetKeyboard()->ReleaseKey(event->key()); -} - -void GRenderWindow::mousePressEvent(QMouseEvent* event) { - if (event->source() == Qt::MouseEventSynthesizedBySystem) - return; // touch input is handled in TouchBeginEvent - - auto pos = event->pos(); - if (event->button() == Qt::LeftButton) { - const auto [x, y] = ScaleTouch(pos); - this->TouchPressed(x, y); - } else if (event->button() == Qt::RightButton) { - InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); - } -} - -void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { - if (event->source() == Qt::MouseEventSynthesizedBySystem) - return; // touch input is handled in TouchUpdateEvent - - auto pos = event->pos(); - const auto [x, y] = ScaleTouch(pos); - this->TouchMoved(x, y); - InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); -} - -void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { - if (event->source() == Qt::MouseEventSynthesizedBySystem) - return; // touch input is handled in TouchEndEvent - - if (event->button() == Qt::LeftButton) - this->TouchReleased(); - else if (event->button() == Qt::RightButton) - InputCommon::GetMotionEmu()->EndTilt(); -} - void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { // TouchBegin always has exactly one touch point, so take the .first() const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index c2f2fe87e9..288ce15726 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -119,24 +119,19 @@ public: void PollEvents() override; std::unique_ptr CreateSharedContext() const override; + void ForwardKeyPressEvent(QKeyEvent* event); + void ForwardKeyReleaseEvent(QKeyEvent* event); + void BackupGeometry(); void RestoreGeometry(); void restoreGeometry(const QByteArray& geometry); // overridden QByteArray saveGeometry(); // overridden - qreal windowPixelRatio() const; + qreal GetWindowPixelRatio() const; + std::pair ScaleTouch(const QPointF pos) const; void closeEvent(QCloseEvent* event) override; - - void keyPressEvent(QKeyEvent* event) override; - void keyReleaseEvent(QKeyEvent* event) override; - - void mousePressEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - bool event(QEvent* event) override; - void focusOutEvent(QFocusEvent* event) override; void OnClientAreaResized(unsigned width, unsigned height); @@ -158,7 +153,6 @@ signals: void FirstFrameDisplayed(); private: - std::pair ScaleTouch(const QPointF pos) const; void TouchBeginEvent(const QTouchEvent* event); void TouchUpdateEvent(const QTouchEvent* event); void TouchEndEvent(); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c8db7b907a..ae3b497095 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1965,6 +1965,18 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { event->acceptProposedAction(); } +void GMainWindow::keyPressEvent(QKeyEvent* event) { + if (render_window) { + render_window->ForwardKeyPressEvent(event); + } +} + +void GMainWindow::keyReleaseEvent(QKeyEvent* event) { + if (render_window) { + render_window->ForwardKeyReleaseEvent(event); + } +} + bool GMainWindow::ConfirmChangeGame() { if (emu_thread == nullptr) return true; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index e07c892cf2..0804849954 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -251,4 +251,8 @@ protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; + + // Overrides used to forward signals to the render window when the focus moves out. + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; };