suyu/src/suyu/main.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

5678 lines
220 KiB
C++
Raw Permalink Normal View History

// SPDX-FileCopyrightText: 2014 Citra Emulator Project
chore: make yuzu REUSE compliant [REUSE] is a specification that aims at making file copyright information consistent, so that it can be both human and machine readable. It basically requires that all files have a header containing copyright and licensing information. When this isn't possible, like when dealing with binary assets, generated files or embedded third-party dependencies, it is permitted to insert copyright information in the `.reuse/dep5` file. Oh, and it also requires that all the licenses used in the project are present in the `LICENSES` folder, that's why the diff is so huge. This can be done automatically with `reuse download --all`. The `reuse` tool also contains a handy subcommand that analyzes the project and tells whether or not the project is (still) compliant, `reuse lint`. Following REUSE has a few advantages over the current approach: - Copyright information is easy to access for users / downstream - Files like `dist/license.md` do not need to exist anymore, as `.reuse/dep5` is used instead - `reuse lint` makes it easy to ensure that copyright information of files like binary assets / images is always accurate and up to date To add copyright information of files that didn't have it I looked up who committed what and when, for each file. As yuzu contributors do not have to sign a CLA or similar I couldn't assume that copyright ownership was of the "yuzu Emulator Project", so I used the name and/or email of the commit author instead. [REUSE]: https://reuse.software Follow-up to 01cf05bc75b1e47beb08937439f3ed9339e7b254
2022-05-15 02:06:02 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes>
#include <clocale>
#include <cmath>
#include <fstream>
#include <iostream>
#include <memory>
2014-10-28 08:36:00 +01:00
#include <thread>
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
#include "core/hle/service/am/applet_manager.h"
#include "core/loader/nca.h"
2024-03-23 17:23:00 +01:00
#include "core/loader/nro.h"
2023-09-10 22:26:09 +02:00
#include "core/tools/renderdoc.h"
2023-11-07 03:32:19 +01:00
#ifdef __APPLE__
#include <unistd.h> // for chdir
#endif
#ifdef __unix__
#include <csignal>
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusMessage>
#include <QtDBus/QtDBus>
#include <sys/socket.h>
#include "common/linux/gamemode.h"
#endif
2023-10-02 15:23:25 +02:00
#include <boost/container/flat_set.hpp>
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
2022-11-13 22:14:08 +01:00
#include "applets/qt_amiibo_settings.h"
#include "applets/qt_controller.h"
#include "applets/qt_error.h"
#include "applets/qt_profile_select.h"
#include "applets/qt_software_keyboard.h"
#include "applets/qt_web_browser.h"
#include "common/nvidia_flags.h"
#include "common/settings_enums.h"
#include "configuration/configure_input.h"
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
#include "configuration/configure_per_game.h"
#include "configuration/configure_tas.h"
2023-05-01 05:28:31 +02:00
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/vfs/vfs_real.h"
#include "core/frontend/applets/cabinet.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/general.h"
2022-03-17 08:01:03 +01:00
#include "core/frontend/applets/mii_edit.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/frontend/applets.h"
2024-01-07 00:49:40 +01:00
#include "core/hle/service/set/system_settings_server.h"
#include "frontend_common/content_manager.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
2024-03-08 10:06:48 +01:00
#include "suyu/multiplayer/state.h"
#include "suyu/util/controller_navigation.h"
// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::OpenMode mode) {
return vfs->CreateDirectory(path, mode);
}
// Overloaded function, also removed by palafiate
static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::VirtualDir& dir,
const std::string& path) {
return dir->CreateFile(path);
}
#include <fmt/ostream.h>
#include <glad/glad.h>
#define QT_NO_OPENGL
#include <QClipboard>
#include <QDesktopServices>
Improvement in Directory Path Detection for Shortcuts (#11749) * Improvement in Directory Path Detection for Shortcuts This pull request updates how the directory path for shortcuts is determined. The main changes are: 1. Replaced the use of environment variables to determine the path of the desktop and applications menu with `QStandardPaths::writableLocation`. This change addresses an issue where the desktop path was not correctly identified when its location was customized, as shown in the attached screenshot. 2. Added conversion from `QString` to `std::string` using `toUtf8()`, which correctly handles non-ASCII characters in directory paths. This change ensures that directory paths containing Portuguese words like "Área de trabalho" are supported. 3. Replaced directory checking using `Common::FS::IsDir()` with `QDir::exists()`. These changes should improve cross-platform compatibility and code robustness. Because it couldn't locate my desktop, which wasn't on the C drive, but on the F, and even though localization wouldn't work because it was setting it to find the 'Desktop' folder and in the computer's language it says 'Área de trabalho', that will fix for other languages too. * Update main.cpp * formatting * Update src/yuzu/main.cpp Co-authored-by: Tobias <thm.frey@gmail.com> * Update src/yuzu/main.cpp Co-authored-by: Tobias <thm.frey@gmail.com> * Update main.cpp * Update main.cpp * Update main.cpp desktopPath > desktop_Path applicationsPath > applications_Path * Update main.cpp * formatting * Update main.cpp This code will attempt to use QStandardPaths to find the applications directory. If that fails, it will resort to using the ~/.local/share/applications directory, which is a common location for application shortcuts in Linux. * Update main.cpp * formatting --------- Co-authored-by: Tobias <thm.frey@gmail.com>
2023-10-13 17:57:49 +02:00
#include <QDir>
#include <QFile>
2014-04-01 04:26:50 +02:00
#include <QFileDialog>
#include <QInputDialog>
#include <QMessageBox>
#include <QProgressBar>
#include <QProgressDialog>
#include <QPushButton>
#include <QScreen>
#include <QShortcut>
Improvement in Directory Path Detection for Shortcuts (#11749) * Improvement in Directory Path Detection for Shortcuts This pull request updates how the directory path for shortcuts is determined. The main changes are: 1. Replaced the use of environment variables to determine the path of the desktop and applications menu with `QStandardPaths::writableLocation`. This change addresses an issue where the desktop path was not correctly identified when its location was customized, as shown in the attached screenshot. 2. Added conversion from `QString` to `std::string` using `toUtf8()`, which correctly handles non-ASCII characters in directory paths. This change ensures that directory paths containing Portuguese words like "Área de trabalho" are supported. 3. Replaced directory checking using `Common::FS::IsDir()` with `QDir::exists()`. These changes should improve cross-platform compatibility and code robustness. Because it couldn't locate my desktop, which wasn't on the C drive, but on the F, and even though localization wouldn't work because it was setting it to find the 'Desktop' folder and in the computer's language it says 'Área de trabalho', that will fix for other languages too. * Update main.cpp * formatting * Update src/yuzu/main.cpp Co-authored-by: Tobias <thm.frey@gmail.com> * Update src/yuzu/main.cpp Co-authored-by: Tobias <thm.frey@gmail.com> * Update main.cpp * Update main.cpp * Update main.cpp desktopPath > desktop_Path applicationsPath > applications_Path * Update main.cpp * formatting * Update main.cpp This code will attempt to use QStandardPaths to find the applications directory. If that fails, it will resort to using the ~/.local/share/applications directory, which is a common location for application shortcuts in Linux. * Update main.cpp * formatting --------- Co-authored-by: Tobias <thm.frey@gmail.com>
2023-10-13 17:57:49 +02:00
#include <QStandardPaths>
#include <QStatusBar>
#include <QString>
2019-09-21 14:24:16 +02:00
#include <QSysInfo>
#include <QUrl>
#include <QtConcurrent/QtConcurrent>
#ifdef HAVE_SDL2
#include <SDL.h> // For SDL ScreenSaver functions
#endif
2018-08-29 15:42:53 +02:00
#include <fmt/format.h>
2018-09-16 20:05:51 +02:00
#include "common/detached_tasks.h"
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/literals.h"
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "common/memory_detect.h"
#include "common/microprofile.h"
2015-06-21 15:58:59 +02:00
#include "common/scm_rev.h"
2014-10-28 08:36:00 +01:00
#include "common/scope_exit.h"
#ifdef _WIN32
#include <shlobj.h>
#include "common/windows/timer_resolution.h"
#endif
2019-09-21 14:24:16 +02:00
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#endif
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/content_archive.h"
2018-09-04 23:01:40 +02:00
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/submission_package.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/sm/sm.h"
2015-09-11 06:23:00 +02:00
#include "core/loader/loader.h"
#include "core/perf_stats.h"
#include "frontend_common/config.h"
#include "input_common/drivers/tas_input.h"
#include "input_common/drivers/virtual_amiibo.h"
#include "input_common/main.h"
2024-03-08 10:06:48 +01:00
#include "suyu/about_dialog.h"
#include "suyu/bootmanager.h"
#include "suyu/compatdb.h"
#include "suyu/compatibility_list.h"
#include "suyu/configuration/configure_dialog.h"
#include "suyu/configuration/configure_input_per_game.h"
#include "suyu/configuration/qt_config.h"
#include "suyu/debugger/console.h"
#include "suyu/debugger/controller.h"
#include "suyu/debugger/profiler.h"
#include "suyu/debugger/wait_tree.h"
#include "suyu/discord.h"
#include "suyu/game_list.h"
#include "suyu/game_list_p.h"
#include "suyu/hotkeys.h"
#include "suyu/install_dialog.h"
#include "suyu/loading_screen.h"
#include "suyu/main.h"
#include "suyu/play_time_manager.h"
#include "suyu/startup_checks.h"
#include "suyu/uisettings.h"
#include "suyu/util/clickable_label.h"
#include "suyu/vk_device_info.h"
2024-03-08 20:38:28 +01:00
#include "ui_main.h"
#include "util/overlay_dialog.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
#include "video_core/shader_notify.h"
2024-03-08 10:06:48 +01:00
#ifdef SUYU_CRASH_DUMPS
#include "suyu/breakpad.h"
#endif
using namespace Common::Literals;
2018-09-16 20:05:51 +02:00
#ifdef USE_DISCORD_PRESENCE
2024-03-08 10:06:48 +01:00
#include "suyu/discord_impl.h"
2018-09-16 20:05:51 +02:00
#endif
2016-08-01 23:13:35 +02:00
#ifdef QT_STATICPLUGIN
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
#endif
#ifdef _WIN32
#include <windows.h>
extern "C" {
// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
// graphics
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
#endif
constexpr int default_mouse_hide_timeout = 2500;
constexpr int default_input_update_timeout = 1;
constexpr size_t CopyBufferSize = 1_MiB;
/**
* "Callouts" are one-time instructional messages shown to the user. In the config settings, there
* is a bitfield "callout_flags" options, used to track if a message has already been shown to the
2021-01-02 15:00:05 +01:00
* user. This is 32-bits - if we have more than 32 callouts, we should retire and recycle old ones.
*/
enum class CalloutFlag : uint32_t {
DRDDeprecation = 0x2,
};
2018-08-09 03:44:59 +02:00
const int GMainWindow::max_recent_files_item;
static void RemoveCachedContents() {
2024-03-07 10:21:59 +01:00
const auto cache_dir = Common::FS::GetSuyuPath(Common::FS::SuyuPath::CacheDir);
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
const auto offline_fonts = cache_dir / "fonts";
const auto offline_manual = cache_dir / "offline_web_applet_manual";
const auto offline_legal_information = cache_dir / "offline_web_applet_legal_information";
const auto offline_system_data = cache_dir / "offline_web_applet_system_data";
Common::FS::RemoveDirRecursively(offline_fonts);
Common::FS::RemoveDirRecursively(offline_manual);
Common::FS::RemoveDirRecursively(offline_legal_information);
Common::FS::RemoveDirRecursively(offline_system_data);
}
static void LogRuntimes() {
#ifdef _MSC_VER
// It is possible that the name of the dll will change.
// vcruntime140.dll is for 2015 and onwards
static constexpr char runtime_dll_name[] = "vcruntime140.dll";
UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr);
bool runtime_version_inspection_worked = false;
if (sz > 0) {
std::vector<u8> buf(sz);
if (GetFileVersionInfoA(runtime_dll_name, 0, sz, buf.data())) {
VS_FIXEDFILEINFO* pvi;
sz = sizeof(VS_FIXEDFILEINFO);
if (VerQueryValueA(buf.data(), "\\", reinterpret_cast<LPVOID*>(&pvi), &sz)) {
if (pvi->dwSignature == VS_FFI_SIGNATURE) {
runtime_version_inspection_worked = true;
LOG_INFO(Frontend, "MSVC Compiler: {} Runtime: {}.{}.{}.{}", _MSC_VER,
pvi->dwProductVersionMS >> 16, pvi->dwProductVersionMS & 0xFFFF,
pvi->dwProductVersionLS >> 16, pvi->dwProductVersionLS & 0xFFFF);
}
}
}
}
if (!runtime_version_inspection_worked) {
LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name);
}
#endif
2022-09-02 04:27:33 +02:00
LOG_INFO(Frontend, "Qt Compile: {} Runtime: {}", QT_VERSION_STR, qVersion());
}
static QString PrettyProductName() {
#ifdef _WIN32
// After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
// With that notation change they changed the registry key used to denote the current version
QSettings windows_registry(
QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
QSettings::NativeFormat);
const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
if (release_id == QStringLiteral("2009")) {
const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
const QString display_version =
windows_registry.value(QStringLiteral("DisplayVersion")).toString();
const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
u32 version = 10;
if (current_build >= 22000) {
version = 11;
}
return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
.arg(QString::number(version), display_version, QString::number(current_build),
QString::number(ubr));
}
#endif
return QSysInfo::prettyProductName();
}
#ifdef _WIN32
static void OverrideWindowsFont() {
2023-03-12 04:10:38 +01:00
// Qt5 chooses these fonts on Windows and they have fairly ugly alphanumeric/cyrillic characters
// Asking to use "MS Shell Dlg 2" gives better other chars while leaving the Chinese Characters.
const QString startup_font = QApplication::font().family();
const QStringList ugly_fonts = {QStringLiteral("SimSun"), QStringLiteral("PMingLiU")};
if (ugly_fonts.contains(startup_font)) {
QApplication::setFont(QFont(QStringLiteral("MS Shell Dlg 2"), 9, QFont::Normal));
}
}
#endif
GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan)
2021-10-16 01:36:52 +02:00
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
provider{std::make_unique<FileSys::ManualContentProvider>()} {
#ifdef __unix__
SetupSigInterrupts();
SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());
#endif
system->Initialize();
Common::Log::Initialize();
2023-03-27 20:18:26 +02:00
Common::Log::Start();
LoadTranslation();
2017-02-16 04:23:30 +01:00
setAcceptDrops(true);
2021-10-15 21:27:18 +02:00
ui->setupUi(this);
2014-04-01 04:26:50 +02:00
statusBar()->hide();
startup_icon_theme = QIcon::themeName();
// fallback can only be set once, default theme icons are okay on both light/dark
QIcon::setFallbackThemeName(QStringLiteral("default"));
QIcon::setFallbackSearchPaths(QStringList(QStringLiteral(":/icons")));
2018-03-30 17:38:34 +02:00
default_theme_paths = QIcon::themeSearchPaths();
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
2018-09-16 20:05:51 +02:00
discord_rpc->Update();
play_time_manager = std::make_unique<PlayTime::PlayTimeManager>(system->GetProfileManager());
2023-08-27 03:19:00 +02:00
system->GetRoomNetwork().Init();
RegisterMetaTypes();
InitializeWidgets();
InitializeDebugWidgets();
InitializeRecentFileMenuActions();
InitializeHotkeys();
SetDefaultUIGeometry();
RestoreUIState();
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
UpdateUITheme();
ConnectMenuEvents();
ConnectWidgetEvents();
system->HIDCore().ReloadInputDevices();
controller_dialog->refreshConfiguration();
2021-09-21 02:44:34 +02:00
const auto branch_name = std::string(Common::g_scm_branch);
const auto description = std::string(Common::g_scm_desc);
const auto build_id = std::string(Common::g_build_id);
2024-03-07 10:21:59 +01:00
const auto suyu_build = fmt::format("suyu Development Build | {}-{}", branch_name, description);
const auto override_build =
fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
2024-03-07 10:21:59 +01:00
const auto suyu_build_version = override_build.empty() ? suyu_build : override_build;
2022-11-10 19:46:08 +01:00
const auto processor_count = std::thread::hardware_concurrency();
2024-03-07 10:21:59 +01:00
LOG_INFO(Frontend, "suyu Version: {}", suyu_build_version);
LogRuntimes();
2019-09-21 14:24:16 +02:00
#ifdef ARCHITECTURE_x86_64
const auto& caps = Common::GetCPUCaps();
std::string cpu_string = caps.cpu_string;
if (caps.avx || caps.avx2 || caps.avx512f) {
cpu_string += " | AVX";
if (caps.avx512f) {
cpu_string += "512";
} else if (caps.avx2) {
cpu_string += '2';
}
if (caps.fma || caps.fma4) {
cpu_string += " | FMA";
}
}
LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
2022-11-11 09:36:47 +01:00
if (std::optional<int> processor_core = Common::GetProcessorCount()) {
LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core);
}
2022-11-12 19:31:54 +01:00
#endif
LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);
LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
#ifdef _WIN32
LOG_INFO(Frontend, "Host Timer Resolution: {:.4f} ms",
std::chrono::duration_cast<std::chrono::duration<f64, std::milli>>(
Common::Windows::SetCurrentTimerResolutionToMaximum())
.count());
system->CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution());
#endif
UpdateWindowTitle();
show();
2021-10-14 20:32:19 +02:00
system->SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
provider.get());
system->GetFileSystemController().CreateFactories(*vfs);
// Remove cached contents generated during the previous session
RemoveCachedContents();
// Gen keys if necessary
OnCheckFirmwareDecryption();
2018-08-29 15:42:53 +02:00
game_list->LoadCompatibilityList();
game_list->PopulateAsync(UISettings::values.game_dirs);
// make sure menubar has the arrow cursor instead of inheriting from this
2021-10-15 21:27:18 +02:00
ui->menubar->setCursor(QCursor());
statusBar()->setCursor(QCursor());
mouse_hide_timer.setInterval(default_mouse_hide_timeout);
connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
2021-10-15 21:27:18 +02:00
connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor);
update_input_timer.setInterval(default_input_update_timeout);
connect(&update_input_timer, &QTimer::timeout, this, &GMainWindow::UpdateInputDrivers);
update_input_timer.start();
MigrateConfigFiles();
2022-07-10 22:10:35 +02:00
if (has_broken_vulkan) {
UISettings::values.has_broken_vulkan = true;
QMessageBox::warning(this, tr("Broken Vulkan Installation Detected"),
tr("Vulkan initialization failed during boot.<br><br>Click <a "
2024-03-08 10:06:48 +01:00
"href='https://suyu.dev/wiki/faq/"
"#suyu-starts-with-the-error-broken-vulkan-installation-detected'>"
2022-07-10 22:10:35 +02:00
"here for instructions to fix the issue</a>."));
#ifdef HAS_OPENGL
Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
#else
Settings::values.renderer_backend = Settings::RendererBackend::Null;
#endif
UpdateAPIText();
renderer_status_button->setDisabled(true);
renderer_status_button->setChecked(false);
} else {
VkDeviceInfo::PopulateRecords(vk_device_records, this->window()->windowHandle());
}
#if defined(HAVE_SDL2) && !defined(_WIN32)
SDL_InitSubSystem(SDL_INIT_VIDEO);
// Set a screensaver inhibition reason string. Currently passed to DBus by SDL and visible to
// the user through their desktop environment.
2024-03-08 10:06:48 +01:00
//: TRANSLATORS: This string is shown to the user to explain why suyu needs to prevent the
//: computer from sleeping
QByteArray wakelock_reason = tr("Running a game").toUtf8();
SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, wakelock_reason.data());
// SDL disables the screen saver by default, and setting the hint
// SDL_HINT_VIDEO_ALLOW_SCREENSAVER doesn't seem to work, so we just enable the screen saver
// for now.
SDL_EnableScreenSaver();
#endif
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
#ifdef __unix__
SetupPrepareForSleep();
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
ListenColorSchemeChange();
#endif
QStringList args = QApplication::arguments();
if (args.size() < 2) {
return;
}
QString game_path;
bool has_gamepath = false;
bool is_fullscreen = false;
bool is_qlaunch = false;
for (int i = 1; i < args.size(); ++i) {
// Preserves drag/drop functionality
if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) {
game_path = args[1];
has_gamepath = true;
break;
}
// Launch game in fullscreen mode
if (args[i] == QStringLiteral("-f")) {
is_fullscreen = true;
continue;
}
// Use QLaunch at startup
if (args[i] == QStringLiteral("-ql")) {
is_qlaunch = true;
continue;
}
// Launch game with a specific user
if (args[i] == QStringLiteral("-u")) {
if (i >= args.size() - 1) {
continue;
}
if (args[i + 1].startsWith(QChar::fromLatin1('-'))) {
continue;
}
int user_arg_idx = ++i;
bool argument_ok;
std::size_t selected_user = args[user_arg_idx].toUInt(&argument_ok);
if (!argument_ok) {
// try to look it up by username, only finds the first username that matches.
const std::string user_arg_str = args[user_arg_idx].toStdString();
const auto user_idx = system->GetProfileManager().GetUserIndex(user_arg_str);
if (user_idx == std::nullopt) {
LOG_ERROR(Frontend, "Invalid user argument");
continue;
}
selected_user = user_idx.value();
}
if (!system->GetProfileManager().UserExistsIndex(selected_user)) {
LOG_ERROR(Frontend, "Selected user doesn't exist");
continue;
}
Settings::values.current_user = static_cast<s32>(selected_user);
user_flag_cmd_line = true;
continue;
}
// Launch game at path
if (args[i] == QStringLiteral("-g")) {
if (i >= args.size() - 1) {
continue;
}
if (args[i + 1].startsWith(QChar::fromLatin1('-'))) {
continue;
}
game_path = args[++i];
has_gamepath = true;
}
}
// Override fullscreen setting if gamepath or argument is provided
if (has_gamepath || is_fullscreen) {
ui->action_Fullscreen->setChecked(is_fullscreen);
}
// Open HomeMenu
if (!has_gamepath && is_qlaunch) {
OnHomeMenu();
}
if (!game_path.isEmpty()) {
BootGame(game_path, ApplicationAppletParameters());
}
}
GMainWindow::~GMainWindow() {
// will get automatically deleted otherwise
if (render_window->parent() == nullptr) {
delete render_window;
}
#ifdef __unix__
::close(sig_interrupt_fds[0]);
::close(sig_interrupt_fds[1]);
#endif
}
void GMainWindow::RegisterMetaTypes() {
// Register integral and floating point types
qRegisterMetaType<u8>("u8");
qRegisterMetaType<u16>("u16");
qRegisterMetaType<u32>("u32");
qRegisterMetaType<u64>("u64");
qRegisterMetaType<u128>("u128");
qRegisterMetaType<s8>("s8");
qRegisterMetaType<s16>("s16");
qRegisterMetaType<s32>("s32");
qRegisterMetaType<s64>("s64");
qRegisterMetaType<f32>("f32");
qRegisterMetaType<f64>("f64");
// Register string types
qRegisterMetaType<std::string>("std::string");
qRegisterMetaType<std::wstring>("std::wstring");
qRegisterMetaType<std::u8string>("std::u8string");
qRegisterMetaType<std::u16string>("std::u16string");
qRegisterMetaType<std::u32string>("std::u32string");
qRegisterMetaType<std::string_view>("std::string_view");
qRegisterMetaType<std::wstring_view>("std::wstring_view");
qRegisterMetaType<std::u8string_view>("std::u8string_view");
qRegisterMetaType<std::u16string_view>("std::u16string_view");
qRegisterMetaType<std::u32string_view>("std::u32string_view");
// Register applet types
// Cabinet Applet
qRegisterMetaType<Core::Frontend::CabinetParameters>("Core::Frontend::CabinetParameters");
qRegisterMetaType<std::shared_ptr<Service::NFC::NfcDevice>>(
"std::shared_ptr<Service::NFC::NfcDevice>");
// Controller Applet
qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
// Profile Select Applet
qRegisterMetaType<Core::Frontend::ProfileSelectParameters>(
"Core::Frontend::ProfileSelectParameters");
// Software Keyboard Applet
qRegisterMetaType<Core::Frontend::KeyboardInitializeParameters>(
"Core::Frontend::KeyboardInitializeParameters");
qRegisterMetaType<Core::Frontend::InlineAppearParameters>(
"Core::Frontend::InlineAppearParameters");
qRegisterMetaType<Core::Frontend::InlineTextParameters>("Core::Frontend::InlineTextParameters");
qRegisterMetaType<Service::AM::Frontend::SwkbdResult>("Service::AM::Frontend::SwkbdResult");
qRegisterMetaType<Service::AM::Frontend::SwkbdTextCheckResult>(
"Service::AM::Frontend::SwkbdTextCheckResult");
qRegisterMetaType<Service::AM::Frontend::SwkbdReplyType>(
"Service::AM::Frontend::SwkbdReplyType");
// Web Browser Applet
qRegisterMetaType<Service::AM::Frontend::WebExitReason>("Service::AM::Frontend::WebExitReason");
// Register loader types
qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus");
}
2022-11-13 22:14:08 +01:00
void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
std::shared_ptr<Service::NFC::NfcDevice> nfp_device) {
2023-03-25 18:29:08 +01:00
cabinet_applet =
new QtAmiiboSettingsDialog(this, parameters, input_subsystem.get(), nfp_device);
SCOPE_EXIT {
2023-03-25 18:29:08 +01:00
cabinet_applet->deleteLater();
cabinet_applet = nullptr;
};
2023-03-25 18:29:08 +01:00
cabinet_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
cabinet_applet->setWindowModality(Qt::WindowModal);
if (cabinet_applet->exec() == QDialog::Rejected) {
2022-11-13 22:14:08 +01:00
emit AmiiboSettingsFinished(false, {});
return;
}
2023-03-25 18:29:08 +01:00
emit AmiiboSettingsFinished(true, cabinet_applet->GetName());
}
void GMainWindow::AmiiboSettingsRequestExit() {
if (cabinet_applet) {
cabinet_applet->reject();
}
}
void GMainWindow::ControllerSelectorReconfigureControllers(
const Core::Frontend::ControllerParameters& parameters) {
2023-03-25 18:29:08 +01:00
controller_applet =
new QtControllerSelectorDialog(this, parameters, input_subsystem.get(), *system);
SCOPE_EXIT {
2023-03-25 18:29:08 +01:00
controller_applet->deleteLater();
controller_applet = nullptr;
};
2023-03-25 18:29:08 +01:00
controller_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint |
Qt::WindowStaysOnTopHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint);
controller_applet->setWindowModality(Qt::WindowModal);
bool is_success = controller_applet->exec() != QDialog::Rejected;
// Don't forget to apply settings.
system->HIDCore().DisableAllControllerConfiguration();
2021-10-14 20:32:19 +02:00
system->ApplySettings();
config->SaveAllValues();
UpdateStatusButtons();
emit ControllerSelectorReconfigureFinished(is_success);
}
2023-03-25 18:29:08 +01:00
void GMainWindow::ControllerSelectorRequestExit() {
if (controller_applet) {
controller_applet->reject();
}
}
void GMainWindow::ProfileSelectorSelectProfile(
const Core::Frontend::ProfileSelectParameters& parameters) {
profile_select_applet = new QtProfileSelectionDialog(*system, this, parameters);
SCOPE_EXIT {
2023-03-25 18:29:08 +01:00
profile_select_applet->deleteLater();
profile_select_applet = nullptr;
};
2023-03-25 18:29:08 +01:00
profile_select_applet->setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint |
Qt::WindowStaysOnTopHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
profile_select_applet->setWindowModality(Qt::WindowModal);
if (profile_select_applet->exec() == QDialog::Rejected) {
emit ProfileSelectorFinishedSelection(std::nullopt);
return;
}
const auto uuid = system->GetProfileManager().GetUser(
static_cast<std::size_t>(profile_select_applet->GetIndex()));
if (!uuid.has_value()) {
emit ProfileSelectorFinishedSelection(std::nullopt);
return;
}
emit ProfileSelectorFinishedSelection(uuid);
}
2023-03-25 18:29:08 +01:00
void GMainWindow::ProfileSelectorRequestExit() {
if (profile_select_applet) {
profile_select_applet->reject();
}
}
void GMainWindow::SoftwareKeyboardInitialize(
bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters) {
if (software_keyboard) {
LOG_ERROR(Frontend, "The software keyboard is already initialized!");
return;
}
2021-10-14 20:32:19 +02:00
software_keyboard = new QtSoftwareKeyboardDialog(render_window, *system, is_inline,
std::move(initialize_parameters));
if (is_inline) {
connect(
software_keyboard, &QtSoftwareKeyboardDialog::SubmitInlineText, this,
[this](Service::AM::Frontend::SwkbdReplyType reply_type, std::u16string submitted_text,
s32 cursor_position) {
emit SoftwareKeyboardSubmitInlineText(reply_type, submitted_text, cursor_position);
},
Qt::QueuedConnection);
} else {
connect(
software_keyboard, &QtSoftwareKeyboardDialog::SubmitNormalText, this,
[this](Service::AM::Frontend::SwkbdResult result, std::u16string submitted_text,
bool confirmed) {
emit SoftwareKeyboardSubmitNormalText(result, submitted_text, confirmed);
},
Qt::QueuedConnection);
}
}
void GMainWindow::SoftwareKeyboardShowNormal() {
if (!software_keyboard) {
LOG_ERROR(Frontend, "The software keyboard is not initialized!");
return;
}
const auto& layout = render_window->GetFramebufferLayout();
const auto x = layout.screen.left;
const auto y = layout.screen.top;
const auto w = layout.screen.GetWidth();
const auto h = layout.screen.GetHeight();
const auto scale_ratio = devicePixelRatioF();
software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y) / scale_ratio),
QSize(w, h) / scale_ratio);
}
void GMainWindow::SoftwareKeyboardShowTextCheck(
Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
std::u16string text_check_message) {
if (!software_keyboard) {
LOG_ERROR(Frontend, "The software keyboard is not initialized!");
return;
}
software_keyboard->ShowTextCheckDialog(text_check_result, text_check_message);
}
void GMainWindow::SoftwareKeyboardShowInline(
Core::Frontend::InlineAppearParameters appear_parameters) {
if (!software_keyboard) {
LOG_ERROR(Frontend, "The software keyboard is not initialized!");
return;
}
const auto& layout = render_window->GetFramebufferLayout();
const auto x =
static_cast<int>(layout.screen.left + (0.5f * layout.screen.GetWidth() *
((2.0f * appear_parameters.key_top_translate_x) +
(1.0f - appear_parameters.key_top_scale_x))));
const auto y =
static_cast<int>(layout.screen.top + (layout.screen.GetHeight() *
((2.0f * appear_parameters.key_top_translate_y) +
(1.0f - appear_parameters.key_top_scale_y))));
const auto w = static_cast<int>(layout.screen.GetWidth() * appear_parameters.key_top_scale_x);
const auto h = static_cast<int>(layout.screen.GetHeight() * appear_parameters.key_top_scale_y);
const auto scale_ratio = devicePixelRatioF();
software_keyboard->ShowInlineKeyboard(std::move(appear_parameters),
render_window->mapToGlobal(QPoint(x, y) / scale_ratio),
QSize(w, h) / scale_ratio);
}
void GMainWindow::SoftwareKeyboardHideInline() {
if (!software_keyboard) {
LOG_ERROR(Frontend, "The software keyboard is not initialized!");
return;
}
software_keyboard->HideInlineKeyboard();
}
void GMainWindow::SoftwareKeyboardInlineTextChanged(
Core::Frontend::InlineTextParameters text_parameters) {
if (!software_keyboard) {
LOG_ERROR(Frontend, "The software keyboard is not initialized!");
return;
}
software_keyboard->InlineTextChanged(std::move(text_parameters));
}
void GMainWindow::SoftwareKeyboardExit() {
if (!software_keyboard) {
return;
}
software_keyboard->ExitKeyboard();
software_keyboard = nullptr;
}
void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
const std::string& additional_args, bool is_local) {
2024-03-08 10:06:48 +01:00
#ifdef SUYU_USE_QT_WEB_ENGINE
// Raw input breaks with the web applet, Disable web applets if enabled
if (UISettings::values.disable_web_applet || Settings::values.enable_raw_input) {
emit WebBrowserClosed(Service::AM::Frontend::WebExitReason::WindowClosed,
"http://localhost/");
return;
}
2023-03-25 18:29:08 +01:00
web_applet = new QtNXWebEngineView(this, *system, input_subsystem.get());
2021-10-15 21:27:18 +02:00
ui->action_Pause->setEnabled(false);
ui->action_Restart->setEnabled(false);
ui->action_Stop->setEnabled(false);
{
QProgressDialog loading_progress(this);
loading_progress.setLabelText(tr("Loading Web Applet..."));
loading_progress.setRange(0, 3);
loading_progress.setValue(0);
if (is_local && !Common::FS::Exists(main_url)) {
loading_progress.show();
auto future = QtConcurrent::run([this] { emit WebBrowserExtractOfflineRomFS(); });
while (!future.isFinished()) {
QCoreApplication::processEvents();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
loading_progress.setValue(1);
if (is_local) {
2023-03-25 18:29:08 +01:00
web_applet->LoadLocalWebPage(main_url, additional_args);
} else {
2023-03-25 18:29:08 +01:00
web_applet->LoadExternalWebPage(main_url, additional_args);
}
if (render_window->IsLoadingComplete()) {
render_window->hide();
}
const auto& layout = render_window->GetFramebufferLayout();
const auto scale_ratio = devicePixelRatioF();
2023-03-25 18:29:08 +01:00
web_applet->resize(layout.screen.GetWidth() / scale_ratio,
layout.screen.GetHeight() / scale_ratio);
web_applet->move(layout.screen.left / scale_ratio,
(layout.screen.top / scale_ratio) + menuBar()->height());
web_applet->setZoomFactor(static_cast<qreal>(layout.screen.GetWidth() / scale_ratio) /
static_cast<qreal>(Layout::ScreenUndocked::Width));
2023-03-25 18:29:08 +01:00
web_applet->setFocus();
web_applet->show();
loading_progress.setValue(2);
QCoreApplication::processEvents();
loading_progress.setValue(3);
}
bool exit_check = false;
// TODO (Morph): Remove this
QAction* exit_action = new QAction(tr("Disable Web Applet"), this);
2023-03-25 18:29:08 +01:00
connect(exit_action, &QAction::triggered, this, [this] {
const auto result = QMessageBox::warning(
this, tr("Disable Web Applet"),
tr("Disabling the web applet can lead to undefined behavior and should only be used "
"with Super Mario 3D All-Stars. Are you sure you want to disable the web "
"applet?\n(This can be re-enabled in the Debug settings.)"),
QMessageBox::Yes | QMessageBox::No);
if (result == QMessageBox::Yes) {
UISettings::values.disable_web_applet = true;
2023-03-25 18:29:08 +01:00
web_applet->SetFinished(true);
}
});
2021-10-15 21:27:18 +02:00
ui->menubar->addAction(exit_action);
2023-03-25 18:29:08 +01:00
while (!web_applet->IsFinished()) {
QCoreApplication::processEvents();
if (!exit_check) {
2023-03-25 18:29:08 +01:00
web_applet->page()->runJavaScript(
QStringLiteral("end_applet;"), [&](const QVariant& variant) {
exit_check = false;
if (variant.toBool()) {
2023-03-25 18:29:08 +01:00
web_applet->SetFinished(true);
web_applet->SetExitReason(
Service::AM::Frontend::WebExitReason::EndButtonPressed);
}
});
exit_check = true;
}
2023-03-25 18:29:08 +01:00
if (web_applet->GetCurrentURL().contains(QStringLiteral("localhost"))) {
if (!web_applet->IsFinished()) {
web_applet->SetFinished(true);
web_applet->SetExitReason(Service::AM::Frontend::WebExitReason::CallbackURL);
}
2023-03-25 18:29:08 +01:00
web_applet->SetLastURL(web_applet->GetCurrentURL().toStdString());
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
2023-03-25 18:29:08 +01:00
const auto exit_reason = web_applet->GetExitReason();
const auto last_url = web_applet->GetLastURL();
2023-03-25 18:29:08 +01:00
web_applet->hide();
render_window->setFocus();
if (render_window->IsLoadingComplete()) {
render_window->show();
}
2021-10-15 21:27:18 +02:00
ui->action_Pause->setEnabled(true);
ui->action_Restart->setEnabled(true);
ui->action_Stop->setEnabled(true);
2021-10-15 21:27:18 +02:00
ui->menubar->removeAction(exit_action);
QCoreApplication::processEvents();
emit WebBrowserClosed(exit_reason, last_url);
#else
// Utilize the same fallback as the default web browser applet.
emit WebBrowserClosed(Service::AM::Frontend::WebExitReason::WindowClosed, "http://localhost/");
#endif
}
2023-03-25 18:29:08 +01:00
void GMainWindow::WebBrowserRequestExit() {
2024-03-08 10:06:48 +01:00
#ifdef SUYU_USE_QT_WEB_ENGINE
2023-03-25 18:29:08 +01:00
if (web_applet) {
web_applet->SetExitReason(Service::AM::Frontend::WebExitReason::ExitRequested);
2023-03-25 18:29:08 +01:00
web_applet->SetFinished(true);
}
#endif
}
void GMainWindow::InitializeWidgets() {
2024-03-08 10:06:48 +01:00
#ifdef SUYU_ENABLE_COMPATIBILITY_REPORTING
2021-10-15 21:27:18 +02:00
ui->action_Report_Compatibility->setVisible(true);
2018-09-16 20:05:51 +02:00
#endif
2021-10-14 20:32:19 +02:00
render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem, *system);
2014-04-22 05:15:17 +02:00
render_window->hide();
2014-04-01 04:26:50 +02:00
2023-08-28 00:41:42 +02:00
game_list = new GameList(vfs, provider.get(), *play_time_manager, *system, this);
2021-10-15 21:27:18 +02:00
ui->horizontalLayout->addWidget(game_list);
2017-02-18 21:09:14 +01:00
game_list_placeholder = new GameListPlaceholder(this);
2021-10-15 21:27:18 +02:00
ui->horizontalLayout->addWidget(game_list_placeholder);
game_list_placeholder->setVisible(false);
loading_screen = new LoadingScreen(this);
loading_screen->hide();
2021-10-15 21:27:18 +02:00
ui->horizontalLayout->addWidget(loading_screen);
connect(loading_screen, &LoadingScreen::Hidden, [&] {
loading_screen->Clear();
if (emulation_running) {
render_window->show();
render_window->setFocus();
}
});
multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui->action_Leave_Room,
ui->action_Show_Room, *system);
multiplayer_state->setVisible(false);
2017-02-18 21:09:14 +01:00
// Create status bar
message_label = new QLabel();
// Configured separately for left alignment
message_label->setFrameStyle(QFrame::NoFrame);
message_label->setContentsMargins(4, 0, 4, 0);
message_label->setAlignment(Qt::AlignLeft);
statusBar()->addPermanentWidget(message_label, 1);
2020-07-10 05:36:38 +02:00
shader_building_label = new QLabel();
shader_building_label->setToolTip(tr("The amount of shaders currently being built"));
res_scale_label = new QLabel();
res_scale_label->setToolTip(tr("The current selected resolution scaling multiplier."));
2017-02-18 21:09:14 +01:00
emu_speed_label = new QLabel();
2018-01-16 19:05:21 +01:00
emu_speed_label->setToolTip(
tr("Current emulation speed. Values higher or lower than 100% "
"indicate emulation is running faster or slower than a Switch."));
2017-02-18 21:09:14 +01:00
game_fps_label = new QLabel();
game_fps_label->setToolTip(tr("How many frames per second the game is currently displaying. "
"This will vary from game to game and scene to scene."));
2017-02-18 21:09:14 +01:00
emu_frametime_label = new QLabel();
emu_frametime_label->setToolTip(
2018-01-14 00:49:16 +01:00
tr("Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For "
"full-speed emulation this should be at most 16.67 ms."));
2017-02-18 21:09:14 +01:00
for (auto& label : {shader_building_label, res_scale_label, emu_speed_label, game_fps_label,
emu_frametime_label}) {
2017-02-18 21:09:14 +01:00
label->setVisible(false);
2017-02-21 01:53:40 +01:00
label->setFrameStyle(QFrame::NoFrame);
label->setContentsMargins(4, 0, 4, 0);
statusBar()->addPermanentWidget(label);
2017-02-18 21:09:14 +01:00
}
2023-11-24 18:53:31 +01:00
firmware_label = new QLabel();
firmware_label->setObjectName(QStringLiteral("FirmwareLabel"));
firmware_label->setVisible(false);
firmware_label->setFocusPolicy(Qt::NoFocus);
statusBar()->addPermanentWidget(firmware_label);
2022-07-31 04:46:26 +02:00
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
tas_label = new QLabel();
tas_label->setObjectName(QStringLiteral("TASlabel"));
tas_label->setFocusPolicy(Qt::NoFocus);
statusBar()->insertPermanentWidget(0, tas_label);
volume_popup = new QWidget(this);
volume_popup->setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint | Qt::Popup);
volume_popup->setLayout(new QVBoxLayout());
volume_popup->setMinimumWidth(200);
volume_slider = new QSlider(Qt::Horizontal);
volume_slider->setObjectName(QStringLiteral("volume_slider"));
volume_slider->setMaximum(200);
volume_slider->setPageStep(5);
volume_popup->layout()->addWidget(volume_slider);
volume_button = new VolumeButton();
volume_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
volume_button->setFocusPolicy(Qt::NoFocus);
volume_button->setCheckable(true);
UpdateVolumeUI();
2023-11-10 03:30:00 +01:00
connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) {
Settings::values.audio_muted = false;
const auto volume = static_cast<u8>(percentage);
Settings::values.volume.SetValue(volume);
UpdateVolumeUI();
});
connect(volume_button, &QPushButton::clicked, this, [&] {
UpdateVolumeUI();
volume_popup->setVisible(!volume_popup->isVisible());
QRect rect = volume_button->geometry();
QPoint bottomLeft = statusBar()->mapToGlobal(rect.topLeft());
bottomLeft.setY(bottomLeft.y() - volume_popup->geometry().height());
volume_popup->setGeometry(QRect(bottomLeft, QSize(rect.width(), rect.height())));
});
volume_button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(volume_button, &QPushButton::customContextMenuRequested,
[this](const QPoint& menu_location) {
QMenu context_menu;
context_menu.addAction(
Settings::values.audio_muted ? tr("Unmute") : tr("Mute"), [this] {
Settings::values.audio_muted = !Settings::values.audio_muted;
UpdateVolumeUI();
});
context_menu.addAction(tr("Reset Volume"), [this] {
Settings::values.volume.SetValue(100);
UpdateVolumeUI();
});
context_menu.exec(volume_button->mapToGlobal(menu_location));
volume_button->repaint();
});
connect(volume_button, &VolumeButton::VolumeChanged, this, &GMainWindow::UpdateVolumeUI);
statusBar()->insertPermanentWidget(0, volume_button);
2021-10-22 22:56:08 +02:00
// setup AA button
aa_status_button = new QPushButton();
aa_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
aa_status_button->setFocusPolicy(Qt::NoFocus);
connect(aa_status_button, &QPushButton::clicked, [&] {
auto aa_mode = Settings::values.anti_aliasing.GetValue();
aa_mode = static_cast<Settings::AntiAliasing>(static_cast<u32>(aa_mode) + 1);
if (aa_mode == Settings::AntiAliasing::MaxEnum) {
2021-10-22 22:56:08 +02:00
aa_mode = Settings::AntiAliasing::None;
}
Settings::values.anti_aliasing.SetValue(aa_mode);
aa_status_button->setChecked(true);
UpdateAAText();
});
UpdateAAText();
aa_status_button->setCheckable(true);
aa_status_button->setChecked(true);
aa_status_button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(aa_status_button, &QPushButton::customContextMenuRequested,
[this](const QPoint& menu_location) {
QMenu context_menu;
for (auto const& aa_text_pair : ConfigurationShared::anti_aliasing_texts_map) {
context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] {
Settings::values.anti_aliasing.SetValue(aa_text_pair.first);
UpdateAAText();
});
}
context_menu.exec(aa_status_button->mapToGlobal(menu_location));
aa_status_button->repaint();
});
2021-10-22 22:56:08 +02:00
statusBar()->insertPermanentWidget(0, aa_status_button);
// Setup Filter button
filter_status_button = new QPushButton();
filter_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
filter_status_button->setFocusPolicy(Qt::NoFocus);
connect(filter_status_button, &QPushButton::clicked, this,
&GMainWindow::OnToggleAdaptingFilter);
UpdateFilterText();
filter_status_button->setCheckable(true);
2021-10-22 22:56:08 +02:00
filter_status_button->setChecked(true);
filter_status_button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(filter_status_button, &QPushButton::customContextMenuRequested,
[this](const QPoint& menu_location) {
QMenu context_menu;
for (auto const& filter_text_pair : ConfigurationShared::scaling_filter_texts_map) {
context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] {
Settings::values.scaling_filter.SetValue(filter_text_pair.first);
UpdateFilterText();
});
}
context_menu.exec(filter_status_button->mapToGlobal(menu_location));
filter_status_button->repaint();
});
statusBar()->insertPermanentWidget(0, filter_status_button);
// Setup Dock button
dock_status_button = new QPushButton();
dock_status_button->setObjectName(QStringLiteral("DockingStatusBarButton"));
dock_status_button->setFocusPolicy(Qt::NoFocus);
connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode);
dock_status_button->setCheckable(true);
UpdateDockedButton();
dock_status_button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(dock_status_button, &QPushButton::customContextMenuRequested,
[this](const QPoint& menu_location) {
QMenu context_menu;
for (auto const& pair : ConfigurationShared::use_docked_mode_texts_map) {
context_menu.addAction(pair.second, [this, &pair] {
if (pair.first != Settings::values.use_docked_mode.GetValue()) {
OnToggleDockedMode();
}
});
}
context_menu.exec(dock_status_button->mapToGlobal(menu_location));
dock_status_button->repaint();
});
statusBar()->insertPermanentWidget(0, dock_status_button);
// Setup GPU Accuracy button
gpu_accuracy_button = new QPushButton();
gpu_accuracy_button->setObjectName(QStringLiteral("GPUStatusBarButton"));
gpu_accuracy_button->setCheckable(true);
gpu_accuracy_button->setFocusPolicy(Qt::NoFocus);
connect(gpu_accuracy_button, &QPushButton::clicked, this, &GMainWindow::OnToggleGpuAccuracy);
UpdateGPUAccuracyButton();
gpu_accuracy_button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(gpu_accuracy_button, &QPushButton::customContextMenuRequested,
[this](const QPoint& menu_location) {
QMenu context_menu;
for (auto const& gpu_accuracy_pair : ConfigurationShared::gpu_accuracy_texts_map) {
if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) {
continue;
}
context_menu.addAction(gpu_accuracy_pair.second, [this, gpu_accuracy_pair] {
Settings::values.gpu_accuracy.SetValue(gpu_accuracy_pair.first);
UpdateGPUAccuracyButton();
});
}
context_menu.exec(gpu_accuracy_button->mapToGlobal(menu_location));
gpu_accuracy_button->repaint();
});
statusBar()->insertPermanentWidget(0, gpu_accuracy_button);
// Setup Renderer API button
renderer_status_button = new QPushButton();
renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton"));
renderer_status_button->setCheckable(true);
renderer_status_button->setFocusPolicy(Qt::NoFocus);
2022-11-28 02:37:37 +01:00
connect(renderer_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleGraphicsAPI);
UpdateAPIText();
renderer_status_button->setCheckable(true);
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
Settings::RendererBackend::Vulkan);
renderer_status_button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(renderer_status_button, &QPushButton::customContextMenuRequested,
[this](const QPoint& menu_location) {
QMenu context_menu;
for (auto const& renderer_backend_pair :
ConfigurationShared::renderer_backend_texts_map) {
if (renderer_backend_pair.first == Settings::RendererBackend::Null) {
continue;
}
context_menu.addAction(
renderer_backend_pair.second, [this, renderer_backend_pair] {
Settings::values.renderer_backend.SetValue(renderer_backend_pair.first);
UpdateAPIText();
});
}
context_menu.exec(renderer_status_button->mapToGlobal(menu_location));
renderer_status_button->repaint();
});
statusBar()->insertPermanentWidget(0, renderer_status_button);
2017-02-18 21:09:14 +01:00
statusBar()->setVisible(true);
setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}"));
}
void GMainWindow::InitializeDebugWidgets() {
2021-10-15 21:27:18 +02:00
QMenu* debug_menu = ui->menu_View_Debugging;
2015-09-01 06:35:33 +02:00
#if MICROPROFILE_ENABLED
microProfileDialog = new MicroProfileDialog(this);
microProfileDialog->hide();
debug_menu->addAction(microProfileDialog->toggleViewAction());
#endif
2021-10-14 20:32:19 +02:00
waitTreeWidget = new WaitTreeWidget(*system, this);
2016-04-08 18:28:54 +02:00
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
waitTreeWidget->hide();
debug_menu->addAction(waitTreeWidget->toggleViewAction());
controller_dialog = new ControllerDialog(system->HIDCore(), input_subsystem, this);
2021-02-04 15:54:27 +01:00
controller_dialog->hide();
debug_menu->addAction(controller_dialog->toggleViewAction());
connect(this, &GMainWindow::EmulationStarting, waitTreeWidget,
&WaitTreeWidget::OnEmulationStarting);
connect(this, &GMainWindow::EmulationStopping, waitTreeWidget,
&WaitTreeWidget::OnEmulationStopping);
}
void GMainWindow::InitializeRecentFileMenuActions() {
for (int i = 0; i < max_recent_files_item; ++i) {
actions_recent_files[i] = new QAction(this);
actions_recent_files[i]->setVisible(false);
connect(actions_recent_files[i], &QAction::triggered, this, &GMainWindow::OnMenuRecentFile);
2021-10-15 21:27:18 +02:00
ui->menu_recent_files->addAction(actions_recent_files[i]);
}
2021-10-15 21:27:18 +02:00
ui->menu_recent_files->addSeparator();
QAction* action_clear_recent_files = new QAction(this);
action_clear_recent_files->setText(tr("&Clear Recent Files"));
connect(action_clear_recent_files, &QAction::triggered, this, [this] {
UISettings::values.recent_files.clear();
UpdateRecentFiles();
});
2021-10-15 21:27:18 +02:00
ui->menu_recent_files->addAction(action_clear_recent_files);
UpdateRecentFiles();
}
void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name,
const bool tas_allowed) {
static const auto main_window = std::string("Main Window");
action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name.toStdString()));
action->setShortcutContext(
hotkey_registry.GetShortcutContext(main_window, action_name.toStdString()));
action->setAutoRepeat(false);
this->addAction(action);
2021-11-16 00:57:41 +01:00
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
const auto* controller_hotkey =
hotkey_registry.GetControllerHotkey(main_window, action_name.toStdString(), controller);
connect(
controller_hotkey, &ControllerShortcut::Activated, this,
[action, tas_allowed, this] {
auto [tas_status, current_tas_frame, total_tas_frames] =
input_subsystem->GetTas()->GetStatus();
if (tas_allowed || tas_status == InputCommon::TasInput::TasState::Stopped) {
action->trigger();
}
},
Qt::QueuedConnection);
}
void GMainWindow::InitializeHotkeys() {
hotkey_registry.LoadHotkeys();
LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File"));
LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load/Remove Amiibo"));
2024-03-07 10:21:59 +01:00
LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit suyu"));
LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation"));
LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation"));
LinkActionShortcut(ui->action_Stop, QStringLiteral("Stop Emulation"));
LinkActionShortcut(ui->action_Show_Filter_Bar, QStringLiteral("Toggle Filter Bar"));
LinkActionShortcut(ui->action_Show_Status_Bar, QStringLiteral("Toggle Status Bar"));
LinkActionShortcut(ui->action_Fullscreen, QStringLiteral("Fullscreen"));
LinkActionShortcut(ui->action_Capture_Screenshot, QStringLiteral("Capture Screenshot"));
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);
LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true);
LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true);
LinkActionShortcut(ui->action_View_Lobby,
QStringLiteral("Multiplayer Browse Public Game Lobby"));
LinkActionShortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room"));
LinkActionShortcut(ui->action_Connect_To_Room,
QStringLiteral("Multiplayer Direct Connect to Room"));
LinkActionShortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room"));
LinkActionShortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room"));
static const QString main_window = QStringLiteral("Main Window");
const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
const auto* hotkey =
hotkey_registry.GetHotkey(main_window.toStdString(), action_name.toStdString(), this);
2021-11-16 00:57:41 +01:00
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
const auto* controller_hotkey = hotkey_registry.GetControllerHotkey(
main_window.toStdString(), action_name.toStdString(), controller);
connect(hotkey, &QShortcut::activated, this, function);
connect(controller_hotkey, &ControllerShortcut::Activated, this, function,
Qt::QueuedConnection);
};
connect_shortcut(QStringLiteral("Exit Fullscreen"), [&] {
if (emulation_running && ui->action_Fullscreen->isChecked()) {
ui->action_Fullscreen->setChecked(false);
ToggleFullscreen();
}
});
connect_shortcut(QStringLiteral("Change Adapting Filter"),
&GMainWindow::OnToggleAdaptingFilter);
connect_shortcut(QStringLiteral("Change Docked Mode"), &GMainWindow::OnToggleDockedMode);
connect_shortcut(QStringLiteral("Change GPU Accuracy"), &GMainWindow::OnToggleGpuAccuracy);
connect_shortcut(QStringLiteral("Audio Mute/Unmute"), &GMainWindow::OnMute);
connect_shortcut(QStringLiteral("Audio Volume Down"), &GMainWindow::OnDecreaseVolume);
connect_shortcut(QStringLiteral("Audio Volume Up"), &GMainWindow::OnIncreaseVolume);
connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] {
Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue());
});
2023-09-10 22:26:09 +02:00
connect_shortcut(QStringLiteral("Toggle Renderdoc Capture"), [this] {
if (Settings::values.enable_renderdoc_hotkey) {
system->GetRenderdocAPI().ToggleCapture();
}
});
connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] {
Settings::values.mouse_panning = !Settings::values.mouse_panning;
if (Settings::values.mouse_panning) {
render_window->installEventFilter(render_window);
render_window->setAttribute(Qt::WA_Hover, true);
}
});
}
2014-04-01 04:26:50 +02:00
void GMainWindow::SetDefaultUIGeometry() {
// geometry: 53% of the window contents are in the upper screen half, 47% in the lower half
const QRect screenRect = QGuiApplication::primaryScreen()->geometry();
const int w = screenRect.width() * 2 / 3;
const int h = screenRect.height() * 2 / 3;
const int x = (screenRect.x() + screenRect.width()) / 2 - w / 2;
const int y = (screenRect.y() + screenRect.height()) / 2 - h * 53 / 100;
2014-04-01 04:26:50 +02:00
setGeometry(x, y, w, h);
}
2014-04-01 04:26:50 +02:00
void GMainWindow::RestoreUIState() {
setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
restoreGeometry(UISettings::values.geometry);
// Work-around because the games list isn't supposed to be full screen
if (isFullScreen()) {
showNormal();
}
restoreState(UISettings::values.state);
render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint);
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
#if MICROPROFILE_ENABLED
microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry);
microProfileDialog->setVisible(UISettings::values.microprofile_visible.GetValue());
#endif
game_list->LoadInterfaceLayout();
2015-09-02 14:56:38 +02:00
2021-10-15 21:27:18 +02:00
ui->action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode.GetValue());
2014-04-22 05:15:17 +02:00
ToggleWindowMode();
2014-04-01 04:26:50 +02:00
2021-10-15 21:27:18 +02:00
ui->action_Fullscreen->setChecked(UISettings::values.fullscreen.GetValue());
2021-10-15 21:27:18 +02:00
ui->action_Display_Dock_Widget_Headers->setChecked(
UISettings::values.display_titlebar.GetValue());
2021-10-15 21:27:18 +02:00
OnDisplayTitleBars(ui->action_Display_Dock_Widget_Headers->isChecked());
2017-02-18 21:09:14 +01:00
2021-10-15 21:27:18 +02:00
ui->action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar.GetValue());
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
2021-10-15 21:27:18 +02:00
ui->action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar.GetValue());
ui->action_Show_Folders_In_List->setChecked(UISettings::values.show_folders_in_list.GetValue());
2021-10-15 21:27:18 +02:00
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
2018-07-02 19:10:41 +02:00
Debugger::ToggleConsole();
}
void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) {
if (state != Qt::ApplicationHidden && state != Qt::ApplicationInactive &&
state != Qt::ApplicationActive) {
LOG_DEBUG(Frontend, "ApplicationState unusual flag: {} ", state);
}
2022-02-07 03:46:22 +01:00
if (!emulation_running) {
return;
}
if (UISettings::values.pause_when_in_background) {
2021-12-05 08:06:56 +01:00
if (emu_thread->IsRunning() &&
(state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) {
auto_paused = true;
OnPauseGame();
} else if (!emu_thread->IsRunning() && auto_paused && state == Qt::ApplicationActive) {
auto_paused = false;
OnStartGame();
}
}
2022-02-07 03:46:22 +01:00
if (UISettings::values.mute_when_in_background) {
if (!Settings::values.audio_muted &&
(state & (Qt::ApplicationHidden | Qt::ApplicationInactive))) {
Settings::values.audio_muted = true;
auto_muted = true;
} else if (auto_muted && state == Qt::ApplicationActive) {
Settings::values.audio_muted = false;
auto_muted = false;
}
UpdateVolumeUI();
2022-02-07 03:46:22 +01:00
}
}
void GMainWindow::ConnectWidgetEvents() {
connect(game_list, &GameList::BootGame, this, &GMainWindow::BootGameFromList);
connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile);
connect(game_list, &GameList::OpenDirectory, this, &GMainWindow::OnGameListOpenDirectory);
connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this,
&GMainWindow::OnTransferableShaderCacheOpenFile);
connect(game_list, &GameList::RemoveInstalledEntryRequested, this,
&GMainWindow::OnGameListRemoveInstalledEntry);
connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile);
2023-08-27 03:19:00 +02:00
connect(game_list, &GameList::RemovePlayTimeRequested, this,
&GMainWindow::OnGameListRemovePlayTimeData);
connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
connect(game_list, &GameList::VerifyIntegrityRequested, this,
&GMainWindow::OnGameListVerifyIntegrity);
connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
2018-08-29 15:42:53 +02:00
connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
&GMainWindow::OnGameListNavigateToGamedbEntry);
connect(game_list, &GameList::CreateShortcut, this, &GMainWindow::OnGameListCreateShortcut);
connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory);
connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this,
&GMainWindow::OnGameListAddDirectory);
connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList);
connect(game_list, &GameList::PopulatingCompleted,
[this] { multiplayer_state->UpdateGameList(game_list->GetModel()); });
connect(game_list, &GameList::SaveConfig, this, &GMainWindow::OnSaveConfig);
connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
&GMainWindow::OnGameListOpenPerGameProperties);
2014-04-01 04:26:50 +02:00
connect(this, &GMainWindow::UpdateInstallProgress, this,
&GMainWindow::IncrementInstallProgress);
connect(this, &GMainWindow::EmulationStarting, render_window,
&GRenderWindow::OnEmulationStarting);
connect(this, &GMainWindow::EmulationStopping, render_window,
&GRenderWindow::OnEmulationStopping);
// Software Keyboard Applet
connect(this, &GMainWindow::EmulationStarting, this, &GMainWindow::SoftwareKeyboardExit);
connect(this, &GMainWindow::EmulationStopping, this, &GMainWindow::SoftwareKeyboardExit);
connect(&status_bar_update_timer, &QTimer::timeout, this, &GMainWindow::UpdateStatusBar);
connect(this, &GMainWindow::UpdateThemedIcons, multiplayer_state,
&MultiplayerState::UpdateThemedIcons);
2014-04-01 04:26:50 +02:00
}
void GMainWindow::ConnectMenuEvents() {
const auto connect_menu = [&]<typename Fn>(QAction* action, const Fn& event_fn) {
connect(action, &QAction::triggered, this, event_fn);
// Add actions to this window so that hiding menus in fullscreen won't disable them
addAction(action);
// Add actions to the render window so that they work outside of single window mode
render_window->addAction(action);
};
// File
connect_menu(ui->action_Load_File, &GMainWindow::OnMenuLoadFile);
connect_menu(ui->action_Load_Folder, &GMainWindow::OnMenuLoadFolder);
connect_menu(ui->action_Install_File_NAND, &GMainWindow::OnMenuInstallToNAND);
connect_menu(ui->action_Exit, &QMainWindow::close);
connect_menu(ui->action_Load_Amiibo, &GMainWindow::OnLoadAmiibo);
// Emulation
connect_menu(ui->action_Pause, &GMainWindow::OnPauseContinueGame);
connect_menu(ui->action_Stop, &GMainWindow::OnStopGame);
connect_menu(ui->action_Report_Compatibility, &GMainWindow::OnMenuReportCompatibility);
connect_menu(ui->action_Open_Mods_Page, &GMainWindow::OnOpenModsPage);
connect_menu(ui->action_Open_Quickstart_Guide, &GMainWindow::OnOpenQuickstartGuide);
connect_menu(ui->action_Open_FAQ, &GMainWindow::OnOpenFAQ);
connect_menu(ui->action_Restart, &GMainWindow::OnRestartGame);
connect_menu(ui->action_Configure, &GMainWindow::OnConfigure);
connect_menu(ui->action_Configure_Current_Game, &GMainWindow::OnConfigurePerGame);
// View
connect_menu(ui->action_Fullscreen, &GMainWindow::ToggleFullscreen);
connect_menu(ui->action_Single_Window_Mode, &GMainWindow::ToggleWindowMode);
connect_menu(ui->action_Display_Dock_Widget_Headers, &GMainWindow::OnDisplayTitleBars);
connect_menu(ui->action_Show_Filter_Bar, &GMainWindow::OnToggleFilterBar);
2021-11-16 00:57:41 +01:00
connect_menu(ui->action_Show_Status_Bar, &GMainWindow::OnToggleStatusBar);
connect_menu(ui->action_Show_Folders_In_List, &GMainWindow::OnToggleFoldersInList);
connect_menu(ui->action_Reset_Window_Size_720, &GMainWindow::ResetWindowSize720);
connect_menu(ui->action_Reset_Window_Size_900, &GMainWindow::ResetWindowSize900);
connect_menu(ui->action_Reset_Window_Size_1080, &GMainWindow::ResetWindowSize1080);
ui->menu_Reset_Window_Size->addActions({ui->action_Reset_Window_Size_720,
ui->action_Reset_Window_Size_900,
ui->action_Reset_Window_Size_1080});
2018-01-14 19:15:45 +01:00
// Multiplayer
connect(ui->action_View_Lobby, &QAction::triggered, multiplayer_state,
&MultiplayerState::OnViewLobby);
connect(ui->action_Start_Room, &QAction::triggered, multiplayer_state,
&MultiplayerState::OnCreateRoom);
connect(ui->action_Leave_Room, &QAction::triggered, multiplayer_state,
&MultiplayerState::OnCloseRoom);
connect(ui->action_Connect_To_Room, &QAction::triggered, multiplayer_state,
&MultiplayerState::OnDirectConnectToRoom);
connect(ui->action_Show_Room, &QAction::triggered, multiplayer_state,
&MultiplayerState::OnOpenNetworkRoom);
2022-09-09 22:29:22 +02:00
connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig);
// Tools
connect_menu(ui->action_Load_Album, &GMainWindow::OnAlbum);
connect_menu(ui->action_Load_Cabinet_Nickname_Owner,
[this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); });
connect_menu(ui->action_Load_Cabinet_Eraser,
[this]() { OnCabinet(Service::NFP::CabinetMode::StartGameDataEraser); });
connect_menu(ui->action_Load_Cabinet_Restorer,
[this]() { OnCabinet(Service::NFP::CabinetMode::StartRestorer); });
connect_menu(ui->action_Load_Cabinet_Formatter,
[this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); });
connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit);
connect_menu(ui->action_Open_Controller_Menu, &GMainWindow::OnOpenControllerMenu);
connect_menu(ui->action_Load_Home_Menu, &GMainWindow::OnHomeMenu);
connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot);
// TAS
connect_menu(ui->action_TAS_Start, &GMainWindow::OnTasStartStop);
connect_menu(ui->action_TAS_Record, &GMainWindow::OnTasRecord);
connect_menu(ui->action_TAS_Reset, &GMainWindow::OnTasReset);
connect_menu(ui->action_Configure_Tas, &GMainWindow::OnConfigureTas);
2018-01-14 19:15:45 +01:00
// Help
2024-03-08 10:06:48 +01:00
connect_menu(ui->action_Open_suyu_Folder, &GMainWindow::OnOpenSuyuFolder);
connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents);
connect_menu(ui->action_Install_Firmware, &GMainWindow::OnInstallFirmware);
connect_menu(ui->action_Install_Keys, &GMainWindow::OnInstallDecryptionKeys);
connect_menu(ui->action_About, &GMainWindow::OnAbout);
}
void GMainWindow::UpdateMenuState() {
const bool is_paused = emu_thread == nullptr || !emu_thread->IsRunning();
const bool is_firmware_available = CheckFirmwarePresence();
const std::array running_actions{
ui->action_Stop,
ui->action_Restart,
ui->action_Configure_Current_Game,
ui->action_Report_Compatibility,
ui->action_Load_Amiibo,
ui->action_Pause,
};
const std::array applet_actions{ui->action_Load_Album,
ui->action_Load_Cabinet_Nickname_Owner,
ui->action_Load_Cabinet_Eraser,
ui->action_Load_Cabinet_Restorer,
ui->action_Load_Cabinet_Formatter,
ui->action_Load_Mii_Edit,
ui->action_Open_Controller_Menu};
for (QAction* action : running_actions) {
action->setEnabled(emulation_running);
}
ui->action_Install_Firmware->setEnabled(!emulation_running);
ui->action_Install_Keys->setEnabled(!emulation_running);
for (QAction* action : applet_actions) {
action->setEnabled(is_firmware_available && !emulation_running);
}
ui->action_Capture_Screenshot->setEnabled(emulation_running && !is_paused);
if (emulation_running && is_paused) {
ui->action_Pause->setText(tr("&Continue"));
} else {
ui->action_Pause->setText(tr("&Pause"));
}
2022-09-09 22:29:22 +02:00
multiplayer_state->UpdateNotificationStatus();
}
void GMainWindow::OnDisplayTitleBars(bool show) {
QList<QDockWidget*> widgets = findChildren<QDockWidget*>();
if (show) {
for (QDockWidget* widget : widgets) {
QWidget* old = widget->titleBarWidget();
widget->setTitleBarWidget(nullptr);
if (old != nullptr)
delete old;
}
} else {
for (QDockWidget* widget : widgets) {
QWidget* old = widget->titleBarWidget();
widget->setTitleBarWidget(new QWidget());
if (old != nullptr)
delete old;
}
}
}
#ifdef __unix__
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
void GMainWindow::SetupPrepareForSleep() {
auto bus = QDBusConnection::systemBus();
if (bus.isConnected()) {
const bool success = bus.connect(
QStringLiteral("org.freedesktop.login1"), QStringLiteral("/org/freedesktop/login1"),
QStringLiteral("org.freedesktop.login1.Manager"), QStringLiteral("PrepareForSleep"),
QStringLiteral("b"), this, SLOT(OnPrepareForSleep(bool)));
if (!success) {
LOG_WARNING(Frontend, "Couldn't register PrepareForSleep signal");
}
} else {
LOG_WARNING(Frontend, "QDBusConnection system bus is not connected");
}
}
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
#endif // __unix__
void GMainWindow::OnPrepareForSleep(bool prepare_sleep) {
if (emu_thread == nullptr) {
return;
}
if (prepare_sleep) {
if (emu_thread->IsRunning()) {
auto_paused = true;
OnPauseGame();
}
} else {
if (!emu_thread->IsRunning() && auto_paused) {
auto_paused = false;
OnStartGame();
}
}
}
#ifdef __unix__
std::array<int, 3> GMainWindow::sig_interrupt_fds{0, 0, 0};
void GMainWindow::SetupSigInterrupts() {
if (sig_interrupt_fds[2] == 1) {
return;
}
socketpair(AF_UNIX, SOCK_STREAM, 0, sig_interrupt_fds.data());
sig_interrupt_fds[2] = 1;
struct sigaction sa;
sa.sa_handler = &GMainWindow::HandleSigInterrupt;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESETHAND;
sigaction(SIGINT, &sa, nullptr);
sigaction(SIGTERM, &sa, nullptr);
sig_interrupt_notifier = new QSocketNotifier(sig_interrupt_fds[1], QSocketNotifier::Read, this);
connect(sig_interrupt_notifier, &QSocketNotifier::activated, this,
&GMainWindow::OnSigInterruptNotifierActivated);
connect(this, &GMainWindow::SigInterrupt, this, &GMainWindow::close);
}
void GMainWindow::HandleSigInterrupt(int sig) {
if (sig == SIGINT) {
_exit(1);
}
// Calling into Qt directly from a signal handler is not safe,
// so wake up a QSocketNotifier with this hacky write call instead.
char a = 1;
int ret = write(sig_interrupt_fds[0], &a, sizeof(a));
(void)ret;
}
void GMainWindow::OnSigInterruptNotifierActivated() {
sig_interrupt_notifier->setEnabled(false);
char a;
int ret = read(sig_interrupt_fds[1], &a, sizeof(a));
(void)ret;
sig_interrupt_notifier->setEnabled(true);
emit SigInterrupt();
}
#endif // __unix__
void GMainWindow::PreventOSSleep() {
#ifdef _WIN32
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
#elif defined(HAVE_SDL2)
SDL_DisableScreenSaver();
#endif
}
void GMainWindow::AllowOSSleep() {
#ifdef _WIN32
SetThreadExecutionState(ES_CONTINUOUS);
#elif defined(HAVE_SDL2)
SDL_EnableScreenSaver();
#endif
}
bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletParameters params) {
if (Loader::AppLoader_NRO::IdentifyType(
Core::GetGameFileFromPath(vfs, filename.toStdString())) != Loader::FileType::NRO) {
if (!CheckFirmwarePresence()) {
QMessageBox::critical(this, tr("Component Missing"), tr("Missing Firmware."));
return false;
}
if (!ContentManager::AreKeysPresent()) {
QMessageBox::warning(
this, tr("Encryption Keys Missing"),
tr("In order to use suyu you need to provide your own encryption keys. "
"You can install them by going to Tools -> Install encryption keys."));
return false;
}
2024-03-16 16:57:32 +01:00
}
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr) {
ShutdownGame();
}
if (!render_window->InitRenderTarget()) {
2018-05-30 22:38:22 +02:00
return false;
}
2021-10-14 20:32:19 +02:00
system->SetFilesystem(vfs);
if (params.launch_type == Service::AM::LaunchType::FrontendInitiated) {
system->GetUserChannel().clear();
}
system->SetFrontendAppletSet({
2023-06-27 23:55:23 +02:00
std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings
(UISettings::values.controller_applet_disabled.GetValue() == true)
? nullptr
: std::make_unique<QtControllerSelector>(*this), // Controller Selector
std::make_unique<QtErrorDisplay>(*this), // Error Display
nullptr, // Mii Editor
nullptr, // Parental Controls
nullptr, // Photo Viewer
std::make_unique<QtProfileSelector>(*this), // Profile Selector
std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard
std::make_unique<QtWebBrowser>(*this), // Web Browser
});
const Core::SystemResultStatus result{
system->Load(*render_window, filename.toStdString(), params)};
2014-04-04 03:24:07 +02:00
const auto drd_callout = (UISettings::values.callout_flags.GetValue() &
static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
if (result == Core::SystemResultStatus::Success &&
2021-10-14 20:32:19 +02:00
system->GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory &&
drd_callout) {
UISettings::values.callout_flags = UISettings::values.callout_flags.GetValue() |
static_cast<u32>(CalloutFlag::DRDDeprecation);
QMessageBox::warning(
this, tr("Warning Outdated Game Format"),
tr("You are using the deconstructed ROM directory format for this game, which is an "
"outdated format that has been superseded by others such as NCA, NAX, XCI, or "
"NSP. Deconstructed ROM directories lack icons, metadata, and update "
2024-03-08 10:06:48 +01:00
"support.<br><br>For an explanation of the various Switch formats suyu supports, <a "
"href='https://suyu.dev/wiki/overview-of-switch-game-formats'>check out our "
"wiki</a>. This message will not be shown again."));
}
if (result != Core::SystemResultStatus::Success) {
switch (result) {
case Core::SystemResultStatus::ErrorGetLoader:
2018-07-02 18:13:26 +02:00
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filename.toStdString());
QMessageBox::critical(this, tr("Error while loading ROM!"),
2016-12-17 07:20:47 +01:00
tr("The ROM format is not supported."));
break;
case Core::SystemResultStatus::ErrorVideoCore:
QMessageBox::critical(
this, tr("An error occurred initializing the video core."),
2024-03-08 10:06:48 +01:00
tr("suyu has encountered an error while running the video core. "
2021-12-16 06:57:45 +01:00
"This is usually caused by outdated GPU drivers, including integrated ones. "
"Please see the log for more details. "
"For more information on accessing the log, please see the following page: "
2024-03-08 10:06:48 +01:00
"<a href='https://suyu.dev/help/reference/log-files/'>"
"How to Upload the Log File</a>. "));
break;
default:
if (result > Core::SystemResultStatus::ErrorLoader) {
const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader);
const u16 error_id = static_cast<u16>(result) - loader_id;
const std::string error_code = fmt::format("({:04X}-{:04X})", loader_id, error_id);
LOG_CRITICAL(Frontend, "Failed to load ROM! {}", error_code);
const auto title =
tr("Error while loading ROM! %1", "%1 signifies a numeric error code.")
.arg(QString::fromStdString(error_code));
const auto description =
2024-03-08 10:06:48 +01:00
tr("%1<br>Please follow <a href='https://suyu.dev/help/quickstart/'>the "
"suyu quickstart guide</a> to redump your files.<br>You can refer "
2024-04-17 14:07:46 +02:00
"to the suyu wiki</a> or the suyu Chat</a> for help.",
"%1 signifies an error string.")
.arg(QString::fromStdString(
GetResultStatusString(static_cast<Loader::ResultStatus>(error_id))));
QMessageBox::critical(this, title, description);
} else {
QMessageBox::critical(
this, tr("Error while loading ROM!"),
tr("An unknown error occurred. Please see the log for more details."));
}
break;
}
return false;
2014-04-04 03:24:07 +02:00
}
2022-05-27 01:57:35 +02:00
current_game_path = filename;
return true;
}
bool GMainWindow::SelectAndSetCurrentUser(
const Core::Frontend::ProfileSelectParameters& parameters) {
QtProfileSelectionDialog dialog(*system, this, parameters);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
if (dialog.exec() == QDialog::Rejected) {
return false;
}
Settings::values.current_user = dialog.GetIndex();
return true;
}
void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
// Ensure all NCAs are registered before launching the game
2024-01-18 21:31:41 +01:00
const auto file = vfs->OpenFile(filepath, FileSys::OpenMode::Read);
if (!file) {
return;
}
auto loader = Loader::GetLoader(*system, file);
if (!loader) {
return;
}
const auto file_type = loader->GetFileType();
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
return;
}
u64 program_id = 0;
const auto res2 = loader->ReadProgramId(program_id);
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
provider->AddEntry(FileSys::TitleType::Application,
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), program_id,
file);
} else if (res2 == Loader::ResultStatus::Success &&
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
const auto nsp = file_type == Loader::FileType::NSP
? std::make_shared<FileSys::NSP>(file)
: FileSys::XCI{file}.GetSecurePartitionNSP();
for (const auto& title : nsp->GetNCAs()) {
for (const auto& entry : title.second) {
provider->AddEntry(entry.first.first, entry.first.second, title.first,
entry.second->GetBaseFile());
}
}
}
}
void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletParameters params,
StartGameType type) {
2024-03-08 10:06:48 +01:00
LOG_INFO(Frontend, "suyu starting...");
if (params.program_id == 0 ||
params.program_id > static_cast<u64>(Service::AM::AppletProgramId::MaxProgramId)) {
StoreRecentFile(filename); // Put the filename on top of the list
}
// Save configurations
UpdateUISettings();
game_list->SaveInterfaceLayout();
config->SaveAllValues();
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
u64 title_id{0};
last_filename_booted = filename;
ConfigureFilesystemProvider(filename.toStdString());
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
const auto loader = Loader::GetLoader(*system, v_file, params.program_id, params.program_index);
if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success &&
type == StartGameType::Normal) {
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
// Load per game settings
const auto file_path =
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())};
const auto config_file_name = title_id == 0
? Common::FS::PathToUTF8String(file_path.filename())
: fmt::format("{:016X}", title_id);
QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
system->HIDCore().ReloadInputDevices();
system->ApplySettings();
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
}
Settings::LogSettings();
if (UISettings::values.select_user_on_boot && !user_flag_cmd_line) {
const Core::Frontend::ProfileSelectParameters parameters{
.mode = Service::AM::Frontend::UiMode::UserSelector,
.invalid_uid_list = {},
.display_options = {},
.purpose = Service::AM::Frontend::UserSelectionPurpose::General,
};
if (SelectAndSetCurrentUser(parameters) == false) {
return;
}
}
// If the user specifies -u (successfully) on the cmd line, don't prompt for a user on first
// game startup only. If the user stops emulation and starts a new one, go back to the expected
// behavior of asking.
user_flag_cmd_line = false;
if (!LoadROM(filename, params)) {
return;
}
2022-07-17 00:48:45 +02:00
system->SetShuttingDown(false);
game_list->setDisabled(true);
2022-07-17 00:48:45 +02:00
// Create and start the emulation thread
2021-10-14 20:32:19 +02:00
emu_thread = std::make_unique<EmuThread>(*system);
emit EmulationStarting(emu_thread.get());
emu_thread->start();
2014-04-22 05:15:17 +02:00
// Register an ExecuteProgram callback such that Core can execute a sub-program
2021-10-14 20:32:19 +02:00
system->RegisterExecuteProgramCallback(
2022-06-14 20:09:45 +02:00
[this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
system->RegisterExitCallback([this] {
emu_thread->ForceStop();
render_window->Exit();
});
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity);
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
// before the CPU continues
connect(emu_thread.get(), &EmuThread::DebugModeEntered, waitTreeWidget,
&WaitTreeWidget::OnDebugModeEntered, Qt::BlockingQueuedConnection);
connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget,
&WaitTreeWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection);
connect(emu_thread.get(), &EmuThread::LoadProgress, loading_screen,
&LoadingScreen::OnLoadProgress, Qt::QueuedConnection);
// Update the GUI
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
UpdateStatusButtons();
2021-10-15 21:27:18 +02:00
if (ui->action_Single_Window_Mode->isChecked()) {
2015-09-01 06:35:33 +02:00
game_list->hide();
game_list_placeholder->hide();
2015-09-01 06:35:33 +02:00
}
status_bar_update_timer.start(500);
renderer_status_button->setDisabled(true);
2021-02-03 19:34:25 +01:00
if (UISettings::values.hide_mouse || Settings::values.mouse_panning) {
render_window->installEventFilter(render_window);
render_window->setAttribute(Qt::WA_Hover, true);
}
2021-02-24 03:39:02 +01:00
if (UISettings::values.hide_mouse) {
mouse_hide_timer.start();
}
2022-06-19 06:34:28 +02:00
render_window->InitializeCamera();
std::string title_name;
2020-06-08 23:58:04 +02:00
std::string title_version;
2021-10-14 20:32:19 +02:00
const auto res = system->GetGameName(title_name);
const auto metadata = [this, title_id] {
2021-10-14 20:32:19 +02:00
const FileSys::PatchManager pm(title_id, system->GetFileSystemController(),
system->GetContentProvider());
return pm.GetControlMetadata();
}();
2020-06-08 23:58:04 +02:00
if (metadata.first != nullptr) {
title_version = metadata.first->GetVersionString();
title_name = metadata.first->GetApplicationName();
}
2020-06-08 23:58:04 +02:00
if (res != Loader::ResultStatus::Success || title_name.empty()) {
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
title_name = Common::FS::PathToUTF8String(
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())}
.filename());
2020-06-08 23:58:04 +02:00
}
2023-10-21 22:47:43 +02:00
const bool is_64bit = system->Kernel().ApplicationProcess()->Is64Bit();
const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)");
title_name = tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit")
.arg(QString::fromStdString(title_name), instruction_set_suffix)
.toStdString();
2020-06-08 23:58:04 +02:00
LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
2021-10-14 20:32:19 +02:00
const auto gpu_vendor = system->GPU().Renderer().GetDeviceVendor();
UpdateWindowTitle(title_name, title_version, gpu_vendor);
2021-10-14 20:32:19 +02:00
loading_screen->Prepare(system->GetAppLoader());
loading_screen->show();
emulation_running = true;
2021-10-15 21:27:18 +02:00
if (ui->action_Fullscreen->isChecked()) {
ShowFullscreen();
}
OnStartGame();
2014-04-01 04:26:50 +02:00
}
void GMainWindow::BootGameFromList(const QString& filename, StartGameType with_config) {
BootGame(filename, ApplicationAppletParameters(), with_config);
}
2022-12-24 23:31:34 +01:00
bool GMainWindow::OnShutdownBegin() {
if (!emulation_running) {
2022-12-24 23:31:34 +01:00
return false;
}
2021-10-15 21:27:18 +02:00
if (ui->action_Fullscreen->isChecked()) {
HideFullscreen();
}
AllowOSSleep();
// Disable unlimited frame rate
Settings::values.use_speed_limit.SetValue(true);
2022-12-24 23:31:34 +01:00
if (system->IsShuttingDown()) {
return false;
}
2022-07-17 00:48:45 +02:00
system->SetShuttingDown(true);
2018-09-16 20:05:51 +02:00
discord_rpc->Pause();
2022-07-18 09:41:29 +02:00
RequestGameExit();
2022-12-17 20:34:03 +01:00
emu_thread->disconnect();
emu_thread->SetRunning(true);
emit EmulationStopping();
int shutdown_time = 1000;
if (system->DebuggerEnabled()) {
shutdown_time = 0;
} else if (system->GetExitLocked()) {
shutdown_time = 5000;
}
shutdown_timer.setSingleShot(true);
shutdown_timer.start(shutdown_time);
connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired);
connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped);
// Disable everything to prevent anything from being triggered here
ui->action_Pause->setEnabled(false);
ui->action_Restart->setEnabled(false);
ui->action_Stop->setEnabled(false);
2022-12-24 23:31:34 +01:00
return true;
}
void GMainWindow::OnShutdownBeginDialog() {
shutdown_dialog = new OverlayDialog(this, *system, QString{}, tr("Closing software..."),
QString{}, QString{}, Qt::AlignHCenter | Qt::AlignVCenter);
shutdown_dialog->open();
}
void GMainWindow::OnEmulationStopTimeExpired() {
if (emu_thread) {
emu_thread->ForceStop();
}
}
void GMainWindow::OnEmulationStopped() {
shutdown_timer.stop();
if (emu_thread) {
emu_thread->disconnect();
emu_thread->wait();
emu_thread.reset();
}
if (shutdown_dialog) {
shutdown_dialog->deleteLater();
shutdown_dialog = nullptr;
}
emulation_running = false;
2018-09-16 20:05:51 +02:00
discord_rpc->Update();
#ifdef __unix__
Common::Linux::StopGamemode();
#endif
// The emulation is stopped, so closing the window or not does not matter anymore
disconnect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
// Update the GUI
UpdateMenuState();
render_window->hide();
loading_screen->hide();
loading_screen->Clear();
if (game_list->IsEmpty()) {
game_list_placeholder->show();
} else {
game_list->show();
}
game_list->SetFilterFocus();
tas_label->clear();
input_subsystem->GetTas()->Stop();
OnTasStateChanged();
2022-06-19 06:34:28 +02:00
render_window->FinalizeCamera();
system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::None);
// Enable all controllers
system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
render_window->removeEventFilter(render_window);
render_window->setAttribute(Qt::WA_Hover, false);
UpdateWindowTitle();
// Disable status bar updates
status_bar_update_timer.stop();
2020-07-10 05:36:38 +02:00
shader_building_label->setVisible(false);
res_scale_label->setVisible(false);
emu_speed_label->setVisible(false);
game_fps_label->setVisible(false);
emu_frametime_label->setVisible(false);
renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan);
2023-11-24 18:53:31 +01:00
if (!firmware_label->text().isEmpty()) {
firmware_label->setVisible(true);
}
2022-05-27 01:57:35 +02:00
current_game_path.clear();
// When closing the game, destroy the GLWindow to clear the context after the game is closed
render_window->ReleaseRenderTarget();
// Enable game list
game_list->setEnabled(true);
Settings::RestoreGlobalState(system->IsPoweredOn());
system->HIDCore().ReloadInputDevices();
UpdateStatusButtons();
}
void GMainWindow::ShutdownGame() {
if (!emulation_running) {
return;
}
play_time_manager->Stop();
OnShutdownBegin();
OnEmulationStopTimeExpired();
OnEmulationStopped();
}
void GMainWindow::StoreRecentFile(const QString& filename) {
UISettings::values.recent_files.prepend(filename);
UISettings::values.recent_files.removeDuplicates();
while (UISettings::values.recent_files.size() > max_recent_files_item) {
UISettings::values.recent_files.removeLast();
}
UpdateRecentFiles();
}
void GMainWindow::UpdateRecentFiles() {
const int num_recent_files =
std::min(static_cast<int>(UISettings::values.recent_files.size()), max_recent_files_item);
for (int i = 0; i < num_recent_files; i++) {
const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg(
QFileInfo(UISettings::values.recent_files[i]).fileName());
actions_recent_files[i]->setText(text);
actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
actions_recent_files[i]->setToolTip(UISettings::values.recent_files[i]);
actions_recent_files[i]->setVisible(true);
}
for (int j = num_recent_files; j < max_recent_files_item; ++j) {
actions_recent_files[j]->setVisible(false);
}
// Enable the recent files menu if the list isn't empty
2021-10-15 21:27:18 +02:00
ui->menu_recent_files->setEnabled(num_recent_files != 0);
}
void GMainWindow::OnGameListLoadFile(QString game_path, u64 program_id) {
auto params = ApplicationAppletParameters();
params.program_id = program_id;
BootGame(game_path, params);
2015-09-01 06:35:33 +02:00
}
void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
const std::string& game_path) {
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
std::filesystem::path path;
QString open_target;
const auto [user_save_size, device_save_size] = [this, &game_path, &program_id] {
2021-10-14 20:32:19 +02:00
const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
system->GetContentProvider()};
const auto control = pm.GetControlMetadata().first;
if (control != nullptr) {
return std::make_pair(control->GetDefaultNormalSaveSize(),
control->GetDeviceSaveDataSize());
} else {
const auto file = Core::GetGameFileFromPath(vfs, game_path);
2021-10-14 20:32:19 +02:00
const auto loader = Loader::GetLoader(*system, file);
FileSys::NACP nacp{};
loader->ReadControlData(nacp);
return std::make_pair(nacp.GetDefaultNormalSaveSize(), nacp.GetDeviceSaveDataSize());
}
}();
const bool has_user_save{user_save_size > 0};
const bool has_device_save{device_save_size > 0};
ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
switch (target) {
case GameListOpenTarget::SaveData: {
open_target = tr("Save Data");
2024-03-07 10:21:59 +01:00
const auto nand_dir = Common::FS::GetSuyuPath(Common::FS::SuyuPath::NANDDir);
auto vfs_nand_dir =
2024-01-18 21:31:41 +01:00
vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
if (has_user_save) {
// User save data
const auto select_profile = [this] {
const Core::Frontend::ProfileSelectParameters parameters{
.mode = Service::AM::Frontend::UiMode::UserSelector,
.invalid_uid_list = {},
.display_options = {},
.purpose = Service::AM::Frontend::UserSelectionPurpose::General,
};
QtProfileSelectionDialog dialog(*system, this, parameters);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
if (dialog.exec() == QDialog::Rejected) {
return -1;
}
return dialog.GetIndex();
};
const auto index = select_profile();
if (index == -1) {
return;
}
const auto user_id =
system->GetProfileManager().GetUser(static_cast<std::size_t>(index));
ASSERT(user_id);
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
2024-02-19 15:36:20 +01:00
{}, vfs_nand_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account,
program_id, user_id->AsU128(), 0);
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path);
} else {
// Device save data
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath(
2024-02-19 15:36:20 +01:00
{}, vfs_nand_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account,
program_id, {}, 0);
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path);
}
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
if (!Common::FS::CreateDirs(path)) {
LOG_ERROR(Frontend, "Unable to create the directories for save data");
}
break;
}
case GameListOpenTarget::ModData: {
open_target = tr("Mod Data");
2024-03-07 10:21:59 +01:00
path = Common::FS::GetSuyuPath(Common::FS::SuyuPath::LoadDir) /
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
fmt::format("{:016X}", program_id);
break;
}
default:
UNIMPLEMENTED();
break;
}
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path));
const QDir dir(qpath);
if (!dir.exists()) {
QMessageBox::warning(this, tr("Error Opening %1 Folder").arg(open_target),
tr("Folder does not exist!"));
return;
}
LOG_INFO(Frontend, "Opening {} path for program_id={:016x}", open_target.toStdString(),
program_id);
QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
}
void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
2024-03-07 10:21:59 +01:00
const auto shader_cache_dir = Common::FS::GetSuyuPath(Common::FS::SuyuPath::ShaderDir);
const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)};
if (!Common::FS::CreateDirs(shader_cache_folder_path)) {
QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"),
2021-10-16 18:53:06 +02:00
tr("Failed to create the shader cache directory for this title."));
return;
}
const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)};
const auto qt_shader_cache_path = QString::fromStdString(shader_path_string);
QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path));
}
static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog& dialog,
const FileSys::VirtualDir& src, const FileSys::VirtualDir& dest,
bool full) {
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
return false;
if (dialog.wasCanceled())
return false;
std::vector<u8> buffer(CopyBufferSize);
auto last_timestamp = std::chrono::steady_clock::now();
const auto QtRawCopy = [&](const FileSys::VirtualFile& src_file,
const FileSys::VirtualFile& dest_file) {
if (src_file == nullptr || dest_file == nullptr) {
return false;
}
if (!dest_file->Resize(src_file->GetSize())) {
return false;
}
for (std::size_t i = 0; i < src_file->GetSize(); i += buffer.size()) {
if (dialog.wasCanceled()) {
dest_file->Resize(0);
return false;
}
using namespace std::literals::chrono_literals;
const auto new_timestamp = std::chrono::steady_clock::now();
if ((new_timestamp - last_timestamp) > 33ms) {
last_timestamp = new_timestamp;
dialog.setValue(
static_cast<int>(std::min(read_size, total_size) * 100 / total_size));
QCoreApplication::processEvents();
}
const auto read = src_file->Read(buffer.data(), buffer.size(), i);
dest_file->Write(buffer.data(), read, i);
read_size += read;
}
return true;
};
if (full) {
for (const auto& file : src->GetFiles()) {
const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName());
if (!QtRawCopy(file, out))
return false;
}
}
for (const auto& dir : src->GetSubdirectories()) {
const auto out = dest->CreateSubdirectory(dir->GetName());
if (!RomFSRawCopy(total_size, read_size, dialog, dir, out, full))
return false;
}
return true;
}
QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const {
switch (type) {
case InstalledEntryType::Game:
return tr("Error Removing Contents");
case InstalledEntryType::Update:
return tr("Error Removing Update");
case InstalledEntryType::AddOnContent:
return tr("Error Removing DLC");
default:
return QStringLiteral("Error Removing <Invalid Type>");
}
}
void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) {
const QString entry_question = [type] {
switch (type) {
case InstalledEntryType::Game:
return tr("Remove Installed Game Contents?");
case InstalledEntryType::Update:
return tr("Remove Installed Game Update?");
case InstalledEntryType::AddOnContent:
return tr("Remove Installed Game DLC?");
default:
return QStringLiteral("Remove Installed Game <Invalid Type>?");
}
}();
if (!question(this, tr("Remove Entry"), entry_question, QMessageBox::Yes | QMessageBox::No,
QMessageBox::No)) {
return;
}
switch (type) {
case InstalledEntryType::Game:
RemoveBaseContent(program_id, type);
[[fallthrough]];
case InstalledEntryType::Update:
RemoveUpdateContent(program_id, type);
2020-07-17 12:14:13 +02:00
if (type != InstalledEntryType::Game) {
break;
}
2020-07-17 12:14:13 +02:00
[[fallthrough]];
case InstalledEntryType::AddOnContent:
RemoveAddOnContent(program_id, type);
break;
}
2024-03-07 10:21:59 +01:00
Common::FS::RemoveDirRecursively(Common::FS::GetSuyuPath(Common::FS::SuyuPath::CacheDir) /
"game_list");
game_list->PopulateAsync(UISettings::values.game_dirs);
}
void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) {
const auto res =
ContentManager::RemoveBaseContent(system->GetFileSystemController(), program_id);
if (res) {
QMessageBox::information(this, tr("Successfully Removed"),
tr("Successfully removed the installed base game."));
} else {
QMessageBox::warning(
this, GetGameListErrorRemoving(type),
tr("The base game is not installed in the NAND and cannot be removed."));
}
}
void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {
const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id);
if (res) {
QMessageBox::information(this, tr("Successfully Removed"),
tr("Successfully removed the installed update."));
} else {
QMessageBox::warning(this, GetGameListErrorRemoving(type),
tr("There is no update installed for this title."));
}
}
void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
const size_t count = ContentManager::RemoveAllDLC(*system, program_id);
if (count == 0) {
QMessageBox::warning(this, GetGameListErrorRemoving(type),
tr("There are no DLC installed for this title."));
return;
}
QMessageBox::information(this, tr("Successfully Removed"),
tr("Successfully removed %1 installed DLC.").arg(count));
}
void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
const std::string& game_path) {
2022-09-21 18:51:31 +02:00
const QString question = [target] {
switch (target) {
case GameListRemoveTarget::GlShaderCache:
return tr("Delete OpenGL Transferable Shader Cache?");
case GameListRemoveTarget::VkShaderCache:
return tr("Delete Vulkan Transferable Shader Cache?");
case GameListRemoveTarget::AllShaderCache:
return tr("Delete All Transferable Shader Caches?");
case GameListRemoveTarget::CustomConfiguration:
return tr("Remove Custom Game Configuration?");
case GameListRemoveTarget::CacheStorage:
return tr("Remove Cache Storage?");
default:
return QString{};
}
}();
if (!GMainWindow::question(this, tr("Remove File"), question,
QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) {
return;
}
switch (target) {
case GameListRemoveTarget::VkShaderCache:
RemoveVulkanDriverPipelineCache(program_id);
[[fallthrough]];
case GameListRemoveTarget::GlShaderCache:
RemoveTransferableShaderCache(program_id, target);
break;
case GameListRemoveTarget::AllShaderCache:
RemoveAllTransferableShaderCaches(program_id);
break;
case GameListRemoveTarget::CustomConfiguration:
RemoveCustomConfiguration(program_id, game_path);
break;
case GameListRemoveTarget::CacheStorage:
RemoveCacheStorage(program_id);
break;
}
}
2023-08-27 03:19:00 +02:00
void GMainWindow::OnGameListRemovePlayTimeData(u64 program_id) {
if (QMessageBox::question(this, tr("Remove Play Time Data"), tr("Reset play time?"),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No) != QMessageBox::Yes) {
return;
}
2023-08-28 00:41:42 +02:00
play_time_manager->ResetProgramPlayTime(program_id);
2023-08-27 03:19:00 +02:00
game_list->PopulateAsync(UISettings::values.game_dirs);
}
void GMainWindow::RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target) {
const auto target_file_name = [target] {
switch (target) {
case GameListRemoveTarget::GlShaderCache:
return "opengl.bin";
case GameListRemoveTarget::VkShaderCache:
return "vulkan.bin";
default:
return "";
}
}();
2024-03-07 10:21:59 +01:00
const auto shader_cache_dir = Common::FS::GetSuyuPath(Common::FS::SuyuPath::ShaderDir);
const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id);
const auto target_file = shader_cache_folder_path / target_file_name;
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
if (!Common::FS::Exists(target_file)) {
QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"),
tr("A shader cache for this title does not exist."));
return;
}
if (Common::FS::RemoveFile(target_file)) {
QMessageBox::information(this, tr("Successfully Removed"),
tr("Successfully removed the transferable shader cache."));
} else {
QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"),
tr("Failed to remove the transferable shader cache."));
}
}
void GMainWindow::RemoveVulkanDriverPipelineCache(u64 program_id) {
static constexpr std::string_view target_file_name = "vulkan_pipelines.bin";
2024-03-07 10:21:59 +01:00
const auto shader_cache_dir = Common::FS::GetSuyuPath(Common::FS::SuyuPath::ShaderDir);
const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id);
const auto target_file = shader_cache_folder_path / target_file_name;
if (!Common::FS::Exists(target_file)) {
return;
}
if (!Common::FS::RemoveFile(target_file)) {
QMessageBox::warning(this, tr("Error Removing Vulkan Driver Pipeline Cache"),
tr("Failed to remove the driver pipeline cache."));
}
}
void GMainWindow::RemoveAllTransferableShaderCaches(u64 program_id) {
2024-03-07 10:21:59 +01:00
const auto shader_cache_dir = Common::FS::GetSuyuPath(Common::FS::SuyuPath::ShaderDir);
const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id);
if (!Common::FS::Exists(program_shader_cache_dir)) {
QMessageBox::warning(this, tr("Error Removing Transferable Shader Caches"),
tr("A shader cache for this title does not exist."));
return;
}
if (Common::FS::RemoveDirRecursively(program_shader_cache_dir)) {
QMessageBox::information(this, tr("Successfully Removed"),
tr("Successfully removed the transferable shader caches."));
} else {
QMessageBox::warning(this, tr("Error Removing Transferable Shader Caches"),
tr("Failed to remove the transferable shader cache directory."));
}
}
void GMainWindow::RemoveCustomConfiguration(u64 program_id, const std::string& game_path) {
const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path));
const auto config_file_name =
program_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()).append(".ini")
: fmt::format("{:016X}.ini", program_id);
const auto custom_config_file_path =
2024-03-07 10:21:59 +01:00
Common::FS::GetSuyuPath(Common::FS::SuyuPath::ConfigDir) / "custom" / config_file_name;
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
if (!Common::FS::Exists(custom_config_file_path)) {
QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
tr("A custom configuration for this title does not exist."));
return;
}
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
if (Common::FS::RemoveFile(custom_config_file_path)) {
QMessageBox::information(this, tr("Successfully Removed"),
tr("Successfully removed the custom game configuration."));
} else {
QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
tr("Failed to remove the custom game configuration."));
}
}
void GMainWindow::RemoveCacheStorage(u64 program_id) {
2024-03-07 10:21:59 +01:00
const auto nand_dir = Common::FS::GetSuyuPath(Common::FS::SuyuPath::NANDDir);
auto vfs_nand_dir =
2024-01-18 21:31:41 +01:00
vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath(
2024-02-19 15:36:20 +01:00
{}, vfs_nand_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Cache,
0 /* program_id */, {}, 0);
const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path);
// Not an error if it wasn't cleared.
Common::FS::RemoveDirRecursively(path);
}
void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path,
DumpRomFSTarget target) {
const auto failed = [this] {
QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
tr("There was an error copying the RomFS files or the user "
"cancelled the operation."));
};
const auto loader =
Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read));
if (loader == nullptr) {
failed();
return;
}
FileSys::VirtualFile packed_update_raw{};
loader->ReadUpdateRaw(packed_update_raw);
const auto& installed = system->GetContentProvider();
u64 title_id{};
u8 raw_type{};
if (!SelectRomFSDumpTarget(installed, program_id, &title_id, &raw_type)) {
failed();
return;
}
const auto type = static_cast<FileSys::ContentRecordType>(raw_type);
const auto base_nca = installed.GetEntry(title_id, type);
if (!base_nca) {
failed();
return;
}
const FileSys::NCA update_nca{packed_update_raw, nullptr};
if (type != FileSys::ContentRecordType::Program ||
update_nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS ||
update_nca.GetTitleId() != FileSys::GetUpdateTitleID(title_id)) {
packed_update_raw = {};
}
const auto base_romfs = base_nca->GetRomFS();
const auto dump_dir =
target == DumpRomFSTarget::Normal
? Common::FS::GetSuyuPath(Common::FS::SuyuPath::DumpDir)
: Common::FS::GetSuyuPath(Common::FS::SuyuPath::SDMCDir) / "atmosphere" / "contents";
const auto romfs_dir = fmt::format("{:016X}/romfs", title_id);
const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir);
const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed};
auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false);
const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::OpenMode::ReadWrite);
if (out == nullptr) {
failed();
vfs->DeleteDirectory(path);
return;
}
bool ok = false;
const QStringList selections{tr("Full"), tr("Skeleton")};
const auto res = QInputDialog::getItem(
this, tr("Select RomFS Dump Mode"),
tr("Please select the how you would like the RomFS dumped.<br>Full will copy all of the "
"files into the new directory while <br>skeleton will only create the directory "
"structure."),
selections, 0, false, &ok);
if (!ok) {
failed();
vfs->DeleteDirectory(path);
return;
}
const auto extracted = FileSys::ExtractRomFS(romfs);
if (extracted == nullptr) {
failed();
return;
}
const auto full = res == selections.constFirst();
// The expected required space is the size of the RomFS + 1 GiB
const auto minimum_free_space = romfs->GetSize() + 0x40000000;
if (full && Common::FS::GetFreeSpaceSize(path) < minimum_free_space) {
QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
tr("There is not enough free space at %1 to extract the RomFS. Please "
"free up space or select a different dump directory at "
"Emulation > Configure > System > Filesystem > Dump Root")
.arg(QString::fromStdString(path)));
return;
}
QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, 100, this);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
size_t read_size = 0;
if (RomFSRawCopy(romfs->GetSize(), read_size, progress, extracted, out, full)) {
progress.close();
QMessageBox::information(this, tr("RomFS Extraction Succeeded!"),
tr("The operation completed successfully."));
QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path)));
} else {
progress.close();
failed();
vfs->DeleteDirectory(path);
}
}
void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
const auto NotImplemented = [this] {
QMessageBox::warning(this, tr("Integrity verification couldn't be performed!"),
tr("File contents were not checked for validity."));
};
QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
const auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
progress.setValue(static_cast<int>((processed_size * 100) / total_size));
return progress.wasCanceled();
};
const auto result = ContentManager::VerifyGameContents(*system, game_path, QtProgressCallback);
progress.close();
switch (result) {
case ContentManager::GameVerificationResult::Success:
QMessageBox::information(this, tr("Integrity verification succeeded!"),
tr("The operation completed successfully."));
break;
case ContentManager::GameVerificationResult::Failed:
QMessageBox::critical(this, tr("Integrity verification failed!"),
tr("File contents may be corrupt."));
break;
case ContentManager::GameVerificationResult::NotImplemented:
NotImplemented();
}
}
void GMainWindow::OnGameListCopyTID(u64 program_id) {
QClipboard* clipboard = QGuiApplication::clipboard();
clipboard->setText(QString::fromStdString(fmt::format("{:016X}", program_id)));
}
void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
const CompatibilityList& compatibility_list) {
const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
2018-08-29 15:42:53 +02:00
QString directory;
if (it != compatibility_list.end()) {
2018-08-29 15:42:53 +02:00
directory = it->second.second;
}
2018-08-29 15:42:53 +02:00
2024-03-08 10:06:48 +01:00
QDesktopServices::openUrl(QUrl(QStringLiteral("https://suyu.dev/game/") + directory));
2018-08-29 15:42:53 +02:00
}
bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
const std::string& comment,
const std::filesystem::path& icon_path,
const std::filesystem::path& command,
const std::string& arguments, const std::string& categories,
2023-10-17 04:42:45 +02:00
const std::string& keywords, const std::string& name) try {
2023-10-16 08:26:40 +02:00
#if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD
2023-10-17 07:57:35 +02:00
std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop");
2023-10-17 04:42:45 +02:00
std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc);
if (!shortcut_stream.is_open()) {
LOG_ERROR(Frontend, "Failed to create shortcut");
return false;
}
2023-11-05 01:39:43 +01:00
// TODO: Migrate fmt::print to std::print in futures STD C++ 23.
2023-10-17 04:42:45 +02:00
fmt::print(shortcut_stream, "[Desktop Entry]\n");
fmt::print(shortcut_stream, "Type=Application\n");
fmt::print(shortcut_stream, "Version=1.0\n");
fmt::print(shortcut_stream, "Name={}\n", name);
if (!comment.empty()) {
fmt::print(shortcut_stream, "Comment={}\n", comment);
Improvement in Directory Path Detection for Shortcuts (#11749) * Improvement in Directory Path Detection for Shortcuts This pull request updates how the directory path for shortcuts is determined. The main changes are: 1. Replaced the use of environment variables to determine the path of the desktop and applications menu with `QStandardPaths::writableLocation`. This change addresses an issue where the desktop path was not correctly identified when its location was customized, as shown in the attached screenshot. 2. Added conversion from `QString` to `std::string` using `toUtf8()`, which correctly handles non-ASCII characters in directory paths. This change ensures that directory paths containing Portuguese words like "Área de trabalho" are supported. 3. Replaced directory checking using `Common::FS::IsDir()` with `QDir::exists()`. These changes should improve cross-platform compatibility and code robustness. Because it couldn't locate my desktop, which wasn't on the C drive, but on the F, and even though localization wouldn't work because it was setting it to find the 'Desktop' folder and in the computer's language it says 'Área de trabalho', that will fix for other languages too. * Update main.cpp * formatting * Update src/yuzu/main.cpp Co-authored-by: Tobias <thm.frey@gmail.com> * Update src/yuzu/main.cpp Co-authored-by: Tobias <thm.frey@gmail.com> * Update main.cpp * Update main.cpp * Update main.cpp desktopPath > desktop_Path applicationsPath > applications_Path * Update main.cpp * formatting * Update main.cpp This code will attempt to use QStandardPaths to find the applications directory. If that fails, it will resort to using the ~/.local/share/applications directory, which is a common location for application shortcuts in Linux. * Update main.cpp * formatting --------- Co-authored-by: Tobias <thm.frey@gmail.com>
2023-10-13 17:57:49 +02:00
}
2023-10-17 04:42:45 +02:00
if (std::filesystem::is_regular_file(icon_path)) {
fmt::print(shortcut_stream, "Icon={}\n", icon_path.string());
}
fmt::print(shortcut_stream, "TryExec={}\n", command.string());
2023-10-17 07:57:35 +02:00
fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments);
2023-10-17 04:42:45 +02:00
if (!categories.empty()) {
fmt::print(shortcut_stream, "Categories={}\n", categories);
}
if (!keywords.empty()) {
fmt::print(shortcut_stream, "Keywords={}\n", keywords);
}
return true;
2023-10-16 08:26:40 +02:00
#elif defined(_WIN32) // Windows
2023-10-17 07:57:35 +02:00
HRESULT hr = CoInitialize(nullptr);
2023-10-17 04:42:45 +02:00
if (FAILED(hr)) {
LOG_ERROR(Frontend, "CoInitialize failed");
return false;
}
SCOPE_EXIT {
CoUninitialize();
};
2023-10-17 04:42:45 +02:00
IShellLinkW* ps1 = nullptr;
IPersistFile* persist_file = nullptr;
SCOPE_EXIT {
2023-10-17 04:42:45 +02:00
if (persist_file != nullptr) {
persist_file->Release();
}
2023-10-17 04:42:45 +02:00
if (ps1 != nullptr) {
ps1->Release();
}
};
2023-10-17 07:57:35 +02:00
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
reinterpret_cast<void**>(&ps1));
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to create IShellLinkW instance");
2023-10-17 04:42:45 +02:00
return false;
}
2023-10-17 07:57:35 +02:00
hres = ps1->SetPath(command.c_str());
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set path");
return false;
Improvement in Directory Path Detection for Shortcuts (#11749) * Improvement in Directory Path Detection for Shortcuts This pull request updates how the directory path for shortcuts is determined. The main changes are: 1. Replaced the use of environment variables to determine the path of the desktop and applications menu with `QStandardPaths::writableLocation`. This change addresses an issue where the desktop path was not correctly identified when its location was customized, as shown in the attached screenshot. 2. Added conversion from `QString` to `std::string` using `toUtf8()`, which correctly handles non-ASCII characters in directory paths. This change ensures that directory paths containing Portuguese words like "Área de trabalho" are supported. 3. Replaced directory checking using `Common::FS::IsDir()` with `QDir::exists()`. These changes should improve cross-platform compatibility and code robustness. Because it couldn't locate my desktop, which wasn't on the C drive, but on the F, and even though localization wouldn't work because it was setting it to find the 'Desktop' folder and in the computer's language it says 'Área de trabalho', that will fix for other languages too. * Update main.cpp * formatting * Update src/yuzu/main.cpp Co-authored-by: Tobias <thm.frey@gmail.com> * Update src/yuzu/main.cpp Co-authored-by: Tobias <thm.frey@gmail.com> * Update main.cpp * Update main.cpp * Update main.cpp desktopPath > desktop_Path applicationsPath > applications_Path * Update main.cpp * formatting * Update main.cpp This code will attempt to use QStandardPaths to find the applications directory. If that fails, it will resort to using the ~/.local/share/applications directory, which is a common location for application shortcuts in Linux. * Update main.cpp * formatting --------- Co-authored-by: Tobias <thm.frey@gmail.com>
2023-10-13 17:57:49 +02:00
}
2023-10-17 07:57:35 +02:00
if (!arguments.empty()) {
2023-10-17 04:42:45 +02:00
hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data());
2023-10-17 07:57:35 +02:00
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set arguments");
return false;
}
Improvement in Directory Path Detection for Shortcuts (#11749) * Improvement in Directory Path Detection for Shortcuts This pull request updates how the directory path for shortcuts is determined. The main changes are: 1. Replaced the use of environment variables to determine the path of the desktop and applications menu with `QStandardPaths::writableLocation`. This change addresses an issue where the desktop path was not correctly identified when its location was customized, as shown in the attached screenshot. 2. Added conversion from `QString` to `std::string` using `toUtf8()`, which correctly handles non-ASCII characters in directory paths. This change ensures that directory paths containing Portuguese words like "Área de trabalho" are supported. 3. Replaced directory checking using `Common::FS::IsDir()` with `QDir::exists()`. These changes should improve cross-platform compatibility and code robustness. Because it couldn't locate my desktop, which wasn't on the C drive, but on the F, and even though localization wouldn't work because it was setting it to find the 'Desktop' folder and in the computer's language it says 'Área de trabalho', that will fix for other languages too. * Update main.cpp * formatting * Update src/yuzu/main.cpp Co-authored-by: Tobias <thm.frey@gmail.com> * Update src/yuzu/main.cpp Co-authored-by: Tobias <thm.frey@gmail.com> * Update main.cpp * Update main.cpp * Update main.cpp desktopPath > desktop_Path applicationsPath > applications_Path * Update main.cpp * formatting * Update main.cpp This code will attempt to use QStandardPaths to find the applications directory. If that fails, it will resort to using the ~/.local/share/applications directory, which is a common location for application shortcuts in Linux. * Update main.cpp * formatting --------- Co-authored-by: Tobias <thm.frey@gmail.com>
2023-10-13 17:57:49 +02:00
}
2023-10-17 07:57:35 +02:00
if (!comment.empty()) {
2023-10-17 04:42:45 +02:00
hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data());
2023-10-17 07:57:35 +02:00
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set description");
return false;
}
Improvement in Directory Path Detection for Shortcuts (#11749) * Improvement in Directory Path Detection for Shortcuts This pull request updates how the directory path for shortcuts is determined. The main changes are: 1. Replaced the use of environment variables to determine the path of the desktop and applications menu with `QStandardPaths::writableLocation`. This change addresses an issue where the desktop path was not correctly identified when its location was customized, as shown in the attached screenshot. 2. Added conversion from `QString` to `std::string` using `toUtf8()`, which correctly handles non-ASCII characters in directory paths. This change ensures that directory paths containing Portuguese words like "Área de trabalho" are supported. 3. Replaced directory checking using `Common::FS::IsDir()` with `QDir::exists()`. These changes should improve cross-platform compatibility and code robustness. Because it couldn't locate my desktop, which wasn't on the C drive, but on the F, and even though localization wouldn't work because it was setting it to find the 'Desktop' folder and in the computer's language it says 'Área de trabalho', that will fix for other languages too. * Update main.cpp * formatting * Update src/yuzu/main.cpp Co-authored-by: Tobias <thm.frey@gmail.com> * Update src/yuzu/main.cpp Co-authored-by: Tobias <thm.frey@gmail.com> * Update main.cpp * Update main.cpp * Update main.cpp desktopPath > desktop_Path applicationsPath > applications_Path * Update main.cpp * formatting * Update main.cpp This code will attempt to use QStandardPaths to find the applications directory. If that fails, it will resort to using the ~/.local/share/applications directory, which is a common location for application shortcuts in Linux. * Update main.cpp * formatting --------- Co-authored-by: Tobias <thm.frey@gmail.com>
2023-10-13 17:57:49 +02:00
}
2023-10-17 07:57:35 +02:00
if (std::filesystem::is_regular_file(icon_path)) {
hres = ps1->SetIconLocation(icon_path.c_str(), 0);
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set icon location");
return false;
}
}
2023-10-17 07:57:35 +02:00
hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&persist_file));
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to get IPersistFile interface");
return false;
2023-10-17 04:42:45 +02:00
}
2023-10-17 07:57:35 +02:00
hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE);
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to save shortcut");
return false;
2023-10-17 04:42:45 +02:00
}
2023-10-17 07:57:35 +02:00
return true;
2023-10-16 08:26:40 +02:00
#else // Unsupported platform
return false;
#endif
2023-10-17 04:42:45 +02:00
} catch (const std::exception& e) {
LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what());
return false;
}
// Messages in pre-defined message boxes for less code spaghetti
2023-10-16 21:01:46 +02:00
bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) {
int result = 0;
2023-10-17 04:42:45 +02:00
QMessageBox::StandardButtons buttons;
switch (imsg) {
case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES:
buttons = QMessageBox::Yes | QMessageBox::No;
result =
QMessageBox::information(parent, tr("Create Shortcut"),
tr("Do you want to launch the game in fullscreen?"), buttons);
2023-10-17 04:42:45 +02:00
return result == QMessageBox::Yes;
case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS:
2023-10-16 21:01:46 +02:00
QMessageBox::information(parent, tr("Create Shortcut"),
tr("Successfully created a shortcut to %1").arg(game_title));
2023-10-17 04:42:45 +02:00
return false;
case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING:
2023-10-16 21:01:46 +02:00
buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel;
result =
QMessageBox::warning(this, tr("Create Shortcut"),
tr("This will create a shortcut to the current AppImage. This may "
"not work well if you update. Continue?"),
buttons);
2023-10-17 04:42:45 +02:00
return result == QMessageBox::Ok;
default:
buttons = QMessageBox::Ok;
2023-10-16 21:01:46 +02:00
QMessageBox::critical(parent, tr("Create Shortcut"),
tr("Failed to create a shortcut to %1").arg(game_title), buttons);
2023-10-17 04:42:45 +02:00
return false;
}
}
bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
2023-10-16 08:26:40 +02:00
std::filesystem::path& out_icon_path) {
2024-03-08 10:06:48 +01:00
// Get path to Suyu icons directory & icon extension
std::string ico_extension = "png";
#if defined(_WIN32)
2024-03-07 10:21:59 +01:00
out_icon_path = Common::FS::GetSuyuPath(Common::FS::SuyuPath::IconsDir);
ico_extension = "ico";
#elif defined(__linux__) || defined(__FreeBSD__)
2023-10-16 08:26:40 +02:00
out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256";
#endif
// Create icons directory if it doesn't exist
2023-10-16 08:26:40 +02:00
if (!Common::FS::CreateDirs(out_icon_path)) {
QMessageBox::critical(
this, tr("Create Icon"),
tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.")
2023-10-16 08:26:40 +02:00
.arg(QString::fromStdString(out_icon_path.string())),
QMessageBox::StandardButton::Ok);
out_icon_path.clear();
return false;
}
// Create icon file path
2024-03-08 10:06:48 +01:00
out_icon_path /= (program_id == 0 ? fmt::format("suyu-{}.{}", game_file_name, ico_extension)
: fmt::format("suyu-{:016X}.{}", program_id, ico_extension));
return true;
}
void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
GameListShortcutTarget target) {
2024-03-08 10:06:48 +01:00
// Get path to suyu executable
const QStringList args = QApplication::arguments();
2024-03-08 10:06:48 +01:00
std::filesystem::path suyu_command = args[0].toStdString();
// If relative path, make it an absolute path
2024-03-08 10:06:48 +01:00
if (suyu_command.c_str()[0] == '.') {
suyu_command = Common::FS::GetCurrentDir() / suyu_command;
}
// Shortcut path
std::filesystem::path shortcut_path{};
if (target == GameListShortcutTarget::Desktop) {
2023-10-15 19:44:23 +02:00
shortcut_path =
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString();
} else if (target == GameListShortcutTarget::Applications) {
2023-10-15 19:44:23 +02:00
shortcut_path =
QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
}
2024-02-23 00:37:18 +01:00
if (!std::filesystem::exists(shortcut_path)) {
GMainWindow::CreateShortcutMessagesGUI(
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
QString::fromStdString(shortcut_path.generic_string()));
LOG_ERROR(Frontend, "Invalid shortcut target {}", shortcut_path.generic_string());
return;
}
// Get title from game file
const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
system->GetContentProvider()};
const auto control = pm.GetControlMetadata();
const auto loader =
Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read));
std::string game_title = fmt::format("{:016X}", program_id);
if (control.first != nullptr) {
game_title = control.first->GetApplicationName();
} else {
loader->ReadTitle(game_title);
}
// Delete illegal characters from title
const std::string illegal_chars = "<>:\"/\\|?*.";
for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) {
if (illegal_chars.find(*it) != std::string::npos) {
game_title.erase(it.base() - 1);
}
2024-02-23 00:37:18 +01:00
}
const QString qt_game_title = QString::fromStdString(game_title);
// Get icon from game file
std::vector<u8> icon_image_file{};
if (control.second != nullptr) {
icon_image_file = control.second->ReadAllBytes();
} else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
}
QImage icon_data =
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
std::filesystem::path out_icon_path;
if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
if (!SaveIconToFile(out_icon_path, icon_data)) {
LOG_ERROR(Frontend, "Could not write icon to file");
}
}
2024-02-23 00:37:18 +01:00
#if defined(__linux__)
2023-10-16 21:01:46 +02:00
// Special case for AppImages
// Warn once if we are making a shortcut to a volatile AppImage
const std::string appimage_ending =
std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage");
2024-03-08 10:06:48 +01:00
if (suyu_command.string().ends_with(appimage_ending) &&
!UISettings::values.shortcut_already_warned) {
if (GMainWindow::CreateShortcutMessagesGUI(
2023-10-16 21:01:46 +02:00
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qt_game_title)) {
return;
}
UISettings::values.shortcut_already_warned = true;
}
#endif // __linux__
// Create shortcut
2023-11-08 22:04:30 +01:00
std::string arguments = fmt::format("-g \"{:s}\"", game_path);
if (GMainWindow::CreateShortcutMessagesGUI(
2023-10-16 21:01:46 +02:00
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) {
arguments = "-f " + arguments;
}
2024-03-08 10:06:48 +01:00
const std::string comment = fmt::format("Start {:s} with the suyu Emulator", game_title);
const std::string categories = "Game;Emulator;Qt;";
const std::string keywords = "Switch;Nintendo;";
2024-03-08 10:06:48 +01:00
if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, suyu_command,
2023-10-16 21:01:46 +02:00
arguments, categories, keywords, game_title)) {
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS,
2023-10-16 21:01:46 +02:00
qt_game_title);
return;
}
2023-10-16 21:01:46 +02:00
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
qt_game_title);
}
2019-05-03 19:21:57 +02:00
void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
std::filesystem::path fs_path;
2019-05-05 03:07:09 +02:00
if (directory == QStringLiteral("SDMC")) {
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
fs_path =
2024-03-07 10:21:59 +01:00
Common::FS::GetSuyuPath(Common::FS::SuyuPath::SDMCDir) / "Nintendo/Contents/registered";
2019-05-05 03:07:09 +02:00
} else if (directory == QStringLiteral("UserNAND")) {
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
fs_path =
2024-03-07 10:21:59 +01:00
Common::FS::GetSuyuPath(Common::FS::SuyuPath::NANDDir) / "user/Contents/registered";
2019-05-05 03:07:09 +02:00
} else if (directory == QStringLiteral("SysNAND")) {
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
fs_path =
2024-03-07 10:21:59 +01:00
Common::FS::GetSuyuPath(Common::FS::SuyuPath::NANDDir) / "system/Contents/registered";
} else {
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
fs_path = directory.toStdString();
}
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
const auto qt_path = QString::fromStdString(Common::FS::PathToUTF8String(fs_path));
if (!Common::FS::IsDir(fs_path)) {
QMessageBox::critical(this, tr("Error Opening %1").arg(qt_path),
tr("Folder does not exist!"));
return;
}
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
QDesktopServices::openUrl(QUrl::fromLocalFile(qt_path));
}
void GMainWindow::OnGameListAddDirectory() {
2019-05-03 19:21:57 +02:00
const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
if (dir_path.isEmpty()) {
return;
}
UISettings::GameDir game_dir{dir_path.toStdString(), false, true};
if (!UISettings::values.game_dirs.contains(game_dir)) {
UISettings::values.game_dirs.append(game_dir);
game_list->PopulateAsync(UISettings::values.game_dirs);
} else {
LOG_WARNING(Frontend, "Selected directory is already in the game list");
}
OnSaveConfig();
}
void GMainWindow::OnGameListShowList(bool show) {
2021-10-15 21:27:18 +02:00
if (emulation_running && ui->action_Single_Window_Mode->isChecked())
return;
game_list->setVisible(show);
game_list_placeholder->setVisible(!show);
};
void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
u64 title_id{};
const auto v_file = Core::GetGameFileFromPath(vfs, file);
2021-10-14 20:32:19 +02:00
const auto loader = Loader::GetLoader(*system, v_file);
if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
QMessageBox::information(this, tr("Properties"),
tr("The game properties could not be loaded."));
return;
}
OpenPerGameConfiguration(title_id, file);
}
void GMainWindow::OnMenuLoadFile() {
2021-11-16 00:57:41 +01:00
if (is_load_file_select_active) {
return;
}
is_load_file_select_active = true;
const QString extensions =
QStringLiteral("*.")
.append(GameList::supported_file_extensions.join(QStringLiteral(" *.")))
.append(QStringLiteral(" main"));
const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)",
"%1 is an identifier for the Switch executable file extensions.")
.arg(extensions);
const QString filename = QFileDialog::getOpenFileName(
this, tr("Load File"), QString::fromStdString(UISettings::values.roms_path), file_filter);
2021-11-16 00:57:41 +01:00
is_load_file_select_active = false;
if (filename.isEmpty()) {
return;
}
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
BootGame(filename, ApplicationAppletParameters());
2014-04-01 04:26:50 +02:00
}
2018-06-14 18:27:29 +02:00
void GMainWindow::OnMenuLoadFolder() {
const QString dir_path =
QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
2018-06-14 18:27:29 +02:00
if (dir_path.isNull()) {
return;
}
const QDir dir{dir_path};
const QStringList matching_main = dir.entryList({QStringLiteral("main")}, QDir::Files);
2018-06-14 23:25:40 +02:00
if (matching_main.size() == 1) {
BootGame(dir.path() + QDir::separator() + matching_main[0], ApplicationAppletParameters());
2018-06-14 23:25:40 +02:00
} else {
QMessageBox::warning(this, tr("Invalid Directory Selected"),
tr("The directory you have selected does not contain a 'main' file."));
2018-06-14 18:27:29 +02:00
}
}
void GMainWindow::IncrementInstallProgress() {
install_progress->setValue(install_progress->value() + 1);
}
void GMainWindow::OnMenuInstallToNAND() {
2018-08-10 05:10:32 +02:00
const QString file_filter =
2018-08-25 17:50:15 +02:00
tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive "
"(*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge "
"Image (*.xci)");
QStringList filenames = QFileDialog::getOpenFileNames(
this, tr("Install Files"), QString::fromStdString(UISettings::values.roms_path),
file_filter);
if (filenames.isEmpty()) {
return;
}
InstallDialog installDialog(this, filenames);
if (installDialog.exec() == QDialog::Rejected) {
return;
}
const QStringList files = installDialog.GetFiles();
if (files.isEmpty()) {
return;
}
2022-12-18 07:36:21 +01:00
// Save folder location of the first selected file
UISettings::values.roms_path = QFileInfo(filenames[0]).path().toStdString();
2022-12-18 07:36:21 +01:00
int remaining = filenames.size();
// This would only overflow above 2^51 bytes (2.252 PB)
int total_size = 0;
for (const QString& file : files) {
total_size += static_cast<int>(QFile(file).size() / CopyBufferSize);
}
if (total_size < 0) {
LOG_CRITICAL(Frontend, "Attempting to install too many files, aborting.");
return;
}
QStringList new_files{}; // Newly installed files that do not yet exist in the NAND
QStringList overwritten_files{}; // Files that overwrote those existing in the NAND
QStringList failed_files{}; // Files that failed to install due to errors
bool detected_base_install{}; // Whether a base game was attempted to be installed
2021-10-15 21:27:18 +02:00
ui->action_Install_File_NAND->setEnabled(false);
install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this);
install_progress->setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint);
install_progress->setAttribute(Qt::WA_DeleteOnClose, true);
install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40);
install_progress->show();
for (const QString& file : files) {
install_progress->setWindowTitle(tr("%n file(s) remaining", "", remaining));
install_progress->setLabelText(
tr("Installing file \"%1\"...").arg(QFileInfo(file).fileName()));
QFuture<ContentManager::InstallResult> future;
ContentManager::InstallResult result;
if (file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) {
const auto progress_callback = [this](size_t size, size_t progress) {
emit UpdateInstallProgress();
if (install_progress->wasCanceled()) {
return true;
}
return false;
};
future = QtConcurrent::run([this, &file, progress_callback] {
return ContentManager::InstallNSP(*system, *vfs, file.toStdString(),
progress_callback);
});
while (!future.isFinished()) {
QCoreApplication::processEvents();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
result = future.result();
} else {
result = InstallNCA(file);
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
switch (result) {
case ContentManager::InstallResult::Success:
new_files.append(QFileInfo(file).fileName());
break;
case ContentManager::InstallResult::Overwrite:
overwritten_files.append(QFileInfo(file).fileName());
break;
case ContentManager::InstallResult::Failure:
failed_files.append(QFileInfo(file).fileName());
break;
case ContentManager::InstallResult::BaseInstallAttempted:
failed_files.append(QFileInfo(file).fileName());
detected_base_install = true;
break;
}
--remaining;
}
install_progress->close();
if (detected_base_install) {
QMessageBox::warning(
this, tr("Install Results"),
tr("To avoid possible conflicts, we discourage users from installing base games to the "
"NAND.\nPlease, only use this feature to install updates and DLC."));
}
const QString install_results =
(new_files.isEmpty() ? QString{}
: tr("%n file(s) were newly installed\n", "", new_files.size())) +
(overwritten_files.isEmpty()
? QString{}
: tr("%n file(s) were overwritten\n", "", overwritten_files.size())) +
(failed_files.isEmpty() ? QString{}
: tr("%n file(s) failed to install\n", "", failed_files.size()));
QMessageBox::information(this, tr("Install Results"), install_results);
2024-03-07 10:21:59 +01:00
Common::FS::RemoveDirRecursively(Common::FS::GetSuyuPath(Common::FS::SuyuPath::CacheDir) /
"game_list");
game_list->PopulateAsync(UISettings::values.game_dirs);
2021-10-15 21:27:18 +02:00
ui->action_Install_File_NAND->setEnabled(true);
}
ContentManager::InstallResult GMainWindow::InstallNCA(const QString& filename) {
const QStringList tt_options{tr("System Application"),
tr("System Archive"),
tr("System Application Update"),
tr("Firmware Package (Type A)"),
tr("Firmware Package (Type B)"),
tr("Game"),
tr("Game Update"),
tr("Game DLC"),
tr("Delta Title")};
bool ok;
const auto item = QInputDialog::getItem(
this, tr("Select NCA Install Type..."),
tr("Please select the type of title you would like to install this NCA as:\n(In "
"most instances, the default 'Game' is fine.)"),
tt_options, 5, false, &ok);
auto index = tt_options.indexOf(item);
if (!ok || index == -1) {
QMessageBox::warning(this, tr("Failed to Install"),
tr("The title type you selected for the NCA is invalid."));
return ContentManager::InstallResult::Failure;
}
// If index is equal to or past Game, add the jump in TitleType.
if (index >= 5) {
index += static_cast<size_t>(FileSys::TitleType::Application) -
static_cast<size_t>(FileSys::TitleType::FirmwarePackageB);
}
const bool is_application = index >= static_cast<s32>(FileSys::TitleType::Application);
2021-10-14 20:32:19 +02:00
const auto& fs_controller = system->GetFileSystemController();
auto* registered_cache = is_application ? fs_controller.GetUserNANDContents()
: fs_controller.GetSystemNANDContents();
const auto progress_callback = [this](size_t size, size_t progress) {
emit UpdateInstallProgress();
if (install_progress->wasCanceled()) {
return true;
}
return false;
};
return ContentManager::InstallNCA(*vfs, filename.toStdString(), *registered_cache,
static_cast<FileSys::TitleType>(index), progress_callback);
}
void GMainWindow::OnMenuRecentFile() {
QAction* action = qobject_cast<QAction*>(sender());
assert(action);
const QString filename = action->data().toString();
if (QFileInfo::exists(filename)) {
BootGame(filename, ApplicationAppletParameters());
} else {
// Display an error message and remove the file from the list.
QMessageBox::information(this, tr("File not found"),
tr("File \"%1\" not found").arg(filename));
UISettings::values.recent_files.removeOne(filename);
UpdateRecentFiles();
}
}
void GMainWindow::OnStartGame() {
PreventOSSleep();
emu_thread->SetRunning(true);
UpdateMenuState();
OnTasStateChanged();
2018-09-16 20:05:51 +02:00
2023-08-27 03:19:00 +02:00
play_time_manager->SetProgramId(system->GetApplicationProcessProgramID());
play_time_manager->Start();
2018-09-16 20:05:51 +02:00
discord_rpc->Update();
#ifdef __unix__
Common::Linux::StartGamemode();
#endif
}
void GMainWindow::OnRestartGame() {
if (!system->IsPoweredOn()) {
return;
}
if (ConfirmShutdownGame()) {
// Make a copy since ShutdownGame edits game_path
const auto current_game = QString(current_game_path);
ShutdownGame();
BootGame(current_game, ApplicationAppletParameters());
}
2014-04-01 04:26:50 +02:00
}
void GMainWindow::OnPauseGame() {
emu_thread->SetRunning(false);
2023-08-27 03:19:00 +02:00
play_time_manager->Stop();
UpdateMenuState();
AllowOSSleep();
#ifdef __unix__
Common::Linux::StopGamemode();
#endif
2014-04-01 04:26:50 +02:00
}
void GMainWindow::OnPauseContinueGame() {
if (emulation_running) {
if (emu_thread->IsRunning()) {
OnPauseGame();
} else {
OnStartGame();
}
}
}
void GMainWindow::OnStopGame() {
if (ConfirmShutdownGame()) {
play_time_manager->Stop();
// Update game list to show new play time
game_list->PopulateAsync(UISettings::values.game_dirs);
if (OnShutdownBegin()) {
OnShutdownBeginDialog();
} else {
OnEmulationStopped();
}
}
}
bool GMainWindow::ConfirmShutdownGame() {
if (UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Always) {
if (system->GetExitLocked()) {
if (!ConfirmForceLockedExit()) {
return false;
}
} else {
if (!ConfirmChangeGame()) {
return false;
}
}
} else {
if (UISettings::values.confirm_before_stopping.GetValue() ==
ConfirmStop::Ask_Based_On_Game &&
system->GetExitLocked()) {
if (!ConfirmForceLockedExit()) {
return false;
}
}
2022-12-24 23:31:34 +01:00
}
return true;
2014-04-01 04:26:50 +02:00
}
void GMainWindow::OnLoadComplete() {
loading_screen->OnLoadComplete();
}
void GMainWindow::OnExecuteProgram(std::size_t program_index) {
ShutdownGame();
auto params = ApplicationAppletParameters();
params.program_index = static_cast<s32>(program_index);
params.launch_type = Service::AM::LaunchType::ApplicationInitiated;
BootGame(last_filename_booted, params);
}
void GMainWindow::OnExit() {
ShutdownGame();
}
2022-09-09 22:29:22 +02:00
void GMainWindow::OnSaveConfig() {
system->ApplySettings();
config->SaveAllValues();
2022-09-09 22:29:22 +02:00
}
void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
2023-03-25 18:29:08 +01:00
error_applet = new OverlayDialog(render_window, *system, error_code, error_text, QString{},
tr("OK"), Qt::AlignLeft | Qt::AlignVCenter);
SCOPE_EXIT {
2023-03-25 18:29:08 +01:00
error_applet->deleteLater();
error_applet = nullptr;
};
2023-03-25 18:29:08 +01:00
error_applet->exec();
emit ErrorDisplayFinished();
}
2023-03-25 18:29:08 +01:00
void GMainWindow::ErrorDisplayRequestExit() {
if (error_applet) {
error_applet->reject();
}
}
2018-09-16 20:05:51 +02:00
void GMainWindow::OnMenuReportCompatibility() {
2022-11-21 17:31:18 +01:00
#if defined(ARCHITECTURE_x86_64) && !defined(__APPLE__)
const auto& caps = Common::GetCPUCaps();
const bool has_fma = caps.fma || caps.fma4;
const auto processor_count = std::thread::hardware_concurrency();
const bool has_4threads = processor_count == 0 || processor_count >= 4;
const bool has_8gb_ram = Common::GetMemInfo().TotalPhysicalMemory >= 8_GiB;
const bool has_broken_vulkan = UISettings::values.has_broken_vulkan;
if (!has_fma || !has_4threads || !has_8gb_ram || has_broken_vulkan) {
QMessageBox::critical(this, tr("Hardware requirements not met"),
tr("Your system does not meet the recommended hardware requirements. "
"Compatibility reporting has been disabled."));
return;
}
2024-03-08 10:06:48 +01:00
if (!Settings::values.suyu_token.GetValue().empty() &&
!Settings::values.suyu_username.GetValue().empty()) {
2024-03-13 19:11:09 +01:00
CompatDB compatdb{this};
2018-09-16 20:05:51 +02:00
compatdb.exec();
} else {
QMessageBox::critical(
2024-03-08 10:06:48 +01:00
this, tr("Missing suyu Account"),
tr("In order to submit a game compatibility test case, you must link your suyu "
"account.<br><br/>To link your suyu account, go to Emulation &gt; Configuration "
2018-09-16 20:05:51 +02:00
"&gt; "
"Web."));
}
2022-11-21 17:31:18 +01:00
#else
QMessageBox::critical(this, tr("Hardware requirements not met"),
tr("Your system does not meet the recommended hardware requirements. "
"Compatibility reporting has been disabled."));
#endif
2018-09-16 20:05:51 +02:00
}
2020-06-25 23:32:43 +02:00
void GMainWindow::OpenURL(const QUrl& url) {
const bool open = QDesktopServices::openUrl(url);
if (!open) {
QMessageBox::warning(this, tr("Error opening URL"),
2020-06-25 23:31:01 +02:00
tr("Unable to open the URL \"%1\".").arg(url.toString()));
}
}
void GMainWindow::OnOpenModsPage() {
2024-03-08 10:06:48 +01:00
OpenURL(QUrl(QStringLiteral("https://gitlab.com/suyu-emu/suyu/wiki/Switch-Mods")));
}
2020-06-26 18:50:28 +02:00
void GMainWindow::OnOpenQuickstartGuide() {
2024-03-08 10:06:48 +01:00
OpenURL(QUrl(QStringLiteral("https://suyu.dev/help/quickstart/")));
}
2020-06-27 02:14:29 +02:00
void GMainWindow::OnOpenFAQ() {
2024-03-08 10:06:48 +01:00
OpenURL(QUrl(QStringLiteral("https://suyu.dev/wiki/faq/")));
}
void GMainWindow::ToggleFullscreen() {
if (!emulation_running) {
return;
}
2021-10-15 21:27:18 +02:00
if (ui->action_Fullscreen->isChecked()) {
ShowFullscreen();
} else {
HideFullscreen();
}
}
// We're going to return the screen that the given window has the most pixels on
static QScreen* GuessCurrentScreen(QWidget* window) {
const QList<QScreen*> screens = QGuiApplication::screens();
return *std::max_element(
screens.cbegin(), screens.cend(), [window](const QScreen* left, const QScreen* right) {
const QSize left_size = left->geometry().intersected(window->geometry()).size();
const QSize right_size = right->geometry().intersected(window->geometry()).size();
return (left_size.height() * left_size.width()) <
(right_size.height() * right_size.width());
});
}
bool GMainWindow::UsingExclusiveFullscreen() {
return Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive ||
2022-12-16 14:47:22 +01:00
QGuiApplication::platformName() == QStringLiteral("wayland") ||
QGuiApplication::platformName() == QStringLiteral("wayland-egl");
}
void GMainWindow::ShowFullscreen() {
const auto show_fullscreen = [this](QWidget* window) {
if (UsingExclusiveFullscreen()) {
window->showFullScreen();
return;
}
window->hide();
window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint);
const auto screen_geometry = GuessCurrentScreen(window)->geometry();
window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(),
screen_geometry.height() + 1);
window->raise();
window->showNormal();
};
2021-10-15 21:27:18 +02:00
if (ui->action_Single_Window_Mode->isChecked()) {
UISettings::values.geometry = saveGeometry();
2021-10-15 21:27:18 +02:00
ui->menubar->hide();
statusBar()->hide();
show_fullscreen(this);
} else {
UISettings::values.renderwindow_geometry = render_window->saveGeometry();
show_fullscreen(render_window);
}
}
void GMainWindow::HideFullscreen() {
2021-10-15 21:27:18 +02:00
if (ui->action_Single_Window_Mode->isChecked()) {
if (UsingExclusiveFullscreen()) {
showNormal();
restoreGeometry(UISettings::values.geometry);
} else {
hide();
setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
restoreGeometry(UISettings::values.geometry);
raise();
show();
}
2021-10-15 21:27:18 +02:00
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
ui->menubar->show();
} else {
if (UsingExclusiveFullscreen()) {
render_window->showNormal();
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
} else {
render_window->hide();
render_window->setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
render_window->raise();
render_window->show();
}
}
}
void GMainWindow::ToggleWindowMode() {
2021-10-15 21:27:18 +02:00
if (ui->action_Single_Window_Mode->isChecked()) {
// Render in the main window...
2014-04-22 05:15:17 +02:00
render_window->BackupGeometry();
2021-10-15 21:27:18 +02:00
ui->horizontalLayout->addWidget(render_window);
render_window->setFocusPolicy(Qt::StrongFocus);
if (emulation_running) {
render_window->setVisible(true);
render_window->setFocus();
game_list->hide();
}
} else {
// Render in a separate window...
2021-10-15 21:27:18 +02:00
ui->horizontalLayout->removeWidget(render_window);
render_window->setParent(nullptr);
render_window->setFocusPolicy(Qt::NoFocus);
if (emulation_running) {
render_window->setVisible(true);
render_window->RestoreGeometry();
2015-09-01 06:35:33 +02:00
game_list->show();
}
2014-04-01 04:26:50 +02:00
}
}
void GMainWindow::ResetWindowSize(u32 width, u32 height) {
const auto aspect_ratio = Layout::EmulationAspectRatio(
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
static_cast<Layout::AspectRatio>(Settings::values.aspect_ratio.GetValue()),
static_cast<float>(height) / width);
2021-10-15 21:27:18 +02:00
if (!ui->action_Single_Window_Mode->isChecked()) {
render_window->resize(height / aspect_ratio, height);
} else {
2021-10-15 21:27:18 +02:00
const bool show_status_bar = ui->action_Show_Status_Bar->isChecked();
const auto status_bar_height = show_status_bar ? statusBar()->height() : 0;
resize(height / aspect_ratio, height + menuBar()->height() + status_bar_height);
}
}
void GMainWindow::ResetWindowSize720() {
ResetWindowSize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
}
void GMainWindow::ResetWindowSize900() {
ResetWindowSize(1600U, 900U);
}
void GMainWindow::ResetWindowSize1080() {
ResetWindowSize(Layout::ScreenDocked::Width, Layout::ScreenDocked::Height);
}
void GMainWindow::OnConfigure() {
const QString old_theme = UISettings::values.theme;
DarkModeState old_dark_mode_state = UISettings::values.dark_mode_state;
const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue();
const auto old_language_index = Settings::values.language_index.GetValue();
#ifdef __unix__
const bool old_gamemode = Settings::values.enable_gamemode.GetValue();
#endif
Settings::SetConfiguringGlobal(true);
ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(),
vk_device_records, *system,
!multiplayer_state->IsHostingPublicRoom());
connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
&GMainWindow::OnLanguageChanged);
const auto result = configure_dialog.exec();
if (result != QDialog::Accepted && !UISettings::values.configuration_applied &&
!UISettings::values.reset_to_defaults) {
// Runs if the user hit Cancel or closed the window, and did not ever press the Apply button
// or `Reset to Defaults` button
return;
} else if (result == QDialog::Accepted) {
// Only apply new changes if user hit Okay
// This is here to avoid applying changes if the user hit Apply, made some changes, then hit
// Cancel
configure_dialog.ApplyConfiguration();
} else if (UISettings::values.reset_to_defaults) {
LOG_INFO(Frontend, "Resetting all settings to defaults");
if (!Common::FS::RemoveFile(config->GetConfigFilePath())) {
LOG_WARNING(Frontend, "Failed to remove configuration file");
}
if (!Common::FS::RemoveDirContentsRecursively(
2024-03-07 10:21:59 +01:00
Common::FS::GetSuyuPath(Common::FS::SuyuPath::ConfigDir) / "custom")) {
LOG_WARNING(Frontend, "Failed to remove custom configuration files");
}
if (!Common::FS::RemoveDirRecursively(
2024-03-07 10:21:59 +01:00
Common::FS::GetSuyuPath(Common::FS::SuyuPath::CacheDir) / "game_list")) {
LOG_WARNING(Frontend, "Failed to remove game metadata cache files");
}
// Explicitly save the game directories, since reinitializing config does not explicitly do
// so.
QVector<UISettings::GameDir> old_game_dirs = std::move(UISettings::values.game_dirs);
QVector<u64> old_favorited_ids = std::move(UISettings::values.favorited_ids);
Settings::values.disabled_addons.clear();
config = std::make_unique<QtConfig>();
UISettings::values.reset_to_defaults = false;
UISettings::values.game_dirs = std::move(old_game_dirs);
UISettings::values.favorited_ids = std::move(old_favorited_ids);
InitializeRecentFileMenuActions();
SetDefaultUIGeometry();
RestoreUIState();
}
InitializeHotkeys();
if (UISettings::values.theme != old_theme ||
UISettings::values.dark_mode_state != old_dark_mode_state) {
UpdateUITheme();
}
if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) {
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
}
#ifdef __unix__
if (Settings::values.enable_gamemode.GetValue() != old_gamemode) {
SetGamemodeEnabled(Settings::values.enable_gamemode.GetValue());
}
#endif
if (!multiplayer_state->IsHostingPublicRoom()) {
multiplayer_state->UpdateCredentials();
}
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
if (reload || Settings::values.language_index.GetValue() != old_language_index) {
game_list->PopulateAsync(UISettings::values.game_dirs);
2016-01-24 18:34:05 +01:00
}
UISettings::values.configuration_applied = false;
config->SaveAllValues();
2021-02-03 19:34:25 +01:00
if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) {
render_window->installEventFilter(render_window);
render_window->setAttribute(Qt::WA_Hover, true);
} else {
render_window->removeEventFilter(render_window);
render_window->setAttribute(Qt::WA_Hover, false);
}
2021-02-24 03:39:02 +01:00
if (UISettings::values.hide_mouse) {
mouse_hide_timer.start();
}
2022-06-19 06:34:28 +02:00
// Restart camera config
if (emulation_running) {
render_window->FinalizeCamera();
render_window->InitializeCamera();
}
if (!UISettings::values.has_broken_vulkan) {
renderer_status_button->setEnabled(!emulation_running);
}
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
UpdateStatusButtons();
controller_dialog->refreshConfiguration();
system->ApplySettings();
2014-04-01 04:26:50 +02:00
}
void GMainWindow::OnConfigureTas() {
ConfigureTasDialog dialog(this);
const auto result = dialog.exec();
if (result != QDialog::Accepted && !UISettings::values.configuration_applied) {
2021-10-14 20:32:19 +02:00
Settings::RestoreGlobalState(system->IsPoweredOn());
return;
} else if (result == QDialog::Accepted) {
dialog.ApplyConfiguration();
OnSaveConfig();
}
}
void GMainWindow::OnTasStartStop() {
if (!emulation_running) {
return;
}
2021-11-16 00:57:41 +01:00
// Disable system buttons to prevent TAS from executing a hotkey
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
controller->ResetSystemButtons();
input_subsystem->GetTas()->StartStop();
OnTasStateChanged();
}
void GMainWindow::OnTasRecord() {
if (!emulation_running) {
return;
}
2021-11-16 00:57:41 +01:00
if (is_tas_recording_dialog_active) {
return;
}
// Disable system buttons to prevent TAS from recording a hotkey
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
controller->ResetSystemButtons();
2021-11-22 03:07:37 +01:00
const bool is_recording = input_subsystem->GetTas()->Record();
if (!is_recording) {
2021-11-16 00:57:41 +01:00
is_tas_recording_dialog_active = true;
bool answer = question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
input_subsystem->GetTas()->SaveRecording(answer);
2021-11-16 00:57:41 +01:00
is_tas_recording_dialog_active = false;
}
OnTasStateChanged();
}
void GMainWindow::OnTasReset() {
input_subsystem->GetTas()->Reset();
}
void GMainWindow::OnToggleDockedMode() {
const bool is_docked = Settings::IsDockedMode();
auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (!is_docked && handheld->IsConnected()) {
QMessageBox::warning(this, tr("Invalid config detected"),
tr("Handheld controller can't be used on docked mode. Pro "
"controller will be selected."));
handheld->Disconnect();
player_1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
player_1->Connect();
controller_dialog->refreshConfiguration();
}
Settings::values.use_docked_mode.SetValue(is_docked ? Settings::ConsoleMode::Handheld
: Settings::ConsoleMode::Docked);
UpdateDockedButton();
OnDockedModeChanged(is_docked, !is_docked, *system);
}
void GMainWindow::OnToggleGpuAccuracy() {
switch (Settings::values.gpu_accuracy.GetValue()) {
case Settings::GpuAccuracy::High: {
Settings::values.gpu_accuracy.SetValue(Settings::GpuAccuracy::Normal);
break;
}
case Settings::GpuAccuracy::Normal:
case Settings::GpuAccuracy::Extreme:
default: {
Settings::values.gpu_accuracy.SetValue(Settings::GpuAccuracy::High);
break;
}
}
system->ApplySettings();
UpdateGPUAccuracyButton();
}
void GMainWindow::OnMute() {
Settings::values.audio_muted = !Settings::values.audio_muted;
UpdateVolumeUI();
}
void GMainWindow::OnDecreaseVolume() {
Settings::values.audio_muted = false;
const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue());
int step = 5;
if (current_volume <= 30) {
step = 2;
}
if (current_volume <= 6) {
step = 1;
}
Settings::values.volume.SetValue(std::max(current_volume - step, 0));
UpdateVolumeUI();
}
void GMainWindow::OnIncreaseVolume() {
Settings::values.audio_muted = false;
const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue());
int step = 5;
if (current_volume < 30) {
step = 2;
}
if (current_volume < 6) {
step = 1;
}
Settings::values.volume.SetValue(current_volume + step);
UpdateVolumeUI();
}
void GMainWindow::OnToggleAdaptingFilter() {
auto filter = Settings::values.scaling_filter.GetValue();
filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
if (filter == Settings::ScalingFilter::MaxEnum) {
filter = Settings::ScalingFilter::NearestNeighbor;
}
Settings::values.scaling_filter.SetValue(filter);
filter_status_button->setChecked(true);
UpdateFilterText();
}
2022-11-28 02:37:37 +01:00
void GMainWindow::OnToggleGraphicsAPI() {
auto api = Settings::values.renderer_backend.GetValue();
if (api != Settings::RendererBackend::Vulkan) {
2022-11-28 02:37:37 +01:00
api = Settings::RendererBackend::Vulkan;
} else {
#ifdef HAS_OPENGL
2022-11-28 02:37:37 +01:00
api = Settings::RendererBackend::OpenGL;
#else
api = Settings::RendererBackend::Null;
#endif
2022-11-28 02:37:37 +01:00
}
Settings::values.renderer_backend.SetValue(api);
renderer_status_button->setChecked(api == Settings::RendererBackend::Vulkan);
UpdateAPIText();
}
void GMainWindow::OnConfigurePerGame() {
const u64 title_id = system->GetApplicationProcessProgramID();
2022-05-27 01:57:35 +02:00
OpenPerGameConfiguration(title_id, current_game_path.toStdString());
}
void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) {
const auto v_file = Core::GetGameFileFromPath(vfs, file_name);
Settings::SetConfiguringGlobal(false);
ConfigurePerGame dialog(this, title_id, file_name, vk_device_records, *system);
dialog.LoadFromFile(v_file);
const auto result = dialog.exec();
if (result != QDialog::Accepted && !UISettings::values.configuration_applied) {
2021-10-14 20:32:19 +02:00
Settings::RestoreGlobalState(system->IsPoweredOn());
return;
} else if (result == QDialog::Accepted) {
dialog.ApplyConfiguration();
}
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
if (reload) {
game_list->PopulateAsync(UISettings::values.game_dirs);
}
// Do not cause the global config to write local settings into the config file
2021-10-14 20:32:19 +02:00
const bool is_powered_on = system->IsPoweredOn();
Settings::RestoreGlobalState(is_powered_on);
system->HIDCore().ReloadInputDevices();
UISettings::values.configuration_applied = false;
if (!is_powered_on) {
config->SaveAllValues();
}
}
void GMainWindow::OnLoadAmiibo() {
if (emu_thread == nullptr || !emu_thread->IsRunning()) {
return;
}
2021-11-16 00:57:41 +01:00
if (is_amiibo_file_select_active) {
return;
}
auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
// Remove amiibo if one is connected
if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {
virtual_amiibo->CloseAmiibo();
2022-02-20 08:58:34 +01:00
QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
return;
}
if (virtual_amiibo->GetCurrentState() != InputCommon::VirtualAmiibo::State::WaitingForAmiibo) {
QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos"));
return;
}
2021-11-16 00:57:41 +01:00
is_amiibo_file_select_active = true;
const QString extensions{QStringLiteral("*.bin")};
const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), {}, file_filter);
2021-11-16 00:57:41 +01:00
is_amiibo_file_select_active = false;
if (filename.isEmpty()) {
return;
}
LoadAmiibo(filename);
}
bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton) {
QMessageBox* box_dialog = new QMessageBox(parent);
box_dialog->setWindowTitle(title);
box_dialog->setText(text);
box_dialog->setStandardButtons(buttons);
box_dialog->setDefaultButton(defaultButton);
ControllerNavigation* controller_navigation =
new ControllerNavigation(system->HIDCore(), box_dialog);
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
[box_dialog](Qt::Key key) {
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
QCoreApplication::postEvent(box_dialog, event);
});
int res = box_dialog->exec();
controller_navigation->UnloadController();
return res == QMessageBox::Yes;
}
void GMainWindow::LoadAmiibo(const QString& filename) {
auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
const QString title = tr("Error loading Amiibo data");
2022-02-20 08:58:34 +01:00
// Remove amiibo if one is connected
if (virtual_amiibo->GetCurrentState() == InputCommon::VirtualAmiibo::State::TagNearby) {
virtual_amiibo->CloseAmiibo();
2022-02-20 08:58:34 +01:00
QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed"));
return;
}
switch (virtual_amiibo->LoadAmiibo(filename.toStdString())) {
case InputCommon::VirtualAmiibo::Info::NotAnAmiibo:
QMessageBox::warning(this, title, tr("The selected file is not a valid amiibo"));
break;
case InputCommon::VirtualAmiibo::Info::UnableToLoad:
QMessageBox::warning(this, title, tr("The selected file is already on use"));
break;
case InputCommon::VirtualAmiibo::Info::WrongDeviceState:
QMessageBox::warning(this, title, tr("The current game is not looking for amiibos"));
break;
case InputCommon::VirtualAmiibo::Info::Unknown:
QMessageBox::warning(this, title, tr("An unknown error occurred"));
break;
default:
break;
}
}
2024-03-08 10:06:48 +01:00
void GMainWindow::OnOpenSuyuFolder() {
QDesktopServices::openUrl(QUrl::fromLocalFile(
2024-03-07 10:21:59 +01:00
QString::fromStdString(Common::FS::GetSuyuPathString(Common::FS::SuyuPath::SuyuDir))));
}
void GMainWindow::OnVerifyInstalledContents() {
// Initialize a progress dialog.
QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
// Declare progress callback.
auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
progress.setValue(static_cast<int>((processed_size * 100) / total_size));
return progress.wasCanceled();
};
const std::vector<std::string> result =
ContentManager::VerifyInstalledContents(*system, *provider, QtProgressCallback);
progress.close();
if (result.empty()) {
QMessageBox::information(this, tr("Integrity verification succeeded!"),
tr("The operation completed successfully."));
} else {
const auto failed_names =
QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
QMessageBox::critical(
this, tr("Integrity verification failed!"),
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
}
}
void GMainWindow::OnInstallFirmware() {
// Don't do this while emulation is running, that'd probably be a bad idea.
if (emu_thread != nullptr && emu_thread->IsRunning()) {
return;
}
// Check for installed keys, error out, suggest restart?
if (!ContentManager::AreKeysPresent()) {
2024-02-17 21:41:24 +01:00
QMessageBox::information(
this, tr("Keys not installed"),
2024-03-08 10:06:48 +01:00
tr("Install decryption keys and restart suyu before attempting to install firmware."));
return;
}
const QString firmware_source_location = QFileDialog::getExistingDirectory(
this, tr("Select Dumped Firmware Source Location"), {}, QFileDialog::ShowDirsOnly);
if (firmware_source_location.isEmpty()) {
return;
}
QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
progress.show();
// Declare progress callback.
auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
progress.setValue(static_cast<int>((processed_size * 100) / total_size));
return progress.wasCanceled();
};
LOG_INFO(Frontend, "Installing firmware from {}", firmware_source_location.toStdString());
2024-02-17 21:58:41 +01:00
// Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in
2024-02-17 21:41:24 +01:00
// there.)
std::filesystem::path firmware_source_path = firmware_source_location.toStdString();
if (!Common::FS::IsDir(firmware_source_path)) {
progress.close();
return;
}
std::vector<std::filesystem::path> out;
2024-02-17 21:41:24 +01:00
const Common::FS::DirEntryCallable callback =
[&out](const std::filesystem::directory_entry& entry) {
if (entry.path().has_extension() && entry.path().extension() == ".nca") {
2024-02-17 21:41:24 +01:00
out.emplace_back(entry.path());
}
2024-02-17 21:41:24 +01:00
return true;
};
QtProgressCallback(100, 10);
Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File);
if (out.size() <= 0) {
progress.close();
QMessageBox::warning(this, tr("Firmware install failed"),
tr("Unable to locate potential firmware NCA files"));
return;
}
// Locate and erase the content of nand/system/Content/registered/*.nca, if any.
auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory();
if (!sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) {
progress.close();
2024-02-17 21:41:24 +01:00
QMessageBox::critical(this, tr("Firmware install failed"),
tr("Failed to delete one or more firmware file."));
return;
}
LOG_INFO(Frontend,
"Cleaned nand/system/Content/registered folder in preparation for new firmware.");
QtProgressCallback(100, 20);
auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered");
bool success = true;
int i = 0;
for (const auto& firmware_src_path : out) {
i++;
auto firmware_src_vfile =
vfs->OpenFile(firmware_src_path.generic_string(), FileSys::OpenMode::Read);
2024-02-17 21:41:24 +01:00
auto firmware_dst_vfile =
firmware_vdir->CreateFileRelative(firmware_src_path.filename().string());
if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
LOG_ERROR(Frontend, "Failed to copy firmware file {} to {} in registered folder!",
firmware_src_path.generic_string(), firmware_src_path.filename().string());
success = false;
}
if (QtProgressCallback(
100, 20 + static_cast<int>(((i) / static_cast<float>(out.size())) * 70.0))) {
progress.close();
QMessageBox::warning(
this, tr("Firmware install failed"),
tr("Firmware installation cancelled, firmware may be in bad state, "
2024-03-08 10:06:48 +01:00
"restart suyu or re-install firmware."));
return;
}
}
if (!success) {
progress.close();
QMessageBox::critical(this, tr("Firmware install failed"),
tr("One or more firmware files failed to copy into NAND."));
return;
}
// Re-scan VFS for the newly placed firmware files.
system->GetFileSystemController().CreateFactories(*vfs);
auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) {
progress.setValue(90 + static_cast<int>((processed_size * 10) / total_size));
return progress.wasCanceled();
};
auto result =
ContentManager::VerifyInstalledContents(*system, *provider, VerifyFirmwareCallback, true);
if (result.size() > 0) {
const auto failed_names =
QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
progress.close();
QMessageBox::critical(
this, tr("Firmware integrity verification failed!"),
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
return;
}
progress.close();
OnCheckFirmwareDecryption();
}
void GMainWindow::OnInstallDecryptionKeys() {
// Don't do this while emulation is running.
if (emu_thread != nullptr && emu_thread->IsRunning()) {
return;
}
const QString key_source_location = QFileDialog::getOpenFileName(
this, tr("Select Dumped Keys Location"), {}, QStringLiteral("prod.keys (prod.keys)"), {},
QFileDialog::ReadOnly);
if (key_source_location.isEmpty()) {
return;
}
// Verify that it contains prod.keys, title.keys and optionally, key_retail.bin
LOG_INFO(Frontend, "Installing key files from {}", key_source_location.toStdString());
const std::filesystem::path prod_key_path = key_source_location.toStdString();
const std::filesystem::path key_source_path = prod_key_path.parent_path();
if (!Common::FS::IsDir(key_source_path)) {
return;
}
bool prod_keys_found = false;
std::vector<std::filesystem::path> source_key_files;
if (Common::FS::Exists(prod_key_path)) {
prod_keys_found = true;
source_key_files.emplace_back(prod_key_path);
}
if (Common::FS::Exists(key_source_path / "title.keys")) {
source_key_files.emplace_back(key_source_path / "title.keys");
}
if (Common::FS::Exists(key_source_path / "key_retail.bin")) {
source_key_files.emplace_back(key_source_path / "key_retail.bin");
}
// There should be at least prod.keys.
if (source_key_files.empty() || !prod_keys_found) {
QMessageBox::warning(this, tr("Decryption Keys install failed"),
tr("prod.keys is a required decryption key file."));
return;
}
2024-03-08 10:06:48 +01:00
const auto suyu_keys_dir = Common::FS::GetSuyuPath(Common::FS::SuyuPath::KeysDir);
for (auto key_file : source_key_files) {
2024-03-08 10:06:48 +01:00
std::filesystem::path destination_key_file = suyu_keys_dir / key_file.filename();
if (!std::filesystem::copy_file(key_file, destination_key_file,
std::filesystem::copy_options::overwrite_existing)) {
LOG_ERROR(Frontend, "Failed to copy file {} to {}", key_file.string(),
destination_key_file.string());
QMessageBox::critical(this, tr("Decryption Keys install failed"),
tr("One or more keys failed to copy."));
return;
}
}
// Reinitialize the key manager, re-read the vfs (for update/dlc files),
// and re-populate the game list in the UI if the user has already added
// game folders.
Core::Crypto::KeyManager::Instance().ReloadKeys();
system->GetFileSystemController().CreateFactories(*vfs);
game_list->PopulateAsync(UISettings::values.game_dirs);
if (ContentManager::AreKeysPresent()) {
QMessageBox::information(this, tr("Decryption Keys install succeeded"),
tr("Decryption Keys were successfully installed"));
} else {
QMessageBox::critical(
this, tr("Decryption Keys install failed"),
tr("Decryption Keys failed to initialize. Check that your dumping tools are "
"up to date and re-dump keys."));
}
OnCheckFirmwareDecryption();
}
2018-01-14 19:15:45 +01:00
void GMainWindow::OnAbout() {
AboutDialog aboutDialog(this);
aboutDialog.exec();
}
void GMainWindow::OnToggleFilterBar() {
2021-10-15 21:27:18 +02:00
game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked());
if (ui->action_Show_Filter_Bar->isChecked()) {
game_list->SetFilterFocus();
} else {
game_list->ClearFilter();
}
}
2021-11-16 00:57:41 +01:00
void GMainWindow::OnToggleStatusBar() {
statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked());
}
void GMainWindow::OnToggleFoldersInList() {
UISettings::values.show_folders_in_list = ui->action_Show_Folders_In_List->isChecked();
game_list->ClearList();
game_list->LoadCompatibilityList();
game_list->PopulateAsync(UISettings::values.game_dirs);
}
void GMainWindow::OnAlbum() {
constexpr u64 AlbumId = static_cast<u64>(Service::AM::AppletProgramId::PhotoViewer);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Album applet."));
return;
}
auto album_nca = bis_system->GetEntry(AlbumId, FileSys::ContentRecordType::Program);
if (!album_nca) {
QMessageBox::warning(this, tr("Album Applet"),
tr("Album applet is not available. Please reinstall firmware."));
return;
}
system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::PhotoViewer);
const auto filename = QString::fromStdString(album_nca->GetFullPath());
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
BootGame(filename, LibraryAppletParameters(AlbumId, Service::AM::AppletId::PhotoViewer));
}
void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
constexpr u64 CabinetId = static_cast<u64>(Service::AM::AppletProgramId::Cabinet);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Cabinet applet."));
return;
}
auto cabinet_nca = bis_system->GetEntry(CabinetId, FileSys::ContentRecordType::Program);
if (!cabinet_nca) {
QMessageBox::warning(this, tr("Cabinet Applet"),
tr("Cabinet applet is not available. Please reinstall firmware."));
return;
}
system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Cabinet);
system->GetFrontendAppletHolder().SetCabinetMode(mode);
const auto filename = QString::fromStdString(cabinet_nca->GetFullPath());
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
BootGame(filename, LibraryAppletParameters(CabinetId, Service::AM::AppletId::Cabinet));
}
void GMainWindow::OnMiiEdit() {
constexpr u64 MiiEditId = static_cast<u64>(Service::AM::AppletProgramId::MiiEdit);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Mii editor."));
return;
}
auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program);
if (!mii_applet_nca) {
QMessageBox::warning(this, tr("Mii Edit Applet"),
tr("Mii editor is not available. Please reinstall firmware."));
return;
}
system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::MiiEdit);
const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath()));
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
BootGame(filename, LibraryAppletParameters(MiiEditId, Service::AM::AppletId::MiiEdit));
}
void GMainWindow::OnOpenControllerMenu() {
constexpr u64 ControllerAppletId = static_cast<u64>(Service::AM::AppletProgramId::Controller);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Controller Menu."));
return;
}
auto controller_applet_nca =
bis_system->GetEntry(ControllerAppletId, FileSys::ContentRecordType::Program);
if (!controller_applet_nca) {
QMessageBox::warning(this, tr("Controller Applet"),
tr("Controller Menu is not available. Please reinstall firmware."));
return;
}
system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Controller);
const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath()));
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
BootGame(filename,
LibraryAppletParameters(ControllerAppletId, Service::AM::AppletId::Controller));
}
void GMainWindow::OnHomeMenu() {
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Home Menu."));
return;
}
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
if (!qlaunch_applet_nca) {
QMessageBox::warning(this, tr("Home Menu Applet"),
tr("Home Menu is not available. Please reinstall firmware."));
return;
}
system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::QLaunch);
const auto filename = QString::fromStdString((qlaunch_applet_nca->GetFullPath()));
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
BootGame(filename, LibraryAppletParameters(QLaunchId, Service::AM::AppletId::QLaunch));
}
void GMainWindow::OnCaptureScreenshot() {
if (emu_thread == nullptr || !emu_thread->IsRunning()) {
return;
}
const u64 title_id = system->GetApplicationProcessProgramID();
const auto screenshot_path =
2024-03-07 10:21:59 +01:00
QString::fromStdString(Common::FS::GetSuyuPathString(Common::FS::SuyuPath::ScreenshotsDir));
const auto date =
QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz"));
QString filename = QStringLiteral("%1/%2_%3.png")
.arg(screenshot_path)
.arg(title_id, 16, 16, QLatin1Char{'0'})
.arg(date);
if (!Common::FS::CreateDir(screenshot_path.toStdString())) {
return;
}
#ifdef _WIN32
if (UISettings::values.enable_screenshot_save_as) {
OnPauseGame();
filename = QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), filename,
tr("PNG Image (*.png)"));
OnStartGame();
if (filename.isEmpty()) {
return;
2019-01-20 04:14:19 +01:00
}
}
#endif
render_window->CaptureScreenshot(filename);
}
// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant
void GMainWindow::MigrateConfigFiles() {
2024-03-07 10:21:59 +01:00
const auto config_dir_fs_path = Common::FS::GetSuyuPath(Common::FS::SuyuPath::ConfigDir);
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
const QDir config_dir =
QDir(QString::fromStdString(Common::FS::PathToUTF8String(config_dir_fs_path)));
const QStringList config_dir_list = config_dir.entryList(QStringList(QStringLiteral("*.ini")));
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
if (!Common::FS::CreateDirs(config_dir_fs_path / "custom")) {
LOG_ERROR(Frontend, "Failed to create new config file directory");
}
for (auto it = config_dir_list.constBegin(); it != config_dir_list.constEnd(); ++it) {
const auto filename = it->toStdString();
if (filename.find_first_not_of("0123456789abcdefACBDEF", 0) < 16) {
continue;
}
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
const auto origin = config_dir_fs_path / filename;
const auto destination = config_dir_fs_path / "custom" / filename;
2022-08-07 23:01:11 +02:00
LOG_INFO(Frontend, "Migrating config file from {} to {}", origin.string(),
destination.string());
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
if (!Common::FS::RenameFile(origin, destination)) {
// Delete the old config file if one already exists in the new location.
Common::FS::RemoveFile(origin);
}
}
}
void GMainWindow::UpdateWindowTitle(std::string_view title_name, std::string_view title_version,
std::string_view gpu_vendor) {
2019-10-05 06:10:04 +02:00
const auto branch_name = std::string(Common::g_scm_branch);
const auto description = std::string(Common::g_scm_desc);
const auto build_id = std::string(Common::g_build_id);
const auto suyu_title = fmt::format("suyu | {}-{}", branch_name, description);
const auto override_title =
fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
const auto window_title = override_title.empty() ? suyu_title : override_title;
2020-06-08 23:58:04 +02:00
if (title_name.empty()) {
setWindowTitle(QString::fromStdString(window_title));
} else {
const auto run_title = [window_title, title_name, title_version, gpu_vendor]() {
if (title_version.empty()) {
return fmt::format("{} | {} | {}", window_title, title_name, gpu_vendor);
}
return fmt::format("{} | {} | {} | {}", window_title, title_name, title_version,
gpu_vendor);
}();
setWindowTitle(QString::fromStdString(run_title));
}
}
std::string GMainWindow::CreateTASFramesString(
std::array<size_t, InputCommon::TasInput::PLAYER_NUMBER> frames) const {
std::string string = "";
size_t maxPlayerIndex = 0;
for (size_t i = 0; i < frames.size(); i++) {
if (frames[i] != 0) {
if (maxPlayerIndex != 0)
string += ", ";
while (maxPlayerIndex++ != i)
string += "0, ";
string += std::to_string(frames[i]);
}
}
return string;
}
QString GMainWindow::GetTasStateDescription() const {
auto [tas_status, current_tas_frame, total_tas_frames] = input_subsystem->GetTas()->GetStatus();
std::string tas_frames_string = CreateTASFramesString(total_tas_frames);
switch (tas_status) {
2021-10-21 06:18:04 +02:00
case InputCommon::TasInput::TasState::Running:
return tr("TAS state: Running %1/%2")
.arg(current_tas_frame)
.arg(QString::fromStdString(tas_frames_string));
case InputCommon::TasInput::TasState::Recording:
return tr("TAS state: Recording %1").arg(total_tas_frames[0]);
case InputCommon::TasInput::TasState::Stopped:
return tr("TAS state: Idle %1/%2")
.arg(current_tas_frame)
.arg(QString::fromStdString(tas_frames_string));
default:
2021-06-27 21:02:38 +02:00
return tr("TAS State: Invalid");
}
}
void GMainWindow::OnTasStateChanged() {
bool is_running = false;
bool is_recording = false;
if (emulation_running) {
2021-11-22 05:37:50 +01:00
const InputCommon::TasInput::TasState tas_status =
std::get<0>(input_subsystem->GetTas()->GetStatus());
is_running = tas_status == InputCommon::TasInput::TasState::Running;
is_recording = tas_status == InputCommon::TasInput::TasState::Recording;
}
ui->action_TAS_Start->setText(is_running ? tr("&Stop Running") : tr("&Start"));
ui->action_TAS_Record->setText(is_recording ? tr("Stop R&ecording") : tr("R&ecord"));
ui->action_TAS_Start->setEnabled(emulation_running);
ui->action_TAS_Record->setEnabled(emulation_running);
ui->action_TAS_Reset->setEnabled(emulation_running);
}
void GMainWindow::UpdateStatusBar() {
if (emu_thread == nullptr || !system->IsPoweredOn()) {
status_bar_update_timer.stop();
return;
}
if (Settings::values.tas_enable) {
tas_label->setText(GetTasStateDescription());
} else {
tas_label->clear();
}
2021-10-14 20:32:19 +02:00
auto results = system->GetAndResetPerfStats();
auto& shader_notify = system->GPU().ShaderNotify();
const int shaders_building = shader_notify.ShadersBuilding();
2020-07-10 05:36:38 +02:00
if (shaders_building > 0) {
shader_building_label->setText(tr("Building: %n shader(s)", "", shaders_building));
2020-07-10 05:36:38 +02:00
shader_building_label->setVisible(true);
} else {
shader_building_label->setVisible(false);
}
const auto res_info = Settings::values.resolution_info;
const auto res_scale = res_info.up_factor;
res_scale_label->setText(
tr("Scale: %1x", "%1 is the resolution scaling factor").arg(res_scale));
if (Settings::values.use_speed_limit.GetValue()) {
2018-08-21 01:14:06 +02:00
emu_speed_label->setText(tr("Speed: %1% / %2%")
.arg(results.emulation_speed * 100.0, 0, 'f', 0)
.arg(Settings::values.speed_limit.GetValue()));
2018-08-21 01:14:06 +02:00
} else {
emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0));
}
if (!Settings::values.use_speed_limit) {
game_fps_label->setText(
tr("Game: %1 FPS (Unlocked)").arg(std::round(results.average_game_fps), 0, 'f', 0));
} else {
game_fps_label->setText(
tr("Game: %1 FPS").arg(std::round(results.average_game_fps), 0, 'f', 0));
}
emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2));
res_scale_label->setVisible(true);
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue());
game_fps_label->setVisible(true);
emu_frametime_label->setVisible(true);
2023-11-24 18:53:31 +01:00
firmware_label->setVisible(false);
}
void GMainWindow::UpdateGPUAccuracyButton() {
const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue();
const auto gpu_accuracy_text =
ConfigurationShared::gpu_accuracy_texts_map.find(gpu_accuracy)->second;
gpu_accuracy_button->setText(gpu_accuracy_text.toUpper());
gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal);
}
void GMainWindow::UpdateDockedButton() {
const auto console_mode = Settings::values.use_docked_mode.GetValue();
dock_status_button->setChecked(Settings::IsDockedMode());
dock_status_button->setText(
ConfigurationShared::use_docked_mode_texts_map.find(console_mode)->second.toUpper());
}
2022-11-28 02:37:37 +01:00
void GMainWindow::UpdateAPIText() {
const auto api = Settings::values.renderer_backend.GetValue();
const auto renderer_status_text =
ConfigurationShared::renderer_backend_texts_map.find(api)->second;
renderer_status_button->setText(
api == Settings::RendererBackend::OpenGL
? tr("%1 %2").arg(renderer_status_text.toUpper(),
ConfigurationShared::shader_backend_texts_map
.find(Settings::values.shader_backend.GetValue())
->second)
: renderer_status_text.toUpper());
2022-11-28 02:37:37 +01:00
}
void GMainWindow::UpdateFilterText() {
const auto filter = Settings::values.scaling_filter.GetValue();
const auto filter_text = ConfigurationShared::scaling_filter_texts_map.find(filter)->second;
filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR")
: filter_text.toUpper());
}
2021-10-22 22:56:08 +02:00
void GMainWindow::UpdateAAText() {
const auto aa_mode = Settings::values.anti_aliasing.GetValue();
const auto aa_text = ConfigurationShared::anti_aliasing_texts_map.find(aa_mode)->second;
aa_status_button->setText(aa_mode == Settings::AntiAliasing::None
? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA"))
: aa_text.toUpper());
2021-10-22 22:56:08 +02:00
}
void GMainWindow::UpdateVolumeUI() {
const auto volume_value = static_cast<int>(Settings::values.volume.GetValue());
volume_slider->setValue(volume_value);
if (Settings::values.audio_muted) {
volume_button->setChecked(false);
volume_button->setText(tr("VOLUME: MUTE"));
} else {
volume_button->setChecked(true);
volume_button->setText(tr("VOLUME: %1%", "Volume percentage (e.g. 50%)").arg(volume_value));
}
}
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
void GMainWindow::UpdateStatusButtons() {
renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
Settings::RendererBackend::Vulkan);
2022-11-28 02:37:37 +01:00
UpdateAPIText();
UpdateGPUAccuracyButton();
UpdateDockedButton();
UpdateFilterText();
2021-10-22 22:56:08 +02:00
UpdateAAText();
UpdateVolumeUI();
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
}
void GMainWindow::UpdateUISettings() {
2021-10-15 21:27:18 +02:00
if (!ui->action_Fullscreen->isChecked()) {
UISettings::values.geometry = saveGeometry();
UISettings::values.renderwindow_geometry = render_window->saveGeometry();
}
UISettings::values.state = saveState();
#if MICROPROFILE_ENABLED
UISettings::values.microprofile_geometry = microProfileDialog->saveGeometry();
UISettings::values.microprofile_visible = microProfileDialog->isVisible();
#endif
2021-10-15 21:27:18 +02:00
UISettings::values.single_window_mode = ui->action_Single_Window_Mode->isChecked();
UISettings::values.fullscreen = ui->action_Fullscreen->isChecked();
UISettings::values.display_titlebar = ui->action_Display_Dock_Widget_Headers->isChecked();
UISettings::values.show_filter_bar = ui->action_Show_Filter_Bar->isChecked();
UISettings::values.show_status_bar = ui->action_Show_Status_Bar->isChecked();
UISettings::values.show_folders_in_list = ui->action_Show_Folders_In_List->isChecked();
UISettings::values.first_start = false;
}
void GMainWindow::UpdateInputDrivers() {
if (!input_subsystem) {
return;
}
input_subsystem->PumpEvents();
}
void GMainWindow::HideMouseCursor() {
2021-02-24 03:39:02 +01:00
if (emu_thread == nullptr && UISettings::values.hide_mouse) {
mouse_hide_timer.stop();
ShowMouseCursor();
return;
}
render_window->setCursor(QCursor(Qt::BlankCursor));
}
void GMainWindow::ShowMouseCursor() {
render_window->unsetCursor();
2021-02-24 03:39:02 +01:00
if (emu_thread != nullptr && UISettings::values.hide_mouse) {
mouse_hide_timer.start();
}
}
void GMainWindow::OnMouseActivity() {
2021-02-03 19:34:25 +01:00
if (!Settings::values.mouse_panning) {
ShowMouseCursor();
}
}
void GMainWindow::OnCheckFirmwareDecryption() {
2021-10-14 20:32:19 +02:00
system->GetFileSystemController().CreateFactories(*vfs);
if (!ContentManager::AreKeysPresent()) {
2024-03-16 16:57:32 +01:00
QMessageBox::warning(this, tr("Derivation Components Missing"),
tr("Encryption keys are missing. "
2024-03-23 05:24:42 +01:00
"In order to use this emulator, "
"you need to provide your own encryption keys "
2024-03-18 23:13:59 +01:00
"in order to play them."));
2023-05-01 05:28:31 +02:00
}
2024-03-16 16:57:32 +01:00
2023-11-24 18:53:31 +01:00
SetFirmwareVersion();
UpdateMenuState();
2018-09-24 03:35:32 +02:00
}
bool GMainWindow::CheckFirmwarePresence() {
constexpr u64 MiiEditId = static_cast<u64>(Service::AM::AppletProgramId::MiiEdit);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
return false;
}
auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program);
if (!mii_applet_nca) {
return false;
}
return true;
}
2023-11-24 18:53:31 +01:00
void GMainWindow::SetFirmwareVersion() {
Service::Set::FirmwareVersionFormat firmware_data{};
const auto result = Service::Set::GetFirmwareVersionImpl(
firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2);
if (result.IsError() || !CheckFirmwarePresence()) {
LOG_INFO(Frontend, "Installed firmware: No firmware available");
firmware_label->setVisible(false);
return;
}
firmware_label->setVisible(true);
const std::string display_version(firmware_data.display_version.data());
const std::string display_title(firmware_data.display_title.data());
LOG_INFO(Frontend, "Installed firmware: {}", display_title);
firmware_label->setText(QString::fromStdString(display_version));
firmware_label->setToolTip(QString::fromStdString(display_title));
}
bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
u64* selected_title_id, u8* selected_content_record_type) {
using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>;
boost::container::flat_set<ContentInfo> available_title_ids;
const auto RetrieveEntries = [&](FileSys::TitleType title_type,
FileSys::ContentRecordType record_type) {
const auto entries = installed.ListEntriesFilter(title_type, record_type);
for (const auto& entry : entries) {
if (FileSys::GetBaseTitleID(entry.title_id) == program_id &&
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) {
available_title_ids.insert({entry.title_id, title_type, record_type});
}
}
};
RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::HtmlDocument);
RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::LegalInformation);
RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
if (available_title_ids.empty()) {
return false;
}
size_t title_index = 0;
if (available_title_ids.size() > 1) {
QStringList list;
for (auto& [title_id, title_type, record_type] : available_title_ids) {
const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id));
if (record_type == FileSys::ContentRecordType::Program) {
list.push_back(QStringLiteral("Program [%1]").arg(hex_title_id));
} else if (record_type == FileSys::ContentRecordType::HtmlDocument) {
list.push_back(QStringLiteral("HTML document [%1]").arg(hex_title_id));
} else if (record_type == FileSys::ContentRecordType::LegalInformation) {
list.push_back(QStringLiteral("Legal information [%1]").arg(hex_title_id));
} else {
list.push_back(
QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id));
}
}
bool ok;
const auto res = QInputDialog::getItem(
this, tr("Select RomFS Dump Target"),
tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
if (!ok) {
return false;
}
title_index = list.indexOf(res);
}
const auto& [title_id, title_type, record_type] = *available_title_ids.nth(title_index);
*selected_title_id = title_id;
*selected_content_record_type = static_cast<u8>(record_type);
return true;
}
bool GMainWindow::ConfirmClose() {
if (emu_thread == nullptr ||
UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) {
return true;
}
if (!system->GetExitLocked() &&
UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) {
return true;
}
2024-03-08 10:06:48 +01:00
const auto text = tr("Are you sure you want to close suyu?");
return question(this, tr("suyu"), text);
}
void GMainWindow::closeEvent(QCloseEvent* event) {
if (!ConfirmClose()) {
event->ignore();
return;
}
UpdateUISettings();
game_list->SaveInterfaceLayout();
UISettings::SaveWindowState();
hotkey_registry.SaveHotkeys();
// Unload controllers early
controller_dialog->UnloadController();
game_list->UnloadController();
2014-04-01 04:26:50 +02:00
// Shutdown session if the emu thread is active...
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
if (emu_thread != nullptr) {
ShutdownGame();
configuration: implement per-game configurations (#4098) * Switch game settings to use a pointer In order to add full per-game settings, we need to be able to tell yuzu to switch to using either the global or game configuration. Using a pointer makes it easier to switch. * configuration: add new UI without changing existing funcitonality The new UI also adds General, System, Graphics, Advanced Graphics, and Audio tabs, but as yet they do nothing. This commit keeps yuzu to the same functionality as originally branched. * configuration: Rename files These weren't included in the last commit. Now they are. * configuration: setup global configuration checkbox Global config checkbox now enables/disables the appropriate tabs in the game properties dialog. The use global configuration setting is now saved to the config, defaulting to true. This also addresses some changes requested in the PR. * configuration: swap to per-game config memory for properties dialog Does not set memory going in-game. Swaps to game values when opening the properties dialog, then swaps back when closing it. Uses a `memcpy` to swap. Also implements saving config files, limited to certain groups of configurations so as to not risk setting unsafe configurations. * configuration: change config interfaces to use config-specific pointers When a game is booted, we need to be able to open the configuration dialogs without changing the settings pointer in the game's emualtion. A new pointer specific to just the configuration dialogs can be used to separate changes to just those config dialogs without affecting the emulation. * configuration: boot a game using per-game settings Swaps values where needed to boot a game. * configuration: user correct config during emulation Creates a new pointer specifically for modifying the configuration while emulation is in progress. Both the regular configuration dialog and the game properties dialog now use the pointer Settings::config_values to focus edits to the correct struct. * settings: split Settings::values into two different structs By splitting the settings into two mutually exclusive structs, it becomes easier, as a developer, to determine how to use the Settings structs after per-game configurations is merged. Other benefits include only duplicating the required settings in memory. * settings: move use_docked_mode to Controls group `use_docked_mode` is set in the input settings and cannot be accessed from the system settings. Grouping it with system settings causes it to be saved with per-game settings, which may make transferring configs more difficult later on, especially since docked mode cannot be set from within the game properties dialog. * configuration: Fix the other yuzu executables and a regression In main.cpp, we have to get the title ID before the ROM is loaded, else the renderer will reflect only the global settings and now the user's game specific settings. * settings: use a template to duplicate memory for each setting Replaces the type of each variable in the Settings::Values struct with a new class that allows basic data reading and writing. The new struct Settings::Setting duplicates the data in memory and can manage global overrides per each setting. * configuration: correct add-ons config and swap settings when apropriate Any add-ons interaction happens directly through the global values struct. Swapping bewteen structs now also includes copying the necessary global configs that cannot be changed nor saved in per-game settings. General and System config menus now update based on whether it is viewing the global or per-game settings. * settings: restore old values struct No longer needed with the Settings::Setting class template. * configuration: implement hierarchical game properties dialog This sets the apropriate global or local data in each setting. * clang format * clang format take 2 can the docker container save this? * address comments and style issues * config: read and write settings with global awareness Adds new functions to read and write settings while keeping the global state in focus. Files now generated per-game are much smaller since often they only need address the global state. * settings: restore global state when necessary Upon closing a game or the game properties dialog, we need to restore all global settings to the original global state so that we can properly open the configuration dialog or boot a different game. * configuration: guard setting values incorrectly This disables setting values while a game is running if the setting is overwritten by a per game setting. * config: don't write local settings in the global config Simple guards to prevent writing the wrong settings in the wrong files. * configuration: add comments, assume less, and clang format No longer assumes that a disabled UI element means the global state is turned off, instead opting to directly answer that question. Still however assumes a game is running if it is in that state. * configuration: fix a logic error Should not be negated * restore settings' global state regardless of accept/cancel Fixes loading a properties dialog and causing the global config dialog to show local settings. * fix more logic errors Fixed the frame limit would set the global setting from the game properties dialog. Also strengthened the Settings::Setting member variables and simplified the logic in config reading (ReadSettingGlobal). * fix another logic error In my efforts to guard RestoreGlobalState, I accidentally negated the IsPowered condition. * configure_audio: set toggle_stretched_audio to tristate * fixed custom rtc and rng seed overwriting the global value * clang format * rebased * clang format take 4 * address my own review Basically revert unintended changes * settings: literal instead of casting "No need to cast, use 1U instead" Thanks, Morph! Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> * Revert "settings: literal instead of casting " This reverts commit 95e992a87c898f3e882ffdb415bb0ef9f80f613f. * main: fix status buttons reporting wrong settings after stop emulation * settings: Log UseDockedMode in the Controls group This should have happened when use_docked_mode was moved over to the controls group internally. This just reflects this in the log. * main: load settings if the file has a title id In other words, don't exit if the loader has trouble getting a title id. * use a zero * settings: initalize resolution factor with constructor instead of casting * Revert "settings: initalize resolution factor with constructor instead of casting" This reverts commit 54c35ecb46a29953842614620f9b7de1aa9d5dc8. * configure_graphics: guard device selector when Vulkan is global Prevents the user from editing the device selector if Vulkan is the global renderer backend. Also resets the vulkan_device variable when the users switches back-and-forth between global and Vulkan. * address reviewer concerns Changes function variables to const wherever they don't need to be changed. Sets Settings::Setting to final as it should not be inherited from. Sets ConfigurationShared::use_global_text to static. Co-Authored-By: VolcaEM <volcaem@users.noreply.github.com> * main: load per-game settings after LoadROM This prevents `Restart Emulation` from restoring the global settings *after* the per-game settings were applied. Thanks to BSoDGamingYT for finding this bug. * Revert "main: load per-game settings after LoadROM" This reverts commit 9d0d48c52d2dcf3bfb1806cc8fa7d5a271a8a804. * main: only restore global settings when necessary Loading the per-game settings cannot happen after the ROM is loaded, so we have to specify when to restore the global state. Again thanks to BSoD for finding the bug. * configuration_shared: address reviewer concerns except operator overrides Dropping operator override usage in next commit. Co-Authored-By: LC <lioncash@users.noreply.github.com> * settings: Drop operator overrides from Setting template Requires using GetValue and SetValue explicitly. Also reverts a change that broke title ID formatting in the game properties dialog. * complete rebase * configuration_shared: translate "Use global configuration" Uses ConfigurePerGame to do so, since its usage, at least as of now, corresponds with ConfigurationShared. * configure_per_game: address reviewer concern As far as I understand, it prevents the program from unnecessarily copying strings. Co-Authored-By: LC <lioncash@users.noreply.github.com> Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com> Co-authored-by: VolcaEM <volcaem@users.noreply.github.com> Co-authored-by: LC <lioncash@users.noreply.github.com>
2020-07-10 04:42:09 +02:00
}
2014-04-01 04:26:50 +02:00
render_window->close();
multiplayer_state->Close();
system->HIDCore().UnloadInputDevices();
system->GetRoomNetwork().Shutdown();
2014-04-01 04:26:50 +02:00
QWidget::closeEvent(event);
}
static bool IsSingleFileDropEvent(const QMimeData* mime) {
return mime->hasUrls() && mime->urls().length() == 1;
}
void GMainWindow::AcceptDropEvent(QDropEvent* event) {
if (IsSingleFileDropEvent(event->mimeData())) {
event->setDropAction(Qt::DropAction::LinkAction);
event->accept();
}
}
bool GMainWindow::DropAction(QDropEvent* event) {
if (!IsSingleFileDropEvent(event->mimeData())) {
return false;
}
const QMimeData* mime_data = event->mimeData();
const QString& filename = mime_data->urls().at(0).toLocalFile();
if (emulation_running && QFileInfo(filename).suffix() == QStringLiteral("bin")) {
// Amiibo
LoadAmiibo(filename);
} else {
// Game
if (ConfirmChangeGame()) {
BootGame(filename, ApplicationAppletParameters());
}
2017-02-16 04:23:30 +01:00
}
return true;
}
void GMainWindow::dropEvent(QDropEvent* event) {
DropAction(event);
2017-02-16 04:23:30 +01:00
}
void GMainWindow::dragEnterEvent(QDragEnterEvent* event) {
AcceptDropEvent(event);
2017-02-16 04:23:30 +01:00
}
void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
AcceptDropEvent(event);
2017-02-16 04:23:30 +01:00
}
bool GMainWindow::ConfirmChangeGame() {
if (emu_thread == nullptr)
return true;
// Use custom question to link controller navigation
return question(
2024-03-08 10:06:48 +01:00
this, tr("suyu"),
2017-02-16 04:23:30 +01:00
tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
2017-02-16 04:23:30 +01:00
}
bool GMainWindow::ConfirmForceLockedExit() {
if (emu_thread == nullptr) {
return true;
}
2024-03-08 10:06:48 +01:00
const auto text = tr("The currently running application has requested suyu to not exit.\n\n"
"Would you like to bypass this and exit anyway?");
2024-03-08 10:06:48 +01:00
return question(this, tr("suyu"), text);
}
void GMainWindow::RequestGameExit() {
if (!system->IsPoweredOn()) {
return;
}
system->SetExitRequested(true);
system->GetAppletManager().RequestExit();
}
void GMainWindow::filterBarSetChecked(bool state) {
2021-10-15 21:27:18 +02:00
ui->action_Show_Filter_Bar->setChecked(state);
emit(OnToggleFilterBar());
}
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
void GMainWindow::UpdateUITheme() {
const QString default_theme = QString::fromStdString(UISettings::default_theme.data());
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
QString current_theme = UISettings::values.theme;
if (current_theme.isEmpty()) {
current_theme = default_theme;
}
UpdateIcons(current_theme);
/* Find the stylesheet to load */
if (TryLoadStylesheet(current_theme)) {
return;
}
// Reading new theme failed, loading default stylesheet
LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme",
current_theme.toStdString());
if (TryLoadStylesheet(QStringLiteral(":/%1").arg(default_theme))) {
return;
}
// Reading default failed, loading empty stylesheet
LOG_ERROR(Frontend, "Unable to set default style, stylesheet file not found");
qApp->setStyleSheet({});
setStyleSheet({});
}
void GMainWindow::UpdateIcons(const QString& theme_path) {
// Get the theme directory from its path
std::size_t last_slash = theme_path.toStdString().find_last_of("/");
QString theme_dir = QString::fromStdString(theme_path.toStdString().substr(last_slash + 1));
// Append _dark to the theme name to use dark variant icons
if (CheckDarkMode()) {
LOG_DEBUG(Frontend, "Using icons from: {}", theme_dir.toStdString() + "_dark");
QIcon::setThemeName(theme_dir + QStringLiteral("_dark"));
} else {
LOG_DEBUG(Frontend, "Using icons from: {}", theme_dir.toStdString());
QIcon::setThemeName(theme_dir);
}
const QString theme_directory{
QString::fromStdString(Common::FS::GetSuyuPathString(Common::FS::SuyuPath::ThemesDir))};
// Set path for default icons
// Use icon resources from application binary and current theme local subdirectory, if it exists
QStringList theme_paths;
theme_paths << QString::fromStdString(":/icons") << QStringLiteral("%1").arg(theme_directory);
QIcon::setThemeSearchPaths(theme_paths);
// Change current directory, to allow user themes to add their own icons
QDir::setCurrent(QStringLiteral("%1/%2").arg(theme_directory, UISettings::values.theme));
emit UpdateThemedIcons();
}
bool GMainWindow::TryLoadStylesheet(const QString& theme_uri) {
LOG_DEBUG(Frontend, "TryLoadStylesheet()");
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
QString style_path;
// Use themed stylesheet if it exists
if (CheckDarkMode()) {
style_path = theme_uri + QStringLiteral("/dark.qss");
} else {
style_path = theme_uri + QStringLiteral("/light.qss");
}
if (!QFile::exists(style_path)) {
LOG_DEBUG(Frontend, "No themed (light/dark) stylesheet, using default one");
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
// Use common stylesheet if themed one does not exist
style_path = theme_uri + QStringLiteral("/style.qss");
}
// Loading stylesheet
QFile style_file(style_path);
if (style_file.open(QFile::ReadOnly | QFile::Text)) {
// Update the color palette before applying the stylesheet
UpdateThemePalette();
LOG_DEBUG(Frontend, "Loading stylesheet in: {}", theme_uri.toStdString());
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
QTextStream ts_theme(&style_file);
qApp->setStyleSheet(ts_theme.readAll());
setStyleSheet(ts_theme.readAll());
SetCustomStylesheet();
return true;
}
// Opening the file failed
return false;
}
bool GMainWindow::TryLoadStylesheet(const std::filesystem::path& theme_path) {
return TryLoadStylesheet(QString::fromStdString(theme_path.string() + "/"));
}
static void AdjustLinkColor() {
QPalette new_pal(qApp->palette());
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
if (GMainWindow::CheckDarkMode()) {
new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
} else {
new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
}
if (qApp->palette().color(QPalette::Link) != new_pal.color(QPalette::Link)) {
qApp->setPalette(new_pal);
}
}
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
void GMainWindow::UpdateThemePalette() {
LOG_DEBUG(Frontend, "UpdateThemePalette()");
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
QPalette themePalette(qApp->palette());
#ifdef _WIN32
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
QColor dark(25, 25, 25);
QString style_name;
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
if (CheckDarkMode()) {
// We check that the dark mode state is "On" and force a dark palette
if (UISettings::values.dark_mode_state == DarkModeState::On) {
// Set Default Windows Dark palette on Windows platforms to force Dark mode
themePalette.setColor(QPalette::Window, Qt::black);
themePalette.setColor(QPalette::WindowText, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127));
themePalette.setColor(QPalette::Base, Qt::black);
themePalette.setColor(QPalette::AlternateBase, dark);
themePalette.setColor(QPalette::ToolTipBase, Qt::white);
themePalette.setColor(QPalette::ToolTipText, Qt::black);
themePalette.setColor(QPalette::Text, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127));
themePalette.setColor(QPalette::Dark, QColor(128, 128, 128));
themePalette.setColor(QPalette::Shadow, Qt::white);
themePalette.setColor(QPalette::Button, Qt::black);
themePalette.setColor(QPalette::ButtonText, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127));
themePalette.setColor(QPalette::BrightText, QColor(192, 192, 192));
themePalette.setColor(QPalette::Link, QColor(0, 140, 200));
themePalette.setColor(QPalette::Highlight, QColor(24, 70, 93));
themePalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(0, 85, 255));
themePalette.setColor(QPalette::HighlightedText, QColor(239, 240, 241));
themePalette.setColor(QPalette::Disabled, QPalette::HighlightedText,
QColor(239, 240, 241));
}
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
// AlternateBase is kept at rgb(233, 231, 227) or rgb(245, 245, 245) on Windows dark
// palette, fix this. Sometimes, it even is rgb(0, 0, 0), but uses a very light gray for
// alternate rows, do not know why
if (themePalette.alternateBase().color() == QColor(233, 231, 227) ||
themePalette.alternateBase().color() == QColor(245, 245, 245) ||
themePalette.alternateBase().color() == QColor(0, 0, 0)) {
themePalette.setColor(QPalette::AlternateBase, dark);
alternate_base_modified = true;
}
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
// Use fusion theme, since its close to windowsvista, but works well with a dark palette
style_name = QStringLiteral("fusion");
} else {
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
// Reset AlternateBase if it has been modified
if (alternate_base_modified) {
themePalette.setColor(QPalette::AlternateBase, QColor(245, 245, 245));
alternate_base_modified = false;
}
// Reset light palette
themePalette = this->style()->standardPalette();
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
// Reset Windows theme to the default
style_name = QStringLiteral("windowsvista");
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
}
LOG_DEBUG(Frontend, "Using style: {}", style_name.toStdString());
qApp->setStyle(style_name);
#elif defined(__APPLE__)
// Force the usage of the light palette in light mode
if (CheckDarkMode()) {
// Reset dark palette
themePalette = this->style()->standardPalette();
} else {
themePalette.setColor(QPalette::Window, QColor(236, 236, 236));
themePalette.setColor(QPalette::WindowText, Qt::black);
themePalette.setColor(QPalette::Disabled, QPalette::WindowText, Qt::black);
themePalette.setColor(QPalette::Base, Qt::white);
themePalette.setColor(QPalette::AlternateBase, QColor(245, 245, 245));
themePalette.setColor(QPalette::ToolTipBase, Qt::white);
themePalette.setColor(QPalette::ToolTipText, Qt::black);
themePalette.setColor(QPalette::Text, Qt::black);
themePalette.setColor(QPalette::Disabled, QPalette::Text, Qt::black);
themePalette.setColor(QPalette::Dark, QColor(191, 191, 191));
themePalette.setColor(QPalette::Shadow, Qt::black);
themePalette.setColor(QPalette::Button, QColor(236, 236, 236));
themePalette.setColor(QPalette::ButtonText, Qt::black);
themePalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(147, 147, 147));
themePalette.setColor(QPalette::BrightText, Qt::white);
themePalette.setColor(QPalette::Link, QColor(0, 140, 200));
themePalette.setColor(QPalette::Highlight, QColor(179, 215, 255));
themePalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(220, 220, 220));
themePalette.setColor(QPalette::HighlightedText, Qt::black);
themePalette.setColor(QPalette::Disabled, QPalette::HighlightedText, Qt::black);
}
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
#else
if (CheckDarkMode()) {
// Set Dark palette on non Windows platforms (that may not have a dark palette)
themePalette.setColor(QPalette::Window, QColor(53, 53, 53));
themePalette.setColor(QPalette::WindowText, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127));
themePalette.setColor(QPalette::Base, QColor(42, 42, 42));
themePalette.setColor(QPalette::AlternateBase, QColor(66, 66, 66));
themePalette.setColor(QPalette::ToolTipBase, Qt::white);
themePalette.setColor(QPalette::ToolTipText, QColor(53, 53, 53));
themePalette.setColor(QPalette::Text, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127));
themePalette.setColor(QPalette::Dark, QColor(35, 35, 35));
themePalette.setColor(QPalette::Shadow, QColor(20, 20, 20));
themePalette.setColor(QPalette::Button, QColor(53, 53, 53));
themePalette.setColor(QPalette::ButtonText, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127));
themePalette.setColor(QPalette::BrightText, Qt::red);
themePalette.setColor(QPalette::Link, QColor(42, 130, 218));
themePalette.setColor(QPalette::Highlight, QColor(42, 130, 218));
themePalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80));
themePalette.setColor(QPalette::HighlightedText, Qt::white);
themePalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127));
} else {
// Reset light palette
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
themePalette = this->style()->standardPalette();
}
#endif
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
qApp->setPalette(themePalette);
AdjustLinkColor();
}
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
void GMainWindow::SetCustomStylesheet() {
setStyleSheet(QStringLiteral("QStatusBar::item { border: none; }"));
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
// Set "dark" qss property value, that may be used in stylesheets
bool is_dark_mode = CheckDarkMode();
if (renderer_status_button) {
renderer_status_button->setProperty("dark", is_dark_mode);
}
if (gpu_accuracy_button) {
gpu_accuracy_button->setProperty("dark", is_dark_mode);
}
#ifdef _WIN32
// Windows dark mode uses "fusion" style. Make it look like more "windowsvista" light style
if (is_dark_mode) {
/* the groove expands to the size of the slider by default. by giving it a height, it has a
fixed size */
/* handle is placed by default on the contents rect of the groove. Negative margin expands
it outside the groove */
setStyleSheet(QStringLiteral("QSlider:horizontal{ height:30px; }\
QSlider::sub-page:horizontal { background-color: palette(highlight); }\
QSlider::add-page:horizontal { background-color: palette(midlight);}\
QSlider::groove:horizontal { border-width: 1px; margin: 1px 0; height: 2px;}\
QSlider::handle:horizontal { border-width: 1px; border-style: solid; border-color: palette(dark);\
width: 10px; margin: -10px 0px; }\
QSlider::handle { background-color: palette(button); }\
QSlider::handle:hover { background-color: palette(highlight); }"));
}
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
#endif
}
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
#ifdef __unix__
bool GMainWindow::ListenColorSchemeChange() {
auto bus = QDBusConnection::sessionBus();
if (bus.isConnected()) {
const QString dbus_service = QStringLiteral("org.freedesktop.portal.Desktop");
const QString dbus_path = QStringLiteral("/org/freedesktop/portal/desktop");
const QString dbus_interface = QStringLiteral("org.freedesktop.portal.Settings");
const QString dbus_method = QStringLiteral("SettingChanged");
QStringList dbus_arguments;
dbus_arguments << QStringLiteral("org.freedesktop.appearance")
<< QStringLiteral("color-scheme");
const QString dbus_signature = QStringLiteral("ssv");
LOG_INFO(Frontend, "Connected to DBus, listening for OS theme changes");
return bus.connect(dbus_service, dbus_path, dbus_interface, dbus_method, dbus_arguments,
dbus_signature, this, SLOT(UpdateUITheme()));
}
LOG_WARNING(Frontend, "Unable to connect to DBus to listen for OS theme changes");
return false;
}
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
#endif
bool GMainWindow::CheckDarkMode() {
bool is_dark_mode_auto;
#ifdef _WIN32
// Dark mode cannot be changed after the app started on Windows
is_dark_mode_auto = qgetenv("QT_QPA_PLATFORM").contains("darkmode=2");
#else
is_dark_mode_auto = UISettings::values.dark_mode_state == DarkModeState::Auto;
#endif
if (!is_dark_mode_auto) {
return UISettings::values.dark_mode_state == DarkModeState::On;
} else {
const QPalette current_palette(qApp->palette());
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
#ifdef __unix__
QProcess process;
// Using the freedesktop specifications for checking dark mode
LOG_DEBUG(Frontend, "Retrieving theme from freedesktop color-scheme...");
QStringList gdbus_arguments;
gdbus_arguments << QStringLiteral("--dest=org.freedesktop.portal.Desktop")
<< QStringLiteral("--object-path /org/freedesktop/portal/desktop")
<< QStringLiteral("--method org.freedesktop.portal.Settings.Read")
<< QStringLiteral("org.freedesktop.appearance color-scheme");
process.start(QStringLiteral("gdbus call --session"), gdbus_arguments);
process.waitForFinished(1000);
QByteArray dbus_output = process.readAllStandardOutput();
if (!dbus_output.isEmpty()) {
const int systemColorSchema = QString::fromUtf8(dbus_output).trimmed().right(1).toInt();
return systemColorSchema == 1;
}
// Try alternative for Gnome if the previous one failed
QStringList gsettings_arguments;
gsettings_arguments << QStringLiteral("get")
<< QStringLiteral("org.gnome.desktop.interface")
<< QStringLiteral("color-scheme");
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
LOG_DEBUG(Frontend, "failed, retrieving theme from gsettings color-scheme...");
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
process.start(QStringLiteral("gsettings"), gsettings_arguments);
process.waitForFinished(1000);
QByteArray gsettings_output = process.readAllStandardOutput();
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
// Try older gtk-theme method if the previous one failed
if (gsettings_output.isEmpty()) {
LOG_DEBUG(Frontend, "failed, retrieving theme from gtk-theme...");
gsettings_arguments.takeLast();
gsettings_arguments << QStringLiteral("gtk-theme");
process.start(QStringLiteral("gsettings"), gsettings_arguments);
process.waitForFinished(1000);
gsettings_output = process.readAllStandardOutput();
}
// Interpret gsettings value if it succeeded
if (!gsettings_output.isEmpty()) {
QString systeme_theme = QString::fromUtf8(gsettings_output);
LOG_DEBUG(Frontend, "Gsettings output: {}", systeme_theme.toStdString());
return systeme_theme.contains(QStringLiteral("dark"), Qt::CaseInsensitive);
}
LOG_DEBUG(Frontend, "failed, retrieving theme from palette");
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
#endif
// Use default method based on palette swap by OS. It is the only method on Windows with
// Qt 5. Windows needs QT_QPA_PLATFORM env variable set to windows:darkmode=2 to force
// palette change
return (current_palette.color(QPalette::WindowText).lightness() >
current_palette.color(QPalette::Window).lightness());
}
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
}
void GMainWindow::changeEvent(QEvent* event) {
// PaletteChange event appears to only reach so far into the GUI, explicitly asking to
// UpdateUITheme is a decent work around
if (event->type() == QEvent::PaletteChange ||
event->type() == QEvent::ApplicationPaletteChange) {
LOG_DEBUG(Frontend,
"Window color palette changed by event: {} (QEvent::PaletteChange is: {})",
event->type(), QEvent::PaletteChange);
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
const QPalette test_palette(qApp->palette());
// Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too
const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
if (last_window_color != window_color) {
last_window_color = window_color;
UpdateUITheme();
}
}
QWidget::changeEvent(event);
Automatic dark theme switching for Windows and Linux - Windows dark theme uses "fusion" style, which is better suited, but has minor differences - Improve OS theme detection - Linux: - Listen for OS color schemes changes on D-Bus - Read OS scheme for D-Bus. Fallback with gsettings, reading org.gnome.desktop.interface. First "color-scheme" key, then "gtk-theme". Finally, fallback to checking window palette - Windows (dark mode detection was not implemented before): - Force dark palette when OS uses dark mode by setting QT_QPA_PLATFORM to "windows:darkmode=2" - This enables to detect dark mode by checking the window palette - Improve theming capabilites: - Linux uses custom palette when dark mode is detected. By using palette(xxx) in .qss files, there is no need to create a dark stylesheet - Allow themes to have stylesheet variants, dark.qss and light.qss - If current mode is dark, use dark icons for controller and keyboard applets - Add "dark" property to RendererStatusBarButton and GPUStatusBarButton, set to true when dark mode is used. Allows to have distinct colors for GPU API and accuracy buttons depending on dark mode or not - Enable all themes to have dark icon alternatives, not just "default" and "colorful" - If dark mode, icons are loaded from the directory "THEME-NAME_dark/icons" - If current mode is dark, use dark icons for controller and keyboard applets - Only qdarkstyle, qdarkstyle_midnight_blue, colorful_dark and colorful_midnight_blue used elements specific to dark themes
2024-02-04 04:04:47 +01:00
}
void GMainWindow::LoadTranslation() {
bool loaded;
if (UISettings::values.language.GetValue().empty()) {
// If the selected language is empty, use system locale
loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/"));
} else {
// Otherwise load from the specified file
loaded = translator.load(QString::fromStdString(UISettings::values.language.GetValue()),
QStringLiteral(":/languages/"));
}
if (loaded) {
qApp->installTranslator(&translator);
} else {
UISettings::values.language = std::string("en");
}
}
void GMainWindow::OnLanguageChanged(const QString& locale) {
if (UISettings::values.language.GetValue() != std::string("en")) {
qApp->removeTranslator(&translator);
}
UISettings::values.language = locale.toStdString();
LoadTranslation();
2021-10-15 21:27:18 +02:00
ui->retranslateUi(this);
multiplayer_state->retranslateUi();
UpdateWindowTitle();
}
void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
2018-09-16 20:05:51 +02:00
#ifdef USE_DISCORD_PRESENCE
if (state) {
2021-10-14 20:32:19 +02:00
discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(*system);
2018-09-16 20:05:51 +02:00
} else {
discord_rpc = std::make_unique<DiscordRPC::NullImpl>();
}
#else
discord_rpc = std::make_unique<DiscordRPC::NullImpl>();
#endif
discord_rpc->Update();
}
#ifdef __unix__
void GMainWindow::SetGamemodeEnabled(bool state) {
if (emulation_running) {
Common::Linux::SetGamemodeState(state);
}
}
#endif
Service::AM::FrontendAppletParameters GMainWindow::ApplicationAppletParameters() {
2024-01-07 03:21:01 +01:00
return Service::AM::FrontendAppletParameters{
.applet_id = Service::AM::AppletId::Application,
.applet_type = Service::AM::AppletType::Application,
};
}
Service::AM::FrontendAppletParameters GMainWindow::LibraryAppletParameters(
u64 program_id, Service::AM::AppletId applet_id) {
return Service::AM::FrontendAppletParameters{
.program_id = program_id,
.applet_id = applet_id,
.applet_type = Service::AM::AppletType::LibraryApplet,
};
}
void VolumeButton::wheelEvent(QWheelEvent* event) {
int num_degrees = event->angleDelta().y() / 8;
int num_steps = (num_degrees / 15) * scroll_multiplier;
// Stated in QT docs: Most mouse types work in steps of 15 degrees, in which case the delta
// value is a multiple of 120; i.e., 120 units * 1/8 = 15 degrees.
if (num_steps > 0) {
Settings::values.volume.SetValue(
std::min(200, Settings::values.volume.GetValue() + num_steps));
} else {
Settings::values.volume.SetValue(
std::max(0, Settings::values.volume.GetValue() + num_steps));
}
scroll_multiplier = std::min(MaxMultiplier, scroll_multiplier * 2);
scroll_timer.start(100); // reset the multiplier if no scroll event occurs within 100 ms
emit VolumeChanged();
event->accept();
}
void VolumeButton::ResetMultiplier() {
scroll_multiplier = 1;
}
2014-04-01 04:26:50 +02:00
#ifdef main
#undef main
#endif
static void SetHighDPIAttributes() {
#ifdef _WIN32
// For Windows, we want to avoid scaling artifacts on fractional scaling ratios.
// This is done by setting the optimal scaling policy for the primary screen.
// Create a temporary QApplication.
int temp_argc = 0;
char** temp_argv = nullptr;
QApplication temp{temp_argc, temp_argv};
// Get the current screen geometry.
const QScreen* primary_screen = QGuiApplication::primaryScreen();
if (primary_screen == nullptr) {
return;
}
const QRect screen_rect = primary_screen->geometry();
const int real_width = screen_rect.width();
const int real_height = screen_rect.height();
const float real_ratio = primary_screen->logicalDotsPerInch() / 96.0f;
// Recommended minimum width and height for proper window fit.
// Any screen with a lower resolution than this will still have a scale of 1.
constexpr float minimum_width = 1350.0f;
constexpr float minimum_height = 900.0f;
const float width_ratio = std::max(1.0f, real_width / minimum_width);
const float height_ratio = std::max(1.0f, real_height / minimum_height);
// Get the lower of the 2 ratios and truncate, this is the maximum integer scale.
const float max_ratio = std::trunc(std::min(width_ratio, height_ratio));
if (max_ratio > real_ratio) {
QApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::Round);
} else {
QApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::Floor);
}
#else
// Other OSes should be better than Windows at fractional scaling.
QApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
}
int main(int argc, char* argv[]) {
std::unique_ptr<QtConfig> config = std::make_unique<QtConfig>();
UISettings::RestoreWindowState(config);
2022-07-10 22:10:35 +02:00
bool has_broken_vulkan = false;
bool is_child = false;
if (CheckEnvVars(&is_child)) {
return 0;
}
if (StartupChecks(argv[0], &has_broken_vulkan,
Settings::values.perform_vulkan_check.GetValue())) {
return 0;
}
2024-03-08 10:06:48 +01:00
#ifdef SUYU_CRASH_DUMPS
Breakpad::InstallCrashHandler();
#endif
2018-09-16 20:05:51 +02:00
Common::DetachedTasks detached_tasks;
MicroProfileOnThreadCreate("Frontend");
SCOPE_EXIT {
MicroProfileShutdown();
};
Common::ConfigureNvidiaEnvironmentFlags();
// Init settings params
2024-03-08 10:06:48 +01:00
QCoreApplication::setOrganizationName(QStringLiteral("suyu team"));
QCoreApplication::setApplicationName(QStringLiteral("suyu"));
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
#ifdef _WIN32
QByteArray current_qt_qpa = qgetenv("QT_QPA_PLATFORM");
// Follow dark mode setting, if the "-platform" launch option is not set.
// Otherwise, just follow dark mode for the window decoration (title bar).
if (!current_qt_qpa.contains(":darkmode=")) {
if (UISettings::values.dark_mode_state == DarkModeState::Auto) {
// When setting is Auto, force adapting window decoration and stylesheet palette to use
// Windows theme. Default is darkmode:0, which always uses light palette
if (current_qt_qpa.isEmpty()) {
// Set the value
qputenv("QT_QPA_PLATFORM", QByteArray("windows:darkmode=2"));
} else {
// Concatenate to the existing value
qputenv("QT_QPA_PLATFORM", current_qt_qpa + ",darkmode=2");
}
} else {
// When setting is no Auto, adapt window decoration to the palette used
if (current_qt_qpa.isEmpty()) {
// Set the value
qputenv("QT_QPA_PLATFORM", QByteArray("windows:darkmode=1"));
} else {
// Concatenate to the existing value
qputenv("QT_QPA_PLATFORM", current_qt_qpa + ",darkmode=1");
}
}
}
// Increases the maximum open file limit to 8192
_setmaxstdio(8192);
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
#endif
#ifdef __APPLE__
2019-09-05 03:40:49 +02:00
// If you start a bundle (binary) on OSX without the Terminal, the working directory is "/".
2020-07-10 05:36:38 +02:00
// But since we require the working directory to be the executable path for the location of
// the user folder in the Qt Frontend, we need to cd into that working directory
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270) * common: fs: fs_types: Create filesystem types Contains various filesystem types used by the Common::FS library * common: fs: fs_util: Add std::string to std::u8string conversion utility * common: fs: path_util: Add utlity functions for paths Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library * common: fs: file: Rewrite the IOFile implementation * common: fs: Reimplement Common::FS library using std::filesystem * common: fs: fs_paths: Add fs_paths to replace common_paths * common: fs: path_util: Add the rest of the path functions * common: Remove the previous Common::FS implementation * general: Remove unused fs includes * string_util: Remove unused function and include * nvidia_flags: Migrate to the new Common::FS library * settings: Migrate to the new Common::FS library * logging: backend: Migrate to the new Common::FS library * core: Migrate to the new Common::FS library * perf_stats: Migrate to the new Common::FS library * reporter: Migrate to the new Common::FS library * telemetry_session: Migrate to the new Common::FS library * key_manager: Migrate to the new Common::FS library * bis_factory: Migrate to the new Common::FS library * registered_cache: Migrate to the new Common::FS library * xts_archive: Migrate to the new Common::FS library * service: acc: Migrate to the new Common::FS library * applets/profile: Migrate to the new Common::FS library * applets/web: Migrate to the new Common::FS library * service: filesystem: Migrate to the new Common::FS library * loader: Migrate to the new Common::FS library * gl_shader_disk_cache: Migrate to the new Common::FS library * nsight_aftermath_tracker: Migrate to the new Common::FS library * vulkan_library: Migrate to the new Common::FS library * configure_debug: Migrate to the new Common::FS library * game_list_worker: Migrate to the new Common::FS library * config: Migrate to the new Common::FS library * configure_filesystem: Migrate to the new Common::FS library * configure_per_game_addons: Migrate to the new Common::FS library * configure_profile_manager: Migrate to the new Common::FS library * configure_ui: Migrate to the new Common::FS library * input_profiles: Migrate to the new Common::FS library * yuzu_cmd: config: Migrate to the new Common::FS library * yuzu_cmd: Migrate to the new Common::FS library * vfs_real: Migrate to the new Common::FS library * vfs: Migrate to the new Common::FS library * vfs_libzip: Migrate to the new Common::FS library * service: bcat: Migrate to the new Common::FS library * yuzu: main: Migrate to the new Common::FS library * vfs_real: Delete the contents of an existing file in CreateFile Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now. * input_profiles: Don't iterate the input profile dir if it does not exist Silences an error produced in the log if the directory does not exist. * game_list_worker: Skip parsing file if the returned VfsFile is nullptr Prevents crashes in GetLoader when the virtual file is nullptr * common: fs: Validate paths for path length * service: filesystem: Open the mod load directory as read only
2021-05-26 01:32:56 +02:00
const auto bin_path = Common::FS::GetBundleDirectory() / "..";
chdir(Common::FS::PathToUTF8String(bin_path).c_str());
#endif
#ifdef __linux__
// Set the DISPLAY variable in order to open web browsers
// TODO (lat9nq): Find a better solution for AppImages to start external applications
if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) {
qputenv("DISPLAY", ":0");
}
// Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop
// suffix.
2024-04-01 02:08:49 +02:00
QGuiApplication::setDesktopFileName(QStringLiteral("dev.suyu_emu.suyu"));
#endif
SetHighDPIAttributes();
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// Disables the "?" button on all dialogs. Disabled by default on Qt6.
QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
#endif
2019-01-12 05:06:34 +01:00
// Enables the core to make the qt created contexts current on std::threads
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
2014-04-01 04:26:50 +02:00
QApplication app(argc, argv);
#ifdef _WIN32
OverrideWindowsFont();
#endif
// Workaround for QTBUG-85409, for Suzhou numerals the number 1 is actually \u3021
// so we can see if we get \u3008 instead
// TL;DR all other number formats are consecutive in unicode code points
// This bug is fixed in Qt6, specifically 6.0.0-alpha1
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
const QLocale locale = QLocale::system();
if (QStringLiteral("\u3008") == locale.toString(1)) {
QLocale::setDefault(QLocale::system().name());
}
#endif
// Qt changes the locale and causes issues in float conversion using std::to_string() when
// generating shaders
setlocale(LC_ALL, "C");
GMainWindow main_window{std::move(config), has_broken_vulkan};
// After settings have been loaded by GMainWindow, apply the filter
2014-04-01 04:26:50 +02:00
main_window.show();
2018-07-10 12:02:14 +02:00
QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window,
&GMainWindow::OnAppFocusStateChanged);
2018-09-16 20:05:51 +02:00
int result = app.exec();
detached_tasks.WaitForAllTasks();
return result;
2014-04-01 04:26:50 +02:00
}