mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-04-21 20:14:45 +00:00
updating branch with main
This commit is contained in:
commit
04edf7ec9e
50 changed files with 3336 additions and 232 deletions
5
.gitmodules
vendored
5
.gitmodules
vendored
|
@ -85,3 +85,8 @@
|
|||
[submodule "externals/half"]
|
||||
path = externals/half
|
||||
url = https://github.com/ROCm/half.git
|
||||
[submodule "externals/dear_imgui"]
|
||||
path = externals/dear_imgui
|
||||
url = https://github.com/shadps4-emu/ext-imgui.git
|
||||
shallow = true
|
||||
branch = docking
|
||||
|
|
|
@ -31,6 +31,22 @@ endif()
|
|||
|
||||
option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF)
|
||||
|
||||
# First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR.
|
||||
if (APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||
set(BASE_ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}")
|
||||
else()
|
||||
set(BASE_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
# Next, match common architecture strings down to a known common value.
|
||||
if (BASE_ARCHITECTURE MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
|
||||
set(ARCHITECTURE "x86_64")
|
||||
elseif (BASE_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
|
||||
set(ARCHITECTURE "arm64")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported CPU architecture: ${BASE_ARCHITECTURE}")
|
||||
endif()
|
||||
|
||||
# This function should be passed a list of all files in a target. It will automatically generate file groups
|
||||
# following the directory hierarchy, so that the layout of the files in IDEs matches the one in the filesystem.
|
||||
function(create_target_directory_groups target_name)
|
||||
|
@ -204,6 +220,7 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
|
|||
src/core/libraries/system/commondialog.h
|
||||
src/core/libraries/system/msgdialog.cpp
|
||||
src/core/libraries/system/msgdialog.h
|
||||
src/core/libraries/system/msgdialog_ui.cpp
|
||||
src/core/libraries/system/posix.cpp
|
||||
src/core/libraries/system/posix.h
|
||||
src/core/libraries/save_data/error_codes.h
|
||||
|
@ -308,6 +325,7 @@ set(COMMON src/common/logging/backend.cpp
|
|||
src/common/logging/text_formatter.h
|
||||
src/common/logging/types.h
|
||||
src/common/alignment.h
|
||||
src/common/arch.h
|
||||
src/common/assert.cpp
|
||||
src/common/assert.h
|
||||
src/common/bit_field.h
|
||||
|
@ -325,6 +343,7 @@ set(COMMON src/common/logging/backend.cpp
|
|||
src/common/error.cpp
|
||||
src/common/error.h
|
||||
src/common/scope_exit.h
|
||||
src/common/fixed_value.h
|
||||
src/common/func_traits.h
|
||||
src/common/native_clock.cpp
|
||||
src/common/native_clock.h
|
||||
|
@ -356,8 +375,6 @@ set(CORE src/core/aerolib/stubs.cpp
|
|||
src/core/aerolib/aerolib.h
|
||||
src/core/address_space.cpp
|
||||
src/core/address_space.h
|
||||
src/core/cpu_patches.cpp
|
||||
src/core/cpu_patches.h
|
||||
src/core/crypto/crypto.cpp
|
||||
src/core/crypto/crypto.h
|
||||
src/core/crypto/keys.h
|
||||
|
@ -415,6 +432,12 @@ set(CORE src/core/aerolib/stubs.cpp
|
|||
src/core/virtual_memory.h
|
||||
)
|
||||
|
||||
if (ARCHITECTURE STREQUAL "x86_64")
|
||||
set(CORE ${CORE}
|
||||
src/core/cpu_patches.cpp
|
||||
src/core/cpu_patches.h)
|
||||
endif()
|
||||
|
||||
set(SHADER_RECOMPILER src/shader_recompiler/exception.h
|
||||
src/shader_recompiler/profile.h
|
||||
src/shader_recompiler/recompiler.cpp
|
||||
|
@ -561,6 +584,19 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
|
|||
src/video_core/renderdoc.h
|
||||
)
|
||||
|
||||
set(IMGUI src/imgui/imgui_config.h
|
||||
src/imgui/imgui_layer.h
|
||||
src/imgui/imgui_std.h
|
||||
src/imgui/layer/video_info.cpp
|
||||
src/imgui/layer/video_info.h
|
||||
src/imgui/renderer/imgui_core.cpp
|
||||
src/imgui/renderer/imgui_core.h
|
||||
src/imgui/renderer/imgui_impl_sdl3.cpp
|
||||
src/imgui/renderer/imgui_impl_sdl3.h
|
||||
src/imgui/renderer/imgui_impl_vulkan.cpp
|
||||
src/imgui/renderer/imgui_impl_vulkan.h
|
||||
)
|
||||
|
||||
set(INPUT src/input/controller.cpp
|
||||
src/input/controller.h
|
||||
)
|
||||
|
@ -617,6 +653,7 @@ endif()
|
|||
if (ENABLE_QT_GUI)
|
||||
qt_add_executable(shadps4
|
||||
${AUDIO_CORE}
|
||||
${IMGUI}
|
||||
${INPUT}
|
||||
${QT_GUI}
|
||||
${COMMON}
|
||||
|
@ -629,6 +666,7 @@ if (ENABLE_QT_GUI)
|
|||
else()
|
||||
add_executable(shadps4
|
||||
${AUDIO_CORE}
|
||||
${IMGUI}
|
||||
${INPUT}
|
||||
${COMMON}
|
||||
${CORE}
|
||||
|
@ -645,9 +683,12 @@ endif()
|
|||
|
||||
create_target_directory_groups(shadps4)
|
||||
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg)
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::SPIRV glslang::glslang SDL3::SDL3)
|
||||
|
||||
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
|
||||
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
|
||||
|
||||
if (APPLE)
|
||||
option(USE_SYSTEM_VULKAN_LOADER "Enables using the system Vulkan loader instead of directly linking with MoltenVK. Useful for loading validation layers." OFF)
|
||||
if (USE_SYSTEM_VULKAN_LOADER)
|
||||
|
@ -658,8 +699,10 @@ if (APPLE)
|
|||
target_link_libraries(shadps4 PRIVATE ${MOLTENVK})
|
||||
endif()
|
||||
|
||||
# Reserve system-managed memory space.
|
||||
target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,GUEST_SYSTEM,0x400000,-image_base,0x20000000000)
|
||||
if (ARCHITECTURE STREQUAL "x86_64")
|
||||
# Reserve system-managed memory space.
|
||||
target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,GUEST_SYSTEM,0x400000,-image_base,0x20000000000)
|
||||
endif()
|
||||
|
||||
# Replacement for std::chrono::time_zone
|
||||
target_link_libraries(shadps4 PRIVATE date::date-tz)
|
||||
|
|
|
@ -9,7 +9,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
||||
#### Debian & Ubuntu
|
||||
```
|
||||
sudo apt-get install build-essential libasound2-dev libpulse-dev libopenal-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git libevdev-dev libsdl2-2.0 libsdl2-dev libjack-dev libsndio-dev
|
||||
sudo apt-get install build-essential libasound2-dev libpulse-dev libopenal-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git libevdev-dev libsdl2-2.0 libsdl2-dev libjack-dev libsndio-dev qt6-base-dev qt6-tools-dev
|
||||
```
|
||||
|
||||
#### Fedora
|
||||
|
@ -34,9 +34,9 @@ git clone --recursive https://github.com/shadps4-emu/shadPS4.git
|
|||
cd shadPS4
|
||||
```
|
||||
|
||||
Generate the build directory in the shadPS4 directory:
|
||||
Generate the build directory in the shadPS4 directory. To enable the QT GUI, pass the ```-DENABLE_QT_GUI=ON``` flag:
|
||||
```
|
||||
cmake -S . -B build/
|
||||
cmake -S . -B build/ -DENABLE_QT_GUI=ON
|
||||
```
|
||||
|
||||
Enter the directory:
|
||||
|
@ -49,8 +49,11 @@ Use make to build the project:
|
|||
cmake --build . --parallel$(nproc)
|
||||
```
|
||||
|
||||
Now run the emulator:
|
||||
|
||||
Now run the emulator. If QT is enabled:
|
||||
```
|
||||
./shadps4
|
||||
```
|
||||
Otherwise, specify the path to your PKG's boot file:
|
||||
```
|
||||
./shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin
|
||||
```
|
||||
|
|
12
externals/CMakeLists.txt
vendored
12
externals/CMakeLists.txt
vendored
|
@ -43,7 +43,6 @@ else()
|
|||
endif()
|
||||
|
||||
if (NOT TARGET FFmpeg::ffmpeg)
|
||||
set(ARCHITECTURE "x86_64")
|
||||
add_subdirectory(ffmpeg-core)
|
||||
add_library(FFmpeg::ffmpeg ALIAS ffmpeg)
|
||||
endif()
|
||||
|
@ -155,6 +154,17 @@ if (APPLE)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# Dear ImGui
|
||||
add_library(Dear_ImGui
|
||||
dear_imgui/imgui.cpp
|
||||
dear_imgui/imgui_demo.cpp
|
||||
dear_imgui/imgui_draw.cpp
|
||||
dear_imgui/imgui_internal.h
|
||||
dear_imgui/imgui_tables.cpp
|
||||
dear_imgui/imgui_widgets.cpp
|
||||
)
|
||||
target_include_directories(Dear_ImGui INTERFACE dear_imgui/)
|
||||
|
||||
# Tracy
|
||||
option(TRACY_ENABLE "" ON)
|
||||
option(TRACY_NO_CRASH_HANDLER "" ON) # Otherwise texture cache exceptions will be treaten as a crash
|
||||
|
|
1
externals/dear_imgui
vendored
Submodule
1
externals/dear_imgui
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 636cd4a7d623a2bc9bf59bb3acbb4ca075befba3
|
10
src/common/arch.h
Normal file
10
src/common/arch.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
#define ARCH_X86_64 1
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define ARCH_ARM64 1
|
||||
#endif
|
|
@ -1,10 +1,17 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/backend.h"
|
||||
|
||||
#if defined(ARCH_X86_64)
|
||||
#define Crash() __asm__ __volatile__("int $3")
|
||||
#elif defined(ARCH_ARM64)
|
||||
#define Crash() __asm__ __volatile__("brk 0")
|
||||
#else
|
||||
#error "Missing Crash() implementation for target CPU architecture."
|
||||
#endif
|
||||
|
||||
void assert_fail_impl() {
|
||||
Common::Log::Stop();
|
||||
|
@ -18,3 +25,8 @@ void assert_fail_impl() {
|
|||
Crash();
|
||||
throw std::runtime_error("Unreachable code");
|
||||
}
|
||||
|
||||
void assert_fail_debug_msg(const char* msg) {
|
||||
LOG_CRITICAL(Debug, "Assertion Failed!\n{}", msg);
|
||||
assert_fail_impl();
|
||||
}
|
||||
|
|
35
src/common/fixed_value.h
Normal file
35
src/common/fixed_value.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief A template class that encapsulates a fixed, compile-time constant value.
|
||||
*
|
||||
* @tparam T The type of the value.
|
||||
* @tparam Value The fixed value of type T.
|
||||
*
|
||||
* This class provides a way to encapsulate a value that is constant and known at compile-time.
|
||||
* The value is stored as a private member and cannot be changed. Any attempt to assign a new
|
||||
* value to an object of this class will reset it to the fixed value.
|
||||
*/
|
||||
template <typename T, T Value>
|
||||
class FixedValue {
|
||||
T m_value{Value};
|
||||
|
||||
public:
|
||||
constexpr FixedValue() = default;
|
||||
|
||||
constexpr explicit(false) operator T() const {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
FixedValue& operator=(const T&) {
|
||||
m_value = Value;
|
||||
return *this;
|
||||
}
|
||||
FixedValue& operator=(T&&) noexcept {
|
||||
m_value = {Value};
|
||||
return *this;
|
||||
}
|
||||
};
|
|
@ -117,6 +117,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
|||
CLS(Render) \
|
||||
SUB(Render, Vulkan) \
|
||||
SUB(Render, Recompiler) \
|
||||
CLS(ImGui) \
|
||||
CLS(Input) \
|
||||
CLS(Tty) \
|
||||
CLS(Loader)
|
||||
|
|
|
@ -84,6 +84,7 @@ enum class Class : u8 {
|
|||
Render, ///< Video Core
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
Render_Recompiler, ///< Shader recompiler
|
||||
ImGui, ///< ImGui
|
||||
Loader, ///< ROM loader
|
||||
Input, ///< Input emulation
|
||||
Tty, ///< Debug output from emu
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "common/arch.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
@ -13,15 +15,20 @@ namespace Common {
|
|||
|
||||
#ifdef _MSC_VER
|
||||
__forceinline static u64 FencedRDTSC() {
|
||||
#ifdef ARCH_X86_64
|
||||
_mm_lfence();
|
||||
_ReadWriteBarrier();
|
||||
const u64 result = __rdtsc();
|
||||
_mm_lfence();
|
||||
_ReadWriteBarrier();
|
||||
return result;
|
||||
#else
|
||||
#error "Missing FencedRDTSC() implementation for target CPU architecture."
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
static inline u64 FencedRDTSC() {
|
||||
#ifdef ARCH_X86_64
|
||||
u64 eax;
|
||||
u64 edx;
|
||||
asm volatile("lfence\n\t"
|
||||
|
@ -29,6 +36,16 @@ static inline u64 FencedRDTSC() {
|
|||
"lfence\n\t"
|
||||
: "=a"(eax), "=d"(edx));
|
||||
return (edx << 32) | eax;
|
||||
#elif defined(ARCH_ARM64)
|
||||
u64 ret;
|
||||
asm volatile("isb\n\t"
|
||||
"mrs %0, cntvct_el0\n\t"
|
||||
"isb\n\t"
|
||||
: "=r"(ret)::"memory");
|
||||
return ret;
|
||||
#else
|
||||
#error "Missing FencedRDTSC() implementation for target CPU architecture."
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
|
||||
#include <boost/icl/separate_interval_set.hpp>
|
||||
#include "common/alignment.h"
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
#include "core/address_space.h"
|
||||
#include "core/libraries/kernel/memory_management.h"
|
||||
#include "core/memory.h"
|
||||
#include "libraries/error_codes.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
@ -15,9 +17,8 @@
|
|||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#include "libraries/error_codes.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#if defined(__APPLE__) && defined(ARCH_X86_64)
|
||||
// Reserve space for the system address space using a zerofill section.
|
||||
asm(".zerofill GUEST_SYSTEM,GUEST_SYSTEM,__guest_system,0xFBFC00000");
|
||||
#endif
|
||||
|
@ -308,12 +309,12 @@ struct AddressSpace::Impl {
|
|||
|
||||
constexpr int protection_flags = PROT_READ | PROT_WRITE;
|
||||
constexpr int base_map_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
|
||||
#ifdef __APPLE__
|
||||
// On ARM64 Macs, we run into limitations due to the commpage from 0xFC0000000 - 0xFFFFFFFFF
|
||||
// and the GPU carveout region from 0x1000000000 - 0x6FFFFFFFFF. We can allocate the system
|
||||
// managed region, as well as system reserved if reduced in size slightly, but we cannot map
|
||||
// the user region where we want, so we must let the OS put it wherever possible and hope
|
||||
// the game won't rely on its location.
|
||||
#if defined(__APPLE__) && defined(ARCH_X86_64)
|
||||
// On ARM64 Macs under Rosetta 2, we run into limitations due to the commpage from
|
||||
// 0xFC0000000 - 0xFFFFFFFFF and the GPU carveout region from 0x1000000000 - 0x6FFFFFFFFF.
|
||||
// We can allocate the system managed region, as well as system reserved if reduced in size
|
||||
// slightly, but we cannot map the user region where we want, so we must let the OS put it
|
||||
// wherever possible and hope the game won't rely on its location.
|
||||
system_managed_base = reinterpret_cast<u8*>(
|
||||
mmap(reinterpret_cast<void*>(SYSTEM_MANAGED_MIN), system_managed_size, protection_flags,
|
||||
base_map_flags | MAP_FIXED, -1, 0));
|
||||
|
@ -325,12 +326,22 @@ struct AddressSpace::Impl {
|
|||
protection_flags, base_map_flags, -1, 0));
|
||||
#else
|
||||
const auto virtual_size = system_managed_size + system_reserved_size + user_size;
|
||||
#if defined(ARCH_X86_64)
|
||||
const auto virtual_base =
|
||||
reinterpret_cast<u8*>(mmap(reinterpret_cast<void*>(SYSTEM_MANAGED_MIN), virtual_size,
|
||||
protection_flags, base_map_flags | MAP_FIXED, -1, 0));
|
||||
system_managed_base = virtual_base;
|
||||
system_reserved_base = reinterpret_cast<u8*>(SYSTEM_RESERVED_MIN);
|
||||
user_base = reinterpret_cast<u8*>(USER_MIN);
|
||||
#else
|
||||
// Map memory wherever possible and instruction translation can handle offsetting to the
|
||||
// base.
|
||||
const auto virtual_base = reinterpret_cast<u8*>(
|
||||
mmap(nullptr, virtual_size, protection_flags, base_map_flags, -1, 0));
|
||||
system_managed_base = virtual_base;
|
||||
system_reserved_base = virtual_base + SYSTEM_RESERVED_MIN - SYSTEM_MANAGED_MIN;
|
||||
user_base = virtual_base + USER_MIN - SYSTEM_MANAGED_MIN;
|
||||
#endif
|
||||
#endif
|
||||
if (system_managed_base == MAP_FAILED || system_reserved_base == MAP_FAILED ||
|
||||
user_base == MAP_FAILED) {
|
||||
|
@ -430,9 +441,11 @@ struct AddressSpace::Impl {
|
|||
if (write) {
|
||||
flags |= PROT_WRITE;
|
||||
}
|
||||
#ifdef ARCH_X86_64
|
||||
if (execute) {
|
||||
flags |= PROT_EXEC;
|
||||
}
|
||||
#endif
|
||||
int ret = mprotect(reinterpret_cast<void*>(virtual_addr), size, flags);
|
||||
ASSERT_MSG(ret == 0, "mprotect failed: {}", strerror(errno));
|
||||
}
|
||||
|
@ -463,8 +476,14 @@ AddressSpace::~AddressSpace() = default;
|
|||
|
||||
void* AddressSpace::Map(VAddr virtual_addr, size_t size, u64 alignment, PAddr phys_addr,
|
||||
bool is_exec) {
|
||||
return impl->Map(virtual_addr, phys_addr, size,
|
||||
is_exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
|
||||
#if ARCH_X86_64
|
||||
const auto prot = is_exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
|
||||
#else
|
||||
// On non-native architectures, we can simplify things by ignoring the execute flag for the
|
||||
// canonical copy of the memory and rely on the JIT to map translated code as executable.
|
||||
constexpr auto prot = PAGE_READWRITE;
|
||||
#endif
|
||||
return impl->Map(virtual_addr, phys_addr, size, prot);
|
||||
}
|
||||
|
||||
void* AddressSpace::MapFile(VAddr virtual_addr, size_t size, size_t offset, u32 prot,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "common/arch.h"
|
||||
#include "common/enum.h"
|
||||
#include "common/types.h"
|
||||
|
||||
|
@ -23,7 +24,7 @@ constexpr VAddr CODE_BASE_OFFSET = 0x100000000ULL;
|
|||
constexpr VAddr SYSTEM_MANAGED_MIN = 0x00000400000ULL;
|
||||
constexpr VAddr SYSTEM_MANAGED_MAX = 0x07FFFFBFFFULL;
|
||||
constexpr VAddr SYSTEM_RESERVED_MIN = 0x07FFFFC000ULL;
|
||||
#ifdef __APPLE__
|
||||
#if defined(__APPLE__) && defined(ARCH_X86_64)
|
||||
// Can only comfortably reserve the first 0x7C0000000 of system reserved space.
|
||||
constexpr VAddr SYSTEM_RESERVED_MAX = 0xFBFFFFFFFULL;
|
||||
#else
|
||||
|
|
|
@ -20,6 +20,17 @@ extern "C" {
|
|||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
// The av_err2str macro in libavutil/error.h does not play nice with C++
|
||||
#ifdef av_err2str
|
||||
#undef av_err2str
|
||||
#include <string>
|
||||
av_always_inline std::string av_err2string(int errnum) {
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
return av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, errnum);
|
||||
}
|
||||
#define av_err2str(err) av_err2string(err).c_str()
|
||||
#endif // av_err2str
|
||||
|
||||
namespace Libraries::AvPlayer {
|
||||
|
||||
using namespace Kernel;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <thread>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
#include "common/logging/log.h"
|
||||
|
@ -989,7 +990,9 @@ static void cleanup_thread(void* arg) {
|
|||
static void* run_thread(void* arg) {
|
||||
auto* thread = static_cast<ScePthread>(arg);
|
||||
Common::SetCurrentThreadName(thread->name.c_str());
|
||||
#ifdef ARCH_X86_64
|
||||
Core::InitializeThreadPatchStack();
|
||||
#endif
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
linker->InitTlsForThread(false);
|
||||
void* ret = nullptr;
|
||||
|
@ -1182,6 +1185,7 @@ int PS4_SYSV_ABI scePthreadCondattrDestroy(ScePthreadCondattr* attr) {
|
|||
int result = pthread_condattr_destroy(&(*attr)->cond_attr);
|
||||
|
||||
LOG_DEBUG(Kernel_Pthread, "scePthreadCondattrDestroy: result = {} ", result);
|
||||
delete *attr;
|
||||
|
||||
switch (result) {
|
||||
case 0:
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
namespace Libraries::CommonDialog {
|
||||
|
||||
bool g_isInitialized = false;
|
||||
bool g_isUsed = false;
|
||||
|
||||
int PS4_SYSV_ABI _ZN3sce16CommonDialogUtil12getSelfAppIdEv() {
|
||||
LOG_ERROR(Lib_CommonDlg, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
|
@ -83,14 +86,19 @@ int PS4_SYSV_ABI _ZTVN3sce16CommonDialogUtil6ClientE() {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceCommonDialogInitialize() {
|
||||
LOG_ERROR(Lib_CommonDlg, "(DUMMY) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceCommonDialogInitialize() {
|
||||
if (g_isInitialized) {
|
||||
LOG_INFO(Lib_CommonDlg, "already initialized");
|
||||
return Error::ALREADY_SYSTEM_INITIALIZED;
|
||||
}
|
||||
LOG_DEBUG(Lib_CommonDlg, "initialized");
|
||||
g_isInitialized = true;
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceCommonDialogIsUsed() {
|
||||
LOG_ERROR(Lib_CommonDlg, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
bool PS4_SYSV_ABI sceCommonDialogIsUsed() {
|
||||
LOG_TRACE(Lib_CommonDlg, "called");
|
||||
return g_isUsed;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI Func_0FF577E4E8457883() {
|
||||
|
|
|
@ -11,9 +11,44 @@ class SymbolsResolver;
|
|||
|
||||
namespace Libraries::CommonDialog {
|
||||
|
||||
struct OrbisCommonDialogBaseParam {
|
||||
enum class Status : u32 {
|
||||
NONE = 0,
|
||||
INITIALIZED = 1,
|
||||
RUNNING = 2,
|
||||
FINISHED = 3,
|
||||
};
|
||||
|
||||
enum class Result : u32 {
|
||||
OK = 0,
|
||||
USER_CANCELED = 1,
|
||||
};
|
||||
|
||||
enum class Error : u32 {
|
||||
OK = 0,
|
||||
NOT_SYSTEM_INITIALIZED = 0x80B80001,
|
||||
ALREADY_SYSTEM_INITIALIZED = 0x80B80002,
|
||||
NOT_INITIALIZED = 0x80B80003,
|
||||
ALREADY_INITIALIZED = 0x80B80004,
|
||||
NOT_FINISHED = 0x80B80005,
|
||||
INVALID_STATE = 0x80B80006,
|
||||
RESULT_NONE = 0x80B80007,
|
||||
BUSY = 0x80B80008,
|
||||
OUT_OF_MEMORY = 0x80B80009,
|
||||
PARAM_INVALID = 0x80B8000A,
|
||||
NOT_RUNNING = 0x80B8000B,
|
||||
ALREADY_CLOSE = 0x80B8000C,
|
||||
ARG_NULL = 0x80B8000D,
|
||||
UNEXPECTED_FATAL = 0x80B8000E,
|
||||
NOT_SUPPORTED = 0x80B8000F,
|
||||
INHIBIT_SHAREPLAY_CLIENT = 0x80B80010,
|
||||
};
|
||||
|
||||
extern bool g_isInitialized;
|
||||
extern bool g_isUsed;
|
||||
|
||||
struct BaseParam {
|
||||
std::size_t size;
|
||||
u8 reserved[36];
|
||||
std::array<u8, 36> reserved;
|
||||
u32 magic;
|
||||
};
|
||||
|
||||
|
@ -32,8 +67,8 @@ int PS4_SYSV_ABI _ZNK3sce16CommonDialogUtil6Client8getAppIdEv();
|
|||
int PS4_SYSV_ABI _ZNK3sce16CommonDialogUtil6Client8isFinishEv();
|
||||
int PS4_SYSV_ABI _ZNK3sce16CommonDialogUtil6Client9getResultEv();
|
||||
int PS4_SYSV_ABI _ZTVN3sce16CommonDialogUtil6ClientE();
|
||||
int PS4_SYSV_ABI sceCommonDialogInitialize();
|
||||
int PS4_SYSV_ABI sceCommonDialogIsUsed();
|
||||
Error PS4_SYSV_ABI sceCommonDialogInitialize();
|
||||
bool PS4_SYSV_ABI sceCommonDialogIsUsed();
|
||||
int PS4_SYSV_ABI Func_0FF577E4E8457883();
|
||||
int PS4_SYSV_ABI Func_41716C2CE379416C();
|
||||
int PS4_SYSV_ABI Func_483A427D8F6E0748();
|
||||
|
|
|
@ -1,79 +1,157 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/system/msgdialog.h"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include "imgui_internal.h"
|
||||
#include "msgdialog_ui.h"
|
||||
|
||||
namespace Libraries::MsgDialog {
|
||||
|
||||
int PS4_SYSV_ABI sceMsgDialogClose() {
|
||||
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
using CommonDialog::Error;
|
||||
using CommonDialog::Result;
|
||||
using CommonDialog::Status;
|
||||
|
||||
int PS4_SYSV_ABI sceMsgDialogGetResult() {
|
||||
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
static auto g_status = Status::NONE;
|
||||
static MsgDialogState g_state{};
|
||||
static DialogResult g_result{};
|
||||
static MsgDialogUi g_msg_dialog_ui;
|
||||
|
||||
int PS4_SYSV_ABI sceMsgDialogGetStatus() {
|
||||
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceMsgDialogInitialize() {
|
||||
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceMsgDialogOpen(const OrbisMsgDialogParam* param) {
|
||||
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
|
||||
switch (param->mode) {
|
||||
case ORBIS_MSG_DIALOG_MODE_USER_MSG:
|
||||
LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen userMsg type = %s msg = %s",
|
||||
magic_enum::enum_name(param->userMsgParam->buttonType), param->userMsgParam->msg);
|
||||
break;
|
||||
case ORBIS_MSG_DIALOG_MODE_PROGRESS_BAR:
|
||||
LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen progressBar type = %s msg = %s",
|
||||
magic_enum::enum_name(param->progBarParam->barType), param->progBarParam->msg);
|
||||
break;
|
||||
case ORBIS_MSG_DIALOG_MODE_SYSTEM_MSG:
|
||||
LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen systemMsg type: %s",
|
||||
magic_enum::enum_name(param->sysMsgParam->sysMsgType));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
Error PS4_SYSV_ABI sceMsgDialogClose() {
|
||||
LOG_DEBUG(Lib_MsgDlg, "called");
|
||||
if (g_status != Status::RUNNING) {
|
||||
return Error::NOT_RUNNING;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
g_msg_dialog_ui.Finish(ButtonId::INVALID);
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceMsgDialogProgressBarInc() {
|
||||
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceMsgDialogGetResult(DialogResult* result) {
|
||||
LOG_DEBUG(Lib_MsgDlg, "called");
|
||||
if (g_status != Status::FINISHED) {
|
||||
return Error::NOT_FINISHED;
|
||||
}
|
||||
if (result == nullptr) {
|
||||
return Error::ARG_NULL;
|
||||
}
|
||||
for (const auto v : result->reserved) {
|
||||
if (v != 0) {
|
||||
return Error::PARAM_INVALID;
|
||||
}
|
||||
}
|
||||
*result = g_result;
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceMsgDialogProgressBarSetMsg() {
|
||||
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Status PS4_SYSV_ABI sceMsgDialogGetStatus() {
|
||||
LOG_TRACE(Lib_MsgDlg, "called status={}", magic_enum::enum_name(g_status));
|
||||
return g_status;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceMsgDialogProgressBarSetValue() {
|
||||
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceMsgDialogInitialize() {
|
||||
LOG_DEBUG(Lib_MsgDlg, "called");
|
||||
if (!CommonDialog::g_isInitialized) {
|
||||
return Error::NOT_SYSTEM_INITIALIZED;
|
||||
}
|
||||
if (g_status != Status::NONE) {
|
||||
return Error::ALREADY_INITIALIZED;
|
||||
}
|
||||
if (CommonDialog::g_isUsed) {
|
||||
return Error::BUSY;
|
||||
}
|
||||
g_status = Status::INITIALIZED;
|
||||
CommonDialog::g_isUsed = true;
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceMsgDialogTerminate() {
|
||||
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceMsgDialogOpen(const OrbisParam* param) {
|
||||
if (g_status != Status::INITIALIZED && g_status != Status::FINISHED) {
|
||||
LOG_INFO(Lib_MsgDlg, "called without initialize");
|
||||
return Error::INVALID_STATE;
|
||||
}
|
||||
if (param == nullptr) {
|
||||
LOG_DEBUG(Lib_MsgDlg, "called param:(NULL)");
|
||||
return Error::ARG_NULL;
|
||||
}
|
||||
LOG_DEBUG(Lib_MsgDlg, "called param->mode: {}", magic_enum::enum_name(param->mode));
|
||||
ASSERT(param->size == sizeof(OrbisParam));
|
||||
ASSERT(param->baseParam.size == sizeof(CommonDialog::BaseParam));
|
||||
g_result = {};
|
||||
g_state = MsgDialogState{*param};
|
||||
g_status = Status::RUNNING;
|
||||
g_msg_dialog_ui = MsgDialogUi(&g_state, &g_status, &g_result);
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceMsgDialogUpdateStatus() {
|
||||
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceMsgDialogProgressBarInc(OrbisMsgDialogProgressBarTarget target, u32 delta) {
|
||||
LOG_DEBUG(Lib_MsgDlg, "called");
|
||||
if (g_status != Status::RUNNING) {
|
||||
return Error::NOT_RUNNING;
|
||||
}
|
||||
if (g_state.GetMode() != MsgDialogMode::PROGRESS_BAR) {
|
||||
return Error::NOT_SUPPORTED;
|
||||
}
|
||||
if (target != OrbisMsgDialogProgressBarTarget::DEFAULT) {
|
||||
return Error::PARAM_INVALID;
|
||||
}
|
||||
g_state.GetState<MsgDialogState::ProgressState>().progress += delta;
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
Error PS4_SYSV_ABI sceMsgDialogProgressBarSetMsg(OrbisMsgDialogProgressBarTarget target,
|
||||
const char* msg) {
|
||||
LOG_DEBUG(Lib_MsgDlg, "called");
|
||||
if (g_status != Status::RUNNING) {
|
||||
return Error::NOT_RUNNING;
|
||||
}
|
||||
if (g_state.GetMode() != MsgDialogMode::PROGRESS_BAR) {
|
||||
return Error::NOT_SUPPORTED;
|
||||
}
|
||||
if (target != OrbisMsgDialogProgressBarTarget::DEFAULT) {
|
||||
return Error::PARAM_INVALID;
|
||||
}
|
||||
g_state.GetState<MsgDialogState::ProgressState>().msg = msg;
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
Error PS4_SYSV_ABI sceMsgDialogProgressBarSetValue(OrbisMsgDialogProgressBarTarget target,
|
||||
u32 value) {
|
||||
LOG_DEBUG(Lib_MsgDlg, "called");
|
||||
if (g_status != Status::RUNNING) {
|
||||
return Error::NOT_RUNNING;
|
||||
}
|
||||
if (g_state.GetMode() != MsgDialogMode::PROGRESS_BAR) {
|
||||
return Error::NOT_SUPPORTED;
|
||||
}
|
||||
if (target != OrbisMsgDialogProgressBarTarget::DEFAULT) {
|
||||
return Error::PARAM_INVALID;
|
||||
}
|
||||
g_state.GetState<MsgDialogState::ProgressState>().progress = value;
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
Error PS4_SYSV_ABI sceMsgDialogTerminate() {
|
||||
LOG_DEBUG(Lib_MsgDlg, "called");
|
||||
if (g_status == Status::RUNNING) {
|
||||
sceMsgDialogClose();
|
||||
}
|
||||
if (g_status == Status::NONE) {
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
g_status = Status::NONE;
|
||||
CommonDialog::g_isUsed = false;
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
Status PS4_SYSV_ABI sceMsgDialogUpdateStatus() {
|
||||
LOG_TRACE(Lib_MsgDlg, "called status={}", magic_enum::enum_name(g_status));
|
||||
return g_status;
|
||||
}
|
||||
|
||||
void RegisterlibSceMsgDialog(Core::Loader::SymbolsResolver* sym) {
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/system/commondialog.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
|
@ -12,95 +11,23 @@ class SymbolsResolver;
|
|||
|
||||
namespace Libraries::MsgDialog {
|
||||
|
||||
using OrbisUserServiceUserId = s32;
|
||||
struct DialogResult;
|
||||
struct OrbisParam;
|
||||
enum class OrbisMsgDialogProgressBarTarget : u32;
|
||||
|
||||
enum OrbisCommonDialogStatus {
|
||||
ORBIS_COMMON_DIALOG_STATUS_NONE = 0,
|
||||
ORBIS_COMMON_DIALOG_STATUS_INITIALIZED = 1,
|
||||
ORBIS_COMMON_DIALOG_STATUS_RUNNING = 2,
|
||||
ORBIS_COMMON_DIALOG_STATUS_FINISHED = 3
|
||||
};
|
||||
|
||||
enum OrbisMsgDialogMode {
|
||||
ORBIS_MSG_DIALOG_MODE_USER_MSG = 1,
|
||||
ORBIS_MSG_DIALOG_MODE_PROGRESS_BAR = 2,
|
||||
ORBIS_MSG_DIALOG_MODE_SYSTEM_MSG = 3,
|
||||
};
|
||||
|
||||
enum OrbisMsgDialogButtonType {
|
||||
ORBIS_MSG_DIALOG_BUTTON_TYPE_OK = 0,
|
||||
ORBIS_MSG_DIALOG_BUTTON_TYPE_YESNO = 1,
|
||||
ORBIS_MSG_DIALOG_BUTTON_TYPE_NONE = 2,
|
||||
ORBIS_MSG_DIALOG_BUTTON_TYPE_OK_CANCEL = 3,
|
||||
ORBIS_MSG_DIALOG_BUTTON_TYPE_WAIT = 5,
|
||||
ORBIS_MSG_DIALOG_BUTTON_TYPE_WAIT_CANCEL = 6,
|
||||
ORBIS_MSG_DIALOG_BUTTON_TYPE_YESNO_FOCUS_NO = 7,
|
||||
ORBIS_MSG_DIALOG_BUTTON_TYPE_OK_CANCEL_FOCUS_CANCEL = 8,
|
||||
ORBIS_MSG_DIALOG_BUTTON_TYPE_2BUTTONS = 9,
|
||||
};
|
||||
|
||||
enum OrbisMsgDialogProgressBarType {
|
||||
ORBIS_MSG_DIALOG_PROGRESSBAR_TYPE_PERCENTAGE = 0,
|
||||
ORBIS_MSG_DIALOG_PROGRESSBAR_TYPE_PERCENTAGE_CANCEL = 1,
|
||||
};
|
||||
|
||||
enum OrbisMsgDialogSystemMessageType {
|
||||
ORBIS_MSG_DIALOG_SYSMSG_TYPE_TRC_EMPTY_STORE = 0,
|
||||
ORBIS_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION = 1,
|
||||
ORBIS_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_UGC_RESTRICTION = 2,
|
||||
ORBIS_MSG_DIALOG_SYSMSG_TYPE_CAMERA_NOT_CONNECTED = 4,
|
||||
ORBIS_MSG_DIALOG_SYSMSG_TYPE_WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED = 5,
|
||||
};
|
||||
|
||||
struct OrbisMsgDialogButtonsParam {
|
||||
const char* msg1;
|
||||
const char* msg2;
|
||||
char reserved[32];
|
||||
};
|
||||
|
||||
struct OrbisMsgDialogUserMessageParam {
|
||||
OrbisMsgDialogButtonType buttonType;
|
||||
s32 : 32;
|
||||
const char* msg;
|
||||
OrbisMsgDialogButtonsParam* buttonsParam;
|
||||
char reserved[24];
|
||||
};
|
||||
|
||||
struct OrbisMsgDialogProgressBarParam {
|
||||
OrbisMsgDialogProgressBarType barType;
|
||||
int32_t : 32;
|
||||
const char* msg;
|
||||
char reserved[64];
|
||||
};
|
||||
|
||||
struct OrbisMsgDialogSystemMessageParam {
|
||||
OrbisMsgDialogSystemMessageType sysMsgType;
|
||||
char reserved[32];
|
||||
};
|
||||
|
||||
struct OrbisMsgDialogParam {
|
||||
CommonDialog::OrbisCommonDialogBaseParam baseParam;
|
||||
std::size_t size;
|
||||
OrbisMsgDialogMode mode;
|
||||
s32 : 32;
|
||||
OrbisMsgDialogUserMessageParam* userMsgParam;
|
||||
OrbisMsgDialogProgressBarParam* progBarParam;
|
||||
OrbisMsgDialogSystemMessageParam* sysMsgParam;
|
||||
OrbisUserServiceUserId userId;
|
||||
char reserved[40];
|
||||
s32 : 32;
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI sceMsgDialogClose();
|
||||
int PS4_SYSV_ABI sceMsgDialogGetResult();
|
||||
int PS4_SYSV_ABI sceMsgDialogGetStatus();
|
||||
int PS4_SYSV_ABI sceMsgDialogInitialize();
|
||||
s32 PS4_SYSV_ABI sceMsgDialogOpen(const OrbisMsgDialogParam* param);
|
||||
int PS4_SYSV_ABI sceMsgDialogProgressBarInc();
|
||||
int PS4_SYSV_ABI sceMsgDialogProgressBarSetMsg();
|
||||
int PS4_SYSV_ABI sceMsgDialogProgressBarSetValue();
|
||||
int PS4_SYSV_ABI sceMsgDialogTerminate();
|
||||
int PS4_SYSV_ABI sceMsgDialogUpdateStatus();
|
||||
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogClose();
|
||||
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogGetResult(DialogResult* result);
|
||||
CommonDialog::Status PS4_SYSV_ABI sceMsgDialogGetStatus();
|
||||
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogInitialize();
|
||||
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogOpen(const OrbisParam* param);
|
||||
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogProgressBarInc(OrbisMsgDialogProgressBarTarget,
|
||||
u32 delta);
|
||||
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogProgressBarSetMsg(OrbisMsgDialogProgressBarTarget,
|
||||
const char* msg);
|
||||
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogProgressBarSetValue(OrbisMsgDialogProgressBarTarget,
|
||||
u32 value);
|
||||
CommonDialog::Error PS4_SYSV_ABI sceMsgDialogTerminate();
|
||||
CommonDialog::Status PS4_SYSV_ABI sceMsgDialogUpdateStatus();
|
||||
|
||||
void RegisterlibSceMsgDialog(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::MsgDialog
|
||||
|
|
272
src/core/libraries/system/msgdialog_ui.cpp
Normal file
272
src/core/libraries/system/msgdialog_ui.cpp
Normal file
|
@ -0,0 +1,272 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
#include "common/assert.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "msgdialog_ui.h"
|
||||
|
||||
using namespace ImGui;
|
||||
using namespace Libraries::CommonDialog;
|
||||
using namespace Libraries::MsgDialog;
|
||||
|
||||
static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f};
|
||||
static constexpr float PROGRESS_BAR_WIDTH{0.8f};
|
||||
|
||||
struct {
|
||||
int count = 0;
|
||||
const char* text1;
|
||||
const char* text2;
|
||||
} static constexpr user_button_texts[] = {
|
||||
{1, "OK"}, // 0 OK
|
||||
{2, "Yes", "No"}, // 1 YESNO
|
||||
{0}, // 2 NONE
|
||||
{2, "OK", "Cancel"}, // 3 OK_CANCEL
|
||||
{}, // 4 !!NOP
|
||||
{1, "Wait"}, // 5 WAIT
|
||||
{2, "Wait", "Cancel"}, // 6 WAIT_CANCEL
|
||||
{2, "Yes", "No"}, // 7 YESNO_FOCUS_NO
|
||||
{2, "OK", "Cancel"}, // 8 OK_CANCEL_FOCUS_CANCEL
|
||||
{0xFF}, // 9 TWO_BUTTONS
|
||||
};
|
||||
static_assert(std::size(user_button_texts) == static_cast<int>(ButtonType::TWO_BUTTONS) + 1);
|
||||
|
||||
static void DrawCenteredText(const char* text) {
|
||||
const auto ws = GetWindowSize();
|
||||
const auto text_size = CalcTextSize(text, nullptr, false, ws.x - 40.0f);
|
||||
PushTextWrapPos(ws.x - 30.0f);
|
||||
SetCursorPos({
|
||||
(ws.x - text_size.x) / 2.0f,
|
||||
(ws.y - text_size.y) / 2.0f - 50.0f,
|
||||
});
|
||||
Text("%s", text);
|
||||
PopTextWrapPos();
|
||||
}
|
||||
|
||||
MsgDialogState::MsgDialogState(const OrbisParam& param) {
|
||||
this->mode = param.mode;
|
||||
switch (mode) {
|
||||
case MsgDialogMode::USER_MSG: {
|
||||
ASSERT(param.userMsgParam);
|
||||
const auto& v = *param.userMsgParam;
|
||||
auto state = UserState{
|
||||
.type = v.buttonType,
|
||||
.msg = std::string(v.msg),
|
||||
};
|
||||
if (v.buttonType == ButtonType::TWO_BUTTONS) {
|
||||
ASSERT(v.buttonsParam);
|
||||
state.btn_param1 = std::string(v.buttonsParam->msg1);
|
||||
state.btn_param2 = std::string(v.buttonsParam->msg2);
|
||||
}
|
||||
this->state = state;
|
||||
} break;
|
||||
case MsgDialogMode::PROGRESS_BAR: {
|
||||
ASSERT(param.progBarParam);
|
||||
const auto& v = *param.progBarParam;
|
||||
this->state = ProgressState{
|
||||
.type = v.barType,
|
||||
.msg = std::string(v.msg),
|
||||
.progress = 0,
|
||||
};
|
||||
} break;
|
||||
case MsgDialogMode::SYSTEM_MSG: {
|
||||
ASSERT(param.sysMsgParam);
|
||||
const auto& v = *param.sysMsgParam;
|
||||
this->state = SystemState{
|
||||
.type = v.sysMsgType,
|
||||
};
|
||||
} break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown dialog mode");
|
||||
}
|
||||
}
|
||||
|
||||
void MsgDialogUi::DrawUser() {
|
||||
const auto& [button_type, msg, btn_param1, btn_param2] =
|
||||
state->GetState<MsgDialogState::UserState>();
|
||||
const auto ws = GetWindowSize();
|
||||
DrawCenteredText(msg.c_str());
|
||||
ASSERT(button_type <= ButtonType::TWO_BUTTONS);
|
||||
auto [count, text1, text2] = user_button_texts[static_cast<u32>(button_type)];
|
||||
if (count == 0xFF) { // TWO_BUTTONS -> User defined message
|
||||
count = 2;
|
||||
text1 = btn_param1.c_str();
|
||||
text2 = btn_param2.c_str();
|
||||
}
|
||||
const bool focus_first = button_type < ButtonType::YESNO_FOCUS_NO;
|
||||
SetCursorPos({
|
||||
ws.x / 2.0f - BUTTON_SIZE.x / 2.0f * static_cast<float>(count),
|
||||
ws.y - 10.0f - BUTTON_SIZE.y,
|
||||
});
|
||||
BeginGroup();
|
||||
if (count > 0) {
|
||||
// First button at the right, so we render the second button first
|
||||
if (count == 2) {
|
||||
PushID(2);
|
||||
if (Button(text2, BUTTON_SIZE)) {
|
||||
switch (button_type) {
|
||||
case ButtonType::OK_CANCEL:
|
||||
case ButtonType::WAIT_CANCEL:
|
||||
case ButtonType::OK_CANCEL_FOCUS_CANCEL:
|
||||
Finish(ButtonId::INVALID, Result::USER_CANCELED);
|
||||
break;
|
||||
default:
|
||||
Finish(ButtonId::BUTTON2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (first_render && !focus_first) {
|
||||
SetItemCurrentNavFocus();
|
||||
}
|
||||
PopID();
|
||||
SameLine();
|
||||
}
|
||||
PushID(1);
|
||||
if (Button(text1, BUTTON_SIZE)) {
|
||||
Finish(ButtonId::BUTTON1);
|
||||
}
|
||||
if (first_render && focus_first) {
|
||||
SetItemCurrentNavFocus();
|
||||
}
|
||||
PopID();
|
||||
SameLine();
|
||||
}
|
||||
EndGroup();
|
||||
}
|
||||
|
||||
void MsgDialogUi::DrawProgressBar() {
|
||||
const auto& [bar_type, msg, progress_bar_value] =
|
||||
state->GetState<MsgDialogState::ProgressState>();
|
||||
DrawCenteredText(msg.c_str());
|
||||
const auto ws = GetWindowSize();
|
||||
SetCursorPos({
|
||||
ws.x * ((1 - PROGRESS_BAR_WIDTH) / 2.0f),
|
||||
ws.y - 10.0f - BUTTON_SIZE.y,
|
||||
});
|
||||
const bool has_cancel = bar_type == ProgressBarType::PERCENTAGE_CANCEL;
|
||||
float bar_width = PROGRESS_BAR_WIDTH * ws.x;
|
||||
if (has_cancel) {
|
||||
bar_width -= BUTTON_SIZE.x - 10.0f;
|
||||
}
|
||||
BeginGroup();
|
||||
ProgressBar(static_cast<float>(progress_bar_value) / 100.0f, {bar_width, BUTTON_SIZE.y});
|
||||
if (has_cancel) {
|
||||
SameLine();
|
||||
if (Button("Cancel", BUTTON_SIZE)) {
|
||||
Finish(ButtonId::INVALID, Result::USER_CANCELED);
|
||||
}
|
||||
if (first_render) {
|
||||
SetItemCurrentNavFocus();
|
||||
}
|
||||
}
|
||||
EndGroup();
|
||||
}
|
||||
|
||||
struct {
|
||||
const char* text;
|
||||
} static constexpr system_message_texts[] = {
|
||||
"No product available in the store.", // TRC_EMPTY_STORE
|
||||
"PSN chat restriction.", // TRC_PSN_CHAT_RESTRICTION
|
||||
"User-generated Media restriction", // TRC_PSN_UGC_RESTRICTION
|
||||
nullptr, // !!NOP
|
||||
"Camera not connected.", // CAMERA_NOT_CONNECTED
|
||||
"Warning: profile picture and name are not set", // WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED
|
||||
};
|
||||
static_assert(std::size(system_message_texts) ==
|
||||
static_cast<int>(SystemMessageType::WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED) + 1);
|
||||
|
||||
void MsgDialogUi::DrawSystemMessage() {
|
||||
// TODO: Implement go to settings & user profile
|
||||
const auto& [msg_type] = state->GetState<MsgDialogState::SystemState>();
|
||||
ASSERT(msg_type <= SystemMessageType::WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED);
|
||||
auto [msg] = system_message_texts[static_cast<u32>(msg_type)];
|
||||
DrawCenteredText(msg);
|
||||
const auto ws = GetWindowSize();
|
||||
SetCursorPos({
|
||||
ws.x / 2.0f - BUTTON_SIZE.x / 2.0f,
|
||||
ws.y - 10.0f - BUTTON_SIZE.y,
|
||||
});
|
||||
if (Button("OK", BUTTON_SIZE)) {
|
||||
Finish(ButtonId::OK);
|
||||
}
|
||||
if (first_render) {
|
||||
SetItemCurrentNavFocus();
|
||||
}
|
||||
}
|
||||
|
||||
MsgDialogUi::MsgDialogUi(MsgDialogState* state, Status* status, DialogResult* result)
|
||||
: state(state), status(status), result(result) {
|
||||
if (status && *status == Status::RUNNING) {
|
||||
first_render = true;
|
||||
AddLayer(this);
|
||||
}
|
||||
}
|
||||
MsgDialogUi::~MsgDialogUi() {
|
||||
Finish(ButtonId::INVALID);
|
||||
}
|
||||
MsgDialogUi::MsgDialogUi(MsgDialogUi&& other) noexcept
|
||||
: Layer(other), state(other.state), status(other.status), result(other.result) {
|
||||
other.state = nullptr;
|
||||
other.status = nullptr;
|
||||
other.result = nullptr;
|
||||
}
|
||||
MsgDialogUi& MsgDialogUi::operator=(MsgDialogUi other) {
|
||||
using std::swap;
|
||||
swap(state, other.state);
|
||||
swap(status, other.status);
|
||||
swap(result, other.result);
|
||||
if (status && *status == Status::RUNNING) {
|
||||
first_render = true;
|
||||
AddLayer(this);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void MsgDialogUi::Finish(ButtonId buttonId, Result r) {
|
||||
if (result) {
|
||||
result->result = r;
|
||||
result->buttonId = buttonId;
|
||||
}
|
||||
if (status) {
|
||||
*status = Status::FINISHED;
|
||||
}
|
||||
state = nullptr;
|
||||
status = nullptr;
|
||||
result = nullptr;
|
||||
RemoveLayer(this);
|
||||
}
|
||||
|
||||
void MsgDialogUi::Draw() {
|
||||
if (status == nullptr || *status != Status::RUNNING) {
|
||||
return;
|
||||
}
|
||||
const auto& io = GetIO();
|
||||
|
||||
const ImVec2 window_size{
|
||||
std::min(io.DisplaySize.x, 500.0f),
|
||||
std::min(io.DisplaySize.y, 300.0f),
|
||||
};
|
||||
|
||||
CentralizeWindow();
|
||||
SetNextWindowSize(window_size);
|
||||
SetNextWindowFocus();
|
||||
SetNextWindowCollapsed(false);
|
||||
KeepNavHighlight();
|
||||
// Hack to allow every dialog to have a unique window
|
||||
if (Begin("Message Dialog##MessageDialog", nullptr, ImGuiWindowFlags_NoSavedSettings)) {
|
||||
switch (state->GetMode()) {
|
||||
case MsgDialogMode::USER_MSG:
|
||||
DrawUser();
|
||||
break;
|
||||
case MsgDialogMode::PROGRESS_BAR:
|
||||
DrawProgressBar();
|
||||
break;
|
||||
case MsgDialogMode::SYSTEM_MSG:
|
||||
DrawSystemMessage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
End();
|
||||
|
||||
first_render = false;
|
||||
}
|
177
src/core/libraries/system/msgdialog_ui.h
Normal file
177
src/core/libraries/system/msgdialog_ui.h
Normal file
|
@ -0,0 +1,177 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "common/fixed_value.h"
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/system/commondialog.h"
|
||||
#include "imgui/imgui_layer.h"
|
||||
|
||||
namespace Libraries::MsgDialog {
|
||||
|
||||
using OrbisUserServiceUserId = s32;
|
||||
|
||||
enum class MsgDialogMode : u32 {
|
||||
USER_MSG = 1,
|
||||
PROGRESS_BAR = 2,
|
||||
SYSTEM_MSG = 3,
|
||||
};
|
||||
|
||||
enum class ButtonId : u32 {
|
||||
INVALID = 0,
|
||||
OK = 1,
|
||||
YES = 1,
|
||||
NO = 2,
|
||||
BUTTON1 = 1,
|
||||
BUTTON2 = 2,
|
||||
};
|
||||
|
||||
enum class ButtonType : u32 {
|
||||
OK = 0,
|
||||
YESNO = 1,
|
||||
NONE = 2,
|
||||
OK_CANCEL = 3,
|
||||
WAIT = 5,
|
||||
WAIT_CANCEL = 6,
|
||||
YESNO_FOCUS_NO = 7,
|
||||
OK_CANCEL_FOCUS_CANCEL = 8,
|
||||
TWO_BUTTONS = 9,
|
||||
};
|
||||
|
||||
enum class ProgressBarType : u32 {
|
||||
PERCENTAGE = 0,
|
||||
PERCENTAGE_CANCEL = 1,
|
||||
};
|
||||
|
||||
enum class SystemMessageType : u32 {
|
||||
TRC_EMPTY_STORE = 0,
|
||||
TRC_PSN_CHAT_RESTRICTION = 1,
|
||||
TRC_PSN_UGC_RESTRICTION = 2,
|
||||
CAMERA_NOT_CONNECTED = 4,
|
||||
WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED = 5,
|
||||
};
|
||||
|
||||
enum class OrbisMsgDialogProgressBarTarget : u32 {
|
||||
DEFAULT = 0,
|
||||
};
|
||||
|
||||
struct ButtonsParam {
|
||||
const char* msg1{};
|
||||
const char* msg2{};
|
||||
std::array<char, 32> reserved{};
|
||||
};
|
||||
|
||||
struct UserMessageParam {
|
||||
ButtonType buttonType{};
|
||||
s32 : 32;
|
||||
const char* msg{};
|
||||
ButtonsParam* buttonsParam{};
|
||||
std::array<char, 24> reserved{};
|
||||
};
|
||||
|
||||
struct ProgressBarParam {
|
||||
ProgressBarType barType{};
|
||||
s32 : 32;
|
||||
const char* msg{};
|
||||
std::array<char, 64> reserved{};
|
||||
};
|
||||
|
||||
struct SystemMessageParam {
|
||||
SystemMessageType sysMsgType{};
|
||||
std::array<char, 32> reserved{};
|
||||
};
|
||||
|
||||
struct OrbisParam {
|
||||
CommonDialog::BaseParam baseParam;
|
||||
std::size_t size;
|
||||
MsgDialogMode mode;
|
||||
s32 : 32;
|
||||
UserMessageParam* userMsgParam;
|
||||
ProgressBarParam* progBarParam;
|
||||
SystemMessageParam* sysMsgParam;
|
||||
OrbisUserServiceUserId userId;
|
||||
std::array<char, 40> reserved;
|
||||
s32 : 32;
|
||||
};
|
||||
|
||||
struct DialogResult {
|
||||
FixedValue<u32, 0> mode{};
|
||||
CommonDialog::Result result{CommonDialog::Result::OK};
|
||||
ButtonId buttonId{ButtonId::INVALID};
|
||||
std::array<char, 32> reserved{};
|
||||
};
|
||||
|
||||
// State is used to copy all the data from the param struct
|
||||
class MsgDialogState {
|
||||
public:
|
||||
struct UserState {
|
||||
ButtonType type{};
|
||||
std::string msg{};
|
||||
std::string btn_param1{};
|
||||
std::string btn_param2{};
|
||||
};
|
||||
struct ProgressState {
|
||||
ProgressBarType type{};
|
||||
std::string msg{};
|
||||
u32 progress{};
|
||||
};
|
||||
struct SystemState {
|
||||
SystemMessageType type{};
|
||||
};
|
||||
|
||||
private:
|
||||
OrbisUserServiceUserId user_id{};
|
||||
MsgDialogMode mode{};
|
||||
std::variant<UserState, ProgressState, SystemState, std::monostate> state{std::monostate{}};
|
||||
|
||||
public:
|
||||
explicit MsgDialogState(const OrbisParam& param);
|
||||
MsgDialogState() = default;
|
||||
|
||||
[[nodiscard]] OrbisUserServiceUserId GetUserId() const {
|
||||
return user_id;
|
||||
}
|
||||
|
||||
[[nodiscard]] MsgDialogMode GetMode() const {
|
||||
return mode;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] T& GetState() {
|
||||
return std::get<T>(state);
|
||||
}
|
||||
};
|
||||
|
||||
class MsgDialogUi final : public ImGui::Layer {
|
||||
bool first_render{false};
|
||||
MsgDialogState* state{};
|
||||
CommonDialog::Status* status{};
|
||||
DialogResult* result{};
|
||||
|
||||
void DrawUser();
|
||||
void DrawProgressBar();
|
||||
void DrawSystemMessage();
|
||||
|
||||
public:
|
||||
explicit MsgDialogUi(MsgDialogState* state = nullptr, CommonDialog::Status* status = nullptr,
|
||||
DialogResult* result = nullptr);
|
||||
~MsgDialogUi() override;
|
||||
MsgDialogUi(const MsgDialogUi& other) = delete;
|
||||
MsgDialogUi(MsgDialogUi&& other) noexcept;
|
||||
MsgDialogUi& operator=(MsgDialogUi other);
|
||||
|
||||
void Finish(ButtonId buttonId, CommonDialog::Result r = CommonDialog::Result::OK);
|
||||
|
||||
void SetProgressBarValue(u32 value, bool increment);
|
||||
|
||||
void Draw() override;
|
||||
|
||||
bool ShouldGrabGamepad() override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace Libraries::MsgDialog
|
|
@ -161,10 +161,6 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) {
|
|||
}
|
||||
|
||||
std::chrono::microseconds VideoOutDriver::Flip(const Request& req) {
|
||||
if (!req) {
|
||||
return std::chrono::microseconds{0};
|
||||
}
|
||||
|
||||
const auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// Whatever the game is rendering show splash if it is active
|
||||
|
@ -207,6 +203,11 @@ std::chrono::microseconds VideoOutDriver::Flip(const Request& req) {
|
|||
return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
}
|
||||
|
||||
void VideoOutDriver::DrawBlankFrame() {
|
||||
const auto empty_frame = renderer->PrepareBlankFrame(false);
|
||||
renderer->Present(empty_frame);
|
||||
}
|
||||
|
||||
bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
|
||||
bool is_eop /*= false*/) {
|
||||
{
|
||||
|
@ -283,7 +284,14 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
|
|||
auto& vblank_status = main_port.vblank_status;
|
||||
if (vblank_status.count % (main_port.flip_rate + 1) == 0) {
|
||||
const auto request = receive_request();
|
||||
delay = Flip(request);
|
||||
if (!request) {
|
||||
delay = std::chrono::microseconds{0};
|
||||
if (!main_port.is_open) {
|
||||
DrawBlankFrame();
|
||||
}
|
||||
} else {
|
||||
delay = Flip(request);
|
||||
}
|
||||
FRAME_END;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,7 @@ private:
|
|||
};
|
||||
|
||||
std::chrono::microseconds Flip(const Request& req);
|
||||
void DrawBlankFrame(); // Used when there is no flip request to keep ImGui up to date
|
||||
void SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false);
|
||||
void PresentThread(std::stop_token token);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
|
@ -27,6 +28,7 @@ static PS4_SYSV_ABI void ProgramExitFunc() {
|
|||
}
|
||||
|
||||
static void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc exit_func) {
|
||||
#ifdef ARCH_X86_64
|
||||
// reinterpret_cast<entry_func_t>(addr)(params, exit_func); // can't be used, stack has to have
|
||||
// a specific layout
|
||||
asm volatile("andq $-16, %%rsp\n" // Align to 16 bytes
|
||||
|
@ -46,6 +48,9 @@ static void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc exit_func) {
|
|||
:
|
||||
: "r"(addr), "r"(params), "r"(exit_func)
|
||||
: "rax", "rsi", "rdi");
|
||||
#else
|
||||
UNIMPLEMENTED_MSG("Missing RunMainEntry() implementation for target CPU architecture.");
|
||||
#endif
|
||||
}
|
||||
|
||||
Linker::Linker() : memory{Memory::Instance()} {}
|
||||
|
@ -85,7 +90,9 @@ void Linker::Execute() {
|
|||
|
||||
// Init primary thread.
|
||||
Common::SetCurrentThreadName("GAME_MainThread");
|
||||
#ifdef ARCH_X86_64
|
||||
InitializeThreadPatchStack();
|
||||
#endif
|
||||
Libraries::Kernel::pthreadInitSelfMainThread();
|
||||
InitTlsForThread(true);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/alignment.h"
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#ifdef ENABLE_QT_GUI
|
||||
|
@ -134,9 +135,11 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
|
|||
LOG_INFO(Core_Linker, "segment_mode ..........: {}", segment_mode);
|
||||
|
||||
add_segment(elf_pheader[i]);
|
||||
#ifdef ARCH_X86_64
|
||||
if (elf_pheader[i].p_flags & PF_EXEC) {
|
||||
PatchInstructions(segment_addr, segment_file_size, c);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case PT_DYNAMIC:
|
||||
|
|
|
@ -2,23 +2,28 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <mutex>
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/types.h"
|
||||
#include "core/tls.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(__APPLE__) && defined(ARCH_X86_64)
|
||||
#include <architecture/i386/table.h>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
#include <i386/user_ldt.h>
|
||||
#include <sys/mman.h>
|
||||
#elif !defined(ARCH_X86_64)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace Core {
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// Windows
|
||||
|
||||
static DWORD slot = 0;
|
||||
static std::once_flag slot_alloc_flag;
|
||||
|
||||
|
@ -40,7 +45,9 @@ Tcb* GetTcbBase() {
|
|||
return reinterpret_cast<Tcb*>(TlsGetValue(GetTcbKey()));
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(__APPLE__) && defined(ARCH_X86_64)
|
||||
|
||||
// Apple x86_64
|
||||
|
||||
// Reserve space in the 32-bit address range for allocating TCB pages.
|
||||
asm(".zerofill TCB_SPACE,TCB_SPACE,__guest_system,0x3FC000");
|
||||
|
@ -132,7 +139,9 @@ Tcb* GetTcbBase() {
|
|||
return tcb;
|
||||
}
|
||||
|
||||
#else
|
||||
#elif defined(ARCH_X86_64)
|
||||
|
||||
// Other POSIX x86_64
|
||||
|
||||
void SetTcbBase(void* image_address) {
|
||||
asm volatile("wrgsbase %0" ::"r"(image_address) : "memory");
|
||||
|
@ -144,6 +153,32 @@ Tcb* GetTcbBase() {
|
|||
return tcb;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// POSIX non-x86_64
|
||||
// Just sets up a simple thread-local variable to store it, then instruction translation can point
|
||||
// code to it.
|
||||
|
||||
static pthread_key_t slot = 0;
|
||||
static std::once_flag slot_alloc_flag;
|
||||
|
||||
static void AllocTcbKey() {
|
||||
ASSERT(pthread_key_create(&slot, nullptr) == 0);
|
||||
}
|
||||
|
||||
pthread_key_t GetTcbKey() {
|
||||
std::call_once(slot_alloc_flag, &AllocTcbKey);
|
||||
return slot;
|
||||
}
|
||||
|
||||
void SetTcbBase(void* image_address) {
|
||||
ASSERT(pthread_setspecific(GetTcbKey(), image_address) == 0);
|
||||
}
|
||||
|
||||
Tcb* GetTcbBase() {
|
||||
return static_cast<Tcb*>(pthread_getspecific(GetTcbKey()));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Core
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.4 KiB |
29
src/imgui/imgui_config.h
Normal file
29
src/imgui/imgui_config.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
// WARNING: All includes from this file must be relative to allow Dear_ImGui project to compile
|
||||
// without having this project include paths.
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
extern void assert_fail_debug_msg(const char* msg);
|
||||
|
||||
#define ImDrawIdx std::uint32_t
|
||||
|
||||
#define IM_STRINGIZE(x) IM_STRINGIZE2(x)
|
||||
#define IM_STRINGIZE2(x) #x
|
||||
#define IM_ASSERT(_EXPR) \
|
||||
([&]() { \
|
||||
if (!(_EXPR)) [[unlikely]] { \
|
||||
assert_fail_debug_msg(#_EXPR " at " __FILE__ ":" IM_STRINGIZE(__LINE__)); \
|
||||
} \
|
||||
}())
|
||||
|
||||
#define IMGUI_USE_WCHAR32
|
||||
#define IMGUI_ENABLE_STB_TRUETYPE
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
#define IM_VEC2_CLASS_EXTRA \
|
||||
constexpr ImVec2(float _v) : x(_v), y(_v) {}
|
21
src/imgui/imgui_layer.h
Normal file
21
src/imgui/imgui_layer.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
class Layer {
|
||||
public:
|
||||
virtual ~Layer() = default;
|
||||
static void AddLayer(Layer* layer);
|
||||
static void RemoveLayer(Layer* layer);
|
||||
|
||||
virtual void Draw() = 0;
|
||||
|
||||
virtual bool ShouldGrabGamepad() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ImGui
|
27
src/imgui/imgui_std.h
Normal file
27
src/imgui/imgui_std.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "imgui_internal.h"
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
inline void CentralizeWindow() {
|
||||
const auto display_size = GetIO().DisplaySize;
|
||||
SetNextWindowPos(display_size / 2.0f, ImGuiCond_Always, {0.5f});
|
||||
}
|
||||
|
||||
inline void KeepNavHighlight() {
|
||||
GetCurrentContext()->NavDisableHighlight = false;
|
||||
}
|
||||
|
||||
inline void SetItemCurrentNavFocus() {
|
||||
const auto ctx = GetCurrentContext();
|
||||
SetFocusID(ctx->LastItemData.ID, ctx->CurrentWindow);
|
||||
ctx->NavInitResult.Clear();
|
||||
}
|
||||
|
||||
} // namespace ImGui
|
18
src/imgui/layer/video_info.cpp
Normal file
18
src/imgui/layer/video_info.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
#include "video_info.h"
|
||||
|
||||
void ImGui::Layers::VideoInfo::Draw() {
|
||||
const ImGuiIO& io = GetIO();
|
||||
|
||||
m_show = IsKeyPressed(ImGuiKey_F10, false) ^ m_show;
|
||||
|
||||
if (m_show) {
|
||||
if (Begin("Video Info")) {
|
||||
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
}
|
||||
End();
|
||||
}
|
||||
}
|
23
src/imgui/layer/video_info.h
Normal file
23
src/imgui/layer/video_info.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui/imgui_layer.h"
|
||||
|
||||
namespace Vulkan {
|
||||
class RendererVulkan;
|
||||
}
|
||||
namespace ImGui::Layers {
|
||||
|
||||
class VideoInfo : public Layer {
|
||||
bool m_show = false;
|
||||
::Vulkan::RendererVulkan* renderer{};
|
||||
|
||||
public:
|
||||
explicit VideoInfo(::Vulkan::RendererVulkan* renderer) : renderer(renderer) {}
|
||||
|
||||
void Draw() override;
|
||||
};
|
||||
|
||||
} // namespace ImGui::Layers
|
213
src/imgui/renderer/imgui_core.cpp
Normal file
213
src/imgui/renderer/imgui_core.cpp
Normal file
|
@ -0,0 +1,213 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <imgui.h>
|
||||
#include "common/config.h"
|
||||
#include "common/path_util.h"
|
||||
#include "imgui/imgui_layer.h"
|
||||
#include "imgui_core.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#include "imgui_impl_vulkan.h"
|
||||
#include "sdl_window.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
|
||||
static void CheckVkResult(const vk::Result err) {
|
||||
LOG_ERROR(ImGui, "Vulkan error {}", vk::to_string(err));
|
||||
}
|
||||
|
||||
static std::vector<ImGui::Layer*> layers;
|
||||
|
||||
// Update layers before rendering to allow layer changes to be applied during rendering.
|
||||
// Using deque to keep the order of changes in case a Layer is removed then added again between
|
||||
// frames.
|
||||
static std::deque<std::pair<bool, ImGui::Layer*>> change_layers;
|
||||
static std::mutex change_layers_mutex{};
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
namespace Core {
|
||||
|
||||
void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& window,
|
||||
const u32 image_count, vk::Format surface_format,
|
||||
const vk::AllocationCallbacks* allocator) {
|
||||
|
||||
const auto config_path = GetUserPath(Common::FS::PathType::UserDir) / "imgui.ini";
|
||||
const auto log_path = GetUserPath(Common::FS::PathType::LogDir) / "imgui_log.txt";
|
||||
|
||||
CreateContext();
|
||||
ImGuiIO& io = GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
io.DisplaySize = ImVec2((float)window.getWidth(), (float)window.getHeight());
|
||||
io.IniFilename = SDL_strdup(config_path.string().c_str());
|
||||
io.LogFilename = SDL_strdup(log_path.string().c_str());
|
||||
StyleColorsDark();
|
||||
|
||||
Sdl::Init(window.GetSdlWindow());
|
||||
|
||||
const Vulkan::InitInfo vk_info{
|
||||
.instance = instance.GetInstance(),
|
||||
.physical_device = instance.GetPhysicalDevice(),
|
||||
.device = instance.GetDevice(),
|
||||
.queue_family = instance.GetPresentQueueFamilyIndex(),
|
||||
.queue = instance.GetPresentQueue(),
|
||||
.image_count = image_count,
|
||||
.min_allocation_size = 1024 * 1024,
|
||||
.pipeline_rendering_create_info{
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachmentFormats = &surface_format,
|
||||
},
|
||||
.allocator = allocator,
|
||||
.check_vk_result_fn = &CheckVkResult,
|
||||
};
|
||||
Vulkan::Init(vk_info);
|
||||
}
|
||||
|
||||
void OnResize() {
|
||||
Sdl::OnResize();
|
||||
}
|
||||
|
||||
void Shutdown(const vk::Device& device) {
|
||||
device.waitIdle();
|
||||
|
||||
const ImGuiIO& io = GetIO();
|
||||
const auto ini_filename = (void*)io.IniFilename;
|
||||
const auto log_filename = (void*)io.LogFilename;
|
||||
|
||||
Vulkan::Shutdown();
|
||||
Sdl::Shutdown();
|
||||
DestroyContext();
|
||||
|
||||
SDL_free(ini_filename);
|
||||
SDL_free(log_filename);
|
||||
}
|
||||
|
||||
bool ProcessEvent(SDL_Event* event) {
|
||||
Sdl::ProcessEvent(event);
|
||||
switch (event->type) {
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
case SDL_EVENT_MOUSE_WHEEL:
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
return GetIO().WantCaptureMouse;
|
||||
case SDL_EVENT_TEXT_INPUT:
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP:
|
||||
return GetIO().WantCaptureKeyboard;
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
|
||||
return (GetIO().BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void NewFrame() {
|
||||
{
|
||||
std::scoped_lock lock{change_layers_mutex};
|
||||
while (!change_layers.empty()) {
|
||||
const auto [to_be_added, layer] = change_layers.front();
|
||||
if (to_be_added) {
|
||||
layers.push_back(layer);
|
||||
} else {
|
||||
const auto [begin, end] = std::ranges::remove(layers, layer);
|
||||
layers.erase(begin, end);
|
||||
}
|
||||
change_layers.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
Vulkan::NewFrame();
|
||||
Sdl::NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
bool capture_gamepad = false;
|
||||
for (auto* layer : layers) {
|
||||
layer->Draw();
|
||||
if (layer->ShouldGrabGamepad()) {
|
||||
capture_gamepad = true;
|
||||
}
|
||||
}
|
||||
if (capture_gamepad) {
|
||||
GetIO().BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
} else {
|
||||
GetIO().BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||
}
|
||||
}
|
||||
|
||||
void Render(const vk::CommandBuffer& cmdbuf, ::Vulkan::Frame* frame) {
|
||||
ImGui::Render();
|
||||
ImDrawData* draw_data = GetDrawData();
|
||||
if (draw_data->CmdListsCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Config::vkMarkersEnabled()) {
|
||||
cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
|
||||
.pLabelName = "ImGui Render",
|
||||
});
|
||||
}
|
||||
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::PipelineStageFlagBits::eColorAttachmentOutput, {}, {}, {},
|
||||
{vk::ImageMemoryBarrier{
|
||||
.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite,
|
||||
.dstAccessMask = vk::AccessFlagBits::eColorAttachmentRead,
|
||||
.oldLayout = vk::ImageLayout::eUndefined,
|
||||
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = frame->image,
|
||||
.subresourceRange{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
}});
|
||||
|
||||
vk::RenderingAttachmentInfo color_attachments[1]{
|
||||
{
|
||||
.imageView = frame->image_view,
|
||||
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||
.loadOp = vk::AttachmentLoadOp::eLoad,
|
||||
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||
},
|
||||
};
|
||||
vk::RenderingInfo render_info = {};
|
||||
render_info.renderArea = {
|
||||
.offset = {0, 0},
|
||||
.extent = {frame->width, frame->height},
|
||||
};
|
||||
render_info.layerCount = 1;
|
||||
render_info.colorAttachmentCount = 1;
|
||||
render_info.pColorAttachments = color_attachments;
|
||||
cmdbuf.beginRendering(render_info);
|
||||
Vulkan::RenderDrawData(*draw_data, cmdbuf);
|
||||
cmdbuf.endRendering();
|
||||
if (Config::vkMarkersEnabled()) {
|
||||
cmdbuf.endDebugUtilsLabelEXT();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
void Layer::AddLayer(Layer* layer) {
|
||||
std::scoped_lock lock{change_layers_mutex};
|
||||
change_layers.emplace_back(true, layer);
|
||||
}
|
||||
|
||||
void Layer::RemoveLayer(Layer* layer) {
|
||||
std::scoped_lock lock{change_layers_mutex};
|
||||
change_layers.emplace_back(false, layer);
|
||||
}
|
||||
|
||||
} // namespace ImGui
|
31
src/imgui/renderer/imgui_core.h
Normal file
31
src/imgui/renderer/imgui_core.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "vulkan/vulkan_handles.hpp"
|
||||
|
||||
union SDL_Event;
|
||||
|
||||
namespace Vulkan {
|
||||
struct Frame;
|
||||
}
|
||||
|
||||
namespace ImGui::Core {
|
||||
|
||||
void Initialize(const Vulkan::Instance& instance, const Frontend::WindowSDL& window,
|
||||
u32 image_count, vk::Format surface_format,
|
||||
const vk::AllocationCallbacks* allocator = nullptr);
|
||||
|
||||
void OnResize();
|
||||
|
||||
void Shutdown(const vk::Device& device);
|
||||
|
||||
bool ProcessEvent(SDL_Event* event);
|
||||
|
||||
void NewFrame();
|
||||
|
||||
void Render(const vk::CommandBuffer& cmdbuf, Vulkan::Frame* frame);
|
||||
|
||||
} // namespace ImGui::Core
|
789
src/imgui/renderer/imgui_impl_sdl3.cpp
Normal file
789
src/imgui/renderer/imgui_impl_sdl3.cpp
Normal file
|
@ -0,0 +1,789 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Based on imgui_impl_sdl3.cpp from Dear ImGui repository
|
||||
|
||||
#include <imgui.h>
|
||||
#include "imgui_impl_sdl3.h"
|
||||
|
||||
// SDL
|
||||
#include <SDL3/SDL.h>
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace ImGui::Sdl {
|
||||
|
||||
// SDL Data
|
||||
struct SdlData {
|
||||
SDL_Window* window{};
|
||||
SDL_WindowID window_id{};
|
||||
Uint64 time{};
|
||||
const char* clipboard_text_data{};
|
||||
|
||||
// IME handling
|
||||
SDL_Window* ime_window{};
|
||||
|
||||
// Mouse handling
|
||||
Uint32 mouse_window_id{};
|
||||
int mouse_buttons_down{};
|
||||
SDL_Cursor* mouse_cursors[ImGuiMouseCursor_COUNT]{};
|
||||
SDL_Cursor* mouse_last_cursor{};
|
||||
int mouse_pending_leave_frame{};
|
||||
|
||||
// Gamepad handling
|
||||
ImVector<SDL_Gamepad*> gamepads{};
|
||||
GamepadMode gamepad_mode{};
|
||||
bool want_update_gamepads_list{};
|
||||
};
|
||||
|
||||
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui
|
||||
// contexts It is STRONGLY preferred that you use docking branch with multi-viewports (== single
|
||||
// Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||
static SdlData* GetBackendData() {
|
||||
return ImGui::GetCurrentContext() ? (SdlData*)ImGui::GetIO().BackendPlatformUserData : nullptr;
|
||||
}
|
||||
|
||||
static const char* GetClipboardText(ImGuiContext*) {
|
||||
SdlData* bd = GetBackendData();
|
||||
if (bd->clipboard_text_data)
|
||||
SDL_free((void*)bd->clipboard_text_data);
|
||||
const char* sdl_clipboard_text = SDL_GetClipboardText();
|
||||
bd->clipboard_text_data = sdl_clipboard_text;
|
||||
return bd->clipboard_text_data;
|
||||
}
|
||||
|
||||
static void SetClipboardText(ImGuiContext*, const char* text) {
|
||||
SDL_SetClipboardText(text);
|
||||
}
|
||||
|
||||
static void PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) {
|
||||
SdlData* bd = GetBackendData();
|
||||
auto window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle;
|
||||
SDL_Window* window = SDL_GetWindowFromID(window_id);
|
||||
if ((!data->WantVisible || bd->ime_window != window) && bd->ime_window != nullptr) {
|
||||
SDL_StopTextInput(bd->ime_window);
|
||||
bd->ime_window = nullptr;
|
||||
}
|
||||
if (data->WantVisible) {
|
||||
SDL_Rect r;
|
||||
r.x = (int)data->InputPos.x;
|
||||
r.y = (int)data->InputPos.y;
|
||||
r.w = 1;
|
||||
r.h = (int)data->InputLineHeight;
|
||||
SDL_SetTextInputArea(window, &r, 0);
|
||||
SDL_StartTextInput(window);
|
||||
bd->ime_window = window;
|
||||
}
|
||||
}
|
||||
|
||||
static ImGuiKey KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) {
|
||||
// Keypad doesn't have individual key values in SDL3
|
||||
switch (scancode) {
|
||||
case SDL_SCANCODE_KP_0:
|
||||
return ImGuiKey_Keypad0;
|
||||
case SDL_SCANCODE_KP_1:
|
||||
return ImGuiKey_Keypad1;
|
||||
case SDL_SCANCODE_KP_2:
|
||||
return ImGuiKey_Keypad2;
|
||||
case SDL_SCANCODE_KP_3:
|
||||
return ImGuiKey_Keypad3;
|
||||
case SDL_SCANCODE_KP_4:
|
||||
return ImGuiKey_Keypad4;
|
||||
case SDL_SCANCODE_KP_5:
|
||||
return ImGuiKey_Keypad5;
|
||||
case SDL_SCANCODE_KP_6:
|
||||
return ImGuiKey_Keypad6;
|
||||
case SDL_SCANCODE_KP_7:
|
||||
return ImGuiKey_Keypad7;
|
||||
case SDL_SCANCODE_KP_8:
|
||||
return ImGuiKey_Keypad8;
|
||||
case SDL_SCANCODE_KP_9:
|
||||
return ImGuiKey_Keypad9;
|
||||
case SDL_SCANCODE_KP_PERIOD:
|
||||
return ImGuiKey_KeypadDecimal;
|
||||
case SDL_SCANCODE_KP_DIVIDE:
|
||||
return ImGuiKey_KeypadDivide;
|
||||
case SDL_SCANCODE_KP_MULTIPLY:
|
||||
return ImGuiKey_KeypadMultiply;
|
||||
case SDL_SCANCODE_KP_MINUS:
|
||||
return ImGuiKey_KeypadSubtract;
|
||||
case SDL_SCANCODE_KP_PLUS:
|
||||
return ImGuiKey_KeypadAdd;
|
||||
case SDL_SCANCODE_KP_ENTER:
|
||||
return ImGuiKey_KeypadEnter;
|
||||
case SDL_SCANCODE_KP_EQUALS:
|
||||
return ImGuiKey_KeypadEqual;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (keycode) {
|
||||
case SDLK_TAB:
|
||||
return ImGuiKey_Tab;
|
||||
case SDLK_LEFT:
|
||||
return ImGuiKey_LeftArrow;
|
||||
case SDLK_RIGHT:
|
||||
return ImGuiKey_RightArrow;
|
||||
case SDLK_UP:
|
||||
return ImGuiKey_UpArrow;
|
||||
case SDLK_DOWN:
|
||||
return ImGuiKey_DownArrow;
|
||||
case SDLK_PAGEUP:
|
||||
return ImGuiKey_PageUp;
|
||||
case SDLK_PAGEDOWN:
|
||||
return ImGuiKey_PageDown;
|
||||
case SDLK_HOME:
|
||||
return ImGuiKey_Home;
|
||||
case SDLK_END:
|
||||
return ImGuiKey_End;
|
||||
case SDLK_INSERT:
|
||||
return ImGuiKey_Insert;
|
||||
case SDLK_DELETE:
|
||||
return ImGuiKey_Delete;
|
||||
case SDLK_BACKSPACE:
|
||||
return ImGuiKey_Backspace;
|
||||
case SDLK_SPACE:
|
||||
return ImGuiKey_Space;
|
||||
case SDLK_RETURN:
|
||||
return ImGuiKey_Enter;
|
||||
case SDLK_ESCAPE:
|
||||
return ImGuiKey_Escape;
|
||||
case SDLK_APOSTROPHE:
|
||||
return ImGuiKey_Apostrophe;
|
||||
case SDLK_COMMA:
|
||||
return ImGuiKey_Comma;
|
||||
case SDLK_MINUS:
|
||||
return ImGuiKey_Minus;
|
||||
case SDLK_PERIOD:
|
||||
return ImGuiKey_Period;
|
||||
case SDLK_SLASH:
|
||||
return ImGuiKey_Slash;
|
||||
case SDLK_SEMICOLON:
|
||||
return ImGuiKey_Semicolon;
|
||||
case SDLK_EQUALS:
|
||||
return ImGuiKey_Equal;
|
||||
case SDLK_LEFTBRACKET:
|
||||
return ImGuiKey_LeftBracket;
|
||||
case SDLK_BACKSLASH:
|
||||
return ImGuiKey_Backslash;
|
||||
case SDLK_RIGHTBRACKET:
|
||||
return ImGuiKey_RightBracket;
|
||||
case SDLK_GRAVE:
|
||||
return ImGuiKey_GraveAccent;
|
||||
case SDLK_CAPSLOCK:
|
||||
return ImGuiKey_CapsLock;
|
||||
case SDLK_SCROLLLOCK:
|
||||
return ImGuiKey_ScrollLock;
|
||||
case SDLK_NUMLOCKCLEAR:
|
||||
return ImGuiKey_NumLock;
|
||||
case SDLK_PRINTSCREEN:
|
||||
return ImGuiKey_PrintScreen;
|
||||
case SDLK_PAUSE:
|
||||
return ImGuiKey_Pause;
|
||||
case SDLK_LCTRL:
|
||||
return ImGuiKey_LeftCtrl;
|
||||
case SDLK_LSHIFT:
|
||||
return ImGuiKey_LeftShift;
|
||||
case SDLK_LALT:
|
||||
return ImGuiKey_LeftAlt;
|
||||
case SDLK_LGUI:
|
||||
return ImGuiKey_LeftSuper;
|
||||
case SDLK_RCTRL:
|
||||
return ImGuiKey_RightCtrl;
|
||||
case SDLK_RSHIFT:
|
||||
return ImGuiKey_RightShift;
|
||||
case SDLK_RALT:
|
||||
return ImGuiKey_RightAlt;
|
||||
case SDLK_RGUI:
|
||||
return ImGuiKey_RightSuper;
|
||||
case SDLK_APPLICATION:
|
||||
return ImGuiKey_Menu;
|
||||
case SDLK_0:
|
||||
return ImGuiKey_0;
|
||||
case SDLK_1:
|
||||
return ImGuiKey_1;
|
||||
case SDLK_2:
|
||||
return ImGuiKey_2;
|
||||
case SDLK_3:
|
||||
return ImGuiKey_3;
|
||||
case SDLK_4:
|
||||
return ImGuiKey_4;
|
||||
case SDLK_5:
|
||||
return ImGuiKey_5;
|
||||
case SDLK_6:
|
||||
return ImGuiKey_6;
|
||||
case SDLK_7:
|
||||
return ImGuiKey_7;
|
||||
case SDLK_8:
|
||||
return ImGuiKey_8;
|
||||
case SDLK_9:
|
||||
return ImGuiKey_9;
|
||||
case SDLK_A:
|
||||
return ImGuiKey_A;
|
||||
case SDLK_B:
|
||||
return ImGuiKey_B;
|
||||
case SDLK_C:
|
||||
return ImGuiKey_C;
|
||||
case SDLK_D:
|
||||
return ImGuiKey_D;
|
||||
case SDLK_E:
|
||||
return ImGuiKey_E;
|
||||
case SDLK_F:
|
||||
return ImGuiKey_F;
|
||||
case SDLK_G:
|
||||
return ImGuiKey_G;
|
||||
case SDLK_H:
|
||||
return ImGuiKey_H;
|
||||
case SDLK_I:
|
||||
return ImGuiKey_I;
|
||||
case SDLK_J:
|
||||
return ImGuiKey_J;
|
||||
case SDLK_K:
|
||||
return ImGuiKey_K;
|
||||
case SDLK_L:
|
||||
return ImGuiKey_L;
|
||||
case SDLK_M:
|
||||
return ImGuiKey_M;
|
||||
case SDLK_N:
|
||||
return ImGuiKey_N;
|
||||
case SDLK_O:
|
||||
return ImGuiKey_O;
|
||||
case SDLK_P:
|
||||
return ImGuiKey_P;
|
||||
case SDLK_Q:
|
||||
return ImGuiKey_Q;
|
||||
case SDLK_R:
|
||||
return ImGuiKey_R;
|
||||
case SDLK_S:
|
||||
return ImGuiKey_S;
|
||||
case SDLK_T:
|
||||
return ImGuiKey_T;
|
||||
case SDLK_U:
|
||||
return ImGuiKey_U;
|
||||
case SDLK_V:
|
||||
return ImGuiKey_V;
|
||||
case SDLK_W:
|
||||
return ImGuiKey_W;
|
||||
case SDLK_X:
|
||||
return ImGuiKey_X;
|
||||
case SDLK_Y:
|
||||
return ImGuiKey_Y;
|
||||
case SDLK_Z:
|
||||
return ImGuiKey_Z;
|
||||
case SDLK_F1:
|
||||
return ImGuiKey_F1;
|
||||
case SDLK_F2:
|
||||
return ImGuiKey_F2;
|
||||
case SDLK_F3:
|
||||
return ImGuiKey_F3;
|
||||
case SDLK_F4:
|
||||
return ImGuiKey_F4;
|
||||
case SDLK_F5:
|
||||
return ImGuiKey_F5;
|
||||
case SDLK_F6:
|
||||
return ImGuiKey_F6;
|
||||
case SDLK_F7:
|
||||
return ImGuiKey_F7;
|
||||
case SDLK_F8:
|
||||
return ImGuiKey_F8;
|
||||
case SDLK_F9:
|
||||
return ImGuiKey_F9;
|
||||
case SDLK_F10:
|
||||
return ImGuiKey_F10;
|
||||
case SDLK_F11:
|
||||
return ImGuiKey_F11;
|
||||
case SDLK_F12:
|
||||
return ImGuiKey_F12;
|
||||
case SDLK_F13:
|
||||
return ImGuiKey_F13;
|
||||
case SDLK_F14:
|
||||
return ImGuiKey_F14;
|
||||
case SDLK_F15:
|
||||
return ImGuiKey_F15;
|
||||
case SDLK_F16:
|
||||
return ImGuiKey_F16;
|
||||
case SDLK_F17:
|
||||
return ImGuiKey_F17;
|
||||
case SDLK_F18:
|
||||
return ImGuiKey_F18;
|
||||
case SDLK_F19:
|
||||
return ImGuiKey_F19;
|
||||
case SDLK_F20:
|
||||
return ImGuiKey_F20;
|
||||
case SDLK_F21:
|
||||
return ImGuiKey_F21;
|
||||
case SDLK_F22:
|
||||
return ImGuiKey_F22;
|
||||
case SDLK_F23:
|
||||
return ImGuiKey_F23;
|
||||
case SDLK_F24:
|
||||
return ImGuiKey_F24;
|
||||
case SDLK_AC_BACK:
|
||||
return ImGuiKey_AppBack;
|
||||
case SDLK_AC_FORWARD:
|
||||
return ImGuiKey_AppForward;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ImGuiKey_None;
|
||||
}
|
||||
|
||||
static void UpdateKeyModifiers(SDL_Keymod sdl_key_mods) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & SDL_KMOD_CTRL) != 0);
|
||||
io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & SDL_KMOD_SHIFT) != 0);
|
||||
io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & SDL_KMOD_ALT) != 0);
|
||||
io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0);
|
||||
}
|
||||
|
||||
static ImGuiViewport* GetViewportForWindowId(SDL_WindowID window_id) {
|
||||
SdlData* bd = GetBackendData();
|
||||
return (window_id == bd->window_id) ? ImGui::GetMainViewport() : nullptr;
|
||||
}
|
||||
|
||||
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to
|
||||
// use your inputs.
|
||||
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or
|
||||
// clear/overwrite your copy of the mouse data.
|
||||
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main
|
||||
// application, or clear/overwrite your copy of the keyboard data. Generally you may always pass all
|
||||
// inputs to dear imgui, and hide them from your application based on those two flags. If you have
|
||||
// multiple SDL events and some of them are not meant to be used by dear imgui, you may need to
|
||||
// filter events based on their windowID field.
|
||||
bool ProcessEvent(const SDL_Event* event) {
|
||||
SdlData* bd = GetBackendData();
|
||||
IM_ASSERT(bd != nullptr &&
|
||||
"Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
switch (event->type) {
|
||||
case SDL_EVENT_MOUSE_MOTION: {
|
||||
if (GetViewportForWindowId(event->motion.windowID) == NULL)
|
||||
return false;
|
||||
ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
|
||||
io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID
|
||||
? ImGuiMouseSource_TouchScreen
|
||||
: ImGuiMouseSource_Mouse);
|
||||
io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_WHEEL: {
|
||||
if (GetViewportForWindowId(event->wheel.windowID) == NULL)
|
||||
return false;
|
||||
// IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x,
|
||||
// (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
|
||||
float wheel_x = -event->wheel.x;
|
||||
float wheel_y = event->wheel.y;
|
||||
#ifdef __EMSCRIPTEN__
|
||||
wheel_x /= 100.0f;
|
||||
#endif
|
||||
io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID
|
||||
? ImGuiMouseSource_TouchScreen
|
||||
: ImGuiMouseSource_Mouse);
|
||||
io.AddMouseWheelEvent(wheel_x, wheel_y);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP: {
|
||||
if (GetViewportForWindowId(event->button.windowID) == NULL)
|
||||
return false;
|
||||
int mouse_button = -1;
|
||||
if (event->button.button == SDL_BUTTON_LEFT) {
|
||||
mouse_button = 0;
|
||||
}
|
||||
if (event->button.button == SDL_BUTTON_RIGHT) {
|
||||
mouse_button = 1;
|
||||
}
|
||||
if (event->button.button == SDL_BUTTON_MIDDLE) {
|
||||
mouse_button = 2;
|
||||
}
|
||||
if (event->button.button == SDL_BUTTON_X1) {
|
||||
mouse_button = 3;
|
||||
}
|
||||
if (event->button.button == SDL_BUTTON_X2) {
|
||||
mouse_button = 4;
|
||||
}
|
||||
if (mouse_button == -1)
|
||||
break;
|
||||
io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID
|
||||
? ImGuiMouseSource_TouchScreen
|
||||
: ImGuiMouseSource_Mouse);
|
||||
io.AddMouseButtonEvent(mouse_button, (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN));
|
||||
bd->mouse_buttons_down = (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN)
|
||||
? (bd->mouse_buttons_down | (1 << mouse_button))
|
||||
: (bd->mouse_buttons_down & ~(1 << mouse_button));
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_TEXT_INPUT: {
|
||||
if (GetViewportForWindowId(event->text.windowID) == NULL)
|
||||
return false;
|
||||
io.AddInputCharactersUTF8(event->text.text);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP: {
|
||||
if (GetViewportForWindowId(event->key.windowID) == NULL)
|
||||
return false;
|
||||
// IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type ==
|
||||
// SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode,
|
||||
// event->key.mod);
|
||||
UpdateKeyModifiers((SDL_Keymod)event->key.mod);
|
||||
ImGuiKey key = KeyEventToImGuiKey(event->key.key, event->key.scancode);
|
||||
io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN));
|
||||
io.SetKeyEventNativeData(
|
||||
key, event->key.key, event->key.scancode,
|
||||
event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend
|
||||
// uses SDLK_*** as indices to IsKeyXXX() functions.
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_MOUSE_ENTER: {
|
||||
if (GetViewportForWindowId(event->window.windowID) == NULL)
|
||||
return false;
|
||||
bd->mouse_window_id = event->window.windowID;
|
||||
bd->mouse_pending_leave_frame = 0;
|
||||
return true;
|
||||
}
|
||||
// - In some cases, when detaching a window from main viewport SDL may send
|
||||
// SDL_WINDOWEVENT_ENTER one frame too late,
|
||||
// causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse
|
||||
// position. This is why we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See
|
||||
// issue #5012 for details.
|
||||
// FIXME: Unconfirmed whether this is still needed with SDL3.
|
||||
case SDL_EVENT_WINDOW_MOUSE_LEAVE: {
|
||||
if (GetViewportForWindowId(event->window.windowID) == NULL)
|
||||
return false;
|
||||
bd->mouse_pending_leave_frame = ImGui::GetFrameCount() + 1;
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
||||
case SDL_EVENT_WINDOW_FOCUS_LOST: {
|
||||
if (GetViewportForWindowId(event->window.windowID) == NULL)
|
||||
return false;
|
||||
io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
case SDL_EVENT_GAMEPAD_REMOVED: {
|
||||
bd->want_update_gamepads_list = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window) {
|
||||
viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(window);
|
||||
viewport->PlatformHandleRaw = nullptr;
|
||||
#if defined(_WIN32) && !defined(__WINRT__)
|
||||
viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(
|
||||
SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
|
||||
#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
|
||||
viewport->PlatformHandleRaw = SDL_GetPointerProperty(
|
||||
SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Init(SDL_Window* window) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
|
||||
|
||||
// Setup backend capabilities flags
|
||||
SdlData* bd = IM_NEW(SdlData)();
|
||||
io.BackendPlatformUserData = (void*)bd;
|
||||
io.BackendPlatformName = "imgui_impl_sdl3_shadps4";
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests
|
||||
// (optional, rarely used)
|
||||
|
||||
bd->window = window;
|
||||
bd->window_id = SDL_GetWindowID(window);
|
||||
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
platform_io.Platform_SetClipboardTextFn = SetClipboardText;
|
||||
platform_io.Platform_GetClipboardTextFn = GetClipboardText;
|
||||
platform_io.Platform_SetImeDataFn = PlatformSetImeData;
|
||||
|
||||
// Gamepad handling
|
||||
bd->gamepad_mode = ImGui_ImplSDL3_GamepadMode_AutoFirst;
|
||||
bd->want_update_gamepads_list = true;
|
||||
|
||||
// Load mouse cursors
|
||||
#define CURSOR(left, right) \
|
||||
bd->mouse_cursors[ImGuiMouseCursor_##left] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_##right)
|
||||
CURSOR(Arrow, DEFAULT);
|
||||
CURSOR(TextInput, TEXT);
|
||||
CURSOR(ResizeAll, MOVE);
|
||||
CURSOR(ResizeNS, NS_RESIZE);
|
||||
CURSOR(ResizeEW, EW_RESIZE);
|
||||
CURSOR(ResizeNESW, NESW_RESIZE);
|
||||
CURSOR(ResizeNWSE, NWSE_RESIZE);
|
||||
CURSOR(Hand, POINTER);
|
||||
CURSOR(NotAllowed, NOT_ALLOWED);
|
||||
#undef CURSOR
|
||||
|
||||
// Set platform dependent data in viewport
|
||||
// Our mouse update function expect PlatformHandle to be filled for the main viewport
|
||||
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
|
||||
SetupPlatformHandles(main_viewport, window);
|
||||
|
||||
// From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't
|
||||
// emit the event. Without this, when clicking to gain focus, our widgets wouldn't activate even
|
||||
// though they showed as hovered. (This is unfortunately a global SDL setting, so enabling it
|
||||
// might have a side-effect on your application. It is unlikely to make a difference, but if
|
||||
// your app absolutely needs to ignore the initial on-focus click: you can ignore
|
||||
// SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)
|
||||
#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
|
||||
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
||||
#endif
|
||||
|
||||
// From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows
|
||||
// (see #5710)
|
||||
#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE
|
||||
SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void CloseGamepads();
|
||||
|
||||
void Shutdown() {
|
||||
SdlData* bd = GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
if (bd->clipboard_text_data) {
|
||||
SDL_free((void*)bd->clipboard_text_data);
|
||||
}
|
||||
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
|
||||
SDL_DestroyCursor(bd->mouse_cursors[cursor_n]);
|
||||
CloseGamepads();
|
||||
|
||||
io.BackendPlatformName = nullptr;
|
||||
io.BackendPlatformUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos |
|
||||
ImGuiBackendFlags_HasGamepad);
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
static void UpdateMouseData() {
|
||||
SdlData* bd = GetBackendData();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused
|
||||
// (below)
|
||||
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries
|
||||
// shouldn't e.g. trigger other operations outside
|
||||
SDL_CaptureMouse((bd->mouse_buttons_down != 0) ? SDL_TRUE : SDL_FALSE);
|
||||
SDL_Window* focused_window = SDL_GetKeyboardFocus();
|
||||
const bool is_app_focused = (bd->window == focused_window);
|
||||
|
||||
if (is_app_focused) {
|
||||
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when
|
||||
// ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
||||
if (io.WantSetMousePos)
|
||||
SDL_WarpMouseInWindow(bd->window, io.MousePos.x, io.MousePos.y);
|
||||
|
||||
// (Optional) Fallback to provide mouse position when focused (SDL_EVENT_MOUSE_MOTION
|
||||
// already provides this when hovered or captured)
|
||||
if (bd->mouse_buttons_down == 0) {
|
||||
// Single-viewport mode: mouse position in client window coordinates (io.MousePos is
|
||||
// (0,0) when the mouse is on the upper-left corner of the app window)
|
||||
float mouse_x_global, mouse_y_global;
|
||||
int window_x, window_y;
|
||||
SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);
|
||||
SDL_GetWindowPosition(focused_window, &window_x, &window_y);
|
||||
io.AddMousePosEvent(mouse_x_global - (float)window_x, mouse_y_global - (float)window_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateMouseCursor() {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||
return;
|
||||
SdlData* bd = GetBackendData();
|
||||
|
||||
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) {
|
||||
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||
SDL_HideCursor();
|
||||
} else {
|
||||
// Show OS mouse cursor
|
||||
SDL_Cursor* expected_cursor = bd->mouse_cursors[imgui_cursor]
|
||||
? bd->mouse_cursors[imgui_cursor]
|
||||
: bd->mouse_cursors[ImGuiMouseCursor_Arrow];
|
||||
if (bd->mouse_last_cursor != expected_cursor) {
|
||||
SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113)
|
||||
bd->mouse_last_cursor = expected_cursor;
|
||||
}
|
||||
SDL_ShowCursor();
|
||||
}
|
||||
}
|
||||
|
||||
static void CloseGamepads() {
|
||||
SdlData* bd = GetBackendData();
|
||||
if (bd->gamepad_mode != ImGui_ImplSDL3_GamepadMode_Manual)
|
||||
for (SDL_Gamepad* gamepad : bd->gamepads)
|
||||
SDL_CloseGamepad(gamepad);
|
||||
bd->gamepads.resize(0);
|
||||
}
|
||||
|
||||
void SetGamepadMode(GamepadMode mode, SDL_Gamepad** manual_gamepads_array,
|
||||
int manual_gamepads_count) {
|
||||
SdlData* bd = GetBackendData();
|
||||
CloseGamepads();
|
||||
if (mode == ImGui_ImplSDL3_GamepadMode_Manual) {
|
||||
IM_ASSERT(manual_gamepads_array != nullptr && manual_gamepads_count > 0);
|
||||
for (int n = 0; n < manual_gamepads_count; n++)
|
||||
bd->gamepads.push_back(manual_gamepads_array[n]);
|
||||
} else {
|
||||
IM_ASSERT(manual_gamepads_array == nullptr && manual_gamepads_count <= 0);
|
||||
bd->want_update_gamepads_list = true;
|
||||
}
|
||||
bd->gamepad_mode = mode;
|
||||
}
|
||||
|
||||
static void UpdateGamepadButton(SdlData* bd, ImGuiIO& io, ImGuiKey key,
|
||||
SDL_GamepadButton button_no) {
|
||||
bool merged_value = false;
|
||||
for (SDL_Gamepad* gamepad : bd->gamepads)
|
||||
merged_value |= SDL_GetGamepadButton(gamepad, button_no) != 0;
|
||||
io.AddKeyEvent(key, merged_value);
|
||||
}
|
||||
|
||||
static inline float Saturate(float v) {
|
||||
return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v;
|
||||
}
|
||||
static void UpdateGamepadAnalog(SdlData* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadAxis axis_no,
|
||||
float v0, float v1) {
|
||||
float merged_value = 0.0f;
|
||||
for (SDL_Gamepad* gamepad : bd->gamepads) {
|
||||
float vn = Saturate((float)(SDL_GetGamepadAxis(gamepad, axis_no) - v0) / (float)(v1 - v0));
|
||||
if (merged_value < vn)
|
||||
merged_value = vn;
|
||||
}
|
||||
io.AddKeyAnalogEvent(key, merged_value > 0.1f, merged_value);
|
||||
}
|
||||
|
||||
static void UpdateGamepads() {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
SdlData* bd = GetBackendData();
|
||||
|
||||
// Update list of gamepads to use
|
||||
if (bd->want_update_gamepads_list && bd->gamepad_mode != ImGui_ImplSDL3_GamepadMode_Manual) {
|
||||
CloseGamepads();
|
||||
int sdl_gamepads_count = 0;
|
||||
const SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count);
|
||||
for (int n = 0; n < sdl_gamepads_count; n++)
|
||||
if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n])) {
|
||||
bd->gamepads.push_back(gamepad);
|
||||
if (bd->gamepad_mode == ImGui_ImplSDL3_GamepadMode_AutoFirst)
|
||||
break;
|
||||
}
|
||||
bd->want_update_gamepads_list = false;
|
||||
}
|
||||
|
||||
// FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
|
||||
return;
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||
if (bd->gamepads.Size == 0)
|
||||
return;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
|
||||
// Update gamepad inputs
|
||||
const int thumb_dead_zone = 8000; // SDL_gamepad.h suggests using this value.
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadStart, SDL_GAMEPAD_BUTTON_START);
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadBack, SDL_GAMEPAD_BUTTON_BACK);
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceLeft,
|
||||
SDL_GAMEPAD_BUTTON_WEST); // Xbox X, PS Square
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceRight,
|
||||
SDL_GAMEPAD_BUTTON_EAST); // Xbox B, PS Circle
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceUp,
|
||||
SDL_GAMEPAD_BUTTON_NORTH); // Xbox Y, PS Triangle
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceDown,
|
||||
SDL_GAMEPAD_BUTTON_SOUTH); // Xbox A, PS Cross
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadLeft, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadRight, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadUp, SDL_GAMEPAD_BUTTON_DPAD_UP);
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadDown, SDL_GAMEPAD_BUTTON_DPAD_DOWN);
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadL1, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER);
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadR1, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
|
||||
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadL2, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 0.0f, 32767);
|
||||
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadR2, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0.0f, 32767);
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadL3, SDL_GAMEPAD_BUTTON_LEFT_STICK);
|
||||
UpdateGamepadButton(bd, io, ImGuiKey_GamepadR3, SDL_GAMEPAD_BUTTON_RIGHT_STICK);
|
||||
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickLeft, SDL_GAMEPAD_AXIS_LEFTX,
|
||||
-thumb_dead_zone, -32768);
|
||||
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickRight, SDL_GAMEPAD_AXIS_LEFTX,
|
||||
+thumb_dead_zone, +32767);
|
||||
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickUp, SDL_GAMEPAD_AXIS_LEFTY, -thumb_dead_zone,
|
||||
-32768);
|
||||
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickDown, SDL_GAMEPAD_AXIS_LEFTY,
|
||||
+thumb_dead_zone, +32767);
|
||||
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickLeft, SDL_GAMEPAD_AXIS_RIGHTX,
|
||||
-thumb_dead_zone, -32768);
|
||||
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickRight, SDL_GAMEPAD_AXIS_RIGHTX,
|
||||
+thumb_dead_zone, +32767);
|
||||
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickUp, SDL_GAMEPAD_AXIS_RIGHTY, -thumb_dead_zone,
|
||||
-32768);
|
||||
UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_GAMEPAD_AXIS_RIGHTY,
|
||||
+thumb_dead_zone, +32767);
|
||||
}
|
||||
|
||||
void NewFrame() {
|
||||
SdlData* bd = GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
|
||||
// (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens
|
||||
// in VMs and Emscripten, see #6189, #6114, #3644)
|
||||
static Uint64 frequency = SDL_GetPerformanceFrequency();
|
||||
Uint64 current_time = SDL_GetPerformanceCounter();
|
||||
if (current_time <= bd->time)
|
||||
current_time = bd->time + 1;
|
||||
io.DeltaTime = bd->time > 0 ? (float)((double)(current_time - bd->time) / (double)frequency)
|
||||
: (float)(1.0f / 60.0f);
|
||||
bd->time = current_time;
|
||||
|
||||
if (bd->mouse_pending_leave_frame && bd->mouse_pending_leave_frame >= ImGui::GetFrameCount() &&
|
||||
bd->mouse_buttons_down == 0) {
|
||||
bd->mouse_window_id = 0;
|
||||
bd->mouse_pending_leave_frame = 0;
|
||||
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||
}
|
||||
|
||||
UpdateMouseData();
|
||||
UpdateMouseCursor();
|
||||
|
||||
// Update game controllers (if enabled and available)
|
||||
UpdateGamepads();
|
||||
}
|
||||
|
||||
void OnResize() {
|
||||
SdlData* bd = GetBackendData();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
int w, h;
|
||||
int display_w, display_h;
|
||||
SDL_GetWindowSize(bd->window, &w, &h);
|
||||
if (SDL_GetWindowFlags(bd->window) & SDL_WINDOW_MINIMIZED) {
|
||||
w = h = 0;
|
||||
}
|
||||
SDL_GetWindowSizeInPixels(bd->window, &display_w, &display_h);
|
||||
io.DisplaySize = ImVec2((float)w, (float)h);
|
||||
if (w > 0 && h > 0) {
|
||||
io.DisplayFramebufferScale = {(float)display_w / (float)w, (float)display_h / (float)h};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ImGui::Sdl
|
31
src/imgui/renderer/imgui_impl_sdl3.h
Normal file
31
src/imgui/renderer/imgui_impl_sdl3.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Based on imgui_impl_sdl3.h from Dear ImGui repository
|
||||
|
||||
#pragma once
|
||||
|
||||
struct SDL_Window;
|
||||
struct SDL_Renderer;
|
||||
struct SDL_Gamepad;
|
||||
typedef union SDL_Event SDL_Event;
|
||||
|
||||
namespace ImGui::Sdl {
|
||||
|
||||
bool Init(SDL_Window* window);
|
||||
void Shutdown();
|
||||
void NewFrame();
|
||||
bool ProcessEvent(const SDL_Event* event);
|
||||
void OnResize();
|
||||
|
||||
// Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad.
|
||||
// You may override this. When using manual mode, caller is responsible for opening/closing gamepad.
|
||||
enum GamepadMode {
|
||||
ImGui_ImplSDL3_GamepadMode_AutoFirst,
|
||||
ImGui_ImplSDL3_GamepadMode_AutoAll,
|
||||
ImGui_ImplSDL3_GamepadMode_Manual
|
||||
};
|
||||
void SetGamepadMode(GamepadMode mode, SDL_Gamepad** manual_gamepads_array = NULL,
|
||||
int manual_gamepads_count = -1);
|
||||
|
||||
}; // namespace ImGui::Sdl
|
1107
src/imgui/renderer/imgui_impl_vulkan.cpp
Normal file
1107
src/imgui/renderer/imgui_impl_vulkan.cpp
Normal file
File diff suppressed because it is too large
Load diff
43
src/imgui/renderer/imgui_impl_vulkan.h
Normal file
43
src/imgui/renderer/imgui_impl_vulkan.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Based on imgui_impl_vulkan.h from Dear ImGui repository
|
||||
|
||||
#pragma once
|
||||
|
||||
#define VULKAN_HPP_NO_EXCEPTIONS
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
struct ImDrawData;
|
||||
|
||||
namespace ImGui::Vulkan {
|
||||
|
||||
struct InitInfo {
|
||||
vk::Instance instance;
|
||||
vk::PhysicalDevice physical_device;
|
||||
vk::Device device;
|
||||
uint32_t queue_family;
|
||||
vk::Queue queue;
|
||||
uint32_t image_count; // >= 2
|
||||
vk::DeviceSize min_allocation_size; // Minimum allocation size
|
||||
vk::PipelineCache pipeline_cache;
|
||||
uint32_t subpass;
|
||||
vk::PipelineRenderingCreateInfoKHR pipeline_rendering_create_info;
|
||||
|
||||
// (Optional) Allocation, Logging
|
||||
const vk::AllocationCallbacks* allocator{};
|
||||
void (*check_vk_result_fn)(vk::Result err);
|
||||
};
|
||||
|
||||
vk::DescriptorSet AddTexture(vk::Sampler sampler, vk::ImageView image_view,
|
||||
vk::ImageLayout image_layout);
|
||||
|
||||
void RemoveTexture(vk::DescriptorSet descriptor_set);
|
||||
|
||||
bool Init(InitInfo info);
|
||||
void Shutdown();
|
||||
void NewFrame();
|
||||
void RenderDrawData(ImDrawData& draw_data, vk::CommandBuffer command_buffer,
|
||||
vk::Pipeline pipeline = VK_NULL_HANDLE);
|
||||
|
||||
} // namespace ImGui::Vulkan
|
|
@ -577,7 +577,7 @@ void MainWindow::SaveWindowState() const {
|
|||
void MainWindow::InstallPkg() {
|
||||
QFileDialog dialog;
|
||||
dialog.setFileMode(QFileDialog::ExistingFiles);
|
||||
dialog.setNameFilter(tr("PKG File (*.PKG)"));
|
||||
dialog.setNameFilter(tr("PKG File (*.PKG *.pkg)"));
|
||||
if (dialog.exec()) {
|
||||
QStringList fileNames = dialog.selectedFiles();
|
||||
int nPkg = fileNames.size();
|
||||
|
@ -949,4 +949,4 @@ void MainWindow::OnLanguageChanged(const std::string& locale) {
|
|||
Config::setEmulatorLanguage(locale);
|
||||
|
||||
LoadTranslation();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,13 +8,13 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
|
|||
|
||||
switch (theme) {
|
||||
case Theme::Dark:
|
||||
mw_searchbar->setStyleSheet("background-color: #1e1e1e; /* Dark background */"
|
||||
"color: #ffffff; /* White text */"
|
||||
"border: 1px solid #ffffff; /* White border */"
|
||||
mw_searchbar->setStyleSheet("background-color: #1e1e1e;" // Dark background
|
||||
"color: #ffffff;" // White text
|
||||
"border: 2px solid #ffffff;" // White border
|
||||
"padding: 5px;");
|
||||
themePalette.setColor(QPalette::Window, QColor(53, 53, 53));
|
||||
themePalette.setColor(QPalette::Window, QColor(50, 50, 50));
|
||||
themePalette.setColor(QPalette::WindowText, Qt::white);
|
||||
themePalette.setColor(QPalette::Base, QColor(25, 25, 25));
|
||||
themePalette.setColor(QPalette::Base, QColor(20, 20, 20));
|
||||
themePalette.setColor(QPalette::AlternateBase, QColor(25, 25, 25));
|
||||
themePalette.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
|
||||
themePalette.setColor(QPalette::ToolTipBase, Qt::white);
|
||||
|
@ -30,8 +30,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
|
|||
break;
|
||||
|
||||
case Theme::Light:
|
||||
mw_searchbar->setStyleSheet("background-color: #ffffff; /* Light gray background */"
|
||||
"color: #000000; /* Black text */"
|
||||
mw_searchbar->setStyleSheet("background-color: #ffffff;" // Light gray background
|
||||
"color: #000000;" // Black text
|
||||
"border: 2px solid #000000;" // Black border
|
||||
"padding: 5px;");
|
||||
themePalette.setColor(QPalette::Window, QColor(240, 240, 240)); // Light gray
|
||||
themePalette.setColor(QPalette::WindowText, Qt::black); // Black
|
||||
|
@ -49,9 +50,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
|
|||
break;
|
||||
|
||||
case Theme::Green:
|
||||
mw_searchbar->setStyleSheet("background-color: #354535; /* Dark green background */"
|
||||
"color: #ffffff; /* White text */"
|
||||
"border: 1px solid #ffffff; /* White border */"
|
||||
mw_searchbar->setStyleSheet("background-color: #1e1e1e;" // Dark background
|
||||
"color: #ffffff;" // White text
|
||||
"border: 2px solid #ffffff;" // White border
|
||||
"padding: 5px;");
|
||||
themePalette.setColor(QPalette::Window, QColor(53, 69, 53)); // Dark green background
|
||||
themePalette.setColor(QPalette::WindowText, Qt::white); // White text
|
||||
|
@ -72,9 +73,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
|
|||
break;
|
||||
|
||||
case Theme::Blue:
|
||||
mw_searchbar->setStyleSheet("background-color: #283c5a; /* Dark blue background */"
|
||||
"color: #ffffff; /* White text */"
|
||||
"border: 1px solid #ffffff; /* White border */"
|
||||
mw_searchbar->setStyleSheet("background-color: #1e1e1e;" // Dark background
|
||||
"color: #ffffff;" // White text
|
||||
"border: 2px solid #ffffff;" // White border
|
||||
"padding: 5px;");
|
||||
themePalette.setColor(QPalette::Window, QColor(40, 60, 90)); // Dark blue background
|
||||
themePalette.setColor(QPalette::WindowText, Qt::white); // White text
|
||||
|
@ -95,9 +96,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
|
|||
break;
|
||||
|
||||
case Theme::Violet:
|
||||
mw_searchbar->setStyleSheet("background-color: #643278; /* Violet background */"
|
||||
"color: #ffffff; /* White text */"
|
||||
"border: 1px solid #ffffff; /* White border */"
|
||||
mw_searchbar->setStyleSheet("background-color: #1e1e1e;" // Dark background
|
||||
"color: #ffffff;" // White text
|
||||
"border: 2px solid #ffffff;" // White border
|
||||
"padding: 5px;");
|
||||
themePalette.setColor(QPalette::Window, QColor(100, 50, 120)); // Violet background
|
||||
themePalette.setColor(QPalette::WindowText, Qt::white); // White text
|
||||
|
|
|
@ -267,8 +267,8 @@ public:
|
|||
menuView->addAction(menuGame_List_Mode->menuAction());
|
||||
menuView->addAction(menuGame_List_Icons->menuAction());
|
||||
menuView->addAction(menuThemes->menuAction());
|
||||
menuThemes->addAction(setThemeLight);
|
||||
menuThemes->addAction(setThemeDark);
|
||||
menuThemes->addAction(setThemeLight);
|
||||
menuThemes->addAction(setThemeGreen);
|
||||
menuThemes->addAction(setThemeBlue);
|
||||
menuThemes->addAction(setThemeViolet);
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<message>
|
||||
<location filename="../about_dialog.ui" line="99"/>
|
||||
<source>This software should not be used to play games you have not legally obtained.</source>
|
||||
<translation>Ce logiciel ne doit pas être utilisé pour jouer à des jeux que vous n'avez pas obtenus légalement.</translation>
|
||||
<translation>Ce logiciel ne doit pas être utilisé pour jouer à des jeux que vous n'avez pas obtenus légalement.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -60,7 +60,7 @@
|
|||
<message>
|
||||
<location filename="../game_install_dialog.cpp" line="31"/>
|
||||
<source>Directory to install games</source>
|
||||
<translation>Répertoire d'installation des jeux</translation>
|
||||
<translation>Répertoire d'installation des jeux</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../game_install_dialog.cpp" line="50"/>
|
||||
|
@ -75,7 +75,7 @@
|
|||
<message>
|
||||
<location filename="../game_install_dialog.cpp" line="75"/>
|
||||
<source>The value for location to install games is not valid.</source>
|
||||
<translation>Le répertoire d'installation des jeux n'est pas valide.</translation>
|
||||
<translation>Le répertoire d'installation des jeux n'est pas valide.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -118,7 +118,7 @@
|
|||
<message>
|
||||
<location filename="../gui_context_menus.h" line="61"/>
|
||||
<source>Copy Serial</source>
|
||||
<translation>Copier le numéro de série</translation>
|
||||
<translation>Copier le N° de série</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../gui_context_menus.h" line="62"/>
|
||||
|
@ -201,7 +201,7 @@
|
|||
<message>
|
||||
<location filename="../main_window_ui.h" line="327"/>
|
||||
<source>Exit the application.</source>
|
||||
<translation>Fermer l'application.</translation>
|
||||
<translation>Fermer l'application.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../main_window_ui.h" line="330"/>
|
||||
|
@ -291,7 +291,7 @@
|
|||
<message>
|
||||
<location filename="../main_window_ui.h" line="354"/>
|
||||
<source>Game List Mode</source>
|
||||
<translation>Mode d'affichage</translation>
|
||||
<translation>Mode d'affichage</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../main_window_ui.h" line="355"/>
|
||||
|
@ -301,7 +301,7 @@
|
|||
<message>
|
||||
<location filename="../main_window_ui.h" line="356"/>
|
||||
<source>Utils</source>
|
||||
<translation>Utilitaire</translation>
|
||||
<translation>Utilitaires</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../main_window_ui.h" line="357"/>
|
||||
|
@ -316,12 +316,12 @@
|
|||
<message>
|
||||
<location filename="../main_window_ui.h" line="359"/>
|
||||
<source>Dark</source>
|
||||
<translation>Noir</translation>
|
||||
<translation>Sombre</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../main_window_ui.h" line="360"/>
|
||||
<source>Light</source>
|
||||
<translation>Blanc</translation>
|
||||
<translation>Clair</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../main_window_ui.h" line="361"/>
|
||||
|
@ -341,7 +341,7 @@
|
|||
<message>
|
||||
<location filename="../main_window_ui.h" line="364"/>
|
||||
<source>toolBar</source>
|
||||
<translation>Bare d'outils</translation>
|
||||
<translation>Bare d'outils</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -385,7 +385,7 @@
|
|||
<message>
|
||||
<location filename="../settings_dialog.ui" line="95"/>
|
||||
<source>Emulator Language</source>
|
||||
<translation>Langage de l'émulateur</translation>
|
||||
<translation>Langage de l'émulateur</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settings_dialog.ui" line="114"/>
|
||||
|
@ -400,7 +400,7 @@
|
|||
<message>
|
||||
<location filename="../settings_dialog.ui" line="129"/>
|
||||
<source>Show Splash</source>
|
||||
<translation>Afficher l'image du jeu</translation>
|
||||
<translation>Afficher l'image du jeu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settings_dialog.ui" line="136"/>
|
||||
|
@ -410,7 +410,7 @@
|
|||
<message>
|
||||
<location filename="../settings_dialog.ui" line="155"/>
|
||||
<source>Username</source>
|
||||
<translation>Nom d'utilisateur</translation>
|
||||
<translation>Nom d'utilisateur</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settings_dialog.ui" line="178"/>
|
||||
|
@ -538,7 +538,7 @@
|
|||
<message>
|
||||
<location filename="../main_window.cpp" line="392"/>
|
||||
<source>All Patches available for all games have been downloaded.</source>
|
||||
<translation>Tous les patchs disponibles pour les jeux ont été téléchargés.</translation>
|
||||
<translation>Tous les patchs disponibles ont été téléchargés.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../main_window.cpp" line="549"/>
|
||||
|
@ -906,7 +906,7 @@
|
|||
<message>
|
||||
<location filename="../cheats_patches.cpp" line="1163"/>
|
||||
<source>Can't apply cheats before the game is started</source>
|
||||
<translation>Impossible d'appliquer les triches avant que le jeu ne commence.</translation>
|
||||
<translation>Impossible d'appliquer les Cheats avant que le jeu ne commence.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -972,7 +972,7 @@
|
|||
<message>
|
||||
<location filename="../game_list_frame.cpp" line="35"/>
|
||||
<source>Path</source>
|
||||
<translation>Chemin</translation>
|
||||
<translation>Répertoire</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
|
@ -9,6 +9,7 @@
|
|||
#include "common/config.h"
|
||||
#include "common/version.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
#include "imgui/renderer/imgui_core.h"
|
||||
#include "input/controller.h"
|
||||
#include "sdl_window.h"
|
||||
#include "video_core/renderdoc.h"
|
||||
|
@ -80,6 +81,10 @@ void WindowSDL::waitEvent() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (ImGui::Core::ProcessEvent(&event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_WINDOW_RESIZED:
|
||||
case SDL_EVENT_WINDOW_MAXIMIZED:
|
||||
|
@ -115,6 +120,7 @@ void WindowSDL::waitEvent() {
|
|||
|
||||
void WindowSDL::onResize() {
|
||||
SDL_GetWindowSizeInPixels(window, &width, &height);
|
||||
ImGui::Core::OnResize();
|
||||
}
|
||||
|
||||
void WindowSDL::onKeyPress(const SDL_Event* event) {
|
||||
|
|
|
@ -58,6 +58,10 @@ public:
|
|||
return is_open;
|
||||
}
|
||||
|
||||
[[nodiscard]] SDL_Window* GetSdlWindow() const {
|
||||
return window;
|
||||
}
|
||||
|
||||
WindowSystemInfo getWindowInfo() const {
|
||||
return window_info;
|
||||
}
|
||||
|
|
|
@ -221,11 +221,15 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
|||
const auto marker_sz = nop->header.count.Value() * 2;
|
||||
const std::string_view label{reinterpret_cast<const char*>(&nop->data_block[1]),
|
||||
marker_sz};
|
||||
rasterizer->ScopeMarkerBegin(label);
|
||||
if (rasterizer) {
|
||||
rasterizer->ScopeMarkerBegin(label);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PM4CmdNop::PayloadType::DebugMarkerPop: {
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
if (rasterizer) {
|
||||
rasterizer->ScopeMarkerEnd();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -536,7 +540,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
|||
break;
|
||||
}
|
||||
case PM4ItOpcode::PfpSyncMe: {
|
||||
rasterizer->CpSync();
|
||||
if (rasterizer) {
|
||||
rasterizer->CpSync();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <thread>
|
||||
#include "common/alignment.h"
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
#include "video_core/page_manager.h"
|
||||
|
@ -159,6 +160,27 @@ struct PageManager::Impl {
|
|||
int uffd;
|
||||
};
|
||||
#else
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
#if defined(ARCH_X86_64)
|
||||
#define IS_WRITE_ERROR(ctx) ((ctx)->uc_mcontext->__es.__err & 0x2)
|
||||
#elif defined(ARCH_ARM64)
|
||||
#define IS_WRITE_ERROR(ctx) ((ctx)->uc_mcontext->__es.__esr & 0x40)
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if defined(ARCH_X86_64)
|
||||
#define IS_WRITE_ERROR(ctx) ((ctx)->uc_mcontext.gregs[REG_ERR] & 0x2)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef IS_WRITE_ERROR
|
||||
#error "Missing IS_WRITE_ERROR() implementation for target OS and CPU architecture.
|
||||
#endif
|
||||
|
||||
struct PageManager::Impl {
|
||||
Impl(Vulkan::Rasterizer* rasterizer_) {
|
||||
rasterizer = rasterizer_;
|
||||
|
@ -194,12 +216,7 @@ struct PageManager::Impl {
|
|||
static void GuestFaultSignalHandler(int sig, siginfo_t* info, void* raw_context) {
|
||||
ucontext_t* ctx = reinterpret_cast<ucontext_t*>(raw_context);
|
||||
const VAddr address = reinterpret_cast<VAddr>(info->si_addr);
|
||||
#ifdef __APPLE__
|
||||
const u32 err = ctx->uc_mcontext->__es.__err;
|
||||
#else
|
||||
const greg_t err = ctx->uc_mcontext.gregs[REG_ERR];
|
||||
#endif
|
||||
if (err & 0x2) {
|
||||
if (IS_WRITE_ERROR(ctx)) {
|
||||
const VAddr addr_aligned = Common::AlignDown(address, PAGESIZE);
|
||||
rasterizer->InvalidateMemory(addr_aligned, PAGESIZE);
|
||||
} else {
|
||||
|
|
|
@ -660,8 +660,8 @@ void EmitQuadToTriangleListIndices(u8* out_ptr, u32 num_vertices) {
|
|||
*out_data++ = i;
|
||||
*out_data++ = i + 1;
|
||||
*out_data++ = i + 2;
|
||||
*out_data++ = i + 2;
|
||||
*out_data++ = i;
|
||||
*out_data++ = i + 2;
|
||||
*out_data++ = i + 3;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "common/singleton.h"
|
||||
#include "core/file_format/splash.h"
|
||||
#include "core/libraries/system/systemservice.h"
|
||||
#include "imgui/renderer/imgui_core.h"
|
||||
#include "sdl_window.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
|
@ -73,7 +74,7 @@ RendererVulkan::RendererVulkan(Frontend::WindowSDL& window_, AmdGpu::Liverpool*
|
|||
draw_scheduler{instance}, present_scheduler{instance}, flip_scheduler{instance},
|
||||
swapchain{instance, window},
|
||||
rasterizer{std::make_unique<Rasterizer>(instance, draw_scheduler, liverpool)},
|
||||
texture_cache{rasterizer->GetTextureCache()} {
|
||||
texture_cache{rasterizer->GetTextureCache()}, video_info_ui{this} {
|
||||
const u32 num_images = swapchain.GetImageCount();
|
||||
const vk::Device device = instance.GetDevice();
|
||||
|
||||
|
@ -84,9 +85,14 @@ RendererVulkan::RendererVulkan(Frontend::WindowSDL& window_, AmdGpu::Liverpool*
|
|||
frame.present_done = device.createFence({.flags = vk::FenceCreateFlagBits::eSignaled});
|
||||
free_queue.push(&frame);
|
||||
}
|
||||
|
||||
// Setup ImGui
|
||||
ImGui::Core::Initialize(instance, window, num_images, swapchain.GetSurfaceFormat().format);
|
||||
ImGui::Layer::AddLayer(&video_info_ui);
|
||||
}
|
||||
|
||||
RendererVulkan::~RendererVulkan() {
|
||||
ImGui::Layer::RemoveLayer(&video_info_ui);
|
||||
draw_scheduler.Finish();
|
||||
const vk::Device device = instance.GetDevice();
|
||||
for (auto& frame : present_frames) {
|
||||
|
@ -94,6 +100,7 @@ RendererVulkan::~RendererVulkan() {
|
|||
device.destroyImageView(frame.image_view);
|
||||
device.destroyFence(frame.present_done);
|
||||
}
|
||||
ImGui::Core::Shutdown(device);
|
||||
}
|
||||
|
||||
void RendererVulkan::RecreateFrame(Frame* frame, u32 width, u32 height) {
|
||||
|
@ -254,6 +261,8 @@ Frame* RendererVulkan::PrepareFrameInternal(VideoCore::Image& image, bool is_eop
|
|||
}
|
||||
|
||||
void RendererVulkan::Present(Frame* frame) {
|
||||
ImGui::Core::NewFrame();
|
||||
|
||||
swapchain.AcquireNextImage();
|
||||
|
||||
const vk::Image swapchain_image = swapchain.Image();
|
||||
|
@ -286,7 +295,7 @@ void RendererVulkan::Present(Frame* frame) {
|
|||
vk::ImageMemoryBarrier{
|
||||
.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite,
|
||||
.dstAccessMask = vk::AccessFlagBits::eTransferRead,
|
||||
.oldLayout = vk::ImageLayout::eGeneral,
|
||||
.oldLayout = vk::ImageLayout::eUndefined,
|
||||
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
|
@ -317,6 +326,8 @@ void RendererVulkan::Present(Frame* frame) {
|
|||
},
|
||||
};
|
||||
|
||||
ImGui::Core::Render(cmdbuf, frame);
|
||||
|
||||
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::DependencyFlagBits::eByRegion, {}, {}, pre_barriers);
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
|
||||
#include "imgui/layer/video_info.h"
|
||||
#include "video_core/amdgpu/liverpool.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
|
@ -103,6 +105,8 @@ private:
|
|||
std::condition_variable_any frame_cv;
|
||||
std::optional<VideoCore::Image> splash_img;
|
||||
std::vector<VAddr> vo_buffers_addr;
|
||||
|
||||
ImGui::Layers::VideoInfo video_info_ui;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -204,7 +204,7 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, bool force_depth /*= false*/) n
|
|||
tiling_mode = image.GetTilingMode();
|
||||
pixel_format = LiverpoolToVK::SurfaceFormat(image.GetDataFmt(), image.GetNumberFmt());
|
||||
// Override format if image is forced to be a depth target
|
||||
if (force_depth || tiling_mode == AmdGpu::TilingMode::Depth_MacroTiled) {
|
||||
if (force_depth) {
|
||||
if (pixel_format == vk::Format::eR32Sfloat || pixel_format == vk::Format::eR8Unorm) {
|
||||
pixel_format = vk::Format::eD32SfloatS8Uint;
|
||||
} else if (pixel_format == vk::Format::eR16Unorm) {
|
||||
|
@ -260,7 +260,6 @@ void ImageInfo::UpdateSize() {
|
|||
|
||||
switch (tiling_mode) {
|
||||
case AmdGpu::TilingMode::Display_Linear: {
|
||||
ASSERT(!props.is_cube);
|
||||
std::tie(mip_info.pitch, mip_info.size) =
|
||||
ImageSizeLinearAligned(mip_w, mip_h, bpp, num_samples);
|
||||
mip_info.height = mip_h;
|
||||
|
|
Loading…
Add table
Reference in a new issue