mirror of
https://github.com/yuzu-mirror/yuzu.git
synced 2024-11-03 07:20:00 +00:00
yuzu, video_core: Screenshot functionality
Allows capturing screenshot at the current internal resolution (native for software renderer), but a setting is available to capture it in other resolutions. The screenshot is saved to a single PNG in the current layout.
This commit is contained in:
parent
f761e3ef86
commit
a2be49305d
15 changed files with 229 additions and 44 deletions
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "core/frontend/framebuffer_layout.h"
|
#include "core/frontend/framebuffer_layout.h"
|
||||||
|
#include "core/settings.h"
|
||||||
|
|
||||||
namespace Layout {
|
namespace Layout {
|
||||||
|
|
||||||
|
@ -42,4 +43,18 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale) {
|
||||||
|
int width, height;
|
||||||
|
|
||||||
|
if (Settings::values.use_docked_mode) {
|
||||||
|
width = ScreenDocked::WidthDocked * res_scale;
|
||||||
|
height = ScreenDocked::HeightDocked * res_scale;
|
||||||
|
} else {
|
||||||
|
width = ScreenUndocked::Width * res_scale;
|
||||||
|
height = ScreenUndocked::Height * res_scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultFrameLayout(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Layout
|
} // namespace Layout
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
namespace Layout {
|
namespace Layout {
|
||||||
|
|
||||||
enum ScreenUndocked : unsigned { Width = 1280, Height = 720 };
|
enum ScreenUndocked : unsigned { Width = 1280, Height = 720 };
|
||||||
|
enum ScreenDocked : unsigned { WidthDocked = 1920, HeightDocked = 1080 };
|
||||||
|
|
||||||
/// Describes the layout of the window framebuffer
|
/// Describes the layout of the window framebuffer
|
||||||
struct FramebufferLayout {
|
struct FramebufferLayout {
|
||||||
|
@ -34,4 +35,10 @@ struct FramebufferLayout {
|
||||||
*/
|
*/
|
||||||
FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height);
|
FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method to get frame layout by resolution scale
|
||||||
|
* @param res_scale resolution scale factor
|
||||||
|
*/
|
||||||
|
FramebufferLayout FrameLayoutFromResolutionScale(u16 res_scale);
|
||||||
|
|
||||||
} // namespace Layout
|
} // namespace Layout
|
||||||
|
|
|
@ -27,4 +27,16 @@ void RendererBase::UpdateCurrentFramebufferLayout() {
|
||||||
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
|
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RendererBase::RequestScreenshot(void* data, std::function<void()> callback,
|
||||||
|
const Layout::FramebufferLayout& layout) {
|
||||||
|
if (renderer_settings.screenshot_requested) {
|
||||||
|
LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
renderer_settings.screenshot_bits = data;
|
||||||
|
renderer_settings.screenshot_complete_callback = std::move(callback);
|
||||||
|
renderer_settings.screenshot_framebuffer_layout = layout;
|
||||||
|
renderer_settings.screenshot_requested = true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "core/frontend/emu_window.h"
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
|
@ -21,6 +22,12 @@ namespace VideoCore {
|
||||||
struct RendererSettings {
|
struct RendererSettings {
|
||||||
std::atomic_bool use_framelimiter{false};
|
std::atomic_bool use_framelimiter{false};
|
||||||
std::atomic_bool set_background_color{false};
|
std::atomic_bool set_background_color{false};
|
||||||
|
|
||||||
|
// Screenshot
|
||||||
|
std::atomic<bool> screenshot_requested{false};
|
||||||
|
void* screenshot_bits;
|
||||||
|
std::function<void()> screenshot_complete_callback;
|
||||||
|
Layout::FramebufferLayout screenshot_framebuffer_layout;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RendererBase : NonCopyable {
|
class RendererBase : NonCopyable {
|
||||||
|
@ -57,9 +64,29 @@ public:
|
||||||
return *rasterizer;
|
return *rasterizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Core::Frontend::EmuWindow& GetRenderWindow() {
|
||||||
|
return render_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Core::Frontend::EmuWindow& GetRenderWindow() const {
|
||||||
|
return render_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
RendererSettings& Settings() {
|
||||||
|
return renderer_settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RendererSettings& Settings() const {
|
||||||
|
return renderer_settings;
|
||||||
|
}
|
||||||
|
|
||||||
/// Refreshes the settings common to all renderers
|
/// Refreshes the settings common to all renderers
|
||||||
void RefreshBaseSettings();
|
void RefreshBaseSettings();
|
||||||
|
|
||||||
|
/// Request a screenshot of the next frame
|
||||||
|
void RequestScreenshot(void* data, std::function<void()> callback,
|
||||||
|
const Layout::FramebufferLayout& layout);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
|
Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
|
||||||
std::unique_ptr<RasterizerInterface> rasterizer;
|
std::unique_ptr<RasterizerInterface> rasterizer;
|
||||||
|
|
|
@ -138,7 +138,12 @@ void RendererOpenGL::SwapBuffers(
|
||||||
|
|
||||||
// Load the framebuffer from memory, draw it to the screen, and swap buffers
|
// Load the framebuffer from memory, draw it to the screen, and swap buffers
|
||||||
LoadFBToScreenInfo(*framebuffer);
|
LoadFBToScreenInfo(*framebuffer);
|
||||||
DrawScreen();
|
|
||||||
|
if (renderer_settings.screenshot_requested)
|
||||||
|
CaptureScreenshot();
|
||||||
|
|
||||||
|
DrawScreen(render_window.GetFramebufferLayout());
|
||||||
|
|
||||||
render_window.SwapBuffers();
|
render_window.SwapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,14 +388,13 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
|
||||||
/**
|
/**
|
||||||
* Draws the emulated screens to the emulator window.
|
* Draws the emulated screens to the emulator window.
|
||||||
*/
|
*/
|
||||||
void RendererOpenGL::DrawScreen() {
|
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||||
if (renderer_settings.set_background_color) {
|
if (renderer_settings.set_background_color) {
|
||||||
// Update background color before drawing
|
// Update background color before drawing
|
||||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||||
0.0f);
|
0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& layout = render_window.GetFramebufferLayout();
|
|
||||||
const auto& screen = layout.screen;
|
const auto& screen = layout.screen;
|
||||||
|
|
||||||
glViewport(0, 0, layout.width, layout.height);
|
glViewport(0, 0, layout.width, layout.height);
|
||||||
|
@ -414,6 +418,37 @@ void RendererOpenGL::DrawScreen() {
|
||||||
/// Updates the framerate
|
/// Updates the framerate
|
||||||
void RendererOpenGL::UpdateFramerate() {}
|
void RendererOpenGL::UpdateFramerate() {}
|
||||||
|
|
||||||
|
void RendererOpenGL::CaptureScreenshot() {
|
||||||
|
// Draw the current frame to the screenshot framebuffer
|
||||||
|
screenshot_framebuffer.Create();
|
||||||
|
GLuint old_read_fb = state.draw.read_framebuffer;
|
||||||
|
GLuint old_draw_fb = state.draw.draw_framebuffer;
|
||||||
|
state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle;
|
||||||
|
state.Apply();
|
||||||
|
|
||||||
|
Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
|
||||||
|
|
||||||
|
GLuint renderbuffer;
|
||||||
|
glGenRenderbuffers(1, &renderbuffer);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, layout.width, layout.height);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
|
||||||
|
|
||||||
|
DrawScreen(layout);
|
||||||
|
|
||||||
|
glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||||
|
renderer_settings.screenshot_bits);
|
||||||
|
|
||||||
|
screenshot_framebuffer.Release();
|
||||||
|
state.draw.read_framebuffer = old_read_fb;
|
||||||
|
state.draw.draw_framebuffer = old_draw_fb;
|
||||||
|
state.Apply();
|
||||||
|
glDeleteRenderbuffers(1, &renderbuffer);
|
||||||
|
|
||||||
|
renderer_settings.screenshot_complete_callback();
|
||||||
|
renderer_settings.screenshot_requested = false;
|
||||||
|
}
|
||||||
|
|
||||||
static const char* GetSource(GLenum source) {
|
static const char* GetSource(GLenum source) {
|
||||||
#define RET(s) \
|
#define RET(s) \
|
||||||
case GL_DEBUG_SOURCE_##s: \
|
case GL_DEBUG_SOURCE_##s: \
|
||||||
|
|
|
@ -16,6 +16,10 @@ namespace Core::Frontend {
|
||||||
class EmuWindow;
|
class EmuWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Layout {
|
||||||
|
class FramebufferLayout;
|
||||||
|
}
|
||||||
|
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
/// Structure used for storing information about the textures for the Switch screen
|
/// Structure used for storing information about the textures for the Switch screen
|
||||||
|
@ -66,10 +70,12 @@ private:
|
||||||
|
|
||||||
void ConfigureFramebufferTexture(TextureInfo& texture,
|
void ConfigureFramebufferTexture(TextureInfo& texture,
|
||||||
const Tegra::FramebufferConfig& framebuffer);
|
const Tegra::FramebufferConfig& framebuffer);
|
||||||
void DrawScreen();
|
void DrawScreen(const Layout::FramebufferLayout& layout);
|
||||||
void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
|
void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
|
||||||
void UpdateFramerate();
|
void UpdateFramerate();
|
||||||
|
|
||||||
|
void CaptureScreenshot();
|
||||||
|
|
||||||
// Loads framebuffer from emulated memory into the display information structure
|
// Loads framebuffer from emulated memory into the display information structure
|
||||||
void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
|
void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
|
||||||
// Fills active OpenGL texture with the given RGBA color.
|
// Fills active OpenGL texture with the given RGBA color.
|
||||||
|
@ -82,6 +88,7 @@ private:
|
||||||
OGLVertexArray vertex_array;
|
OGLVertexArray vertex_array;
|
||||||
OGLBuffer vertex_buffer;
|
OGLBuffer vertex_buffer;
|
||||||
OGLProgram shader;
|
OGLProgram shader;
|
||||||
|
OGLFramebuffer screenshot_framebuffer;
|
||||||
|
|
||||||
/// Display information for Switch screen
|
/// Display information for Switch screen
|
||||||
ScreenInfo screen_info;
|
ScreenInfo screen_info;
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/settings.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
@ -13,4 +15,10 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
|
||||||
return std::make_unique<OpenGL::RendererOpenGL>(emu_window);
|
return std::make_unique<OpenGL::RendererOpenGL>(emu_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u16 GetResolutionScaleFactor(const RendererBase& renderer) {
|
||||||
|
return !Settings::values.resolution_factor
|
||||||
|
? renderer.GetRenderWindow().GetFramebufferLayout().GetScalingRatio()
|
||||||
|
: Settings::values.resolution_factor;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
|
|
@ -22,4 +22,6 @@ class RendererBase;
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window);
|
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window);
|
||||||
|
|
||||||
|
u16 GetResolutionScaleFactor(const RendererBase& renderer);
|
||||||
|
|
||||||
} // namespace VideoCore
|
} // namespace VideoCore
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include "input_common/keyboard.h"
|
#include "input_common/keyboard.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
#include "input_common/motion_emu.h"
|
#include "input_common/motion_emu.h"
|
||||||
|
#include "video_core/renderer_base.h"
|
||||||
|
#include "video_core/video_core.h"
|
||||||
#include "yuzu/bootmanager.h"
|
#include "yuzu/bootmanager.h"
|
||||||
|
|
||||||
EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
|
EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {}
|
||||||
|
@ -333,6 +335,22 @@ void GRenderWindow::InitRenderTarget() {
|
||||||
BackupGeometry();
|
BackupGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::CaptureScreenshot(u16 res_scale, const QString& screenshot_path) {
|
||||||
|
auto& renderer = Core::System::GetInstance().Renderer();
|
||||||
|
|
||||||
|
if (!res_scale)
|
||||||
|
res_scale = VideoCore::GetResolutionScaleFactor(renderer);
|
||||||
|
|
||||||
|
const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
|
||||||
|
screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
|
||||||
|
renderer.RequestScreenshot(screenshot_image.bits(),
|
||||||
|
[=] {
|
||||||
|
screenshot_image.mirrored(false, true).save(screenshot_path);
|
||||||
|
LOG_INFO(Frontend, "The screenshot is saved.");
|
||||||
|
},
|
||||||
|
layout);
|
||||||
|
}
|
||||||
|
|
||||||
void GRenderWindow::OnMinimalClientAreaChangeRequest(
|
void GRenderWindow::OnMinimalClientAreaChangeRequest(
|
||||||
const std::pair<unsigned, unsigned>& minimal_size) {
|
const std::pair<unsigned, unsigned>& minimal_size) {
|
||||||
setMinimumSize(minimal_size.first, minimal_size.second);
|
setMinimumSize(minimal_size.first, minimal_size.second);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <QGLWidget>
|
#include <QGLWidget>
|
||||||
|
#include <QImage>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
@ -139,6 +140,8 @@ public:
|
||||||
|
|
||||||
void InitRenderTarget();
|
void InitRenderTarget();
|
||||||
|
|
||||||
|
void CaptureScreenshot(u16 res_scale, const QString& screenshot_path);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void moveContext(); // overridden
|
void moveContext(); // overridden
|
||||||
|
|
||||||
|
@ -165,6 +168,9 @@ private:
|
||||||
|
|
||||||
EmuThread* emu_thread;
|
EmuThread* emu_thread;
|
||||||
|
|
||||||
|
/// Temporary storage of the screenshot taken
|
||||||
|
QImage screenshot_image;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void showEvent(QShowEvent* event) override;
|
void showEvent(QShowEvent* event) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -445,6 +445,8 @@ void Config::ReadValues() {
|
||||||
UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
|
UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
|
||||||
UISettings::values.enable_discord_presence =
|
UISettings::values.enable_discord_presence =
|
||||||
qt_config->value("enable_discord_presence", true).toBool();
|
qt_config->value("enable_discord_presence", true).toBool();
|
||||||
|
UISettings::values.screenshot_resolution_factor =
|
||||||
|
static_cast<u16>(qt_config->value("screenshot_resolution_factor", 0).toUInt());
|
||||||
|
|
||||||
qt_config->beginGroup("UIGameList");
|
qt_config->beginGroup("UIGameList");
|
||||||
UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
|
UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
|
||||||
|
@ -648,6 +650,8 @@ void Config::SaveValues() {
|
||||||
qt_config->beginGroup("UI");
|
qt_config->beginGroup("UI");
|
||||||
qt_config->setValue("theme", UISettings::values.theme);
|
qt_config->setValue("theme", UISettings::values.theme);
|
||||||
qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence);
|
qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence);
|
||||||
|
qt_config->setValue("screenshot_resolution_factor",
|
||||||
|
UISettings::values.screenshot_resolution_factor);
|
||||||
|
|
||||||
qt_config->beginGroup("UIGameList");
|
qt_config->beginGroup("UIGameList");
|
||||||
qt_config->setValue("show_unknown", UISettings::values.show_unknown);
|
qt_config->setValue("show_unknown", UISettings::values.show_unknown);
|
||||||
|
@ -669,6 +673,7 @@ void Config::SaveValues() {
|
||||||
qt_config->beginGroup("Paths");
|
qt_config->beginGroup("Paths");
|
||||||
qt_config->setValue("romsPath", UISettings::values.roms_path);
|
qt_config->setValue("romsPath", UISettings::values.roms_path);
|
||||||
qt_config->setValue("symbolsPath", UISettings::values.symbols_path);
|
qt_config->setValue("symbolsPath", UISettings::values.symbols_path);
|
||||||
|
qt_config->setValue("screenshotPath", UISettings::values.screenshot_path);
|
||||||
qt_config->setValue("gameListRootDir", UISettings::values.gamedir);
|
qt_config->setValue("gameListRootDir", UISettings::values.gamedir);
|
||||||
qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan);
|
qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan);
|
||||||
qt_config->setValue("recentFiles", UISettings::values.recent_files);
|
qt_config->setValue("recentFiles", UISettings::values.recent_files);
|
||||||
|
|
|
@ -334,6 +334,9 @@ void GMainWindow::InitializeHotkeys() {
|
||||||
Qt::ApplicationShortcut);
|
Qt::ApplicationShortcut);
|
||||||
hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
|
hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
|
||||||
Qt::ApplicationShortcut);
|
Qt::ApplicationShortcut);
|
||||||
|
hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot",
|
||||||
|
QKeySequence(QKeySequence::Print));
|
||||||
|
|
||||||
hotkey_registry.LoadHotkeys();
|
hotkey_registry.LoadHotkeys();
|
||||||
|
|
||||||
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
|
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
|
||||||
|
@ -393,6 +396,12 @@ void GMainWindow::InitializeHotkeys() {
|
||||||
OnLoadAmiibo();
|
OnLoadAmiibo();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
connect(hotkey_registry.GetHotkey("Main Window", "Capture Screenshot", this),
|
||||||
|
&QShortcut::activated, this, [&] {
|
||||||
|
if (emu_thread->IsRunning()) {
|
||||||
|
OnCaptureScreenshot();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::SetDefaultUIGeometry() {
|
void GMainWindow::SetDefaultUIGeometry() {
|
||||||
|
@ -488,6 +497,10 @@ void GMainWindow::ConnectMenuEvents() {
|
||||||
hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
|
hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
|
||||||
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
|
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
|
||||||
|
|
||||||
|
// Movie
|
||||||
|
connect(ui.action_Capture_Screenshot, &QAction::triggered, this,
|
||||||
|
&GMainWindow::OnCaptureScreenshot);
|
||||||
|
|
||||||
// Help
|
// Help
|
||||||
connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
|
connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
|
||||||
connect(ui.action_Rederive, &QAction::triggered, this,
|
connect(ui.action_Rederive, &QAction::triggered, this,
|
||||||
|
@ -724,6 +737,7 @@ void GMainWindow::ShutdownGame() {
|
||||||
ui.action_Restart->setEnabled(false);
|
ui.action_Restart->setEnabled(false);
|
||||||
ui.action_Report_Compatibility->setEnabled(false);
|
ui.action_Report_Compatibility->setEnabled(false);
|
||||||
ui.action_Load_Amiibo->setEnabled(false);
|
ui.action_Load_Amiibo->setEnabled(false);
|
||||||
|
ui.action_Capture_Screenshot->setEnabled(false);
|
||||||
render_window->hide();
|
render_window->hide();
|
||||||
game_list->show();
|
game_list->show();
|
||||||
game_list->setFilterFocus();
|
game_list->setFilterFocus();
|
||||||
|
@ -1261,6 +1275,7 @@ void GMainWindow::OnStartGame() {
|
||||||
|
|
||||||
discord_rpc->Update();
|
discord_rpc->Update();
|
||||||
ui.action_Load_Amiibo->setEnabled(true);
|
ui.action_Load_Amiibo->setEnabled(true);
|
||||||
|
ui.action_Capture_Screenshot->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnPauseGame() {
|
void GMainWindow::OnPauseGame() {
|
||||||
|
@ -1269,6 +1284,7 @@ void GMainWindow::OnPauseGame() {
|
||||||
ui.action_Start->setEnabled(true);
|
ui.action_Start->setEnabled(true);
|
||||||
ui.action_Pause->setEnabled(false);
|
ui.action_Pause->setEnabled(false);
|
||||||
ui.action_Stop->setEnabled(true);
|
ui.action_Stop->setEnabled(true);
|
||||||
|
ui.action_Capture_Screenshot->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnStopGame() {
|
void GMainWindow::OnStopGame() {
|
||||||
|
@ -1431,6 +1447,18 @@ void GMainWindow::OnToggleFilterBar() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnCaptureScreenshot() {
|
||||||
|
OnPauseGame();
|
||||||
|
const QString path =
|
||||||
|
QFileDialog::getSaveFileName(this, tr("Capture Screenshot"),
|
||||||
|
UISettings::values.screenshot_path, tr("PNG Image (*.png)"));
|
||||||
|
if (!path.isEmpty()) {
|
||||||
|
UISettings::values.screenshot_path = QFileInfo(path).path();
|
||||||
|
render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path);
|
||||||
|
}
|
||||||
|
OnStartGame();
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::UpdateStatusBar() {
|
void GMainWindow::UpdateStatusBar() {
|
||||||
if (emu_thread == nullptr) {
|
if (emu_thread == nullptr) {
|
||||||
status_bar_update_timer.stop();
|
status_bar_update_timer.stop();
|
||||||
|
|
|
@ -186,6 +186,7 @@ private slots:
|
||||||
void ShowFullscreen();
|
void ShowFullscreen();
|
||||||
void HideFullscreen();
|
void HideFullscreen();
|
||||||
void ToggleWindowMode();
|
void ToggleWindowMode();
|
||||||
|
void OnCaptureScreenshot();
|
||||||
void OnCoreError(Core::System::ResultStatus, std::string);
|
void OnCoreError(Core::System::ResultStatus, std::string);
|
||||||
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
|
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,8 @@
|
||||||
<string>Tools</string>
|
<string>Tools</string>
|
||||||
</property>
|
</property>
|
||||||
<addaction name="action_Rederive"/>
|
<addaction name="action_Rederive"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="action_Capture_Screenshot"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menu_Help">
|
<widget class="QMenu" name="menu_Help">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
@ -284,6 +286,14 @@
|
||||||
<string>Open yuzu Folder</string>
|
<string>Open yuzu Folder</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_Capture_Screenshot">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Capture Screenshot</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace UISettings {
|
namespace UISettings {
|
||||||
|
|
||||||
|
@ -42,8 +43,11 @@ struct Values {
|
||||||
// Discord RPC
|
// Discord RPC
|
||||||
bool enable_discord_presence;
|
bool enable_discord_presence;
|
||||||
|
|
||||||
|
u16 screenshot_resolution_factor;
|
||||||
|
|
||||||
QString roms_path;
|
QString roms_path;
|
||||||
QString symbols_path;
|
QString symbols_path;
|
||||||
|
QString screenshot_path;
|
||||||
QString gamedir;
|
QString gamedir;
|
||||||
bool gamedir_deepscan;
|
bool gamedir_deepscan;
|
||||||
QStringList recent_files;
|
QStringList recent_files;
|
||||||
|
|
Loading…
Reference in a new issue