mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-03-10 23:45:16 +01:00
Merge branch 'master' into patch-3
This commit is contained in:
commit
0323397dee
6
.github/workflows/rpcs3.yml
vendored
6
.github/workflows/rpcs3.yml
vendored
|
|
@ -134,7 +134,7 @@ jobs:
|
|||
runs-on: macos-14
|
||||
env:
|
||||
CCACHE_DIR: /tmp/ccache_dir
|
||||
QT_VER: '6.10.1'
|
||||
QT_VER: '6.10.2'
|
||||
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.1'
|
||||
QT_VER: '6.10.2'
|
||||
QT_VER_MSVC: 'msvc2022'
|
||||
QT_DATE: '202511161843'
|
||||
QT_DATE: '202601261212'
|
||||
LLVM_VER: '19.1.7'
|
||||
VULKAN_VER: '1.3.268.0'
|
||||
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
|
||||
|
|
|
|||
2
3rdparty/libpng/libpng
vendored
2
3rdparty/libpng/libpng
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 4e3f57d50f552841550a36eabbb3fbcecacb7750
|
||||
Subproject commit 02f2b4f4699f0ef9111a6534f093b53732df4452
|
||||
10
BUILDING.md
10
BUILDING.md
|
|
@ -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.1](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.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)
|
||||
- [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.1\msvc2022_64\`
|
||||
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.10.2\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.1\msvc2022_64\`
|
||||
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.10.2\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.1](https://www.qt.io/download-qt-installer)
|
||||
- [Qt 6.10.2](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.1\msvc2022_64`, version will fill in automatically
|
||||
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.10.2\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**)
|
||||
|
||||
|
|
|
|||
|
|
@ -688,6 +688,30 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
|
|||
mem = std::make_unique<MemoryManager1>(std::move(symbols_cement));
|
||||
}
|
||||
|
||||
std::vector<std::string> attributes;
|
||||
|
||||
#if defined(ARCH_ARM64)
|
||||
if (utils::has_sha3())
|
||||
attributes.push_back("+sha3");
|
||||
else
|
||||
attributes.push_back("-sha3");
|
||||
|
||||
if (utils::has_dotprod())
|
||||
attributes.push_back("+dotprod");
|
||||
else
|
||||
attributes.push_back("-dotprod");
|
||||
|
||||
if (utils::has_sve())
|
||||
attributes.push_back("+sve");
|
||||
else
|
||||
attributes.push_back("-sve");
|
||||
|
||||
if (utils::has_sve2())
|
||||
attributes.push_back("+sve2");
|
||||
else
|
||||
attributes.push_back("-sve2");
|
||||
#endif
|
||||
|
||||
{
|
||||
m_engine.reset(llvm::EngineBuilder(std::move(null_mod))
|
||||
.setErrorStr(&result)
|
||||
|
|
@ -699,6 +723,7 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
|
|||
//.setCodeModel(llvm::CodeModel::Large)
|
||||
#endif
|
||||
.setRelocationModel(llvm::Reloc::Model::PIC_)
|
||||
.setMAttrs(attributes)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -634,7 +634,7 @@ u32 microphone_device::capture_audio()
|
|||
if (ALCenum err = alcGetError(micdevice.device); err != ALC_NO_ERROR)
|
||||
{
|
||||
cellMic.error("Error getting number of captured samples of device '%s' (error=%s)", micdevice.name, fmt::alc_error{micdevice.device, err});
|
||||
return CELL_MICIN_ERROR_FATAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
num_samples = std::min<u32>(num_samples, samples_in);
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ public:
|
|||
if (over_size > Size)
|
||||
{
|
||||
m_tail += (over_size - Size);
|
||||
if (m_tail > Size)
|
||||
if (m_tail >= Size)
|
||||
m_tail -= Size;
|
||||
|
||||
m_used = Size;
|
||||
|
|
|
|||
|
|
@ -556,6 +556,8 @@ usb_handler_thread::usb_handler_thread()
|
|||
|
||||
switch (g_cfg.audio.microphone_type)
|
||||
{
|
||||
case microphone_handler::null:
|
||||
break;
|
||||
case microphone_handler::standard:
|
||||
usb_devices.push_back(std::make_shared<usb_device_mic>(0, get_new_location(), MicType::Logitech));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ void MouseHandlerBase::save(utils::serial& ar)
|
|||
|
||||
bool MouseHandlerBase::is_time_for_update(double elapsed_time_ms)
|
||||
{
|
||||
steady_clock::time_point now = steady_clock::now();
|
||||
const double elapsed_ms = (now - last_update).count() / 1'000'000.;
|
||||
const steady_clock::time_point now = steady_clock::now();
|
||||
const double elapsed_ms = (now - m_last_update).count() / 1'000'000.;
|
||||
|
||||
if (elapsed_ms > elapsed_time_ms)
|
||||
{
|
||||
last_update = now;
|
||||
m_last_update = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ class MouseHandlerBase
|
|||
protected:
|
||||
MouseInfo m_info{};
|
||||
std::vector<Mouse> m_mice;
|
||||
steady_clock::time_point last_update{};
|
||||
steady_clock::time_point m_last_update{};
|
||||
|
||||
bool is_time_for_update(double elapsed_time_ms = 10.0); // 4-10 ms, let's use 10 for now
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ error_code generic_async_transaction_context::wait_for_completion()
|
|||
return *result;
|
||||
}
|
||||
|
||||
completion_cond.wait(lock);
|
||||
completion_cond.wait(lock, [this] { return result.has_value(); });
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -951,13 +951,16 @@ namespace np
|
|||
{
|
||||
thread_base::set_name("NP Trans Worker");
|
||||
|
||||
auto res = trans_ctx->wake_cond.wait_for(lock, std::chrono::microseconds(trans_ctx->timeout));
|
||||
bool has_value = trans_ctx->wake_cond.wait_for(lock, std::chrono::microseconds(trans_ctx->timeout), [&]
|
||||
{
|
||||
return trans_ctx->result.has_value();
|
||||
});
|
||||
{
|
||||
std::lock_guard lock_threads(this->mutex_async_transactions);
|
||||
this->async_transactions.erase(req_id);
|
||||
}
|
||||
|
||||
if (res == std::cv_status::timeout)
|
||||
if (!has_value)
|
||||
{
|
||||
trans_ctx->result = SCE_NP_COMMUNITY_ERROR_TIMEOUT;
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1467,7 +1467,7 @@ namespace rpcn
|
|||
return error;
|
||||
}
|
||||
|
||||
bool rpcn_client::add_friend(const std::string& friend_username)
|
||||
std::optional<ErrorType> rpcn_client::add_friend(const std::string& friend_username)
|
||||
{
|
||||
std::vector<u8> data;
|
||||
std::copy(friend_username.begin(), friend_username.end(), std::back_inserter(data));
|
||||
|
|
@ -1478,19 +1478,18 @@ namespace rpcn
|
|||
std::vector<u8> packet_data;
|
||||
if (!forge_send_reply(CommandType::AddFriend, req_id, data, packet_data))
|
||||
{
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vec_stream reply(packet_data);
|
||||
auto error = static_cast<ErrorType>(reply.get<u8>());
|
||||
const auto error = static_cast<ErrorType>(reply.get<u8>());
|
||||
|
||||
if (error != rpcn::ErrorType::NoError)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (error == ErrorType::NoError)
|
||||
rpcn_log.success("add_friend(\"%s\") succeeded", friend_username);
|
||||
else
|
||||
rpcn_log.error("add_friend(\"%s\") failed with error: %s", error);
|
||||
|
||||
rpcn_log.success("You have successfully added \"%s\" as a friend", friend_username);
|
||||
return true;
|
||||
return error;
|
||||
}
|
||||
|
||||
bool rpcn_client::remove_friend(const std::string& friend_username)
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ namespace rpcn
|
|||
ErrorType send_reset_token(std::string_view npid, std::string_view email);
|
||||
ErrorType reset_password(std::string_view npid, std::string_view token, std::string_view password);
|
||||
ErrorType delete_account();
|
||||
bool add_friend(const std::string& friend_username);
|
||||
std::optional<ErrorType> add_friend(const std::string& friend_username);
|
||||
bool remove_friend(const std::string& friend_username);
|
||||
|
||||
u32 get_num_friends();
|
||||
|
|
|
|||
|
|
@ -1351,7 +1351,7 @@ void GLGSRender::notify_tile_unbound(u32 tile)
|
|||
}
|
||||
}
|
||||
|
||||
bool GLGSRender::release_GCM_label(u32 address, u32 args)
|
||||
bool GLGSRender::release_GCM_label(u32 type, u32 address, u32 args)
|
||||
{
|
||||
if (!backend_config.supports_host_gpu_labels)
|
||||
{
|
||||
|
|
@ -1360,7 +1360,7 @@ bool GLGSRender::release_GCM_label(u32 address, u32 args)
|
|||
|
||||
auto host_ctx = ensure(m_host_dma_ctrl->host_ctx());
|
||||
|
||||
if (host_ctx->texture_loads_completed())
|
||||
if (type == NV4097_TEXTURE_READ_SEMAPHORE_RELEASE && host_ctx->texture_loads_completed())
|
||||
{
|
||||
// We're about to poll waiting for GPU state, ensure the context is still valid.
|
||||
gl::check_state();
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ public:
|
|||
void discard_occlusion_query(rsx::reports::occlusion_query_info* query) override;
|
||||
|
||||
// DMA
|
||||
bool release_GCM_label(u32 address, u32 data) override;
|
||||
bool release_GCM_label(u32 type, u32 address, u32 data) override;
|
||||
void enqueue_host_context_write(u32 offset, u32 size, const void* data);
|
||||
void on_guest_texture_read();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "Emu/RSX/RSXThread.h"
|
||||
#include "Emu/RSX/Core/RSXReservationLock.hpp"
|
||||
#include "Emu/RSX/Common/tiled_dma_copy.hpp"
|
||||
#include "Emu/RSX/Host/MM.h"
|
||||
|
||||
#include "context_accessors.define.h"
|
||||
|
||||
|
|
@ -581,9 +582,11 @@ namespace rsx
|
|||
const u16 out_h = REGS(ctx)->blit_engine_output_height();
|
||||
|
||||
// Lock here. RSX cannot execute any locking operations from this point, including ZCULL read barriers
|
||||
const u32 read_length = src.pitch * src.height;
|
||||
const u32 write_length = dst.pitch * dst.clip_height;
|
||||
auto res = ::rsx::reservation_lock<true>(
|
||||
dst.rsx_address, dst.pitch * dst.clip_height,
|
||||
src.rsx_address, src.pitch * src.height);
|
||||
dst.rsx_address, write_length,
|
||||
src.rsx_address, read_length);
|
||||
|
||||
if (!g_cfg.video.force_cpu_blit_processing &&
|
||||
(dst.dma == CELL_GCM_CONTEXT_DMA_MEMORY_FRAME_BUFFER || src.dma == CELL_GCM_CONTEXT_DMA_MEMORY_FRAME_BUFFER) &&
|
||||
|
|
@ -593,6 +596,14 @@ namespace rsx
|
|||
return;
|
||||
}
|
||||
|
||||
// Conservative MM flush
|
||||
rsx::simple_array<utils::address_range64> flush_mm_ranges =
|
||||
{
|
||||
utils::address_range64::start_length(reinterpret_cast<u64>(dst.pixels), write_length),
|
||||
utils::address_range64::start_length(reinterpret_cast<u64>(src.pixels), read_length)
|
||||
};
|
||||
rsx::mm_flush(flush_mm_ranges);
|
||||
|
||||
std::vector<u8> mirror_tmp;
|
||||
bool src_is_temp = false;
|
||||
|
||||
|
|
@ -619,7 +630,7 @@ namespace rsx
|
|||
const bool interpolate = in_inter == blit_engine::transfer_interpolator::foh;
|
||||
|
||||
auto real_dst = dst.pixels;
|
||||
const auto tiled_region = RSX(ctx)->get_tiled_memory_region(utils::address_range32::start_length(dst.rsx_address, dst.pitch * dst.clip_height));
|
||||
const auto tiled_region = RSX(ctx)->get_tiled_memory_region(utils::address_range32::start_length(dst.rsx_address, write_length));
|
||||
std::vector<u8> tmp;
|
||||
|
||||
if (tiled_region)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ namespace rsx
|
|||
RSX(ctx)->performance_counters.idle_time += (get_system_time() - start);
|
||||
}
|
||||
|
||||
void semaphore_release(context* ctx, u32 /*reg*/, u32 arg)
|
||||
void semaphore_release(context* ctx, u32 reg, u32 arg)
|
||||
{
|
||||
const u32 offset = REGS(ctx)->semaphore_offset_406e();
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ namespace rsx
|
|||
arg = 1;
|
||||
}
|
||||
|
||||
util::write_gcm_label<false, true>(ctx, addr, arg);
|
||||
util::write_gcm_label<false, true>(ctx, reg, addr, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -690,7 +690,7 @@ namespace rsx
|
|||
});
|
||||
}
|
||||
|
||||
void texture_read_semaphore_release(context* ctx, u32 /*reg*/, u32 arg)
|
||||
void texture_read_semaphore_release(context* ctx, u32 reg, u32 arg)
|
||||
{
|
||||
// Pipeline barrier seems to be equivalent to a SHADER_READ stage barrier.
|
||||
// Ideally the GPU only needs to have cached all textures declared up to this point before writing the label.
|
||||
|
|
@ -715,15 +715,15 @@ namespace rsx
|
|||
|
||||
if (g_cfg.video.strict_rendering_mode) [[ unlikely ]]
|
||||
{
|
||||
util::write_gcm_label<true, true>(ctx, addr, arg);
|
||||
util::write_gcm_label<true, true>(ctx, reg, addr, arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
util::write_gcm_label<true, false>(ctx, addr, arg);
|
||||
util::write_gcm_label<true, false>(ctx, reg, addr, arg);
|
||||
}
|
||||
}
|
||||
|
||||
void back_end_write_semaphore_release(context* ctx, u32 /*reg*/, u32 arg)
|
||||
void back_end_write_semaphore_release(context* ctx, u32 reg, u32 arg)
|
||||
{
|
||||
// Full pipeline barrier. GPU must flush pipeline before writing the label
|
||||
|
||||
|
|
@ -744,7 +744,7 @@ namespace rsx
|
|||
}
|
||||
|
||||
const u32 val = (arg & 0xff00ff00) | ((arg & 0xff) << 16) | ((arg >> 16) & 0xff);
|
||||
util::write_gcm_label<true, true>(ctx, addr, val);
|
||||
util::write_gcm_label<true, true>(ctx, reg, addr, val);
|
||||
}
|
||||
|
||||
void sync(context* ctx, u32, u32)
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ namespace rsx
|
|||
namespace util
|
||||
{
|
||||
template <bool FlushDMA, bool FlushPipe>
|
||||
static void write_gcm_label(context* ctx, u32 address, u32 data)
|
||||
static void write_gcm_label(context* ctx, u32 type, u32 address, u32 data)
|
||||
{
|
||||
const bool is_flip_sema = (address == (RSX(ctx)->label_addr + 0x10) || address == (RSX(ctx)->device_addr + 0x30));
|
||||
if (!is_flip_sema)
|
||||
{
|
||||
// First, queue the GPU work. If it flushes the queue for us, the following routines will be faster.
|
||||
const bool handled = RSX(ctx)->get_backend_config().supports_host_gpu_labels && RSX(ctx)->release_GCM_label(address, data);
|
||||
const bool handled = RSX(ctx)->get_backend_config().supports_host_gpu_labels && RSX(ctx)->release_GCM_label(type, address, data);
|
||||
|
||||
if (vm::_ref<RsxSemaphore>(address) == data)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ namespace glsl
|
|||
glsl_compute_program = 2,
|
||||
|
||||
// Meta
|
||||
glsl_invalid_program = 0xff
|
||||
glsl_invalid_program = 7
|
||||
};
|
||||
|
||||
enum glsl_rules : unsigned char
|
||||
|
|
|
|||
|
|
@ -380,7 +380,7 @@ namespace rsx
|
|||
flags32_t read_barrier(u32 memory_address, u32 memory_range, bool unconditional);
|
||||
virtual void write_barrier(u32 /*memory_address*/, u32 /*memory_range*/) {}
|
||||
virtual void sync_hint(FIFO::interrupt_hint hint, reports::sync_hint_payload_t payload);
|
||||
virtual bool release_GCM_label(u32 /*address*/, u32 /*value*/) { return false; }
|
||||
virtual bool release_GCM_label(u32 /*type*/, u32 /*address*/, u32 /*value*/) { return false; }
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
|||
|
|
@ -1541,7 +1541,7 @@ std::pair<volatile vk::host_data_t*, VkBuffer> VKGSRender::map_host_object_data(
|
|||
return { m_host_dma_ctrl->host_ctx(), m_host_object_data->value };
|
||||
}
|
||||
|
||||
bool VKGSRender::release_GCM_label(u32 address, u32 args)
|
||||
bool VKGSRender::release_GCM_label(u32 type, u32 address, u32 args)
|
||||
{
|
||||
if (!backend_config.supports_host_gpu_labels)
|
||||
{
|
||||
|
|
@ -1550,7 +1550,7 @@ bool VKGSRender::release_GCM_label(u32 address, u32 args)
|
|||
|
||||
auto host_ctx = ensure(m_host_dma_ctrl->host_ctx());
|
||||
|
||||
if (host_ctx->texture_loads_completed())
|
||||
if (type == NV4097_TEXTURE_READ_SEMAPHORE_RELEASE && host_ctx->texture_loads_completed())
|
||||
{
|
||||
// All texture loads already seen by the host GPU
|
||||
// Wait for all previously submitted labels to be flushed
|
||||
|
|
@ -1572,13 +1572,10 @@ bool VKGSRender::release_GCM_label(u32 address, u32 args)
|
|||
|
||||
const auto release_event_id = host_ctx->on_label_acquire();
|
||||
|
||||
vk::insert_global_memory_barrier(*m_current_command_buffer);
|
||||
|
||||
if (host_ctx->has_unflushed_texture_loads())
|
||||
{
|
||||
if (vk::is_renderpass_open(*m_current_command_buffer))
|
||||
{
|
||||
vk::end_renderpass(*m_current_command_buffer);
|
||||
}
|
||||
|
||||
vkCmdUpdateBuffer(*m_current_command_buffer, mapping.second->value, mapping.first, 4, &write_data);
|
||||
flush_command_queue();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ private:
|
|||
void frame_context_cleanup(vk::frame_context_t *ctx);
|
||||
void advance_queued_frames();
|
||||
void present(vk::frame_context_t *ctx);
|
||||
void reinitialize_swapchain();
|
||||
bool reinitialize_swapchain();
|
||||
|
||||
vk::viewable_image* get_present_source(vk::present_surface_info* info, const rsx::avconf& avconfig);
|
||||
|
||||
|
|
@ -254,7 +254,7 @@ public:
|
|||
// Sync
|
||||
void write_barrier(u32 address, u32 range) override;
|
||||
void sync_hint(rsx::FIFO::interrupt_hint hint, rsx::reports::sync_hint_payload_t payload) override;
|
||||
bool release_GCM_label(u32 address, u32 data) override;
|
||||
bool release_GCM_label(u32 type, u32 address, u32 data) override;
|
||||
|
||||
void begin_occlusion_query(rsx::reports::occlusion_query_info* query) override;
|
||||
void end_occlusion_query(rsx::reports::occlusion_query_info* query) override;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
void VKGSRender::reinitialize_swapchain()
|
||||
bool VKGSRender::reinitialize_swapchain()
|
||||
{
|
||||
m_swapchain_dims.width = m_frame->client_width();
|
||||
m_swapchain_dims.height = m_frame->client_height();
|
||||
|
|
@ -44,7 +44,7 @@ void VKGSRender::reinitialize_swapchain()
|
|||
if (m_swapchain_dims.width == 0 || m_swapchain_dims.height == 0)
|
||||
{
|
||||
swapchain_unavailable = true;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE: This operation will create a hard sync point
|
||||
|
|
@ -97,7 +97,7 @@ void VKGSRender::reinitialize_swapchain()
|
|||
{
|
||||
rsx_log.warning("Swapchain initialization failed. Request ignored [%dx%d]", m_swapchain_dims.width, m_swapchain_dims.height);
|
||||
swapchain_unavailable = true;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Re-initialize CPU frame contexts
|
||||
|
|
@ -135,6 +135,7 @@ void VKGSRender::reinitialize_swapchain()
|
|||
|
||||
swapchain_unavailable = false;
|
||||
should_reinitialize_swapchain = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VKGSRender::present(vk::frame_context_t *ctx)
|
||||
|
|
@ -426,11 +427,32 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
|
|||
|
||||
if (swapchain_unavailable || should_reinitialize_swapchain)
|
||||
{
|
||||
reinitialize_swapchain();
|
||||
// Reinitializing the swapchain is a failable operation. However, not all failures are fatal (e.g minimized window).
|
||||
// In the worst case, we can have the driver refuse to create the swapchain while we already deleted the previous one.
|
||||
// In such scenarios, we have to retry a few times before giving up as we cannot proceed without a swapchain.
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
if (reinitialize_swapchain() || m_current_frame)
|
||||
{
|
||||
// If m_current_frame exists, then the initialization failure is non-fatal. Proceed as usual.
|
||||
break;
|
||||
}
|
||||
|
||||
if (Emu.IsStopped())
|
||||
{
|
||||
m_frame->flip(m_context);
|
||||
rsx::thread::flip(info);
|
||||
return;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
}
|
||||
|
||||
m_profiler.start();
|
||||
|
||||
ensure(m_current_frame, "Invalid swapchain setup. Resizing the game window failed.");
|
||||
|
||||
if (m_current_frame == &m_aux_frame_context)
|
||||
{
|
||||
m_current_frame = &m_frame_context_storage[m_current_queue_index];
|
||||
|
|
@ -582,6 +604,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
|
|||
rsx_log.warning("vkAcquireNextImageKHR failed with VK_ERROR_OUT_OF_DATE_KHR. Flip request ignored until surface is recreated.");
|
||||
swapchain_unavailable = true;
|
||||
reinitialize_swapchain();
|
||||
ensure(m_current_frame, "Could not reinitialize swapchain after VK_ERROR_OUT_OF_DATE_KHR signal!");
|
||||
continue;
|
||||
default:
|
||||
vk::die_with_error(status);
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ void init_fxo_for_exec(utils::serial* ar, bool full = false)
|
|||
// Reserved area
|
||||
if (!load_and_check_reserved(*ar, advance))
|
||||
{
|
||||
sys_log.error("Potential failure to load savestate: padding buyes are not 0. %s", *ar);
|
||||
sys_log.error("Potential failure to load savestate: padding bytes are not 0. %s", *ar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -310,7 +310,7 @@ static void fixup_settings(const psf::registry* _psf)
|
|||
|
||||
if (g_cfg.net.net_active == np_internet_status::disabled && g_cfg.net.psn_status != np_psn_status::disabled)
|
||||
{
|
||||
sys_log.warning("Net status was set to disconnected so psn status was disabled");
|
||||
sys_log.warning("Net status was set to disconnected so PSN status was disabled.");
|
||||
g_cfg.net.psn_status.set(np_psn_status::disabled);
|
||||
}
|
||||
}
|
||||
|
|
@ -4642,7 +4642,7 @@ game_boot_result Emulator::InsertDisc(const std::string& path)
|
|||
else
|
||||
{
|
||||
// TODO: find out where other discs are mounted
|
||||
sys_log.todo("Mounting non-ps2/ps3 disc in dev_bdvd. Is this correct? (path='%s')", disc_root);
|
||||
sys_log.todo("Mounting non-PS2/PS3 disc in dev_bdvd. Is this correct? (path='%s')", disc_root);
|
||||
ensure(vfs::mount("/dev_bdvd", disc_root));
|
||||
}
|
||||
|
||||
|
|
|
|||
146
rpcs3/Input/mouse_gyro_handler.cpp
Normal file
146
rpcs3/Input/mouse_gyro_handler.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#include "mouse_gyro_handler.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QWheelEvent>
|
||||
#include <QWindow>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
void mouse_gyro_handler::clear()
|
||||
{
|
||||
active = false;
|
||||
reset = false;
|
||||
gyro_x = DEFAULT_MOTION_X;
|
||||
gyro_y = DEFAULT_MOTION_Y;
|
||||
gyro_z = DEFAULT_MOTION_Z;
|
||||
}
|
||||
|
||||
bool mouse_gyro_handler::toggle_enabled()
|
||||
{
|
||||
enabled = !enabled;
|
||||
clear();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::set_gyro_active()
|
||||
{
|
||||
active = true;
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::set_gyro_reset()
|
||||
{
|
||||
active = false;
|
||||
reset = true;
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::set_gyro_xz(s32 off_x, s32 off_y)
|
||||
{
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
gyro_x = static_cast<u16>(std::clamp(off_x, 0, DEFAULT_MOTION_X * 2 - 1));
|
||||
gyro_z = static_cast<u16>(std::clamp(off_y, 0, DEFAULT_MOTION_Z * 2 - 1));
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::set_gyro_y(s32 steps)
|
||||
{
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
gyro_y = static_cast<u16>(std::clamp(gyro_y + steps, 0, DEFAULT_MOTION_Y * 2 - 1));
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::handle_event(QEvent* ev, const QWindow& win)
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
// Mouse-based motion input.
|
||||
// Captures mouse events while the game window is focused.
|
||||
// Updates motion sensor values via mouse position and mouse wheel while RMB is held.
|
||||
// Intentionally independent of chosen pad configuration.
|
||||
switch (ev->type())
|
||||
{
|
||||
case QEvent::MouseButtonPress:
|
||||
{
|
||||
auto* e = static_cast<QMouseEvent*>(ev);
|
||||
if (e->button() == Qt::RightButton)
|
||||
{
|
||||
// Enable mouse-driven gyro emulation while RMB is held.
|
||||
set_gyro_active();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::MouseButtonRelease:
|
||||
{
|
||||
auto* e = static_cast<QMouseEvent*>(ev);
|
||||
if (e->button() == Qt::RightButton)
|
||||
{
|
||||
// Disable gyro emulation and request a one-shot motion reset.
|
||||
set_gyro_reset();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::MouseMove:
|
||||
{
|
||||
auto* e = static_cast<QMouseEvent*>(ev);
|
||||
|
||||
// Track cursor offset from window center.
|
||||
const QPoint center(win.width() / 2, win.height() / 2);
|
||||
const QPoint cur = e->position().toPoint();
|
||||
|
||||
const s32 off_x = cur.x() - center.x() + DEFAULT_MOTION_X;
|
||||
const s32 off_y = cur.y() - center.y() + DEFAULT_MOTION_Z;
|
||||
|
||||
// Determine motion from relative mouse position while gyro emulation is active.
|
||||
set_gyro_xz(off_x, off_y);
|
||||
|
||||
break;
|
||||
}
|
||||
case QEvent::Wheel:
|
||||
{
|
||||
auto* e = static_cast<QWheelEvent*>(ev);
|
||||
|
||||
// Track mouse wheel steps.
|
||||
const s32 steps = e->angleDelta().y() / 120;
|
||||
|
||||
// Accumulate mouse wheel steps while gyro emulation is active.
|
||||
set_gyro_y(steps);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::apply_gyro(const std::shared_ptr<Pad>& pad)
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
if (!pad || !pad->is_connected())
|
||||
return;
|
||||
|
||||
// Inject mouse-based motion sensor values into pad sensors for gyro emulation.
|
||||
// The Qt frontend maps cursor offset and wheel input to absolute motion values while RMB is held.
|
||||
if (reset)
|
||||
{
|
||||
// RMB released → reset motion
|
||||
pad->m_sensors[0].m_value = DEFAULT_MOTION_X;
|
||||
pad->m_sensors[1].m_value = DEFAULT_MOTION_Y;
|
||||
pad->m_sensors[2].m_value = DEFAULT_MOTION_Z;
|
||||
clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// RMB held → accumulate motion
|
||||
// Axes have been chosen as tested in Sly 4 minigames. Top-down view motion uses X/Z axes.
|
||||
pad->m_sensors[0].m_value = gyro_x; // Mouse X → Motion X
|
||||
pad->m_sensors[1].m_value = gyro_y; // Mouse Wheel → Motion Y
|
||||
pad->m_sensors[2].m_value = gyro_z; // Mouse Y → Motion Z
|
||||
}
|
||||
}
|
||||
33
rpcs3/Input/mouse_gyro_handler.h
Normal file
33
rpcs3/Input/mouse_gyro_handler.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Emu/Io/pad_types.h"
|
||||
|
||||
class QEvent;
|
||||
class QWindow;
|
||||
|
||||
// Mouse-based motion sensor emulation state.
|
||||
class mouse_gyro_handler
|
||||
{
|
||||
private:
|
||||
atomic_t<bool> enabled = false; // Whether mouse-based gyro emulation mode has been enabled by using the associated hotkey
|
||||
|
||||
atomic_t<bool> active = false; // Whether right mouse button is currently held (gyro active)
|
||||
atomic_t<bool> reset = false; // One-shot reset request on right mouse button release
|
||||
atomic_t<s32> gyro_x = DEFAULT_MOTION_X; // Accumulated from mouse X position relative to center
|
||||
atomic_t<s32> gyro_y = DEFAULT_MOTION_Y; // Accumulated from mouse wheel delta
|
||||
atomic_t<s32> gyro_z = DEFAULT_MOTION_Z; // Accumulated from mouse Y position relative to center
|
||||
|
||||
void set_gyro_active();
|
||||
void set_gyro_reset();
|
||||
void set_gyro_xz(s32 off_x, s32 off_y);
|
||||
void set_gyro_y(s32 steps);
|
||||
|
||||
public:
|
||||
void clear();
|
||||
bool toggle_enabled();
|
||||
|
||||
void handle_event(QEvent* ev, const QWindow& win);
|
||||
void apply_gyro(const std::shared_ptr<Pad>& pad);
|
||||
};
|
||||
|
|
@ -81,6 +81,9 @@ void pad_thread::Init()
|
|||
{
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
// Reset mouse-based gyro state
|
||||
m_mouse_gyro.clear();
|
||||
|
||||
// Cache old settings if possible
|
||||
std::array<pad_setting, CELL_PAD_MAX_PORT_NUM> pad_settings;
|
||||
for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; i++) // max 7 pads
|
||||
|
|
@ -606,6 +609,10 @@ void pad_thread::operator()()
|
|||
if (Emu.IsRunning())
|
||||
{
|
||||
update_pad_states();
|
||||
|
||||
// Apply mouse-based gyro emulation.
|
||||
// Intentionally bound to Player 1 only.
|
||||
m_mouse_gyro.apply_gyro(m_pads[0]);
|
||||
}
|
||||
|
||||
m_info.now_connect = connected_devices + num_ldd_pad;
|
||||
|
|
@ -624,15 +631,18 @@ void pad_thread::operator()()
|
|||
if (!pad->is_connected())
|
||||
continue;
|
||||
|
||||
for (const auto& button : pad->m_buttons)
|
||||
for (const Button& button : pad->m_buttons)
|
||||
{
|
||||
if (button.m_pressed && (
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_CROSS ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_CIRCLE ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_TRIANGLE ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_SQUARE ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_START ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_SELECT))
|
||||
(button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1 && (
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_START ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_SELECT)) ||
|
||||
(button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2 && (
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_CROSS ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_CIRCLE ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_TRIANGLE ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_SQUARE))
|
||||
))
|
||||
{
|
||||
any_button_pressed = true;
|
||||
break;
|
||||
|
|
@ -669,7 +679,7 @@ void pad_thread::operator()()
|
|||
break;
|
||||
}
|
||||
|
||||
for (const auto& button : pad->m_buttons)
|
||||
for (const Button& button : pad->m_buttons)
|
||||
{
|
||||
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1 && button.m_outKeyCode == CELL_PAD_CTRL_PS && button.m_pressed)
|
||||
{
|
||||
|
|
@ -728,7 +738,7 @@ void pad_thread::operator()()
|
|||
if (!pad->is_connected())
|
||||
continue;
|
||||
|
||||
for (const auto& button : pad->m_buttons)
|
||||
for (const Button& button : pad->m_buttons)
|
||||
{
|
||||
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1 && button.m_outKeyCode == CELL_PAD_CTRL_START && button.m_pressed)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "Emu/Io/pad_types.h"
|
||||
#include "Emu/Io/pad_config.h"
|
||||
#include "Emu/Io/pad_config_types.h"
|
||||
#include "Input/mouse_gyro_handler.h"
|
||||
#include "Utilities/mutex.h"
|
||||
|
||||
#include <map>
|
||||
|
|
@ -41,6 +42,8 @@ public:
|
|||
|
||||
static auto constexpr thread_name = "Pad Thread"sv;
|
||||
|
||||
mouse_gyro_handler& get_mouse_gyro() { return m_mouse_gyro; }
|
||||
|
||||
protected:
|
||||
void Init();
|
||||
void InitLddPad(u32 handle, const u32* port_status);
|
||||
|
|
@ -67,6 +70,8 @@ private:
|
|||
bool m_resume_emulation_flag = false;
|
||||
bool m_ps_button_pressed = false;
|
||||
atomic_t<bool> m_home_menu_open = false;
|
||||
|
||||
mouse_gyro_handler m_mouse_gyro;
|
||||
};
|
||||
|
||||
namespace pad
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@
|
|||
<ClCompile Include="Input\dualsense_pad_handler.cpp" />
|
||||
<ClCompile Include="Input\gui_pad_thread.cpp" />
|
||||
<ClCompile Include="Input\hid_pad_handler.cpp" />
|
||||
<ClCompile Include="Input\mouse_gyro_handler.cpp" />
|
||||
<ClCompile Include="Input\ps_move_calibration.cpp" />
|
||||
<ClCompile Include="Input\ps_move_config.cpp" />
|
||||
<ClCompile Include="Input\ps_move_tracker.cpp" />
|
||||
|
|
@ -1079,6 +1080,7 @@
|
|||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="Input\mouse_gyro_handler.h" />
|
||||
<ClInclude Include="Input\ps_move_calibration.h" />
|
||||
<ClInclude Include="Input\ps_move_config.h" />
|
||||
<ClInclude Include="Input\ps_move_tracker.h" />
|
||||
|
|
|
|||
|
|
@ -1272,6 +1272,9 @@
|
|||
<ClCompile Include="QTGeneratedFiles\Release\moc_game_list_context_menu.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Input\mouse_gyro_handler.cpp">
|
||||
<Filter>Io</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input\ds4_pad_handler.h">
|
||||
|
|
@ -1511,6 +1514,9 @@
|
|||
<ClInclude Include="Input\sdl_camera_video_sink.h">
|
||||
<Filter>Io\camera</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Input\mouse_gyro_handler.h">
|
||||
<Filter>Io</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h">
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ add_library(rpcs3_ui STATIC
|
|||
../Input/hid_pad_handler.cpp
|
||||
../Input/keyboard_pad_handler.cpp
|
||||
../Input/mm_joystick_handler.cpp
|
||||
../Input/mouse_gyro_handler.cpp
|
||||
../Input/pad_thread.cpp
|
||||
../Input/product_info.cpp
|
||||
../Input/ps_move_calibration.cpp
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ void downloader::start(const std::string& url, bool follow_location, bool show_p
|
|||
|
||||
// The downloader's signals are expected to be disconnected and customized before start is called.
|
||||
// Therefore we need to (re)connect its signal(s) here and not in the constructor.
|
||||
connect(this, &downloader::signal_buffer_update, this, &downloader::handle_buffer_update);
|
||||
connect(this, &downloader::signal_buffer_update, this, &downloader::handle_buffer_update, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
|
||||
|
||||
if (show_progress_dialog)
|
||||
{
|
||||
|
|
@ -169,7 +169,7 @@ usz downloader::update_buffer(char* data, usz size)
|
|||
const auto old_size = m_curl_buf.size();
|
||||
const auto new_size = old_size + size;
|
||||
m_curl_buf.resize(static_cast<int>(new_size));
|
||||
memcpy(m_curl_buf.data() + old_size, data, size);
|
||||
std::memcpy(m_curl_buf.data() + old_size, data, size);
|
||||
|
||||
int max = 0;
|
||||
|
||||
|
|
@ -197,6 +197,5 @@ void downloader::handle_buffer_update(int size, int max) const
|
|||
{
|
||||
m_progress_dialog->SetRange(0, max > 0 ? max : m_progress_dialog->maximum());
|
||||
m_progress_dialog->SetValue(size);
|
||||
QApplication::processEvents();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "Emu/RSX/Overlays/overlay_message.h"
|
||||
#include "Emu/Io/interception.h"
|
||||
#include "Emu/Io/recording_config.h"
|
||||
#include "Input/pad_thread.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
|
|
@ -402,6 +403,15 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey
|
|||
audio::change_volume(-5);
|
||||
break;
|
||||
}
|
||||
case gui::shortcuts::shortcut::gw_toggle_mouse_gyro:
|
||||
{
|
||||
if (auto* pad_thr = pad::get_pad_thread(true))
|
||||
{
|
||||
const bool mouse_gyro_enabled = pad_thr->get_mouse_gyro().toggle_enabled();
|
||||
gui_log.notice("Mouse-based gyro emulation %s", mouse_gyro_enabled ? "enabled" : "disabled");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
|
|
@ -1216,6 +1226,16 @@ bool gs_frame::event(QEvent* ev)
|
|||
// This will make the cursor visible again if it was hidden by the mouse idle timeout
|
||||
handle_cursor(visibility(), false, false, true);
|
||||
}
|
||||
|
||||
// Handle events for mouse-based gyro emulation.
|
||||
if (Emu.IsRunning())
|
||||
{
|
||||
if (auto* pad_thr = pad::get_pad_thread(true))
|
||||
{
|
||||
pad_thr->get_mouse_gyro().handle_event(ev, *this);
|
||||
}
|
||||
}
|
||||
|
||||
return QWindow::event(ev);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -163,11 +163,6 @@ log_frame::log_frame(std::shared_ptr<gui_settings> _gui_settings, QWidget* paren
|
|||
CreateAndConnectActions();
|
||||
LoadSettings();
|
||||
|
||||
if (m_ansi_tty)
|
||||
{
|
||||
m_tty_ansi_highlighter = new AnsiHighlighter(m_tty->document());
|
||||
}
|
||||
|
||||
m_timer = new QTimer(this);
|
||||
connect(m_timer, &QTimer::timeout, this, &log_frame::UpdateUI);
|
||||
}
|
||||
|
|
@ -225,7 +220,7 @@ void log_frame::CreateAndConnectActions()
|
|||
// I, for one, welcome our lambda overlord
|
||||
// It's either this or a signal mapper
|
||||
// Then, probably making a list of these actions so that it's easier to iterate to generate the mapper.
|
||||
auto l_initAct = [this](QAction* act, logs::level logLevel)
|
||||
const auto l_initAct = [this](QAction* act, logs::level logLevel)
|
||||
{
|
||||
act->setCheckable(true);
|
||||
|
||||
|
|
@ -298,7 +293,7 @@ void log_frame::CreateAndConnectActions()
|
|||
|
||||
if (m_ansi_tty && !m_tty_ansi_highlighter)
|
||||
{
|
||||
m_tty_ansi_highlighter = new AnsiHighlighter(m_tty->document());
|
||||
m_tty_ansi_highlighter = new AnsiHighlighter(m_tty);
|
||||
}
|
||||
else if (!m_ansi_tty && m_tty_ansi_highlighter)
|
||||
{
|
||||
|
|
@ -607,6 +602,12 @@ void log_frame::RepaintTextColors()
|
|||
html.replace(old_style, new_style);
|
||||
|
||||
m_log->document()->setHtml(html);
|
||||
|
||||
if (m_tty_ansi_highlighter)
|
||||
{
|
||||
m_tty_ansi_highlighter->update_colors(m_tty);
|
||||
m_tty_ansi_highlighter->rehighlight();
|
||||
}
|
||||
}
|
||||
|
||||
void log_frame::UpdateUI()
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ LOG_CHANNEL(gui_log, "GUI");
|
|||
log_viewer::log_viewer(std::shared_ptr<gui_settings> gui_settings)
|
||||
: m_gui_settings(std::move(gui_settings))
|
||||
{
|
||||
setWindowTitle(tr("Log Viewer"));
|
||||
update_title();
|
||||
|
||||
setObjectName("log_viewer");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setAttribute(Qt::WA_StyledBackground);
|
||||
|
|
@ -59,15 +60,33 @@ log_viewer::log_viewer(std::shared_ptr<gui_settings> gui_settings)
|
|||
connect(m_log_text, &QWidget::customContextMenuRequested, this, &log_viewer::show_context_menu);
|
||||
}
|
||||
|
||||
void log_viewer::update_title()
|
||||
{
|
||||
QString suffix;
|
||||
|
||||
if (!m_filter_term.isEmpty())
|
||||
{
|
||||
suffix = tr(" | Filter '%0'").arg(m_filter_term);
|
||||
}
|
||||
|
||||
if (!m_exclude_term.isEmpty())
|
||||
{
|
||||
suffix += tr(" | Exclude '%0'").arg(m_exclude_term);
|
||||
}
|
||||
|
||||
setWindowTitle(tr("Log Viewer%0").arg(suffix));
|
||||
}
|
||||
|
||||
void log_viewer::show_context_menu(const QPoint& pos)
|
||||
{
|
||||
QMenu menu;
|
||||
QAction* clear = new QAction(tr("&Clear"));
|
||||
QAction* copy = new QAction(tr("&Copy"));
|
||||
QAction* open = new QAction(tr("&Open log file"));
|
||||
QAction* save = new QAction(tr("&Save filtered log"));
|
||||
QAction* filter = new QAction(tr("&Filter log"));
|
||||
QAction* config = new QAction(tr("&Check config"));
|
||||
QAction* clear = new QAction(tr("&Clear"));
|
||||
QAction* copy = new QAction(tr("&Copy"));
|
||||
QAction* open = new QAction(tr("&Open log file"));
|
||||
QAction* save = new QAction(tr("&Save filtered log"));
|
||||
QAction* filter = new QAction(tr("&Filter log%0").arg(m_filter_term.isEmpty() ? "" : QString(" (%0)").arg(m_filter_term)));
|
||||
QAction* exclude = new QAction(tr("&Exclude%0").arg(m_exclude_term.isEmpty() ? "" : QString(" (%0)").arg(m_exclude_term)));
|
||||
QAction* config = new QAction(tr("&Check config"));
|
||||
|
||||
QAction* timestamps = new QAction(tr("&Show Timestamps"));
|
||||
timestamps->setCheckable(true);
|
||||
|
|
@ -91,7 +110,7 @@ void log_viewer::show_context_menu(const QPoint& pos)
|
|||
QAction* trace_act = new QAction(tr("Trace"), log_level_acts);
|
||||
log_level_acts->setExclusive(false);
|
||||
|
||||
auto init_action = [this](QAction* act, logs::level logLevel)
|
||||
const auto init_action = [this](QAction* act, logs::level logLevel)
|
||||
{
|
||||
act->setCheckable(true);
|
||||
act->setChecked(m_log_levels.test(static_cast<u32>(logLevel)));
|
||||
|
|
@ -120,6 +139,7 @@ void log_viewer::show_context_menu(const QPoint& pos)
|
|||
menu.addAction(open);
|
||||
menu.addAction(config);
|
||||
menu.addAction(filter);
|
||||
menu.addAction(exclude);
|
||||
menu.addAction(save);
|
||||
menu.addSeparator();
|
||||
menu.addAction(timestamps);
|
||||
|
|
@ -187,7 +207,22 @@ void log_viewer::show_context_menu(const QPoint& pos)
|
|||
|
||||
connect(filter, &QAction::triggered, this, [this]()
|
||||
{
|
||||
m_filter_term = QInputDialog::getText(this, tr("Filter log"), tr("Enter text"), QLineEdit::EchoMode::Normal, m_filter_term);
|
||||
bool ok = false;
|
||||
QString filter_term = QInputDialog::getText(this, tr("Filter log"), tr("Enter text"), QLineEdit::EchoMode::Normal, m_filter_term, &ok);
|
||||
if (!ok) return;
|
||||
m_filter_term = std::move(filter_term);
|
||||
update_title();
|
||||
filter_log();
|
||||
});
|
||||
|
||||
connect(exclude, &QAction::triggered, this, [this]()
|
||||
{
|
||||
bool ok = false;
|
||||
QString exclude_term = QInputDialog::getText(this, tr("Exclude"), tr("Enter text (comma separated)"), QLineEdit::EchoMode::Normal, m_exclude_term, &ok);
|
||||
if (!ok) return;
|
||||
m_exclude_term = std::move(exclude_term);
|
||||
m_exclude_terms = m_exclude_term.split(',', Qt::SkipEmptyParts);
|
||||
update_title();
|
||||
filter_log();
|
||||
});
|
||||
|
||||
|
|
@ -309,7 +344,7 @@ void log_viewer::filter_log()
|
|||
if (!m_log_levels.test(static_cast<u32>(logs::level::notice))) excluded_log_levels.push_back("·! ");
|
||||
if (!m_log_levels.test(static_cast<u32>(logs::level::trace))) excluded_log_levels.push_back("·T ");
|
||||
|
||||
if (m_filter_term.isEmpty() && excluded_log_levels.empty() && m_show_timestamps && m_show_threads && !m_last_actions_only)
|
||||
if (m_filter_term.isEmpty() && m_exclude_terms.isEmpty() && excluded_log_levels.empty() && m_show_timestamps && m_show_threads && !m_last_actions_only)
|
||||
{
|
||||
set_text_and_keep_position(m_full_log);
|
||||
return;
|
||||
|
|
@ -322,44 +357,49 @@ void log_viewer::filter_log()
|
|||
|
||||
const auto add_line = [this, &result, &excluded_log_levels, ×tamp_regexp, &thread_regexp](QString& line)
|
||||
{
|
||||
bool exclude_line = false;
|
||||
|
||||
for (const QString& log_level_prefix : excluded_log_levels)
|
||||
if (!line.isEmpty())
|
||||
{
|
||||
if (line.startsWith(log_level_prefix))
|
||||
for (QStringView log_level_prefix : excluded_log_levels)
|
||||
{
|
||||
exclude_line = true;
|
||||
break;
|
||||
if (line.startsWith(log_level_prefix))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (QStringView term : m_exclude_terms)
|
||||
{
|
||||
if (line.contains(term))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exclude_line)
|
||||
if (!m_filter_term.isEmpty() && !line.contains(m_filter_term))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_filter_term.isEmpty() || line.contains(m_filter_term))
|
||||
if (line.isEmpty())
|
||||
{
|
||||
if (line.isEmpty())
|
||||
{
|
||||
result += "\n";
|
||||
return;
|
||||
}
|
||||
result += "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_show_timestamps)
|
||||
{
|
||||
line.remove(timestamp_regexp);
|
||||
}
|
||||
if (!m_show_timestamps)
|
||||
{
|
||||
line.remove(timestamp_regexp);
|
||||
}
|
||||
|
||||
if (!m_show_threads)
|
||||
{
|
||||
line.remove(thread_regexp);
|
||||
}
|
||||
if (!m_show_threads)
|
||||
{
|
||||
line.remove(thread_regexp);
|
||||
}
|
||||
|
||||
if (!line.isEmpty())
|
||||
{
|
||||
result += line + "\n";
|
||||
}
|
||||
if (!line.isEmpty())
|
||||
{
|
||||
result += line + "\n";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ private Q_SLOTS:
|
|||
void show_context_menu(const QPoint& pos);
|
||||
|
||||
private:
|
||||
void update_title();
|
||||
void set_text_and_keep_position(const QString& text);
|
||||
void filter_log();
|
||||
bool is_valid_file(const QMimeData& md, bool save = false);
|
||||
|
|
@ -30,6 +31,8 @@ private:
|
|||
std::shared_ptr<gui_settings> m_gui_settings;
|
||||
QString m_path_last;
|
||||
QString m_filter_term;
|
||||
QString m_exclude_term;
|
||||
QStringList m_exclude_terms;
|
||||
QString m_full_log;
|
||||
QPlainTextEdit* m_log_text;
|
||||
LogHighlighter* m_log_highlighter;
|
||||
|
|
|
|||
|
|
@ -173,21 +173,45 @@ namespace gui
|
|||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QColor get_foreground_color()
|
||||
|
||||
QColor get_foreground_color(QWidget* widget)
|
||||
{
|
||||
if (widget)
|
||||
{
|
||||
widget->ensurePolished();
|
||||
return widget->palette().color(QPalette::ColorRole::WindowText);
|
||||
}
|
||||
|
||||
QLabel dummy_color;
|
||||
dummy_color.ensurePolished();
|
||||
return dummy_color.palette().color(QPalette::ColorRole::WindowText);
|
||||
}
|
||||
|
||||
QColor get_background_color()
|
||||
QColor get_background_color(QWidget* widget)
|
||||
{
|
||||
if (widget)
|
||||
{
|
||||
widget->ensurePolished();
|
||||
return widget->palette().color(QPalette::ColorRole::Window);
|
||||
}
|
||||
|
||||
QLabel dummy_color;
|
||||
dummy_color.ensurePolished();
|
||||
return dummy_color.palette().color(QPalette::ColorRole::Window);
|
||||
}
|
||||
|
||||
QColor adjust_color_for_background(const QColor& fg, const QColor& bg)
|
||||
{
|
||||
const int diff = fg.lightness() - bg.lightness();
|
||||
|
||||
if (std::abs(diff) >= 40)
|
||||
{
|
||||
return fg;
|
||||
}
|
||||
|
||||
return (bg.lightness() < 128) ? fg.lighter(180) : fg.darker(180);
|
||||
}
|
||||
|
||||
QColor get_label_color(const QString& object_name, const QColor& fallback_light, const QColor& fallback_dark, QPalette::ColorRole color_role)
|
||||
{
|
||||
if (!gui::custom_stylesheet_active || !gui::stylesheet.contains(object_name))
|
||||
|
|
|
|||
|
|
@ -68,11 +68,14 @@ namespace gui
|
|||
// Returns a list of all base names of files in dir whose complete file names contain one of the given name_filters
|
||||
QStringList get_dir_entries(const QDir& dir, const QStringList& name_filters, bool full_path = false);
|
||||
|
||||
// Returns the foreground color of QLabel with respect to the current light/dark mode.
|
||||
QColor get_foreground_color();
|
||||
// Returns the foreground color of QLabel or the given widget with respect to the current light/dark mode.
|
||||
QColor get_foreground_color(QWidget* widget = nullptr);
|
||||
|
||||
// Returns the background color of QLabel with respect to the current light/dark mode.
|
||||
QColor get_background_color();
|
||||
// Returns the background color of QLabel or the given widget with respect to the current light/dark mode.
|
||||
QColor get_background_color(QWidget* widget = nullptr);
|
||||
|
||||
// Returns an adjusted color with better contrast, depending on the background.
|
||||
QColor adjust_color_for_background(const QColor& fg, const QColor& bg);
|
||||
|
||||
// Returns the color specified by its color_role for the QLabels with object_name
|
||||
QColor get_label_color(const QString& object_name, const QColor& fallback_light, const QColor& fallback_dark, QPalette::ColorRole color_role = QPalette::WindowText);
|
||||
|
|
|
|||
|
|
@ -1262,13 +1262,10 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
|
|||
|
||||
connect(accept_request_action, &QAction::triggered, this, [this, str_sel_friend]()
|
||||
{
|
||||
if (!m_rpcn->add_friend(str_sel_friend))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error adding a friend!"), tr("An error occurred while trying to add a friend!"), QMessageBox::Ok);
|
||||
}
|
||||
else
|
||||
if (add_friend_with_error_dialog(str_sel_friend))
|
||||
{
|
||||
QMessageBox::information(this, tr("Friend added!"), tr("You've successfully added a friend!"), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1304,11 +1301,8 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
|
|||
|
||||
connect(send_friend_request_action, &QAction::triggered, this, [this, str_sel_friend]()
|
||||
{
|
||||
if (!m_rpcn->add_friend(str_sel_friend))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error sending a friend request!"), tr("An error occurred while trying to send a friend request!"), QMessageBox::Ok);
|
||||
if (!add_friend_with_error_dialog(str_sel_friend))
|
||||
return;
|
||||
}
|
||||
|
||||
QString qstr_friend = QString::fromStdString(str_sel_friend);
|
||||
add_update_list(m_lst_requests, qstr_friend, m_icon_request_sent, QVariant(false));
|
||||
|
|
@ -1341,11 +1335,7 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
|
|||
QMessageBox::critical(this, tr("Error validating username!"), tr("The username you entered is invalid!"), QMessageBox::Ok);
|
||||
}
|
||||
|
||||
if (!m_rpcn->add_friend(str_friend_username))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error adding friend!"), tr("An error occurred while adding a friend!"), QMessageBox::Ok);
|
||||
}
|
||||
else
|
||||
if (add_friend_with_error_dialog(str_friend_username))
|
||||
{
|
||||
add_update_list(m_lst_requests, QString::fromStdString(str_friend_username), m_icon_request_sent, QVariant(false));
|
||||
QMessageBox::information(this, tr("Friend added!"), tr("Friend was successfully added!"), QMessageBox::Ok);
|
||||
|
|
@ -1360,6 +1350,42 @@ rpcn_friends_dialog::~rpcn_friends_dialog()
|
|||
m_rpcn->remove_friend_cb(friend_callback, this);
|
||||
}
|
||||
|
||||
bool rpcn_friends_dialog::add_friend_with_error_dialog(const std::string& friend_username)
|
||||
{
|
||||
QString err_msg;
|
||||
const auto opt_error = m_rpcn->add_friend(friend_username);
|
||||
|
||||
if (opt_error.has_value())
|
||||
{
|
||||
const auto error = opt_error.value();
|
||||
|
||||
if (error != rpcn::ErrorType::NoError)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case rpcn::ErrorType::NotFound: err_msg = tr("The specified username does not exist."); break;
|
||||
case rpcn::ErrorType::InvalidInput: err_msg = tr("You cannot add yourself as a friend."); break;
|
||||
case rpcn::ErrorType::Blocked: err_msg = tr("You or the other user have the other blocked."); break;
|
||||
case rpcn::ErrorType::AlreadyFriend: err_msg = tr("You are already friends with this user."); break;
|
||||
case rpcn::ErrorType::DbFail: err_msg = tr("A database error occurred. Please try again later."); break;
|
||||
default: err_msg = tr("An unexpected error occurred."); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
err_msg = tr("Failed to send the friend request.");
|
||||
}
|
||||
|
||||
if (!err_msg.isEmpty())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Friend Request Failed"), err_msg, QMessageBox::Ok);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rpcn_friends_dialog::is_ok() const
|
||||
{
|
||||
return m_rpcn_ok;
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ public:
|
|||
private:
|
||||
void add_update_list(QListWidget* list, const QString& name, const QIcon& icon, const QVariant& data);
|
||||
void remove_list(QListWidget* list, const QString& name);
|
||||
bool add_friend_with_error_dialog(const std::string& friend_username);
|
||||
|
||||
private Q_SLOTS:
|
||||
void add_update_friend(const QString& name, bool status);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ void fmt_class_string<shortcut>::format(std::string& out, u64 arg)
|
|||
case shortcut::gw_mute_unmute: return "gw_mute_unmute";
|
||||
case shortcut::gw_volume_up: return "gw_volume_up";
|
||||
case shortcut::gw_volume_down: return "gw_volume_down";
|
||||
case shortcut::gw_toggle_mouse_gyro: return "gw_toggle_mouse_gyro";
|
||||
case shortcut::count: return "count";
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +89,7 @@ shortcut_settings::shortcut_settings()
|
|||
{ shortcut::gw_mute_unmute, shortcut_info{ "gw_mute_unmute", tr("Mute/Unmute Audio"), "Ctrl+Shift+M", shortcut_handler_id::game_window, false } },
|
||||
{ shortcut::gw_volume_up, shortcut_info{ "gw_volume_up", tr("Volume Up"), "Ctrl+Shift++", shortcut_handler_id::game_window, true } },
|
||||
{ shortcut::gw_volume_down, shortcut_info{ "gw_volume_down", tr("Volume Down"), "Ctrl+Shift+-", shortcut_handler_id::game_window, true } },
|
||||
{ shortcut::gw_toggle_mouse_gyro, shortcut_info{ "gw_toggle_mouse_gyro", tr("Toggle Mouse-based Gyro"), "Ctrl+G", shortcut_handler_id::game_window, false } },
|
||||
})
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ namespace gui
|
|||
gw_mute_unmute,
|
||||
gw_volume_up,
|
||||
gw_volume_down,
|
||||
gw_toggle_mouse_gyro,
|
||||
|
||||
count
|
||||
};
|
||||
|
|
|
|||
|
|
@ -184,12 +184,37 @@ GlslHighlighter::GlslHighlighter(QTextDocument* parent) : Highlighter(parent)
|
|||
commentEndExpression = QRegularExpression("\\*/");
|
||||
}
|
||||
|
||||
AnsiHighlighter::AnsiHighlighter(QTextDocument* parent) : Highlighter(parent)
|
||||
AnsiHighlighter::AnsiHighlighter(QPlainTextEdit* text_edit)
|
||||
: Highlighter(text_edit ? text_edit->document() : nullptr)
|
||||
{
|
||||
m_escape_format.setForeground(Qt::darkGray);
|
||||
m_escape_format.setFontItalic(true);
|
||||
update_colors(text_edit);
|
||||
}
|
||||
|
||||
m_foreground_color = gui::utils::get_foreground_color();
|
||||
void AnsiHighlighter::update_colors(QPlainTextEdit* text_edit)
|
||||
{
|
||||
m_foreground_color = gui::utils::get_foreground_color(text_edit);
|
||||
m_background_color = gui::utils::get_background_color(text_edit);
|
||||
|
||||
m_foreground_colors[30] = gui::utils::adjust_color_for_background(Qt::black, m_background_color);
|
||||
m_foreground_colors[31] = gui::utils::adjust_color_for_background(Qt::red, m_background_color);
|
||||
m_foreground_colors[32] = gui::utils::adjust_color_for_background(Qt::darkGreen, m_background_color);
|
||||
m_foreground_colors[33] = gui::utils::adjust_color_for_background(Qt::darkYellow, m_background_color);
|
||||
m_foreground_colors[34] = gui::utils::adjust_color_for_background(Qt::darkBlue, m_background_color);
|
||||
m_foreground_colors[35] = gui::utils::adjust_color_for_background(Qt::darkMagenta, m_background_color);
|
||||
m_foreground_colors[36] = gui::utils::adjust_color_for_background(Qt::darkCyan, m_background_color);
|
||||
m_foreground_colors[37] = gui::utils::adjust_color_for_background(Qt::lightGray, m_background_color);
|
||||
m_foreground_colors[39] = m_foreground_color;
|
||||
m_foreground_colors[90] = gui::utils::adjust_color_for_background(Qt::darkGray, m_background_color);
|
||||
m_foreground_colors[91] = gui::utils::adjust_color_for_background(Qt::red, m_background_color);
|
||||
m_foreground_colors[92] = gui::utils::adjust_color_for_background(Qt::green, m_background_color);
|
||||
m_foreground_colors[93] = gui::utils::adjust_color_for_background(Qt::yellow, m_background_color);
|
||||
m_foreground_colors[94] = gui::utils::adjust_color_for_background(Qt::blue, m_background_color);
|
||||
m_foreground_colors[95] = gui::utils::adjust_color_for_background(Qt::magenta, m_background_color);
|
||||
m_foreground_colors[96] = gui::utils::adjust_color_for_background(Qt::cyan, m_background_color);
|
||||
m_foreground_colors[97] = gui::utils::adjust_color_for_background(Qt::white, m_background_color);
|
||||
|
||||
m_escape_format.setForeground(gui::utils::adjust_color_for_background(Qt::darkGray, m_background_color));
|
||||
m_escape_format.setFontItalic(true);
|
||||
}
|
||||
|
||||
void AnsiHighlighter::highlightBlock(const QString& text)
|
||||
|
|
@ -235,39 +260,26 @@ void AnsiHighlighter::highlightBlock(const QString& text)
|
|||
if (!ok) continue;
|
||||
switch (code)
|
||||
{
|
||||
case 0:
|
||||
current_format = QTextCharFormat();
|
||||
current_format.setForeground(m_foreground_color);
|
||||
break;
|
||||
case 1:
|
||||
current_format.setFontWeight(QFont::Bold);
|
||||
break;
|
||||
case 3:
|
||||
current_format.setFontItalic(true);
|
||||
break;
|
||||
case 4:
|
||||
current_format.setFontUnderline(true);
|
||||
break;
|
||||
case 30: current_format.setForeground(Qt::black); break;
|
||||
case 31: current_format.setForeground(Qt::red); break;
|
||||
case 32: current_format.setForeground(Qt::darkGreen); break;
|
||||
case 33: current_format.setForeground(Qt::darkYellow); break;
|
||||
case 34: current_format.setForeground(Qt::darkBlue); break;
|
||||
case 35: current_format.setForeground(Qt::darkMagenta); break;
|
||||
case 36: current_format.setForeground(Qt::darkCyan); break;
|
||||
case 37: current_format.setForeground(Qt::lightGray); break;
|
||||
case 39: current_format.setForeground(m_foreground_color); break;
|
||||
case 90: current_format.setForeground(Qt::darkGray); break;
|
||||
case 91: current_format.setForeground(Qt::red); break;
|
||||
case 92: current_format.setForeground(Qt::green); break;
|
||||
case 93: current_format.setForeground(Qt::yellow); break;
|
||||
case 94: current_format.setForeground(Qt::blue); break;
|
||||
case 95: current_format.setForeground(Qt::magenta); break;
|
||||
case 96: current_format.setForeground(Qt::cyan); break;
|
||||
case 97: current_format.setForeground(Qt::white); break;
|
||||
case 0:
|
||||
current_format = QTextCharFormat();
|
||||
current_format.setForeground(m_foreground_color);
|
||||
break;
|
||||
case 1:
|
||||
current_format.setFontWeight(QFont::Bold);
|
||||
break;
|
||||
case 3:
|
||||
current_format.setFontItalic(true);
|
||||
break;
|
||||
case 4:
|
||||
current_format.setFontUnderline(true);
|
||||
break;
|
||||
default:
|
||||
// Background and extended colors not yet handled
|
||||
default:
|
||||
break;
|
||||
if (const auto it = m_foreground_colors.find(code); it != m_foreground_colors.cend())
|
||||
{
|
||||
current_format.setForeground(it->second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -278,5 +290,7 @@ void AnsiHighlighter::highlightBlock(const QString& text)
|
|||
|
||||
// Apply remaining format
|
||||
if (pos < text.length())
|
||||
{
|
||||
setFormat(pos, text.length() - pos, current_format);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
#include <QSyntaxHighlighter>
|
||||
#include <QRegularExpression>
|
||||
#include <QBrush>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
#include <map>
|
||||
|
||||
// Inspired by https://doc.qt.io/qt-5/qtwidgets-richtext-syntaxhighlighter-example.html
|
||||
|
||||
|
|
@ -59,7 +62,9 @@ class AnsiHighlighter : public Highlighter
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AnsiHighlighter(QTextDocument* parent = nullptr);
|
||||
explicit AnsiHighlighter(QPlainTextEdit* text_edit);
|
||||
|
||||
void update_colors(QPlainTextEdit* text_edit);
|
||||
|
||||
protected:
|
||||
const QRegularExpression ansi_re = QRegularExpression("\x1b\\[[0-9;]*m");
|
||||
|
|
@ -67,6 +72,8 @@ protected:
|
|||
|
||||
QTextCharFormat m_escape_format;
|
||||
QColor m_foreground_color;
|
||||
QColor m_background_color;
|
||||
std::map<int, QColor> m_foreground_colors;
|
||||
|
||||
void highlightBlock(const QString& text) override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ namespace utils
|
|||
inline void pause()
|
||||
{
|
||||
#if defined(ARCH_ARM64)
|
||||
__asm__ volatile("yield");
|
||||
__asm__ volatile("isb" ::: "memory");
|
||||
#elif defined(ARCH_X64)
|
||||
_mm_pause();
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -1011,7 +1011,12 @@ struct atomic_storage<T, 16> : atomic_storage<T, 0>
|
|||
static inline T exchange(T& dest, T value)
|
||||
{
|
||||
__atomic_thread_fence(__ATOMIC_ACQ_REL);
|
||||
// GCC has recently started thinking using this instrinsic is breaking strict aliasing rules
|
||||
// TODO: remove if this ever get fixed in GCC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
return std::bit_cast<T>(__sync_lock_test_and_set(reinterpret_cast<u128*>(&dest), std::bit_cast<u128>(value)));
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
static inline void store(T& dest, T value)
|
||||
|
|
|
|||
|
|
@ -575,34 +575,28 @@ namespace utils
|
|||
return;
|
||||
}
|
||||
|
||||
// Prepare resampler
|
||||
av.swr = swr_alloc();
|
||||
if (!av.swr)
|
||||
{
|
||||
media_log.error("audio_decoder: Failed to allocate resampler for stream #%u in file '%s'", stream_index, path);
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const int dst_channels = 2;
|
||||
const AVChannelLayout dst_channel_layout = AV_CHANNEL_LAYOUT_STEREO;
|
||||
const AVSampleFormat dst_format = AV_SAMPLE_FMT_FLT;
|
||||
|
||||
int set_err = 0;
|
||||
if ((set_err = av_opt_set_int(av.swr, "in_channel_count", stream->codecpar->ch_layout.nb_channels, 0)) ||
|
||||
(set_err = av_opt_set_int(av.swr, "out_channel_count", dst_channels, 0)) ||
|
||||
(set_err = av_opt_set_chlayout(av.swr, "in_channel_layout", &stream->codecpar->ch_layout, 0)) ||
|
||||
(set_err = av_opt_set_chlayout(av.swr, "out_channel_layout", &dst_channel_layout, 0)) ||
|
||||
(set_err = av_opt_set_int(av.swr, "in_sample_rate", stream->codecpar->sample_rate, 0)) ||
|
||||
(set_err = av_opt_set_int(av.swr, "out_sample_rate", sample_rate, 0)) ||
|
||||
(set_err = av_opt_set_sample_fmt(av.swr, "in_sample_fmt", static_cast<AVSampleFormat>(stream->codecpar->format), 0)) ||
|
||||
(set_err = av_opt_set_sample_fmt(av.swr, "out_sample_fmt", dst_format, 0)))
|
||||
const int set_err = swr_alloc_set_opts2(&av.swr, &dst_channel_layout, dst_format,
|
||||
sample_rate, &stream->codecpar->ch_layout,
|
||||
static_cast<AVSampleFormat>(stream->codecpar->format),
|
||||
stream->codecpar->sample_rate, 0, nullptr);
|
||||
if (set_err < 0)
|
||||
{
|
||||
media_log.error("audio_decoder: Failed to set resampler options: Error: %d='%s'", set_err, av_error_to_string(set_err));
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!av.swr)
|
||||
{
|
||||
media_log.error("audio_decoder: Failed to allocate resampler for stream #%u in file '%s'", stream_index, path);
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (int err = swr_init(av.swr); err < 0 || !swr_is_initialized(av.swr))
|
||||
{
|
||||
media_log.error("audio_decoder: Resampler has not been properly initialized: %d='%s'", err, av_error_to_string(err));
|
||||
|
|
|
|||
|
|
@ -16,9 +16,15 @@
|
|||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/resource.h>
|
||||
#ifndef __APPLE__
|
||||
#ifdef __APPLE__
|
||||
#include <sys/sysctl.h>
|
||||
#else
|
||||
#include <sys/utsname.h>
|
||||
#include <errno.h>
|
||||
#if defined(ARCH_ARM64) && defined(__linux__)
|
||||
#include <sys/auxv.h>
|
||||
#include <asm/hwcap.h>
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
@ -444,6 +450,103 @@ u32 utils::get_rep_movsb_threshold()
|
|||
return g_value;
|
||||
}
|
||||
|
||||
#ifdef ARCH_ARM64
|
||||
|
||||
bool utils::has_neon()
|
||||
{
|
||||
static const bool g_value = []() -> bool
|
||||
{
|
||||
#if defined(__linux__)
|
||||
return (getauxval(AT_HWCAP) & HWCAP_ASIMD) != 0;
|
||||
#elif defined(__APPLE__)
|
||||
int val = 0;
|
||||
size_t len = sizeof(val);
|
||||
sysctlbyname("hw.optional.AdvSIMD", &val, &len, nullptr, 0);
|
||||
int val_legacy = 0;
|
||||
size_t len_legacy = sizeof(val_legacy);
|
||||
sysctlbyname("hw.optional.neon", &val_legacy, &len_legacy, nullptr, 0);
|
||||
return val != 0 || val_legacy != 0;
|
||||
#elif defined(_WIN32)
|
||||
return IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE) != 0;
|
||||
#endif
|
||||
}();
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_sha3()
|
||||
{
|
||||
static const bool g_value = []() -> bool
|
||||
{
|
||||
#if defined(__linux__)
|
||||
return (getauxval(AT_HWCAP) & HWCAP_SHA3) != 0;
|
||||
#elif defined(__APPLE__)
|
||||
int val = 0;
|
||||
size_t len = sizeof(val);
|
||||
sysctlbyname("hw.optional.arm.FEAT_SHA3", &val, &len, nullptr, 0);
|
||||
return val != 0;
|
||||
#elif defined(_WIN32)
|
||||
return IsProcessorFeaturePresent(PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
#endif
|
||||
}();
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_dotprod()
|
||||
{
|
||||
static const bool g_value = []() -> bool
|
||||
{
|
||||
#if defined(__linux__)
|
||||
return (getauxval(AT_HWCAP) & HWCAP_ASIMDDP) != 0;
|
||||
#elif defined(__APPLE__)
|
||||
int val = 0;
|
||||
size_t len = sizeof(val);
|
||||
sysctlbyname("hw.optional.arm.FEAT_DotProd", &val, &len, nullptr, 0);
|
||||
return val != 0;
|
||||
#elif defined(_WIN32)
|
||||
return IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
#endif
|
||||
}();
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_sve()
|
||||
{
|
||||
static const bool g_value = []() -> bool
|
||||
{
|
||||
#if defined(__linux__)
|
||||
return (getauxval(AT_HWCAP) & HWCAP_SVE) != 0;
|
||||
#elif defined(__APPLE__)
|
||||
int val = 0;
|
||||
size_t len = sizeof(val);
|
||||
sysctlbyname("hw.optional.arm.FEAT_SVE", &val, &len, nullptr, 0);
|
||||
return val != 0;
|
||||
#elif defined(_WIN32)
|
||||
return IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
#endif
|
||||
}();
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_sve2()
|
||||
{
|
||||
static const bool g_value = []() -> bool
|
||||
{
|
||||
#if defined(__linux__)
|
||||
return (getauxval(AT_HWCAP2) & HWCAP2_SVE2) != 0;
|
||||
#elif defined(__APPLE__)
|
||||
int val = 0;
|
||||
size_t len = sizeof(val);
|
||||
sysctlbyname("hw.optional.arm.FEAT_SVE2", &val, &len, nullptr, 0);
|
||||
return val != 0;
|
||||
#elif defined(_WIN32)
|
||||
return IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE) != 0;
|
||||
#endif
|
||||
}();
|
||||
return g_value;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::string utils::get_cpu_brand()
|
||||
{
|
||||
#if defined(ARCH_X64)
|
||||
|
|
@ -496,6 +599,17 @@ std::string utils::get_system_info()
|
|||
{
|
||||
fmt::append(result, " | TSC: Disabled");
|
||||
}
|
||||
#ifdef ARCH_ARM64
|
||||
|
||||
if (has_neon())
|
||||
{
|
||||
result += " | Neon";
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Neon support not present");
|
||||
}
|
||||
#else
|
||||
|
||||
if (has_avx())
|
||||
{
|
||||
|
|
@ -562,6 +676,7 @@ std::string utils::get_system_info()
|
|||
{
|
||||
result += " | TSX disabled via microcode";
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,17 @@ namespace utils
|
|||
bool has_appropriate_um_wait();
|
||||
|
||||
bool has_um_wait();
|
||||
#ifdef ARCH_ARM64
|
||||
bool has_neon();
|
||||
|
||||
bool has_sha3();
|
||||
|
||||
bool has_dotprod();
|
||||
|
||||
bool has_sve();
|
||||
|
||||
bool has_sve2();
|
||||
#endif
|
||||
std::string get_cpu_brand();
|
||||
|
||||
std::string get_system_info();
|
||||
|
|
|
|||
Loading…
Reference in a new issue