Merge branch 'master' into master

This commit is contained in:
Kravickas 2026-03-30 16:00:28 +02:00 committed by GitHub
commit d3f7f1da66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 1388 additions and 165 deletions

View file

@ -6,7 +6,7 @@
QT_HOST="http://qt.mirror.constant.com/"
QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g")
QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]')
QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}."
QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}_${QT_VER_MSVC}_64/qt.qt${QT_VER_MAIN}.${QT_URL_VER}."
QT_PREFIX_2="win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}"
QT_SUFFIX="-Windows-Windows_11_24H2-${QT_VER_MSVC_UP}-Windows-Windows_11_24H2-X86_64.7z"
QT_BASE_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtbase${QT_SUFFIX}"

View file

@ -30,23 +30,23 @@ jobs:
matrix:
include:
- os: ubuntu-24.04
docker_img: "rpcs3/rpcs3-ci-jammy:1.9"
docker_img: "rpcs3/rpcs3-ci-jammy:1.10"
build_sh: "/rpcs3/.ci/build-linux.sh"
compiler: clang
UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux"
- os: ubuntu-24.04
docker_img: "rpcs3/rpcs3-ci-jammy:1.9"
docker_img: "rpcs3/rpcs3-ci-jammy:1.10"
build_sh: "/rpcs3/.ci/build-linux.sh"
compiler: gcc
- os: ubuntu-24.04-arm
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.9"
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.10"
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
compiler: clang
UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64"
- os: ubuntu-24.04-arm
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.9"
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.10"
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
compiler: gcc
name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }}
@ -134,7 +134,7 @@ jobs:
runs-on: macos-14
env:
CCACHE_DIR: /tmp/ccache_dir
QT_VER: '6.10.2'
QT_VER: '6.11.0'
QT_VER_MAIN: '6'
LLVM_COMPILER_VER: '21'
RELEASE_MESSAGE: ../GitHubReleaseMessage.txt
@ -213,9 +213,9 @@ jobs:
env:
COMPILER: msvc
QT_VER_MAIN: '6'
QT_VER: '6.10.2'
QT_VER: '6.11.0'
QT_VER_MSVC: 'msvc2022'
QT_DATE: '202601261212'
QT_DATE: '202603180535'
LLVM_VER: '19.1.7'
VULKAN_VER: '1.3.268.0'
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'

@ -1 +1 @@
Subproject commit c3e304954a9cfd154bc0dfbfea2b01cd61d6546d
Subproject commit d5515b5b8be3901aac04e5bd8bd5c89f287bcd33

@ -1 +1 @@
Subproject commit b077c81eb635392e694ccedbab8b644297ec0285
Subproject commit 922d04b3568c6428a9fb905ddee3ef5a68db3108

View file

@ -20,26 +20,26 @@ The following tools are required to build RPCS3 on Windows 10 or later:
with standalone **CMake** tool.
- [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
- [Qt 6.10.2](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
- [Qt 6.11.0](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode.
In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs:
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.10.2\msvc2022_64\`
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.11.0\msvc2022_64\`
- or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022)
**NOTE:** If you have issues with the **Visual Studio Qt Plugin**, you may want to uninstall it and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2022) instead.
In order to build **RPCS3** with the `CMake` solution (with both **Visual Studio** and standalone **CMake** tool):
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.10.2\msvc2022_64\`
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.11.0\msvc2022_64\`
### Linux
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager:
- Clang 17+ or GCC 13+
- [CMake 3.28.0+](https://www.cmake.org/download/)
- [Qt 6.10.2](https://www.qt.io/download-qt-installer)
- [Qt 6.11.0](https://www.qt.io/download-qt-installer)
- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
- [SDL3](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend)
@ -123,7 +123,7 @@ Start **Visual Studio**, click on `Open a project or solution` and select the `r
##### Configuring the Qt Plugin (if used)
1) go to `Extensions->Qt VS Tools->Qt Versions`
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.10.2\msvc2022_64`, version will fill in automatically
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.11.0\msvc2022_64`, version will fill in automatically
3) go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the **Legacy Qt Plugin**)
4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**)

View file

@ -393,7 +393,7 @@ namespace cfg
void set(const s64& value)
{
ensure(value >= Min && value <= Max);
if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max);
m_value = static_cast<int_type>(value);
}
@ -484,7 +484,7 @@ namespace cfg
void set(const f64& value)
{
ensure(value >= Min && value <= Max);
if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max);
m_value = static_cast<float_type>(value);
}
@ -571,7 +571,7 @@ namespace cfg
void set(const u64& value)
{
ensure(value >= Min && value <= Max);
if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max);
m_value = static_cast<int_type>(value);
}

View file

@ -1,4 +1,3 @@
set(WOLFSSL_LIBRARY ON)
set(WOLFSSL_INCLUDE_DIR ON)
set(WOLFSSL_LIBRARIES wolfssl)
set(WOLFSSL_LIBRARY wolfssl)
set(WOLFSSL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/3rdparty/wolfssl)
set(WOLFSSL_FOUND TRUE)

View file

@ -3,9 +3,9 @@ if(USE_SYSTEM_ZLIB)
find_package(ZLIB)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
else()
add_library(ZLIB::ZLIB INTERFACE IMPORTED)
add_library(ZLIB::ZLIB STATIC IMPORTED)
set_target_properties(ZLIB::ZLIB PROPERTIES
INTERFACE_LINK_LIBRARIES zlibstatic
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib/libzlibstatic.a"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/3rdparty/zlib/zlib;${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib")
set(ZLIB_FOUND TRUE)
endif()

View file

@ -1286,7 +1286,7 @@ extern bool ppu_patch(u32 addr, u32 value)
{
if (addr % 4)
{
ppu_log.fatal("Patch failed at 0x%x: unanligned memory address.", addr);
ppu_log.fatal("Patch failed at 0x%x: unaligned memory address.", addr);
return false;
}

View file

@ -2323,7 +2323,7 @@ public:
llvm::Value* loop_dictator_after_adjustment{};
spu_opcode_t reg_target{};
reg_target.rt = reduced_loop_info->cond_val_register_idx;
reg_target.rt = static_cast<u32>(reduced_loop_info->cond_val_register_idx);
if (reg_target.rt != reduced_loop_info->cond_val_register_idx)
{
@ -2375,7 +2375,7 @@ public:
else
{
spu_opcode_t reg_incr{};
reg_incr.rt = reduced_loop_info->cond_val_incr;
reg_incr.rt = static_cast<u32>(reduced_loop_info->cond_val_incr);
if (reg_incr.rt != reduced_loop_info->cond_val_incr)
{
@ -2425,7 +2425,7 @@ public:
else
{
spu_opcode_t reg_target2{};
reg_target2.rt = reduced_loop_info->cond_val_register_argument_idx;
reg_target2.rt = static_cast<u32>(reduced_loop_info->cond_val_register_argument_idx);
if (reg_target2.rt != reduced_loop_info->cond_val_register_argument_idx)
{

View file

@ -13,13 +13,13 @@ struct cfg_recording final : cfg::node
node_video(cfg::node* _this) : cfg::node(_this, "Video") {}
cfg::uint<0, 60> framerate{this, "Framerate", 30};
cfg::uint<0, 7680> width{this, "Width", 1280};
cfg::uint<0, 4320> height{this, "Height", 720};
cfg::uint<640, 7680> width{this, "Width", 1280};
cfg::uint<360, 4320> height{this, "Height", 720};
cfg::uint<0, 192> pixel_format{this, "AVPixelFormat", 0}; // AVPixelFormat::AV_PIX_FMT_YUV420P
cfg::uint<0, 0xFFFF> video_codec{this, "AVCodecID", 12}; // AVCodecID::AV_CODEC_ID_MPEG4
cfg::uint<0, 25000000> video_bps{this, "Video Bitrate", 4000000};
cfg::uint<0, 5> max_b_frames{this, "Max B-Frames", 2};
cfg::uint<0, 20> gop_size{this, "Group of Pictures Size", 12};
cfg::uint<1'000'000, 60'000'000> video_bps{this, "Video Bitrate", 4'000'000};
cfg::uint<0, 3> max_b_frames{this, "Max B-Frames", 2};
cfg::uint<1, 120> gop_size{this, "Group of Pictures Size", 30};
} video{ this };
@ -28,7 +28,7 @@ struct cfg_recording final : cfg::node
node_audio(cfg::node* _this) : cfg::node(_this, "Audio") {}
cfg::uint<0x10000, 0x17000> audio_codec{this, "AVCodecID", 86018}; // AVCodecID::AV_CODEC_ID_AAC
cfg::uint<0, 25000000> audio_bps{this, "Audio Bitrate", 320000};
cfg::uint<64'000, 320'000> audio_bps{this, "Audio Bitrate", 192'000};
} audio{ this };

View file

@ -431,25 +431,25 @@ namespace rsx
m_is_compiled = false;
}
void overlay_element::set_text(const std::string& text)
void overlay_element::set_text(std::string_view text)
{
std::u32string new_text = utf8_to_u32string(text);
const bool is_dirty = this->text != new_text;
this->text = std::move(new_text);
if (is_dirty)
{
this->text = std::move(new_text);
m_is_compiled = false;
}
}
void overlay_element::set_unicode_text(const std::u32string& text)
void overlay_element::set_unicode_text(std::u32string_view text)
{
const bool is_dirty = this->text != text;
this->text = text;
if (is_dirty)
{
this->text = text;
m_is_compiled = false;
}
}

View file

@ -242,8 +242,8 @@ namespace rsx
// NOTE: Functions as a simple position offset. Top left corner is the anchor.
virtual void set_margin(u16 left, u16 top);
virtual void set_margin(u16 margin);
virtual void set_text(const std::string& text);
virtual void set_unicode_text(const std::u32string& text);
virtual void set_text(std::string_view text);
virtual void set_unicode_text(std::u32string_view text);
void set_text(localized_string_id id);
void set_text(const localized_string& container);
virtual void set_font(const char* font_name, u16 font_size);

View file

@ -95,12 +95,12 @@ namespace rsx
}
}
void edit_text::set_text(const std::string& text)
void edit_text::set_text(std::string_view text)
{
set_unicode_text(utf8_to_u32string(text));
}
void edit_text::set_unicode_text(const std::u32string& text)
void edit_text::set_unicode_text(std::u32string_view text)
{
value = text;

View file

@ -26,8 +26,8 @@ namespace rsx
using label::label;
void set_text(const std::string& text) override;
void set_unicode_text(const std::u32string& text) override;
void set_text(std::string_view text) override;
void set_unicode_text(std::u32string_view text) override;
void set_placeholder(const std::u32string& placeholder_text);

View file

@ -90,7 +90,7 @@ namespace rsx
return m_loc_id == id;
}
bool message_item::text_matches(const std::u32string& text) const
bool message_item::text_matches(std::u32string_view text) const
{
return m_text.text == text;
}

View file

@ -31,7 +31,7 @@ namespace rsx
compiled_resource& get_compiled() override;
bool id_matches(localized_string_id id) const;
bool text_matches(const std::u32string& text) const;
bool text_matches(std::u32string_view text) const;
void set_label_text(const std::string& text);

View file

@ -948,7 +948,7 @@ namespace rsx
perf_overlay->set_update_interval(perf_settings.update_interval);
perf_overlay->set_font(perf_settings.font);
perf_overlay->set_font_size(perf_settings.font_size);
perf_overlay->set_margins(perf_settings.margin_x, perf_settings.margin_y, perf_settings.center_x.get(), perf_settings.center_y.get());
perf_overlay->set_margins(static_cast<f32>(perf_settings.margin_x.get()), static_cast<f32>(perf_settings.margin_y.get()), perf_settings.center_x.get(), perf_settings.center_y.get());
perf_overlay->use_window_space = perf_settings.perf_overlay_use_window_space.get();
perf_overlay->set_opacity(perf_settings.opacity / 100.f);
perf_overlay->set_body_colors(perf_settings.color_body, perf_settings.background_body);

View file

@ -58,7 +58,7 @@ namespace rsx
set_pos(x + dx, y + dy);
}
void progress_bar::set_text(const std::string& str)
void progress_bar::set_text(std::string_view str)
{
text_view.set_text(str);
text_view.align_text(text_align::center);

View file

@ -24,7 +24,7 @@ namespace rsx
void set_pos(s16 _x, s16 _y) override;
void set_size(u16 _w, u16 _h) override;
void translate(s16 dx, s16 dy) override;
void set_text(const std::string& str) override;
void set_text(std::string_view str) override;
compiled_resource& get_compiled() override;
};

View file

@ -60,7 +60,7 @@ static auto s_ascii_lowering_map = []()
}();
template<typename F>
void process_multibyte(const std::string& s, F&& func)
void process_multibyte(std::string_view s, F&& func)
{
const usz end = s.length();
for (usz index = 0; index < end; ++index)
@ -110,7 +110,7 @@ void process_multibyte(const std::string& s, F&& func)
}
}
std::string utf8_to_ascii8(const std::string& utf8_string)
std::string utf8_to_ascii8(std::string_view utf8_string)
{
std::string out;
out.reserve(utf8_string.length());
@ -135,7 +135,7 @@ std::string utf8_to_ascii8(const std::string& utf8_string)
return out;
}
std::string utf16_to_ascii8(const std::u16string& utf16_string)
std::string utf16_to_ascii8(std::u16string_view utf16_string)
{
// Strip extended codes, map to '#' instead (placeholder)
std::string out;
@ -152,7 +152,7 @@ std::string utf16_to_ascii8(const std::u16string& utf16_string)
return out;
}
std::u16string ascii8_to_utf16(const std::string& ascii_string)
std::u16string ascii8_to_utf16(std::string_view ascii_string)
{
std::u16string out;
out.reserve(ascii_string.length());
@ -168,7 +168,7 @@ std::u16string ascii8_to_utf16(const std::string& ascii_string)
return out;
}
std::u32string utf8_to_u32string(const std::string& utf8_string)
std::u32string utf8_to_u32string(std::string_view utf8_string)
{
std::u32string result;
result.reserve(utf8_string.size());
@ -181,7 +181,7 @@ std::u32string utf8_to_u32string(const std::string& utf8_string)
return result;
}
std::u16string u32string_to_utf16(const std::u32string& utf32_string)
std::u16string u32string_to_utf16(std::u32string_view utf32_string)
{
std::u16string result;
result.reserve(utf32_string.size());
@ -194,7 +194,7 @@ std::u16string u32string_to_utf16(const std::u32string& utf32_string)
return result;
}
std::u32string utf16_to_u32string(const std::u16string& utf16_string)
std::u32string utf16_to_u32string(std::u16string_view utf16_string)
{
std::u32string result;
result.reserve(utf16_string.size());

View file

@ -218,9 +218,9 @@ void operator < (const vector3_base<T>& lhs, T rhs)
using vector3i = vector3_base<int>;
using vector3f = vector3_base<float>;
std::string utf8_to_ascii8(const std::string& utf8_string);
std::string utf16_to_ascii8(const std::u16string& utf16_string);
std::u16string ascii8_to_utf16(const std::string& ascii_string);
std::u32string utf8_to_u32string(const std::string& utf8_string);
std::u16string u32string_to_utf16(const std::u32string& utf32_string);
std::u32string utf16_to_u32string(const std::u16string& utf16_string);
std::string utf8_to_ascii8(std::string_view utf8_string);
std::string utf16_to_ascii8(std::u16string_view utf16_string);
std::u16string ascii8_to_utf16(std::string_view ascii_string);
std::u32string utf8_to_u32string(std::string_view utf8_string);
std::u16string u32string_to_utf16(std::u32string_view utf32_string);
std::u32string utf16_to_u32string(std::u16string_view utf16_string);

View file

@ -1868,6 +1868,7 @@ namespace rsx
}
default:
rsx_log.fatal("Unhandled framebuffer option changed 0x%x", opt);
break;
}
}

View file

@ -2837,7 +2837,7 @@ bool Emulator::Pause(bool freeze_emulation, bool show_resume_message)
auto msg_ref = std::make_shared<atomic_t<u32>>(1);
// No timeout
rsx::overlays::queue_message(status == system_state::paused ? localized_string_id::EMULATION_PAUSED_RESUME_WITH_START : localized_string_id::EMULATION_FROZEN, -1, msg_ref);
rsx::overlays::queue_message(status == system_state::paused ? localized_string_id::EMULATION_PAUSED_RESUME_WITH_START : localized_string_id::EMULATION_FROZEN, umax, msg_ref);
m_pause_msgs_refs.emplace_back(msg_ref);
auto refresh_l = [this, msg_ref, status]()
@ -3160,7 +3160,11 @@ void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savesta
if (async_op)
{
std::thread{perform_kill}.detach();
std::thread{[perform_kill]()
{
thread_base::set_name("Perform Kill");
perform_kill();
}}.detach();
}
else
{

View file

@ -49,7 +49,7 @@ void headless_application::InitializeCallbacks()
{
EmuCallbacks callbacks = CreateCallbacks();
callbacks.try_to_quit = [this](bool force_quit, std::function<void()> on_exit) -> bool
callbacks.try_to_quit = [](bool force_quit, std::function<void()> on_exit) -> bool
{
if (force_quit)
{

View file

@ -69,6 +69,7 @@ DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResoluti
#include "rpcs3_version.h"
#include "Emu/System.h"
#include "Emu/system_utils.hpp"
#include "Emu/RSX/Overlays/overlay_message.h"
#include <thread>
#include <charconv>
@ -312,7 +313,8 @@ public:
{
if (msg == logs::level::fatal || (msg == logs::level::always && m_log_always))
{
std::string _msg = "RPCS3: ";
static const std::string rpcs3_prefix = "RPCS3: ";
std::string _msg = rpcs3_prefix;
if (!prefix.empty())
{
@ -351,7 +353,11 @@ public:
#endif
if (msg == logs::level::fatal)
{
std::string overlay_msg = "Fatal error: " + _msg.substr(rpcs3_prefix.size());
fmt::trim_back(overlay_msg, " \t\n");
// Pause emulation if fatal error encountered
rsx::overlays::queue_message(overlay_msg, umax);
Emu.Pause(true);
}
}

View file

@ -386,6 +386,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_pad_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_recording_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_patch_creator_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -689,6 +692,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_pad_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_recording_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_patch_creator_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -868,6 +874,7 @@
<ClCompile Include="rpcs3qt\osk_dialog_frame.cpp" />
<ClCompile Include="rpcs3qt\pad_led_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\pad_motion_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\recording_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\patch_creator_dialog.cpp" />
<ClCompile Include="rpcs3qt\patch_manager_dialog.cpp" />
<ClCompile Include="rpcs3qt\pkg_install_dialog.cpp" />
@ -1157,6 +1164,7 @@
<ClInclude Include="QTGeneratedFiles\ui_pad_led_settings_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_pad_motion_settings_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_pad_settings_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_recording_settings_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_patch_creator_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_patch_manager_dialog.h" />
<ClInclude Include="QTGeneratedFiles\ui_settings_dialog.h" />
@ -1624,6 +1632,16 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\recording_settings_dialog.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\protobuf\protobuf\src" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\pad_device_info.h" />
<ClInclude Include="rpcs3qt\permissions.h" />
<ClInclude Include="rpcs3qt\progress_indicator.h" />
@ -2196,6 +2214,16 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\recording_settings_dialog.ui">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Uic%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Uic%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\uic.exe" -o ".\QTGeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\shortcut_dialog.ui">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Uic%27ing %(Identity)...</Message>

View file

@ -291,6 +291,12 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_pad_settings_dialog.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_recording_settings_dialog.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_recording_settings_dialog.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_rsx_debugger.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
@ -441,6 +447,9 @@
<ClCompile Include="rpcs3qt\pad_settings_dialog.cpp">
<Filter>Gui\settings</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\recording_settings_dialog.cpp">
<Filter>Gui\settings</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\log_frame.cpp">
<Filter>Gui\log</Filter>
</ClCompile>
@ -1304,6 +1313,9 @@
<ClInclude Include="QTGeneratedFiles\ui_pad_led_settings_dialog.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="QTGeneratedFiles\ui_recording_settings_dialog.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="QTGeneratedFiles\ui_settings_dialog.h">
<Filter>Generated Files</Filter>
</ClInclude>
@ -1555,6 +1567,9 @@
<CustomBuild Include="rpcs3qt\settings_dialog.ui">
<Filter>Form Files</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\recording_settings_dialog.ui">
<Filter>Form Files</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\welcome_dialog.ui">
<Filter>Form Files</Filter>
</CustomBuild>
@ -1597,6 +1612,9 @@
<CustomBuild Include="rpcs3qt\pad_led_settings_dialog.h">
<Filter>Gui\settings</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\recording_settings_dialog.h">
<Filter>Gui\settings</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\log_frame.h">
<Filter>Gui\log</Filter>
</CustomBuild>

View file

@ -83,6 +83,7 @@ add_library(rpcs3_ui STATIC
qt_video_source.cpp
raw_mouse_settings_dialog.cpp
register_editor_dialog.cpp
recording_settings_dialog.cpp
recvmessage_dialog_frame.cpp
render_creator.cpp
rpcn_settings_dialog.cpp
@ -135,6 +136,7 @@ add_library(rpcs3_ui STATIC
patch_creator_dialog.ui
patch_manager_dialog.ui
ps_move_tracker_dialog.ui
recording_settings_dialog.ui
settings_dialog.ui
shortcut_dialog.ui
welcome_dialog.ui

View file

@ -181,7 +181,7 @@ void auto_pause_settings_dialog::ShowContextMenu(const QPoint &pos)
OnEntryConfig(idx, true);
});
connect(remove, &QAction::triggered, this, &auto_pause_settings_dialog::OnRemove);
connect(config, &QAction::triggered, this, [=, this]() {OnEntryConfig(row, false); });
connect(config, &QAction::triggered, this, [=]() {OnEntryConfig(row, false); });
myMenu.exec(m_pause_list->viewport()->mapToGlobal(pos));
}

View file

@ -66,7 +66,7 @@ clans_settings_dialog::clans_settings_dialog(QWidget* parent)
g_cfg_clans.save();
});
connect(m_cbx_protocol, &QComboBox::currentIndexChanged, this, [this](int index)
connect(m_cbx_protocol, &QComboBox::currentIndexChanged, this, [](int index)
{
if (index < 0)
return;

View file

@ -8,6 +8,7 @@
#include "progress_dialog.h"
#include "util/logs.hpp"
#include "Utilities/Thread.h"
#include <thread>
@ -82,6 +83,8 @@ void downloader::start(const std::string& url, bool follow_location, bool show_p
m_thread = QThread::create([this]
{
thread_base::set_name("Downloader");
// Reset error buffer before we call curl_easy_perform
m_curl->reset_error_buffer();

View file

@ -207,7 +207,7 @@ bool emu_settings::ValidateSettings(bool cleanup)
bool is_clean = true;
std::function<void(int, YAML::Node&, std::vector<std::string>&, cfg::_base*)> search_level;
search_level = [&search_level, &is_clean, &cleanup, this](int level, YAML::Node& yml_node, std::vector<std::string>& keys, cfg::_base* cfg_base)
search_level = [&search_level, &is_clean, &cleanup](int level, YAML::Node& yml_node, std::vector<std::string>& keys, cfg::_base* cfg_base)
{
if (!yml_node || !yml_node.IsMap())
{

View file

@ -15,7 +15,7 @@ find_dialog::find_dialog(QPlainTextEdit* edit, QWidget *parent, Qt::WindowFlags
QCheckBox* cb_case_sensitive = new QCheckBox(tr("Case sensitive"));
cb_case_sensitive->setChecked(m_case_sensitive);
connect(cb_case_sensitive, &QCheckBox::toggled, this, [=](bool checked)
connect(cb_case_sensitive, &QCheckBox::toggled, this, [this](bool checked)
{
m_case_sensitive = checked;
});

View file

@ -908,7 +908,7 @@ void game_list_actions::BatchActionBySerials(progress_dialog* pdlg, const std::s
const int serials_size = ::narrow<int>(serials.size());
*iterate_over_serial = [=, this, index_ptr = index](int index)
*iterate_over_serial = [=, index_ptr = index](int index)
{
if (index == serials_size)
{

View file

@ -21,7 +21,7 @@ game_list_grid::game_list_grid()
Q_EMIT IconReady(game, item);
};
connect(this, &game_list_grid::IconReady, this, [this](const game_info& game, const movie_item_base* item)
connect(this, &game_list_grid::IconReady, this, [](const game_info& game, const movie_item_base* item)
{
if (game && item && game->item == item) item->image_change_callback();
}, Qt::QueuedConnection); // The default 'AutoConnection' doesn't seem to work in this specific case...

View file

@ -54,7 +54,7 @@ game_list_table::game_list_table(game_list_frame* frame, std::shared_ptr<persist
}
});
connect(this, &game_list::IconReady, this, [this](const game_info& game, const movie_item_base* item)
connect(this, &game_list::IconReady, this, [](const game_info& game, const movie_item_base* item)
{
if (game && item && game->item == item) item->image_change_callback();
});

View file

@ -793,7 +793,7 @@ f64 gs_frame::client_display_rate()
{
f64 rate = 20.; // Minimum is 20
Emu.BlockingCallFromMainThread([this, &rate]()
Emu.BlockingCallFromMainThread([&rate]()
{
const QList<QScreen*> screens = QGuiApplication::screens();

View file

@ -737,8 +737,8 @@ void gui_application::InitializeCallbacks()
callbacks.get_msg_dialog = [this]() -> std::shared_ptr<MsgDialogBase> { return m_show_gui ? std::make_shared<msg_dialog_frame>() : nullptr; };
callbacks.get_osk_dialog = [this]() -> std::shared_ptr<OskDialogBase> { return m_show_gui ? std::make_shared<osk_dialog_frame>() : nullptr; };
callbacks.get_save_dialog = []() -> std::unique_ptr<SaveDialogBase> { return std::make_unique<save_data_dialog>(); };
callbacks.get_sendmessage_dialog = [this]() -> std::shared_ptr<SendMessageDialogBase> { return std::make_shared<sendmessage_dialog_frame>(); };
callbacks.get_recvmessage_dialog = [this]() -> std::shared_ptr<RecvMessageDialogBase> { return std::make_shared<recvmessage_dialog_frame>(); };
callbacks.get_sendmessage_dialog = []() -> std::shared_ptr<SendMessageDialogBase> { return std::make_shared<sendmessage_dialog_frame>(); };
callbacks.get_recvmessage_dialog = []() -> std::shared_ptr<RecvMessageDialogBase> { return std::make_shared<recvmessage_dialog_frame>(); };
callbacks.get_trophy_notification_dialog = [this]() -> std::unique_ptr<TrophyNotificationBase> { return std::make_unique<trophy_notification_helper>(m_game_window); };
callbacks.on_run = [this](bool start_playtime) { OnEmulatorRun(start_playtime); };
@ -839,7 +839,7 @@ void gui_application::InitializeCallbacks()
};
}
callbacks.on_emulation_stop_no_response = [this](std::shared_ptr<atomic_t<bool>> closed_successfully, int seconds_waiting_already)
callbacks.on_emulation_stop_no_response = [](std::shared_ptr<atomic_t<bool>> closed_successfully, int seconds_waiting_already)
{
const std::string terminate_message = tr("Stopping emulator took too long."
"\nSome thread has probably deadlocked. Aborting.").toStdString();
@ -849,7 +849,7 @@ void gui_application::InitializeCallbacks()
report_fatal_error(terminate_message);
}
Emu.CallFromMainThread([this, closed_successfully, seconds_waiting_already, terminate_message]
Emu.CallFromMainThread([closed_successfully, seconds_waiting_already, terminate_message]
{
const auto seconds = std::make_shared<int>(seconds_waiting_already);

View file

@ -316,7 +316,7 @@ void kernel_explorer::update()
add_solid_node(find_node(root, additional_nodes::process_info), QString::fromStdString(fmt::format("Process Info, Sdk Version: 0x%08x, PPC SEG: %#x, SFO Category: %s (Fake: %s)", g_ps3_process_info.sdk_ver, g_ps3_process_info.ppc_seg, Emu.GetCat(), Emu.GetFakeCat())));
auto display_program_segments = [this](QTreeWidgetItem* tree, const ppu_module<lv2_obj>& m)
auto display_program_segments = [](QTreeWidgetItem* tree, const ppu_module<lv2_obj>& m)
{
for (usz i = 0; i < m.segs.size(); i++)
{
@ -661,7 +661,7 @@ void kernel_explorer::update()
const s32 prio = ppu.prio.load().prio;
std::string prio_text = fmt::format("%4d", prio);
prio_text = fmt::replace_all(prio_text, " ", " ");
ppu_threads.emplace_back(prio, fmt::format(u8"PPU 0x%07x: PRIO: %s, “%s”Joiner: %s, Status: %s, State: %s, %s func: “%s”%s", id, prio_text, *ppu.ppu_tname.load(), ppu.joiner.load(), status, ppu.state.load()
, ppu.ack_suspend ? "After" : (ppu.current_function ? "In" : "Last"), func ? func : "", get_wait_time_str(ppu.start_time)));
}, idm::unlocked);

View file

@ -46,6 +46,7 @@
#include "welcome_dialog.h"
#include "music_player_dialog.h"
#include "sound_effect_manager_dialog.h"
#include "recording_settings_dialog.h"
#include <thread>
#include <unordered_set>
@ -1734,7 +1735,7 @@ void main_window::DecryptSPRXLibraries()
dlg->set_button_enabled(QDialogButtonBox::StandardButton::Ok, text.size() - (text.indexOf('x') + 1) == 32);
});
connect(dlg, &QDialog::accepted, this, [this, iterate, dlg, mod_index, decrypter, repeat_count]()
connect(dlg, &QDialog::accepted, this, [iterate, dlg, mod_index, decrypter, repeat_count]()
{
std::string text = dlg->get_input_text().toStdString();
@ -3120,16 +3121,22 @@ void main_window::CreateConnects()
connect(ui->actionManage_Screenshots, &QAction::triggered, this, [this]
{
screenshot_manager_dialog* screenshot_manager = new screenshot_manager_dialog();
screenshot_manager_dialog* screenshot_manager = new screenshot_manager_dialog(m_game_list_frame ? m_game_list_frame->GetGameInfo() : std::vector<game_info>{});
screenshot_manager->show();
});
connect(ui->actionManage_SoundEffects, &QAction::triggered, this, [this]
connect(ui->actionManage_SoundEffects, &QAction::triggered, this, []
{
sound_effect_manager_dialog* dlg = new sound_effect_manager_dialog();
dlg->show();
});
connect(ui->actionRecording, &QAction::triggered, this, [this]
{
recording_settings_dialog* dlg = new recording_settings_dialog(this);
dlg->open();
});
connect(ui->toolsCgDisasmAct, &QAction::triggered, this, [this]
{
cg_disasm_window* cgdw = new cg_disasm_window(m_gui_settings);
@ -3376,7 +3383,7 @@ void main_window::CreateConnects()
welcome->open();
});
connect(ui->supportAct, &QAction::triggered, this, [this]
connect(ui->supportAct, &QAction::triggered, this, []
{
QDesktopServices::openUrl(QUrl("https://rpcs3.net/patreon"));
});

View file

@ -290,6 +290,7 @@
<addaction name="separator"/>
<addaction name="confShortcutsAct"/>
<addaction name="actionManage_SoundEffects"/>
<addaction name="actionRecording"/>
<addaction name="separator"/>
<addaction name="confAutopauseManagerAct"/>
</widget>
@ -1536,6 +1537,11 @@
<string>Play Hover Music</string>
</property>
</action>
<action name="actionRecording">
<property name="text">
<string>Recording</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

View file

@ -504,6 +504,8 @@ memory_viewer_panel::memory_viewer_panel(QWidget* parent, std::shared_ptr<CPUDis
m_search_thread = QThread::create([this, wstr, m_modes = m_modes]()
{
thread_base::set_name("MemViewerSearch");
gui_log.notice("Searching for %s (mode: %s)", wstr, m_modes);
u64 found = 0;

View file

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "movie_item_base.h"
#include "Utilities/Thread.h"
movie_item_base::movie_item_base() : qt_video_source()
{
@ -33,6 +34,8 @@ void movie_item_base::call_icon_load_func(int index)
m_icon_loading = true;
m_icon_load_thread.reset(QThread::create([this, index]()
{
thread_base::set_name(fmt::format("IconLoad %d", index));
if (m_icon_load_callback)
{
m_icon_load_callback(index);
@ -63,6 +66,8 @@ void movie_item_base::call_size_calc_func()
m_size_on_disk_loading = true;
m_size_calc_thread.reset(QThread::create([this]()
{
thread_base::set_name("SizeCalc");
if (m_size_calc_callback)
{
m_size_calc_callback();

View file

@ -405,6 +405,8 @@ void ps_move_tracker_dialog::reset_camera()
m_tracker_thread.reset(QThread::create([this]()
{
thread_base::set_name("PS Move Tracker");
while (!m_stop_threads)
{
process_camera_frame();

View file

@ -279,6 +279,11 @@ namespace gui
exp_img.setDevicePixelRatio(device_pixel_ratio);
exp_img.fill(Qt::transparent);
if (pixmap.isNull())
{
return exp_img;
}
// Load scaled pixmap
pixmap = pixmap.scaled(icon_size, Qt::KeepAspectRatio, mode);

View file

@ -0,0 +1,455 @@
#include "stdafx.h"
#include "recording_settings_dialog.h"
#include "ui_recording_settings_dialog.h"
#include <QPushButton>
#ifdef _MSC_VER
#pragma warning(push, 0)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wall"
#pragma GCC diagnostic ignored "-Wextra"
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
}
#ifdef _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif
LOG_CHANNEL(cfg_log, "CFG");
static std::vector<const AVCodec*> get_video_codecs(const AVOutputFormat* fmt)
{
std::vector<const AVCodec*> codecs;
void* opaque = nullptr;
while (const AVCodec* codec = av_codec_iterate(&opaque))
{
if (!codec->pix_fmts)
continue;
if (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL)
continue;
if (codec->type != AVMediaType::AVMEDIA_TYPE_VIDEO)
continue;
switch (codec->id)
{
case AV_CODEC_ID_H264:
case AV_CODEC_ID_HEVC:
case AV_CODEC_ID_MPEG4:
case AV_CODEC_ID_AV1:
break;
default:
continue;
}
if (!av_codec_is_encoder(codec))
continue;
if (avformat_query_codec(fmt, codec->id, FF_COMPLIANCE_NORMAL) != 1)
continue;
codecs.push_back(codec);
}
return codecs;
}
static std::vector<const AVCodec*> get_audio_codecs(const AVOutputFormat* fmt)
{
std::vector<const AVCodec*> codecs;
void* opaque = nullptr;
while (const AVCodec* codec = av_codec_iterate(&opaque))
{
if (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL)
continue;
if (codec->type != AVMediaType::AVMEDIA_TYPE_AUDIO)
continue;
if (!av_codec_is_encoder(codec))
continue;
if (avformat_query_codec(fmt, codec->id, FF_COMPLIANCE_NORMAL) != 1)
continue;
codecs.push_back(codec);
}
return codecs;
}
recording_settings_dialog::recording_settings_dialog(QWidget* parent)
: QDialog(parent), ui(new Ui::recording_settings_dialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
if (!g_cfg_recording.load())
{
cfg_log.notice("Could not load recording config. Using defaults.");
}
ui->combo_presets->addItem(tr("720p 30fps"), static_cast<int>(quality_preset::_720p_30));
ui->combo_presets->addItem(tr("720p 60fps"), static_cast<int>(quality_preset::_720p_60));
ui->combo_presets->addItem(tr("1080p 30fps"), static_cast<int>(quality_preset::_1080p_30));
ui->combo_presets->addItem(tr("1080p 60fps"), static_cast<int>(quality_preset::_1080p_60));
ui->combo_presets->addItem(tr("1440p 30fps"), static_cast<int>(quality_preset::_1440p_30));
ui->combo_presets->addItem(tr("1440p 60fps"), static_cast<int>(quality_preset::_1440p_60));
ui->combo_presets->addItem(tr("2160p 30fps"), static_cast<int>(quality_preset::_2160p_30));
ui->combo_presets->addItem(tr("2160p 60fps"), static_cast<int>(quality_preset::_2160p_60));
ui->combo_presets->addItem(tr("Custom"), static_cast<int>(quality_preset::custom));
connect(ui->combo_presets, &QComboBox::currentIndexChanged, this, [this](int index)
{
const QVariant var = ui->combo_presets->itemData(index);
if (var.canConvert<int>())
{
const quality_preset preset = static_cast<quality_preset>(var.toInt());
select_preset(preset, g_cfg_recording);
update_ui();
}
});
ui->combo_resolution->addItem("360p", QVariant::fromValue(QPair<int, int>(640, 360)));
ui->combo_resolution->addItem("480p", QVariant::fromValue(QPair<int, int>(854, 480)));
ui->combo_resolution->addItem("720p", QVariant::fromValue(QPair<int, int>(1280, 720)));
ui->combo_resolution->addItem("1080p", QVariant::fromValue(QPair<int, int>(1920, 1080)));
ui->combo_resolution->addItem("1440p", QVariant::fromValue(QPair<int, int>(2560, 1440)));
ui->combo_resolution->addItem("2160p", QVariant::fromValue(QPair<int, int>(3840, 2160)));
connect(ui->combo_resolution, &QComboBox::currentIndexChanged, this, [this](int index)
{
const QVariant var = ui->combo_resolution->itemData(index);
if (var.canConvert<QPair<int, int>>())
{
const QPair<int, int> size = var.value<QPair<int, int>>();
g_cfg_recording.video.width.set(size.first);
g_cfg_recording.video.height.set(size.second);
update_preset();
}
});
const AVOutputFormat* fmt = av_guess_format("mp4", nullptr, nullptr);
m_video_codecs = get_video_codecs(fmt);
m_audio_codecs = get_audio_codecs(fmt);
for (const AVCodec* codec : m_video_codecs)
{
if (!codec) continue;
const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id);
ui->combo_video_codec->addItem(QString::fromStdString(name), static_cast<int>(codec->id));
}
for (const AVCodec* codec : m_audio_codecs)
{
if (!codec) continue;
const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id);
ui->combo_audio_codec->addItem(QString::fromStdString(name), static_cast<int>(codec->id));
}
connect(ui->combo_video_codec, &QComboBox::currentIndexChanged, this, [this](int index)
{
const QVariant var = ui->combo_video_codec->itemData(index);
if (var.canConvert<int>())
{
const int codec_id = var.toInt();
g_cfg_recording.video.video_codec.set(codec_id);
update_preset();
}
});
connect(ui->combo_audio_codec, &QComboBox::currentIndexChanged, this, [this](int index)
{
const QVariant var = ui->combo_audio_codec->itemData(index);
if (var.canConvert<int>())
{
const int codec_id = var.toInt();
g_cfg_recording.audio.audio_codec.set(codec_id);
update_preset();
}
});
ui->combo_framerate->addItem("30", 30);
ui->combo_framerate->addItem("60", 60);
connect(ui->combo_framerate, &QComboBox::currentIndexChanged, this, [this](int index)
{
const QVariant var = ui->combo_framerate->itemData(index);
if (var.canConvert<int>())
{
const int fps = var.toInt();
g_cfg_recording.video.framerate.set(fps);
update_preset();
}
});
ui->spinbox_video_bitrate->setSingleStep(1);
ui->spinbox_video_bitrate->setMinimum(g_cfg_recording.video.video_bps.min);
ui->spinbox_video_bitrate->setMaximum(g_cfg_recording.video.video_bps.max);
connect(ui->spinbox_video_bitrate, &QSpinBox::valueChanged, this, [this](int value)
{
g_cfg_recording.video.video_bps.set(value);
update_preset();
});
ui->spinbox_audio_bitrate->setSingleStep(1);
ui->spinbox_audio_bitrate->setMinimum(g_cfg_recording.audio.audio_bps.min);
ui->spinbox_audio_bitrate->setMaximum(g_cfg_recording.audio.audio_bps.max);
connect(ui->spinbox_audio_bitrate, &QSpinBox::valueChanged, this, [this](int value)
{
g_cfg_recording.audio.audio_bps.set(value);
update_preset();
});
ui->spinbox_gop_size->setSingleStep(1);
ui->spinbox_gop_size->setMinimum(g_cfg_recording.video.gop_size.min);
ui->spinbox_gop_size->setMaximum(g_cfg_recording.video.gop_size.max);
connect(ui->spinbox_gop_size, &QSpinBox::valueChanged, this, [this](int value)
{
g_cfg_recording.video.gop_size.set(value);
update_preset();
});
ui->spinbox_max_b_frames->setSingleStep(1);
ui->spinbox_max_b_frames->setMinimum(g_cfg_recording.video.max_b_frames.min);
ui->spinbox_max_b_frames->setMaximum(g_cfg_recording.video.max_b_frames.max);
connect(ui->spinbox_max_b_frames, &QSpinBox::valueChanged, this, [this](int value)
{
g_cfg_recording.video.max_b_frames.set(value);
update_preset();
});
connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button)
{
if (button == ui->buttonBox->button(QDialogButtonBox::Save))
{
g_cfg_recording.save();
accept();
}
else if (button == ui->buttonBox->button(QDialogButtonBox::Cancel))
{
reject();
}
else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults))
{
g_cfg_recording.from_default();
update_ui();
update_preset();
}
});
connect(this, &QDialog::rejected, this, []()
{
if (!g_cfg_recording.load())
{
cfg_log.notice("Could not load recording config. Using defaults.");
}
});
update_ui();
update_preset();
}
recording_settings_dialog::~recording_settings_dialog()
{
}
void recording_settings_dialog::update_preset()
{
const quality_preset preset = current_preset();
ui->combo_presets->setCurrentIndex(ui->combo_presets->findData(static_cast<int>(preset)));
}
void recording_settings_dialog::update_ui()
{
ui->combo_resolution->blockSignals(true);
ui->combo_framerate->blockSignals(true);
ui->combo_video_codec->blockSignals(true);
ui->combo_audio_codec->blockSignals(true);
ui->spinbox_video_bitrate->blockSignals(true);
ui->spinbox_audio_bitrate->blockSignals(true);
ui->spinbox_gop_size->blockSignals(true);
ui->spinbox_max_b_frames->blockSignals(true);
ui->combo_resolution->setCurrentIndex(ui->combo_resolution->findData(QVariant::fromValue(QPair<int, int>(g_cfg_recording.video.width.get(), g_cfg_recording.video.height.get()))));
ui->combo_framerate->setCurrentIndex(ui->combo_framerate->findData(static_cast<int>(g_cfg_recording.video.framerate.get())));
ui->combo_video_codec->setCurrentIndex(ui->combo_video_codec->findData(static_cast<int>(g_cfg_recording.video.video_codec.get())));
ui->combo_audio_codec->setCurrentIndex(ui->combo_audio_codec->findData(static_cast<int>(g_cfg_recording.audio.audio_codec.get())));
ui->spinbox_video_bitrate->setValue(g_cfg_recording.video.video_bps);
ui->spinbox_audio_bitrate->setValue(g_cfg_recording.audio.audio_bps);
ui->spinbox_gop_size->setValue(g_cfg_recording.video.gop_size);
ui->spinbox_max_b_frames->setValue(g_cfg_recording.video.max_b_frames);
ui->combo_resolution->blockSignals(false);
ui->combo_framerate->blockSignals(false);
ui->combo_video_codec->blockSignals(false);
ui->combo_audio_codec->blockSignals(false);
ui->spinbox_video_bitrate->blockSignals(false);
ui->spinbox_audio_bitrate->blockSignals(false);
ui->spinbox_gop_size->blockSignals(false);
ui->spinbox_max_b_frames->blockSignals(false);
const auto get_codec_name = [](const std::vector<const AVCodec*>& codecs, u32 id)
{
for (const AVCodec* codec : codecs)
{
if (codec && codec->id == static_cast<AVCodecID>(id))
{
const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id);
return name;
}
}
return std::string();
};
ui->label_info_keys->setText(
tr("Resolution:") + "\n" +
tr("Framerate:") + "\n" +
tr("Video Codec:") + "\n" +
tr("Video Bitrate:") + "\n" +
tr("Audio Codec:") + "\n" +
tr("Audio Bitrate:") + "\n" +
tr("Gop-Size:") + "\n" +
tr("Max B-Frames:")
);
ui->label_info_values->setText(QString::fromStdString(
fmt::format("%d x %d\n%d fps\n%s\n%d\n%s\n%d\n%d\n%d",
g_cfg_recording.video.width.get(), g_cfg_recording.video.height.get(),
g_cfg_recording.video.framerate.get(),
get_codec_name(m_video_codecs, g_cfg_recording.video.video_codec.get()),
g_cfg_recording.video.video_bps.get(),
get_codec_name(m_audio_codecs, g_cfg_recording.audio.audio_codec.get()),
g_cfg_recording.audio.audio_bps.get(),
g_cfg_recording.video.gop_size.get(),
g_cfg_recording.video.max_b_frames.get()
)
));
}
void recording_settings_dialog::select_preset(quality_preset preset, cfg_recording& cfg)
{
if (preset == quality_preset::custom)
{
return;
}
cfg.audio.audio_codec.set(static_cast<u32>(AVCodecID::AV_CODEC_ID_AAC));
cfg.audio.audio_bps.set(192'000); // 192 kbps
cfg.video.video_codec.set(static_cast<u32>(AVCodecID::AV_CODEC_ID_MPEG4));
cfg.video.pixel_format.set(static_cast<u32>(::AV_PIX_FMT_YUV420P));
switch (preset)
{
case quality_preset::_720p_30:
case quality_preset::_720p_60:
cfg.video.width.set(1280);
cfg.video.height.set(720);
break;
case quality_preset::_1080p_30:
case quality_preset::_1080p_60:
cfg.video.width.set(1920);
cfg.video.height.set(1080);
break;
case quality_preset::_1440p_30:
case quality_preset::_1440p_60:
cfg.video.width.set(2560);
cfg.video.height.set(1440);
break;
case quality_preset::_2160p_30:
case quality_preset::_2160p_60:
cfg.video.width.set(3840);
cfg.video.height.set(2160);
break;
case quality_preset::custom:
break;
}
switch (preset)
{
case quality_preset::_720p_30:
case quality_preset::_1080p_30:
case quality_preset::_1440p_30:
case quality_preset::_2160p_30:
cfg.video.framerate.set(30);
break;
case quality_preset::_720p_60:
case quality_preset::_1080p_60:
case quality_preset::_1440p_60:
case quality_preset::_2160p_60:
cfg.video.framerate.set(60);
break;
case quality_preset::custom:
break;
}
switch (preset)
{
case quality_preset::_720p_30:
cfg.video.video_bps.set(4'000'000);
break;
case quality_preset::_720p_60:
cfg.video.video_bps.set(6'000'000);
break;
case quality_preset::_1080p_30:
cfg.video.video_bps.set(8'000'000);
break;
case quality_preset::_1080p_60:
cfg.video.video_bps.set(12'000'000);
break;
case quality_preset::_1440p_30:
cfg.video.video_bps.set(16'000'000);
break;
case quality_preset::_1440p_60:
cfg.video.video_bps.set(24'000'000);
break;
case quality_preset::_2160p_30:
cfg.video.video_bps.set(40'000'000);
break;
case quality_preset::_2160p_60:
cfg.video.video_bps.set(60'000'000);
break;
case quality_preset::custom:
break;
}
cfg.video.gop_size.set(cfg.video.framerate.get());
cfg.video.max_b_frames.set(2);
}
recording_settings_dialog::quality_preset recording_settings_dialog::current_preset()
{
for (u32 i = 0; i < static_cast<u32>(quality_preset::custom); i++)
{
const quality_preset preset = static_cast<quality_preset>(i);
cfg_recording cfg;
select_preset(preset, cfg);
if (g_cfg_recording.video.framerate.get() == cfg.video.framerate.get() &&
g_cfg_recording.video.width.get() == cfg.video.width.get() &&
g_cfg_recording.video.height.get() == cfg.video.height.get() &&
g_cfg_recording.video.pixel_format.get() == cfg.video.pixel_format.get() &&
g_cfg_recording.video.video_codec.get() == cfg.video.video_codec.get() &&
g_cfg_recording.video.video_bps.get() == cfg.video.video_bps.get() &&
g_cfg_recording.video.max_b_frames.get() == cfg.video.max_b_frames.get() &&
g_cfg_recording.video.gop_size.get() == cfg.video.gop_size.get() &&
g_cfg_recording.audio.audio_codec.get() == cfg.audio.audio_codec.get() &&
g_cfg_recording.audio.audio_bps.get() == cfg.audio.audio_bps.get())
{
return preset;
}
}
return quality_preset::custom;
}

View file

@ -0,0 +1,47 @@
#pragma once
#include "util/types.hpp"
#include "Emu/Io/recording_config.h"
#include <QDialog>
namespace Ui
{
class recording_settings_dialog;
}
struct AVCodec;
class recording_settings_dialog : public QDialog
{
Q_OBJECT
public:
recording_settings_dialog(QWidget* parent = nullptr);
virtual ~recording_settings_dialog();
private:
enum class quality_preset
{
_720p_30,
_720p_60,
_1080p_30,
_1080p_60,
_1440p_30,
_1440p_60,
_2160p_30,
_2160p_60,
custom
};
void update_preset();
void update_ui();
static void select_preset(quality_preset preset, cfg_recording& cfg);
static quality_preset current_preset();
Ui::recording_settings_dialog* ui;
std::vector<const AVCodec*> m_video_codecs;
std::vector<const AVCodec*> m_audio_codecs;
};

View file

@ -0,0 +1,273 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>recording_settings_dialog</class>
<widget class="QDialog" name="recording_settings_dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>692</width>
<height>734</height>
</rect>
</property>
<property name="windowTitle">
<string>Recording Settings</string>
</property>
<layout class="QVBoxLayout" name="main_layout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="presets_tab">
<attribute name="title">
<string>Presets</string>
</attribute>
<layout class="QVBoxLayout" name="layout_presets_tab">
<item>
<widget class="QGroupBox" name="gb_presets">
<property name="title">
<string>Preset</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_presets">
<item>
<widget class="QComboBox" name="combo_presets"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_info">
<property name="title">
<string>Info</string>
</property>
<layout class="QHBoxLayout" name="layout_gb_info">
<item>
<widget class="QLabel" name="label_info_keys">
<property name="text">
<string>Keys</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_info_values">
<property name="text">
<string>Values</string>
</property>
</widget>
</item>
<item>
<spacer name="info_spacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="spacer_presets_tab">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="advanced_tab">
<attribute name="title">
<string>Advanced</string>
</attribute>
<layout class="QVBoxLayout" name="layout_advanced_tab">
<item>
<widget class="QGroupBox" name="gb_advanced_video">
<property name="title">
<string>Video</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_advanced_video">
<item>
<widget class="QGroupBox" name="gb_video_codec">
<property name="title">
<string>Codec</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_video_codec">
<item>
<widget class="QComboBox" name="combo_video_codec"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_resolution">
<property name="title">
<string>Resolution</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_resolution">
<item>
<widget class="QComboBox" name="combo_resolution"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_framerate">
<property name="title">
<string>Framerate</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_framerate">
<item>
<widget class="QComboBox" name="combo_framerate"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_video_bitrate">
<property name="title">
<string>Bitrate</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_video_bitrate">
<item>
<widget class="QSpinBox" name="spinbox_video_bitrate"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_gop_size">
<property name="title">
<string>Group of Pictures Size</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_gop_size">
<item>
<widget class="QSpinBox" name="spinbox_gop_size"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_max_b_frames">
<property name="title">
<string>Max. B-Frames</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_max_b_frames">
<item>
<widget class="QSpinBox" name="spinbox_max_b_frames"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_advanced_audio">
<property name="title">
<string>Audio</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_advanced_audio">
<item>
<widget class="QGroupBox" name="gb_audio_codec">
<property name="title">
<string>Codec</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_audio_codec">
<item>
<widget class="QComboBox" name="combo_audio_codec"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_audio_bitrate">
<property name="title">
<string>Bitrate</string>
</property>
<layout class="QVBoxLayout" name="layout_gb_audio_bitrate">
<item>
<widget class="QSpinBox" name="spinbox_audio_bitrate"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="spacer_advanced_tab">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::RestoreDefaults|QDialogButtonBox::StandardButton::Save</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>recording_settings_dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>recording_settings_dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -575,7 +575,7 @@ void savestate_manager_dialog::ShowGameTableContextMenu(const QPoint& pos)
if (!name.isEmpty())
{
QAction* copy_name = new QAction(tr("&Copy Name"), menu);
connect(copy_name, &QAction::triggered, this, [this, name]()
connect(copy_name, &QAction::triggered, this, [name]()
{
QApplication::clipboard()->setText(name);
});

View file

@ -1,26 +1,40 @@
#include "screenshot_item.h"
#include "qt_utils.h"
#include "Utilities/Thread.h"
#include <QVBoxLayout>
screenshot_item::screenshot_item(QWidget* parent)
screenshot_item::screenshot_item(QWidget* parent, QSize icon_size, const QString& icon_path, const QPixmap& placeholder)
: flow_widget_item(parent)
, m_icon_path(icon_path)
, m_icon_size(icon_size)
{
setToolTip(icon_path);
cb_on_first_visibility = [this]()
{
m_thread.reset(QThread::create([this]()
{
const QPixmap pixmap = gui::utils::get_aligned_pixmap(icon_path, icon_size, 1.0, Qt::SmoothTransformation, gui::utils::align_h::center, gui::utils::align_v::center);
thread_base::set_name("Screenshot item");
const QPixmap src_icon = QPixmap(m_icon_path);
if (src_icon.isNull()) return;
const QPixmap pixmap = gui::utils::get_aligned_pixmap(src_icon, m_icon_size, 1.0, Qt::SmoothTransformation, gui::utils::align_h::center, gui::utils::align_v::center);
Q_EMIT signal_icon_update(pixmap);
}));
m_thread->start();
};
label = new QLabel(this);
m_label = new QLabel(this);
m_label->setPixmap(placeholder);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(label);
layout->addWidget(m_label);
setLayout(layout);
connect(this, &screenshot_item::signal_icon_update, this, &screenshot_item::update_icon, Qt::ConnectionType::QueuedConnection);
}
screenshot_item::~screenshot_item()
@ -30,3 +44,23 @@ screenshot_item::~screenshot_item()
m_thread->wait();
}
}
void screenshot_item::update_icon(const QPixmap& pixmap)
{
if (m_label)
{
m_label->setPixmap(pixmap);
}
}
void screenshot_item::mouseDoubleClickEvent(QMouseEvent* ev)
{
flow_widget_item::mouseDoubleClickEvent(ev);
if (!ev) return;
if (ev->button() == Qt::LeftButton)
{
Q_EMIT signal_icon_preview(m_icon_path);
}
}

View file

@ -3,22 +3,29 @@
#include "flow_widget_item.h"
#include <QLabel>
#include <QThread>
#include <QMouseEvent>
class screenshot_item : public flow_widget_item
{
Q_OBJECT
public:
screenshot_item(QWidget* parent);
screenshot_item(QWidget* parent, QSize icon_size, const QString& icon_path, const QPixmap& placeholder);
virtual ~screenshot_item();
QString icon_path;
QSize icon_size;
QLabel* label{};
private:
QLabel* m_label{};
QString m_icon_path;
QSize m_icon_size;
std::unique_ptr<QThread> m_thread;
protected:
void mouseDoubleClickEvent(QMouseEvent* ev) override;
Q_SIGNALS:
void signal_icon_update(const QPixmap& pixmap);
void signal_icon_preview(const QString& path);
public Q_SLOTS:
void update_icon(const QPixmap& pixmap);
};

View file

@ -10,13 +10,15 @@
#include <QApplication>
#include <QDir>
#include <QDirIterator>
#include <QGroupBox>
#include <QScreen>
#include <QVBoxLayout>
#include <QtConcurrent>
LOG_CHANNEL(gui_log, "GUI");
screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog(parent)
screenshot_manager_dialog::screenshot_manager_dialog(const std::vector<game_info>& games, QWidget* parent)
: QDialog(parent), m_games(games)
{
setWindowTitle(tr("Screenshots"));
setAttribute(Qt::WA_DeleteOnClose);
@ -28,11 +30,50 @@ screenshot_manager_dialog::screenshot_manager_dialog(QWidget* parent) : QDialog(
m_placeholder = QPixmap(m_icon_size);
m_placeholder.fill(Qt::gray);
connect(this, &screenshot_manager_dialog::signal_icon_preview, this, &screenshot_manager_dialog::show_preview);
connect(this, &screenshot_manager_dialog::signal_entry_parsed, this, &screenshot_manager_dialog::add_entry);
connect(this, &screenshot_manager_dialog::signal_entry_parsed, this, &screenshot_manager_dialog::add_entry, Qt::ConnectionType::QueuedConnection);
QVBoxLayout* layout = new QVBoxLayout;
m_combo_sort_filter = new QComboBox();
m_combo_sort_filter->setSizeAdjustPolicy(QComboBox::AdjustToContents);
m_combo_sort_filter->addItem(tr("Sort by Game"), static_cast<int>(sort_filter::game));
m_combo_sort_filter->addItem(tr("Sort by Date"), static_cast<int>(sort_filter::date));
connect(m_combo_sort_filter, &QComboBox::currentIndexChanged, this, [this](int /*index*/){ reload(); });
m_combo_type_filter = new QComboBox();
m_combo_type_filter->setSizeAdjustPolicy(QComboBox::AdjustToContents);
m_combo_type_filter->addItem(tr("All Screenshots"), static_cast<int>(type_filter::all));
m_combo_type_filter->addItem(tr("RPCS3 Screenshots"), static_cast<int>(type_filter::rpcs3));
m_combo_type_filter->addItem(tr("Cell Screenshots"), static_cast<int>(type_filter::cell));
connect(m_combo_type_filter, &QComboBox::currentIndexChanged, this, [this](int /*index*/){ reload(); });
m_combo_game_filter = new QComboBox();
m_combo_game_filter->setSizeAdjustPolicy(QComboBox::AdjustToContents);
m_combo_game_filter->addItem(tr("All Games"), QString());
connect(m_combo_game_filter, &QComboBox::currentIndexChanged, this, [this](int /*index*/){ reload(); });
QHBoxLayout* sort_layout = new QHBoxLayout();
sort_layout->addWidget(m_combo_sort_filter);
QGroupBox* gb_sort = new QGroupBox(tr("Sort"));
gb_sort->setLayout(sort_layout);
QHBoxLayout* type_layout = new QHBoxLayout();
type_layout->addWidget(m_combo_type_filter);
QGroupBox* gb_type = new QGroupBox(tr("Filter Type"));
gb_type->setLayout(type_layout);
QHBoxLayout* game_layout = new QHBoxLayout();
game_layout->addWidget(m_combo_game_filter);
QGroupBox* gb_game = new QGroupBox(tr("Filter Game"));
gb_game->setLayout(game_layout);
QHBoxLayout* top_layout = new QHBoxLayout();
top_layout->addWidget(gb_sort);
top_layout->addWidget(gb_type);
top_layout->addWidget(gb_game);
top_layout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Minimum));
QVBoxLayout* layout = new QVBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
layout->addLayout(top_layout);
layout->addWidget(m_flow_widget);
setLayout(layout);
@ -47,14 +88,8 @@ screenshot_manager_dialog::~screenshot_manager_dialog()
void screenshot_manager_dialog::add_entry(const QString& path)
{
screenshot_item* item = new screenshot_item(m_flow_widget);
ensure(item->label);
item->setToolTip(path);
item->installEventFilter(this);
item->label->setPixmap(m_placeholder);
item->icon_path = path;
item->icon_size = m_icon_size;
connect(item, &screenshot_item::signal_icon_update, this, &screenshot_manager_dialog::update_icon);
screenshot_item* item = new screenshot_item(m_flow_widget, m_icon_size, path, m_placeholder);
connect(item, &screenshot_item::signal_icon_preview, this, &screenshot_manager_dialog::show_preview);
m_flow_widget->add_widget(item);
}
@ -65,28 +100,74 @@ void screenshot_manager_dialog::show_preview(const QString& path)
preview->show();
}
void screenshot_manager_dialog::update_icon(const QPixmap& pixmap)
{
if (screenshot_item* item = static_cast<screenshot_item*>(QObject::sender()))
{
if (item->label)
{
item->label->setPixmap(pixmap);
}
}
}
void screenshot_manager_dialog::reload()
{
m_abort_parsing = true;
m_parsing_watcher.disconnect();
gui::utils::stop_future_watcher(m_parsing_watcher, true);
const std::string screenshot_path_qt = fs::get_config_dir() + "screenshots/";
const std::string screenshot_path_cell = rpcs3::utils::get_hdd0_dir() + "/photo/";
const type_filter t_filter = static_cast<type_filter>(m_combo_type_filter->currentData().toInt());
const sort_filter s_filter = static_cast<sort_filter>(m_combo_sort_filter->currentData().toInt());
const QString game_filter = m_combo_game_filter->currentData().toString();
const std::string screenshot_path_rpcs3 = fs::get_config_dir() + "screenshots/";
const std::string screenshot_path_cell = rpcs3::utils::get_hdd0_dir() + "/photo/";
std::vector<std::string> folders;
switch (t_filter)
{
case type_filter::all:
folders.push_back(screenshot_path_rpcs3);
folders.push_back(screenshot_path_cell);
break;
case type_filter::rpcs3:
folders.push_back(screenshot_path_rpcs3);
break;
case type_filter::cell:
folders.push_back(screenshot_path_cell);
break;
}
m_flow_widget->clear();
m_game_folders.clear();
m_abort_parsing = false;
m_parsing_watcher.setFuture(QtConcurrent::map(m_parsing_threads, [this, screenshot_path_qt, screenshot_path_cell](int index)
connect(&m_parsing_watcher, &QFutureWatcher<void>::finished, this, [this]()
{
std::vector<std::pair<QString, QString>> games;
for (const auto& [dirname, paths] : m_game_folders)
{
const std::string serial = dirname.toStdString();
std::string text = serial;
for (const auto& game : m_games)
{
if (game && game->info.serial == serial)
{
text = fmt::format("%s (%s)", game->info.name, serial);
break;
}
}
games.push_back(std::pair(dirname, QString::fromStdString(text)));
}
std::sort(games.begin(), games.end(), [](const std::pair<QString, QString>& l, const std::pair<QString, QString>& r)
{
return l.second < r.second;
});
const QString old_filter = m_combo_game_filter->currentData().toString();
m_combo_game_filter->blockSignals(true);
m_combo_game_filter->clear();
m_combo_game_filter->addItem(tr("All Games"), QString());
for (const auto& [dirname, text] : games)
{
m_combo_game_filter->addItem(text, dirname);
}
m_combo_game_filter->setCurrentIndex(m_combo_game_filter->findData(old_filter));
m_combo_game_filter->blockSignals(false);
});
m_parsing_watcher.setFuture(QtConcurrent::map(m_parsing_threads, [this, folders, game_filter, s_filter](int index)
{
if (index != 0)
{
@ -95,26 +176,68 @@ void screenshot_manager_dialog::reload()
const QStringList filter{ QStringLiteral("*.png") };
for (const std::string& path : { screenshot_path_qt, screenshot_path_cell })
for (const std::string& folder : folders)
{
if (m_abort_parsing)
{
return;
}
if (path.empty())
if (folder.empty())
{
gui_log.error("Screenshot manager: Trying to load screenshots from empty path!");
continue;
}
QDirIterator dir_iter(QString::fromStdString(path), filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
QDirIterator dir_iter(QString::fromStdString(folder), filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
while (dir_iter.hasNext() && !m_abort_parsing)
{
Q_EMIT signal_entry_parsed(dir_iter.next());
QFileInfo info(dir_iter.next());
const QString dirname = info.dir().dirName();
m_game_folders[dirname].push_back(std::move(info));
}
}
switch (s_filter)
{
case sort_filter::game:
{
for (const auto& [dirname, infos] : m_game_folders)
{
if (game_filter.isEmpty() || game_filter == dirname)
{
for (const QFileInfo& info : infos)
{
Q_EMIT signal_entry_parsed(info.filePath());
}
}
}
break;
}
case sort_filter::date:
{
std::vector<QFileInfo> sorted_infos;
for (const auto& [dirname, infos] : m_game_folders)
{
if (game_filter.isEmpty() || game_filter == dirname)
{
sorted_infos.insert(sorted_infos.end(), infos.begin(), infos.end());
}
}
std::sort(sorted_infos.begin(), sorted_infos.end(), [](const QFileInfo& a, const QFileInfo& b)
{
return a.lastModified() < b.lastModified();
});
for (const QFileInfo& info : sorted_infos)
{
Q_EMIT signal_entry_parsed(info.filePath());
}
break;
}
}
}));
}
@ -123,17 +246,3 @@ void screenshot_manager_dialog::showEvent(QShowEvent* event)
QDialog::showEvent(event);
reload();
}
bool screenshot_manager_dialog::eventFilter(QObject* watched, QEvent* event)
{
if (event && event->type() == QEvent::MouseButtonDblClick && static_cast<QMouseEvent*>(event)->button() == Qt::LeftButton)
{
if (screenshot_item* item = static_cast<screenshot_item*>(watched))
{
Q_EMIT signal_icon_preview(item->icon_path);
return true;
}
}
return false;
}

View file

@ -1,30 +1,29 @@
#pragma once
#include "flow_widget.h"
#include "gui_game_info.h"
#include <QComboBox>
#include <QDialog>
#include <QFileInfo>
#include <QFutureWatcher>
#include <QPixmap>
#include <QSize>
#include <QEvent>
#include <QShowEvent>
#include <array>
#include <vector>
#include <map>
class screenshot_manager_dialog : public QDialog
{
Q_OBJECT
public:
screenshot_manager_dialog(QWidget* parent = nullptr);
screenshot_manager_dialog(const std::vector<game_info>& games, QWidget* parent = nullptr);
~screenshot_manager_dialog();
bool eventFilter(QObject* watched, QEvent* event) override;
Q_SIGNALS:
void signal_entry_parsed(const QString& path);
void signal_icon_preview(const QString& path);
public Q_SLOTS:
void update_icon(const QPixmap& pixmap);
private Q_SLOTS:
void add_entry(const QString& path);
@ -36,11 +35,28 @@ protected:
private:
void reload();
enum class type_filter
{
all,
rpcs3,
cell
};
enum class sort_filter
{
game,
date
};
std::vector<game_info> m_games;
bool m_abort_parsing = false;
const std::array<int, 1> m_parsing_threads{0};
QFutureWatcher<void> m_parsing_watcher;
flow_widget* m_flow_widget = nullptr;
QComboBox* m_combo_sort_filter = nullptr;
QComboBox* m_combo_game_filter = nullptr;
QComboBox* m_combo_type_filter = nullptr;
QSize m_icon_size;
QPixmap m_placeholder;
std::map<QString, std::vector<QFileInfo>> m_game_folders;
};

View file

@ -858,7 +858,7 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
ui->vulkansched->setEnabled(is_vulkan);
};
const auto apply_fsr_specific_options = [r_creator, this]()
const auto apply_fsr_specific_options = [this]()
{
const auto [text, value] = get_data(ui->outputScalingMode, ui->outputScalingMode->currentIndex());
const bool fsr_selected = static_cast<output_scaling_mode>(value) == output_scaling_mode::fsr;
@ -2095,7 +2095,7 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
connect(ui->edit_button_game_window_title_format, &QAbstractButton::clicked, [get_game_window_title, set_game_window_title, this]()
{
auto get_game_window_title_label = [get_game_window_title, set_game_window_title, this](const QString& format)
auto get_game_window_title_label = [get_game_window_title](const QString& format)
{
const QString game_window_title = get_game_window_title(format);

View file

@ -41,7 +41,7 @@ sound_effect_manager_dialog::sound_effect_manager_dialog(QWidget* parent)
}
QPushButton* button = new QPushButton("", this);
connect(button, &QAbstractButton::clicked, this, [this, button, sound, name]()
connect(button, &QAbstractButton::clicked, this, [this, sound, name]()
{
const std::string path = rsx::overlays::get_sound_filepath(sound);
if (fs::is_file(path))

View file

@ -71,6 +71,8 @@ namespace gui::utils
}
const std::vector<u8> data = vdf.to_vector<u8>();
vdf.close();
usz last_pos = 0;
usz pos = 0;
@ -507,9 +509,17 @@ namespace gui::utils
sys_log.success("Removed steam shortcut(s) for '%s'", entry.app_name);
}
update_steam_input_config(user_dir);
return true;
}
bool steam_shortcut::steam_installed()
{
const std::string path = get_steam_path();
return !path.empty() && fs::is_dir(path);
}
u32 steam_shortcut::crc32(const std::string& data)
{
u32 crc = 0xFFFFFFFF;
@ -527,12 +537,6 @@ namespace gui::utils
return ~crc;
}
bool steam_shortcut::steam_installed()
{
const std::string path = get_steam_path();
return !path.empty() && fs::is_dir(path);
}
u32 steam_shortcut::steam_appid(const std::string& exe, const std::string& name)
{
return crc32(exe + name) | 0x80000000;
@ -667,8 +671,177 @@ namespace gui::utils
return str;
}
void steam_shortcut::update_steam_input_config(const std::string& user_dir)
{
if (m_entries_to_add.empty() && m_entries_to_remove.empty())
{
return;
}
const std::string vdf_path = user_dir + "localconfig.vdf";
const std::string backup_path = fs::get_config_dir() + "/localconfig.vdf.backup";
if (fs::is_file(vdf_path) && !fs::copy_file(vdf_path, backup_path, true))
{
sys_log.error("Failed to backup steam localconfig file '%s'", vdf_path);
return;
}
fs::file vdf(vdf_path);
if (!vdf)
{
sys_log.error("update_steam_input_config: Failed to open steam localconfig file '%s': %s", vdf_path, fs::g_tls_error);
return;
}
std::string content = vdf.to_string();
vdf.close();
static const std::string app_section_start = "\n\t\"apps\"\n\t{";
static const std::string app_section_end = "\n\t}\n";
static const std::string entry_section_end = "\n\t\t}";
bool nothing_to_remove = m_entries_to_remove.empty();
usz app_pos = content.rfind(app_section_start);
if (app_pos == umax)
{
if (!nothing_to_remove)
{
// We don't have to remove anything because this section did not exist
sys_log.notice("update_steam_input_config: Could not find \"apps\" section. No need to remove anything.");
nothing_to_remove = true;
}
if (m_entries_to_add.empty())
{
return; // Nothing to do anyway
}
const usz insert_pos = content.rfind("\n}");
if (insert_pos == umax)
{
sys_log.error("update_steam_input_config: Could not find main section end");
return;
}
sys_log.notice("update_steam_input_config: Inserting missing \"apps\" section");
content.insert(insert_pos, fmt::format("%s\n\t}", app_section_start));
app_pos = content.rfind(app_section_start);
ensure(app_pos != umax);
}
const usz search_start = app_pos + app_section_start.size();
const usz insert_pos = content.find(app_section_end, search_start);
if (insert_pos == umax)
{
sys_log.error("update_steam_input_config: Could not find apps section end");
return;
}
const auto appid_string = [](s32 signed_appid)
{
return fmt::format("\n\t\t\"%d\"\n", signed_appid);
};
const auto find_entry = [&content, &appid_string, search_start, insert_pos](s32 signed_appid) -> usz
{
const usz pos = content.find(appid_string(signed_appid), search_start);
if (pos >= insert_pos) return umax;
return pos;
};
bool dirty = false;
for (const shortcut_entry& entry : m_entries_to_remove)
{
constexpr bool removal_diabled = true; // Disabled for now. Steam doesn't seem to remove the entries either
if constexpr (removal_diabled)
{
break;
}
if (nothing_to_remove)
{
break;
}
const s32 signed_appid = static_cast<s32>(entry.appid);
const usz pos = find_entry(signed_appid);
if (pos == umax)
{
// does not exist, do nothing
sys_log.notice("update_steam_input_config: Entry for '%s' with appid '%d' does not exist. Skipping removal", entry.app_name, signed_appid);
continue;
}
// Find the opening brace of this entry
const usz pos_brace_open = content.find('{', pos);
if (pos_brace_open == umax || pos_brace_open >= insert_pos)
{
sys_log.error("update_steam_input_config: Can't find opening brace for entry for '%s' with appid '%d'.", entry.app_name, signed_appid);
continue;
}
// Find the closing brace
const usz pos_brace_close = content.find(entry_section_end, pos_brace_open);
if (pos_brace_close == umax || pos_brace_close >= insert_pos)
{
sys_log.error("update_steam_input_config: Can't find closing brace for entry for '%s' with appid '%d'.", entry.app_name, signed_appid);
continue;
}
// Include the closing brace line
const usz erase_end = pos_brace_close + entry_section_end.size();
// Erase the whole block
content.erase(pos, erase_end - pos);
sys_log.notice("update_steam_input_config: Removed '%s' with appid '%d'", entry.app_name, signed_appid);
dirty = true;
}
for (const shortcut_entry& entry : m_entries_to_add)
{
const s32 signed_appid = static_cast<s32>(entry.appid);
const usz pos = find_entry(signed_appid);
if (pos != umax)
{
// already exists, do nothing
sys_log.notice("update_steam_input_config: Entry for '%s' with appid '%d' already exists", entry.app_name, signed_appid);
continue;
}
sys_log.notice("update_steam_input_config: Inserting '%s' with appid '%d'", entry.app_name, signed_appid);
content.insert(insert_pos, fmt::format(
"%s"
"\t\t{\n"
"\t\t\t\"UseSteamControllerConfig\"\t\t\"0\"\n"
//"\t\t\t\"SteamControllerRumble\"\t\t\"-1\"\n"
//"\t\t\t\"SteamControllerRumbleIntensity\"\t\t\"320\"\n"
"\t\t}", appid_string(signed_appid)));
dirty = true;
}
if (dirty && !fs::write_file(vdf_path, fs::rewrite, content))
{
sys_log.error("Failed to update steam localconfig '%s': '%s'", vdf_path, fs::g_tls_error);
if (!fs::copy_file(backup_path, vdf_path, true))
{
sys_log.error("Failed to restore steam localconfig backup: '%s'", fs::g_tls_error);
}
}
}
#ifdef _WIN32
std::string get_registry_string(const wchar_t* key, const wchar_t* name)
static std::string get_registry_string(const wchar_t* key, const wchar_t* name)
{
HKEY hkey = NULL;
LSTATUS status = RegOpenKeyW(HKEY_CURRENT_USER, key, &hkey);
@ -832,6 +1005,7 @@ namespace gui::utils
// }
const std::string content = vdf.to_string();
vdf.close();
usz user_count = 0;

View file

@ -103,6 +103,8 @@ namespace gui::utils
bool parse_file(const std::string& path);
void update_steam_input_config(const std::string& user_dir);
static u32 crc32(const std::string& data);
static u32 steam_appid(const std::string& exe, const std::string& name);
@ -115,7 +117,7 @@ namespace gui::utils
static std::string steamid64_to_32(const std::string& steam_id);
static std::string get_steam_path();
static std::string get_last_active_steam_user(const std::string& steam_path);
static std::string get_steam_banner_path(steam_banner banner, const std::string& grid_dir, u32 appid);
static void create_steam_banner(steam_banner banner, const std::string& src_path, const QPixmap& src_icon, const std::string& grid_dir, const shortcut_entry& entry);

View file

@ -392,7 +392,7 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
m_trophy_table->create_header_actions(m_trophy_column_acts,
[this](int col) { return m_gui_settings->GetTrophylistColVisibility(static_cast<gui::trophy_list_columns>(col)); },
[this](int col, bool visible) { m_gui_settings->SetTrophylistColVisibility(static_cast<gui::trophy_list_columns>(col), visible); });
m_game_table->create_header_actions(m_game_column_acts,
[this](int col) { return m_gui_settings->GetTrophyGamelistColVisibility(static_cast<gui::trophy_game_list_columns>(col)); },
[this](int col, bool visible) { m_gui_settings->SetTrophyGamelistColVisibility(static_cast<gui::trophy_game_list_columns>(col), visible); });
@ -651,7 +651,7 @@ void trophy_manager_dialog::ResizeGameIcons()
const int trophy_index = item->data(GameUserRole::GameIndex).toInt();
QString trophy_icon_path = QString::fromStdString(m_trophies_db[trophy_index]->path);
item->set_icon_load_func([this, icon_path = std::move(trophy_icon_path), localized_icon, trophy_index, cancel = item->icon_loading_aborted(), dpr](int index)
item->set_icon_load_func([this, icon_path = std::move(trophy_icon_path), localized_icon, cancel = item->icon_loading_aborted(), dpr](int index)
{
if (cancel && cancel->load())
{
@ -897,7 +897,7 @@ void trophy_manager_dialog::ShowTrophyTableContextMenu(const QPoint& pos)
if (!name.isEmpty() && !desc.isEmpty())
{
QAction* copy_both = new QAction(tr("&Copy Name + Description"), copy_menu);
connect(copy_both, &QAction::triggered, this, [this, name, desc]()
connect(copy_both, &QAction::triggered, this, [name, desc]()
{
QApplication::clipboard()->setText(name % QStringLiteral("\n\n") % desc);
});
@ -907,7 +907,7 @@ void trophy_manager_dialog::ShowTrophyTableContextMenu(const QPoint& pos)
if (!name.isEmpty())
{
QAction* copy_name = new QAction(tr("&Copy Name"), copy_menu);
connect(copy_name, &QAction::triggered, this, [this, name]()
connect(copy_name, &QAction::triggered, this, [name]()
{
QApplication::clipboard()->setText(name);
});
@ -917,7 +917,7 @@ void trophy_manager_dialog::ShowTrophyTableContextMenu(const QPoint& pos)
if (!desc.isEmpty())
{
QAction* copy_desc = new QAction(tr("&Copy Description"), copy_menu);
connect(copy_desc, &QAction::triggered, this, [this, desc]()
connect(copy_desc, &QAction::triggered, this, [desc]()
{
QApplication::clipboard()->setText(desc);
});
@ -1038,7 +1038,7 @@ void trophy_manager_dialog::ShowGameTableContextMenu(const QPoint& pos)
if (!name.isEmpty())
{
QAction* copy_name = new QAction(tr("&Copy Name"), menu);
connect(copy_name, &QAction::triggered, this, [this, name]()
connect(copy_name, &QAction::triggered, this, [name]()
{
QApplication::clipboard()->setText(name);
});

View file

@ -23,7 +23,6 @@
#include <QJsonObject>
#include <QJsonDocument>
#include <QTextBrowser>
#include <QThread>
#if defined(_WIN32) || defined(__APPLE__)
#include <7z.h>

View file

@ -81,7 +81,7 @@ vfs_dialog_path_widget::vfs_dialog_path_widget(const QString& name, const QStrin
item->setCheckState(Qt::CheckState::Checked);
});
connect(m_dir_list, &QListWidget::currentRowChanged, this, [this, button_remove_dir](int row)
connect(m_dir_list, &QListWidget::currentRowChanged, this, [button_remove_dir](int row)
{
button_remove_dir->setEnabled(row > 0);
});

View file

@ -743,7 +743,26 @@ std::pair<u64, u64> utils::get_memory_usage()
status.dwLength = sizeof(status);
::GlobalMemoryStatusEx(&status);
return { status.ullTotalPhys, status.ullTotalPhys - status.ullAvailPhys };
#elif __linux__
std::ifstream proc("/proc/meminfo");
std::string line;
uint64_t mem_total = get_total_memory();
uint64_t mem_available = 0;
while (std::getline(proc, line))
{
if (line.rfind("MemTotal:", 0) == 0 && line.find("kB") != std::string::npos)
{
mem_total = std::stoull(line.substr(line.find_first_of("0123456789"))) * 1024;
}
else if (line.rfind("MemAvailable:", 0) == 0 && line.find("kB") != std::string::npos)
{
mem_available = std::stoull(line.substr(line.find_first_of("0123456789"))) * 1024;
break;
}
}
return { mem_total, mem_total - mem_available };
#else
// TODO
return { get_total_memory(), 0 };