diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/citra/emu_window/emu_window_glfw.cpp | 70 | ||||
| -rw-r--r-- | src/citra/emu_window/emu_window_glfw.h | 14 | ||||
| -rw-r--r-- | src/citra_qt/bootmanager.cpp | 76 | ||||
| -rw-r--r-- | src/citra_qt/bootmanager.hxx | 20 | ||||
| -rw-r--r-- | src/citra_qt/main.cpp | 3 | ||||
| -rw-r--r-- | src/common/emu_window.h | 128 | ||||
| -rw-r--r-- | src/common/log.h | 1 | ||||
| -rw-r--r-- | src/common/log_manager.cpp | 1 | ||||
| -rw-r--r-- | src/common/math_util.h | 9 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 31 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.h | 11 |
11 files changed, 281 insertions, 83 deletions
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp index 0c774bbc5..8efb39e2e 100644 --- a/src/citra/emu_window/emu_window_glfw.cpp +++ b/src/citra/emu_window/emu_window_glfw.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 | 2 | // Licensed under GPLv2 |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <GLFW/glfw3.h> | ||
| 6 | |||
| 5 | #include "common/common.h" | 7 | #include "common/common.h" |
| 6 | 8 | ||
| 7 | #include "video_core/video_core.h" | 9 | #include "video_core/video_core.h" |
| @@ -10,22 +12,21 @@ | |||
| 10 | 12 | ||
| 11 | #include "citra/emu_window/emu_window_glfw.h" | 13 | #include "citra/emu_window/emu_window_glfw.h" |
| 12 | 14 | ||
| 15 | EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) { | ||
| 16 | return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win)); | ||
| 17 | } | ||
| 18 | |||
| 13 | /// Called by GLFW when a key event occurs | 19 | /// Called by GLFW when a key event occurs |
| 14 | void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) { | 20 | void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) { |
| 15 | 21 | ||
| 16 | if (!VideoCore::g_emu_window) { | 22 | int keyboard_id = GetEmuWindow(win)->keyboard_id; |
| 17 | return; | ||
| 18 | } | ||
| 19 | |||
| 20 | int keyboard_id = ((EmuWindow_GLFW*)VideoCore::g_emu_window)->keyboard_id; | ||
| 21 | 23 | ||
| 22 | if (action == GLFW_PRESS) { | 24 | if (action == GLFW_PRESS) { |
| 23 | EmuWindow::KeyPressed({key, keyboard_id}); | 25 | EmuWindow::KeyPressed({key, keyboard_id}); |
| 24 | } | 26 | } else if (action == GLFW_RELEASE) { |
| 25 | |||
| 26 | if (action == GLFW_RELEASE) { | ||
| 27 | EmuWindow::KeyReleased({key, keyboard_id}); | 27 | EmuWindow::KeyReleased({key, keyboard_id}); |
| 28 | } | 28 | } |
| 29 | |||
| 29 | HID_User::PadUpdateComplete(); | 30 | HID_User::PadUpdateComplete(); |
| 30 | } | 31 | } |
| 31 | 32 | ||
| @@ -34,6 +35,23 @@ const bool EmuWindow_GLFW::IsOpen() { | |||
| 34 | return glfwWindowShouldClose(m_render_window) == 0; | 35 | return glfwWindowShouldClose(m_render_window) == 0; |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 38 | void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) { | ||
| 39 | _dbg_assert_(GUI, width > 0); | ||
| 40 | _dbg_assert_(GUI, height > 0); | ||
| 41 | |||
| 42 | GetEmuWindow(win)->NotifyFramebufferSizeChanged(std::pair<unsigned,unsigned>(width, height)); | ||
| 43 | } | ||
| 44 | |||
| 45 | void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) { | ||
| 46 | _dbg_assert_(GUI, width > 0); | ||
| 47 | _dbg_assert_(GUI, height > 0); | ||
| 48 | |||
| 49 | // NOTE: GLFW provides no proper way to set a minimal window size. | ||
| 50 | // Hence, we just ignore the corresponding EmuWindow hint. | ||
| 51 | |||
| 52 | GetEmuWindow(win)->NotifyClientAreaSizeChanged(std::pair<unsigned,unsigned>(width, height)); | ||
| 53 | } | ||
| 54 | |||
| 37 | /// EmuWindow_GLFW constructor | 55 | /// EmuWindow_GLFW constructor |
| 38 | EmuWindow_GLFW::EmuWindow_GLFW() { | 56 | EmuWindow_GLFW::EmuWindow_GLFW() { |
| 39 | keyboard_id = KeyMap::NewDeviceId(); | 57 | keyboard_id = KeyMap::NewDeviceId(); |
| @@ -50,19 +68,31 @@ EmuWindow_GLFW::EmuWindow_GLFW() { | |||
| 50 | // GLFW on OSX requires these window hints to be set to create a 3.2+ GL context. | 68 | // GLFW on OSX requires these window hints to be set to create a 3.2+ GL context. |
| 51 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | 69 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); |
| 52 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | 70 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); |
| 53 | 71 | ||
| 54 | m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth, | 72 | std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); |
| 55 | (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight), | 73 | m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth, |
| 56 | m_window_title.c_str(), NULL, NULL); | 74 | (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight), |
| 75 | window_title.c_str(), NULL, NULL); | ||
| 57 | 76 | ||
| 58 | if (m_render_window == NULL) { | 77 | if (m_render_window == NULL) { |
| 59 | printf("Failed to create GLFW window! Exiting..."); | 78 | printf("Failed to create GLFW window! Exiting..."); |
| 60 | exit(1); | 79 | exit(1); |
| 61 | } | 80 | } |
| 62 | 81 | ||
| 63 | // Setup callbacks | ||
| 64 | glfwSetWindowUserPointer(m_render_window, this); | 82 | glfwSetWindowUserPointer(m_render_window, this); |
| 83 | |||
| 84 | // Notify base interface about window state | ||
| 85 | int width, height; | ||
| 86 | glfwGetFramebufferSize(m_render_window, &width, &height); | ||
| 87 | OnFramebufferResizeEvent(m_render_window, width, height); | ||
| 88 | |||
| 89 | glfwGetWindowSize(m_render_window, &width, &height); | ||
| 90 | OnClientAreaResizeEvent(m_render_window, width, height); | ||
| 91 | |||
| 92 | // Setup callbacks | ||
| 65 | glfwSetKeyCallback(m_render_window, OnKeyEvent); | 93 | glfwSetKeyCallback(m_render_window, OnKeyEvent); |
| 94 | glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent); | ||
| 95 | glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent); | ||
| 66 | 96 | ||
| 67 | DoneCurrent(); | 97 | DoneCurrent(); |
| 68 | } | 98 | } |
| @@ -110,3 +140,15 @@ void EmuWindow_GLFW::ReloadSetKeymaps() { | |||
| 110 | KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, HID_User::PAD_CIRCLE_UP); | 140 | KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, HID_User::PAD_CIRCLE_UP); |
| 111 | KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN); | 141 | KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN); |
| 112 | } | 142 | } |
| 143 | |||
| 144 | void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { | ||
| 145 | std::pair<int,int> current_size; | ||
| 146 | glfwGetWindowSize(m_render_window, ¤t_size.first, ¤t_size.second); | ||
| 147 | |||
| 148 | _dbg_assert_(GUI, (int)minimal_size.first > 0 && (int)minimal_size.second > 0); | ||
| 149 | int new_width = std::max(current_size.first, (int)minimal_size.first); | ||
| 150 | int new_height = std::max(current_size.second, (int)minimal_size.second); | ||
| 151 | |||
| 152 | if (current_size != std::make_pair(new_width, new_height)) | ||
| 153 | glfwSetWindowSize(m_render_window, new_width, new_height); | ||
| 154 | } | ||
diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h index 7c3072145..61cef4e65 100644 --- a/src/citra/emu_window/emu_window_glfw.h +++ b/src/citra/emu_window/emu_window_glfw.h | |||
| @@ -4,10 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <GLFW/glfw3.h> | ||
| 8 | |||
| 9 | #include "common/emu_window.h" | 7 | #include "common/emu_window.h" |
| 10 | 8 | ||
| 9 | struct GLFWwindow; | ||
| 10 | |||
| 11 | class EmuWindow_GLFW : public EmuWindow { | 11 | class EmuWindow_GLFW : public EmuWindow { |
| 12 | public: | 12 | public: |
| 13 | EmuWindow_GLFW(); | 13 | EmuWindow_GLFW(); |
| @@ -21,7 +21,7 @@ public: | |||
| 21 | 21 | ||
| 22 | /// Makes the graphics context current for the caller thread | 22 | /// Makes the graphics context current for the caller thread |
| 23 | void MakeCurrent() override; | 23 | void MakeCurrent() override; |
| 24 | 24 | ||
| 25 | /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread | 25 | /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread |
| 26 | void DoneCurrent() override; | 26 | void DoneCurrent() override; |
| 27 | 27 | ||
| @@ -30,9 +30,17 @@ public: | |||
| 30 | /// Whether the window is still open, and a close request hasn't yet been sent | 30 | /// Whether the window is still open, and a close request hasn't yet been sent |
| 31 | const bool IsOpen(); | 31 | const bool IsOpen(); |
| 32 | 32 | ||
| 33 | static void OnClientAreaResizeEvent(GLFWwindow* win, int width, int height); | ||
| 34 | |||
| 35 | static void OnFramebufferResizeEvent(GLFWwindow* win, int width, int height); | ||
| 36 | |||
| 33 | void ReloadSetKeymaps() override; | 37 | void ReloadSetKeymaps() override; |
| 34 | 38 | ||
| 35 | private: | 39 | private: |
| 40 | void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; | ||
| 41 | |||
| 42 | static EmuWindow_GLFW* GetEmuWindow(GLFWwindow* win); | ||
| 43 | |||
| 36 | GLFWwindow* m_render_window; ///< Internal GLFW render window | 44 | GLFWwindow* m_render_window; ///< Internal GLFW render window |
| 37 | 45 | ||
| 38 | /// Device id of keyboard for use with KeyMap | 46 | /// Device id of keyboard for use with KeyMap |
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 20824692d..758f71fda 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp | |||
| @@ -2,6 +2,12 @@ | |||
| 2 | #include <QKeyEvent> | 2 | #include <QKeyEvent> |
| 3 | #include <QApplication> | 3 | #include <QApplication> |
| 4 | 4 | ||
| 5 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | ||
| 6 | // Required for screen DPI information | ||
| 7 | #include <QScreen> | ||
| 8 | #include <QWindow> | ||
| 9 | #endif | ||
| 10 | |||
| 5 | #include "common/common.h" | 11 | #include "common/common.h" |
| 6 | #include "bootmanager.hxx" | 12 | #include "bootmanager.hxx" |
| 7 | 13 | ||
| @@ -82,20 +88,20 @@ void EmuThread::Stop() | |||
| 82 | class GGLWidgetInternal : public QGLWidget | 88 | class GGLWidgetInternal : public QGLWidget |
| 83 | { | 89 | { |
| 84 | public: | 90 | public: |
| 85 | GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) : QGLWidget(fmt, parent) | 91 | GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) |
| 86 | { | 92 | : QGLWidget(fmt, parent), parent(parent) { |
| 87 | parent_ = parent; | ||
| 88 | } | 93 | } |
| 89 | 94 | ||
| 90 | void paintEvent(QPaintEvent* ev) override | 95 | void paintEvent(QPaintEvent* ev) override { |
| 91 | { | ||
| 92 | } | 96 | } |
| 97 | |||
| 93 | void resizeEvent(QResizeEvent* ev) override { | 98 | void resizeEvent(QResizeEvent* ev) override { |
| 94 | parent_->SetClientAreaWidth(size().width()); | 99 | parent->OnClientAreaResized(ev->size().width(), ev->size().height()); |
| 95 | parent_->SetClientAreaHeight(size().height()); | 100 | parent->OnFramebufferSizeChanged(); |
| 96 | } | 101 | } |
| 102 | |||
| 97 | private: | 103 | private: |
| 98 | GRenderWindow* parent_; | 104 | GRenderWindow* parent; |
| 99 | }; | 105 | }; |
| 100 | 106 | ||
| 101 | EmuThread& GRenderWindow::GetEmuThread() | 107 | EmuThread& GRenderWindow::GetEmuThread() |
| @@ -105,6 +111,9 @@ EmuThread& GRenderWindow::GetEmuThread() | |||
| 105 | 111 | ||
| 106 | GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0) | 112 | GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0) |
| 107 | { | 113 | { |
| 114 | std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); | ||
| 115 | setWindowTitle(QString::fromStdString(window_title)); | ||
| 116 | |||
| 108 | keyboard_id = KeyMap::NewDeviceId(); | 117 | keyboard_id = KeyMap::NewDeviceId(); |
| 109 | ReloadSetKeymaps(); | 118 | ReloadSetKeymaps(); |
| 110 | 119 | ||
| @@ -114,16 +123,25 @@ GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this | |||
| 114 | fmt.setProfile(QGLFormat::CoreProfile); | 123 | fmt.setProfile(QGLFormat::CoreProfile); |
| 115 | // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X | 124 | // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X |
| 116 | fmt.setOption(QGL::NoDeprecatedFunctions); | 125 | fmt.setOption(QGL::NoDeprecatedFunctions); |
| 117 | 126 | ||
| 118 | child = new GGLWidgetInternal(fmt, this); | 127 | child = new GGLWidgetInternal(fmt, this); |
| 119 | QBoxLayout* layout = new QHBoxLayout(this); | 128 | QBoxLayout* layout = new QHBoxLayout(this); |
| 120 | resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); | 129 | resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight); |
| 121 | layout->addWidget(child); | 130 | layout->addWidget(child); |
| 122 | layout->setMargin(0); | 131 | layout->setMargin(0); |
| 123 | setLayout(layout); | 132 | setLayout(layout); |
| 124 | QObject::connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext())); | 133 | connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext())); |
| 134 | |||
| 135 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | ||
| 136 | |||
| 137 | OnFramebufferSizeChanged(); | ||
| 138 | NotifyClientAreaSizeChanged(std::pair<unsigned,unsigned>(child->width(), child->height())); | ||
| 125 | 139 | ||
| 126 | BackupGeometry(); | 140 | BackupGeometry(); |
| 141 | |||
| 142 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | ||
| 143 | connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, SLOT(OnFramebufferSizeChanged())); | ||
| 144 | #endif | ||
| 127 | } | 145 | } |
| 128 | 146 | ||
| 129 | void GRenderWindow::moveContext() | 147 | void GRenderWindow::moveContext() |
| @@ -166,14 +184,28 @@ void GRenderWindow::DoneCurrent() | |||
| 166 | } | 184 | } |
| 167 | 185 | ||
| 168 | void GRenderWindow::PollEvents() { | 186 | void GRenderWindow::PollEvents() { |
| 169 | // TODO(ShizZy): Does this belong here? This is a reasonable place to update the window title | 187 | } |
| 170 | // from the main thread, but this should probably be in an event handler... | 188 | |
| 171 | /* | 189 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). |
| 172 | static char title[128]; | 190 | // |
| 173 | sprintf(title, "%s (FPS: %02.02f)", window_title_.c_str(), | 191 | // Older versions get the window size (density independent pixels), |
| 174 | video_core::g_renderer->current_fps()); | 192 | // and hence, do not support DPI scaling ("retina" displays). |
| 175 | setWindowTitle(title); | 193 | // The result will be a viewport that is smaller than the extent of the window. |
| 176 | */ | 194 | void GRenderWindow::OnFramebufferSizeChanged() |
| 195 | { | ||
| 196 | // Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size | ||
| 197 | #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) | ||
| 198 | // windowHandle() might not be accessible until the window is displayed to screen. | ||
| 199 | auto pixel_ratio = windowHandle() ? (windowHandle()->screen()->devicePixelRatio()) : 1.0; | ||
| 200 | |||
| 201 | unsigned width = child->QPaintDevice::width() * pixel_ratio; | ||
| 202 | unsigned height = child->QPaintDevice::height() * pixel_ratio; | ||
| 203 | #else | ||
| 204 | unsigned width = child->QPaintDevice::width(); | ||
| 205 | unsigned height = child->QPaintDevice::height(); | ||
| 206 | #endif | ||
| 207 | |||
| 208 | NotifyFramebufferSizeChanged(std::make_pair(width, height)); | ||
| 177 | } | 209 | } |
| 178 | 210 | ||
| 179 | void GRenderWindow::BackupGeometry() | 211 | void GRenderWindow::BackupGeometry() |
| @@ -236,3 +268,11 @@ void GRenderWindow::ReloadSetKeymaps() | |||
| 236 | KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN); | 268 | KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN); |
| 237 | } | 269 | } |
| 238 | 270 | ||
| 271 | void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) | ||
| 272 | { | ||
| 273 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); | ||
| 274 | } | ||
| 275 | |||
| 276 | void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { | ||
| 277 | setMinimumSize(minimal_size.first, minimal_size.second); | ||
| 278 | } | ||
diff --git a/src/citra_qt/bootmanager.hxx b/src/citra_qt/bootmanager.hxx index f8afc403e..3eec1668e 100644 --- a/src/citra_qt/bootmanager.hxx +++ b/src/citra_qt/bootmanager.hxx | |||
| @@ -1,12 +1,16 @@ | |||
| 1 | #include <atomic> | ||
| 2 | |||
| 1 | #include <QThread> | 3 | #include <QThread> |
| 2 | #include <QGLWidget> | 4 | #include <QGLWidget> |
| 3 | #include <atomic> | 5 | |
| 4 | #include "common/common.h" | 6 | #include "common/common.h" |
| 5 | #include "common/emu_window.h" | 7 | #include "common/emu_window.h" |
| 6 | 8 | ||
| 7 | class GRenderWindow; | 9 | class QScreen; |
| 8 | class QKeyEvent; | 10 | class QKeyEvent; |
| 9 | 11 | ||
| 12 | class GRenderWindow; | ||
| 13 | |||
| 10 | class EmuThread : public QThread | 14 | class EmuThread : public QThread |
| 11 | { | 15 | { |
| 12 | Q_OBJECT | 16 | Q_OBJECT |
| @@ -74,7 +78,7 @@ private: | |||
| 74 | signals: | 78 | signals: |
| 75 | /** | 79 | /** |
| 76 | * Emitted when CPU when we've finished processing a single Gekko instruction | 80 | * Emitted when CPU when we've finished processing a single Gekko instruction |
| 77 | * | 81 | * |
| 78 | * @warning This will only be emitted when the CPU is not running (SetCpuRunning(false)) | 82 | * @warning This will only be emitted when the CPU is not running (SetCpuRunning(false)) |
| 79 | * @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) | 83 | * @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) |
| 80 | */ | 84 | */ |
| @@ -100,7 +104,7 @@ public: | |||
| 100 | void BackupGeometry(); | 104 | void BackupGeometry(); |
| 101 | void RestoreGeometry(); | 105 | void RestoreGeometry(); |
| 102 | void restoreGeometry(const QByteArray& geometry); // overridden | 106 | void restoreGeometry(const QByteArray& geometry); // overridden |
| 103 | QByteArray saveGeometry(); // overridden | 107 | QByteArray saveGeometry(); // overridden |
| 104 | 108 | ||
| 105 | EmuThread& GetEmuThread(); | 109 | EmuThread& GetEmuThread(); |
| 106 | 110 | ||
| @@ -109,10 +113,16 @@ public: | |||
| 109 | 113 | ||
| 110 | void ReloadSetKeymaps() override; | 114 | void ReloadSetKeymaps() override; |
| 111 | 115 | ||
| 116 | void OnClientAreaResized(unsigned width, unsigned height); | ||
| 117 | |||
| 118 | void OnFramebufferSizeChanged(); | ||
| 119 | |||
| 112 | public slots: | 120 | public slots: |
| 113 | void moveContext(); | 121 | void moveContext(); // overridden |
| 114 | 122 | ||
| 115 | private: | 123 | private: |
| 124 | void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override; | ||
| 125 | |||
| 116 | QGLWidget* child; | 126 | QGLWidget* child; |
| 117 | 127 | ||
| 118 | EmuThread emu_thread; | 128 | EmuThread emu_thread; |
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() | |||
| 117 | connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); | 117 | connect(GetHotkey("Main Window", "Load File", this), SIGNAL(activated()), this, SLOT(OnMenuLoadFile())); |
| 118 | connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); | 118 | connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); |
| 119 | 119 | ||
| 120 | setWindowTitle(render_window->GetWindowTitle().c_str()); | 120 | std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); |
| 121 | setWindowTitle(window_title.c_str()); | ||
| 121 | 122 | ||
| 122 | show(); | 123 | show(); |
| 123 | 124 | ||
diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 6c2b598f6..4cb94fed1 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h | |||
| @@ -9,17 +9,33 @@ | |||
| 9 | #include "common/string_util.h" | 9 | #include "common/string_util.h" |
| 10 | #include "common/key_map.h" | 10 | #include "common/key_map.h" |
| 11 | 11 | ||
| 12 | // Abstraction class used to provide an interface between emulation code and the frontend (e.g. SDL, | 12 | /** |
| 13 | // QGLWidget, GLFW, etc...) | 13 | * Abstraction class used to provide an interface between emulation code and the frontend |
| 14 | * (e.g. SDL, QGLWidget, GLFW, etc...). | ||
| 15 | * | ||
| 16 | * Design notes on the interaction between EmuWindow and the emulation core: | ||
| 17 | * - Generally, decisions on anything visible to the user should be left up to the GUI. | ||
| 18 | * For example, the emulation core should not try to dictate some window title or size. | ||
| 19 | * This stuff is not the core's business and only causes problems with regards to thread-safety | ||
| 20 | * anyway. | ||
| 21 | * - Under certain circumstances, it may be desirable for the core to politely request the GUI | ||
| 22 | * to set e.g. a minimum window size. However, the GUI should always be free to ignore any | ||
| 23 | * such hints. | ||
| 24 | * - EmuWindow may expose some of its state as read-only to the emulation core, however care | ||
| 25 | * should be taken to make sure the provided information is self-consistent. This requires | ||
| 26 | * some sort of synchronization (most of this is still a TODO). | ||
| 27 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please | ||
| 28 | * re-read the upper points again and think about it if you don't see this. | ||
| 29 | */ | ||
| 14 | class EmuWindow | 30 | class EmuWindow |
| 15 | { | 31 | { |
| 16 | |||
| 17 | public: | 32 | public: |
| 18 | /// Data structure to store an emuwindow configuration | 33 | /// Data structure to store emuwindow configuration |
| 19 | struct WindowConfig { | 34 | struct WindowConfig { |
| 20 | bool fullscreen; | 35 | bool fullscreen; |
| 21 | int res_width; | 36 | int res_width; |
| 22 | int res_height; | 37 | int res_height; |
| 38 | std::pair<unsigned,unsigned> min_client_area_size; | ||
| 23 | }; | 39 | }; |
| 24 | 40 | ||
| 25 | /// Swap buffers to display the next frame | 41 | /// Swap buffers to display the next frame |
| @@ -42,52 +58,96 @@ public: | |||
| 42 | /// Signals a key release action to the HID module | 58 | /// Signals a key release action to the HID module |
| 43 | static void KeyReleased(KeyMap::HostDeviceKey key); | 59 | static void KeyReleased(KeyMap::HostDeviceKey key); |
| 44 | 60 | ||
| 45 | WindowConfig GetConfig() const { | 61 | /** |
| 46 | return m_config; | 62 | * Returns currently active configuration. |
| 63 | * @note Accesses to the returned object need not be consistent because it may be modified in another thread | ||
| 64 | */ | ||
| 65 | const WindowConfig& GetActiveConfig() const { | ||
| 66 | return active_config; | ||
| 47 | } | 67 | } |
| 48 | 68 | ||
| 69 | /** | ||
| 70 | * Requests the internal configuration to be replaced by the specified argument at some point in the future. | ||
| 71 | * @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. | ||
| 72 | */ | ||
| 49 | void SetConfig(const WindowConfig& val) { | 73 | void SetConfig(const WindowConfig& val) { |
| 50 | m_config = val; | 74 | config = val; |
| 51 | } | ||
| 52 | |||
| 53 | int GetClientAreaWidth() const { | ||
| 54 | return m_client_area_width; | ||
| 55 | } | 75 | } |
| 56 | 76 | ||
| 57 | void SetClientAreaWidth(const int val) { | 77 | /** |
| 58 | m_client_area_width = val; | 78 | * Gets the framebuffer size in pixels. |
| 79 | * @note This method is thread-safe | ||
| 80 | */ | ||
| 81 | const std::pair<unsigned,unsigned> GetFramebufferSize() const { | ||
| 82 | return framebuffer_size; | ||
| 59 | } | 83 | } |
| 60 | 84 | ||
| 61 | int GetClientAreaHeight() const { | 85 | /** |
| 62 | return m_client_area_height; | 86 | * Gets window client area width in logical coordinates. |
| 87 | * @note For high-DPI systems, this is smaller than the framebuffer size. | ||
| 88 | * @note This method is thread-safe | ||
| 89 | */ | ||
| 90 | std::pair<unsigned,unsigned> GetClientAreaSize() const { | ||
| 91 | return std::make_pair(client_area_width, client_area_height); | ||
| 63 | } | 92 | } |
| 64 | 93 | ||
| 65 | void SetClientAreaHeight(const int val) { | 94 | protected: |
| 66 | m_client_area_height = val; | 95 | EmuWindow() |
| 96 | { | ||
| 97 | // TODO: Find a better place to set this. | ||
| 98 | config.min_client_area_size = std::make_pair(400u, 480u); | ||
| 99 | active_config = config; | ||
| 67 | } | 100 | } |
| 101 | virtual ~EmuWindow() {} | ||
| 68 | 102 | ||
| 69 | std::string GetWindowTitle() const { | 103 | /** |
| 70 | return m_window_title; | 104 | * Processes any pending configuration changes from the last SetConfig call. |
| 105 | * This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration | ||
| 106 | * field changed. | ||
| 107 | * @note Implementations will usually want to call this from the GUI thread. | ||
| 108 | * @todo Actually call this in existing implementations. | ||
| 109 | */ | ||
| 110 | void ProcessConfigurationChanges() { | ||
| 111 | // TODO: For proper thread safety, we should eventually implement a proper | ||
| 112 | // multiple-writer/single-reader queue... | ||
| 113 | |||
| 114 | if (config.min_client_area_size != active_config.min_client_area_size) { | ||
| 115 | OnMinimalClientAreaChangeRequest(config.min_client_area_size); | ||
| 116 | config.min_client_area_size = active_config.min_client_area_size; | ||
| 117 | } | ||
| 71 | } | 118 | } |
| 72 | 119 | ||
| 73 | void SetWindowTitle(std::string val) { | 120 | /** |
| 74 | m_window_title = val; | 121 | * Update internal framebuffer size with the given parameter. |
| 122 | * @note EmuWindow implementations will usually use this in window resize event handlers. | ||
| 123 | */ | ||
| 124 | void NotifyFramebufferSizeChanged(const std::pair<unsigned,unsigned>& size) { | ||
| 125 | framebuffer_size = size; | ||
| 75 | } | 126 | } |
| 76 | 127 | ||
| 77 | protected: | 128 | /** |
| 78 | EmuWindow(): | 129 | * Update internal client area size with the given parameter. |
| 79 | m_window_title(Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc)), | 130 | * @note EmuWindow implementations will usually use this in window resize event handlers. |
| 80 | m_client_area_width(640), | 131 | */ |
| 81 | m_client_area_height(480) | 132 | void NotifyClientAreaSizeChanged(const std::pair<unsigned,unsigned>& size) { |
| 82 | {} | 133 | client_area_width = size.first; |
| 83 | virtual ~EmuWindow() {} | 134 | client_area_height = size.second; |
| 135 | } | ||
| 84 | 136 | ||
| 85 | std::string m_window_title; ///< Current window title, should be used by window impl. | 137 | private: |
| 138 | /** | ||
| 139 | * Handler called when the minimal client area was requested to be changed via SetConfig. | ||
| 140 | * For the request to be honored, EmuWindow implementations will usually reimplement this function. | ||
| 141 | */ | ||
| 142 | virtual void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) { | ||
| 143 | // By default, ignore this request and do nothing. | ||
| 144 | } | ||
| 86 | 145 | ||
| 87 | int m_client_area_width; ///< Current client width, should be set by window impl. | 146 | std::pair<unsigned,unsigned> framebuffer_size; |
| 88 | int m_client_area_height; ///< Current client height, should be set by window impl. | ||
| 89 | 147 | ||
| 90 | private: | 148 | unsigned client_area_width; ///< Current client width, should be set by window impl. |
| 91 | WindowConfig m_config; ///< Internal configuration | 149 | unsigned client_area_height; ///< Current client height, should be set by window impl. |
| 92 | 150 | ||
| 151 | WindowConfig config; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges) | ||
| 152 | WindowConfig active_config; ///< Internal active configuration | ||
| 93 | }; | 153 | }; |
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 { | |||
| 69 | HW, | 69 | HW, |
| 70 | TIME, | 70 | TIME, |
| 71 | NETPLAY, | 71 | NETPLAY, |
| 72 | GUI, | ||
| 72 | 73 | ||
| 73 | NUMBER_OF_LOGS // Must be last | 74 | NUMBER_OF_LOGS // Must be last |
| 74 | }; | 75 | }; |
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() | |||
| 75 | m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay"); | 75 | m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay"); |
| 76 | m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager"); | 76 | m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager"); |
| 77 | m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay"); | 77 | m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay"); |
| 78 | m_Log[LogTypes::GUI] = new LogContainer("GUI", "GUI"); | ||
| 78 | 79 | ||
| 79 | m_fileLog = new FileLogListener(FileUtil::GetUserPath(F_MAINLOG_IDX).c_str()); | 80 | m_fileLog = new FileLogListener(FileUtil::GetUserPath(F_MAINLOG_IDX).c_str()); |
| 80 | m_consoleLog = new ConsoleListener(); | 81 | m_consoleLog = new ConsoleListener(); |
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 @@ | |||
| 7 | #include "common/common.h" | 7 | #include "common/common.h" |
| 8 | 8 | ||
| 9 | #include <algorithm> | 9 | #include <algorithm> |
| 10 | #include <type_traits> | ||
| 10 | #include <vector> | 11 | #include <vector> |
| 11 | 12 | ||
| 12 | namespace MathUtil | 13 | namespace MathUtil |
| @@ -109,11 +110,11 @@ struct Rectangle | |||
| 109 | Rectangle(T theLeft, T theTop, T theRight, T theBottom) | 110 | Rectangle(T theLeft, T theTop, T theRight, T theBottom) |
| 110 | : left(theLeft), top(theTop), right(theRight), bottom(theBottom) | 111 | : left(theLeft), top(theTop), right(theRight), bottom(theBottom) |
| 111 | { } | 112 | { } |
| 112 | 113 | ||
| 113 | bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; } | 114 | bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; } |
| 114 | 115 | ||
| 115 | T GetWidth() const { return abs(right - left); } | 116 | T GetWidth() const { return std::abs(static_cast<typename std::make_signed<T>::type>(right - left)); } |
| 116 | T GetHeight() const { return abs(bottom - top); } | 117 | T GetHeight() const { return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); } |
| 117 | 118 | ||
| 118 | // If the rectangle is in a coordinate system with a lower-left origin, use | 119 | // If the rectangle is in a coordinate system with a lower-left origin, use |
| 119 | // this Clamp. | 120 | // this Clamp. |
| @@ -127,7 +128,7 @@ struct Rectangle | |||
| 127 | 128 | ||
| 128 | // If the rectangle is in a coordinate system with an upper-left origin, | 129 | // If the rectangle is in a coordinate system with an upper-left origin, |
| 129 | // use this Clamp. | 130 | // use this Clamp. |
| 130 | void ClampUL(T x1, T y1, T x2, T y2) | 131 | void ClampUL(T x1, T y1, T x2, T y2) |
| 131 | { | 132 | { |
| 132 | if (left < x1) left = x1; | 133 | if (left < x1) left = x1; |
| 133 | if (right > x2) right = x2; | 134 | if (right > x2) right = x2; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 8483f79be..729e8e73d 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 | |||
| 191 | * Draws the emulated screens to the emulator window. | 191 | * Draws the emulated screens to the emulator window. |
| 192 | */ | 192 | */ |
| 193 | void RendererOpenGL::DrawScreens() { | 193 | void RendererOpenGL::DrawScreens() { |
| 194 | glViewport(0, 0, resolution_width, resolution_height); | 194 | auto viewport_extent = GetViewportExtent(); |
| 195 | glViewport(viewport_extent.left, viewport_extent.top, viewport_extent.GetWidth(), viewport_extent.GetHeight()); // TODO: Or bottom? | ||
| 195 | glClear(GL_COLOR_BUFFER_BIT); | 196 | glClear(GL_COLOR_BUFFER_BIT); |
| 196 | 197 | ||
| 197 | glUseProgram(program_id); | 198 | glUseProgram(program_id); |
| @@ -228,6 +229,34 @@ void RendererOpenGL::SetWindow(EmuWindow* window) { | |||
| 228 | render_window = window; | 229 | render_window = window; |
| 229 | } | 230 | } |
| 230 | 231 | ||
| 232 | MathUtil::Rectangle<unsigned> RendererOpenGL::GetViewportExtent() { | ||
| 233 | unsigned framebuffer_width; | ||
| 234 | unsigned framebuffer_height; | ||
| 235 | std::tie(framebuffer_width, framebuffer_height) = render_window->GetFramebufferSize(); | ||
| 236 | |||
| 237 | float window_aspect_ratio = static_cast<float>(framebuffer_height) / framebuffer_width; | ||
| 238 | float emulation_aspect_ratio = static_cast<float>(resolution_height) / resolution_width; | ||
| 239 | |||
| 240 | MathUtil::Rectangle<unsigned> viewport_extent; | ||
| 241 | if (window_aspect_ratio > emulation_aspect_ratio) { | ||
| 242 | // Window is narrower than the emulation content => apply borders to the top and bottom | ||
| 243 | unsigned viewport_height = emulation_aspect_ratio * framebuffer_width; | ||
| 244 | viewport_extent.left = 0; | ||
| 245 | viewport_extent.top = (framebuffer_height - viewport_height) / 2; | ||
| 246 | viewport_extent.right = viewport_extent.left + framebuffer_width; | ||
| 247 | viewport_extent.bottom = viewport_extent.top + viewport_height; | ||
| 248 | } else { | ||
| 249 | // Otherwise, apply borders to the left and right sides of the window. | ||
| 250 | unsigned viewport_width = framebuffer_height / emulation_aspect_ratio; | ||
| 251 | viewport_extent.left = (framebuffer_width - viewport_width) / 2; | ||
| 252 | viewport_extent.top = 0; | ||
| 253 | viewport_extent.right = viewport_extent.left + viewport_width; | ||
| 254 | viewport_extent.bottom = viewport_extent.top + framebuffer_height; | ||
| 255 | } | ||
| 256 | |||
| 257 | return viewport_extent; | ||
| 258 | } | ||
| 259 | |||
| 231 | /// Initialize the renderer | 260 | /// Initialize the renderer |
| 232 | void RendererOpenGL::Init() { | 261 | void RendererOpenGL::Init() { |
| 233 | render_window->MakeCurrent(); | 262 | 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..7fdcec731 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -4,13 +4,15 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | |||
| 7 | #include "generated/gl_3_2_core.h" | 9 | #include "generated/gl_3_2_core.h" |
| 8 | 10 | ||
| 9 | #include "common/common.h" | 11 | #include "common/math_util.h" |
| 12 | |||
| 10 | #include "core/hw/gpu.h" | 13 | #include "core/hw/gpu.h" |
| 11 | #include "video_core/renderer_base.h" | ||
| 12 | 14 | ||
| 13 | #include <array> | 15 | #include "video_core/renderer_base.h" |
| 14 | 16 | ||
| 15 | class EmuWindow; | 17 | class EmuWindow; |
| 16 | 18 | ||
| @@ -52,6 +54,9 @@ private: | |||
| 52 | static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, | 54 | static void LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer, |
| 53 | const TextureInfo& texture); | 55 | const TextureInfo& texture); |
| 54 | 56 | ||
| 57 | /// Computes the viewport rectangle | ||
| 58 | MathUtil::Rectangle<unsigned> GetViewportExtent(); | ||
| 59 | |||
| 55 | EmuWindow* render_window; ///< Handle to render window | 60 | EmuWindow* render_window; ///< Handle to render window |
| 56 | u32 last_mode; ///< Last render mode | 61 | u32 last_mode; ///< Last render mode |
| 57 | 62 | ||