summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/emu_window/emu_window_glfw.cpp70
-rw-r--r--src/citra/emu_window/emu_window_glfw.h14
-rw-r--r--src/citra_qt/bootmanager.cpp76
-rw-r--r--src/citra_qt/bootmanager.hxx20
-rw-r--r--src/citra_qt/main.cpp3
-rw-r--r--src/common/emu_window.h128
-rw-r--r--src/common/log.h1
-rw-r--r--src/common/log_manager.cpp1
-rw-r--r--src/common/math_util.h9
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp31
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h11
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
15EmuWindow_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
14void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) { 20void 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
38void 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
45void 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
38EmuWindow_GLFW::EmuWindow_GLFW() { 56EmuWindow_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
144void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
145 std::pair<int,int> current_size;
146 glfwGetWindowSize(m_render_window, &current_size.first, &current_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
9struct GLFWwindow;
10
11class EmuWindow_GLFW : public EmuWindow { 11class EmuWindow_GLFW : public EmuWindow {
12public: 12public:
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
35private: 39private:
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()
82class GGLWidgetInternal : public QGLWidget 88class GGLWidgetInternal : public QGLWidget
83{ 89{
84public: 90public:
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
97private: 103private:
98 GRenderWindow* parent_; 104 GRenderWindow* parent;
99}; 105};
100 106
101EmuThread& GRenderWindow::GetEmuThread() 107EmuThread& GRenderWindow::GetEmuThread()
@@ -105,6 +111,9 @@ EmuThread& GRenderWindow::GetEmuThread()
105 111
106GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0) 112GRenderWindow::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
129void GRenderWindow::moveContext() 147void GRenderWindow::moveContext()
@@ -166,14 +184,28 @@ void GRenderWindow::DoneCurrent()
166} 184}
167 185
168void GRenderWindow::PollEvents() { 186void 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 */ 194void 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
179void GRenderWindow::BackupGeometry() 211void 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
271void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height)
272{
273 NotifyClientAreaSizeChanged(std::make_pair(width, height));
274}
275
276void 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
7class GRenderWindow; 9class QScreen;
8class QKeyEvent; 10class QKeyEvent;
9 11
12class GRenderWindow;
13
10class EmuThread : public QThread 14class EmuThread : public QThread
11{ 15{
12 Q_OBJECT 16 Q_OBJECT
@@ -74,7 +78,7 @@ private:
74signals: 78signals:
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
112public slots: 120public slots:
113 void moveContext(); 121 void moveContext(); // overridden
114 122
115private: 123private:
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 */
14class EmuWindow 30class EmuWindow
15{ 31{
16
17public: 32public:
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) { 94protected:
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
77protected: 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. 137private:
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
90private: 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
12namespace MathUtil 13namespace 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 */
193void RendererOpenGL::DrawScreens() { 193void 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
232MathUtil::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
232void RendererOpenGL::Init() { 261void 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
15class EmuWindow; 17class 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