diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index b55fde93fe..53b60f7166 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -938,6 +938,7 @@ + @@ -1432,6 +1433,7 @@ + $(QTDIR)\bin\moc.exe;%(FullPath) diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 25d3f4565b..4000519b3f 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -99,6 +99,9 @@ {6992629a-48f1-43ec-8070-d1dba81bcbf9} + + {77ca7382-c296-43af-adb4-fb32f5ab4571} + @@ -587,6 +590,9 @@ Gui\misc dialogs + + Gui\utils + Generated Files\Release - LLVM @@ -679,6 +685,9 @@ Gui\game list + + Gui\utils + @@ -808,4 +817,4 @@ - \ No newline at end of file + diff --git a/rpcs3/rpcs3_app.cpp b/rpcs3/rpcs3_app.cpp index f838f37153..f8ffa178b2 100644 --- a/rpcs3/rpcs3_app.cpp +++ b/rpcs3/rpcs3_app.cpp @@ -1,5 +1,7 @@ #include "rpcs3_app.h" +#include "rpcs3qt/qt_utils.h" + #include "rpcs3qt/welcome_dialog.h" #include "Emu/System.h" @@ -167,37 +169,40 @@ void rpcs3_app::InitializeCallbacks() } bool disableMouse = guiSettings->GetValue(gui::gs_disableMouse).toBool(); + auto frame_geometry = gui::utils::create_centered_window_geometry(RPCS3MainWin->geometry(), w, h); + + gs_frame* frame; switch (video_renderer type = g_cfg.video.renderer) { case video_renderer::null: { - gs_frame* ret = new gs_frame("Null", w, h, RPCS3MainWin->GetAppIcon(), disableMouse); - gameWindow = ret; - return std::unique_ptr(ret); + frame = new gs_frame("Null", frame_geometry, RPCS3MainWin->GetAppIcon(), disableMouse); + break; } case video_renderer::opengl: { - gl_gs_frame* ret = new gl_gs_frame(w, h, RPCS3MainWin->GetAppIcon(), disableMouse); - gameWindow = ret; - return std::unique_ptr(ret); + frame = new gl_gs_frame(frame_geometry, RPCS3MainWin->GetAppIcon(), disableMouse); + break; } case video_renderer::vulkan: { - gs_frame* ret = new gs_frame("Vulkan", w, h, RPCS3MainWin->GetAppIcon(), disableMouse); - gameWindow = ret; - return std::unique_ptr(ret); + frame = new gs_frame("Vulkan", frame_geometry, RPCS3MainWin->GetAppIcon(), disableMouse); + break; } #ifdef _MSC_VER case video_renderer::dx12: { - gs_frame* ret = new gs_frame("DirectX 12", w, h, RPCS3MainWin->GetAppIcon(), disableMouse); - gameWindow = ret; - return std::unique_ptr(ret); + frame = new gs_frame("DirectX 12", frame_geometry, RPCS3MainWin->GetAppIcon(), disableMouse); + break; } #endif - default: fmt::throw_exception("Invalid video renderer: %s" HERE, type); + default: + fmt::throw_exception("Invalid video renderer: %s" HERE, type); } + + gameWindow = frame; + return std::unique_ptr(frame); }; callbacks.get_gs_render = []() -> std::shared_ptr diff --git a/rpcs3/rpcs3qt/gl_gs_frame.cpp b/rpcs3/rpcs3qt/gl_gs_frame.cpp index 2896700a35..f69e1baebc 100644 --- a/rpcs3/rpcs3qt/gl_gs_frame.cpp +++ b/rpcs3/rpcs3qt/gl_gs_frame.cpp @@ -5,8 +5,8 @@ #include #include -gl_gs_frame::gl_gs_frame(int w, int h, QIcon appIcon, bool disableMouse) - : gs_frame("OpenGL", w, h, appIcon, disableMouse) +gl_gs_frame::gl_gs_frame(const QRect& geometry, QIcon appIcon, bool disableMouse) + : gs_frame("OpenGL", geometry, appIcon, disableMouse) { setSurfaceType(QSurface::OpenGLSurface); diff --git a/rpcs3/rpcs3qt/gl_gs_frame.h b/rpcs3/rpcs3qt/gl_gs_frame.h index 6d01af4d80..c5ddfadc23 100644 --- a/rpcs3/rpcs3qt/gl_gs_frame.h +++ b/rpcs3/rpcs3qt/gl_gs_frame.h @@ -9,7 +9,7 @@ private: QSurfaceFormat m_format; public: - gl_gs_frame(int w, int h, QIcon appIcon, bool disableMouse); + gl_gs_frame(const QRect& geometry, QIcon appIcon, bool disableMouse); draw_context_t make_context() override; void set_current(draw_context_t context) override; diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 17361b26d8..e79ac79d69 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -23,7 +23,7 @@ constexpr auto qstr = QString::fromStdString; -gs_frame::gs_frame(const QString& title, int w, int h, QIcon appIcon, bool disableMouse) +gs_frame::gs_frame(const QString& title, const QRect& geometry, QIcon appIcon, bool disableMouse) : QWindow(), m_windowTitle(title), m_disable_mouse(disableMouse) { //Get version by substringing VersionNumber-buildnumber-commithash to get just the part before the dash @@ -55,8 +55,7 @@ gs_frame::gs_frame(const QString& title, int w, int h, QIcon appIcon, bool disab m_show_fps = static_cast(g_cfg.misc.show_fps_in_title); - resize(w, h); - + setGeometry(geometry); setTitle(m_windowTitle); setVisibility(Hidden); create(); @@ -70,6 +69,18 @@ void gs_frame::paintEvent(QPaintEvent *event) Q_UNUSED(event); } +void gs_frame::showEvent(QShowEvent *event) +{ + // we have to calculate new window positions, since the frame is only known once the window was created + // the left and right margins are too big on my setup for some reason yet unknown, so we'll have to ignore them + int x = geometry().left(); //std::max(geometry().left(), frameMargins().left()); + int y = std::max(geometry().top(), frameMargins().top()); + + setPosition(x, y); + + QWindow::showEvent(event); +} + void gs_frame::keyPressEvent(QKeyEvent *keyEvent) { auto l_handleKeyEvent = [this ,keyEvent]() diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index 1122c60459..7c581f16a7 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -9,6 +9,7 @@ class gs_frame : public QWindow, public GSFrameBase { Q_OBJECT + u64 m_frames = 0; QString m_windowTitle; bool m_show_fps; @@ -20,7 +21,7 @@ class gs_frame : public QWindow, public GSFrameBase bool m_minimized = false; public: - gs_frame(const QString& title, int w, int h, QIcon appIcon, bool disableMouse); + gs_frame(const QString& title, const QRect& geometry, QIcon appIcon, bool disableMouse); draw_context_t make_context() override; void set_current(draw_context_t context) override; @@ -29,6 +30,7 @@ public: wm_event get_default_wm_event() const override; protected: virtual void paintEvent(QPaintEvent *event); + virtual void showEvent(QShowEvent *event); void keyPressEvent(QKeyEvent *keyEvent) override; void OnFullScreen(); diff --git a/rpcs3/rpcs3qt/qt_utils.cpp b/rpcs3/rpcs3qt/qt_utils.cpp new file mode 100644 index 0000000000..ebd6030b88 --- /dev/null +++ b/rpcs3/rpcs3qt/qt_utils.cpp @@ -0,0 +1,37 @@ + +#include "qt_utils.h" +#include +#include + +namespace gui +{ + namespace utils + { + QRect create_centered_window_geometry(const QRect& origin, s32 width, s32 height) + { + // Get minimum virtual screen x & y for clamping the + // window x & y later while taking the width and height + // into account, so they don't go offscreen + s32 min_screen_x = std::numeric_limits::max(); + s32 max_screen_x = std::numeric_limits::min(); + s32 min_screen_y = std::numeric_limits::max(); + s32 max_screen_y = std::numeric_limits::min(); + for (auto screen : QApplication::screens()) + { + auto screen_geometry = screen->availableGeometry(); + min_screen_x = std::min(min_screen_x, screen_geometry.x()); + max_screen_x = std::max(max_screen_x, screen_geometry.x() + screen_geometry.width() - width); + min_screen_y = std::min(min_screen_y, screen_geometry.y()); + max_screen_y = std::max(max_screen_y, screen_geometry.y() + screen_geometry.height() - height); + } + + s32 frame_x_raw = origin.left() + ((origin.width() - width) / 2); + s32 frame_y_raw = origin.top() + ((origin.height() - height) / 2); + + s32 frame_x = std::clamp(frame_x_raw, min_screen_x, max_screen_x); + s32 frame_y = std::clamp(frame_y_raw, min_screen_y, max_screen_y); + + return QRect(frame_x, frame_y, width, height); + } + } // utils +} // gui diff --git a/rpcs3/rpcs3qt/qt_utils.h b/rpcs3/rpcs3qt/qt_utils.h new file mode 100644 index 0000000000..7ec6c05844 --- /dev/null +++ b/rpcs3/rpcs3qt/qt_utils.h @@ -0,0 +1,14 @@ +#pragma once + +#include "stdafx.h" +#include + +namespace gui +{ + namespace utils + { + // Creates a frame geometry rectangle with given width height that's centered inside the origin, + // while still considering screen boundaries. + QRect create_centered_window_geometry(const QRect& origin, s32 width, s32 height); + } // utils +} // gui diff --git a/rpcs3/stdafx.h b/rpcs3/stdafx.h index 764bd4d7b8..2d4658f645 100644 --- a/rpcs3/stdafx.h +++ b/rpcs3/stdafx.h @@ -42,5 +42,16 @@ namespace std { inline namespace literals { inline namespace chrono_literals {}} #include #include #include +#include using namespace std::literals; + +// Remove once we move to C++17 +namespace std +{ + template + constexpr const T clamp(const T value, const T min, const T max) + { + return value < min ? min : value > max ? max : value; + } +}