summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/frontend/framebuffer_layout.cpp15
-rw-r--r--src/core/frontend/framebuffer_layout.h7
-rw-r--r--src/video_core/renderer_base.cpp12
-rw-r--r--src/video_core/renderer_base.h27
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp41
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h9
-rw-r--r--src/video_core/video_core.cpp8
-rw-r--r--src/video_core/video_core.h2
-rw-r--r--src/yuzu/bootmanager.cpp18
-rw-r--r--src/yuzu/bootmanager.h6
-rw-r--r--src/yuzu/configuration/config.cpp5
-rw-r--r--src/yuzu/main.cpp28
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/main.ui88
-rw-r--r--src/yuzu/ui_settings.h4
15 files changed, 228 insertions, 43 deletions
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index 8f07aedc9..f8662d193 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "core/frontend/framebuffer_layout.h" 8#include "core/frontend/framebuffer_layout.h"
9#include "core/settings.h"
9 10
10namespace Layout { 11namespace Layout {
11 12
@@ -42,4 +43,18 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
42 return res; 43 return res;
43} 44}
44 45
46FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale) {
47 int width, height;
48
49 if (Settings::values.use_docked_mode) {
50 width = ScreenDocked::WidthDocked * res_scale;
51 height = ScreenDocked::HeightDocked * res_scale;
52 } else {
53 width = ScreenUndocked::Width * res_scale;
54 height = ScreenUndocked::Height * res_scale;
55 }
56
57 return DefaultFrameLayout(width, height);
58}
59
45} // namespace Layout 60} // namespace Layout
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index f3fddfa94..e06647794 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -9,6 +9,7 @@
9namespace Layout { 9namespace Layout {
10 10
11enum ScreenUndocked : unsigned { Width = 1280, Height = 720 }; 11enum ScreenUndocked : unsigned { Width = 1280, Height = 720 };
12enum ScreenDocked : unsigned { WidthDocked = 1920, HeightDocked = 1080 };
12 13
13/// Describes the layout of the window framebuffer 14/// Describes the layout of the window framebuffer
14struct FramebufferLayout { 15struct FramebufferLayout {
@@ -34,4 +35,10 @@ struct FramebufferLayout {
34 */ 35 */
35FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height); 36FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height);
36 37
38/**
39 * Convenience method to get frame layout by resolution scale
40 * @param res_scale resolution scale factor
41 */
42FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale);
43
37} // namespace Layout 44} // namespace Layout
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 1482cdb40..94223f45f 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -27,4 +27,16 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
27 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height); 27 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
28} 28}
29 29
30void RendererBase::RequestScreenshot(void* data, std::function<void()> callback,
31 const Layout::FramebufferLayout& layout) {
32 if (renderer_settings.screenshot_requested) {
33 LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
34 return;
35 }
36 renderer_settings.screenshot_bits = data;
37 renderer_settings.screenshot_complete_callback = std::move(callback);
38 renderer_settings.screenshot_framebuffer_layout = layout;
39 renderer_settings.screenshot_requested = true;
40}
41
30} // namespace VideoCore 42} // namespace VideoCore
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 669e26e15..1d54c3723 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -9,6 +9,7 @@
9#include <optional> 9#include <optional>
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/frontend/emu_window.h"
12#include "video_core/gpu.h" 13#include "video_core/gpu.h"
13#include "video_core/rasterizer_interface.h" 14#include "video_core/rasterizer_interface.h"
14 15
@@ -21,6 +22,12 @@ namespace VideoCore {
21struct RendererSettings { 22struct RendererSettings {
22 std::atomic_bool use_framelimiter{false}; 23 std::atomic_bool use_framelimiter{false};
23 std::atomic_bool set_background_color{false}; 24 std::atomic_bool set_background_color{false};
25
26 // Screenshot
27 std::atomic<bool> screenshot_requested{false};
28 void* screenshot_bits;
29 std::function<void()> screenshot_complete_callback;
30 Layout::FramebufferLayout screenshot_framebuffer_layout;
24}; 31};
25 32
26class RendererBase : NonCopyable { 33class RendererBase : NonCopyable {
@@ -57,9 +64,29 @@ public:
57 return *rasterizer; 64 return *rasterizer;
58 } 65 }
59 66
67 Core::Frontend::EmuWindow& GetRenderWindow() {
68 return render_window;
69 }
70
71 const Core::Frontend::EmuWindow& GetRenderWindow() const {
72 return render_window;
73 }
74
75 RendererSettings& Settings() {
76 return renderer_settings;
77 }
78
79 const RendererSettings& Settings() const {
80 return renderer_settings;
81 }
82
60 /// Refreshes the settings common to all renderers 83 /// Refreshes the settings common to all renderers
61 void RefreshBaseSettings(); 84 void RefreshBaseSettings();
62 85
86 /// Request a screenshot of the next frame
87 void RequestScreenshot(void* data, std::function<void()> callback,
88 const Layout::FramebufferLayout& layout);
89
63protected: 90protected:
64 Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle. 91 Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
65 std::unique_ptr<RasterizerInterface> rasterizer; 92 std::unique_ptr<RasterizerInterface> rasterizer;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 4fd0d66c5..49a1989e4 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -138,7 +138,12 @@ void RendererOpenGL::SwapBuffers(
138 138
139 // Load the framebuffer from memory, draw it to the screen, and swap buffers 139 // Load the framebuffer from memory, draw it to the screen, and swap buffers
140 LoadFBToScreenInfo(*framebuffer); 140 LoadFBToScreenInfo(*framebuffer);
141 DrawScreen(); 141
142 if (renderer_settings.screenshot_requested)
143 CaptureScreenshot();
144
145 DrawScreen(render_window.GetFramebufferLayout());
146
142 render_window.SwapBuffers(); 147 render_window.SwapBuffers();
143 } 148 }
144 149
@@ -383,14 +388,13 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
383/** 388/**
384 * Draws the emulated screens to the emulator window. 389 * Draws the emulated screens to the emulator window.
385 */ 390 */
386void RendererOpenGL::DrawScreen() { 391void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
387 if (renderer_settings.set_background_color) { 392 if (renderer_settings.set_background_color) {
388 // Update background color before drawing 393 // Update background color before drawing
389 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 394 glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
390 0.0f); 395 0.0f);
391 } 396 }
392 397
393 const auto& layout = render_window.GetFramebufferLayout();
394 const auto& screen = layout.screen; 398 const auto& screen = layout.screen;
395 399
396 glViewport(0, 0, layout.width, layout.height); 400 glViewport(0, 0, layout.width, layout.height);
@@ -414,6 +418,37 @@ void RendererOpenGL::DrawScreen() {
414/// Updates the framerate 418/// Updates the framerate
415void RendererOpenGL::UpdateFramerate() {} 419void RendererOpenGL::UpdateFramerate() {}
416 420
421void RendererOpenGL::CaptureScreenshot() {
422 // Draw the current frame to the screenshot framebuffer
423 screenshot_framebuffer.Create();
424 GLuint old_read_fb = state.draw.read_framebuffer;
425 GLuint old_draw_fb = state.draw.draw_framebuffer;
426 state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle;
427 state.Apply();
428
429 Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
430
431 GLuint renderbuffer;
432 glGenRenderbuffers(1, &renderbuffer);
433 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
434 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, layout.width, layout.height);
435 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
436
437 DrawScreen(layout);
438
439 glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
440 renderer_settings.screenshot_bits);
441
442 screenshot_framebuffer.Release();
443 state.draw.read_framebuffer = old_read_fb;
444 state.draw.draw_framebuffer = old_draw_fb;
445 state.Apply();
446 glDeleteRenderbuffers(1, &renderbuffer);
447
448 renderer_settings.screenshot_complete_callback();
449 renderer_settings.screenshot_requested = false;
450}
451
417static const char* GetSource(GLenum source) { 452static const char* GetSource(GLenum source) {
418#define RET(s) \ 453#define RET(s) \
419 case GL_DEBUG_SOURCE_##s: \ 454 case GL_DEBUG_SOURCE_##s: \
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index c0868c0e4..067fad81b 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -16,6 +16,10 @@ namespace Core::Frontend {
16class EmuWindow; 16class EmuWindow;
17} 17}
18 18
19namespace Layout {
20class FramebufferLayout;
21}
22
19namespace OpenGL { 23namespace OpenGL {
20 24
21/// Structure used for storing information about the textures for the Switch screen 25/// Structure used for storing information about the textures for the Switch screen
@@ -66,10 +70,12 @@ private:
66 70
67 void ConfigureFramebufferTexture(TextureInfo& texture, 71 void ConfigureFramebufferTexture(TextureInfo& texture,
68 const Tegra::FramebufferConfig& framebuffer); 72 const Tegra::FramebufferConfig& framebuffer);
69 void DrawScreen(); 73 void DrawScreen(const Layout::FramebufferLayout& layout);
70 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h); 74 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
71 void UpdateFramerate(); 75 void UpdateFramerate();
72 76
77 void CaptureScreenshot();
78
73 // Loads framebuffer from emulated memory into the display information structure 79 // Loads framebuffer from emulated memory into the display information structure
74 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); 80 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
75 // Fills active OpenGL texture with the given RGBA color. 81 // Fills active OpenGL texture with the given RGBA color.
@@ -82,6 +88,7 @@ private:
82 OGLVertexArray vertex_array; 88 OGLVertexArray vertex_array;
83 OGLBuffer vertex_buffer; 89 OGLBuffer vertex_buffer;
84 OGLProgram shader; 90 OGLProgram shader;
91 OGLFramebuffer screenshot_framebuffer;
85 92
86 /// Display information for Switch screen 93 /// Display information for Switch screen
87 ScreenInfo screen_info; 94 ScreenInfo screen_info;
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 07e3a7d24..f7de3471b 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -3,6 +3,8 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include "core/core.h"
7#include "core/settings.h"
6#include "video_core/renderer_base.h" 8#include "video_core/renderer_base.h"
7#include "video_core/renderer_opengl/renderer_opengl.h" 9#include "video_core/renderer_opengl/renderer_opengl.h"
8#include "video_core/video_core.h" 10#include "video_core/video_core.h"
@@ -13,4 +15,10 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
13 return std::make_unique<OpenGL::RendererOpenGL>(emu_window); 15 return std::make_unique<OpenGL::RendererOpenGL>(emu_window);
14} 16}
15 17
18u16 GetResolutionScaleFactor(const RendererBase& renderer) {
19 return !Settings::values.resolution_factor
20 ? renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio()
21 : Settings::values.resolution_factor;
22}
23
16} // namespace VideoCore 24} // namespace VideoCore
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index f79f85dfe..5b373bcb1 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -22,4 +22,6 @@ class RendererBase;
22 */ 22 */
23std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window); 23std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window);
24 24
25u16 GetResolutionScaleFactor(const RendererBase& renderer);
26
25} // namespace VideoCore 27} // namespace VideoCore
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 384e17921..40db7a5e9 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -14,6 +14,8 @@
14#include "input_common/keyboard.h" 14#include "input_common/keyboard.h"
15#include "input_common/main.h" 15#include "input_common/main.h"
16#include "input_common/motion_emu.h" 16#include "input_common/motion_emu.h"
17#include "video_core/renderer_base.h"
18#include "video_core/video_core.h"
17#include "yuzu/bootmanager.h" 19#include "yuzu/bootmanager.h"
18 20
19EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} 21EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
@@ -333,6 +335,22 @@ void GRenderWindow::InitRenderTarget() {
333 BackupGeometry(); 335 BackupGeometry();
334} 336}
335 337
338void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) {
339 auto& renderer = Core::System::GetInstance().Renderer();
340
341 if (!res_scale)
342 res_scale = VideoCore::GetResolutionScaleFactor(renderer);
343
344 const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
345 screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
346 renderer.RequestScreenshot(screenshot_image.bits(),
347 [=] {
348 screenshot_image.mirrored(false, true).save(screenshot_path);
349 LOG_INFO(Frontend, "The screenshot is saved.");
350 },
351 layout);
352}
353
336void GRenderWindow::OnMinimalClientAreaChangeRequest( 354void GRenderWindow::OnMinimalClientAreaChangeRequest(
337 const std::pair<unsigned, unsigned>& minimal_size) { 355 const std::pair<unsigned, unsigned>& minimal_size) {
338 setMinimumSize(minimal_size.first, minimal_size.second); 356 setMinimumSize(minimal_size.first, minimal_size.second);
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 873985564..4e3028215 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -8,6 +8,7 @@
8#include <condition_variable> 8#include <condition_variable>
9#include <mutex> 9#include <mutex>
10#include <QGLWidget> 10#include <QGLWidget>
11#include <QImage>
11#include <QThread> 12#include <QThread>
12#include "common/thread.h" 13#include "common/thread.h"
13#include "core/core.h" 14#include "core/core.h"
@@ -139,6 +140,8 @@ public:
139 140
140 void InitRenderTarget(); 141 void InitRenderTarget();
141 142
143 void CaptureScreenshot(u16 res_scale, const QString& screenshot_path);
144
142public slots: 145public slots:
143 void moveContext(); // overridden 146 void moveContext(); // overridden
144 147
@@ -165,6 +168,9 @@ private:
165 168
166 EmuThread* emu_thread; 169 EmuThread* emu_thread;
167 170
171 /// Temporary storage of the screenshot taken
172 QImage screenshot_image;
173
168protected: 174protected:
169 void showEvent(QShowEvent* event) override; 175 void showEvent(QShowEvent* event) override;
170}; 176};
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index c26161169..02e09fa18 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -445,6 +445,8 @@ void Config::ReadValues() {
445 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); 445 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
446 UISettings::values.enable_discord_presence = 446 UISettings::values.enable_discord_presence =
447 qt_config->value("enable_discord_presence", true).toBool(); 447 qt_config->value("enable_discord_presence", true).toBool();
448 UISettings::values.screenshot_resolution_factor =
449 static_cast<u16>(qt_config->value("screenshot_resolution_factor", 0).toUInt());
448 450
449 qt_config->beginGroup("UIGameList"); 451 qt_config->beginGroup("UIGameList");
450 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool(); 452 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
@@ -648,6 +650,8 @@ void Config::SaveValues() {
648 qt_config->beginGroup("UI"); 650 qt_config->beginGroup("UI");
649 qt_config->setValue("theme", UISettings::values.theme); 651 qt_config->setValue("theme", UISettings::values.theme);
650 qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); 652 qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence);
653 qt_config->setValue("screenshot_resolution_factor",
654 UISettings::values.screenshot_resolution_factor);
651 655
652 qt_config->beginGroup("UIGameList"); 656 qt_config->beginGroup("UIGameList");
653 qt_config->setValue("show_unknown", UISettings::values.show_unknown); 657 qt_config->setValue("show_unknown", UISettings::values.show_unknown);
@@ -669,6 +673,7 @@ void Config::SaveValues() {
669 qt_config->beginGroup("Paths"); 673 qt_config->beginGroup("Paths");
670 qt_config->setValue("romsPath", UISettings::values.roms_path); 674 qt_config->setValue("romsPath", UISettings::values.roms_path);
671 qt_config->setValue("symbolsPath", UISettings::values.symbols_path); 675 qt_config->setValue("symbolsPath", UISettings::values.symbols_path);
676 qt_config->setValue("screenshotPath", UISettings::values.screenshot_path);
672 qt_config->setValue("gameListRootDir", UISettings::values.gamedir); 677 qt_config->setValue("gameListRootDir", UISettings::values.gamedir);
673 qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan); 678 qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan);
674 qt_config->setValue("recentFiles", UISettings::values.recent_files); 679 qt_config->setValue("recentFiles", UISettings::values.recent_files);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 22c207a3a..808f14fb3 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -334,6 +334,9 @@ void GMainWindow::InitializeHotkeys() {
334 Qt::ApplicationShortcut); 334 Qt::ApplicationShortcut);
335 hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2), 335 hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
336 Qt::ApplicationShortcut); 336 Qt::ApplicationShortcut);
337 hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot",
338 QKeySequence(QKeySequence::Print));
339
337 hotkey_registry.LoadHotkeys(); 340 hotkey_registry.LoadHotkeys();
338 341
339 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, 342 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
@@ -393,6 +396,12 @@ void GMainWindow::InitializeHotkeys() {
393 OnLoadAmiibo(); 396 OnLoadAmiibo();
394 } 397 }
395 }); 398 });
399 connect(hotkey_registry.GetHotkey("Main Window", "Capture Screenshot", this),
400 &QShortcut::activated, this, [&] {
401 if (emu_thread->IsRunning()) {
402 OnCaptureScreenshot();
403 }
404 });
396} 405}
397 406
398void GMainWindow::SetDefaultUIGeometry() { 407void GMainWindow::SetDefaultUIGeometry() {
@@ -488,6 +497,10 @@ void GMainWindow::ConnectMenuEvents() {
488 hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key()); 497 hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
489 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 498 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
490 499
500 // Movie
501 connect(ui.action_Capture_Screenshot, &QAction::triggered, this,
502 &GMainWindow::OnCaptureScreenshot);
503
491 // Help 504 // Help
492 connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder); 505 connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
493 connect(ui.action_Rederive, &QAction::triggered, this, 506 connect(ui.action_Rederive, &QAction::triggered, this,
@@ -724,6 +737,7 @@ void GMainWindow::ShutdownGame() {
724 ui.action_Restart->setEnabled(false); 737 ui.action_Restart->setEnabled(false);
725 ui.action_Report_Compatibility->setEnabled(false); 738 ui.action_Report_Compatibility->setEnabled(false);
726 ui.action_Load_Amiibo->setEnabled(false); 739 ui.action_Load_Amiibo->setEnabled(false);
740 ui.action_Capture_Screenshot->setEnabled(false);
727 render_window->hide(); 741 render_window->hide();
728 game_list->show(); 742 game_list->show();
729 game_list->setFilterFocus(); 743 game_list->setFilterFocus();
@@ -1261,6 +1275,7 @@ void GMainWindow::OnStartGame() {
1261 1275
1262 discord_rpc->Update(); 1276 discord_rpc->Update();
1263 ui.action_Load_Amiibo->setEnabled(true); 1277 ui.action_Load_Amiibo->setEnabled(true);
1278 ui.action_Capture_Screenshot->setEnabled(true);
1264} 1279}
1265 1280
1266void GMainWindow::OnPauseGame() { 1281void GMainWindow::OnPauseGame() {
@@ -1269,6 +1284,7 @@ void GMainWindow::OnPauseGame() {
1269 ui.action_Start->setEnabled(true); 1284 ui.action_Start->setEnabled(true);
1270 ui.action_Pause->setEnabled(false); 1285 ui.action_Pause->setEnabled(false);
1271 ui.action_Stop->setEnabled(true); 1286 ui.action_Stop->setEnabled(true);
1287 ui.action_Capture_Screenshot->setEnabled(false);
1272} 1288}
1273 1289
1274void GMainWindow::OnStopGame() { 1290void GMainWindow::OnStopGame() {
@@ -1431,6 +1447,18 @@ void GMainWindow::OnToggleFilterBar() {
1431 } 1447 }
1432} 1448}
1433 1449
1450void GMainWindow::OnCaptureScreenshot() {
1451 OnPauseGame();
1452 const QString path =
1453 QFileDialog::getSaveFileName(this, tr("Capture Screenshot"),
1454 UISettings::values.screenshot_path, tr("PNG Image (*.png)"));
1455 if (!path.isEmpty()) {
1456 UISettings::values.screenshot_path = QFileInfo(path).path();
1457 render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path);
1458 }
1459 OnStartGame();
1460}
1461
1434void GMainWindow::UpdateStatusBar() { 1462void GMainWindow::UpdateStatusBar() {
1435 if (emu_thread == nullptr) { 1463 if (emu_thread == nullptr) {
1436 status_bar_update_timer.stop(); 1464 status_bar_update_timer.stop();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 674e73412..9a1df5168 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -186,6 +186,7 @@ private slots:
186 void ShowFullscreen(); 186 void ShowFullscreen();
187 void HideFullscreen(); 187 void HideFullscreen();
188 void ToggleWindowMode(); 188 void ToggleWindowMode();
189 void OnCaptureScreenshot();
189 void OnCoreError(Core::System::ResultStatus, std::string); 190 void OnCoreError(Core::System::ResultStatus, std::string);
190 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 191 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
191 192
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 75e96387f..ffcabb495 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -101,11 +101,13 @@
101 <addaction name="action_Show_Status_Bar"/> 101 <addaction name="action_Show_Status_Bar"/>
102 <addaction name="menu_View_Debugging"/> 102 <addaction name="menu_View_Debugging"/>
103 </widget> 103 </widget>
104 <widget class ="QMenu" name="menu_Tools"> 104 <widget class="QMenu" name="menu_Tools">
105 <property name="title"> 105 <property name="title">
106 <string>Tools</string> 106 <string>Tools</string>
107 </property> 107 </property>
108 <addaction name="action_Rederive" /> 108 <addaction name="action_Rederive"/>
109 <addaction name="separator"/>
110 <addaction name="action_Capture_Screenshot"/>
109 </widget> 111 </widget>
110 <widget class="QMenu" name="menu_Help"> 112 <widget class="QMenu" name="menu_Help">
111 <property name="title"> 113 <property name="title">
@@ -118,7 +120,7 @@
118 <addaction name="menu_File"/> 120 <addaction name="menu_File"/>
119 <addaction name="menu_Emulation"/> 121 <addaction name="menu_Emulation"/>
120 <addaction name="menu_View"/> 122 <addaction name="menu_View"/>
121 <addaction name="menu_Tools" /> 123 <addaction name="menu_Tools"/>
122 <addaction name="menu_Help"/> 124 <addaction name="menu_Help"/>
123 </widget> 125 </widget>
124 <action name="action_Install_File_NAND"> 126 <action name="action_Install_File_NAND">
@@ -173,11 +175,11 @@
173 <string>&amp;Stop</string> 175 <string>&amp;Stop</string>
174 </property> 176 </property>
175 </action> 177 </action>
176 <action name="action_Rederive"> 178 <action name="action_Rederive">
177 <property name="text"> 179 <property name="text">
178 <string>Reinitialize keys...</string> 180 <string>Reinitialize keys...</string>
179 </property> 181 </property>
180 </action> 182 </action>
181 <action name="action_About"> 183 <action name="action_About">
182 <property name="text"> 184 <property name="text">
183 <string>About yuzu</string> 185 <string>About yuzu</string>
@@ -252,39 +254,47 @@
252 <string>Fullscreen</string> 254 <string>Fullscreen</string>
253 </property> 255 </property>
254 </action> 256 </action>
255 <action name="action_Restart"> 257 <action name="action_Restart">
256 <property name="enabled"> 258 <property name="enabled">
257 <bool>false</bool> 259 <bool>false</bool>
258 </property> 260 </property>
259 <property name="text"> 261 <property name="text">
260 <string>Restart</string> 262 <string>Restart</string>
261 </property> 263 </property>
262 </action> 264 </action>
263 <action name="action_Load_Amiibo"> 265 <action name="action_Load_Amiibo">
264 <property name="enabled"> 266 <property name="enabled">
265 <bool>false</bool> 267 <bool>false</bool>
266 </property> 268 </property>
267 <property name="text"> 269 <property name="text">
268 <string>Load Amiibo...</string> 270 <string>Load Amiibo...</string>
269 </property> 271 </property>
270 </action> 272 </action>
271 <action name="action_Report_Compatibility"> 273 <action name="action_Report_Compatibility">
272 <property name="enabled"> 274 <property name="enabled">
273 <bool>false</bool> 275 <bool>false</bool>
274 </property> 276 </property>
275 <property name="text"> 277 <property name="text">
276 <string>Report Compatibility</string> 278 <string>Report Compatibility</string>
277 </property> 279 </property>
278 <property name="visible"> 280 <property name="visible">
279 <bool>false</bool> 281 <bool>false</bool>
280 </property> 282 </property>
281 </action> 283 </action>
282 <action name="action_Open_yuzu_Folder"> 284 <action name="action_Open_yuzu_Folder">
283 <property name="text"> 285 <property name="text">
284 <string>Open yuzu Folder</string> 286 <string>Open yuzu Folder</string>
285 </property> 287 </property>
286 </action> 288 </action>
287 </widget> 289 <action name="action_Capture_Screenshot">
290 <property name="enabled">
291 <bool>false</bool>
292 </property>
293 <property name="text">
294 <string>Capture Screenshot</string>
295 </property>
296 </action>
297 </widget>
288 <resources/> 298 <resources/>
289 <connections/> 299 <connections/>
290</ui> 300</ui>
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index e80aebc0a..b03bc2de6 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -10,6 +10,7 @@
10#include <QByteArray> 10#include <QByteArray>
11#include <QString> 11#include <QString>
12#include <QStringList> 12#include <QStringList>
13#include "common/common_types.h"
13 14
14namespace UISettings { 15namespace UISettings {
15 16
@@ -42,8 +43,11 @@ struct Values {
42 // Discord RPC 43 // Discord RPC
43 bool enable_discord_presence; 44 bool enable_discord_presence;
44 45
46 u16 screenshot_resolution_factor;
47
45 QString roms_path; 48 QString roms_path;
46 QString symbols_path; 49 QString symbols_path;
50 QString screenshot_path;
47 QString gamedir; 51 QString gamedir;
48 bool gamedir_deepscan; 52 bool gamedir_deepscan;
49 QStringList recent_files; 53 QStringList recent_files;