diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/frontend/framebuffer_layout.cpp | 15 | ||||
| -rw-r--r-- | src/core/frontend/framebuffer_layout.h | 7 | ||||
| -rw-r--r-- | src/video_core/renderer_base.cpp | 12 | ||||
| -rw-r--r-- | src/video_core/renderer_base.h | 27 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 41 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.h | 9 | ||||
| -rw-r--r-- | src/video_core/video_core.cpp | 8 | ||||
| -rw-r--r-- | src/video_core/video_core.h | 2 | ||||
| -rw-r--r-- | src/yuzu/bootmanager.cpp | 18 | ||||
| -rw-r--r-- | src/yuzu/bootmanager.h | 6 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 5 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 28 | ||||
| -rw-r--r-- | src/yuzu/main.h | 1 | ||||
| -rw-r--r-- | src/yuzu/main.ui | 88 | ||||
| -rw-r--r-- | src/yuzu/ui_settings.h | 4 |
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 | ||
| 10 | namespace Layout { | 11 | namespace Layout { |
| 11 | 12 | ||
| @@ -42,4 +43,18 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) { | |||
| 42 | return res; | 43 | return res; |
| 43 | } | 44 | } |
| 44 | 45 | ||
| 46 | FramebufferLayout 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 @@ | |||
| 9 | namespace Layout { | 9 | namespace Layout { |
| 10 | 10 | ||
| 11 | enum ScreenUndocked : unsigned { Width = 1280, Height = 720 }; | 11 | enum ScreenUndocked : unsigned { Width = 1280, Height = 720 }; |
| 12 | enum ScreenDocked : unsigned { WidthDocked = 1920, HeightDocked = 1080 }; | ||
| 12 | 13 | ||
| 13 | /// Describes the layout of the window framebuffer | 14 | /// Describes the layout of the window framebuffer |
| 14 | struct FramebufferLayout { | 15 | struct FramebufferLayout { |
| @@ -34,4 +35,10 @@ struct FramebufferLayout { | |||
| 34 | */ | 35 | */ |
| 35 | FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height); | 36 | FramebufferLayout 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 | */ | ||
| 42 | FramebufferLayout 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 | ||
| 30 | void 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 { | |||
| 21 | struct RendererSettings { | 22 | struct 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 | ||
| 26 | class RendererBase : NonCopyable { | 33 | class 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 | |||
| 63 | protected: | 90 | protected: |
| 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 | */ |
| 386 | void RendererOpenGL::DrawScreen() { | 391 | void 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 |
| 415 | void RendererOpenGL::UpdateFramerate() {} | 419 | void RendererOpenGL::UpdateFramerate() {} |
| 416 | 420 | ||
| 421 | void 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 | |||
| 417 | static const char* GetSource(GLenum source) { | 452 | static 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 { | |||
| 16 | class EmuWindow; | 16 | class EmuWindow; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | namespace Layout { | ||
| 20 | class FramebufferLayout; | ||
| 21 | } | ||
| 22 | |||
| 19 | namespace OpenGL { | 23 | namespace 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 | ||
| 18 | u16 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 | */ |
| 23 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window); | 23 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window); |
| 24 | 24 | ||
| 25 | u16 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 | ||
| 19 | EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} | 21 | EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} |
| @@ -333,6 +335,22 @@ void GRenderWindow::InitRenderTarget() { | |||
| 333 | BackupGeometry(); | 335 | BackupGeometry(); |
| 334 | } | 336 | } |
| 335 | 337 | ||
| 338 | void 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 | |||
| 336 | void GRenderWindow::OnMinimalClientAreaChangeRequest( | 354 | void 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 | |||
| 142 | public slots: | 145 | public 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 | |||
| 168 | protected: | 174 | protected: |
| 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 | ||
| 398 | void GMainWindow::SetDefaultUIGeometry() { | 407 | void 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 | ||
| 1266 | void GMainWindow::OnPauseGame() { | 1281 | void 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 | ||
| 1274 | void GMainWindow::OnStopGame() { | 1290 | void GMainWindow::OnStopGame() { |
| @@ -1431,6 +1447,18 @@ void GMainWindow::OnToggleFilterBar() { | |||
| 1431 | } | 1447 | } |
| 1432 | } | 1448 | } |
| 1433 | 1449 | ||
| 1450 | void 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 | |||
| 1434 | void GMainWindow::UpdateStatusBar() { | 1462 | void 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>&Stop</string> | 175 | <string>&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 | ||
| 14 | namespace UISettings { | 15 | namespace 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; |