From c8d933a14211ce6ee02659ecef758a1f303bc87f Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 13 Nov 2014 17:32:03 +0100 Subject: [PATCH 01/13] Add a GUI logging channel. Replace asserts with _dbg_assert_. --- src/common/log.h | 1 + src/common/log_manager.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/common/log.h b/src/common/log.h index bfd73f8a5..822cd21eb 100644 --- a/src/common/log.h +++ b/src/common/log.h @@ -69,6 +69,7 @@ enum LOG_TYPE { HW, TIME, NETPLAY, + GUI, NUMBER_OF_LOGS // Must be last }; diff --git a/src/common/log_manager.cpp b/src/common/log_manager.cpp index 4d590d98f..687f4e337 100644 --- a/src/common/log_manager.cpp +++ b/src/common/log_manager.cpp @@ -75,6 +75,7 @@ LogManager::LogManager() m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay"); m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager"); m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay"); + m_Log[LogTypes::GUI] = new LogContainer("GUI", "GUI"); m_fileLog = new FileLogListener(FileUtil::GetUserPath(F_MAINLOG_IDX).c_str()); m_consoleLog = new ConsoleListener(); From 221a9b023d8c9ca55c093823e9efd6d13d0a54a2 Mon Sep 17 00:00:00 2001 From: Kevin Hartman Date: Fri, 29 Aug 2014 22:23:12 -0700 Subject: [PATCH 02/13] Viewport scaling and display density independence The view is scaled to be as large as possible, without changing the aspect, within the bounds of the window. On "retina" displays, or other displays where window units != pixels, the view should no longer draw incorrectly. --- src/citra/emu_window/emu_window_glfw.cpp | 5 +++ src/citra/emu_window/emu_window_glfw.h | 5 ++- src/citra_qt/bootmanager.cpp | 24 +++++++++++++ src/citra_qt/bootmanager.hxx | 1 + src/common/emu_window.h | 7 ++-- .../renderer_opengl/renderer_opengl.cpp | 36 ++++++++++++++++++- .../renderer_opengl/renderer_opengl.h | 15 ++++++++ 7 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index 0c774bbc5..d0f6e9a9e 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp @@ -34,6 +34,10 @@ const bool EmuWindow_GLFW::IsOpen() { return glfwWindowShouldClose(m_render_window) == 0; } +void EmuWindow_GLFW::GetFramebufferSize(int* fbWidth, int* fbHeight) { + glfwGetFramebufferSize(m_render_window, fbWidth, fbHeight); +} + /// EmuWindow_GLFW constructor EmuWindow_GLFW::EmuWindow_GLFW() { keyboard_id = KeyMap::NewDeviceId(); @@ -64,6 +68,7 @@ EmuWindow_GLFW::EmuWindow_GLFW() { glfwSetWindowUserPointer(m_render_window, this); glfwSetKeyCallback(m_render_window, OnKeyEvent); + DoneCurrent(); } diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h index 7c3072145..e96228765 100644 --- a/src/citra/emu_window/emu_window_glfw.h +++ b/src/citra/emu_window/emu_window_glfw.h @@ -21,7 +21,7 @@ public: /// Makes the graphics context current for the caller thread void MakeCurrent() override; - + /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread void DoneCurrent() override; @@ -32,6 +32,9 @@ public: void ReloadSetKeymaps() override; + /// Gets the size of the window in pixels + void GetFramebufferSize(int* fbWidth, int* fbHeight); + private: GLFWwindow* m_render_window; ///< Internal GLFW render window diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 20824692d..516e115fd 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -2,6 +2,12 @@ #include #include +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) +// Required for screen DPI information +#include +#include +#endif + #include "common/common.h" #include "bootmanager.hxx" @@ -176,6 +182,24 @@ void GRenderWindow::PollEvents() { */ } +// On Qt 5.1+, this correctly gets the size of the framebuffer (pixels). +// +// Older versions get the window size (density independent pixels), +// and hence, do not support DPI scaling ("retina" displays). +// The result will be a viewport that is smaller than the extent of the window. +void GRenderWindow::GetFramebufferSize(int* fbWidth, int* fbHeight) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) + int pixelRatio = child->QPaintDevice::devicePixelRatio(); + + *fbWidth = child->QPaintDevice::width() * pixelRatio; + *fbHeight = child->QPaintDevice::height() * pixelRatio; +#else + *fbWidth = child->QPaintDevice::width(); + *fbHeight = child->QPaintDevice::height(); +#endif +} + void GRenderWindow::BackupGeometry() { geometry = ((QGLWidget*)this)->saveGeometry(); diff --git a/src/citra_qt/bootmanager.hxx b/src/citra_qt/bootmanager.hxx index f8afc403e..ec3e1fe71 100644 --- a/src/citra_qt/bootmanager.hxx +++ b/src/citra_qt/bootmanager.hxx @@ -96,6 +96,7 @@ public: void MakeCurrent() override; void DoneCurrent() override; void PollEvents() override; + void GetFramebufferSize(int* fbWidth, int* fbHeight) override; void BackupGeometry(); void RestoreGeometry(); diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 6c2b598f6..ba9d4fa76 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -49,8 +49,11 @@ public: void SetConfig(const WindowConfig& val) { m_config = val; } - - int GetClientAreaWidth() const { + + /// Gets the size of the window in pixels + virtual void GetFramebufferSize(int* fbWidth, int* fbHeight) = 0; + + int GetClientAreaWidth() const { return m_client_area_width; } diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 8483f79be..3757482db 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -191,7 +191,8 @@ void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x * Draws the emulated screens to the emulator window. */ void RendererOpenGL::DrawScreens() { - glViewport(0, 0, resolution_width, resolution_height); + UpdateViewportExtent(); + glViewport(viewport_extent.x, viewport_extent.y, viewport_extent.width, viewport_extent.height); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program_id); @@ -228,6 +229,39 @@ void RendererOpenGL::SetWindow(EmuWindow* window) { render_window = window; } +void RendererOpenGL::UpdateViewportExtent() { + int width_in_pixels; + int height_in_pixels; + + render_window->GetFramebufferSize(&width_in_pixels, &height_in_pixels); + + // No update needed if framebuffer size hasn't changed + if (width_in_pixels == framebuffer_size.width && height_in_pixels == framebuffer_size.height) { + return; + } + + framebuffer_size.width = width_in_pixels; + framebuffer_size.height = height_in_pixels; + + float window_aspect_ratio = static_cast(height_in_pixels) / width_in_pixels; + float emulation_aspect_ratio = static_cast(resolution_height) / resolution_width; + + if (window_aspect_ratio > emulation_aspect_ratio) { + // If the window is more narrow than the emulation content, borders are applied on the + // top and bottom of the window. + viewport_extent.width = width_in_pixels; + viewport_extent.height = emulation_aspect_ratio * viewport_extent.width; + viewport_extent.x = 0; + viewport_extent.y = (height_in_pixels - viewport_extent.height) / 2; + } else { + // Otherwise, borders are applied on the left and right sides of the window. + viewport_extent.height = height_in_pixels; + viewport_extent.width = (1 / emulation_aspect_ratio) * viewport_extent.height; + viewport_extent.x = (width_in_pixels - viewport_extent.width) / 2; + viewport_extent.y = 0; + } +} + /// Initialize the renderer void RendererOpenGL::Init() { render_window->MakeCurrent(); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index eed201a95..d440e2bc7 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -52,12 +52,27 @@ private: static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, const TextureInfo& texture); + /// Updates the viewport rectangle + void UpdateViewportExtent(); + EmuWindow* render_window; ///< Handle to render window u32 last_mode; ///< Last render mode int resolution_width; ///< Current resolution width int resolution_height; ///< Current resolution height + struct { + int width; + int height; + } framebuffer_size; ///< Current framebuffer size + + struct { + int x; + int y; + int width; + int height; + } viewport_extent; ///< Current viewport rectangle + // OpenGL object IDs GLuint vertex_array_handle; GLuint vertex_buffer_handle; From bd8f491e4c08e9b9a7b852de0b50c144da8ac8c8 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sun, 12 Oct 2014 18:14:57 +0200 Subject: [PATCH 03/13] Fixup EmuWindow interface and implementations thereof. --- src/citra/emu_window/emu_window_glfw.cpp | 53 +++++++++++----- src/citra/emu_window/emu_window_glfw.h | 13 ++-- src/citra_qt/bootmanager.cpp | 53 ++++++++++------ src/citra_qt/bootmanager.hxx | 19 ++++-- src/common/emu_window.h | 61 ++++++++++--------- .../renderer_opengl/renderer_opengl.cpp | 6 +- 6 files changed, 128 insertions(+), 77 deletions(-) diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index d0f6e9a9e..28aa3450c 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 // Refer to the license.txt file included. +#include + #include "common/common.h" #include "video_core/video_core.h" @@ -10,22 +12,21 @@ #include "citra/emu_window/emu_window_glfw.h" +EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) { + return static_cast(glfwGetWindowUserPointer(win)); +} + /// Called by GLFW when a key event occurs void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) { - if (!VideoCore::g_emu_window) { - return; - } - - int keyboard_id = ((EmuWindow_GLFW*)VideoCore::g_emu_window)->keyboard_id; + int keyboard_id = GetEmuWindow(win)->keyboard_id; if (action == GLFW_PRESS) { EmuWindow::KeyPressed({key, keyboard_id}); - } - - if (action == GLFW_RELEASE) { + } else if (action == GLFW_RELEASE) { EmuWindow::KeyReleased({key, keyboard_id}); } + HID_User::PadUpdateComplete(); } @@ -34,8 +35,18 @@ const bool EmuWindow_GLFW::IsOpen() { return glfwWindowShouldClose(m_render_window) == 0; } -void EmuWindow_GLFW::GetFramebufferSize(int* fbWidth, int* fbHeight) { - glfwGetFramebufferSize(m_render_window, fbWidth, fbHeight); +void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) { + _dbg_assert_(GUI, width > 0); + _dbg_assert_(GUI, height > 0); + + GetEmuWindow(win)->NotifyFramebufferSizeChanged(std::pair(width, height)); +} + +void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) { + _dbg_assert_(GUI, width > 0); + _dbg_assert_(GUI, height > 0); + + GetEmuWindow(win)->NotifyClientAreaSizeChanged(std::pair(width, height)); } /// EmuWindow_GLFW constructor @@ -54,20 +65,30 @@ EmuWindow_GLFW::EmuWindow_GLFW() { // GLFW on OSX requires these window hints to be set to create a 3.2+ GL context. glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - + m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth, (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight), - m_window_title.c_str(), NULL, NULL); + GetWindowTitle().c_str(), NULL, NULL); if (m_render_window == NULL) { printf("Failed to create GLFW window! Exiting..."); exit(1); } - - // Setup callbacks - glfwSetWindowUserPointer(m_render_window, this); - glfwSetKeyCallback(m_render_window, OnKeyEvent); + glfwSetWindowUserPointer(m_render_window, this); + + // Notify base interface about window state + int width, height; + glfwGetFramebufferSize(m_render_window, &width, &height); + OnFramebufferResizeEvent(m_render_window, width, height); + + glfwGetWindowSize(m_render_window, &width, &height); + OnClientAreaResizeEvent(m_render_window, width, height); + + // Setup callbacks + glfwSetKeyCallback(m_render_window, OnKeyEvent); + glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent); + glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent); DoneCurrent(); } diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h index e96228765..0da688a54 100644 --- a/src/citra/emu_window/emu_window_glfw.h +++ b/src/citra/emu_window/emu_window_glfw.h @@ -4,10 +4,10 @@ #pragma once -#include - #include "common/emu_window.h" +struct GLFWwindow; + class EmuWindow_GLFW : public EmuWindow { public: EmuWindow_GLFW(); @@ -30,12 +30,15 @@ public: /// Whether the window is still open, and a close request hasn't yet been sent const bool IsOpen(); + static void OnClientAreaResizeEvent(GLFWwindow* win, int width, int height); + + static void OnFramebufferResizeEvent(GLFWwindow* win, int width, int height); + void ReloadSetKeymaps() override; - /// Gets the size of the window in pixels - void GetFramebufferSize(int* fbWidth, int* fbHeight); - private: + static EmuWindow_GLFW* GetEmuWindow(GLFWwindow* win); + GLFWwindow* m_render_window; ///< Internal GLFW render window /// Device id of keyboard for use with KeyMap diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 516e115fd..34a79b306 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -88,20 +88,20 @@ void EmuThread::Stop() class GGLWidgetInternal : public QGLWidget { public: - GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) : QGLWidget(fmt, parent) - { - parent_ = parent; + GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) + : QGLWidget(fmt, parent), parent(parent) { } - void paintEvent(QPaintEvent* ev) override - { + void paintEvent(QPaintEvent* ev) override { } + void resizeEvent(QResizeEvent* ev) override { - parent_->SetClientAreaWidth(size().width()); - parent_->SetClientAreaHeight(size().height()); + parent->OnClientAreaResized(ev->size().width(), ev->size().height()); + parent->OnFramebufferSizeChanged(); } + private: - GRenderWindow* parent_; + GRenderWindow* parent; }; EmuThread& GRenderWindow::GetEmuThread() @@ -120,16 +120,23 @@ GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this fmt.setProfile(QGLFormat::CoreProfile); // 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); QBoxLayout* layout = new QHBoxLayout(this); resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); layout->addWidget(child); layout->setMargin(0); setLayout(layout); - QObject::connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext())); + connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext())); + + OnFramebufferSizeChanged(); + NotifyClientAreaSizeChanged(std::pair(child->width(), child->height())); BackupGeometry(); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, SLOT(OnFramebufferSizeChanged())); +#endif } void GRenderWindow::moveContext() @@ -182,22 +189,26 @@ void GRenderWindow::PollEvents() { */ } -// On Qt 5.1+, this correctly gets the size of the framebuffer (pixels). +// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). // // Older versions get the window size (density independent pixels), // and hence, do not support DPI scaling ("retina" displays). // The result will be a viewport that is smaller than the extent of the window. -void GRenderWindow::GetFramebufferSize(int* fbWidth, int* fbHeight) +void GRenderWindow::OnFramebufferSizeChanged() { -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - int pixelRatio = child->QPaintDevice::devicePixelRatio(); - - *fbWidth = child->QPaintDevice::width() * pixelRatio; - *fbHeight = child->QPaintDevice::height() * pixelRatio; + // Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + // windowHandle() might not be accessible until the window is displayed to screen. + auto pixel_ratio = windowHandle() ? (windowHandle()->screen()->devicePixelRatio()) : 1.0; + + unsigned width = child->QPaintDevice::width() * pixel_ratio; + unsigned height = child->QPaintDevice::height() * pixel_ratio; #else - *fbWidth = child->QPaintDevice::width(); - *fbHeight = child->QPaintDevice::height(); + unsigned width = child->QPaintDevice::width(); + unsigned height = child->QPaintDevice::height(); #endif + + NotifyFramebufferSizeChanged(std::make_pair(width, height)); } void GRenderWindow::BackupGeometry() @@ -260,3 +271,7 @@ void GRenderWindow::ReloadSetKeymaps() KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN); } +void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) +{ + NotifyClientAreaSizeChanged(std::make_pair(width, height)); +} diff --git a/src/citra_qt/bootmanager.hxx b/src/citra_qt/bootmanager.hxx index ec3e1fe71..3a18f98fd 100644 --- a/src/citra_qt/bootmanager.hxx +++ b/src/citra_qt/bootmanager.hxx @@ -1,12 +1,16 @@ +#include + #include #include -#include + #include "common/common.h" #include "common/emu_window.h" -class GRenderWindow; +class QScreen; class QKeyEvent; +class GRenderWindow; + class EmuThread : public QThread { Q_OBJECT @@ -74,7 +78,7 @@ private: signals: /** * Emitted when CPU when we've finished processing a single Gekko instruction - * + * * @warning This will only be emitted when the CPU is not running (SetCpuRunning(false)) * @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns) */ @@ -96,12 +100,11 @@ public: void MakeCurrent() override; void DoneCurrent() override; void PollEvents() override; - void GetFramebufferSize(int* fbWidth, int* fbHeight) override; void BackupGeometry(); void RestoreGeometry(); void restoreGeometry(const QByteArray& geometry); // overridden - QByteArray saveGeometry(); // overridden + QByteArray saveGeometry(); // overridden EmuThread& GetEmuThread(); @@ -110,8 +113,12 @@ public: void ReloadSetKeymaps() override; + void OnClientAreaResized(unsigned width, unsigned height); + + void OnFramebufferSizeChanged(); + public slots: - void moveContext(); + void moveContext(); // overridden private: QGLWidget* child; diff --git a/src/common/emu_window.h b/src/common/emu_window.h index ba9d4fa76..72c40c6a4 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -43,54 +43,59 @@ public: static void KeyReleased(KeyMap::HostDeviceKey key); WindowConfig GetConfig() const { - return m_config; + return config; } void SetConfig(const WindowConfig& val) { - m_config = val; + config = val; } - /// Gets the size of the window in pixels - virtual void GetFramebufferSize(int* fbWidth, int* fbHeight) = 0; - - int GetClientAreaWidth() const { - return m_client_area_width; + /** + * Gets the size of the framebuffer in pixels + */ + const std::pair GetFramebufferSize() const { + return framebuffer_size; } - void SetClientAreaWidth(const int val) { - m_client_area_width = val; + /** + * Gets window client area width in logical coordinates + */ + std::pair GetClientAreaSize() const { + return std::make_pair(client_area_width, client_area_height); } - int GetClientAreaHeight() const { - return m_client_area_height; + std::string GetWindowTitle() const { + return window_title; } - void SetClientAreaHeight(const int val) { - m_client_area_height = val; - } - - std::string GetWindowTitle() const { - return m_window_title; - } - - void SetWindowTitle(std::string val) { - m_window_title = val; + void SetWindowTitle(const std::string& val) { + window_title = val; } protected: - EmuWindow(): - m_window_title(Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc)), + EmuWindow() : // TODO: What the hell... -.- - don't hardcode dimensions here without applying them in a sensible manner... + window_title(Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc)) m_client_area_width(640), - m_client_area_height(480) + m_client_area_height(480), {} virtual ~EmuWindow() {} - std::string m_window_title; ///< Current window title, should be used by window impl. + std::pair NotifyFramebufferSizeChanged(const std::pair& size) { + framebuffer_size = size; + } - int m_client_area_width; ///< Current client width, should be set by window impl. - int m_client_area_height; ///< Current client height, should be set by window impl. + void NotifyClientAreaSizeChanged(std::pair size) { + client_area_width = size.first; + client_area_height = size.second; + } private: - WindowConfig m_config; ///< Internal configuration + std::string window_title; ///< Current window title, should be used by window impl. + std::pair framebuffer_size; + + unsigned client_area_width; ///< Current client width, should be set by window impl. + unsigned client_area_height; ///< Current client height, should be set by window impl. + + WindowConfig config; ///< Internal configuration }; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 3757482db..082a464b3 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -230,10 +230,10 @@ void RendererOpenGL::SetWindow(EmuWindow* window) { } void RendererOpenGL::UpdateViewportExtent() { - int width_in_pixels; - int height_in_pixels; + unsigned width_in_pixels; + unsigned height_in_pixels; - render_window->GetFramebufferSize(&width_in_pixels, &height_in_pixels); + std::tie(width_in_pixels, height_in_pixels) = render_window->GetFramebufferSize(); // No update needed if framebuffer size hasn't changed if (width_in_pixels == framebuffer_size.width && height_in_pixels == framebuffer_size.height) { From 722ce2258949c5edf81c946faedfd040efeb38a6 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sun, 12 Oct 2014 22:46:33 +0200 Subject: [PATCH 04/13] EmuWindow: Add support for specifying minimal client area sizes. --- src/citra/emu_window/emu_window_glfw.cpp | 21 +++++++++++++++ src/citra/emu_window/emu_window_glfw.h | 2 ++ src/citra_qt/bootmanager.cpp | 7 +++++ src/citra_qt/bootmanager.hxx | 2 ++ src/common/emu_window.h | 34 ++++++++++++++++++------ 5 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index 28aa3450c..7e1e1c9a6 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp @@ -46,6 +46,15 @@ void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int hei _dbg_assert_(GUI, width > 0); _dbg_assert_(GUI, height > 0); + // TODO: It's actually more interesting to us what the framebuffer size ends up being. + int adjusted_width = std::max(width, GetEmuWindow(win)->GetActiveConfig().min_client_area_size.first); + int adjusted_height = std::max(height, GetEmuWindow(win)->GetActiveConfig().min_client_area_size.second); + + if (adjusted_width != width || adjusted_height != height) { + glfwSetWindowSize(win, adjusted_width, adjusted_height); + return; + } + GetEmuWindow(win)->NotifyClientAreaSizeChanged(std::pair(width, height)); } @@ -136,3 +145,15 @@ void EmuWindow_GLFW::ReloadSetKeymaps() { KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, HID_User::PAD_CIRCLE_UP); KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN); } + +void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair& minimal_size) { + std::pair current_size; + glfwGetWindowSize(m_render_window, ¤t_size.first, ¤t_size.second); + + _dbg_assert_(GUI, (int)minimal_size.first > 0 && (int)minimal_size.second > 0); + int new_width = std::max(current_size.first, (int)minimal_size.first); + int new_height = std::max(current_size.second, (int)minimal_size.second); + + if (current_size != std::make_pair(new_width, new_height)) + glfwSetWindowSize(m_render_window, new_width, new_height); +} diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h index 0da688a54..61cef4e65 100644 --- a/src/citra/emu_window/emu_window_glfw.h +++ b/src/citra/emu_window/emu_window_glfw.h @@ -37,6 +37,8 @@ public: void ReloadSetKeymaps() override; private: + void OnMinimalClientAreaChangeRequest(const std::pair& minimal_size) override; + static EmuWindow_GLFW* GetEmuWindow(GLFWwindow* win); GLFWwindow* m_render_window; ///< Internal GLFW render window diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 34a79b306..8c12cb228 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -129,6 +129,9 @@ GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this setLayout(layout); connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext())); + setMinimumSize(GetActiveConfig().min_client_area_size.first, + GetActiveConfig().min_client_area_size.second); + OnFramebufferSizeChanged(); NotifyClientAreaSizeChanged(std::pair(child->width(), child->height())); @@ -275,3 +278,7 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { NotifyClientAreaSizeChanged(std::make_pair(width, height)); } + +void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair& minimal_size) { + setMinimumSize(minimal_size.first, minimal_size.second); +} diff --git a/src/citra_qt/bootmanager.hxx b/src/citra_qt/bootmanager.hxx index 3a18f98fd..3eec1668e 100644 --- a/src/citra_qt/bootmanager.hxx +++ b/src/citra_qt/bootmanager.hxx @@ -121,6 +121,8 @@ public slots: void moveContext(); // overridden private: + void OnMinimalClientAreaChangeRequest(const std::pair& minimal_size) override; + QGLWidget* child; EmuThread emu_thread; diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 72c40c6a4..baacc6da2 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -20,6 +20,7 @@ public: bool fullscreen; int res_width; int res_height; + std::pair min_client_area_size; }; /// Swap buffers to display the next frame @@ -42,8 +43,8 @@ public: /// Signals a key release action to the HID module static void KeyReleased(KeyMap::HostDeviceKey key); - WindowConfig GetConfig() const { - return config; + const WindowConfig& GetActiveConfig() const { + return active_config; } void SetConfig(const WindowConfig& val) { @@ -72,24 +73,40 @@ public: window_title = val; } + // Only call this from the GUI thread! + void ProcessConfigurationChanges() { + // TODO: For proper thread safety, we should eventually implement a proper + // multiple-writer/single-reader queue... + + if (config.min_client_area_size != active_config.min_client_area_size) { + OnMinimalClientAreaChangeRequest(config.min_client_area_size); + config.min_client_area_size = active_config.min_client_area_size; + } + } + protected: - EmuWindow() : // TODO: What the hell... -.- - don't hardcode dimensions here without applying them in a sensible manner... + EmuWindow() : window_title(Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc)) - m_client_area_width(640), - m_client_area_height(480), - {} + { + // TODO + config.min_client_area_size = std::make_pair(300u, 500u); + active_config = config; + } virtual ~EmuWindow() {} std::pair NotifyFramebufferSizeChanged(const std::pair& size) { framebuffer_size = size; } - void NotifyClientAreaSizeChanged(std::pair size) { + void NotifyClientAreaSizeChanged(const std::pair& size) { client_area_width = size.first; client_area_height = size.second; } private: + virtual void OnMinimalClientAreaChangeRequest(const std::pair& minimal_size) { + } + std::string window_title; ///< Current window title, should be used by window impl. std::pair framebuffer_size; @@ -97,5 +114,6 @@ private: unsigned client_area_width; ///< Current client width, should be set by window impl. unsigned client_area_height; ///< Current client height, should be set by window impl. - WindowConfig config; ///< Internal configuration + WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges) + WindowConfig active_config; ///< Internal active configuration }; From ded9c8a826b2b69b4c7a63bf4cea9805b5244788 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 13 Nov 2014 18:12:27 +0100 Subject: [PATCH 05/13] EmuWindow: Add documentation. --- src/common/emu_window.h | 75 +++++++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/src/common/emu_window.h b/src/common/emu_window.h index baacc6da2..1465743f2 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -9,13 +9,26 @@ #include "common/string_util.h" #include "common/key_map.h" -// Abstraction class used to provide an interface between emulation code and the frontend (e.g. SDL, -// QGLWidget, GLFW, etc...) +/** + * Abstraction class used to provide an interface between emulation code and the frontend + * (e.g. SDL, QGLWidget, GLFW, etc...). + * + * Design notes on the interaction between EmuWindow and the emulation core: + * - Generally, decisions on anything visible to the user should be left up to the GUI. + * For example, the emulation core should not try to dictate some window title or size. + * This stuff is not the core's business and only causes problems with regards to thread-safety + * anyway. + * - Under certain circumstances, it may be desirable for the core to politely request the GUI + * to set e.g. a minimum window size. However, the GUI should always be free to ignore any + * such hints. + * - EmuWindow may expose some of its state as read-only to the emulation core, however care + * should be taken to make sure the provided information is self-consistent. This requires + * some sort of synchronization (most of this is still a TODO). + */ class EmuWindow { - public: - /// Data structure to store an emuwindow configuration + /// Data structure to store emuwindow configuration struct WindowConfig { bool fullscreen; int res_width; @@ -43,37 +56,64 @@ public: /// Signals a key release action to the HID module static void KeyReleased(KeyMap::HostDeviceKey key); + /** + * Returns currently active configuration. + * @note Accesses to the returned object need not be consistent because it may be modified in another thread + */ const WindowConfig& GetActiveConfig() const { return active_config; } + /** + * Requests the internal configuration to be replaced by the specified argument at some point in the future. + * @note This method is thread-safe, because it delays configuration changes to the GUI event loop. Hence there is no guarantee on when the requested configuration will be active. + */ void SetConfig(const WindowConfig& val) { config = val; } /** - * Gets the size of the framebuffer in pixels + * Gets the framebuffer size in pixels. + * @note This method is thread-safe */ const std::pair GetFramebufferSize() const { return framebuffer_size; } /** - * Gets window client area width in logical coordinates + * Gets window client area width in logical coordinates. + * @note For high-DPI systems, this is smaller than the framebuffer size. + * @note This method is thread-safe */ std::pair GetClientAreaSize() const { return std::make_pair(client_area_width, client_area_height); } + // TODO: Remove std::string GetWindowTitle() const { return window_title; } + // TODO: Remove void SetWindowTitle(const std::string& val) { window_title = val; } - // Only call this from the GUI thread! +protected: + // TODO: Remove window title initialization + EmuWindow() : + window_title(Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc)) + { + // TODO + config.min_client_area_size = std::make_pair(300u, 500u); + active_config = config; + } + virtual ~EmuWindow() {} + + /** + * Processes any pending configuration changes from the last SetConfig call. + * @note Implementations will usually want to call this from the GUI thread. + */ void ProcessConfigurationChanges() { // TODO: For proper thread safety, we should eventually implement a proper // multiple-writer/single-reader queue... @@ -84,20 +124,18 @@ public: } } -protected: - EmuWindow() : - window_title(Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc)) - { - // TODO - config.min_client_area_size = std::make_pair(300u, 500u); - active_config = config; - } - virtual ~EmuWindow() {} - - std::pair NotifyFramebufferSizeChanged(const std::pair& size) { + /** + * Update internal framebuffer size with the given parameter. + * @note EmuWindow implementations will usually use this in window resize event handlers. + */ + void NotifyFramebufferSizeChanged(const std::pair& size) { framebuffer_size = size; } + /** + * Update internal client area size with the given parameter. + * @note EmuWindow implementations will usually use this in window resize event handlers. + */ void NotifyClientAreaSizeChanged(const std::pair& size) { client_area_width = size.first; client_area_height = size.second; @@ -107,6 +145,7 @@ private: virtual void OnMinimalClientAreaChangeRequest(const std::pair& minimal_size) { } + // TODO: Remove std::string window_title; ///< Current window title, should be used by window impl. std::pair framebuffer_size; From 182476c96a6c75e90a90cbb52048bf754fdd786d Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 13 Nov 2014 18:17:39 +0100 Subject: [PATCH 06/13] EmuWindow: Remove window title getters/setters. The window title is none of the emulation core's business. The GUI code is free to put whatever it wants there. Providing properly thread-safe window title getters and setters is a mess anyway. --- src/citra/emu_window/emu_window_glfw.cpp | 7 ++++--- src/citra_qt/bootmanager.cpp | 11 +++-------- src/citra_qt/main.cpp | 3 ++- src/common/emu_window.h | 17 +---------------- 4 files changed, 10 insertions(+), 28 deletions(-) diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index 7e1e1c9a6..9e6f91578 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp @@ -75,9 +75,10 @@ EmuWindow_GLFW::EmuWindow_GLFW() { glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth, - (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight), - GetWindowTitle().c_str(), NULL, NULL); + std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); + m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth, + (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight), + window_title.c_str(), NULL, NULL); if (m_render_window == NULL) { printf("Failed to create GLFW window! Exiting..."); diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 8c12cb228..ace48a237 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -111,6 +111,9 @@ EmuThread& GRenderWindow::GetEmuThread() GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0) { + std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); + setWindowTitle(QString::fromStdString(window_title)); + keyboard_id = KeyMap::NewDeviceId(); ReloadSetKeymaps(); @@ -182,14 +185,6 @@ void GRenderWindow::DoneCurrent() } void GRenderWindow::PollEvents() { - // TODO(ShizZy): Does this belong here? This is a reasonable place to update the window title - // from the main thread, but this should probably be in an event handler... - /* - static char title[128]; - sprintf(title, "%s (FPS: %02.02f)", window_title_.c_str(), - video_core::g_renderer->current_fps()); - setWindowTitle(title); - */ } // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 9a4e36adf..d5554d917 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -117,7 +117,8 @@ GMainWindow::GMainWindow() connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); - setWindowTitle(render_window->GetWindowTitle().c_str()); + std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); + setWindowTitle(window_title.c_str()); show(); diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 1465743f2..3817a7734 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -89,20 +89,8 @@ public: return std::make_pair(client_area_width, client_area_height); } - // TODO: Remove - std::string GetWindowTitle() const { - return window_title; - } - - // TODO: Remove - void SetWindowTitle(const std::string& val) { - window_title = val; - } - protected: - // TODO: Remove window title initialization - EmuWindow() : - window_title(Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc)) + EmuWindow() { // TODO config.min_client_area_size = std::make_pair(300u, 500u); @@ -145,9 +133,6 @@ private: virtual void OnMinimalClientAreaChangeRequest(const std::pair& minimal_size) { } - // TODO: Remove - std::string window_title; ///< Current window title, should be used by window impl. - std::pair framebuffer_size; unsigned client_area_width; ///< Current client width, should be set by window impl. From 19bb01c223a36b7c02253cfedeb9e07c0d5a692b Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 13 Nov 2014 18:24:15 +0100 Subject: [PATCH 07/13] EmuWindow: Better document the purpose of OnMinimalClientAreaChangeRequest. --- src/common/emu_window.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 3817a7734..a9cf71690 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -100,6 +100,8 @@ protected: /** * Processes any pending configuration changes from the last SetConfig call. + * This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration + * field changed. * @note Implementations will usually want to call this from the GUI thread. */ void ProcessConfigurationChanges() { @@ -130,7 +132,12 @@ protected: } private: + /** + * Handler called when the minimal client area was requested to be changed via SetConfig. + * For the request to be honored, EmuWindow implementations will usually reimplement this function. + */ virtual void OnMinimalClientAreaChangeRequest(const std::pair& minimal_size) { + // By default, ignore this request and do nothing. } std::pair framebuffer_size; From c5c6e095f037a0128de156103f5b98d5bf2b417c Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 13 Nov 2014 19:33:29 +0100 Subject: [PATCH 08/13] OpenGL Renderer: Cleanup viewport extent calculation. --- .../renderer_opengl/renderer_opengl.cpp | 49 +++++++++---------- .../renderer_opengl/renderer_opengl.h | 26 +++------- 2 files changed, 30 insertions(+), 45 deletions(-) diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 082a464b3..729e8e73d 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -191,8 +191,8 @@ void RendererOpenGL::DrawSingleScreenRotated(const TextureInfo& texture, float x * Draws the emulated screens to the emulator window. */ void RendererOpenGL::DrawScreens() { - UpdateViewportExtent(); - glViewport(viewport_extent.x, viewport_extent.y, viewport_extent.width, viewport_extent.height); + auto viewport_extent = GetViewportExtent(); + glViewport(viewport_extent.left, viewport_extent.top, viewport_extent.GetWidth(), viewport_extent.GetHeight()); // TODO: Or bottom? glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program_id); @@ -229,37 +229,32 @@ void RendererOpenGL::SetWindow(EmuWindow* window) { render_window = window; } -void RendererOpenGL::UpdateViewportExtent() { - unsigned width_in_pixels; - unsigned height_in_pixels; +MathUtil::Rectangle RendererOpenGL::GetViewportExtent() { + unsigned framebuffer_width; + unsigned framebuffer_height; + std::tie(framebuffer_width, framebuffer_height) = render_window->GetFramebufferSize(); - std::tie(width_in_pixels, height_in_pixels) = render_window->GetFramebufferSize(); - - // No update needed if framebuffer size hasn't changed - if (width_in_pixels == framebuffer_size.width && height_in_pixels == framebuffer_size.height) { - return; - } - - framebuffer_size.width = width_in_pixels; - framebuffer_size.height = height_in_pixels; - - float window_aspect_ratio = static_cast(height_in_pixels) / width_in_pixels; + float window_aspect_ratio = static_cast(framebuffer_height) / framebuffer_width; float emulation_aspect_ratio = static_cast(resolution_height) / resolution_width; + MathUtil::Rectangle viewport_extent; if (window_aspect_ratio > emulation_aspect_ratio) { - // If the window is more narrow than the emulation content, borders are applied on the - // top and bottom of the window. - viewport_extent.width = width_in_pixels; - viewport_extent.height = emulation_aspect_ratio * viewport_extent.width; - viewport_extent.x = 0; - viewport_extent.y = (height_in_pixels - viewport_extent.height) / 2; + // Window is narrower than the emulation content => apply borders to the top and bottom + unsigned viewport_height = emulation_aspect_ratio * framebuffer_width; + viewport_extent.left = 0; + viewport_extent.top = (framebuffer_height - viewport_height) / 2; + viewport_extent.right = viewport_extent.left + framebuffer_width; + viewport_extent.bottom = viewport_extent.top + viewport_height; } else { - // Otherwise, borders are applied on the left and right sides of the window. - viewport_extent.height = height_in_pixels; - viewport_extent.width = (1 / emulation_aspect_ratio) * viewport_extent.height; - viewport_extent.x = (width_in_pixels - viewport_extent.width) / 2; - viewport_extent.y = 0; + // Otherwise, apply borders to the left and right sides of the window. + unsigned viewport_width = framebuffer_height / emulation_aspect_ratio; + viewport_extent.left = (framebuffer_width - viewport_width) / 2; + viewport_extent.top = 0; + viewport_extent.right = viewport_extent.left + viewport_width; + viewport_extent.bottom = viewport_extent.top + framebuffer_height; } + + return viewport_extent; } /// Initialize the renderer diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index d440e2bc7..7fdcec731 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -4,13 +4,15 @@ #pragma once +#include + #include "generated/gl_3_2_core.h" -#include "common/common.h" -#include "core/hw/gpu.h" -#include "video_core/renderer_base.h" +#include "common/math_util.h" -#include +#include "core/hw/gpu.h" + +#include "video_core/renderer_base.h" class EmuWindow; @@ -52,8 +54,8 @@ private: static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, const TextureInfo& texture); - /// Updates the viewport rectangle - void UpdateViewportExtent(); + /// Computes the viewport rectangle + MathUtil::Rectangle GetViewportExtent(); EmuWindow* render_window; ///< Handle to render window u32 last_mode; ///< Last render mode @@ -61,18 +63,6 @@ private: int resolution_width; ///< Current resolution width int resolution_height; ///< Current resolution height - struct { - int width; - int height; - } framebuffer_size; ///< Current framebuffer size - - struct { - int x; - int y; - int width; - int height; - } viewport_extent; ///< Current viewport rectangle - // OpenGL object IDs GLuint vertex_array_handle; GLuint vertex_buffer_handle; From e02ff0121f9594cf441e180115e4cc86eb69a47c Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 13 Nov 2014 20:25:39 +0100 Subject: [PATCH 09/13] MathUtil: Make Rectangle work with unsigned types. --- src/common/math_util.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/common/math_util.h b/src/common/math_util.h index b32e7bb14..b10a25c13 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h @@ -7,6 +7,7 @@ #include "common/common.h" #include +#include #include namespace MathUtil @@ -109,11 +110,11 @@ struct Rectangle Rectangle(T theLeft, T theTop, T theRight, T theBottom) : left(theLeft), top(theTop), right(theRight), bottom(theBottom) { } - + bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; } - T GetWidth() const { return abs(right - left); } - T GetHeight() const { return abs(bottom - top); } + T GetWidth() const { return std::abs(static_cast::type>(right - left)); } + T GetHeight() const { return std::abs(static_cast::type>(bottom - top)); } // If the rectangle is in a coordinate system with a lower-left origin, use // this Clamp. @@ -127,7 +128,7 @@ struct Rectangle // If the rectangle is in a coordinate system with an upper-left origin, // use this Clamp. - void ClampUL(T x1, T y1, T x2, T y2) + void ClampUL(T x1, T y1, T x2, T y2) { if (left < x1) left = x1; if (right > x2) right = x2; From 6a6464807ab5ba804e7b0dd4bfe1629cd5457278 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 13 Nov 2014 20:31:34 +0100 Subject: [PATCH 10/13] EmuWindow: Add a TODO. Implementing this function currently is not critical, as we don't perform any configuration changes, yet. However, the interface is a good starting point for adding this functionality. --- src/common/emu_window.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/emu_window.h b/src/common/emu_window.h index a9cf71690..52c870036 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -103,6 +103,7 @@ protected: * This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration * field changed. * @note Implementations will usually want to call this from the GUI thread. + * @todo Actually call this in existing implementations. */ void ProcessConfigurationChanges() { // TODO: For proper thread safety, we should eventually implement a proper From 688a5c033ec68bb45cde824885ce8d64384c237d Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 13 Nov 2014 20:32:33 +0100 Subject: [PATCH 11/13] citra-qt: Small cleanup. --- src/citra_qt/bootmanager.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index ace48a237..758f71fda 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -132,8 +132,7 @@ GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this setLayout(layout); connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext())); - setMinimumSize(GetActiveConfig().min_client_area_size.first, - GetActiveConfig().min_client_area_size.second); + OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); OnFramebufferSizeChanged(); NotifyClientAreaSizeChanged(std::pair(child->width(), child->height())); From 5785f764aba431967881df01acc08a3405c97097 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 13 Nov 2014 20:45:37 +0100 Subject: [PATCH 12/13] EmuWindow: Add some explicit documentation and set proper minimal client area size. --- src/common/emu_window.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 52c870036..4cb94fed1 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -24,6 +24,8 @@ * - EmuWindow may expose some of its state as read-only to the emulation core, however care * should be taken to make sure the provided information is self-consistent. This requires * some sort of synchronization (most of this is still a TODO). + * - 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 { @@ -92,8 +94,8 @@ public: protected: EmuWindow() { - // TODO - config.min_client_area_size = std::make_pair(300u, 500u); + // TODO: Find a better place to set this. + config.min_client_area_size = std::make_pair(400u, 480u); active_config = config; } virtual ~EmuWindow() {} From 963c923d5931dc9d20eb298001282da88e31c31a Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 13 Nov 2014 20:47:46 +0100 Subject: [PATCH 13/13] citra GLFW: Ignore minimal window size hints. GLFW provides no proper support for this, hence we just allow any window size to be used. --- src/citra/emu_window/emu_window_glfw.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index 9e6f91578..8efb39e2e 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp @@ -46,14 +46,8 @@ void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int hei _dbg_assert_(GUI, width > 0); _dbg_assert_(GUI, height > 0); - // TODO: It's actually more interesting to us what the framebuffer size ends up being. - int adjusted_width = std::max(width, GetEmuWindow(win)->GetActiveConfig().min_client_area_size.first); - int adjusted_height = std::max(height, GetEmuWindow(win)->GetActiveConfig().min_client_area_size.second); - - if (adjusted_width != width || adjusted_height != height) { - glfwSetWindowSize(win, adjusted_width, adjusted_height); - return; - } + // NOTE: GLFW provides no proper way to set a minimal window size. + // Hence, we just ignore the corresponding EmuWindow hint. GetEmuWindow(win)->NotifyClientAreaSizeChanged(std::pair(width, height)); }