Compare commits
160 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0df09335c | ||
|
|
f7a1827aaa | ||
|
|
7c4fb09a7c | ||
|
|
d01bf170c4 | ||
|
|
e23543918b | ||
|
|
69a9bd8187 | ||
|
|
e65966bcfc | ||
|
|
67ff974387 | ||
|
|
b6b78203cc | ||
|
|
54c2a4cafc | ||
|
|
69b3f98d3a | ||
|
|
d57e00801d | ||
|
|
c267aea29b | ||
|
|
f1969ee1f3 | ||
|
|
24dfb43ac6 | ||
|
|
3135dbc29c | ||
|
|
c4b5319446 | ||
|
|
d77af9f8fd | ||
|
|
cb5b68cb0a | ||
|
|
f820e58be4 | ||
|
|
c9ac23683b | ||
|
|
bee5a7acb1 | ||
|
|
b358e88512 | ||
|
|
50d2abaaa9 | ||
|
|
af159a4d08 | ||
|
|
e633021532 | ||
|
|
232d95b56e | ||
|
|
033b46253e | ||
|
|
5a6dc4d041 | ||
|
|
0e6a608245 | ||
|
|
1bf5a337a5 | ||
|
|
b84f4cfb62 | ||
|
|
f0d3f1b376 | ||
|
|
f926559ef4 | ||
|
|
93f7677402 | ||
|
|
756e773096 | ||
|
|
02602afd10 | ||
|
|
d6583d68f6 | ||
|
|
1d555fdd25 | ||
|
|
8b27e73bd7 | ||
|
|
699900eed0 | ||
|
|
74eb16521f | ||
|
|
8a5e6fce07 | ||
|
|
864cbbaf4c | ||
|
|
0e35f1bb18 | ||
|
|
568bcbc29d | ||
|
|
f5ce71793e | ||
|
|
ec38b4e883 | ||
|
|
61586e8794 | ||
|
|
4a6a9b6622 | ||
|
|
d4012a4540 | ||
|
|
7f10db1c20 | ||
|
|
bb175ab430 | ||
|
|
8cdb48224d | ||
|
|
81e14c072a | ||
|
|
0368260c99 | ||
|
|
377c60645c | ||
|
|
0a9fedfac9 | ||
|
|
3533d33ff5 | ||
|
|
45211a7a91 | ||
|
|
f11173f88c | ||
|
|
cc0801745a | ||
|
|
74fd0aa2e8 | ||
|
|
5af4160bf2 | ||
|
|
423a3ed2c8 | ||
|
|
b55c7bbcf7 | ||
|
|
bb893188eb | ||
|
|
1a543723ab | ||
|
|
f17e122025 | ||
|
|
54e74b3572 | ||
|
|
aa7e53ab5c | ||
|
|
ab292c501c | ||
|
|
67486c0568 | ||
|
|
1d0604e33c | ||
|
|
58d82243f7 | ||
|
|
820bcee6a4 | ||
|
|
9dcc229dfe | ||
|
|
aaec85df9e | ||
|
|
da6d4cde56 | ||
|
|
17d8e25cbf | ||
|
|
31d1e06eb1 | ||
|
|
cfbae58b2b | ||
|
|
73b7748984 | ||
|
|
8b4b560df5 | ||
|
|
dd254c603d | ||
|
|
4dcdd3a837 | ||
|
|
3fa2b218ac | ||
|
|
fe2609cb77 | ||
|
|
b6d2c64f4d | ||
|
|
e9265ac598 | ||
|
|
2caac4a395 | ||
|
|
eb36463e03 | ||
|
|
5d46038c5c | ||
|
|
34e4aaddd9 | ||
|
|
a9fa890f14 | ||
|
|
d68795c665 | ||
|
|
32775125b7 | ||
|
|
048da7240d | ||
|
|
96535c13a5 | ||
|
|
233e495c14 | ||
|
|
fb5d4b17de | ||
|
|
2d37ca3726 | ||
|
|
76ac234bf6 | ||
|
|
f472232705 | ||
|
|
43e7c6cf49 | ||
|
|
aef0d88165 | ||
|
|
312ef596a5 | ||
|
|
dd92db3fb0 | ||
|
|
e58c951a59 | ||
|
|
3a6cd5b3c8 | ||
|
|
3d1a221893 | ||
|
|
afe8df5020 | ||
|
|
2e1dd9c649 | ||
|
|
f1aec256d7 | ||
|
|
55ded706d6 | ||
|
|
e9145c3e16 | ||
|
|
3b25426bd9 | ||
|
|
0fd45e78f4 | ||
|
|
06cf050c0a | ||
|
|
d1b7c65b9e | ||
|
|
b8f7f9651e | ||
|
|
c77454b9d0 | ||
|
|
fd5fa48674 | ||
|
|
152422bab1 | ||
|
|
0c3e7b7086 | ||
|
|
f66c6fe554 | ||
|
|
362b28d052 | ||
|
|
a69b9d73f5 | ||
|
|
7fbe2c83a7 | ||
|
|
b9c1e4b0e7 | ||
|
|
beab38601b | ||
|
|
60fecee1ec | ||
|
|
ea680bea60 | ||
|
|
56cf5b7b17 | ||
|
|
02e6602baa | ||
|
|
4ee087fb3c | ||
|
|
19b2571aec | ||
|
|
6209fe0c27 | ||
|
|
8b433beff3 | ||
|
|
7cfb29de23 | ||
|
|
3cf7246e37 | ||
|
|
fed6ab14c3 | ||
|
|
e696ed1f4d | ||
|
|
a81645400f | ||
|
|
48fcb43585 | ||
|
|
5454494adb | ||
|
|
de16c1e453 | ||
|
|
ae53b84efd | ||
|
|
5b95de0c9c | ||
|
|
731b4bd691 | ||
|
|
ba03bfa430 | ||
|
|
5ce6b8fea7 | ||
|
|
0682a908c0 | ||
|
|
c7b6c9de9c | ||
|
|
76d515327b | ||
|
|
c70529c1ec | ||
|
|
7901de2b75 | ||
|
|
9359655712 | ||
|
|
af42320021 | ||
|
|
0080a8da58 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -13,9 +13,6 @@
|
||||
[submodule "dynarmic"]
|
||||
path = externals/dynarmic
|
||||
url = https://github.com/MerryMage/dynarmic.git
|
||||
[submodule "xbyak"]
|
||||
path = externals/xbyak
|
||||
url = https://github.com/herumi/xbyak.git
|
||||
[submodule "fmt"]
|
||||
path = externals/fmt
|
||||
url = https://github.com/fmtlib/fmt.git
|
||||
|
||||
@@ -377,7 +377,7 @@ if (CLANG_FORMAT)
|
||||
set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
|
||||
if (WIN32)
|
||||
add_custom_target(clang-format
|
||||
COMMAND powershell.exe -Command "${CLANG_FORMAT} -i @(Get-ChildItem -Recurse ${SRCS}/* -Include \'*.h\', \'*.cpp\')"
|
||||
COMMAND powershell.exe -Command "Get-ChildItem ${SRCS}/* -Include *.cpp,*.h -Recurse | Foreach {${CLANG_FORMAT} -i $_.fullname}"
|
||||
COMMENT ${CCOMMENT})
|
||||
elseif(MINGW)
|
||||
add_custom_target(clang-format
|
||||
|
||||
9
externals/CMakeLists.txt
vendored
9
externals/CMakeLists.txt
vendored
@@ -9,7 +9,6 @@ target_include_directories(catch-single-include INTERFACE catch/single_include)
|
||||
|
||||
# Dynarmic
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_library(xbyak INTERFACE)
|
||||
set(DYNARMIC_TESTS OFF)
|
||||
set(DYNARMIC_NO_BUNDLED_FMT ON)
|
||||
add_subdirectory(dynarmic)
|
||||
@@ -53,14 +52,6 @@ target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
|
||||
# SoundTouch
|
||||
add_subdirectory(soundtouch)
|
||||
|
||||
# Xbyak
|
||||
if (ARCHITECTURE_x86_64)
|
||||
# Defined before "dynarmic" above
|
||||
# add_library(xbyak INTERFACE)
|
||||
target_include_directories(xbyak INTERFACE ./xbyak/xbyak)
|
||||
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
|
||||
endif()
|
||||
|
||||
# Opus
|
||||
add_subdirectory(opus)
|
||||
target_include_directories(opus INTERFACE ./opus/include)
|
||||
|
||||
2390
externals/glad/include/glad/glad.h
vendored
2390
externals/glad/include/glad/glad.h
vendored
File diff suppressed because one or more lines are too long
2549
externals/glad/src/glad.c
vendored
2549
externals/glad/src/glad.c
vendored
File diff suppressed because one or more lines are too long
1
externals/xbyak
vendored
1
externals/xbyak
vendored
Submodule externals/xbyak deleted from 1de435ed04
@@ -44,7 +44,6 @@ add_library(common STATIC
|
||||
detached_tasks.cpp
|
||||
detached_tasks.h
|
||||
bit_field.h
|
||||
bit_set.h
|
||||
cityhash.cpp
|
||||
cityhash.h
|
||||
color.h
|
||||
@@ -95,14 +94,9 @@ if(ARCHITECTURE_x86_64)
|
||||
PRIVATE
|
||||
x64/cpu_detect.cpp
|
||||
x64/cpu_detect.h
|
||||
x64/xbyak_abi.h
|
||||
x64/xbyak_util.h
|
||||
)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(common)
|
||||
|
||||
target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
|
||||
if (ARCHITECTURE_x86_64)
|
||||
target_link_libraries(common PRIVATE xbyak)
|
||||
endif()
|
||||
|
||||
@@ -52,5 +52,8 @@ __declspec(noinline, noreturn)
|
||||
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
|
||||
#endif
|
||||
|
||||
#define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!")
|
||||
#define UNIMPLEMENTED() ASSERT_MSG(false, "Unimplemented code!")
|
||||
#define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
|
||||
|
||||
#define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!")
|
||||
#define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__)
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
// This file is under the public domain.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#ifdef _WIN32
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#include <initializer_list>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include "common/common_types.h"
|
||||
|
||||
// namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly
|
||||
namespace Common {
|
||||
|
||||
// Helper functions:
|
||||
|
||||
#ifdef _MSC_VER
|
||||
template <typename T>
|
||||
static inline int CountSetBits(T v) {
|
||||
// from https://graphics.stanford.edu/~seander/bithacks.html
|
||||
// GCC has this built in, but MSVC's intrinsic will only emit the actual
|
||||
// POPCNT instruction, which we're not depending on
|
||||
v = v - ((v >> 1) & (T) ~(T)0 / 3);
|
||||
v = (v & (T) ~(T)0 / 15 * 3) + ((v >> 2) & (T) ~(T)0 / 15 * 3);
|
||||
v = (v + (v >> 4)) & (T) ~(T)0 / 255 * 15;
|
||||
return (T)(v * ((T) ~(T)0 / 255)) >> (sizeof(T) - 1) * 8;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u8 val) {
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u16 val) {
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u32 val) {
|
||||
unsigned long index;
|
||||
_BitScanForward(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u64 val) {
|
||||
unsigned long index;
|
||||
_BitScanForward64(&index, val);
|
||||
return (int)index;
|
||||
}
|
||||
#else
|
||||
static inline int CountSetBits(u8 val) {
|
||||
return __builtin_popcount(val);
|
||||
}
|
||||
static inline int CountSetBits(u16 val) {
|
||||
return __builtin_popcount(val);
|
||||
}
|
||||
static inline int CountSetBits(u32 val) {
|
||||
return __builtin_popcount(val);
|
||||
}
|
||||
static inline int CountSetBits(u64 val) {
|
||||
return __builtin_popcountll(val);
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u8 val) {
|
||||
return __builtin_ctz(val);
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u16 val) {
|
||||
return __builtin_ctz(val);
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u32 val) {
|
||||
return __builtin_ctz(val);
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u64 val) {
|
||||
return __builtin_ctzll(val);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
|
||||
// using the set bits of an integer to represent a set of integers. Like that
|
||||
// class, it acts like an array of bools:
|
||||
// BitSet32 bs;
|
||||
// bs[1] = true;
|
||||
// but also like the underlying integer ([0] = least significant bit):
|
||||
// BitSet32 bs2 = ...;
|
||||
// bs = (bs ^ bs2) & BitSet32(0xffff);
|
||||
// The following additional functionality is provided:
|
||||
// - Construction using an initializer list.
|
||||
// BitSet bs { 1, 2, 4, 8 };
|
||||
// - Efficiently iterating through the set bits:
|
||||
// for (int i : bs)
|
||||
// [i is the *index* of a set bit]
|
||||
// (This uses the appropriate CPU instruction to find the next set bit in one
|
||||
// operation.)
|
||||
// - Counting set bits using .Count() - see comment on that method.
|
||||
|
||||
// TODO: use constexpr when MSVC gets out of the Dark Ages
|
||||
|
||||
template <typename IntTy>
|
||||
class BitSet {
|
||||
static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types");
|
||||
|
||||
public:
|
||||
// A reference to a particular bit, returned from operator[].
|
||||
class Ref {
|
||||
public:
|
||||
Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
|
||||
Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
|
||||
operator bool() const {
|
||||
return (m_bs->m_val & m_mask) != 0;
|
||||
}
|
||||
bool operator=(bool set) {
|
||||
m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
|
||||
return set;
|
||||
}
|
||||
|
||||
private:
|
||||
BitSet* m_bs;
|
||||
IntTy m_mask;
|
||||
};
|
||||
|
||||
// A STL-like iterator is required to be able to use range-based for loops.
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {}
|
||||
Iterator(IntTy val) : m_val(val), m_bit(0) {}
|
||||
Iterator& operator=(Iterator other) {
|
||||
new (this) Iterator(other);
|
||||
return *this;
|
||||
}
|
||||
int operator*() {
|
||||
return m_bit + ComputeLsb();
|
||||
}
|
||||
Iterator& operator++() {
|
||||
int lsb = ComputeLsb();
|
||||
m_val >>= lsb + 1;
|
||||
m_bit += lsb + 1;
|
||||
m_has_lsb = false;
|
||||
return *this;
|
||||
}
|
||||
Iterator operator++(int _) {
|
||||
Iterator other(*this);
|
||||
++*this;
|
||||
return other;
|
||||
}
|
||||
bool operator==(Iterator other) const {
|
||||
return m_val == other.m_val;
|
||||
}
|
||||
bool operator!=(Iterator other) const {
|
||||
return m_val != other.m_val;
|
||||
}
|
||||
|
||||
private:
|
||||
int ComputeLsb() {
|
||||
if (!m_has_lsb) {
|
||||
m_lsb = LeastSignificantSetBit(m_val);
|
||||
m_has_lsb = true;
|
||||
}
|
||||
return m_lsb;
|
||||
}
|
||||
IntTy m_val;
|
||||
int m_bit;
|
||||
int m_lsb = -1;
|
||||
bool m_has_lsb = false;
|
||||
};
|
||||
|
||||
BitSet() : m_val(0) {}
|
||||
explicit BitSet(IntTy val) : m_val(val) {}
|
||||
BitSet(std::initializer_list<int> init) {
|
||||
m_val = 0;
|
||||
for (int bit : init)
|
||||
m_val |= (IntTy)1 << bit;
|
||||
}
|
||||
|
||||
static BitSet AllTrue(std::size_t count) {
|
||||
return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
|
||||
}
|
||||
|
||||
Ref operator[](std::size_t bit) {
|
||||
return Ref(this, (IntTy)1 << bit);
|
||||
}
|
||||
const Ref operator[](std::size_t bit) const {
|
||||
return (*const_cast<BitSet*>(this))[bit];
|
||||
}
|
||||
bool operator==(BitSet other) const {
|
||||
return m_val == other.m_val;
|
||||
}
|
||||
bool operator!=(BitSet other) const {
|
||||
return m_val != other.m_val;
|
||||
}
|
||||
bool operator<(BitSet other) const {
|
||||
return m_val < other.m_val;
|
||||
}
|
||||
bool operator>(BitSet other) const {
|
||||
return m_val > other.m_val;
|
||||
}
|
||||
BitSet operator|(BitSet other) const {
|
||||
return BitSet(m_val | other.m_val);
|
||||
}
|
||||
BitSet operator&(BitSet other) const {
|
||||
return BitSet(m_val & other.m_val);
|
||||
}
|
||||
BitSet operator^(BitSet other) const {
|
||||
return BitSet(m_val ^ other.m_val);
|
||||
}
|
||||
BitSet operator~() const {
|
||||
return BitSet(~m_val);
|
||||
}
|
||||
BitSet& operator|=(BitSet other) {
|
||||
return *this = *this | other;
|
||||
}
|
||||
BitSet& operator&=(BitSet other) {
|
||||
return *this = *this & other;
|
||||
}
|
||||
BitSet& operator^=(BitSet other) {
|
||||
return *this = *this ^ other;
|
||||
}
|
||||
operator u32() = delete;
|
||||
operator bool() {
|
||||
return m_val != 0;
|
||||
}
|
||||
|
||||
// Warning: Even though on modern CPUs this is a single fast instruction,
|
||||
// Dolphin's official builds do not currently assume POPCNT support on x86,
|
||||
// so slower explicit bit twiddling is generated. Still should generally
|
||||
// be faster than a loop.
|
||||
unsigned int Count() const {
|
||||
return CountSetBits(m_val);
|
||||
}
|
||||
|
||||
Iterator begin() const {
|
||||
return Iterator(m_val);
|
||||
}
|
||||
Iterator end() const {
|
||||
return Iterator(0);
|
||||
}
|
||||
|
||||
IntTy m_val;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
typedef Common::BitSet<u8> BitSet8;
|
||||
typedef Common::BitSet<u16> BitSet16;
|
||||
typedef Common::BitSet<u32> BitSet32;
|
||||
typedef Common::BitSet<u64> BitSet64;
|
||||
@@ -4,18 +4,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
|
||||
namespace MathUtil {
|
||||
|
||||
static constexpr float PI = 3.14159265f;
|
||||
|
||||
inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1,
|
||||
unsigned length1) {
|
||||
return (std::max(start0, start1) < std::min(start0 + length0, start1 + length1));
|
||||
}
|
||||
constexpr float PI = 3.14159265f;
|
||||
|
||||
template <class T>
|
||||
struct Rectangle {
|
||||
@@ -24,16 +18,16 @@ struct Rectangle {
|
||||
T right{};
|
||||
T bottom{};
|
||||
|
||||
Rectangle() = default;
|
||||
constexpr Rectangle() = default;
|
||||
|
||||
Rectangle(T left, T top, T right, T bottom)
|
||||
constexpr Rectangle(T left, T top, T right, T bottom)
|
||||
: left(left), top(top), right(right), bottom(bottom) {}
|
||||
|
||||
T GetWidth() const {
|
||||
return std::abs(static_cast<typename std::make_signed<T>::type>(right - left));
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
|
||||
}
|
||||
T GetHeight() const {
|
||||
return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top));
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
|
||||
}
|
||||
Rectangle<T> TranslateX(const T x) const {
|
||||
return Rectangle{left + x, top, right + x, bottom};
|
||||
|
||||
@@ -214,6 +214,15 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t
|
||||
return std::string(buffer, len);
|
||||
}
|
||||
|
||||
std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
|
||||
std::size_t max_len) {
|
||||
std::size_t len = 0;
|
||||
while (len < max_len && buffer[len] != '\0')
|
||||
++len;
|
||||
|
||||
return std::u16string(buffer.begin(), buffer.begin() + len);
|
||||
}
|
||||
|
||||
const char* TrimSourcePath(const char* path, const char* root) {
|
||||
const char* p = path;
|
||||
|
||||
|
||||
@@ -66,6 +66,14 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
|
||||
*/
|
||||
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len);
|
||||
|
||||
/**
|
||||
* Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||
* null-terminated, then the string ends at the greatest multiple of two less then or equal to
|
||||
* max_len_bytes.
|
||||
*/
|
||||
std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
|
||||
std::size_t max_len);
|
||||
|
||||
/**
|
||||
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
|
||||
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
|
||||
|
||||
@@ -25,23 +25,6 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
int CurrentThreadId() {
|
||||
#ifdef _MSC_VER
|
||||
return GetCurrentThreadId();
|
||||
#elif defined __APPLE__
|
||||
return mach_thread_self();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Supporting functions
|
||||
void SleepCurrentThread(int ms) {
|
||||
Sleep(ms);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) {
|
||||
@@ -62,7 +45,7 @@ void SwitchCurrentThread() {
|
||||
|
||||
// This is implemented much nicer in upcoming msvc++, see:
|
||||
// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx
|
||||
void SetCurrentThreadName(const char* szThreadName) {
|
||||
void SetCurrentThreadName(const char* name) {
|
||||
static const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||
|
||||
#pragma pack(push, 8)
|
||||
@@ -75,7 +58,7 @@ void SetCurrentThreadName(const char* szThreadName) {
|
||||
#pragma pack(pop)
|
||||
|
||||
info.dwType = 0x1000;
|
||||
info.szName = szThreadName;
|
||||
info.szName = name;
|
||||
info.dwThreadID = -1; // dwThreadID;
|
||||
info.dwFlags = 0;
|
||||
|
||||
@@ -107,10 +90,6 @@ void SetCurrentThreadAffinity(u32 mask) {
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
void SleepCurrentThread(int ms) {
|
||||
usleep(1000 * ms);
|
||||
}
|
||||
|
||||
void SwitchCurrentThread() {
|
||||
usleep(1000 * 1);
|
||||
}
|
||||
@@ -118,15 +97,15 @@ void SwitchCurrentThread() {
|
||||
|
||||
// MinGW with the POSIX threading model does not support pthread_setname_np
|
||||
#if !defined(_WIN32) || defined(_MSC_VER)
|
||||
void SetCurrentThreadName(const char* szThreadName) {
|
||||
void SetCurrentThreadName(const char* name) {
|
||||
#ifdef __APPLE__
|
||||
pthread_setname_np(szThreadName);
|
||||
pthread_setname_np(name);
|
||||
#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
pthread_set_name_np(pthread_self(), szThreadName);
|
||||
pthread_set_name_np(pthread_self(), name);
|
||||
#elif defined(__NetBSD__)
|
||||
pthread_setname_np(pthread_self(), "%s", (void*)szThreadName);
|
||||
pthread_setname_np(pthread_self(), "%s", (void*)name);
|
||||
#else
|
||||
pthread_setname_np(pthread_self(), szThreadName);
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -13,15 +13,8 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
int CurrentThreadId();
|
||||
|
||||
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
|
||||
void SetCurrentThreadAffinity(u32 mask);
|
||||
|
||||
class Event {
|
||||
public:
|
||||
Event() : is_set(false) {}
|
||||
|
||||
void Set() {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
if (!is_set) {
|
||||
@@ -53,14 +46,14 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_set;
|
||||
bool is_set = false;
|
||||
std::condition_variable condvar;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
class Barrier {
|
||||
public:
|
||||
explicit Barrier(std::size_t count_) : count(count_), waiting(0), generation(0) {}
|
||||
explicit Barrier(std::size_t count_) : count(count_) {}
|
||||
|
||||
/// Blocks until all "count" threads have called Sync()
|
||||
void Sync() {
|
||||
@@ -80,12 +73,13 @@ public:
|
||||
private:
|
||||
std::condition_variable condvar;
|
||||
std::mutex mutex;
|
||||
const std::size_t count;
|
||||
std::size_t waiting;
|
||||
std::size_t generation; // Incremented once each time the barrier is used
|
||||
std::size_t count;
|
||||
std::size_t waiting = 0;
|
||||
std::size_t generation = 0; // Incremented once each time the barrier is used
|
||||
};
|
||||
|
||||
void SleepCurrentThread(int ms);
|
||||
void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
|
||||
void SetCurrentThreadAffinity(u32 mask);
|
||||
void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
|
||||
void SetCurrentThreadName(const char* name);
|
||||
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <initializer_list>
|
||||
#include <xbyak.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_set.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
inline int RegToIndex(const Xbyak::Reg& reg) {
|
||||
using Kind = Xbyak::Reg::Kind;
|
||||
ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
|
||||
"RegSet only support GPRs and XMM registers.");
|
||||
ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15.");
|
||||
return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16);
|
||||
}
|
||||
|
||||
inline Xbyak::Reg64 IndexToReg64(int reg_index) {
|
||||
ASSERT(reg_index < 16);
|
||||
return Xbyak::Reg64(reg_index);
|
||||
}
|
||||
|
||||
inline Xbyak::Xmm IndexToXmm(int reg_index) {
|
||||
ASSERT(reg_index >= 16 && reg_index < 32);
|
||||
return Xbyak::Xmm(reg_index - 16);
|
||||
}
|
||||
|
||||
inline Xbyak::Reg IndexToReg(int reg_index) {
|
||||
if (reg_index < 16) {
|
||||
return IndexToReg64(reg_index);
|
||||
} else {
|
||||
return IndexToXmm(reg_index);
|
||||
}
|
||||
}
|
||||
|
||||
inline BitSet32 BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
|
||||
BitSet32 bits;
|
||||
for (const Xbyak::Reg& reg : regs) {
|
||||
bits[RegToIndex(reg)] = true;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
const BitSet32 ABI_ALL_GPRS(0x0000FFFF);
|
||||
const BitSet32 ABI_ALL_XMMS(0xFFFF0000);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// Microsoft x64 ABI
|
||||
const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
|
||||
const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
|
||||
const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
|
||||
const Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
|
||||
const Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
|
||||
|
||||
const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rcx,
|
||||
Xbyak::util::rdx,
|
||||
Xbyak::util::r8,
|
||||
Xbyak::util::r9,
|
||||
Xbyak::util::r10,
|
||||
Xbyak::util::r11,
|
||||
// XMMs
|
||||
Xbyak::util::xmm0,
|
||||
Xbyak::util::xmm1,
|
||||
Xbyak::util::xmm2,
|
||||
Xbyak::util::xmm3,
|
||||
Xbyak::util::xmm4,
|
||||
Xbyak::util::xmm5,
|
||||
});
|
||||
|
||||
const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rbx,
|
||||
Xbyak::util::rsi,
|
||||
Xbyak::util::rdi,
|
||||
Xbyak::util::rbp,
|
||||
Xbyak::util::r12,
|
||||
Xbyak::util::r13,
|
||||
Xbyak::util::r14,
|
||||
Xbyak::util::r15,
|
||||
// XMMs
|
||||
Xbyak::util::xmm6,
|
||||
Xbyak::util::xmm7,
|
||||
Xbyak::util::xmm8,
|
||||
Xbyak::util::xmm9,
|
||||
Xbyak::util::xmm10,
|
||||
Xbyak::util::xmm11,
|
||||
Xbyak::util::xmm12,
|
||||
Xbyak::util::xmm13,
|
||||
Xbyak::util::xmm14,
|
||||
Xbyak::util::xmm15,
|
||||
});
|
||||
|
||||
constexpr std::size_t ABI_SHADOW_SPACE = 0x20;
|
||||
|
||||
#else
|
||||
|
||||
// System V x86-64 ABI
|
||||
const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
|
||||
const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
|
||||
const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
|
||||
const Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
|
||||
const Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
|
||||
|
||||
const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rcx,
|
||||
Xbyak::util::rdx,
|
||||
Xbyak::util::rdi,
|
||||
Xbyak::util::rsi,
|
||||
Xbyak::util::r8,
|
||||
Xbyak::util::r9,
|
||||
Xbyak::util::r10,
|
||||
Xbyak::util::r11,
|
||||
// XMMs
|
||||
Xbyak::util::xmm0,
|
||||
Xbyak::util::xmm1,
|
||||
Xbyak::util::xmm2,
|
||||
Xbyak::util::xmm3,
|
||||
Xbyak::util::xmm4,
|
||||
Xbyak::util::xmm5,
|
||||
Xbyak::util::xmm6,
|
||||
Xbyak::util::xmm7,
|
||||
Xbyak::util::xmm8,
|
||||
Xbyak::util::xmm9,
|
||||
Xbyak::util::xmm10,
|
||||
Xbyak::util::xmm11,
|
||||
Xbyak::util::xmm12,
|
||||
Xbyak::util::xmm13,
|
||||
Xbyak::util::xmm14,
|
||||
Xbyak::util::xmm15,
|
||||
});
|
||||
|
||||
const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rbx,
|
||||
Xbyak::util::rbp,
|
||||
Xbyak::util::r12,
|
||||
Xbyak::util::r13,
|
||||
Xbyak::util::r14,
|
||||
Xbyak::util::r15,
|
||||
});
|
||||
|
||||
constexpr std::size_t ABI_SHADOW_SPACE = 0;
|
||||
|
||||
#endif
|
||||
|
||||
inline void ABI_CalculateFrameSize(BitSet32 regs, std::size_t rsp_alignment,
|
||||
std::size_t needed_frame_size, s32* out_subtraction,
|
||||
s32* out_xmm_offset) {
|
||||
int count = (regs & ABI_ALL_GPRS).Count();
|
||||
rsp_alignment -= count * 8;
|
||||
std::size_t subtraction = 0;
|
||||
int xmm_count = (regs & ABI_ALL_XMMS).Count();
|
||||
if (xmm_count) {
|
||||
// If we have any XMMs to save, we must align the stack here.
|
||||
subtraction = rsp_alignment & 0xF;
|
||||
}
|
||||
subtraction += 0x10 * xmm_count;
|
||||
std::size_t xmm_base_subtraction = subtraction;
|
||||
subtraction += needed_frame_size;
|
||||
subtraction += ABI_SHADOW_SPACE;
|
||||
// Final alignment.
|
||||
rsp_alignment -= subtraction;
|
||||
subtraction += rsp_alignment & 0xF;
|
||||
|
||||
*out_subtraction = (s32)subtraction;
|
||||
*out_xmm_offset = (s32)(subtraction - xmm_base_subtraction);
|
||||
}
|
||||
|
||||
inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
|
||||
std::size_t rsp_alignment,
|
||||
std::size_t needed_frame_size = 0) {
|
||||
s32 subtraction, xmm_offset;
|
||||
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
|
||||
|
||||
for (int reg_index : (regs & ABI_ALL_GPRS)) {
|
||||
code.push(IndexToReg64(reg_index));
|
||||
}
|
||||
|
||||
if (subtraction != 0) {
|
||||
code.sub(code.rsp, subtraction);
|
||||
}
|
||||
|
||||
for (int reg_index : (regs & ABI_ALL_XMMS)) {
|
||||
code.movaps(code.xword[code.rsp + xmm_offset], IndexToXmm(reg_index));
|
||||
xmm_offset += 0x10;
|
||||
}
|
||||
|
||||
return ABI_SHADOW_SPACE;
|
||||
}
|
||||
|
||||
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs,
|
||||
std::size_t rsp_alignment,
|
||||
std::size_t needed_frame_size = 0) {
|
||||
s32 subtraction, xmm_offset;
|
||||
ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset);
|
||||
|
||||
for (int reg_index : (regs & ABI_ALL_XMMS)) {
|
||||
code.movaps(IndexToXmm(reg_index), code.xword[code.rsp + xmm_offset]);
|
||||
xmm_offset += 0x10;
|
||||
}
|
||||
|
||||
if (subtraction != 0) {
|
||||
code.add(code.rsp, subtraction);
|
||||
}
|
||||
|
||||
// GPRs need to be popped in reverse order
|
||||
for (int reg_index = 15; reg_index >= 0; reg_index--) {
|
||||
if (regs[reg_index]) {
|
||||
code.pop(IndexToReg64(reg_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <xbyak.h>
|
||||
#include "common/x64/xbyak_abi.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
// Constants for use with cmpps/cmpss
|
||||
enum {
|
||||
CMP_EQ = 0,
|
||||
CMP_LT = 1,
|
||||
CMP_LE = 2,
|
||||
CMP_UNORD = 3,
|
||||
CMP_NEQ = 4,
|
||||
CMP_NLT = 5,
|
||||
CMP_NLE = 6,
|
||||
CMP_ORD = 7,
|
||||
};
|
||||
|
||||
inline bool IsWithin2G(uintptr_t ref, uintptr_t target) {
|
||||
u64 distance = target - (ref + 5);
|
||||
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
|
||||
}
|
||||
|
||||
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
|
||||
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
|
||||
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
|
||||
std::size_t addr = reinterpret_cast<std::size_t>(f);
|
||||
if (IsWithin2G(code, addr)) {
|
||||
code.call(f);
|
||||
} else {
|
||||
// ABI_RETURN is a safe temp register to use before a call
|
||||
code.mov(ABI_RETURN, addr);
|
||||
code.call(ABI_RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -12,6 +12,8 @@ add_library(core STATIC
|
||||
core_timing.h
|
||||
core_timing_util.cpp
|
||||
core_timing_util.h
|
||||
cpu_core_manager.cpp
|
||||
cpu_core_manager.h
|
||||
crypto/aes_util.cpp
|
||||
crypto/aes_util.h
|
||||
crypto/encryption_layer.cpp
|
||||
@@ -77,6 +79,8 @@ add_library(core STATIC
|
||||
file_sys/vfs_vector.h
|
||||
file_sys/xts_archive.cpp
|
||||
file_sys/xts_archive.h
|
||||
frontend/applets/software_keyboard.cpp
|
||||
frontend/applets/software_keyboard.h
|
||||
frontend/emu_window.cpp
|
||||
frontend/emu_window.h
|
||||
frontend/framebuffer_layout.cpp
|
||||
@@ -150,6 +154,12 @@ add_library(core STATIC
|
||||
hle/service/am/applet_ae.h
|
||||
hle/service/am/applet_oe.cpp
|
||||
hle/service/am/applet_oe.h
|
||||
hle/service/am/applets/applets.cpp
|
||||
hle/service/am/applets/applets.h
|
||||
hle/service/am/applets/software_keyboard.cpp
|
||||
hle/service/am/applets/software_keyboard.h
|
||||
hle/service/am/applets/stub_applet.cpp
|
||||
hle/service/am/applets/stub_applet.h
|
||||
hle/service/am/idle.cpp
|
||||
hle/service/am/idle.h
|
||||
hle/service/am/omm.cpp
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_core_manager.h"
|
||||
#include "core/file_sys/mode.h"
|
||||
#include "core/file_sys/vfs_concat.h"
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
@@ -23,12 +24,13 @@
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/service/am/applets/software_keyboard.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "frontend/applets/software_keyboard.h"
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
@@ -69,64 +71,22 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
|
||||
return vfs->OpenFile(path, FileSys::Mode::Read);
|
||||
}
|
||||
|
||||
/// Runs a CPU core while the system is powered on
|
||||
void RunCpuCore(Cpu& cpu_state) {
|
||||
while (Core::System::GetInstance().IsPoweredOn()) {
|
||||
cpu_state.RunLoop(true);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
struct System::Impl {
|
||||
Cpu& CurrentCpuCore() {
|
||||
if (Settings::values.use_multi_core) {
|
||||
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
|
||||
ASSERT(search != thread_to_cpu.end());
|
||||
ASSERT(search->second);
|
||||
return *search->second;
|
||||
}
|
||||
|
||||
// Otherwise, use single-threaded mode active_core variable
|
||||
return *cpu_cores[active_core];
|
||||
return cpu_core_manager.GetCurrentCore();
|
||||
}
|
||||
|
||||
ResultStatus RunLoop(bool tight_loop) {
|
||||
status = ResultStatus::Success;
|
||||
|
||||
// Update thread_to_cpu in case Core 0 is run from a different host thread
|
||||
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
|
||||
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::HandlePacket();
|
||||
|
||||
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
|
||||
// execute. Otherwise, get out of the loop function.
|
||||
if (GDBStub::GetCpuHaltFlag()) {
|
||||
if (GDBStub::GetCpuStepFlag()) {
|
||||
tight_loop = false;
|
||||
} else {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
|
||||
cpu_cores[active_core]->RunLoop(tight_loop);
|
||||
if (Settings::values.use_multi_core) {
|
||||
// Cores 1-3 are run on other threads in this mode
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::SetCpuStepFlag(false);
|
||||
}
|
||||
cpu_core_manager.RunLoop(tight_loop);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
ResultStatus Init(Frontend::EmuWindow& emu_window) {
|
||||
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||
|
||||
CoreTiming::Init();
|
||||
@@ -136,15 +96,13 @@ struct System::Impl {
|
||||
if (virtual_filesystem == nullptr)
|
||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||
|
||||
/// Create default implementations of applets if one is not provided.
|
||||
if (software_keyboard == nullptr)
|
||||
software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
|
||||
|
||||
auto main_process = Kernel::Process::Create(kernel, "main");
|
||||
kernel.MakeCurrentProcess(main_process.get());
|
||||
|
||||
cpu_barrier = std::make_unique<CpuBarrier>();
|
||||
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
|
||||
for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
|
||||
cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
|
||||
}
|
||||
|
||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
||||
|
||||
@@ -158,17 +116,8 @@ struct System::Impl {
|
||||
|
||||
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
|
||||
|
||||
// Create threads for CPU cores 1-3, and build thread_to_cpu map
|
||||
// CPU core 0 is run on the main thread
|
||||
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
|
||||
if (Settings::values.use_multi_core) {
|
||||
for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
|
||||
cpu_core_threads[index] =
|
||||
std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
|
||||
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
|
||||
}
|
||||
}
|
||||
|
||||
cpu_core_manager.Initialize(system);
|
||||
is_powered_on = true;
|
||||
LOG_DEBUG(Core, "Initialized OK");
|
||||
|
||||
// Reset counters and set time origin to current frame
|
||||
@@ -178,7 +127,8 @@ struct System::Impl {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
|
||||
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
|
||||
const std::string& filepath) {
|
||||
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
|
||||
|
||||
if (!app_loader) {
|
||||
@@ -195,7 +145,7 @@ struct System::Impl {
|
||||
return ResultStatus::ErrorSystemMode;
|
||||
}
|
||||
|
||||
ResultStatus init_result{Init(emu_window)};
|
||||
ResultStatus init_result{Init(system, emu_window)};
|
||||
if (init_result != ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
|
||||
static_cast<int>(init_result));
|
||||
@@ -225,6 +175,8 @@ struct System::Impl {
|
||||
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
|
||||
perf_results.frametime * 1000.0);
|
||||
|
||||
is_powered_on = false;
|
||||
|
||||
// Shutdown emulation session
|
||||
renderer.reset();
|
||||
GDBStub::Shutdown();
|
||||
@@ -234,19 +186,7 @@ struct System::Impl {
|
||||
gpu_core.reset();
|
||||
|
||||
// Close all CPU/threading state
|
||||
cpu_barrier->NotifyEnd();
|
||||
if (Settings::values.use_multi_core) {
|
||||
for (auto& thread : cpu_core_threads) {
|
||||
thread->join();
|
||||
thread.reset();
|
||||
}
|
||||
}
|
||||
thread_to_cpu.clear();
|
||||
for (auto& cpu_core : cpu_cores) {
|
||||
cpu_core.reset();
|
||||
}
|
||||
cpu_exclusive_monitor.reset();
|
||||
cpu_barrier.reset();
|
||||
cpu_core_manager.Shutdown();
|
||||
|
||||
// Shutdown kernel and core timing
|
||||
kernel.Shutdown();
|
||||
@@ -283,11 +223,11 @@ struct System::Impl {
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::shared_ptr<Tegra::DebugContext> debug_context;
|
||||
std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
|
||||
std::unique_ptr<CpuBarrier> cpu_barrier;
|
||||
std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
|
||||
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
|
||||
std::size_t active_core{}; ///< Active core, only used in single thread mode
|
||||
CpuCoreManager cpu_core_manager;
|
||||
bool is_powered_on = false;
|
||||
|
||||
/// Frontend applets
|
||||
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
|
||||
|
||||
/// Service manager
|
||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||
@@ -298,9 +238,6 @@ struct System::Impl {
|
||||
ResultStatus status = ResultStatus::Success;
|
||||
std::string status_details = "";
|
||||
|
||||
/// Map of guest threads to CPU cores
|
||||
std::map<std::thread::id, Cpu*> thread_to_cpu;
|
||||
|
||||
Core::PerfStats perf_stats;
|
||||
Core::FrameLimiter frame_limiter;
|
||||
};
|
||||
@@ -325,17 +262,15 @@ System::ResultStatus System::SingleStep() {
|
||||
}
|
||||
|
||||
void System::InvalidateCpuInstructionCaches() {
|
||||
for (auto& cpu : impl->cpu_cores) {
|
||||
cpu->ArmInterface().ClearInstructionCache();
|
||||
}
|
||||
impl->cpu_core_manager.InvalidateAllInstructionCaches();
|
||||
}
|
||||
|
||||
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
|
||||
return impl->Load(emu_window, filepath);
|
||||
return impl->Load(*this, emu_window, filepath);
|
||||
}
|
||||
|
||||
bool System::IsPoweredOn() const {
|
||||
return impl->cpu_barrier && impl->cpu_barrier->IsAlive();
|
||||
return impl->is_powered_on;
|
||||
}
|
||||
|
||||
void System::PrepareReschedule() {
|
||||
@@ -399,21 +334,20 @@ const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
|
||||
}
|
||||
|
||||
Cpu& System::CpuCore(std::size_t core_index) {
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return *impl->cpu_cores[core_index];
|
||||
return impl->cpu_core_manager.GetCore(core_index);
|
||||
}
|
||||
|
||||
const Cpu& System::CpuCore(std::size_t core_index) const {
|
||||
ASSERT(core_index < NUM_CPU_CORES);
|
||||
return *impl->cpu_cores[core_index];
|
||||
return impl->cpu_core_manager.GetCore(core_index);
|
||||
}
|
||||
|
||||
ExclusiveMonitor& System::Monitor() {
|
||||
return *impl->cpu_exclusive_monitor;
|
||||
return impl->cpu_core_manager.GetExclusiveMonitor();
|
||||
}
|
||||
|
||||
const ExclusiveMonitor& System::Monitor() const {
|
||||
return *impl->cpu_exclusive_monitor;
|
||||
return impl->cpu_core_manager.GetExclusiveMonitor();
|
||||
}
|
||||
|
||||
Tegra::GPU& System::GPU() {
|
||||
@@ -488,8 +422,16 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
|
||||
return impl->virtual_filesystem;
|
||||
}
|
||||
|
||||
void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) {
|
||||
impl->software_keyboard = std::move(applet);
|
||||
}
|
||||
|
||||
const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
|
||||
return *impl->software_keyboard;
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
|
||||
return impl->Init(emu_window);
|
||||
return impl->Init(*this, emu_window);
|
||||
}
|
||||
|
||||
void System::Shutdown() {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
class SoftwareKeyboardApplet;
|
||||
} // namespace Core::Frontend
|
||||
|
||||
namespace FileSys {
|
||||
@@ -236,6 +237,10 @@ public:
|
||||
|
||||
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
|
||||
|
||||
void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet);
|
||||
|
||||
const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
|
||||
|
||||
private:
|
||||
System();
|
||||
|
||||
|
||||
142
src/core/cpu_core_manager.cpp
Normal file
142
src/core/cpu_core_manager.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_cpu.h"
|
||||
#include "core/cpu_core_manager.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Core {
|
||||
namespace {
|
||||
void RunCpuCore(const System& system, Cpu& cpu_state) {
|
||||
while (system.IsPoweredOn()) {
|
||||
cpu_state.RunLoop(true);
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
CpuCoreManager::CpuCoreManager() = default;
|
||||
CpuCoreManager::~CpuCoreManager() = default;
|
||||
|
||||
void CpuCoreManager::Initialize(System& system) {
|
||||
barrier = std::make_unique<CpuBarrier>();
|
||||
exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
|
||||
|
||||
for (std::size_t index = 0; index < cores.size(); ++index) {
|
||||
cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index);
|
||||
}
|
||||
|
||||
// Create threads for CPU cores 1-3, and build thread_to_cpu map
|
||||
// CPU core 0 is run on the main thread
|
||||
thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
|
||||
if (!Settings::values.use_multi_core) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < core_threads.size(); ++index) {
|
||||
core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system),
|
||||
std::ref(*cores[index + 1]));
|
||||
thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuCoreManager::Shutdown() {
|
||||
barrier->NotifyEnd();
|
||||
if (Settings::values.use_multi_core) {
|
||||
for (auto& thread : core_threads) {
|
||||
thread->join();
|
||||
thread.reset();
|
||||
}
|
||||
}
|
||||
|
||||
thread_to_cpu.clear();
|
||||
for (auto& cpu_core : cores) {
|
||||
cpu_core.reset();
|
||||
}
|
||||
|
||||
exclusive_monitor.reset();
|
||||
barrier.reset();
|
||||
}
|
||||
|
||||
Cpu& CpuCoreManager::GetCore(std::size_t index) {
|
||||
return *cores.at(index);
|
||||
}
|
||||
|
||||
const Cpu& CpuCoreManager::GetCore(std::size_t index) const {
|
||||
return *cores.at(index);
|
||||
}
|
||||
|
||||
ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() {
|
||||
return *exclusive_monitor;
|
||||
}
|
||||
|
||||
const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const {
|
||||
return *exclusive_monitor;
|
||||
}
|
||||
|
||||
Cpu& CpuCoreManager::GetCurrentCore() {
|
||||
if (Settings::values.use_multi_core) {
|
||||
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
|
||||
ASSERT(search != thread_to_cpu.end());
|
||||
ASSERT(search->second);
|
||||
return *search->second;
|
||||
}
|
||||
|
||||
// Otherwise, use single-threaded mode active_core variable
|
||||
return *cores[active_core];
|
||||
}
|
||||
|
||||
const Cpu& CpuCoreManager::GetCurrentCore() const {
|
||||
if (Settings::values.use_multi_core) {
|
||||
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
|
||||
ASSERT(search != thread_to_cpu.end());
|
||||
ASSERT(search->second);
|
||||
return *search->second;
|
||||
}
|
||||
|
||||
// Otherwise, use single-threaded mode active_core variable
|
||||
return *cores[active_core];
|
||||
}
|
||||
|
||||
void CpuCoreManager::RunLoop(bool tight_loop) {
|
||||
// Update thread_to_cpu in case Core 0 is run from a different host thread
|
||||
thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
|
||||
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::HandlePacket();
|
||||
|
||||
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
|
||||
// execute. Otherwise, get out of the loop function.
|
||||
if (GDBStub::GetCpuHaltFlag()) {
|
||||
if (GDBStub::GetCpuStepFlag()) {
|
||||
tight_loop = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
|
||||
cores[active_core]->RunLoop(tight_loop);
|
||||
if (Settings::values.use_multi_core) {
|
||||
// Cores 1-3 are run on other threads in this mode
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::SetCpuStepFlag(false);
|
||||
}
|
||||
}
|
||||
|
||||
void CpuCoreManager::InvalidateAllInstructionCaches() {
|
||||
for (auto& cpu : cores) {
|
||||
cpu->ArmInterface().ClearInstructionCache();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
59
src/core/cpu_core_manager.h
Normal file
59
src/core/cpu_core_manager.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
namespace Core {
|
||||
|
||||
class Cpu;
|
||||
class CpuBarrier;
|
||||
class ExclusiveMonitor;
|
||||
class System;
|
||||
|
||||
class CpuCoreManager {
|
||||
public:
|
||||
CpuCoreManager();
|
||||
CpuCoreManager(const CpuCoreManager&) = delete;
|
||||
CpuCoreManager(CpuCoreManager&&) = delete;
|
||||
|
||||
~CpuCoreManager();
|
||||
|
||||
CpuCoreManager& operator=(const CpuCoreManager&) = delete;
|
||||
CpuCoreManager& operator=(CpuCoreManager&&) = delete;
|
||||
|
||||
void Initialize(System& system);
|
||||
void Shutdown();
|
||||
|
||||
Cpu& GetCore(std::size_t index);
|
||||
const Cpu& GetCore(std::size_t index) const;
|
||||
|
||||
Cpu& GetCurrentCore();
|
||||
const Cpu& GetCurrentCore() const;
|
||||
|
||||
ExclusiveMonitor& GetExclusiveMonitor();
|
||||
const ExclusiveMonitor& GetExclusiveMonitor() const;
|
||||
|
||||
void RunLoop(bool tight_loop);
|
||||
|
||||
void InvalidateAllInstructionCaches();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t NUM_CPU_CORES = 4;
|
||||
|
||||
std::unique_ptr<ExclusiveMonitor> exclusive_monitor;
|
||||
std::unique_ptr<CpuBarrier> barrier;
|
||||
std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores;
|
||||
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads;
|
||||
std::size_t active_core{}; ///< Active core, only used in single thread mode
|
||||
|
||||
/// Map of guest threads to CPU cores
|
||||
std::map<std::thread::id, Cpu*> thread_to_cpu;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
@@ -32,7 +32,18 @@ enum class GamecardSize : u8 {
|
||||
};
|
||||
|
||||
struct GamecardInfo {
|
||||
std::array<u8, 0x70> data;
|
||||
u64_le firmware_version;
|
||||
u32_le access_control_flags;
|
||||
u32_le read_wait_time1;
|
||||
u32_le read_wait_time2;
|
||||
u32_le write_wait_time1;
|
||||
u32_le write_wait_time2;
|
||||
u32_le firmware_mode;
|
||||
u32_le cup_version;
|
||||
std::array<u8, 4> reserved1;
|
||||
u64_le update_partition_hash;
|
||||
u64_le cup_id;
|
||||
std::array<u8, 0x38> reserved2;
|
||||
};
|
||||
static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size.");
|
||||
|
||||
|
||||
@@ -26,6 +26,11 @@ namespace FileSys {
|
||||
constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
|
||||
constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
|
||||
|
||||
constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
|
||||
"main", "main.npdm", "rtld", "sdk", "subsdk0", "subsdk1", "subsdk2",
|
||||
"subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
|
||||
};
|
||||
|
||||
struct NSOBuildHeader {
|
||||
u32_le magic;
|
||||
INSERT_PADDING_BYTES(0x3C);
|
||||
@@ -57,6 +62,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
if (exefs == nullptr)
|
||||
return exefs;
|
||||
|
||||
if (Settings::values.dump_exefs) {
|
||||
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
|
||||
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
|
||||
VfsRawCopyD(exefs, exefs_dir);
|
||||
}
|
||||
}
|
||||
|
||||
const auto installed = Service::FileSystem::GetUnionContents();
|
||||
|
||||
// Game Updates
|
||||
@@ -70,6 +84,30 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
exefs = update->GetExeFS();
|
||||
}
|
||||
|
||||
// LayeredExeFS
|
||||
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
if (load_dir != nullptr && load_dir->GetSize() > 0) {
|
||||
auto patch_dirs = load_dir->GetSubdirectories();
|
||||
std::sort(
|
||||
patch_dirs.begin(), patch_dirs.end(),
|
||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
||||
|
||||
std::vector<VirtualDir> layers;
|
||||
layers.reserve(patch_dirs.size() + 1);
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
auto exefs_dir = subdir->GetSubdirectory("exefs");
|
||||
if (exefs_dir != nullptr)
|
||||
layers.push_back(std::move(exefs_dir));
|
||||
}
|
||||
layers.push_back(exefs);
|
||||
|
||||
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
|
||||
if (layered != nullptr) {
|
||||
LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
|
||||
exefs = std::move(layered);
|
||||
}
|
||||
}
|
||||
|
||||
return exefs;
|
||||
}
|
||||
|
||||
@@ -314,18 +352,25 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
|
||||
if (IsDirValidAndNonEmpty(exefs_dir)) {
|
||||
bool ips = false;
|
||||
bool ipswitch = false;
|
||||
bool layeredfs = false;
|
||||
|
||||
for (const auto& file : exefs_dir->GetFiles()) {
|
||||
if (file->GetExtension() == "ips")
|
||||
if (file->GetExtension() == "ips") {
|
||||
ips = true;
|
||||
else if (file->GetExtension() == "pchtxt")
|
||||
} else if (file->GetExtension() == "pchtxt") {
|
||||
ipswitch = true;
|
||||
} else if (std::find(EXEFS_FILE_NAMES.begin(), EXEFS_FILE_NAMES.end(),
|
||||
file->GetName()) != EXEFS_FILE_NAMES.end()) {
|
||||
layeredfs = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ips)
|
||||
AppendCommaIfNotEmpty(types, "IPS");
|
||||
if (ipswitch)
|
||||
AppendCommaIfNotEmpty(types, "IPSwitch");
|
||||
if (layeredfs)
|
||||
AppendCommaIfNotEmpty(types, "LayeredExeFS");
|
||||
}
|
||||
if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
|
||||
AppendCommaIfNotEmpty(types, "LayeredFS");
|
||||
|
||||
29
src/core/frontend/applets/software_keyboard.cpp
Normal file
29
src/core/frontend/applets/software_keyboard.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default;
|
||||
|
||||
void DefaultSoftwareKeyboardApplet::RequestText(
|
||||
std::function<void(std::optional<std::u16string>)> out,
|
||||
SoftwareKeyboardParameters parameters) const {
|
||||
if (parameters.initial_text.empty())
|
||||
out(u"yuzu");
|
||||
|
||||
out(parameters.initial_text);
|
||||
}
|
||||
|
||||
void DefaultSoftwareKeyboardApplet::SendTextCheckDialog(
|
||||
std::u16string error_message, std::function<void()> finished_check) const {
|
||||
LOG_WARNING(Service_AM,
|
||||
"(STUBBED) called - Default fallback software keyboard does not support text "
|
||||
"check! (error_message={})",
|
||||
Common::UTF16ToUTF8(error_message));
|
||||
finished_check();
|
||||
}
|
||||
} // namespace Core::Frontend
|
||||
54
src/core/frontend/applets/software_keyboard.h
Normal file
54
src/core/frontend/applets/software_keyboard.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
struct SoftwareKeyboardParameters {
|
||||
std::u16string submit_text;
|
||||
std::u16string header_text;
|
||||
std::u16string sub_text;
|
||||
std::u16string guide_text;
|
||||
std::u16string initial_text;
|
||||
std::size_t max_length;
|
||||
bool password;
|
||||
bool cursor_at_beginning;
|
||||
|
||||
union {
|
||||
u8 value;
|
||||
|
||||
BitField<1, 1, u8> disable_space;
|
||||
BitField<2, 1, u8> disable_address;
|
||||
BitField<3, 1, u8> disable_percent;
|
||||
BitField<4, 1, u8> disable_slash;
|
||||
BitField<6, 1, u8> disable_number;
|
||||
BitField<7, 1, u8> disable_download_code;
|
||||
};
|
||||
};
|
||||
|
||||
class SoftwareKeyboardApplet {
|
||||
public:
|
||||
virtual ~SoftwareKeyboardApplet();
|
||||
|
||||
virtual void RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
SoftwareKeyboardParameters parameters) const = 0;
|
||||
virtual void SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const = 0;
|
||||
};
|
||||
|
||||
class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
|
||||
public:
|
||||
void RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
SoftwareKeyboardParameters parameters) const override;
|
||||
void SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const override;
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
@@ -132,4 +132,11 @@ using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>
|
||||
*/
|
||||
using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
|
||||
|
||||
/**
|
||||
* A mouse device is an input device that returns a tuple of two floats and four ints.
|
||||
* The first two floats are X and Y device coordinates of the mouse (from 0-1).
|
||||
* The s32s are the mouse wheel.
|
||||
*/
|
||||
using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>;
|
||||
|
||||
} // namespace Input
|
||||
|
||||
@@ -71,10 +71,6 @@ constexpr u32 PSTATE_REGISTER = 33;
|
||||
constexpr u32 UC_ARM64_REG_Q0 = 34;
|
||||
constexpr u32 FPCR_REGISTER = 66;
|
||||
|
||||
// TODO/WiP - Used while working on support for FPU
|
||||
constexpr u32 TODO_DUMMY_REG_997 = 997;
|
||||
constexpr u32 TODO_DUMMY_REG_998 = 998;
|
||||
|
||||
// For sample XML files see the GDB source /gdb/features
|
||||
// GDB also wants the l character at the start
|
||||
// This XML defines what the registers are for this specific ARM device
|
||||
@@ -260,6 +256,36 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr)
|
||||
}
|
||||
}
|
||||
|
||||
static u128 FpuRead(std::size_t id, Kernel::Thread* thread = nullptr) {
|
||||
if (!thread) {
|
||||
return u128{0};
|
||||
}
|
||||
|
||||
auto& thread_context = thread->GetContext();
|
||||
|
||||
if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
|
||||
return thread_context.vector_registers[id - UC_ARM64_REG_Q0];
|
||||
} else if (id == FPCR_REGISTER) {
|
||||
return u128{thread_context.fpcr, 0};
|
||||
} else {
|
||||
return u128{0};
|
||||
}
|
||||
}
|
||||
|
||||
static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr) {
|
||||
if (!thread) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& thread_context = thread->GetContext();
|
||||
|
||||
if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
|
||||
thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val;
|
||||
} else if (id == FPCR_REGISTER) {
|
||||
thread_context.fpcr = val[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns hex string character into the equivalent byte.
|
||||
*
|
||||
@@ -409,6 +435,27 @@ static u64 GdbHexToLong(const u8* src) {
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a gdb-formatted hex string into a u128.
|
||||
*
|
||||
* @param src Pointer to hex string.
|
||||
*/
|
||||
static u128 GdbHexToU128(const u8* src) {
|
||||
u128 output;
|
||||
|
||||
for (int i = 0; i < 16; i += 2) {
|
||||
output[0] = (output[0] << 4) | HexCharToValue(src[15 - i - 1]);
|
||||
output[0] = (output[0] << 4) | HexCharToValue(src[15 - i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i += 2) {
|
||||
output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i - 1]);
|
||||
output[1] = (output[1] << 4) | HexCharToValue(src[16 + 15 - i]);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// Read a byte from the gdb client.
|
||||
static u8 ReadByte() {
|
||||
u8 c;
|
||||
@@ -599,8 +646,7 @@ static void HandleQuery() {
|
||||
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
|
||||
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
|
||||
for (const auto& thread : threads) {
|
||||
val += fmt::format("{:x}", thread->GetThreadID());
|
||||
val += ",";
|
||||
val += fmt::format("{:x},", thread->GetThreadID());
|
||||
}
|
||||
}
|
||||
val.pop_back();
|
||||
@@ -791,11 +837,15 @@ static void ReadRegister() {
|
||||
} else if (id == PSTATE_REGISTER) {
|
||||
IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread)));
|
||||
} else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
|
||||
LongToGdbHex(reply, RegRead(id, current_thread));
|
||||
u128 r = FpuRead(id, current_thread);
|
||||
LongToGdbHex(reply, r[0]);
|
||||
LongToGdbHex(reply + 16, r[1]);
|
||||
} else if (id == FPCR_REGISTER) {
|
||||
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread));
|
||||
} else {
|
||||
LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread));
|
||||
u128 r = FpuRead(id, current_thread);
|
||||
IntToGdbHex(reply, static_cast<u32>(r[0]));
|
||||
} else if (id == FPCR_REGISTER + 1) {
|
||||
u128 r = FpuRead(id, current_thread);
|
||||
IntToGdbHex(reply, static_cast<u32>(r[0] >> 32));
|
||||
}
|
||||
|
||||
SendReply(reinterpret_cast<char*>(reply));
|
||||
@@ -822,13 +872,18 @@ static void ReadRegisters() {
|
||||
|
||||
bufptr += 8;
|
||||
|
||||
for (u32 reg = UC_ARM64_REG_Q0; reg <= UC_ARM64_REG_Q0 + 31; reg++) {
|
||||
LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));
|
||||
u128 r;
|
||||
|
||||
for (u32 reg = UC_ARM64_REG_Q0; reg < FPCR_REGISTER; reg++) {
|
||||
r = FpuRead(reg, current_thread);
|
||||
LongToGdbHex(bufptr + reg * 32, r[0]);
|
||||
LongToGdbHex(bufptr + reg * 32 + 16, r[1]);
|
||||
}
|
||||
|
||||
bufptr += 32 * 32;
|
||||
|
||||
LongToGdbHex(bufptr, RegRead(TODO_DUMMY_REG_998, current_thread));
|
||||
r = FpuRead(FPCR_REGISTER, current_thread);
|
||||
IntToGdbHex(bufptr, static_cast<u32>(r[0]));
|
||||
|
||||
bufptr += 8;
|
||||
|
||||
@@ -853,14 +908,12 @@ static void WriteRegister() {
|
||||
} else if (id == PSTATE_REGISTER) {
|
||||
RegWrite(id, GdbHexToInt(buffer_ptr), current_thread);
|
||||
} else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
|
||||
RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);
|
||||
FpuWrite(id, GdbHexToU128(buffer_ptr), current_thread);
|
||||
} else if (id == FPCR_REGISTER) {
|
||||
RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread);
|
||||
} else {
|
||||
RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread);
|
||||
} else if (id == FPCR_REGISTER + 1) {
|
||||
}
|
||||
|
||||
// Update Unicorn context skipping scheduler, no running threads at this point
|
||||
// Update ARM context, skipping scheduler - no running threads at this point
|
||||
Core::System::GetInstance()
|
||||
.ArmInterface(current_core)
|
||||
.LoadContext(current_thread->GetContext());
|
||||
@@ -885,13 +938,13 @@ static void WriteRegisters() {
|
||||
} else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) {
|
||||
RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);
|
||||
} else if (reg == FPCR_REGISTER) {
|
||||
RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread);
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
|
||||
} else if (reg == FPCR_REGISTER + 1) {
|
||||
RegWrite(FPCR_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);
|
||||
}
|
||||
}
|
||||
|
||||
// Update Unicorn context skipping scheduler, no running threads at this point
|
||||
// Update ARM context, skipping scheduler - no running threads at this point
|
||||
Core::System::GetInstance()
|
||||
.ArmInterface(current_core)
|
||||
.LoadContext(current_thread->GetContext());
|
||||
@@ -917,12 +970,6 @@ static void ReadMemory() {
|
||||
SendReply("E01");
|
||||
}
|
||||
|
||||
const auto& vm_manager = Core::CurrentProcess()->VMManager();
|
||||
if (addr < vm_manager.GetCodeRegionBaseAddress() ||
|
||||
addr >= vm_manager.GetMapRegionEndAddress()) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(addr)) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
@@ -967,7 +1014,7 @@ void Break(bool is_memory_break) {
|
||||
static void Step() {
|
||||
if (command_length > 1) {
|
||||
RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
|
||||
// Update Unicorn context skipping scheduler, no running threads at this point
|
||||
// Update ARM context, skipping scheduler - no running threads at this point
|
||||
Core::System::GetInstance()
|
||||
.ArmInterface(current_core)
|
||||
.LoadContext(current_thread->GetContext());
|
||||
@@ -1010,7 +1057,7 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
|
||||
breakpoint.addr = addr;
|
||||
breakpoint.len = len;
|
||||
Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
|
||||
static constexpr std::array<u8, 4> btrap{{0x00, 0x7d, 0x20, 0xd4}};
|
||||
static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4};
|
||||
Memory::WriteBlock(addr, btrap.data(), btrap.size());
|
||||
Core::System::GetInstance().InvalidateCpuInstructionCaches();
|
||||
p.insert({addr, breakpoint});
|
||||
@@ -1321,13 +1368,15 @@ void SetCpuStepFlag(bool is_step) {
|
||||
}
|
||||
|
||||
void SendTrap(Kernel::Thread* thread, int trap) {
|
||||
if (send_trap) {
|
||||
if (!halt_loop || current_thread == thread) {
|
||||
current_thread = thread;
|
||||
SendSignal(thread, trap);
|
||||
}
|
||||
halt_loop = true;
|
||||
send_trap = false;
|
||||
if (!send_trap) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!halt_loop || current_thread == thread) {
|
||||
current_thread = thread;
|
||||
SendSignal(thread, trap);
|
||||
}
|
||||
halt_loop = true;
|
||||
send_trap = false;
|
||||
}
|
||||
}; // namespace GDBStub
|
||||
|
||||
@@ -12,12 +12,23 @@
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
constexpr u16 GetSlot(Handle handle) {
|
||||
return handle >> 15;
|
||||
}
|
||||
|
||||
constexpr u16 GetGeneration(Handle handle) {
|
||||
return handle & 0x7FFF;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
HandleTable::HandleTable() {
|
||||
next_generation = 1;
|
||||
Clear();
|
||||
}
|
||||
|
||||
HandleTable::~HandleTable() = default;
|
||||
|
||||
ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
|
||||
DEBUG_ASSERT(obj != nullptr);
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ enum KernelHandle : Handle {
|
||||
class HandleTable final : NonCopyable {
|
||||
public:
|
||||
HandleTable();
|
||||
~HandleTable();
|
||||
|
||||
/**
|
||||
* Allocates a handle for the given object.
|
||||
@@ -89,18 +90,8 @@ public:
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
/**
|
||||
* This is the maximum limit of handles allowed per process in CTR-OS. It can be further
|
||||
* reduced by ExHeader values, but this is not emulated here.
|
||||
*/
|
||||
static const std::size_t MAX_COUNT = 4096;
|
||||
|
||||
static u16 GetSlot(Handle handle) {
|
||||
return handle >> 15;
|
||||
}
|
||||
static u16 GetGeneration(Handle handle) {
|
||||
return handle & 0x7FFF;
|
||||
}
|
||||
/// This is the maximum limit of handles allowed per process in Horizon
|
||||
static constexpr std::size_t MAX_COUNT = 1024;
|
||||
|
||||
/// Stores the Object referenced by the handle or null if the slot is empty.
|
||||
std::array<SharedPtr<Object>, MAX_COUNT> objects;
|
||||
|
||||
@@ -105,7 +105,7 @@ struct KernelCore::Impl {
|
||||
void Initialize(KernelCore& kernel) {
|
||||
Shutdown();
|
||||
|
||||
InitializeResourceLimits(kernel);
|
||||
InitializeSystemResourceLimit(kernel);
|
||||
InitializeThreads();
|
||||
InitializeTimers();
|
||||
}
|
||||
@@ -118,7 +118,7 @@ struct KernelCore::Impl {
|
||||
process_list.clear();
|
||||
current_process = nullptr;
|
||||
|
||||
resource_limits.fill(nullptr);
|
||||
system_resource_limit = nullptr;
|
||||
|
||||
thread_wakeup_callback_handle_table.Clear();
|
||||
thread_wakeup_event_type = nullptr;
|
||||
@@ -129,63 +129,17 @@ struct KernelCore::Impl {
|
||||
named_ports.clear();
|
||||
}
|
||||
|
||||
void InitializeResourceLimits(KernelCore& kernel) {
|
||||
// Create the four resource limits that the system uses
|
||||
// Create the APPLICATION resource limit
|
||||
SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications");
|
||||
resource_limit->max_priority = 0x18;
|
||||
resource_limit->max_commit = 0x4000000;
|
||||
resource_limit->max_threads = 0x20;
|
||||
resource_limit->max_events = 0x20;
|
||||
resource_limit->max_mutexes = 0x20;
|
||||
resource_limit->max_semaphores = 0x8;
|
||||
resource_limit->max_timers = 0x8;
|
||||
resource_limit->max_shared_mems = 0x10;
|
||||
resource_limit->max_address_arbiters = 0x2;
|
||||
resource_limit->max_cpu_time = 0x1E;
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
|
||||
// Creates the default system resource limit
|
||||
void InitializeSystemResourceLimit(KernelCore& kernel) {
|
||||
system_resource_limit = ResourceLimit::Create(kernel, "System");
|
||||
|
||||
// Create the SYS_APPLET resource limit
|
||||
resource_limit = ResourceLimit::Create(kernel, "System Applets");
|
||||
resource_limit->max_priority = 0x4;
|
||||
resource_limit->max_commit = 0x5E00000;
|
||||
resource_limit->max_threads = 0x1D;
|
||||
resource_limit->max_events = 0xB;
|
||||
resource_limit->max_mutexes = 0x8;
|
||||
resource_limit->max_semaphores = 0x4;
|
||||
resource_limit->max_timers = 0x4;
|
||||
resource_limit->max_shared_mems = 0x8;
|
||||
resource_limit->max_address_arbiters = 0x3;
|
||||
resource_limit->max_cpu_time = 0x2710;
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
|
||||
|
||||
// Create the LIB_APPLET resource limit
|
||||
resource_limit = ResourceLimit::Create(kernel, "Library Applets");
|
||||
resource_limit->max_priority = 0x4;
|
||||
resource_limit->max_commit = 0x600000;
|
||||
resource_limit->max_threads = 0xE;
|
||||
resource_limit->max_events = 0x8;
|
||||
resource_limit->max_mutexes = 0x8;
|
||||
resource_limit->max_semaphores = 0x4;
|
||||
resource_limit->max_timers = 0x4;
|
||||
resource_limit->max_shared_mems = 0x8;
|
||||
resource_limit->max_address_arbiters = 0x1;
|
||||
resource_limit->max_cpu_time = 0x2710;
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
|
||||
|
||||
// Create the OTHER resource limit
|
||||
resource_limit = ResourceLimit::Create(kernel, "Others");
|
||||
resource_limit->max_priority = 0x4;
|
||||
resource_limit->max_commit = 0x2180000;
|
||||
resource_limit->max_threads = 0xE1;
|
||||
resource_limit->max_events = 0x108;
|
||||
resource_limit->max_mutexes = 0x25;
|
||||
resource_limit->max_semaphores = 0x43;
|
||||
resource_limit->max_timers = 0x2C;
|
||||
resource_limit->max_shared_mems = 0x1F;
|
||||
resource_limit->max_address_arbiters = 0x2D;
|
||||
resource_limit->max_cpu_time = 0x3E8;
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
|
||||
// If setting the default system values fails, then something seriously wrong has occurred.
|
||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
|
||||
.IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
|
||||
}
|
||||
|
||||
void InitializeThreads() {
|
||||
@@ -208,7 +162,7 @@ struct KernelCore::Impl {
|
||||
std::vector<SharedPtr<Process>> process_list;
|
||||
Process* current_process = nullptr;
|
||||
|
||||
std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
|
||||
SharedPtr<ResourceLimit> system_resource_limit;
|
||||
|
||||
/// The event type of the generic timer callback event
|
||||
CoreTiming::EventType* timer_callback_event_type = nullptr;
|
||||
@@ -239,9 +193,8 @@ void KernelCore::Shutdown() {
|
||||
impl->Shutdown();
|
||||
}
|
||||
|
||||
SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
|
||||
ResourceLimitCategory category) const {
|
||||
return impl->resource_limits.at(static_cast<std::size_t>(category));
|
||||
SharedPtr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
|
||||
return impl->system_resource_limit;
|
||||
}
|
||||
|
||||
SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {
|
||||
|
||||
@@ -24,8 +24,6 @@ class ResourceLimit;
|
||||
class Thread;
|
||||
class Timer;
|
||||
|
||||
enum class ResourceLimitCategory : u8;
|
||||
|
||||
/// Represents a single instance of the kernel.
|
||||
class KernelCore {
|
||||
private:
|
||||
@@ -47,8 +45,8 @@ public:
|
||||
/// Clears all resources in use by the kernel instance.
|
||||
void Shutdown();
|
||||
|
||||
/// Retrieves a shared pointer to a ResourceLimit identified by the given category.
|
||||
SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;
|
||||
/// Retrieves a shared pointer to the system resource limit instance.
|
||||
SharedPtr<ResourceLimit> GetSystemResourceLimit() const;
|
||||
|
||||
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
|
||||
SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
@@ -28,7 +29,7 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
|
||||
process->name = std::move(name);
|
||||
process->flags.raw = 0;
|
||||
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
|
||||
process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION);
|
||||
process->resource_limit = kernel.GetSystemResourceLimit();
|
||||
process->status = ProcessStatus::Created;
|
||||
process->program_id = 0;
|
||||
process->process_id = kernel.CreateNewProcessID();
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
|
||||
return static_cast<std::size_t>(type);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
|
||||
ResourceLimit::~ResourceLimit() = default;
|
||||
@@ -19,59 +23,22 @@ SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string n
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
|
||||
switch (resource) {
|
||||
case ResourceType::Commit:
|
||||
return current_commit;
|
||||
case ResourceType::Thread:
|
||||
return current_threads;
|
||||
case ResourceType::Event:
|
||||
return current_events;
|
||||
case ResourceType::Mutex:
|
||||
return current_mutexes;
|
||||
case ResourceType::Semaphore:
|
||||
return current_semaphores;
|
||||
case ResourceType::Timer:
|
||||
return current_timers;
|
||||
case ResourceType::SharedMemory:
|
||||
return current_shared_mems;
|
||||
case ResourceType::AddressArbiter:
|
||||
return current_address_arbiters;
|
||||
case ResourceType::CPUTime:
|
||||
return current_cpu_time;
|
||||
default:
|
||||
LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
|
||||
UNIMPLEMENTED();
|
||||
return 0;
|
||||
}
|
||||
s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
|
||||
return values.at(ResourceTypeToIndex(resource));
|
||||
}
|
||||
|
||||
u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
|
||||
switch (resource) {
|
||||
case ResourceType::Priority:
|
||||
return max_priority;
|
||||
case ResourceType::Commit:
|
||||
return max_commit;
|
||||
case ResourceType::Thread:
|
||||
return max_threads;
|
||||
case ResourceType::Event:
|
||||
return max_events;
|
||||
case ResourceType::Mutex:
|
||||
return max_mutexes;
|
||||
case ResourceType::Semaphore:
|
||||
return max_semaphores;
|
||||
case ResourceType::Timer:
|
||||
return max_timers;
|
||||
case ResourceType::SharedMemory:
|
||||
return max_shared_mems;
|
||||
case ResourceType::AddressArbiter:
|
||||
return max_address_arbiters;
|
||||
case ResourceType::CPUTime:
|
||||
return max_cpu_time;
|
||||
default:
|
||||
LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
|
||||
UNIMPLEMENTED();
|
||||
return 0;
|
||||
s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
|
||||
return limits.at(ResourceTypeToIndex(resource));
|
||||
}
|
||||
|
||||
ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) {
|
||||
const auto index = ResourceTypeToIndex(resource);
|
||||
|
||||
if (value < values[index]) {
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
values[index] = value;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -4,31 +4,25 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
|
||||
enum class ResourceLimitCategory : u8 {
|
||||
APPLICATION = 0,
|
||||
SYS_APPLET = 1,
|
||||
LIB_APPLET = 2,
|
||||
OTHER = 3
|
||||
};
|
||||
|
||||
enum class ResourceType {
|
||||
Priority = 0,
|
||||
Commit = 1,
|
||||
Thread = 2,
|
||||
Event = 3,
|
||||
Mutex = 4,
|
||||
Semaphore = 5,
|
||||
Timer = 6,
|
||||
SharedMemory = 7,
|
||||
AddressArbiter = 8,
|
||||
CPUTime = 9,
|
||||
PhysicalMemory,
|
||||
Threads,
|
||||
Events,
|
||||
TransferMemory,
|
||||
Sessions,
|
||||
|
||||
// Used as a count, not an actual type.
|
||||
ResourceTypeCount
|
||||
};
|
||||
|
||||
class ResourceLimit final : public Object {
|
||||
@@ -55,61 +49,51 @@ public:
|
||||
* @param resource Requested resource type
|
||||
* @returns The current value of the resource type
|
||||
*/
|
||||
s32 GetCurrentResourceValue(ResourceType resource) const;
|
||||
s64 GetCurrentResourceValue(ResourceType resource) const;
|
||||
|
||||
/**
|
||||
* Gets the max value for the specified resource.
|
||||
* @param resource Requested resource type
|
||||
* @returns The max value of the resource type
|
||||
*/
|
||||
u32 GetMaxResourceValue(ResourceType resource) const;
|
||||
s64 GetMaxResourceValue(ResourceType resource) const;
|
||||
|
||||
/// Name of resource limit object.
|
||||
std::string name;
|
||||
|
||||
/// Max thread priority that a process in this category can create
|
||||
s32 max_priority = 0;
|
||||
|
||||
/// Max memory that processes in this category can use
|
||||
s32 max_commit = 0;
|
||||
|
||||
///< Max number of objects that can be collectively created by the processes in this category
|
||||
s32 max_threads = 0;
|
||||
s32 max_events = 0;
|
||||
s32 max_mutexes = 0;
|
||||
s32 max_semaphores = 0;
|
||||
s32 max_timers = 0;
|
||||
s32 max_shared_mems = 0;
|
||||
s32 max_address_arbiters = 0;
|
||||
|
||||
/// Max CPU time that the processes in this category can utilize
|
||||
s32 max_cpu_time = 0;
|
||||
|
||||
// TODO(Subv): Increment these in their respective Kernel::T::Create functions, keeping in mind
|
||||
// that APPLICATION resource limits should not be affected by the objects created by service
|
||||
// modules.
|
||||
// Currently we have no way of distinguishing if a Create was called by the running application,
|
||||
// or by a service module. Approach this once we have separated the service modules into their
|
||||
// own processes
|
||||
|
||||
/// Current memory that the processes in this category are using
|
||||
s32 current_commit = 0;
|
||||
|
||||
///< Current number of objects among all processes in this category
|
||||
s32 current_threads = 0;
|
||||
s32 current_events = 0;
|
||||
s32 current_mutexes = 0;
|
||||
s32 current_semaphores = 0;
|
||||
s32 current_timers = 0;
|
||||
s32 current_shared_mems = 0;
|
||||
s32 current_address_arbiters = 0;
|
||||
|
||||
/// Current CPU time that the processes in this category are utilizing
|
||||
s32 current_cpu_time = 0;
|
||||
/**
|
||||
* Sets the limit value for a given resource type.
|
||||
*
|
||||
* @param resource The resource type to apply the limit to.
|
||||
* @param value The limit to apply to the given resource type.
|
||||
*
|
||||
* @return A result code indicating if setting the limit value
|
||||
* was successful or not.
|
||||
*
|
||||
* @note The supplied limit value *must* be greater than or equal to
|
||||
* the current resource value for the given resource type,
|
||||
* otherwise ERR_INVALID_STATE will be returned.
|
||||
*/
|
||||
ResultCode SetLimitValue(ResourceType resource, s64 value);
|
||||
|
||||
private:
|
||||
explicit ResourceLimit(KernelCore& kernel);
|
||||
~ResourceLimit() override;
|
||||
|
||||
// TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create
|
||||
// functions
|
||||
//
|
||||
// Currently we have no way of distinguishing if a Create was called by the running application,
|
||||
// or by a service module. Approach this once we have separated the service modules into their
|
||||
// own processes
|
||||
|
||||
using ResourceArray =
|
||||
std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>;
|
||||
|
||||
/// Maximum values a resource type may reach.
|
||||
ResourceArray limits{};
|
||||
/// Current resource limit values.
|
||||
ResourceArray values{};
|
||||
|
||||
/// Name of resource limit object.
|
||||
std::string name;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -61,7 +61,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce
|
||||
}
|
||||
|
||||
SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
|
||||
KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
|
||||
KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, std::size_t offset, u64 size,
|
||||
MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
|
||||
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
|
||||
|
||||
@@ -78,10 +78,10 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
|
||||
return shared_memory;
|
||||
}
|
||||
|
||||
ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
|
||||
ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermission permissions,
|
||||
MemoryPermission other_permissions) {
|
||||
const MemoryPermission own_other_permissions =
|
||||
target_process == owner_process ? this->permissions : this->other_permissions;
|
||||
&target_process == owner_process ? this->permissions : this->other_permissions;
|
||||
|
||||
// Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
|
||||
if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
|
||||
@@ -106,7 +106,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
|
||||
VAddr target_address = address;
|
||||
|
||||
// Map the memory block into the target process
|
||||
auto result = target_process->VMManager().MapMemoryBlock(
|
||||
auto result = target_process.VMManager().MapMemoryBlock(
|
||||
target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
|
||||
if (result.Failed()) {
|
||||
LOG_ERROR(
|
||||
@@ -116,14 +116,14 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
|
||||
return result.Code();
|
||||
}
|
||||
|
||||
return target_process->VMManager().ReprotectRange(target_address, size,
|
||||
ConvertPermissions(permissions));
|
||||
return target_process.VMManager().ReprotectRange(target_address, size,
|
||||
ConvertPermissions(permissions));
|
||||
}
|
||||
|
||||
ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) {
|
||||
ResultCode SharedMemory::Unmap(Process& target_process, VAddr address) {
|
||||
// TODO(Subv): Verify what happens if the application tries to unmap an address that is not
|
||||
// mapped to a SharedMemory.
|
||||
return target_process->VMManager().UnmapRange(address, size);
|
||||
return target_process.VMManager().UnmapRange(address, size);
|
||||
}
|
||||
|
||||
VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
|
||||
@@ -132,7 +132,11 @@ VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
|
||||
return static_cast<VMAPermission>(masked_permissions);
|
||||
}
|
||||
|
||||
u8* SharedMemory::GetPointer(u32 offset) {
|
||||
u8* SharedMemory::GetPointer(std::size_t offset) {
|
||||
return backing_block->data() + backing_block_offset + offset;
|
||||
}
|
||||
|
||||
const u8* SharedMemory::GetPointer(std::size_t offset) const {
|
||||
return backing_block->data() + backing_block_offset + offset;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
*/
|
||||
static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel,
|
||||
std::shared_ptr<std::vector<u8>> heap_block,
|
||||
u32 offset, u32 size,
|
||||
std::size_t offset, u64 size,
|
||||
MemoryPermission permissions,
|
||||
MemoryPermission other_permissions,
|
||||
std::string name = "Unknown Applet");
|
||||
@@ -81,6 +81,11 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
/// Gets the size of the underlying memory block in bytes.
|
||||
u64 GetSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the specified MemoryPermission into the equivalent VMAPermission.
|
||||
* @param permission The MemoryPermission to convert.
|
||||
@@ -94,44 +99,51 @@ public:
|
||||
* @param permissions Memory block map permissions (specified by SVC field)
|
||||
* @param other_permissions Memory block map other permissions (specified by SVC field)
|
||||
*/
|
||||
ResultCode Map(Process* target_process, VAddr address, MemoryPermission permissions,
|
||||
ResultCode Map(Process& target_process, VAddr address, MemoryPermission permissions,
|
||||
MemoryPermission other_permissions);
|
||||
|
||||
/**
|
||||
* Unmaps a shared memory block from the specified address in system memory
|
||||
* @param target_process Process from which to umap the memory block.
|
||||
* @param target_process Process from which to unmap the memory block.
|
||||
* @param address Address in system memory where the shared memory block is mapped
|
||||
* @return Result code of the unmap operation
|
||||
*/
|
||||
ResultCode Unmap(Process* target_process, VAddr address);
|
||||
ResultCode Unmap(Process& target_process, VAddr address);
|
||||
|
||||
/**
|
||||
* Gets a pointer to the shared memory block
|
||||
* @param offset Offset from the start of the shared memory block to get pointer
|
||||
* @return Pointer to the shared memory block from the specified offset
|
||||
* @return A pointer to the shared memory block from the specified offset
|
||||
*/
|
||||
u8* GetPointer(u32 offset = 0);
|
||||
u8* GetPointer(std::size_t offset = 0);
|
||||
|
||||
/// Process that created this shared memory block.
|
||||
SharedPtr<Process> owner_process;
|
||||
/// Address of shared memory block in the owner process if specified.
|
||||
VAddr base_address;
|
||||
/// Backing memory for this shared memory block.
|
||||
std::shared_ptr<std::vector<u8>> backing_block;
|
||||
/// Offset into the backing block for this shared memory.
|
||||
std::size_t backing_block_offset;
|
||||
/// Size of the memory block. Page-aligned.
|
||||
u64 size;
|
||||
/// Permission restrictions applied to the process which created the block.
|
||||
MemoryPermission permissions;
|
||||
/// Permission restrictions applied to other processes mapping the block.
|
||||
MemoryPermission other_permissions;
|
||||
/// Name of shared memory object.
|
||||
std::string name;
|
||||
/**
|
||||
* Gets a constant pointer to the shared memory block
|
||||
* @param offset Offset from the start of the shared memory block to get pointer
|
||||
* @return A constant pointer to the shared memory block from the specified offset
|
||||
*/
|
||||
const u8* GetPointer(std::size_t offset = 0) const;
|
||||
|
||||
private:
|
||||
explicit SharedMemory(KernelCore& kernel);
|
||||
~SharedMemory() override;
|
||||
|
||||
/// Backing memory for this shared memory block.
|
||||
std::shared_ptr<std::vector<u8>> backing_block;
|
||||
/// Offset into the backing block for this shared memory.
|
||||
std::size_t backing_block_offset = 0;
|
||||
/// Size of the memory block. Page-aligned.
|
||||
u64 size = 0;
|
||||
/// Permission restrictions applied to the process which created the block.
|
||||
MemoryPermission permissions{};
|
||||
/// Permission restrictions applied to other processes mapping the block.
|
||||
MemoryPermission other_permissions{};
|
||||
/// Process that created this shared memory block.
|
||||
SharedPtr<Process> owner_process;
|
||||
/// Address of shared memory block in the owner process if specified.
|
||||
VAddr base_address = 0;
|
||||
/// Name of shared memory object.
|
||||
std::string name;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -736,13 +736,6 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
|
||||
|
||||
const auto* const current_process = Core::CurrentProcess();
|
||||
|
||||
// Note: The kernel uses the current process's resource limit instead of
|
||||
// the one from the thread owner's resource limit.
|
||||
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
|
||||
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
@@ -796,7 +789,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
|
||||
return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare);
|
||||
return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare);
|
||||
}
|
||||
|
||||
static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
|
||||
@@ -826,7 +819,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
|
||||
return shared_memory->Unmap(current_process, addr);
|
||||
return shared_memory->Unmap(*current_process, addr);
|
||||
}
|
||||
|
||||
/// Query process memory
|
||||
@@ -885,10 +878,6 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||
}
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
|
||||
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
if (processor_id == THREADPROCESSORID_DEFAULT) {
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
@@ -1194,9 +1183,39 @@ static ResultCode ResetSignal(Handle handle) {
|
||||
|
||||
/// Creates a TransferMemory object
|
||||
static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) {
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
|
||||
permissions);
|
||||
*handle = 0;
|
||||
LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
|
||||
permissions);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address ({:016X}) is not page aligned!", addr);
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (!Common::Is4KBAligned(size) || size == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Size ({:016X}) is not page aligned or equal to zero!", size);
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})",
|
||||
addr, size);
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const auto perms = static_cast<MemoryPermission>(permissions);
|
||||
if (perms != MemoryPermission::None && perms != MemoryPermission::Read &&
|
||||
perms != MemoryPermission::ReadWrite) {
|
||||
LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
|
||||
permissions);
|
||||
return ERR_INVALID_MEMORY_PERMISSIONS;
|
||||
}
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const auto shared_mem_handle = SharedMemory::Create(
|
||||
kernel, handle_table.Get<Process>(CurrentProcess), size, perms, perms, addr);
|
||||
|
||||
CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,19 @@
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <stack>
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
#include "core/hle/service/am/applets/software_keyboard.h"
|
||||
#include "core/hle/service/am/applets/stub_applet.h"
|
||||
#include "core/hle/service/am/idle.h"
|
||||
#include "core/hle/service/am/omm.h"
|
||||
#include "core/hle/service/am/spsm.h"
|
||||
@@ -28,6 +33,13 @@
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
|
||||
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
|
||||
|
||||
enum class AppletId : u32 {
|
||||
SoftwareKeyboard = 0x11,
|
||||
};
|
||||
|
||||
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
|
||||
|
||||
struct LaunchParameters {
|
||||
@@ -471,16 +483,38 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
} else {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
IStorage::IStorage(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorage::Open, "Open"},
|
||||
{1, nullptr, "OpenTransferStorage"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IStorage::~IStorage() = default;
|
||||
|
||||
const std::vector<u8>& IStorage::GetData() const {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
|
||||
const bool use_docked_mode{Settings::values.use_docked_mode};
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@@ -500,100 +534,14 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
|
||||
public:
|
||||
explicit IStorageAccessor(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorageAccessor::GetSize, "GetSize"},
|
||||
{10, &IStorageAccessor::Write, "Write"},
|
||||
{11, &IStorageAccessor::Read, "Read"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<u8> buffer;
|
||||
|
||||
void GetSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u64>(buffer.size()));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void Write(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::vector<u8> data{ctx.ReadBuffer()};
|
||||
|
||||
ASSERT(offset + data.size() <= buffer.size());
|
||||
|
||||
std::memcpy(&buffer[offset], data.data(), data.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
|
||||
void Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::size_t size{ctx.GetWriteBufferSize()};
|
||||
|
||||
ASSERT(offset + size <= buffer.size());
|
||||
|
||||
ctx.WriteBuffer(buffer.data() + offset, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
};
|
||||
|
||||
class IStorage final : public ServiceFramework<IStorage> {
|
||||
public:
|
||||
explicit IStorage(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorage::Open, "Open"},
|
||||
{1, nullptr, "OpenTransferStorage"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<u8> buffer;
|
||||
|
||||
void Open(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
};
|
||||
|
||||
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
|
||||
public:
|
||||
explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
|
||||
explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet)
|
||||
: ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
|
||||
{1, nullptr, "IsCompleted"},
|
||||
{1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"},
|
||||
{10, &ILibraryAppletAccessor::Start, "Start"},
|
||||
{20, nullptr, "RequestExit"},
|
||||
{25, nullptr, "Terminate"},
|
||||
@@ -602,10 +550,10 @@ public:
|
||||
{100, &ILibraryAppletAccessor::PushInData, "PushInData"},
|
||||
{101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
|
||||
{102, nullptr, "PushExtraStorage"},
|
||||
{103, nullptr, "PushInteractiveInData"},
|
||||
{104, nullptr, "PopInteractiveOutData"},
|
||||
{105, nullptr, "GetPopOutDataEvent"},
|
||||
{106, nullptr, "GetPopInteractiveOutDataEvent"},
|
||||
{103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
|
||||
{104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
|
||||
{105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"},
|
||||
{106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
|
||||
{110, nullptr, "NeedsToExitProcess"},
|
||||
{120, nullptr, "GetLibraryAppletInfo"},
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
@@ -614,40 +562,50 @@ public:
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:StateChangedEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||
state_changed_event->Signal();
|
||||
const auto event = applet->GetBroker().GetStateChangedEvent();
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(state_changed_event);
|
||||
rb.PushCopyObjects(event);
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void IsCompleted(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(applet->TransactionComplete());
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetResult(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(applet->GetStatus());
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void Start(Kernel::HLERequestContext& ctx) {
|
||||
ASSERT(applet != nullptr);
|
||||
|
||||
applet->Initialize();
|
||||
applet->Execute();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void PushInData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
|
||||
applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -657,25 +615,145 @@ private:
|
||||
|
||||
void PopOutData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::IStorage>(std::move(storage_stack.top()));
|
||||
|
||||
storage_stack.pop();
|
||||
const auto storage = applet->GetBroker().PopNormalDataToGame();
|
||||
if (storage == nullptr) {
|
||||
rb.Push(ERR_NO_DATA_IN_CHANNEL);
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorage>(std::move(*storage));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
std::stack<std::shared_ptr<AM::IStorage>> storage_stack;
|
||||
Kernel::SharedPtr<Kernel::Event> state_changed_event;
|
||||
void PushInteractiveInData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>());
|
||||
|
||||
ASSERT(applet->IsInitialized());
|
||||
applet->ExecuteInteractive();
|
||||
applet->Execute();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
const auto storage = applet->GetBroker().PopInteractiveDataToGame();
|
||||
if (storage == nullptr) {
|
||||
rb.Push(ERR_NO_DATA_IN_CHANNEL);
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorage>(std::move(*storage));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(applet->GetBroker().GetNormalDataEvent());
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
std::shared_ptr<Applets::Applet> applet;
|
||||
};
|
||||
|
||||
void IStorage::Open(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorageAccessor>(*this);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
IStorageAccessor::IStorageAccessor(IStorage& storage)
|
||||
: ServiceFramework("IStorageAccessor"), backing(storage) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorageAccessor::GetSize, "GetSize"},
|
||||
{10, &IStorageAccessor::Write, "Write"},
|
||||
{11, &IStorageAccessor::Read, "Read"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IStorageAccessor::~IStorageAccessor() = default;
|
||||
|
||||
void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u64>(backing.buffer.size()));
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
|
||||
void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::vector<u8> data{ctx.ReadBuffer()};
|
||||
|
||||
if (data.size() > backing.buffer.size() - offset) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
|
||||
}
|
||||
|
||||
std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
|
||||
void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const u64 offset{rp.Pop<u64>()};
|
||||
const std::size_t size{ctx.GetWriteBufferSize()};
|
||||
|
||||
if (size > backing.buffer.size() - offset) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(backing.buffer.data() + offset, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}", offset);
|
||||
}
|
||||
|
||||
ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
|
||||
{1, nullptr, "TerminateAllLibraryApplets"},
|
||||
{2, nullptr, "AreAnyLibraryAppletsLeft"},
|
||||
{10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
|
||||
{11, nullptr, "CreateTransferMemoryStorage"},
|
||||
{11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
|
||||
{12, nullptr, "CreateHandleStorage"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
@@ -683,11 +761,37 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
|
||||
|
||||
ILibraryAppletCreator::~ILibraryAppletCreator() = default;
|
||||
|
||||
static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
|
||||
switch (id) {
|
||||
case AppletId::SoftwareKeyboard:
|
||||
return std::make_shared<Applets::SoftwareKeyboard>();
|
||||
default:
|
||||
LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
|
||||
static_cast<u32>(id));
|
||||
return std::make_shared<Applets::StubApplet>();
|
||||
}
|
||||
}
|
||||
|
||||
void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto applet_id = rp.PopRaw<AppletId>();
|
||||
const auto applet_mode = rp.PopRaw<u32>();
|
||||
|
||||
LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}",
|
||||
static_cast<u32>(applet_id), applet_mode);
|
||||
|
||||
const auto applet = GetAppletFromId(applet_id);
|
||||
|
||||
if (applet == nullptr) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::ILibraryAppletAccessor>();
|
||||
rb.PushIpcInterface<AM::ILibraryAppletAccessor>(applet);
|
||||
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
}
|
||||
@@ -704,6 +808,31 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called, size={}", size);
|
||||
}
|
||||
|
||||
void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
rp.SetCurrentOffset(3);
|
||||
const auto handle{rp.Pop<Kernel::Handle>()};
|
||||
|
||||
const auto shared_mem =
|
||||
Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>(
|
||||
handle);
|
||||
|
||||
if (shared_mem == nullptr) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
const u8* mem_begin = shared_mem->GetPointer();
|
||||
const u8* mem_end = mem_begin + shared_mem->GetSize();
|
||||
std::vector<u8> memory{mem_begin, mem_end};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory)));
|
||||
}
|
||||
|
||||
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
|
||||
@@ -155,6 +155,34 @@ private:
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
class IStorage final : public ServiceFramework<IStorage> {
|
||||
public:
|
||||
explicit IStorage(std::vector<u8> buffer);
|
||||
~IStorage() override;
|
||||
|
||||
const std::vector<u8>& GetData() const;
|
||||
|
||||
private:
|
||||
void Open(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::vector<u8> buffer;
|
||||
|
||||
friend class IStorageAccessor;
|
||||
};
|
||||
|
||||
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
|
||||
public:
|
||||
explicit IStorageAccessor(IStorage& backing);
|
||||
~IStorageAccessor() override;
|
||||
|
||||
private:
|
||||
void GetSize(Kernel::HLERequestContext& ctx);
|
||||
void Write(Kernel::HLERequestContext& ctx);
|
||||
void Read(Kernel::HLERequestContext& ctx);
|
||||
|
||||
IStorage& backing;
|
||||
};
|
||||
|
||||
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
|
||||
public:
|
||||
ILibraryAppletCreator();
|
||||
@@ -163,6 +191,7 @@ public:
|
||||
private:
|
||||
void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
|
||||
void CreateStorage(Kernel::HLERequestContext& ctx);
|
||||
void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
|
||||
|
||||
113
src/core/hle/service/am/applets/applets.cpp
Normal file
113
src/core/hle/service/am/applets/applets.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
AppletDataBroker::AppletDataBroker() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:StateChangedEvent");
|
||||
pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:PopDataOutEvent");
|
||||
pop_interactive_out_data_event = Kernel::Event::Create(
|
||||
kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||
}
|
||||
|
||||
AppletDataBroker::~AppletDataBroker() = default;
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
|
||||
if (out_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(out_channel.front());
|
||||
out_channel.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
|
||||
if (in_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(in_channel.front());
|
||||
in_channel.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
|
||||
if (out_interactive_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(out_interactive_channel.front());
|
||||
out_interactive_channel.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
|
||||
if (in_interactive_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(in_interactive_channel.front());
|
||||
in_interactive_channel.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
|
||||
in_channel.push(std::make_unique<IStorage>(storage));
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
|
||||
out_channel.push(std::make_unique<IStorage>(storage));
|
||||
pop_out_data_event->Signal();
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
|
||||
in_interactive_channel.push(std::make_unique<IStorage>(storage));
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
|
||||
out_interactive_channel.push(std::make_unique<IStorage>(storage));
|
||||
pop_interactive_out_data_event->Signal();
|
||||
}
|
||||
|
||||
void AppletDataBroker::SignalStateChanged() const {
|
||||
state_changed_event->Signal();
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetNormalDataEvent() const {
|
||||
return pop_out_data_event;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetInteractiveDataEvent() const {
|
||||
return pop_interactive_out_data_event;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetStateChangedEvent() const {
|
||||
return state_changed_event;
|
||||
}
|
||||
|
||||
Applet::Applet() = default;
|
||||
|
||||
Applet::~Applet() = default;
|
||||
|
||||
void Applet::Initialize() {
|
||||
const auto common = broker.PopNormalDataToApplet();
|
||||
ASSERT(common != nullptr);
|
||||
|
||||
const auto common_data = common->GetData();
|
||||
|
||||
ASSERT(common_data.size() >= sizeof(CommonArguments));
|
||||
std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments));
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
112
src/core/hle/service/am/applets/applets.h
Normal file
112
src/core/hle/service/am/applets/applets.h
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Kernel {
|
||||
class Event;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
class IStorage;
|
||||
|
||||
namespace Applets {
|
||||
|
||||
class AppletDataBroker final {
|
||||
public:
|
||||
AppletDataBroker();
|
||||
~AppletDataBroker();
|
||||
|
||||
std::unique_ptr<IStorage> PopNormalDataToGame();
|
||||
std::unique_ptr<IStorage> PopNormalDataToApplet();
|
||||
|
||||
std::unique_ptr<IStorage> PopInteractiveDataToGame();
|
||||
std::unique_ptr<IStorage> PopInteractiveDataToApplet();
|
||||
|
||||
void PushNormalDataFromGame(IStorage storage);
|
||||
void PushNormalDataFromApplet(IStorage storage);
|
||||
|
||||
void PushInteractiveDataFromGame(IStorage storage);
|
||||
void PushInteractiveDataFromApplet(IStorage storage);
|
||||
|
||||
void SignalStateChanged() const;
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> GetNormalDataEvent() const;
|
||||
Kernel::SharedPtr<Kernel::Event> GetInteractiveDataEvent() const;
|
||||
Kernel::SharedPtr<Kernel::Event> GetStateChangedEvent() const;
|
||||
|
||||
private:
|
||||
// Queues are named from applet's perspective
|
||||
|
||||
// PopNormalDataToApplet and PushNormalDataFromGame
|
||||
std::queue<std::unique_ptr<IStorage>> in_channel;
|
||||
|
||||
// PopNormalDataToGame and PushNormalDataFromApplet
|
||||
std::queue<std::unique_ptr<IStorage>> out_channel;
|
||||
|
||||
// PopInteractiveDataToApplet and PushInteractiveDataFromGame
|
||||
std::queue<std::unique_ptr<IStorage>> in_interactive_channel;
|
||||
|
||||
// PopInteractiveDataToGame and PushInteractiveDataFromApplet
|
||||
std::queue<std::unique_ptr<IStorage>> out_interactive_channel;
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> state_changed_event;
|
||||
|
||||
// Signaled on PushNormalDataFromApplet
|
||||
Kernel::SharedPtr<Kernel::Event> pop_out_data_event;
|
||||
|
||||
// Signaled on PushInteractiveDataFromApplet
|
||||
Kernel::SharedPtr<Kernel::Event> pop_interactive_out_data_event;
|
||||
};
|
||||
|
||||
class Applet {
|
||||
public:
|
||||
Applet();
|
||||
virtual ~Applet();
|
||||
|
||||
virtual void Initialize();
|
||||
|
||||
virtual bool TransactionComplete() const = 0;
|
||||
virtual ResultCode GetStatus() const = 0;
|
||||
virtual void ExecuteInteractive() = 0;
|
||||
virtual void Execute() = 0;
|
||||
|
||||
bool IsInitialized() const {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
AppletDataBroker& GetBroker() {
|
||||
return broker;
|
||||
}
|
||||
|
||||
const AppletDataBroker& GetBroker() const {
|
||||
return broker;
|
||||
}
|
||||
|
||||
protected:
|
||||
struct CommonArguments {
|
||||
u32_le arguments_version;
|
||||
u32_le size;
|
||||
u32_le library_version;
|
||||
u32_le theme_color;
|
||||
u8 play_startup_sound;
|
||||
u64_le system_tick;
|
||||
};
|
||||
static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
|
||||
|
||||
CommonArguments common_args{};
|
||||
AppletDataBroker broker;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
} // namespace Applets
|
||||
} // namespace Service::AM
|
||||
161
src/core/hle/service/am/applets/software_keyboard.cpp
Normal file
161
src/core/hle/service/am/applets/software_keyboard.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/software_keyboard.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
|
||||
constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
|
||||
constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
|
||||
constexpr bool INTERACTIVE_STATUS_OK = false;
|
||||
|
||||
static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
|
||||
KeyboardConfig config, std::u16string initial_text) {
|
||||
Core::Frontend::SoftwareKeyboardParameters params{};
|
||||
|
||||
params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
config.submit_text.data(), config.submit_text.size());
|
||||
params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
|
||||
config.header_text.data(), config.header_text.size());
|
||||
params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(),
|
||||
config.sub_text.size());
|
||||
params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(),
|
||||
config.guide_text.size());
|
||||
params.initial_text = initial_text;
|
||||
params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit;
|
||||
params.password = static_cast<bool>(config.is_password);
|
||||
params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position);
|
||||
params.value = static_cast<u8>(config.keyset_disable_bitmask);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
SoftwareKeyboard::SoftwareKeyboard() = default;
|
||||
|
||||
SoftwareKeyboard::~SoftwareKeyboard() = default;
|
||||
|
||||
void SoftwareKeyboard::Initialize() {
|
||||
complete = false;
|
||||
initial_text.clear();
|
||||
final_data.clear();
|
||||
|
||||
Applet::Initialize();
|
||||
|
||||
const auto keyboard_config_storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(keyboard_config_storage != nullptr);
|
||||
const auto& keyboard_config = keyboard_config_storage->GetData();
|
||||
|
||||
ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
|
||||
std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
|
||||
|
||||
const auto work_buffer_storage = broker.PopNormalDataToApplet();
|
||||
ASSERT(work_buffer_storage != nullptr);
|
||||
const auto& work_buffer = work_buffer_storage->GetData();
|
||||
|
||||
if (config.initial_string_size == 0)
|
||||
return;
|
||||
|
||||
std::vector<char16_t> string(config.initial_string_size);
|
||||
std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset,
|
||||
string.size() * 2);
|
||||
initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size());
|
||||
}
|
||||
|
||||
bool SoftwareKeyboard::TransactionComplete() const {
|
||||
return complete;
|
||||
}
|
||||
|
||||
ResultCode SoftwareKeyboard::GetStatus() const {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::ExecuteInteractive() {
|
||||
if (complete)
|
||||
return;
|
||||
|
||||
const auto storage = broker.PopInteractiveDataToApplet();
|
||||
ASSERT(storage != nullptr);
|
||||
const auto data = storage->GetData();
|
||||
const auto status = static_cast<bool>(data[0]);
|
||||
|
||||
if (status == INTERACTIVE_STATUS_OK) {
|
||||
complete = true;
|
||||
} else {
|
||||
const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
|
||||
|
||||
std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
|
||||
std::memcpy(string.data(), data.data() + 4, string.size() * 2);
|
||||
frontend.SendTextCheckDialog(
|
||||
Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
|
||||
[this] { broker.SignalStateChanged(); });
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::Execute() {
|
||||
if (complete) {
|
||||
broker.PushNormalDataFromApplet(IStorage{final_data});
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
|
||||
|
||||
const auto parameters = ConvertToFrontendParameters(config, initial_text);
|
||||
|
||||
frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); },
|
||||
parameters);
|
||||
}
|
||||
|
||||
void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
|
||||
std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE);
|
||||
|
||||
if (text.has_value()) {
|
||||
std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);
|
||||
|
||||
if (config.utf_8) {
|
||||
const u64 size = text->size() + 8;
|
||||
const auto new_text = Common::UTF16ToUTF8(*text);
|
||||
|
||||
std::memcpy(output_sub.data(), &size, sizeof(u64));
|
||||
std::memcpy(output_sub.data() + 8, new_text.data(),
|
||||
std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8));
|
||||
|
||||
output_main[0] = INTERACTIVE_STATUS_OK;
|
||||
std::memcpy(output_main.data() + 4, new_text.data(),
|
||||
std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4));
|
||||
} else {
|
||||
const u64 size = text->size() * 2 + 8;
|
||||
std::memcpy(output_sub.data(), &size, sizeof(u64));
|
||||
std::memcpy(output_sub.data() + 8, text->data(),
|
||||
std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8));
|
||||
|
||||
output_main[0] = INTERACTIVE_STATUS_OK;
|
||||
std::memcpy(output_main.data() + 4, text->data(),
|
||||
std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4));
|
||||
}
|
||||
|
||||
complete = !config.text_check;
|
||||
final_data = output_main;
|
||||
|
||||
if (complete) {
|
||||
broker.PushNormalDataFromApplet(IStorage{output_main});
|
||||
} else {
|
||||
broker.PushInteractiveDataFromApplet(IStorage{output_sub});
|
||||
}
|
||||
|
||||
broker.SignalStateChanged();
|
||||
} else {
|
||||
output_main[0] = 1;
|
||||
complete = true;
|
||||
broker.PushNormalDataFromApplet(IStorage{output_main});
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
}
|
||||
} // namespace Service::AM::Applets
|
||||
74
src/core/hle/service/am/applets/software_keyboard.h
Normal file
74
src/core/hle/service/am/applets/software_keyboard.h
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
enum class KeysetDisable : u32 {
|
||||
Space = 0x02,
|
||||
Address = 0x04,
|
||||
Percent = 0x08,
|
||||
Slashes = 0x10,
|
||||
Numbers = 0x40,
|
||||
DownloadCode = 0x80,
|
||||
};
|
||||
|
||||
struct KeyboardConfig {
|
||||
INSERT_PADDING_BYTES(4);
|
||||
std::array<char16_t, 9> submit_text;
|
||||
u16_le left_symbol_key;
|
||||
u16_le right_symbol_key;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
KeysetDisable keyset_disable_bitmask;
|
||||
u32_le initial_cursor_position;
|
||||
std::array<char16_t, 65> header_text;
|
||||
std::array<char16_t, 129> sub_text;
|
||||
std::array<char16_t, 257> guide_text;
|
||||
u32_le length_limit;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
u32_le is_password;
|
||||
INSERT_PADDING_BYTES(5);
|
||||
bool utf_8;
|
||||
bool draw_background;
|
||||
u32_le initial_string_offset;
|
||||
u32_le initial_string_size;
|
||||
u32_le user_dictionary_offset;
|
||||
u32_le user_dictionary_size;
|
||||
bool text_check;
|
||||
u64_le text_check_callback;
|
||||
};
|
||||
static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size.");
|
||||
|
||||
class SoftwareKeyboard final : public Applet {
|
||||
public:
|
||||
SoftwareKeyboard();
|
||||
~SoftwareKeyboard() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
||||
bool TransactionComplete() const override;
|
||||
ResultCode GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
|
||||
void WriteText(std::optional<std::u16string> text);
|
||||
|
||||
private:
|
||||
KeyboardConfig config;
|
||||
std::u16string initial_text;
|
||||
bool complete = false;
|
||||
std::vector<u8> final_data;
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
70
src/core/hle/service/am/applets/stub_applet.cpp
Normal file
70
src/core/hle/service/am/applets/stub_applet.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/stub_applet.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
|
||||
std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
|
||||
for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
|
||||
const auto data = storage->GetData();
|
||||
LOG_INFO(Service_AM,
|
||||
"called (STUBBED), during {} recieved normal data with size={:08X}, data={}",
|
||||
prefix, data.size(), Common::HexVectorToString(data));
|
||||
}
|
||||
|
||||
storage = broker.PopInteractiveDataToApplet();
|
||||
for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) {
|
||||
const auto data = storage->GetData();
|
||||
LOG_INFO(Service_AM,
|
||||
"called (STUBBED), during {} recieved interactive data with size={:08X}, data={}",
|
||||
prefix, data.size(), Common::HexVectorToString(data));
|
||||
}
|
||||
}
|
||||
|
||||
StubApplet::StubApplet() = default;
|
||||
|
||||
StubApplet::~StubApplet() = default;
|
||||
|
||||
void StubApplet::Initialize() {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
Applet::Initialize();
|
||||
LogCurrentStorage(broker, "Initialize");
|
||||
}
|
||||
|
||||
bool StubApplet::TransactionComplete() const {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
return true;
|
||||
}
|
||||
|
||||
ResultCode StubApplet::GetStatus() const {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void StubApplet::ExecuteInteractive() {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
LogCurrentStorage(broker, "ExecuteInteractive");
|
||||
|
||||
broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
|
||||
broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
void StubApplet::Execute() {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
LogCurrentStorage(broker, "Execute");
|
||||
|
||||
broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
|
||||
broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
} // namespace Service::AM::Applets
|
||||
24
src/core/hle/service/am/applets/stub_applet.h
Normal file
24
src/core/hle/service/am/applets/stub_applet.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
class StubApplet final : public Applet {
|
||||
public:
|
||||
StubApplet();
|
||||
~StubApplet() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
||||
bool TransactionComplete() const override;
|
||||
ResultCode GetStatus() const override;
|
||||
void ExecuteInteractive() override;
|
||||
void Execute() override;
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
@@ -44,8 +44,10 @@ enum class AudioState : u32 {
|
||||
|
||||
class IAudioOut final : public ServiceFramework<IAudioOut> {
|
||||
public:
|
||||
IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core)
|
||||
: ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params) {
|
||||
IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name,
|
||||
std::string&& unique_name)
|
||||
: ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params),
|
||||
device_name(std::move(device_name)) {
|
||||
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
|
||||
@@ -69,7 +71,7 @@ public:
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
|
||||
|
||||
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
|
||||
"IAudioOut", [=]() { buffer_event->Signal(); });
|
||||
std::move(unique_name), [=]() { buffer_event->Signal(); });
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -177,6 +179,7 @@ private:
|
||||
|
||||
AudioCore::AudioOut& audio_core;
|
||||
AudioCore::StreamPtr stream;
|
||||
std::string device_name;
|
||||
|
||||
AudoutParams audio_params{};
|
||||
|
||||
@@ -199,7 +202,15 @@ void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
|
||||
void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
ctx.WriteBuffer(DefaultDevice);
|
||||
const auto device_name_data{ctx.ReadBuffer()};
|
||||
std::string device_name;
|
||||
if (device_name_data[0] != '\0') {
|
||||
device_name.assign(device_name_data.begin(), device_name_data.end());
|
||||
} else {
|
||||
device_name.assign(DefaultDevice.begin(), DefaultDevice.end());
|
||||
}
|
||||
ctx.WriteBuffer(device_name);
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params{rp.PopRaw<AudoutParams>()};
|
||||
if (params.channel_count <= 2) {
|
||||
@@ -212,10 +223,9 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
||||
params.sample_rate = DefaultSampleRate;
|
||||
}
|
||||
|
||||
// TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl
|
||||
// will likely need to be updated as well.
|
||||
ASSERT_MSG(!audio_out_interface, "Unimplemented");
|
||||
audio_out_interface = std::make_shared<IAudioOut>(params, *audio_core);
|
||||
std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
|
||||
auto audio_out_interface = std::make_shared<IAudioOut>(
|
||||
params, *audio_core, std::move(device_name), std::move(unique_name));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -224,6 +234,8 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
|
||||
rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
|
||||
rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
|
||||
|
||||
audio_out_interfaces.push_back(std::move(audio_out_interface));
|
||||
}
|
||||
|
||||
AudOutU::AudOutU() : ServiceFramework("audout:u") {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace AudioCore {
|
||||
@@ -24,7 +25,7 @@ public:
|
||||
~AudOutU() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<IAudioOut> audio_out_interface;
|
||||
std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
|
||||
std::unique_ptr<AudioCore::AudioOut> audio_core;
|
||||
|
||||
void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -6,9 +6,14 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/hid/controllers/debug_pad.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
|
||||
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
|
||||
enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
|
||||
|
||||
Controller_DebugPad::Controller_DebugPad() = default;
|
||||
Controller_DebugPad::~Controller_DebugPad() = default;
|
||||
|
||||
@@ -33,10 +38,44 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
|
||||
|
||||
cur_entry.sampling_number = last_entry.sampling_number + 1;
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
// TODO(ogniK): Update debug pad states
|
||||
cur_entry.attribute.connected.Assign(1);
|
||||
auto& pad = cur_entry.pad_state;
|
||||
|
||||
using namespace Settings::NativeButton;
|
||||
pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
const auto [stick_l_x_f, stick_l_y_f] =
|
||||
analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
|
||||
const auto [stick_r_x_f, stick_r_y_f] =
|
||||
analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
|
||||
cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
|
||||
cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
|
||||
cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
|
||||
cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
|
||||
|
||||
std::memcpy(data, &shared_memory, sizeof(SharedMemory));
|
||||
}
|
||||
|
||||
void Controller_DebugPad::OnLoadInputDevices() {}
|
||||
void Controller_DebugPad::OnLoadInputDevices() {
|
||||
std::transform(Settings::values.debug_pad_buttons.begin(),
|
||||
Settings::values.debug_pad_buttons.begin() +
|
||||
Settings::NativeButton::NUM_BUTTONS_HID,
|
||||
buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
|
||||
std::transform(Settings::values.debug_pad_analogs.begin(),
|
||||
Settings::values.debug_pad_analogs.end(), analogs.begin(),
|
||||
Input::CreateDevice<Input::AnalogDevice>);
|
||||
}
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_DebugPad final : public ControllerBase {
|
||||
@@ -35,11 +38,40 @@ private:
|
||||
};
|
||||
static_assert(sizeof(AnalogStick) == 0x8);
|
||||
|
||||
struct PadState {
|
||||
union {
|
||||
u32_le raw{};
|
||||
BitField<0, 1, u32_le> a;
|
||||
BitField<1, 1, u32_le> b;
|
||||
BitField<2, 1, u32_le> x;
|
||||
BitField<3, 1, u32_le> y;
|
||||
BitField<4, 1, u32_le> l;
|
||||
BitField<5, 1, u32_le> r;
|
||||
BitField<6, 1, u32_le> zl;
|
||||
BitField<7, 1, u32_le> zr;
|
||||
BitField<8, 1, u32_le> plus;
|
||||
BitField<9, 1, u32_le> minus;
|
||||
BitField<10, 1, u32_le> d_left;
|
||||
BitField<11, 1, u32_le> d_up;
|
||||
BitField<12, 1, u32_le> d_right;
|
||||
BitField<13, 1, u32_le> d_down;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
|
||||
|
||||
struct Attributes {
|
||||
union {
|
||||
u32_le raw{};
|
||||
BitField<0, 1, u32_le> connected;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
|
||||
|
||||
struct PadStates {
|
||||
s64_le sampling_number;
|
||||
s64_le sampling_number2;
|
||||
u32_le attribute;
|
||||
u32_le button_state;
|
||||
Attributes attribute;
|
||||
PadState pad_state;
|
||||
AnalogStick r_stick;
|
||||
AnalogStick l_stick;
|
||||
};
|
||||
@@ -52,5 +84,10 @@ private:
|
||||
};
|
||||
static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
|
||||
SharedMemory shared_memory{};
|
||||
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
|
||||
buttons;
|
||||
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
|
||||
analogs;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/hid/controllers/keyboard.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::HID {
|
||||
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
|
||||
constexpr u8 KEYS_PER_BYTE = 8;
|
||||
|
||||
Controller_Keyboard::Controller_Keyboard() = default;
|
||||
Controller_Keyboard::~Controller_Keyboard() = default;
|
||||
@@ -34,10 +36,24 @@ void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
|
||||
|
||||
cur_entry.sampling_number = last_entry.sampling_number + 1;
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
// TODO(ogniK): Update keyboard states
|
||||
|
||||
for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
|
||||
for (std::size_t k = 0; k < KEYS_PER_BYTE; ++k) {
|
||||
cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << k);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < keyboard_mods.size(); ++i) {
|
||||
cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i);
|
||||
}
|
||||
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
|
||||
}
|
||||
|
||||
void Controller_Keyboard::OnLoadInputDevices() {}
|
||||
void Controller_Keyboard::OnLoadInputDevices() {
|
||||
std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(),
|
||||
keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
|
||||
std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
|
||||
keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
|
||||
}
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_Keyboard final : public ControllerBase {
|
||||
@@ -46,5 +48,10 @@ private:
|
||||
};
|
||||
static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
|
||||
SharedMemory shared_memory{};
|
||||
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys>
|
||||
keyboard_keys;
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
|
||||
keyboard_mods;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <cstring>
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/hle/service/hid/controllers/mouse.h"
|
||||
|
||||
namespace Service::HID {
|
||||
@@ -14,7 +15,6 @@ Controller_Mouse::Controller_Mouse() = default;
|
||||
Controller_Mouse::~Controller_Mouse() = default;
|
||||
|
||||
void Controller_Mouse::OnInit() {}
|
||||
|
||||
void Controller_Mouse::OnRelease() {}
|
||||
|
||||
void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
|
||||
@@ -34,10 +34,29 @@ void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
|
||||
|
||||
cur_entry.sampling_number = last_entry.sampling_number + 1;
|
||||
cur_entry.sampling_number2 = cur_entry.sampling_number;
|
||||
// TODO(ogniK): Update mouse states
|
||||
|
||||
if (Settings::values.mouse_enabled) {
|
||||
const auto [px, py, sx, sy] = mouse_device->GetStatus();
|
||||
const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width);
|
||||
const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height);
|
||||
cur_entry.x = x;
|
||||
cur_entry.y = y;
|
||||
cur_entry.delta_x = x - last_entry.x;
|
||||
cur_entry.delta_y = y - last_entry.y;
|
||||
cur_entry.mouse_wheel_x = sx;
|
||||
cur_entry.mouse_wheel_y = sy;
|
||||
|
||||
for (std::size_t i = 0; i < mouse_button_devices.size(); ++i) {
|
||||
cur_entry.button |= (mouse_button_devices[i]->GetStatus() << i);
|
||||
}
|
||||
}
|
||||
|
||||
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
|
||||
}
|
||||
|
||||
void Controller_Mouse::OnLoadInputDevices() {}
|
||||
void Controller_Mouse::OnLoadInputDevices() {
|
||||
mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
|
||||
std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
|
||||
mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
|
||||
}
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
#include <array>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::HID {
|
||||
class Controller_Mouse final : public ControllerBase {
|
||||
@@ -35,7 +37,8 @@ private:
|
||||
s32_le y;
|
||||
s32_le delta_x;
|
||||
s32_le delta_y;
|
||||
s32_le mouse_wheel;
|
||||
s32_le mouse_wheel_x;
|
||||
s32_le mouse_wheel_y;
|
||||
s32_le button;
|
||||
s32_le attribute;
|
||||
};
|
||||
@@ -46,5 +49,9 @@ private:
|
||||
std::array<MouseState, 17> mouse_states;
|
||||
};
|
||||
SharedMemory shared_memory{};
|
||||
|
||||
std::unique_ptr<Input::MouseDevice> mouse_device;
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
|
||||
mouse_button_devices;
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -17,22 +17,13 @@
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
|
||||
constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
|
||||
constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
|
||||
constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
|
||||
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
|
||||
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
|
||||
constexpr std::size_t NPAD_OFFSET = 0x9A00;
|
||||
constexpr u32 BATTERY_FULL = 2;
|
||||
constexpr u32 NPAD_HANDHELD = 32;
|
||||
constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
|
||||
constexpr u32 MAX_NPAD_ID = 7;
|
||||
constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER =
|
||||
Controller_NPad::NPadControllerType::JoyDual;
|
||||
constexpr std::array<u32, 10> npad_id_list{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 32, 16,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
|
||||
};
|
||||
|
||||
enum class JoystickId : std::size_t {
|
||||
@@ -40,7 +31,23 @@ enum class JoystickId : std::size_t {
|
||||
Joystick_Right,
|
||||
};
|
||||
|
||||
static std::size_t NPadIdToIndex(u32 npad_id) {
|
||||
static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
|
||||
switch (type) {
|
||||
case Settings::ControllerType::ProController:
|
||||
return Controller_NPad::NPadControllerType::ProController;
|
||||
case Settings::ControllerType::DualJoycon:
|
||||
return Controller_NPad::NPadControllerType::JoyDual;
|
||||
case Settings::ControllerType::LeftJoycon:
|
||||
return Controller_NPad::NPadControllerType::JoyLeft;
|
||||
case Settings::ControllerType::RightJoycon:
|
||||
return Controller_NPad::NPadControllerType::JoyRight;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return Controller_NPad::NPadControllerType::JoyDual;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
|
||||
switch (npad_id) {
|
||||
case 0:
|
||||
case 1:
|
||||
@@ -63,6 +70,27 @@ static std::size_t NPadIdToIndex(u32 npad_id) {
|
||||
}
|
||||
}
|
||||
|
||||
u32 Controller_NPad::IndexToNPad(std::size_t index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
return static_cast<u32>(index);
|
||||
case 8:
|
||||
return NPAD_HANDHELD;
|
||||
case 9:
|
||||
return NPAD_UNKNOWN;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown npad index {}", index);
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
Controller_NPad::Controller_NPad() = default;
|
||||
Controller_NPad::~Controller_NPad() = default;
|
||||
|
||||
@@ -79,22 +107,32 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
|
||||
controller.joy_styles.handheld.Assign(1);
|
||||
controller.device_type.handheld.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Dual;
|
||||
controller.properties.is_vertical.Assign(1);
|
||||
controller.properties.use_plus.Assign(1);
|
||||
controller.properties.use_minus.Assign(1);
|
||||
break;
|
||||
case NPadControllerType::JoyDual:
|
||||
controller.joy_styles.joycon_dual.Assign(1);
|
||||
controller.device_type.joycon_left.Assign(1);
|
||||
controller.device_type.joycon_right.Assign(1);
|
||||
controller.properties.is_vertical.Assign(1);
|
||||
controller.properties.use_plus.Assign(1);
|
||||
controller.properties.use_minus.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Dual;
|
||||
break;
|
||||
case NPadControllerType::JoyLeft:
|
||||
controller.joy_styles.joycon_left.Assign(1);
|
||||
controller.device_type.joycon_left.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Dual;
|
||||
controller.properties.is_horizontal.Assign(1);
|
||||
controller.properties.use_minus.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Single;
|
||||
break;
|
||||
case NPadControllerType::JoyRight:
|
||||
controller.joy_styles.joycon_right.Assign(1);
|
||||
controller.device_type.joycon_right.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Dual;
|
||||
controller.properties.is_horizontal.Assign(1);
|
||||
controller.properties.use_plus.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Single;
|
||||
break;
|
||||
case NPadControllerType::Pokeball:
|
||||
controller.joy_styles.pokeball.Assign(1);
|
||||
@@ -104,6 +142,9 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
|
||||
case NPadControllerType::ProController:
|
||||
controller.joy_styles.pro_controller.Assign(1);
|
||||
controller.device_type.pro_controller.Assign(1);
|
||||
controller.properties.is_vertical.Assign(1);
|
||||
controller.properties.use_plus.Assign(1);
|
||||
controller.properties.use_minus.Assign(1);
|
||||
controller.pad_assignment = NPadAssignments::Single;
|
||||
break;
|
||||
}
|
||||
@@ -113,14 +154,12 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
|
||||
controller.single_color.button_color = 0;
|
||||
|
||||
controller.dual_color_error = ColorReadError::ReadOk;
|
||||
controller.left_color.body_color = JOYCON_BODY_NEON_BLUE;
|
||||
controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE;
|
||||
controller.right_color.body_color = JOYCON_BODY_NEON_RED;
|
||||
controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED;
|
||||
controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left;
|
||||
controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left;
|
||||
controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right;
|
||||
controller.right_color.button_color =
|
||||
Settings::values.players[controller_idx].button_color_right;
|
||||
|
||||
controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations
|
||||
controller.properties.use_plus.Assign(1);
|
||||
controller.properties.use_minus.Assign(1);
|
||||
controller.battery_level[0] = BATTERY_FULL;
|
||||
controller.battery_level[1] = BATTERY_FULL;
|
||||
controller.battery_level[2] = BATTERY_FULL;
|
||||
@@ -144,26 +183,109 @@ void Controller_NPad::OnInit() {
|
||||
style.pro_controller.Assign(1);
|
||||
style.pokeball.Assign(1);
|
||||
}
|
||||
|
||||
std::transform(
|
||||
Settings::values.players.begin(), Settings::values.players.end(),
|
||||
connected_controllers.begin(), [](const Settings::PlayerInput& player) {
|
||||
return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected};
|
||||
});
|
||||
|
||||
std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
|
||||
[](const ControllerHolder& holder) { return holder.is_connected; });
|
||||
|
||||
// Account for handheld
|
||||
if (connected_controllers[8].is_connected)
|
||||
connected_controllers[8].type = NPadControllerType::Handheld;
|
||||
|
||||
supported_npad_id_types.resize(npad_id_list.size());
|
||||
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
|
||||
npad_id_list.size() * sizeof(u32));
|
||||
|
||||
// Add a default dual joycon controller if none are present.
|
||||
if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
|
||||
[](const ControllerHolder& controller) { return controller.is_connected; })) {
|
||||
supported_npad_id_types.resize(npad_id_list.size());
|
||||
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
|
||||
npad_id_list.size() * sizeof(u32));
|
||||
AddNewController(PREFERRED_CONTROLLER);
|
||||
AddNewController(NPadControllerType::JoyDual);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
|
||||
const auto& controller = connected_controllers[i];
|
||||
if (controller.is_connected) {
|
||||
AddNewControllerAt(controller.type, IndexToNPad(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::OnLoadInputDevices() {
|
||||
std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
|
||||
Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
|
||||
buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
|
||||
std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
|
||||
Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
|
||||
sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
|
||||
const auto& players = Settings::values.players;
|
||||
for (std::size_t i = 0; i < players.size(); ++i) {
|
||||
std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
|
||||
players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
|
||||
buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>);
|
||||
std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
|
||||
players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
|
||||
sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller_NPad::OnRelease() {}
|
||||
|
||||
void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
|
||||
const auto controller_idx = NPadIdToIndex(npad_id);
|
||||
const auto controller_type = connected_controllers[controller_idx].type;
|
||||
if (!connected_controllers[controller_idx].is_connected) {
|
||||
return;
|
||||
}
|
||||
auto& pad_state = npad_pad_states[controller_idx].pad_states;
|
||||
auto& lstick_entry = npad_pad_states[controller_idx].l_stick;
|
||||
auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
|
||||
const auto& button_state = buttons[controller_idx];
|
||||
const auto& analog_state = sticks[controller_idx];
|
||||
|
||||
using namespace Settings::NativeButton;
|
||||
pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
const auto [stick_l_x_f, stick_l_y_f] =
|
||||
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
|
||||
const auto [stick_r_x_f, stick_r_y_f] =
|
||||
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
|
||||
lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
|
||||
lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
|
||||
rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
|
||||
rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
|
||||
}
|
||||
|
||||
void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
if (!IsControllerActivated())
|
||||
return;
|
||||
@@ -199,97 +321,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Pad states
|
||||
ControllerPadState pad_state{};
|
||||
using namespace Settings::NativeButton;
|
||||
pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
AnalogPosition lstick_entry{};
|
||||
AnalogPosition rstick_entry{};
|
||||
|
||||
const auto [stick_l_x_f, stick_l_y_f] =
|
||||
sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
|
||||
const auto [stick_r_x_f, stick_r_y_f] =
|
||||
sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
|
||||
lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
|
||||
lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
|
||||
rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
|
||||
rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
|
||||
|
||||
if (controller_type == NPadControllerType::JoyLeft ||
|
||||
controller_type == NPadControllerType::JoyRight) {
|
||||
if (npad.properties.is_horizontal) {
|
||||
ControllerPadState state{};
|
||||
AnalogPosition temp_lstick_entry{};
|
||||
AnalogPosition temp_rstick_entry{};
|
||||
if (controller_type == NPadControllerType::JoyLeft) {
|
||||
state.d_down.Assign(pad_state.d_left.Value());
|
||||
state.d_left.Assign(pad_state.d_up.Value());
|
||||
state.d_right.Assign(pad_state.d_down.Value());
|
||||
state.d_up.Assign(pad_state.d_right.Value());
|
||||
state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
|
||||
state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
|
||||
|
||||
state.zl.Assign(pad_state.zl.Value());
|
||||
state.plus.Assign(pad_state.minus.Value());
|
||||
|
||||
temp_lstick_entry = lstick_entry;
|
||||
temp_rstick_entry = rstick_entry;
|
||||
std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
|
||||
std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
|
||||
temp_lstick_entry.y *= -1;
|
||||
} else if (controller_type == NPadControllerType::JoyRight) {
|
||||
state.x.Assign(pad_state.a.Value());
|
||||
state.a.Assign(pad_state.b.Value());
|
||||
state.b.Assign(pad_state.y.Value());
|
||||
state.y.Assign(pad_state.b.Value());
|
||||
|
||||
state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
|
||||
state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
|
||||
state.zr.Assign(pad_state.zr.Value());
|
||||
state.plus.Assign(pad_state.plus.Value());
|
||||
|
||||
temp_lstick_entry = lstick_entry;
|
||||
temp_rstick_entry = rstick_entry;
|
||||
std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
|
||||
std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
|
||||
temp_rstick_entry.x *= -1;
|
||||
}
|
||||
pad_state.raw = state.raw;
|
||||
lstick_entry = temp_lstick_entry;
|
||||
rstick_entry = temp_rstick_entry;
|
||||
}
|
||||
}
|
||||
const u32 npad_index = static_cast<u32>(i);
|
||||
RequestPadStateUpdate(npad_index);
|
||||
auto& pad_state = npad_pad_states[npad_index];
|
||||
|
||||
auto& main_controller =
|
||||
npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
|
||||
@@ -304,8 +338,51 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
|
||||
|
||||
if (hold_type == NpadHoldType::Horizontal) {
|
||||
// TODO(ogniK): Remap buttons for different orientations
|
||||
ControllerPadState state{};
|
||||
AnalogPosition temp_lstick_entry{};
|
||||
AnalogPosition temp_rstick_entry{};
|
||||
if (controller_type == NPadControllerType::JoyLeft) {
|
||||
state.d_down.Assign(pad_state.pad_states.d_left.Value());
|
||||
state.d_left.Assign(pad_state.pad_states.d_up.Value());
|
||||
state.d_right.Assign(pad_state.pad_states.d_down.Value());
|
||||
state.d_up.Assign(pad_state.pad_states.d_right.Value());
|
||||
state.l.Assign(pad_state.pad_states.l.Value() |
|
||||
pad_state.pad_states.left_sl.Value());
|
||||
state.r.Assign(pad_state.pad_states.r.Value() |
|
||||
pad_state.pad_states.left_sr.Value());
|
||||
|
||||
state.zl.Assign(pad_state.pad_states.zl.Value());
|
||||
state.plus.Assign(pad_state.pad_states.minus.Value());
|
||||
|
||||
temp_lstick_entry = pad_state.l_stick;
|
||||
temp_rstick_entry = pad_state.r_stick;
|
||||
std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
|
||||
std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
|
||||
temp_lstick_entry.y *= -1;
|
||||
} else if (controller_type == NPadControllerType::JoyRight) {
|
||||
state.x.Assign(pad_state.pad_states.a.Value());
|
||||
state.a.Assign(pad_state.pad_states.b.Value());
|
||||
state.b.Assign(pad_state.pad_states.y.Value());
|
||||
state.y.Assign(pad_state.pad_states.b.Value());
|
||||
|
||||
state.l.Assign(pad_state.pad_states.l.Value() |
|
||||
pad_state.pad_states.right_sl.Value());
|
||||
state.r.Assign(pad_state.pad_states.r.Value() |
|
||||
pad_state.pad_states.right_sr.Value());
|
||||
state.zr.Assign(pad_state.pad_states.zr.Value());
|
||||
state.plus.Assign(pad_state.pad_states.plus.Value());
|
||||
|
||||
temp_lstick_entry = pad_state.l_stick;
|
||||
temp_rstick_entry = pad_state.r_stick;
|
||||
std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
|
||||
std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
|
||||
temp_rstick_entry.x *= -1;
|
||||
}
|
||||
pad_state.pad_states.raw = state.raw;
|
||||
pad_state.l_stick = temp_lstick_entry;
|
||||
pad_state.r_stick = temp_rstick_entry;
|
||||
}
|
||||
|
||||
libnx_entry.connection_status.raw = 0;
|
||||
|
||||
switch (controller_type) {
|
||||
@@ -316,9 +393,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
|
||||
handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
|
||||
handheld_entry.connection_status.IsRightJoyWired.Assign(1);
|
||||
handheld_entry.pad_states.raw = pad_state.raw;
|
||||
handheld_entry.l_stick = lstick_entry;
|
||||
handheld_entry.r_stick = rstick_entry;
|
||||
handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
|
||||
handheld_entry.pad.l_stick = pad_state.l_stick;
|
||||
handheld_entry.pad.r_stick = pad_state.r_stick;
|
||||
break;
|
||||
case NPadControllerType::JoyDual:
|
||||
dual_entry.connection_status.raw = 0;
|
||||
@@ -331,25 +408,25 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
|
||||
libnx_entry.connection_status.IsConnected.Assign(1);
|
||||
|
||||
dual_entry.pad_states.raw = pad_state.raw;
|
||||
dual_entry.l_stick = lstick_entry;
|
||||
dual_entry.r_stick = rstick_entry;
|
||||
dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
|
||||
dual_entry.pad.l_stick = pad_state.l_stick;
|
||||
dual_entry.pad.r_stick = pad_state.r_stick;
|
||||
break;
|
||||
case NPadControllerType::JoyLeft:
|
||||
left_entry.connection_status.raw = 0;
|
||||
|
||||
left_entry.connection_status.IsConnected.Assign(1);
|
||||
left_entry.pad_states.raw = pad_state.raw;
|
||||
left_entry.l_stick = lstick_entry;
|
||||
left_entry.r_stick = rstick_entry;
|
||||
left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
|
||||
left_entry.pad.l_stick = pad_state.l_stick;
|
||||
left_entry.pad.r_stick = pad_state.r_stick;
|
||||
break;
|
||||
case NPadControllerType::JoyRight:
|
||||
right_entry.connection_status.raw = 0;
|
||||
|
||||
right_entry.connection_status.IsConnected.Assign(1);
|
||||
right_entry.pad_states.raw = pad_state.raw;
|
||||
right_entry.l_stick = lstick_entry;
|
||||
right_entry.r_stick = rstick_entry;
|
||||
right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
|
||||
right_entry.pad.l_stick = pad_state.l_stick;
|
||||
right_entry.pad.r_stick = pad_state.r_stick;
|
||||
break;
|
||||
case NPadControllerType::Pokeball:
|
||||
pokeball_entry.connection_status.raw = 0;
|
||||
@@ -357,30 +434,30 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
|
||||
pokeball_entry.connection_status.IsConnected.Assign(1);
|
||||
pokeball_entry.connection_status.IsWired.Assign(1);
|
||||
|
||||
pokeball_entry.pad_states.raw = pad_state.raw;
|
||||
pokeball_entry.l_stick = lstick_entry;
|
||||
pokeball_entry.r_stick = rstick_entry;
|
||||
pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
|
||||
pokeball_entry.pad.l_stick = pad_state.l_stick;
|
||||
pokeball_entry.pad.r_stick = pad_state.r_stick;
|
||||
break;
|
||||
case NPadControllerType::ProController:
|
||||
main_controller.connection_status.raw = 0;
|
||||
|
||||
main_controller.connection_status.IsConnected.Assign(1);
|
||||
main_controller.connection_status.IsWired.Assign(1);
|
||||
main_controller.pad_states.raw = pad_state.raw;
|
||||
main_controller.l_stick = lstick_entry;
|
||||
main_controller.r_stick = rstick_entry;
|
||||
main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
|
||||
main_controller.pad.l_stick = pad_state.l_stick;
|
||||
main_controller.pad.r_stick = pad_state.r_stick;
|
||||
break;
|
||||
}
|
||||
|
||||
// LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
|
||||
// any controllers.
|
||||
libnx_entry.pad_states.raw = pad_state.raw;
|
||||
libnx_entry.l_stick = lstick_entry;
|
||||
libnx_entry.r_stick = rstick_entry;
|
||||
libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw;
|
||||
libnx_entry.pad.l_stick = pad_state.l_stick;
|
||||
libnx_entry.pad.r_stick = pad_state.r_stick;
|
||||
}
|
||||
std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
|
||||
shared_memory_entries.size() * sizeof(NPadEntry));
|
||||
} // namespace Service::HID
|
||||
}
|
||||
|
||||
void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
|
||||
style.raw = style_set.raw;
|
||||
@@ -401,23 +478,24 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
|
||||
if (!controller.is_connected) {
|
||||
continue;
|
||||
}
|
||||
if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
|
||||
const auto best_type = DecideBestController(PREFERRED_CONTROLLER);
|
||||
const bool is_handheld = (best_type == NPadControllerType::Handheld ||
|
||||
PREFERRED_CONTROLLER == NPadControllerType::Handheld);
|
||||
const auto requested_controller =
|
||||
i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
|
||||
: NPadControllerType::Handheld;
|
||||
if (!IsControllerSupported(requested_controller)) {
|
||||
const auto is_handheld = requested_controller == NPadControllerType::Handheld;
|
||||
if (is_handheld) {
|
||||
controller.type = NPadControllerType::None;
|
||||
controller.is_connected = false;
|
||||
AddNewController(best_type);
|
||||
AddNewController(requested_controller);
|
||||
} else {
|
||||
controller.type = best_type;
|
||||
controller.type = requested_controller;
|
||||
InitNewlyAddedControler(i);
|
||||
}
|
||||
had_controller_update = true;
|
||||
}
|
||||
}
|
||||
if (had_controller_update) {
|
||||
styleset_changed_event->Signal();
|
||||
if (had_controller_update) {
|
||||
styleset_changed_event->Signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,15 +528,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
|
||||
return;
|
||||
}
|
||||
for (std::size_t i = 0; i < controller_ids.size(); i++) {
|
||||
std::size_t controller_pos = i;
|
||||
// Handheld controller conversion
|
||||
if (controller_pos == NPAD_HANDHELD) {
|
||||
controller_pos = 8;
|
||||
}
|
||||
// Unknown controller conversion
|
||||
if (controller_pos == NPAD_UNKNOWN) {
|
||||
controller_pos = 9;
|
||||
}
|
||||
std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i));
|
||||
if (connected_controllers[controller_pos].is_connected) {
|
||||
// TODO(ogniK): Vibrate the physical controller
|
||||
}
|
||||
@@ -477,7 +547,9 @@ Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() cons
|
||||
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
|
||||
return last_processed_vibration;
|
||||
}
|
||||
|
||||
void Controller_NPad::AddNewController(NPadControllerType controller) {
|
||||
controller = DecideBestController(controller);
|
||||
if (controller == NPadControllerType::Handheld) {
|
||||
connected_controllers[8] = {controller, true};
|
||||
InitNewlyAddedControler(8);
|
||||
@@ -495,6 +567,18 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
|
||||
InitNewlyAddedControler(controller_id);
|
||||
}
|
||||
|
||||
void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
|
||||
controller = DecideBestController(controller);
|
||||
if (controller == NPadControllerType::Handheld) {
|
||||
connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
|
||||
InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
|
||||
return;
|
||||
}
|
||||
|
||||
connected_controllers[npad_id] = {controller, true};
|
||||
InitNewlyAddedControler(npad_id);
|
||||
}
|
||||
|
||||
void Controller_NPad::ConnectNPad(u32 npad_id) {
|
||||
connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
|
||||
}
|
||||
@@ -503,6 +587,36 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) {
|
||||
connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
|
||||
}
|
||||
|
||||
bool Controller_NPad::IsControllerSupported(NPadControllerType controller) {
|
||||
if (controller == NPadControllerType::Handheld) {
|
||||
// Handheld is not even a supported type, lets stop here
|
||||
if (std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
|
||||
NPAD_HANDHELD) == supported_npad_id_types.end()) {
|
||||
return false;
|
||||
}
|
||||
// Handheld should not be supported in docked mode
|
||||
if (Settings::values.use_docked_mode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
switch (controller) {
|
||||
case NPadControllerType::ProController:
|
||||
return style.pro_controller;
|
||||
case NPadControllerType::Handheld:
|
||||
return style.handheld;
|
||||
case NPadControllerType::JoyDual:
|
||||
return style.joycon_dual;
|
||||
case NPadControllerType::JoyLeft:
|
||||
return style.joycon_left;
|
||||
case NPadControllerType::JoyRight:
|
||||
return style.joycon_right;
|
||||
case NPadControllerType::Pokeball:
|
||||
return style.pokeball;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
|
||||
if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
|
||||
// These are controllers without led patterns
|
||||
@@ -534,6 +648,36 @@ void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
|
||||
can_controllers_vibrate = can_vibrate;
|
||||
}
|
||||
|
||||
void Controller_NPad::ClearAllConnectedControllers() {
|
||||
for (auto& controller : connected_controllers) {
|
||||
if (controller.is_connected && controller.type != NPadControllerType::None) {
|
||||
controller.type = NPadControllerType::None;
|
||||
controller.is_connected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Controller_NPad::DisconnectAllConnectedControllers() {
|
||||
std::for_each(connected_controllers.begin(), connected_controllers.end(),
|
||||
[](ControllerHolder& controller) { controller.is_connected = false; });
|
||||
}
|
||||
|
||||
void Controller_NPad::ConnectAllDisconnectedControllers() {
|
||||
std::for_each(connected_controllers.begin(), connected_controllers.end(),
|
||||
[](ControllerHolder& controller) {
|
||||
if (controller.type != NPadControllerType::None && !controller.is_connected) {
|
||||
controller.is_connected = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Controller_NPad::ClearAllControllers() {
|
||||
std::for_each(connected_controllers.begin(), connected_controllers.end(),
|
||||
[](ControllerHolder& controller) {
|
||||
controller.type = NPadControllerType::None;
|
||||
controller.is_connected = false;
|
||||
});
|
||||
}
|
||||
|
||||
bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
|
||||
const bool support_handheld =
|
||||
std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
|
||||
|
||||
@@ -5,13 +5,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Service::HID {
|
||||
|
||||
constexpr u32 NPAD_HANDHELD = 32;
|
||||
constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
|
||||
|
||||
class Controller_NPad final : public ControllerBase {
|
||||
public:
|
||||
Controller_NPad();
|
||||
@@ -107,11 +112,19 @@ public:
|
||||
Vibration GetLastVibration() const;
|
||||
|
||||
void AddNewController(NPadControllerType controller);
|
||||
void AddNewControllerAt(NPadControllerType controller, u32 npad_id);
|
||||
|
||||
void ConnectNPad(u32 npad_id);
|
||||
void DisconnectNPad(u32 npad_id);
|
||||
LedPattern GetLedPattern(u32 npad_id);
|
||||
void SetVibrationEnabled(bool can_vibrate);
|
||||
void ClearAllConnectedControllers();
|
||||
void DisconnectAllConnectedControllers();
|
||||
void ConnectAllDisconnectedControllers();
|
||||
void ClearAllControllers();
|
||||
|
||||
static std::size_t NPadIdToIndex(u32 npad_id);
|
||||
static u32 IndexToNPad(std::size_t index);
|
||||
|
||||
private:
|
||||
struct CommonHeader {
|
||||
@@ -164,8 +177,11 @@ private:
|
||||
BitField<23, 1, u64_le> r_stick_down;
|
||||
|
||||
// Not always active?
|
||||
BitField<24, 1, u64_le> sl;
|
||||
BitField<25, 1, u64_le> sr;
|
||||
BitField<24, 1, u64_le> left_sl;
|
||||
BitField<25, 1, u64_le> left_sr;
|
||||
|
||||
BitField<26, 1, u64_le> right_sl;
|
||||
BitField<27, 1, u64_le> right_sr;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
|
||||
@@ -189,12 +205,17 @@ private:
|
||||
};
|
||||
static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
|
||||
|
||||
struct GenericStates {
|
||||
s64_le timestamp;
|
||||
s64_le timestamp2;
|
||||
struct ControllerPad {
|
||||
ControllerPadState pad_states;
|
||||
AnalogPosition l_stick;
|
||||
AnalogPosition r_stick;
|
||||
};
|
||||
static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size");
|
||||
|
||||
struct GenericStates {
|
||||
s64_le timestamp;
|
||||
s64_le timestamp2;
|
||||
ControllerPad pad;
|
||||
ConnectionState connection_status;
|
||||
};
|
||||
static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
|
||||
@@ -266,15 +287,20 @@ private:
|
||||
static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
|
||||
|
||||
struct ControllerHolder {
|
||||
Controller_NPad::NPadControllerType type;
|
||||
NPadControllerType type;
|
||||
bool is_connected;
|
||||
};
|
||||
|
||||
NPadType style{};
|
||||
std::array<NPadEntry, 10> shared_memory_entries{};
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
|
||||
std::array<
|
||||
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
|
||||
10>
|
||||
buttons;
|
||||
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
|
||||
std::array<
|
||||
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
|
||||
10>
|
||||
sticks;
|
||||
std::vector<u32> supported_npad_id_types{};
|
||||
NpadHoldType hold_type{NpadHoldType::Vertical};
|
||||
Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
|
||||
@@ -285,5 +311,8 @@ private:
|
||||
void InitNewlyAddedControler(std::size_t controller_idx);
|
||||
bool IsControllerSupported(NPadControllerType controller) const;
|
||||
NPadControllerType DecideBestController(NPadControllerType priority) const;
|
||||
void RequestPadStateUpdate(u32 npad_id);
|
||||
std::array<ControllerPad, 10> npad_pad_states{};
|
||||
bool IsControllerSupported(NPadControllerType controller);
|
||||
};
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -41,16 +41,17 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
|
||||
|
||||
const auto [x, y, pressed] = touch_device->GetStatus();
|
||||
auto& touch_entry = cur_entry.states[0];
|
||||
if (pressed) {
|
||||
touch_entry.attribute.raw = 0;
|
||||
if (pressed && Settings::values.touchscreen.enabled) {
|
||||
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
|
||||
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
|
||||
touch_entry.diameter_x = 15;
|
||||
touch_entry.diameter_y = 15;
|
||||
touch_entry.rotation_angle = 0;
|
||||
touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
|
||||
touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
|
||||
touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
|
||||
const u64 tick = CoreTiming::GetTicks();
|
||||
touch_entry.delta_time = tick - last_touch;
|
||||
last_touch = tick;
|
||||
touch_entry.finger = 0;
|
||||
touch_entry.finger = Settings::values.touchscreen.finger;
|
||||
cur_entry.entry_count = 1;
|
||||
} else {
|
||||
cur_entry.entry_count = 0;
|
||||
@@ -60,6 +61,6 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
|
||||
}
|
||||
|
||||
void Controller_Touchscreen::OnLoadInputDevices() {
|
||||
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
|
||||
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
|
||||
}
|
||||
} // namespace Service::HID
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
@@ -29,9 +30,18 @@ public:
|
||||
void OnLoadInputDevices() override;
|
||||
|
||||
private:
|
||||
struct Attributes {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32_le> start_touch;
|
||||
BitField<1, 1, u32_le> end_touch;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
|
||||
|
||||
struct TouchState {
|
||||
u64_le delta_time;
|
||||
u32_le attribute;
|
||||
Attributes attribute;
|
||||
u32_le finger;
|
||||
u32_le x;
|
||||
u32_le y;
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
namespace Service::HID {
|
||||
|
||||
// Updating period for each HID device.
|
||||
// TODO(shinyquagsire23): These need better values.
|
||||
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
|
||||
// TODO(ogniK): Find actual polling rate of hid
|
||||
constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66;
|
||||
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
|
||||
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
|
||||
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
|
||||
|
||||
@@ -16,35 +16,18 @@
|
||||
|
||||
namespace Service::LDR {
|
||||
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
InvalidMemoryState = 51,
|
||||
InvalidNRO = 52,
|
||||
InvalidNRR = 53,
|
||||
MissingNRRHash = 54,
|
||||
MaximumNRO = 55,
|
||||
MaximumNRR = 56,
|
||||
AlreadyLoaded = 57,
|
||||
InvalidAlignment = 81,
|
||||
InvalidSize = 82,
|
||||
InvalidNROAddress = 84,
|
||||
InvalidNRRAddress = 85,
|
||||
NotInitialized = 87,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr ResultCode ERROR_INVALID_MEMORY_STATE(ErrorModule::Loader, ErrCodes::InvalidMemoryState);
|
||||
constexpr ResultCode ERROR_INVALID_NRO(ErrorModule::Loader, ErrCodes::InvalidNRO);
|
||||
constexpr ResultCode ERROR_INVALID_NRR(ErrorModule::Loader, ErrCodes::InvalidNRR);
|
||||
constexpr ResultCode ERROR_MISSING_NRR_HASH(ErrorModule::Loader, ErrCodes::MissingNRRHash);
|
||||
constexpr ResultCode ERROR_MAXIMUM_NRO(ErrorModule::Loader, ErrCodes::MaximumNRO);
|
||||
constexpr ResultCode ERROR_MAXIMUM_NRR(ErrorModule::Loader, ErrCodes::MaximumNRR);
|
||||
constexpr ResultCode ERROR_ALREADY_LOADED(ErrorModule::Loader, ErrCodes::AlreadyLoaded);
|
||||
constexpr ResultCode ERROR_INVALID_ALIGNMENT(ErrorModule::Loader, ErrCodes::InvalidAlignment);
|
||||
constexpr ResultCode ERROR_INVALID_SIZE(ErrorModule::Loader, ErrCodes::InvalidSize);
|
||||
constexpr ResultCode ERROR_INVALID_NRO_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNROAddress);
|
||||
constexpr ResultCode ERROR_INVALID_NRR_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNRRAddress);
|
||||
constexpr ResultCode ERROR_NOT_INITIALIZED(ErrorModule::Loader, ErrCodes::NotInitialized);
|
||||
constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
|
||||
constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
|
||||
constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
|
||||
constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
|
||||
constexpr ResultCode ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55};
|
||||
constexpr ResultCode ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56};
|
||||
constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
|
||||
constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
|
||||
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
|
||||
constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
|
||||
constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
|
||||
constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
|
||||
|
||||
constexpr u64 MAXIMUM_LOADED_RO = 0x40;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
ILogger() : ServiceFramework("ILogger") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, &ILogger::Initialize, "Initialize"},
|
||||
{0x00000001, nullptr, "SetDestination"},
|
||||
{0x00000001, &ILogger::SetDestination, "SetDestination"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
@@ -178,6 +178,17 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// This service function is intended to be used as a way to
|
||||
// redirect logging output to different destinations, however,
|
||||
// given we always want to see the logging output, it's sufficient
|
||||
// to do nothing and return success here.
|
||||
void SetDestination(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_LM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
std::ostringstream log_stream;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
|
||||
|
||||
namespace Service::Nvidia::Devices {
|
||||
@@ -33,6 +35,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
|
||||
return ZBCQueryTable(input, output);
|
||||
case IoctlCommand::IocFlushL2:
|
||||
return FlushL2(input, output);
|
||||
case IoctlCommand::IocGetGpuTime:
|
||||
return GetGpuTime(input, output);
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented ioctl");
|
||||
return 0;
|
||||
@@ -169,4 +173,13 @@ u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& outp
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
LOG_DEBUG(Service_NVDRV, "called");
|
||||
IoctlGetGpuTime params{};
|
||||
std::memcpy(¶ms, input.data(), input.size());
|
||||
params.gpu_time = CoreTiming::cyclesToNs(CoreTiming::GetTicks());
|
||||
std::memcpy(output.data(), ¶ms, output.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -156,6 +156,11 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size");
|
||||
|
||||
struct IoctlGetGpuTime {
|
||||
u64_le gpu_time;
|
||||
};
|
||||
static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size");
|
||||
|
||||
u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
@@ -164,6 +169,7 @@ private:
|
||||
u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -63,6 +63,17 @@ ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService
|
||||
return MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port));
|
||||
}
|
||||
|
||||
ResultCode ServiceManager::UnregisterService(const std::string& name) {
|
||||
CASCADE_CODE(ValidateServiceName(name));
|
||||
|
||||
const auto iter = registered_services.find(name);
|
||||
if (iter == registered_services.end())
|
||||
return ERR_SERVICE_NOT_REGISTERED;
|
||||
|
||||
registered_services.erase(iter);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort(
|
||||
const std::string& name) {
|
||||
|
||||
@@ -127,13 +138,52 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
void SM::RegisterService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto name_buf = rp.PopRaw<std::array<char, 8>>();
|
||||
const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
|
||||
const std::string name(name_buf.begin(), end);
|
||||
|
||||
const auto unk_bool = static_cast<bool>(rp.PopRaw<u32>());
|
||||
const auto session_count = rp.PopRaw<u32>();
|
||||
|
||||
LOG_DEBUG(Service_SM, "called with unk_bool={}", unk_bool);
|
||||
|
||||
auto handle = service_manager->RegisterService(name, session_count);
|
||||
if (handle.Failed()) {
|
||||
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}",
|
||||
handle.Code().raw);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(handle.Code());
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(handle.Code());
|
||||
rb.PushMoveObjects(std::move(handle).Unwrap());
|
||||
}
|
||||
|
||||
void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto name_buf = rp.PopRaw<std::array<char, 8>>();
|
||||
const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
|
||||
|
||||
const std::string name(name_buf.begin(), end);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(service_manager->UnregisterService(name));
|
||||
}
|
||||
|
||||
SM::SM(std::shared_ptr<ServiceManager> service_manager)
|
||||
: ServiceFramework("sm:", 4), service_manager(std::move(service_manager)) {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0x00000000, &SM::Initialize, "Initialize"},
|
||||
{0x00000001, &SM::GetService, "GetService"},
|
||||
{0x00000002, nullptr, "RegisterService"},
|
||||
{0x00000003, nullptr, "UnregisterService"},
|
||||
{0x00000002, &SM::RegisterService, "RegisterService"},
|
||||
{0x00000003, &SM::UnregisterService, "UnregisterService"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ public:
|
||||
private:
|
||||
void Initialize(Kernel::HLERequestContext& ctx);
|
||||
void GetService(Kernel::HLERequestContext& ctx);
|
||||
void RegisterService(Kernel::HLERequestContext& ctx);
|
||||
void UnregisterService(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<ServiceManager> service_manager;
|
||||
};
|
||||
@@ -48,6 +50,7 @@ public:
|
||||
|
||||
ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name,
|
||||
unsigned int max_sessions);
|
||||
ResultCode UnregisterService(const std::string& name);
|
||||
ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name);
|
||||
ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ConnectToService(const std::string& name);
|
||||
|
||||
|
||||
@@ -510,7 +510,11 @@ private:
|
||||
|
||||
if (transaction == TransactionId::Connect) {
|
||||
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
|
||||
IGBPConnectResponseParcel response{1280, 720};
|
||||
IGBPConnectResponseParcel response{
|
||||
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) *
|
||||
Settings::values.resolution_factor),
|
||||
static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
|
||||
Settings::values.resolution_factor)};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
} else if (transaction == TransactionId::SetPreallocatedBuffer) {
|
||||
IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
|
||||
@@ -692,11 +696,15 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
} else {
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
}
|
||||
|
||||
rb.PushRaw<float>(60.0f);
|
||||
@@ -901,11 +909,15 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
if (Settings::values.use_docked_mode) {
|
||||
rb.Push(static_cast<u64>(DisplayResolution::DockedWidth));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::DockedHeight));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::DockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::DockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
} else {
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
|
||||
static_cast<u32>(Settings::values.resolution_factor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -922,6 +934,8 @@ private:
|
||||
void ListDisplays(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
DisplayInfo display_info;
|
||||
display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
|
||||
display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
|
||||
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -10,6 +10,56 @@
|
||||
|
||||
namespace Settings {
|
||||
|
||||
namespace NativeButton {
|
||||
const std::array<const char*, NumButtons> mapping = {{
|
||||
"button_a",
|
||||
"button_b",
|
||||
"button_x",
|
||||
"button_y",
|
||||
"button_lstick",
|
||||
"button_rstick",
|
||||
"button_l",
|
||||
"button_r",
|
||||
"button_zl",
|
||||
"button_zr",
|
||||
"button_plus",
|
||||
"button_minus",
|
||||
"button_dleft",
|
||||
"button_dup",
|
||||
"button_dright",
|
||||
"button_ddown",
|
||||
"button_lstick_left",
|
||||
"button_lstick_up",
|
||||
"button_lstick_right",
|
||||
"button_lstick_down",
|
||||
"button_rstick_left",
|
||||
"button_rstick_up",
|
||||
"button_rstick_right",
|
||||
"button_rstick_down",
|
||||
"button_sl",
|
||||
"button_sr",
|
||||
"button_home",
|
||||
"button_screenshot",
|
||||
}};
|
||||
}
|
||||
|
||||
namespace NativeAnalog {
|
||||
const std::array<const char*, NumAnalogs> mapping = {{
|
||||
"lstick",
|
||||
"rstick",
|
||||
}};
|
||||
}
|
||||
|
||||
namespace NativeMouseButton {
|
||||
const std::array<const char*, NumMouseButtons> mapping = {{
|
||||
"left",
|
||||
"right",
|
||||
"middle",
|
||||
"forward",
|
||||
"back",
|
||||
}};
|
||||
}
|
||||
|
||||
Values values = {};
|
||||
|
||||
void Apply() {
|
||||
|
||||
@@ -60,36 +60,7 @@ constexpr int BUTTON_NS_END = NumButtons;
|
||||
constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
|
||||
constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
|
||||
|
||||
static const std::array<const char*, NumButtons> mapping = {{
|
||||
"button_a",
|
||||
"button_b",
|
||||
"button_x",
|
||||
"button_y",
|
||||
"button_lstick",
|
||||
"button_rstick",
|
||||
"button_l",
|
||||
"button_r",
|
||||
"button_zl",
|
||||
"button_zr",
|
||||
"button_plus",
|
||||
"button_minus",
|
||||
"button_dleft",
|
||||
"button_dup",
|
||||
"button_dright",
|
||||
"button_ddown",
|
||||
"button_lstick_left",
|
||||
"button_lstick_up",
|
||||
"button_lstick_right",
|
||||
"button_lstick_down",
|
||||
"button_rstick_left",
|
||||
"button_rstick_up",
|
||||
"button_rstick_right",
|
||||
"button_rstick_down",
|
||||
"button_sl",
|
||||
"button_sr",
|
||||
"button_home",
|
||||
"button_screenshot",
|
||||
}};
|
||||
extern const std::array<const char*, NumButtons> mapping;
|
||||
|
||||
} // namespace NativeButton
|
||||
|
||||
@@ -105,12 +76,273 @@ constexpr int STICK_HID_BEGIN = LStick;
|
||||
constexpr int STICK_HID_END = NumAnalogs;
|
||||
constexpr int NUM_STICKS_HID = NumAnalogs;
|
||||
|
||||
static const std::array<const char*, NumAnalogs> mapping = {{
|
||||
"lstick",
|
||||
"rstick",
|
||||
}};
|
||||
extern const std::array<const char*, NumAnalogs> mapping;
|
||||
} // namespace NativeAnalog
|
||||
|
||||
namespace NativeMouseButton {
|
||||
enum Values {
|
||||
Left,
|
||||
Right,
|
||||
Middle,
|
||||
Forward,
|
||||
Back,
|
||||
|
||||
NumMouseButtons,
|
||||
};
|
||||
|
||||
constexpr int MOUSE_HID_BEGIN = Left;
|
||||
constexpr int MOUSE_HID_END = NumMouseButtons;
|
||||
constexpr int NUM_MOUSE_HID = NumMouseButtons;
|
||||
|
||||
extern const std::array<const char*, NumMouseButtons> mapping;
|
||||
} // namespace NativeMouseButton
|
||||
|
||||
namespace NativeKeyboard {
|
||||
enum Keys {
|
||||
None,
|
||||
Error,
|
||||
|
||||
A = 4,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E,
|
||||
F,
|
||||
G,
|
||||
H,
|
||||
I,
|
||||
J,
|
||||
K,
|
||||
L,
|
||||
M,
|
||||
N,
|
||||
O,
|
||||
P,
|
||||
Q,
|
||||
R,
|
||||
S,
|
||||
T,
|
||||
U,
|
||||
V,
|
||||
W,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
N1,
|
||||
N2,
|
||||
N3,
|
||||
N4,
|
||||
N5,
|
||||
N6,
|
||||
N7,
|
||||
N8,
|
||||
N9,
|
||||
N0,
|
||||
Enter,
|
||||
Escape,
|
||||
Backspace,
|
||||
Tab,
|
||||
Space,
|
||||
Minus,
|
||||
Equal,
|
||||
LeftBrace,
|
||||
RightBrace,
|
||||
Backslash,
|
||||
Tilde,
|
||||
Semicolon,
|
||||
Apostrophe,
|
||||
Grave,
|
||||
Comma,
|
||||
Dot,
|
||||
Slash,
|
||||
CapsLockKey,
|
||||
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
|
||||
SystemRequest,
|
||||
ScrollLockKey,
|
||||
Pause,
|
||||
Insert,
|
||||
Home,
|
||||
PageUp,
|
||||
Delete,
|
||||
End,
|
||||
PageDown,
|
||||
Right,
|
||||
Left,
|
||||
Down,
|
||||
Up,
|
||||
|
||||
NumLockKey,
|
||||
KPSlash,
|
||||
KPAsterisk,
|
||||
KPMinus,
|
||||
KPPlus,
|
||||
KPEnter,
|
||||
KP1,
|
||||
KP2,
|
||||
KP3,
|
||||
KP4,
|
||||
KP5,
|
||||
KP6,
|
||||
KP7,
|
||||
KP8,
|
||||
KP9,
|
||||
KP0,
|
||||
KPDot,
|
||||
|
||||
Key102,
|
||||
Compose,
|
||||
Power,
|
||||
KPEqual,
|
||||
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
F16,
|
||||
F17,
|
||||
F18,
|
||||
F19,
|
||||
F20,
|
||||
F21,
|
||||
F22,
|
||||
F23,
|
||||
F24,
|
||||
|
||||
Open,
|
||||
Help,
|
||||
Properties,
|
||||
Front,
|
||||
Stop,
|
||||
Repeat,
|
||||
Undo,
|
||||
Cut,
|
||||
Copy,
|
||||
Paste,
|
||||
Find,
|
||||
Mute,
|
||||
VolumeUp,
|
||||
VolumeDown,
|
||||
CapsLockActive,
|
||||
NumLockActive,
|
||||
ScrollLockActive,
|
||||
KPComma,
|
||||
|
||||
KPLeftParenthesis,
|
||||
KPRightParenthesis,
|
||||
|
||||
LeftControlKey = 0xE0,
|
||||
LeftShiftKey,
|
||||
LeftAltKey,
|
||||
LeftMetaKey,
|
||||
RightControlKey,
|
||||
RightShiftKey,
|
||||
RightAltKey,
|
||||
RightMetaKey,
|
||||
|
||||
MediaPlayPause,
|
||||
MediaStopCD,
|
||||
MediaPrevious,
|
||||
MediaNext,
|
||||
MediaEject,
|
||||
MediaVolumeUp,
|
||||
MediaVolumeDown,
|
||||
MediaMute,
|
||||
MediaWebsite,
|
||||
MediaBack,
|
||||
MediaForward,
|
||||
MediaStop,
|
||||
MediaFind,
|
||||
MediaScrollUp,
|
||||
MediaScrollDown,
|
||||
MediaEdit,
|
||||
MediaSleep,
|
||||
MediaCoffee,
|
||||
MediaRefresh,
|
||||
MediaCalculator,
|
||||
|
||||
NumKeyboardKeys,
|
||||
};
|
||||
|
||||
static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
|
||||
|
||||
enum Modifiers {
|
||||
LeftControl,
|
||||
LeftShift,
|
||||
LeftAlt,
|
||||
LeftMeta,
|
||||
RightControl,
|
||||
RightShift,
|
||||
RightAlt,
|
||||
RightMeta,
|
||||
CapsLock,
|
||||
ScrollLock,
|
||||
NumLock,
|
||||
|
||||
NumKeyboardMods,
|
||||
};
|
||||
|
||||
constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
|
||||
constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
|
||||
constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
|
||||
|
||||
constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
|
||||
constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
|
||||
constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
|
||||
|
||||
} // namespace NativeKeyboard
|
||||
|
||||
using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
|
||||
using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
|
||||
using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
|
||||
using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
|
||||
using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
|
||||
|
||||
constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
|
||||
constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
|
||||
constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
|
||||
constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
|
||||
|
||||
enum class ControllerType {
|
||||
ProController,
|
||||
DualJoycon,
|
||||
RightJoycon,
|
||||
LeftJoycon,
|
||||
};
|
||||
|
||||
struct PlayerInput {
|
||||
bool connected;
|
||||
ControllerType type;
|
||||
ButtonsRaw buttons;
|
||||
AnalogsRaw analogs;
|
||||
|
||||
u32 body_color_right;
|
||||
u32 button_color_right;
|
||||
u32 body_color_left;
|
||||
u32 button_color_left;
|
||||
};
|
||||
|
||||
struct TouchscreenInput {
|
||||
bool enabled;
|
||||
std::string device;
|
||||
|
||||
u32 finger;
|
||||
u32 diameter_x;
|
||||
u32 diameter_y;
|
||||
u32 rotation_angle;
|
||||
};
|
||||
|
||||
struct Values {
|
||||
// System
|
||||
bool use_docked_mode;
|
||||
@@ -120,10 +352,22 @@ struct Values {
|
||||
s32 language_index;
|
||||
|
||||
// Controls
|
||||
std::array<std::string, NativeButton::NumButtons> buttons;
|
||||
std::array<std::string, NativeAnalog::NumAnalogs> analogs;
|
||||
std::array<PlayerInput, 10> players;
|
||||
|
||||
bool mouse_enabled;
|
||||
std::string mouse_device;
|
||||
MouseButtonsRaw mouse_buttons;
|
||||
|
||||
bool keyboard_enabled;
|
||||
KeyboardKeysRaw keyboard_keys;
|
||||
KeyboardModsRaw keyboard_mods;
|
||||
|
||||
bool debug_pad_enabled;
|
||||
ButtonsRaw debug_pad_buttons;
|
||||
AnalogsRaw debug_pad_analogs;
|
||||
|
||||
std::string motion_device;
|
||||
std::string touch_device;
|
||||
TouchscreenInput touchscreen;
|
||||
std::atomic_bool is_device_reload_pending{true};
|
||||
|
||||
// Core
|
||||
@@ -159,6 +403,7 @@ struct Values {
|
||||
bool use_gdbstub;
|
||||
u16 gdbstub_port;
|
||||
std::string program_args;
|
||||
bool dump_exefs;
|
||||
bool dump_nso;
|
||||
|
||||
// WebService
|
||||
|
||||
@@ -69,6 +69,15 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
// TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
|
||||
// register carrying a default value. Assume it's OpenGL's default (1).
|
||||
regs.point_size = 1.0f;
|
||||
|
||||
// TODO(bunnei): Some games do not initialize the color masks (e.g. Sonic Mania). Assuming a
|
||||
// default of enabled fixes rendering here.
|
||||
for (std::size_t color_mask = 0; color_mask < Regs::NumRenderTargets; color_mask++) {
|
||||
regs.color_mask[color_mask].R.Assign(1);
|
||||
regs.color_mask[color_mask].G.Assign(1);
|
||||
regs.color_mask[color_mask].B.Assign(1);
|
||||
regs.color_mask[color_mask].A.Assign(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
|
||||
|
||||
@@ -389,6 +389,13 @@ public:
|
||||
ReverseSubtract = 3,
|
||||
Min = 4,
|
||||
Max = 5,
|
||||
|
||||
// These values are used by Nouveau and some games.
|
||||
AddGL = 0x8006,
|
||||
SubtractGL = 0x8007,
|
||||
ReverseSubtractGL = 0x8008,
|
||||
MinGL = 0x800a,
|
||||
MaxGL = 0x800b
|
||||
};
|
||||
|
||||
enum class Factor : u32 {
|
||||
@@ -624,7 +631,16 @@ public:
|
||||
}
|
||||
} zeta;
|
||||
|
||||
INSERT_PADDING_WORDS(0x5B);
|
||||
INSERT_PADDING_WORDS(0x41);
|
||||
|
||||
union {
|
||||
BitField<0, 4, u32> stencil;
|
||||
BitField<4, 4, u32> unknown;
|
||||
BitField<8, 4, u32> scissor;
|
||||
BitField<12, 4, u32> viewport;
|
||||
} clear_flags;
|
||||
|
||||
INSERT_PADDING_WORDS(0x19);
|
||||
|
||||
std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format;
|
||||
|
||||
@@ -1127,6 +1143,7 @@ ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
|
||||
ASSERT_REG_POSITION(color_mask_common, 0x3E4);
|
||||
ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
|
||||
ASSERT_REG_POSITION(zeta, 0x3F8);
|
||||
ASSERT_REG_POSITION(clear_flags, 0x43E);
|
||||
ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
|
||||
ASSERT_REG_POSITION(rt_control, 0x487);
|
||||
ASSERT_REG_POSITION(zeta_width, 0x48a);
|
||||
|
||||
@@ -153,6 +153,7 @@ enum class PredCondition : u64 {
|
||||
NotEqual = 5,
|
||||
GreaterEqual = 6,
|
||||
LessThanWithNan = 9,
|
||||
LessEqualWithNan = 11,
|
||||
GreaterThanWithNan = 12,
|
||||
NotEqualWithNan = 13,
|
||||
GreaterEqualWithNan = 14,
|
||||
@@ -261,7 +262,7 @@ enum class FlowCondition : u64 {
|
||||
Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for?
|
||||
};
|
||||
|
||||
enum class ControlCode : u64 {
|
||||
enum class ConditionCode : u64 {
|
||||
F = 0,
|
||||
LT = 1,
|
||||
EQ = 2,
|
||||
@@ -569,7 +570,6 @@ union Instruction {
|
||||
BitField<39, 2, u64> tab5cb8_2;
|
||||
BitField<41, 3, u64> tab5c68_1;
|
||||
BitField<44, 2, u64> tab5c68_0;
|
||||
BitField<47, 1, u64> cc;
|
||||
BitField<48, 1, u64> negate_b;
|
||||
} fmul;
|
||||
|
||||
@@ -831,7 +831,7 @@ union Instruction {
|
||||
union {
|
||||
BitField<0, 3, u64> pred0;
|
||||
BitField<3, 3, u64> pred3;
|
||||
BitField<8, 5, ControlCode> cc; // flag in cc
|
||||
BitField<8, 5, ConditionCode> cc; // flag in cc
|
||||
BitField<39, 3, u64> pred39;
|
||||
BitField<42, 1, u64> neg_pred39;
|
||||
BitField<45, 4, PredOperation> op; // op with pred39
|
||||
@@ -1235,7 +1235,7 @@ union Instruction {
|
||||
BitField<60, 1, u64> is_b_gpr;
|
||||
BitField<59, 1, u64> is_c_gpr;
|
||||
BitField<20, 24, s64> smem_imm;
|
||||
BitField<0, 5, ControlCode> flow_control_code;
|
||||
BitField<0, 5, ConditionCode> flow_condition_code;
|
||||
|
||||
Attribute attribute;
|
||||
Sampler sampler;
|
||||
|
||||
@@ -17,6 +17,8 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ABGR8:
|
||||
return 4;
|
||||
default:
|
||||
return 4;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
|
||||
@@ -35,6 +35,7 @@ void MacroInterpreter::Reset() {
|
||||
// The next parameter index starts at 1, because $r1 already has the value of the first
|
||||
// parameter.
|
||||
next_parameter_index = 1;
|
||||
carry_flag = false;
|
||||
}
|
||||
|
||||
bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
|
||||
@@ -135,14 +136,28 @@ MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const {
|
||||
return {macro_memory[offset + pc / sizeof(u32)]};
|
||||
}
|
||||
|
||||
u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const {
|
||||
u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) {
|
||||
switch (operation) {
|
||||
case ALUOperation::Add:
|
||||
return src_a + src_b;
|
||||
// TODO(Subv): Implement AddWithCarry
|
||||
case ALUOperation::Subtract:
|
||||
return src_a - src_b;
|
||||
// TODO(Subv): Implement SubtractWithBorrow
|
||||
case ALUOperation::Add: {
|
||||
const u64 result{static_cast<u64>(src_a) + src_b};
|
||||
carry_flag = result > 0xffffffff;
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
case ALUOperation::AddWithCarry: {
|
||||
const u64 result{static_cast<u64>(src_a) + src_b + (carry_flag ? 1ULL : 0ULL)};
|
||||
carry_flag = result > 0xffffffff;
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
case ALUOperation::Subtract: {
|
||||
const u64 result{static_cast<u64>(src_a) - src_b};
|
||||
carry_flag = result < 0x100000000;
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
case ALUOperation::SubtractWithBorrow: {
|
||||
const u64 result{static_cast<u64>(src_a) - src_b - (carry_flag ? 0ULL : 1ULL)};
|
||||
carry_flag = result < 0x100000000;
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
case ALUOperation::Xor:
|
||||
return src_a ^ src_b;
|
||||
case ALUOperation::Or:
|
||||
|
||||
@@ -117,7 +117,7 @@ private:
|
||||
bool Step(u32 offset, bool is_delay_slot);
|
||||
|
||||
/// Calculates the result of an ALU operation. src_a OP src_b;
|
||||
u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const;
|
||||
u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b);
|
||||
|
||||
/// Performs the result operation on the input result and stores it in the specified register
|
||||
/// (if necessary).
|
||||
@@ -165,5 +165,7 @@ private:
|
||||
std::vector<u32> parameters;
|
||||
/// Index of the next parameter that will be fetched by the 'parm' instruction.
|
||||
u32 next_parameter_index = 0;
|
||||
|
||||
bool carry_flag{};
|
||||
};
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -98,14 +98,9 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
|
||||
has_ARB_direct_state_access = true;
|
||||
} else if (extension == "GL_ARB_multi_bind") {
|
||||
has_ARB_multi_bind = true;
|
||||
} else if (extension == "GL_ARB_separate_shader_objects") {
|
||||
has_ARB_separate_shader_objects = true;
|
||||
} else if (extension == "GL_ARB_vertex_attrib_binding") {
|
||||
has_ARB_vertex_attrib_binding = true;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
|
||||
OpenGLState::ApplyDefaultState();
|
||||
|
||||
// Create render framebuffer
|
||||
@@ -542,6 +537,30 @@ void RasterizerOpenGL::Clear() {
|
||||
ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!");
|
||||
use_stencil = true;
|
||||
clear_state.stencil.test_enabled = true;
|
||||
if (regs.clear_flags.stencil) {
|
||||
// Stencil affects the clear so fill it with the used masks
|
||||
clear_state.stencil.front.test_func = GL_ALWAYS;
|
||||
clear_state.stencil.front.test_mask = regs.stencil_front_func_mask;
|
||||
clear_state.stencil.front.action_stencil_fail = GL_KEEP;
|
||||
clear_state.stencil.front.action_depth_fail = GL_KEEP;
|
||||
clear_state.stencil.front.action_depth_pass = GL_KEEP;
|
||||
clear_state.stencil.front.write_mask = regs.stencil_front_mask;
|
||||
if (regs.stencil_two_side_enable) {
|
||||
clear_state.stencil.back.test_func = GL_ALWAYS;
|
||||
clear_state.stencil.back.test_mask = regs.stencil_back_func_mask;
|
||||
clear_state.stencil.back.action_stencil_fail = GL_KEEP;
|
||||
clear_state.stencil.back.action_depth_fail = GL_KEEP;
|
||||
clear_state.stencil.back.action_depth_pass = GL_KEEP;
|
||||
clear_state.stencil.back.write_mask = regs.stencil_back_mask;
|
||||
} else {
|
||||
clear_state.stencil.back.test_func = GL_ALWAYS;
|
||||
clear_state.stencil.back.test_mask = 0xFFFFFFFF;
|
||||
clear_state.stencil.back.write_mask = 0xFFFFFFFF;
|
||||
clear_state.stencil.back.action_stencil_fail = GL_KEEP;
|
||||
clear_state.stencil.back.action_depth_fail = GL_KEEP;
|
||||
clear_state.stencil.back.action_depth_pass = GL_KEEP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_color && !use_depth && !use_stencil) {
|
||||
@@ -553,6 +572,14 @@ void RasterizerOpenGL::Clear() {
|
||||
|
||||
ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false,
|
||||
regs.clear_buffers.RT.Value());
|
||||
if (regs.clear_flags.scissor) {
|
||||
SyncScissorTest(clear_state);
|
||||
}
|
||||
|
||||
if (regs.clear_flags.viewport) {
|
||||
clear_state.EmulateViewportWithScissor();
|
||||
}
|
||||
|
||||
clear_state.Apply();
|
||||
|
||||
if (use_color) {
|
||||
@@ -588,7 +615,7 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
SyncLogicOpState();
|
||||
SyncCullMode();
|
||||
SyncPrimitiveRestart();
|
||||
SyncScissorTest();
|
||||
SyncScissorTest(state);
|
||||
// Alpha Testing is synced on shaders.
|
||||
SyncTransformFeedback();
|
||||
SyncPointState();
|
||||
@@ -815,7 +842,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
|
||||
}
|
||||
const u32 bias = config.mip_lod_bias.Value();
|
||||
// Sign extend the 13-bit value.
|
||||
const u32 mask = 1U << (13 - 1);
|
||||
constexpr u32 mask = 1U << (13 - 1);
|
||||
const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f;
|
||||
if (lod_bias != bias_lod) {
|
||||
lod_bias = bias_lod;
|
||||
@@ -947,8 +974,8 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
|
||||
auto& viewport = current_state.viewports[i];
|
||||
viewport.x = viewport_rect.left;
|
||||
viewport.y = viewport_rect.bottom;
|
||||
viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth());
|
||||
viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight());
|
||||
viewport.width = viewport_rect.GetWidth();
|
||||
viewport.height = viewport_rect.GetHeight();
|
||||
viewport.depth_range_far = regs.viewports[i].depth_range_far;
|
||||
viewport.depth_range_near = regs.viewports[i].depth_range_near;
|
||||
}
|
||||
@@ -1120,11 +1147,11 @@ void RasterizerOpenGL::SyncLogicOpState() {
|
||||
state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncScissorTest() {
|
||||
void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
|
||||
const auto& src = regs.scissor_test[i];
|
||||
auto& dst = state.viewports[i].scissor;
|
||||
auto& dst = current_state.viewports[i].scissor;
|
||||
dst.enabled = (src.enable != 0);
|
||||
if (dst.enabled == 0) {
|
||||
return;
|
||||
|
||||
@@ -91,19 +91,20 @@ private:
|
||||
void SyncWithConfig(const Tegra::Texture::TSCEntry& info);
|
||||
|
||||
private:
|
||||
Tegra::Texture::TextureFilter mag_filter;
|
||||
Tegra::Texture::TextureFilter min_filter;
|
||||
Tegra::Texture::TextureMipmapFilter mip_filter;
|
||||
Tegra::Texture::WrapMode wrap_u;
|
||||
Tegra::Texture::WrapMode wrap_v;
|
||||
Tegra::Texture::WrapMode wrap_p;
|
||||
bool uses_depth_compare;
|
||||
Tegra::Texture::DepthCompareFunc depth_compare_func;
|
||||
GLvec4 border_color;
|
||||
float min_lod;
|
||||
float max_lod;
|
||||
float lod_bias;
|
||||
float max_anisotropic;
|
||||
Tegra::Texture::TextureFilter mag_filter = Tegra::Texture::TextureFilter::Nearest;
|
||||
Tegra::Texture::TextureFilter min_filter = Tegra::Texture::TextureFilter::Nearest;
|
||||
Tegra::Texture::TextureMipmapFilter mip_filter = Tegra::Texture::TextureMipmapFilter::None;
|
||||
Tegra::Texture::WrapMode wrap_u = Tegra::Texture::WrapMode::ClampToEdge;
|
||||
Tegra::Texture::WrapMode wrap_v = Tegra::Texture::WrapMode::ClampToEdge;
|
||||
Tegra::Texture::WrapMode wrap_p = Tegra::Texture::WrapMode::ClampToEdge;
|
||||
bool uses_depth_compare = false;
|
||||
Tegra::Texture::DepthCompareFunc depth_compare_func =
|
||||
Tegra::Texture::DepthCompareFunc::Always;
|
||||
GLvec4 border_color = {};
|
||||
float min_lod = 0.0f;
|
||||
float max_lod = 16.0f;
|
||||
float lod_bias = 0.0f;
|
||||
float max_anisotropic = 1.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -171,7 +172,7 @@ private:
|
||||
void SyncMultiSampleState();
|
||||
|
||||
/// Syncs the scissor test state to match the guest state
|
||||
void SyncScissorTest();
|
||||
void SyncScissorTest(OpenGLState& current_state);
|
||||
|
||||
/// Syncs the transform feedback state to match the guest state
|
||||
void SyncTransformFeedback();
|
||||
@@ -187,8 +188,6 @@ private:
|
||||
|
||||
bool has_ARB_direct_state_access = false;
|
||||
bool has_ARB_multi_bind = false;
|
||||
bool has_ARB_separate_shader_objects = false;
|
||||
bool has_ARB_vertex_attrib_binding = false;
|
||||
|
||||
OpenGLState state;
|
||||
|
||||
|
||||
@@ -265,11 +265,11 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
|
||||
{GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
||||
true}, // DXN2UNORM
|
||||
{GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM
|
||||
{GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
||||
{GL_COMPRESSED_RGBA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
||||
true}, // BC7U
|
||||
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8,
|
||||
ComponentType::Float, true}, // BC6H_UF16
|
||||
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
|
||||
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
|
||||
true}, // BC6H_UF16
|
||||
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
|
||||
true}, // BC6H_SF16
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
|
||||
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U
|
||||
@@ -306,8 +306,8 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
|
||||
true}, // DXT23_SRGB
|
||||
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
||||
true}, // DXT45_SRGB
|
||||
{GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8,
|
||||
ComponentType::UNorm, true}, // BC7U_SRGB
|
||||
{GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
|
||||
true}, // BC7U_SRGB
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB
|
||||
@@ -346,7 +346,7 @@ static GLenum SurfaceTargetToGL(SurfaceTarget target) {
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
return GL_TEXTURE_CUBE_MAP;
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
return GL_TEXTURE_CUBE_MAP_ARRAY_ARB;
|
||||
return GL_TEXTURE_CUBE_MAP_ARRAY;
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
|
||||
UNREACHABLE();
|
||||
@@ -726,7 +726,7 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
|
||||
const std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
|
||||
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW);
|
||||
if (source_format.compressed) {
|
||||
glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment,
|
||||
static_cast<GLsizei>(src_params.size_in_bytes), nullptr);
|
||||
@@ -1275,6 +1275,31 @@ Surface RasterizerCacheOpenGL::GetUncachedSurface(const SurfaceParams& params) {
|
||||
return surface;
|
||||
}
|
||||
|
||||
void RasterizerCacheOpenGL::FastLayeredCopySurface(const Surface& src_surface,
|
||||
const Surface& dst_surface) {
|
||||
const auto& init_params{src_surface->GetSurfaceParams()};
|
||||
const auto& dst_params{dst_surface->GetSurfaceParams()};
|
||||
VAddr address = init_params.addr;
|
||||
const std::size_t layer_size = dst_params.LayerMemorySize();
|
||||
for (u32 layer = 0; layer < dst_params.depth; layer++) {
|
||||
for (u32 mipmap = 0; mipmap < dst_params.max_mip_level; mipmap++) {
|
||||
const VAddr sub_address = address + dst_params.GetMipmapLevelOffset(mipmap);
|
||||
const Surface& copy = TryGet(sub_address);
|
||||
if (!copy)
|
||||
continue;
|
||||
const auto& src_params{copy->GetSurfaceParams()};
|
||||
const u32 width{std::min(src_params.width, dst_params.MipWidth(mipmap))};
|
||||
const u32 height{std::min(src_params.height, dst_params.MipHeight(mipmap))};
|
||||
|
||||
glCopyImageSubData(copy->Texture().handle, SurfaceTargetToGL(src_params.target), 0, 0,
|
||||
0, 0, dst_surface->Texture().handle,
|
||||
SurfaceTargetToGL(dst_params.target), mipmap, 0, 0, layer, width,
|
||||
height, 1);
|
||||
}
|
||||
address += layer_size;
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerCacheOpenGL::FermiCopySurface(
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) {
|
||||
@@ -1340,11 +1365,13 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
|
||||
CopySurface(old_surface, new_surface, copy_pbo.handle);
|
||||
}
|
||||
break;
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
case SurfaceTarget::Texture3D:
|
||||
AccurateCopySurface(old_surface, new_surface);
|
||||
break;
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
AccurateCopySurface(old_surface, new_surface);
|
||||
FastLayeredCopySurface(old_surface, new_surface);
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
|
||||
|
||||
@@ -350,6 +350,7 @@ private:
|
||||
|
||||
/// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data
|
||||
void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface);
|
||||
void FastLayeredCopySurface(const Surface& src_surface, const Surface& dst_surface);
|
||||
|
||||
/// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
|
||||
/// previously been used. This is to prevent surfaces from being constantly created and
|
||||
|
||||
@@ -84,6 +84,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
|
||||
}
|
||||
|
||||
entries = program_result.second;
|
||||
shader_length = entries.shader_length;
|
||||
|
||||
if (program_type != Maxwell::ShaderProgram::Geometry) {
|
||||
OGLShader shader;
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
}
|
||||
|
||||
std::size_t GetSizeInBytes() const override {
|
||||
return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64);
|
||||
return shader_length;
|
||||
}
|
||||
|
||||
// We do not have to flush this cache as things in it are never modified by us.
|
||||
@@ -82,6 +82,7 @@ private:
|
||||
u32 max_vertices, const std::string& debug_name);
|
||||
|
||||
VAddr addr;
|
||||
std::size_t shader_length;
|
||||
Maxwell::ShaderProgram program_type;
|
||||
GLShader::ShaderSetup setup;
|
||||
GLShader::ShaderEntries entries;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -163,6 +163,7 @@ private:
|
||||
struct ShaderEntries {
|
||||
std::vector<ConstBufferEntry> const_buffer_entries;
|
||||
std::vector<SamplerEntry> texture_samplers;
|
||||
std::size_t shader_length;
|
||||
};
|
||||
|
||||
using ProgramResult = std::pair<std::string, ShaderEntries>;
|
||||
|
||||
@@ -233,6 +233,28 @@ void OpenGLState::ApplyStencilTest() const {
|
||||
config_stencil(GL_BACK, stencil.back, cur_state.stencil.back);
|
||||
}
|
||||
}
|
||||
// Viewport does not affects glClearBuffer so emulate viewport using scissor test
|
||||
void OpenGLState::EmulateViewportWithScissor() {
|
||||
auto& current = viewports[0];
|
||||
if (current.scissor.enabled) {
|
||||
const GLint left = std::max(current.x, current.scissor.x);
|
||||
const GLint right =
|
||||
std::max(current.x + current.width, current.scissor.x + current.scissor.width);
|
||||
const GLint bottom = std::max(current.y, current.scissor.y);
|
||||
const GLint top =
|
||||
std::max(current.y + current.height, current.scissor.y + current.scissor.height);
|
||||
current.scissor.x = std::max(left, 0);
|
||||
current.scissor.y = std::max(bottom, 0);
|
||||
current.scissor.width = std::max(right - left, 0);
|
||||
current.scissor.height = std::max(top - bottom, 0);
|
||||
} else {
|
||||
current.scissor.enabled = true;
|
||||
current.scissor.x = current.x;
|
||||
current.scissor.y = current.y;
|
||||
current.scissor.width = current.width;
|
||||
current.scissor.height = current.height;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyViewport() const {
|
||||
if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) {
|
||||
@@ -242,7 +264,9 @@ void OpenGLState::ApplyViewport() const {
|
||||
const auto& updated = viewports[i];
|
||||
if (updated.x != current.x || updated.y != current.y ||
|
||||
updated.width != current.width || updated.height != current.height) {
|
||||
glViewportIndexedf(i, updated.x, updated.y, updated.width, updated.height);
|
||||
glViewportIndexedf(
|
||||
i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y),
|
||||
static_cast<GLfloat>(updated.width), static_cast<GLfloat>(updated.height));
|
||||
}
|
||||
if (updated.depth_range_near != current.depth_range_near ||
|
||||
updated.depth_range_far != current.depth_range_far) {
|
||||
@@ -270,8 +294,7 @@ void OpenGLState::ApplyViewport() const {
|
||||
const auto& updated = viewports[0];
|
||||
if (updated.x != current.x || updated.y != current.y || updated.width != current.width ||
|
||||
updated.height != current.height) {
|
||||
glViewport(static_cast<GLint>(updated.x), static_cast<GLint>(updated.y),
|
||||
static_cast<GLsizei>(updated.width), static_cast<GLsizei>(updated.height));
|
||||
glViewport(updated.x, updated.y, updated.width, updated.height);
|
||||
}
|
||||
if (updated.depth_range_near != current.depth_range_near ||
|
||||
updated.depth_range_far != current.depth_range_far) {
|
||||
@@ -339,14 +362,14 @@ void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
|
||||
if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
|
||||
updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
|
||||
updated.dst_a_func != current.dst_a_func) {
|
||||
glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func,
|
||||
updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
|
||||
glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func,
|
||||
updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
|
||||
}
|
||||
|
||||
if (blend_changed || updated.rgb_equation != current.rgb_equation ||
|
||||
updated.a_equation != current.a_equation) {
|
||||
glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
|
||||
updated.a_equation);
|
||||
glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation,
|
||||
updated.a_equation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,10 +156,10 @@ public:
|
||||
} draw;
|
||||
|
||||
struct viewport {
|
||||
GLfloat x;
|
||||
GLfloat y;
|
||||
GLfloat width;
|
||||
GLfloat height;
|
||||
GLint x;
|
||||
GLint y;
|
||||
GLint width;
|
||||
GLint height;
|
||||
GLfloat depth_range_near; // GL_DEPTH_RANGE
|
||||
GLfloat depth_range_far; // GL_DEPTH_RANGE
|
||||
struct {
|
||||
@@ -206,6 +206,7 @@ public:
|
||||
OpenGLState& ResetBuffer(GLuint handle);
|
||||
OpenGLState& ResetVertexArray(GLuint handle);
|
||||
OpenGLState& ResetFramebuffer(GLuint handle);
|
||||
void EmulateViewportWithScissor();
|
||||
|
||||
private:
|
||||
static OpenGLState cur_state;
|
||||
|
||||
@@ -218,14 +218,19 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
|
||||
inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
|
||||
switch (equation) {
|
||||
case Maxwell::Blend::Equation::Add:
|
||||
case Maxwell::Blend::Equation::AddGL:
|
||||
return GL_FUNC_ADD;
|
||||
case Maxwell::Blend::Equation::Subtract:
|
||||
case Maxwell::Blend::Equation::SubtractGL:
|
||||
return GL_FUNC_SUBTRACT;
|
||||
case Maxwell::Blend::Equation::ReverseSubtract:
|
||||
case Maxwell::Blend::Equation::ReverseSubtractGL:
|
||||
return GL_FUNC_REVERSE_SUBTRACT;
|
||||
case Maxwell::Blend::Equation::Min:
|
||||
case Maxwell::Blend::Equation::MinGL:
|
||||
return GL_MIN;
|
||||
case Maxwell::Blend::Equation::Max:
|
||||
case Maxwell::Blend::Equation::MaxGL:
|
||||
return GL_MAX;
|
||||
}
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation));
|
||||
|
||||
@@ -304,6 +304,12 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
gl_framebuffer_data.resize(texture.width * texture.height * 4);
|
||||
break;
|
||||
default:
|
||||
internal_format = GL_RGBA;
|
||||
texture.gl_format = GL_RGBA;
|
||||
texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
gl_framebuffer_data.resize(texture.width * texture.height * 4);
|
||||
LOG_CRITICAL(Render_OpenGL, "Unknown framebuffer pixel format: {}",
|
||||
static_cast<u32>(framebuffer.pixel_format));
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -484,7 +490,7 @@ bool RendererOpenGL::Init() {
|
||||
Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_Model", gpu_model);
|
||||
Core::Telemetry().AddField(Telemetry::FieldType::UserSystem, "GPU_OpenGL_Version", gl_version);
|
||||
|
||||
if (!GLAD_GL_VERSION_3_3) {
|
||||
if (!GLAD_GL_VERSION_4_3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ add_executable(yuzu
|
||||
Info.plist
|
||||
about_dialog.cpp
|
||||
about_dialog.h
|
||||
applets/software_keyboard.cpp
|
||||
applets/software_keyboard.h
|
||||
bootmanager.cpp
|
||||
bootmanager.h
|
||||
compatibility_list.cpp
|
||||
@@ -27,8 +29,14 @@ add_executable(yuzu
|
||||
configuration/configure_graphics.h
|
||||
configuration/configure_input.cpp
|
||||
configuration/configure_input.h
|
||||
configuration/configure_input_player.cpp
|
||||
configuration/configure_input_player.h
|
||||
configuration/configure_mouse_advanced.cpp
|
||||
configuration/configure_mouse_advanced.h
|
||||
configuration/configure_system.cpp
|
||||
configuration/configure_system.h
|
||||
configuration/configure_touchscreen_advanced.cpp
|
||||
configuration/configure_touchscreen_advanced.h
|
||||
configuration/configure_web.cpp
|
||||
configuration/configure_web.h
|
||||
debugger/graphics/graphics_breakpoint_observer.cpp
|
||||
@@ -76,7 +84,10 @@ set(UIS
|
||||
configuration/configure_general.ui
|
||||
configuration/configure_graphics.ui
|
||||
configuration/configure_input.ui
|
||||
configuration/configure_input_player.ui
|
||||
configuration/configure_mouse_advanced.ui
|
||||
configuration/configure_system.ui
|
||||
configuration/configure_touchscreen_advanced.ui
|
||||
configuration/configure_web.ui
|
||||
hotkeys.ui
|
||||
main.ui
|
||||
|
||||
152
src/yuzu/applets/software_keyboard.cpp
Normal file
152
src/yuzu/applets/software_keyboard.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFont>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include "core/hle/lock.h"
|
||||
#include "yuzu/applets/software_keyboard.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator(
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters)
|
||||
: parameters(std::move(parameters)) {}
|
||||
|
||||
QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const {
|
||||
if (input.size() > parameters.max_length)
|
||||
return Invalid;
|
||||
if (parameters.disable_space && input.contains(' '))
|
||||
return Invalid;
|
||||
if (parameters.disable_address && input.contains('@'))
|
||||
return Invalid;
|
||||
if (parameters.disable_percent && input.contains('%'))
|
||||
return Invalid;
|
||||
if (parameters.disable_slash && (input.contains('/') || input.contains('\\')))
|
||||
return Invalid;
|
||||
if (parameters.disable_number &&
|
||||
std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) {
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
if (parameters.disable_download_code &&
|
||||
std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) {
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
return Acceptable;
|
||||
}
|
||||
|
||||
QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
|
||||
QWidget* parent, Core::Frontend::SoftwareKeyboardParameters parameters_)
|
||||
: QDialog(parent), parameters(std::move(parameters_)) {
|
||||
layout = new QVBoxLayout;
|
||||
|
||||
header_label = new QLabel(QString::fromStdU16String(parameters.header_text));
|
||||
header_label->setFont({header_label->font().family(), 11, QFont::Bold});
|
||||
if (header_label->text().isEmpty())
|
||||
header_label->setText(tr("Enter text:"));
|
||||
|
||||
sub_label = new QLabel(QString::fromStdU16String(parameters.sub_text));
|
||||
sub_label->setFont({sub_label->font().family(), sub_label->font().pointSize(),
|
||||
sub_label->font().weight(), true});
|
||||
sub_label->setHidden(parameters.sub_text.empty());
|
||||
|
||||
guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text));
|
||||
guide_label->setHidden(parameters.guide_text.empty());
|
||||
|
||||
length_label = new QLabel(QStringLiteral("0/%1").arg(parameters.max_length));
|
||||
length_label->setAlignment(Qt::AlignRight);
|
||||
length_label->setFont({length_label->font().family(), 8});
|
||||
|
||||
line_edit = new QLineEdit;
|
||||
line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters));
|
||||
line_edit->setMaxLength(static_cast<int>(parameters.max_length));
|
||||
line_edit->setText(QString::fromStdU16String(parameters.initial_text));
|
||||
line_edit->setCursorPosition(
|
||||
parameters.cursor_at_beginning ? 0 : static_cast<int>(parameters.initial_text.size()));
|
||||
line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal);
|
||||
|
||||
connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) {
|
||||
length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length));
|
||||
});
|
||||
|
||||
buttons = new QDialogButtonBox;
|
||||
buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
|
||||
buttons->addButton(parameters.submit_text.empty()
|
||||
? tr("OK")
|
||||
: QString::fromStdU16String(parameters.submit_text),
|
||||
QDialogButtonBox::AcceptRole);
|
||||
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject);
|
||||
layout->addWidget(header_label);
|
||||
layout->addWidget(sub_label);
|
||||
layout->addWidget(guide_label);
|
||||
layout->addWidget(length_label);
|
||||
layout->addWidget(line_edit);
|
||||
layout->addWidget(buttons);
|
||||
setLayout(layout);
|
||||
setWindowTitle(tr("Software Keyboard"));
|
||||
}
|
||||
|
||||
QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default;
|
||||
|
||||
void QtSoftwareKeyboardDialog::accept() {
|
||||
ok = true;
|
||||
text = line_edit->text().toStdU16String();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboardDialog::reject() {
|
||||
ok = false;
|
||||
text.clear();
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
std::u16string QtSoftwareKeyboardDialog::GetText() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
bool QtSoftwareKeyboardDialog::GetStatus() const {
|
||||
return ok;
|
||||
}
|
||||
|
||||
QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) {
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection);
|
||||
connect(this, &QtSoftwareKeyboard::MainWindowTextCheckDialog, &main_window,
|
||||
&GMainWindow::SoftwareKeyboardInvokeCheckDialog, Qt::BlockingQueuedConnection);
|
||||
connect(&main_window, &GMainWindow::SoftwareKeyboardFinishedText, this,
|
||||
&QtSoftwareKeyboard::MainWindowFinishedText, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
|
||||
|
||||
void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters) const {
|
||||
text_output = std::move(out);
|
||||
emit MainWindowGetText(parameters);
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const {
|
||||
this->finished_check = std::move(finished_check);
|
||||
emit MainWindowTextCheckDialog(error_message);
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
text_output(text);
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() {
|
||||
// Acquire the HLE mutex
|
||||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
|
||||
finished_check();
|
||||
}
|
||||
79
src/yuzu/applets/software_keyboard.h
Normal file
79
src/yuzu/applets/software_keyboard.h
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QValidator>
|
||||
#include "common/assert.h"
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
|
||||
class GMainWindow;
|
||||
class QDialogButtonBox;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QVBoxLayout;
|
||||
class QtSoftwareKeyboard;
|
||||
|
||||
class QtSoftwareKeyboardValidator final : public QValidator {
|
||||
public:
|
||||
explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters);
|
||||
State validate(QString& input, int& pos) const override;
|
||||
|
||||
private:
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters;
|
||||
};
|
||||
|
||||
class QtSoftwareKeyboardDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QtSoftwareKeyboardDialog(QWidget* parent,
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters);
|
||||
~QtSoftwareKeyboardDialog() override;
|
||||
|
||||
void accept() override;
|
||||
void reject() override;
|
||||
|
||||
std::u16string GetText() const;
|
||||
bool GetStatus() const;
|
||||
|
||||
private:
|
||||
bool ok = false;
|
||||
std::u16string text;
|
||||
|
||||
QDialogButtonBox* buttons;
|
||||
QLabel* header_label;
|
||||
QLabel* sub_label;
|
||||
QLabel* guide_label;
|
||||
QLabel* length_label;
|
||||
QLineEdit* line_edit;
|
||||
QVBoxLayout* layout;
|
||||
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters;
|
||||
};
|
||||
|
||||
class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtSoftwareKeyboard(GMainWindow& parent);
|
||||
~QtSoftwareKeyboard() override;
|
||||
|
||||
void RequestText(std::function<void(std::optional<std::u16string>)> out,
|
||||
Core::Frontend::SoftwareKeyboardParameters parameters) const override;
|
||||
void SendTextCheckDialog(std::u16string error_message,
|
||||
std::function<void()> finished_check) const override;
|
||||
|
||||
signals:
|
||||
void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const;
|
||||
void MainWindowTextCheckDialog(std::u16string error_message) const;
|
||||
|
||||
private:
|
||||
void MainWindowFinishedText(std::optional<std::u16string> text);
|
||||
void MainWindowFinishedCheckDialog();
|
||||
|
||||
mutable std::function<void(std::optional<std::u16string>)> text_output;
|
||||
mutable std::function<void()> finished_check;
|
||||
};
|
||||
@@ -310,7 +310,7 @@ void GRenderWindow::InitRenderTarget() {
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
QGLFormat fmt;
|
||||
fmt.setVersion(3, 3);
|
||||
fmt.setVersion(4, 3);
|
||||
fmt.setProfile(QGLFormat::CoreProfile);
|
||||
fmt.setSwapInterval(false);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <QSettings>
|
||||
#include "common/file_util.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "input_common/main.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/ui_settings.h"
|
||||
@@ -47,40 +48,313 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
|
||||
},
|
||||
}};
|
||||
|
||||
void Config::ReadValues() {
|
||||
qt_config->beginGroup("Controls");
|
||||
const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
|
||||
{
|
||||
Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
|
||||
};
|
||||
|
||||
const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> Config::default_keyboard_keys = {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
Qt::Key_A,
|
||||
Qt::Key_B,
|
||||
Qt::Key_C,
|
||||
Qt::Key_D,
|
||||
Qt::Key_E,
|
||||
Qt::Key_F,
|
||||
Qt::Key_G,
|
||||
Qt::Key_H,
|
||||
Qt::Key_I,
|
||||
Qt::Key_J,
|
||||
Qt::Key_K,
|
||||
Qt::Key_L,
|
||||
Qt::Key_M,
|
||||
Qt::Key_N,
|
||||
Qt::Key_O,
|
||||
Qt::Key_P,
|
||||
Qt::Key_Q,
|
||||
Qt::Key_R,
|
||||
Qt::Key_S,
|
||||
Qt::Key_T,
|
||||
Qt::Key_U,
|
||||
Qt::Key_V,
|
||||
Qt::Key_W,
|
||||
Qt::Key_X,
|
||||
Qt::Key_Y,
|
||||
Qt::Key_Z,
|
||||
Qt::Key_1,
|
||||
Qt::Key_2,
|
||||
Qt::Key_3,
|
||||
Qt::Key_4,
|
||||
Qt::Key_5,
|
||||
Qt::Key_6,
|
||||
Qt::Key_7,
|
||||
Qt::Key_8,
|
||||
Qt::Key_9,
|
||||
Qt::Key_0,
|
||||
Qt::Key_Enter,
|
||||
Qt::Key_Escape,
|
||||
Qt::Key_Backspace,
|
||||
Qt::Key_Tab,
|
||||
Qt::Key_Space,
|
||||
Qt::Key_Minus,
|
||||
Qt::Key_Equal,
|
||||
Qt::Key_BracketLeft,
|
||||
Qt::Key_BracketRight,
|
||||
Qt::Key_Backslash,
|
||||
Qt::Key_Dead_Tilde,
|
||||
Qt::Key_Semicolon,
|
||||
Qt::Key_Apostrophe,
|
||||
Qt::Key_Dead_Grave,
|
||||
Qt::Key_Comma,
|
||||
Qt::Key_Period,
|
||||
Qt::Key_Slash,
|
||||
Qt::Key_CapsLock,
|
||||
|
||||
Qt::Key_F1,
|
||||
Qt::Key_F2,
|
||||
Qt::Key_F3,
|
||||
Qt::Key_F4,
|
||||
Qt::Key_F5,
|
||||
Qt::Key_F6,
|
||||
Qt::Key_F7,
|
||||
Qt::Key_F8,
|
||||
Qt::Key_F9,
|
||||
Qt::Key_F10,
|
||||
Qt::Key_F11,
|
||||
Qt::Key_F12,
|
||||
|
||||
Qt::Key_SysReq,
|
||||
Qt::Key_ScrollLock,
|
||||
Qt::Key_Pause,
|
||||
Qt::Key_Insert,
|
||||
Qt::Key_Home,
|
||||
Qt::Key_PageUp,
|
||||
Qt::Key_Delete,
|
||||
Qt::Key_End,
|
||||
Qt::Key_PageDown,
|
||||
Qt::Key_Right,
|
||||
Qt::Key_Left,
|
||||
Qt::Key_Down,
|
||||
Qt::Key_Up,
|
||||
|
||||
Qt::Key_NumLock,
|
||||
Qt::Key_Slash,
|
||||
Qt::Key_Asterisk,
|
||||
Qt::Key_Minus,
|
||||
Qt::Key_Plus,
|
||||
Qt::Key_Enter,
|
||||
Qt::Key_1,
|
||||
Qt::Key_2,
|
||||
Qt::Key_3,
|
||||
Qt::Key_4,
|
||||
Qt::Key_5,
|
||||
Qt::Key_6,
|
||||
Qt::Key_7,
|
||||
Qt::Key_8,
|
||||
Qt::Key_9,
|
||||
Qt::Key_0,
|
||||
Qt::Key_Period,
|
||||
|
||||
0,
|
||||
0,
|
||||
Qt::Key_PowerOff,
|
||||
Qt::Key_Equal,
|
||||
|
||||
Qt::Key_F13,
|
||||
Qt::Key_F14,
|
||||
Qt::Key_F15,
|
||||
Qt::Key_F16,
|
||||
Qt::Key_F17,
|
||||
Qt::Key_F18,
|
||||
Qt::Key_F19,
|
||||
Qt::Key_F20,
|
||||
Qt::Key_F21,
|
||||
Qt::Key_F22,
|
||||
Qt::Key_F23,
|
||||
Qt::Key_F24,
|
||||
|
||||
Qt::Key_Open,
|
||||
Qt::Key_Help,
|
||||
Qt::Key_Menu,
|
||||
0,
|
||||
Qt::Key_Stop,
|
||||
Qt::Key_AudioRepeat,
|
||||
Qt::Key_Undo,
|
||||
Qt::Key_Cut,
|
||||
Qt::Key_Copy,
|
||||
Qt::Key_Paste,
|
||||
Qt::Key_Find,
|
||||
Qt::Key_VolumeMute,
|
||||
Qt::Key_VolumeUp,
|
||||
Qt::Key_VolumeDown,
|
||||
Qt::Key_CapsLock,
|
||||
Qt::Key_NumLock,
|
||||
Qt::Key_ScrollLock,
|
||||
Qt::Key_Comma,
|
||||
|
||||
Qt::Key_ParenLeft,
|
||||
Qt::Key_ParenRight,
|
||||
};
|
||||
|
||||
const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default_keyboard_mods = {
|
||||
Qt::Key_Control, Qt::Key_Shift, Qt::Key_Alt, Qt::Key_ApplicationLeft,
|
||||
Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight,
|
||||
};
|
||||
|
||||
void Config::ReadPlayerValues() {
|
||||
for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
|
||||
Settings::values.players[p].connected =
|
||||
qt_config->value(QString("player_%1_connected").arg(p), false).toBool();
|
||||
|
||||
Settings::values.players[p].type = static_cast<Settings::ControllerType>(
|
||||
qt_config
|
||||
->value(QString("player_%1_type").arg(p),
|
||||
static_cast<u8>(Settings::ControllerType::DualJoycon))
|
||||
.toUInt());
|
||||
|
||||
Settings::values.players[p].body_color_left =
|
||||
qt_config
|
||||
->value(QString("player_%1_body_color_left").arg(p),
|
||||
Settings::JOYCON_BODY_NEON_BLUE)
|
||||
.toUInt();
|
||||
Settings::values.players[p].body_color_right =
|
||||
qt_config
|
||||
->value(QString("player_%1_body_color_right").arg(p),
|
||||
Settings::JOYCON_BODY_NEON_RED)
|
||||
.toUInt();
|
||||
Settings::values.players[p].button_color_left =
|
||||
qt_config
|
||||
->value(QString("player_%1_button_color_left").arg(p),
|
||||
Settings::JOYCON_BUTTONS_NEON_BLUE)
|
||||
.toUInt();
|
||||
Settings::values.players[p].button_color_right =
|
||||
qt_config
|
||||
->value(QString("player_%1_button_color_right").arg(p),
|
||||
Settings::JOYCON_BUTTONS_NEON_RED)
|
||||
.toUInt();
|
||||
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||
Settings::values.players[p].buttons[i] =
|
||||
qt_config
|
||||
->value(QString("player_%1_").arg(p) + Settings::NativeButton::mapping[i],
|
||||
QString::fromStdString(default_param))
|
||||
.toString()
|
||||
.toStdString();
|
||||
if (Settings::values.players[p].buttons[i].empty())
|
||||
Settings::values.players[p].buttons[i] = default_param;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||
default_analogs[i][3], default_analogs[i][4], 0.5f);
|
||||
Settings::values.players[p].analogs[i] =
|
||||
qt_config
|
||||
->value(QString("player_%1_").arg(p) + Settings::NativeAnalog::mapping[i],
|
||||
QString::fromStdString(default_param))
|
||||
.toString()
|
||||
.toStdString();
|
||||
if (Settings::values.players[p].analogs[i].empty())
|
||||
Settings::values.players[p].analogs[i] = default_param;
|
||||
}
|
||||
}
|
||||
|
||||
std::stable_partition(
|
||||
Settings::values.players.begin(),
|
||||
Settings::values.players.begin() +
|
||||
Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
|
||||
[](const auto& player) { return player.connected; });
|
||||
}
|
||||
|
||||
void Config::ReadDebugValues() {
|
||||
Settings::values.debug_pad_enabled = qt_config->value("debug_pad_enabled", false).toBool();
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||
Settings::values.buttons[i] =
|
||||
Settings::values.debug_pad_buttons[i] =
|
||||
qt_config
|
||||
->value(Settings::NativeButton::mapping[i], QString::fromStdString(default_param))
|
||||
->value(QString("debug_pad_") + Settings::NativeButton::mapping[i],
|
||||
QString::fromStdString(default_param))
|
||||
.toString()
|
||||
.toStdString();
|
||||
if (Settings::values.buttons[i].empty())
|
||||
Settings::values.buttons[i] = default_param;
|
||||
if (Settings::values.debug_pad_buttons[i].empty())
|
||||
Settings::values.debug_pad_buttons[i] = default_param;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
|
||||
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
|
||||
default_analogs[i][3], default_analogs[i][4], 0.5f);
|
||||
Settings::values.analogs[i] =
|
||||
Settings::values.debug_pad_analogs[i] =
|
||||
qt_config
|
||||
->value(Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param))
|
||||
->value(QString("debug_pad_") + Settings::NativeAnalog::mapping[i],
|
||||
QString::fromStdString(default_param))
|
||||
.toString()
|
||||
.toStdString();
|
||||
if (Settings::values.analogs[i].empty())
|
||||
Settings::values.analogs[i] = default_param;
|
||||
if (Settings::values.debug_pad_analogs[i].empty())
|
||||
Settings::values.debug_pad_analogs[i] = default_param;
|
||||
}
|
||||
}
|
||||
|
||||
void Config::ReadKeyboardValues() {
|
||||
Settings::values.keyboard_enabled = qt_config->value("keyboard_enabled", false).toBool();
|
||||
|
||||
std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
|
||||
Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
|
||||
std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
|
||||
Settings::values.keyboard_keys.begin() +
|
||||
Settings::NativeKeyboard::LeftControlKey,
|
||||
InputCommon::GenerateKeyboardParam);
|
||||
std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
|
||||
Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
|
||||
}
|
||||
|
||||
void Config::ReadMouseValues() {
|
||||
Settings::values.mouse_enabled = qt_config->value("mouse_enabled", false).toBool();
|
||||
|
||||
for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
|
||||
Settings::values.mouse_buttons[i] =
|
||||
qt_config
|
||||
->value(QString("mouse_") + Settings::NativeMouseButton::mapping[i],
|
||||
QString::fromStdString(default_param))
|
||||
.toString()
|
||||
.toStdString();
|
||||
if (Settings::values.mouse_buttons[i].empty())
|
||||
Settings::values.mouse_buttons[i] = default_param;
|
||||
}
|
||||
}
|
||||
|
||||
void Config::ReadTouchscreenValues() {
|
||||
Settings::values.touchscreen.enabled = qt_config->value("touchscreen_enabled", true).toBool();
|
||||
Settings::values.touchscreen.device =
|
||||
qt_config->value("touchscreen_device", "engine:emu_window").toString().toStdString();
|
||||
|
||||
Settings::values.touchscreen.finger = qt_config->value("touchscreen_finger", 0).toUInt();
|
||||
Settings::values.touchscreen.rotation_angle = qt_config->value("touchscreen_angle", 0).toUInt();
|
||||
Settings::values.touchscreen.diameter_x =
|
||||
qt_config->value("touchscreen_diameter_x", 15).toUInt();
|
||||
Settings::values.touchscreen.diameter_y =
|
||||
qt_config->value("touchscreen_diameter_y", 15).toUInt();
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::ReadValues() {
|
||||
qt_config->beginGroup("Controls");
|
||||
|
||||
ReadPlayerValues();
|
||||
ReadDebugValues();
|
||||
ReadKeyboardValues();
|
||||
ReadMouseValues();
|
||||
ReadTouchscreenValues();
|
||||
|
||||
Settings::values.motion_device =
|
||||
qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.touch_device =
|
||||
qt_config->value("touch_device", "engine:emu_window").toString().toStdString();
|
||||
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Core");
|
||||
Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
|
||||
@@ -126,6 +400,11 @@ void Config::ReadValues() {
|
||||
.toStdString());
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Core");
|
||||
Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
|
||||
Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("System");
|
||||
Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
|
||||
Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
|
||||
@@ -153,6 +432,7 @@ void Config::ReadValues() {
|
||||
Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
|
||||
Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
|
||||
Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString();
|
||||
Settings::values.dump_exefs = qt_config->value("dump_exefs", false).toBool();
|
||||
Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
@@ -230,18 +510,81 @@ void Config::ReadValues() {
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
||||
void Config::SaveValues() {
|
||||
qt_config->beginGroup("Controls");
|
||||
void Config::SavePlayerValues() {
|
||||
for (int p = 0; p < Settings::values.players.size(); ++p) {
|
||||
qt_config->setValue(QString("player_%1_connected").arg(p),
|
||||
Settings::values.players[p].connected);
|
||||
qt_config->setValue(QString("player_%1_type").arg(p),
|
||||
static_cast<u8>(Settings::values.players[p].type));
|
||||
|
||||
qt_config->setValue(QString("player_%1_body_color_left").arg(p),
|
||||
Settings::values.players[p].body_color_left);
|
||||
qt_config->setValue(QString("player_%1_body_color_right").arg(p),
|
||||
Settings::values.players[p].body_color_right);
|
||||
qt_config->setValue(QString("player_%1_button_color_left").arg(p),
|
||||
Settings::values.players[p].button_color_left);
|
||||
qt_config->setValue(QString("player_%1_button_color_right").arg(p),
|
||||
Settings::values.players[p].button_color_right);
|
||||
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
qt_config->setValue(QString("player_%1_").arg(p) +
|
||||
QString::fromStdString(Settings::NativeButton::mapping[i]),
|
||||
QString::fromStdString(Settings::values.players[p].buttons[i]));
|
||||
}
|
||||
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||
qt_config->setValue(QString("player_%1_").arg(p) +
|
||||
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
|
||||
QString::fromStdString(Settings::values.players[p].analogs[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Config::SaveDebugValues() {
|
||||
qt_config->setValue("debug_pad_enabled", Settings::values.debug_pad_enabled);
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]),
|
||||
QString::fromStdString(Settings::values.buttons[i]));
|
||||
qt_config->setValue(QString("debug_pad_") +
|
||||
QString::fromStdString(Settings::NativeButton::mapping[i]),
|
||||
QString::fromStdString(Settings::values.debug_pad_buttons[i]));
|
||||
}
|
||||
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
|
||||
qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]),
|
||||
QString::fromStdString(Settings::values.analogs[i]));
|
||||
qt_config->setValue(QString("debug_pad_") +
|
||||
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
|
||||
QString::fromStdString(Settings::values.debug_pad_analogs[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void Config::SaveMouseValues() {
|
||||
qt_config->setValue("mouse_enabled", Settings::values.mouse_enabled);
|
||||
|
||||
for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
|
||||
qt_config->setValue(QString("mouse_") +
|
||||
QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
|
||||
QString::fromStdString(Settings::values.mouse_buttons[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void Config::SaveTouchscreenValues() {
|
||||
qt_config->setValue("touchscreen_enabled", Settings::values.touchscreen.enabled);
|
||||
qt_config->setValue("touchscreen_device",
|
||||
QString::fromStdString(Settings::values.touchscreen.device));
|
||||
|
||||
qt_config->setValue("touchscreen_finger", Settings::values.touchscreen.finger);
|
||||
qt_config->setValue("touchscreen_angle", Settings::values.touchscreen.rotation_angle);
|
||||
qt_config->setValue("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x);
|
||||
qt_config->setValue("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y);
|
||||
}
|
||||
|
||||
void Config::SaveValues() {
|
||||
qt_config->beginGroup("Controls");
|
||||
|
||||
SavePlayerValues();
|
||||
SaveDebugValues();
|
||||
SaveMouseValues();
|
||||
SaveTouchscreenValues();
|
||||
|
||||
qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device));
|
||||
qt_config->setValue("touch_device", QString::fromStdString(Settings::values.touch_device));
|
||||
qt_config->setValue("keyboard_enabled", Settings::values.keyboard_enabled);
|
||||
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("Core");
|
||||
@@ -280,7 +623,6 @@ void Config::SaveValues() {
|
||||
qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
|
||||
qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
|
||||
qt_config->setValue("current_user", Settings::values.current_user);
|
||||
|
||||
qt_config->setValue("language_index", Settings::values.language_index);
|
||||
|
||||
qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value());
|
||||
@@ -297,6 +639,7 @@ void Config::SaveValues() {
|
||||
qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
|
||||
qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
|
||||
qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args));
|
||||
qt_config->setValue("dump_exefs", Settings::values.dump_exefs);
|
||||
qt_config->setValue("dump_nso", Settings::values.dump_nso);
|
||||
qt_config->endGroup();
|
||||
|
||||
|
||||
@@ -22,10 +22,24 @@ public:
|
||||
|
||||
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
|
||||
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
|
||||
static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
|
||||
default_mouse_buttons;
|
||||
static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
|
||||
static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
|
||||
|
||||
private:
|
||||
void ReadValues();
|
||||
void ReadPlayerValues();
|
||||
void ReadDebugValues();
|
||||
void ReadKeyboardValues();
|
||||
void ReadMouseValues();
|
||||
void ReadTouchscreenValues();
|
||||
|
||||
void SaveValues();
|
||||
void SavePlayerValues();
|
||||
void SaveDebugValues();
|
||||
void SaveMouseValues();
|
||||
void SaveTouchscreenValues();
|
||||
|
||||
std::unique_ptr<QSettings> qt_config;
|
||||
std::string qt_config_loc;
|
||||
|
||||
@@ -34,6 +34,7 @@ void ConfigureDebug::setConfiguration() {
|
||||
ui->toggle_console->setChecked(UISettings::values.show_console);
|
||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
|
||||
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
|
||||
ui->dump_exefs->setChecked(Settings::values.dump_exefs);
|
||||
ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
|
||||
}
|
||||
|
||||
@@ -43,6 +44,7 @@ void ConfigureDebug::applyConfiguration() {
|
||||
UISettings::values.show_console = ui->toggle_console->isChecked();
|
||||
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
|
||||
Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
|
||||
Settings::values.dump_exefs = ui->dump_exefs->isChecked();
|
||||
Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked();
|
||||
Debugger::ToggleConsole();
|
||||
Log::Filter filter;
|
||||
|
||||
@@ -145,6 +145,16 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="dump_exefs">
|
||||
<property name="whatsThis">
|
||||
<string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dump ExeFS</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_general.h"
|
||||
#include "yuzu/configuration/configure_general.h"
|
||||
@@ -36,7 +32,6 @@ void ConfigureGeneral::setConfiguration() {
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
||||
ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
|
||||
ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
|
||||
ui->enable_nfc->setChecked(Settings::values.enable_nfc);
|
||||
}
|
||||
|
||||
@@ -44,33 +39,6 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
|
||||
ui->widget->Populate(registry);
|
||||
}
|
||||
|
||||
void ConfigureGeneral::OnDockedModeChanged(bool last_state, bool new_state) {
|
||||
if (last_state == new_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||
|
||||
// Message queue is shared between these services, we just need to signal an operation
|
||||
// change to one and it will handle both automatically
|
||||
auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
|
||||
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
|
||||
bool has_signalled = false;
|
||||
|
||||
if (applet_oe != nullptr) {
|
||||
applet_oe->GetMessageQueue()->OperationModeChanged();
|
||||
has_signalled = true;
|
||||
}
|
||||
|
||||
if (applet_ae != nullptr && !has_signalled) {
|
||||
applet_ae->GetMessageQueue()->OperationModeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGeneral::applyConfiguration() {
|
||||
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
@@ -78,9 +46,5 @@ void ConfigureGeneral::applyConfiguration() {
|
||||
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
|
||||
|
||||
Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
|
||||
const bool pre_docked_mode = Settings::values.use_docked_mode;
|
||||
Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
|
||||
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
|
||||
|
||||
Settings::values.enable_nfc = ui->enable_nfc->isChecked();
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ public:
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
void OnDockedModeChanged(bool last_state, bool new_state);
|
||||
|
||||
std::unique_ptr<Ui::ConfigureGeneral> ui;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>300</width>
|
||||
<height>377</height>
|
||||
<height>407</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -71,13 +71,6 @@
|
||||
<layout class="QHBoxLayout" name="EmulationHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="EmulationVerticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_docked_mode">
|
||||
<property name="text">
|
||||
<string>Enable docked mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="enable_nfc">
|
||||
<property name="text">
|
||||
|
||||
@@ -23,31 +23,31 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_frame_limit">
|
||||
<property name="text">
|
||||
<string>Limit Speed Percent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="frame_limit">
|
||||
<property name="suffix">
|
||||
<string>%</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_frame_limit">
|
||||
<property name="text">
|
||||
<string>Limit Speed Percent</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="frame_limit">
|
||||
<property name="suffix">
|
||||
<string>%</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_accurate_gpu_emulation">
|
||||
@@ -61,7 +61,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Internal Resolution:(Currently does nothing.)</string>
|
||||
<string>Internal Resolution</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -96,27 +96,27 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="bg_label">
|
||||
<property name="text">
|
||||
<string>Background Color:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="bg_button">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="bg_label">
|
||||
<property name="text">
|
||||
<string>Background Color:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="bg_button">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -9,334 +9,202 @@
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
#include "common/param_package.h"
|
||||
#include "configuration/configure_touchscreen_advanced.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
#include "core/hle/service/am/applet_oe.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "input_common/main.h"
|
||||
#include "ui_configure_input.h"
|
||||
#include "ui_configure_input_player.h"
|
||||
#include "ui_configure_mouse_advanced.h"
|
||||
#include "ui_configure_touchscreen_advanced.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_input.h"
|
||||
|
||||
const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM>
|
||||
ConfigureInput::analog_sub_buttons{{
|
||||
"up",
|
||||
"down",
|
||||
"left",
|
||||
"right",
|
||||
"modifier",
|
||||
}};
|
||||
|
||||
static QString getKeyName(int key_code) {
|
||||
switch (key_code) {
|
||||
case Qt::Key_Shift:
|
||||
return QObject::tr("Shift");
|
||||
case Qt::Key_Control:
|
||||
return QObject::tr("Ctrl");
|
||||
case Qt::Key_Alt:
|
||||
return QObject::tr("Alt");
|
||||
case Qt::Key_Meta:
|
||||
return "";
|
||||
default:
|
||||
return QKeySequence(key_code).toString();
|
||||
}
|
||||
}
|
||||
|
||||
static void SetAnalogButton(const Common::ParamPackage& input_param,
|
||||
Common::ParamPackage& analog_param, const std::string& button_name) {
|
||||
if (analog_param.Get("engine", "") != "analog_from_button") {
|
||||
analog_param = {
|
||||
{"engine", "analog_from_button"},
|
||||
{"modifier_scale", "0.5"},
|
||||
};
|
||||
}
|
||||
analog_param.Set(button_name, input_param.Serialize());
|
||||
}
|
||||
|
||||
static QString ButtonToText(const Common::ParamPackage& param) {
|
||||
if (!param.Has("engine")) {
|
||||
return QObject::tr("[not set]");
|
||||
} else if (param.Get("engine", "") == "keyboard") {
|
||||
return getKeyName(param.Get("code", 0));
|
||||
} else if (param.Get("engine", "") == "sdl") {
|
||||
if (param.Has("hat")) {
|
||||
return QString(QObject::tr("Hat %1 %2"))
|
||||
.arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
|
||||
}
|
||||
if (param.Has("axis")) {
|
||||
return QString(QObject::tr("Axis %1%2"))
|
||||
.arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
|
||||
}
|
||||
if (param.Has("button")) {
|
||||
return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
|
||||
}
|
||||
return QString();
|
||||
} else {
|
||||
return QObject::tr("[unknown]");
|
||||
}
|
||||
};
|
||||
|
||||
static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
|
||||
if (!param.Has("engine")) {
|
||||
return QObject::tr("[not set]");
|
||||
} else if (param.Get("engine", "") == "analog_from_button") {
|
||||
return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
|
||||
} else if (param.Get("engine", "") == "sdl") {
|
||||
if (dir == "modifier") {
|
||||
return QString(QObject::tr("[unused]"));
|
||||
}
|
||||
|
||||
if (dir == "left" || dir == "right") {
|
||||
return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str());
|
||||
} else if (dir == "up" || dir == "down") {
|
||||
return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str());
|
||||
}
|
||||
return QString();
|
||||
} else {
|
||||
return QObject::tr("[unknown]");
|
||||
}
|
||||
};
|
||||
#include "yuzu/configuration/configure_input_player.h"
|
||||
#include "yuzu/configuration/configure_mouse_advanced.h"
|
||||
|
||||
ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
|
||||
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
|
||||
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
|
||||
ui->setupUi(this);
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
|
||||
button_map = {
|
||||
ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
|
||||
ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
|
||||
ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
|
||||
ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
|
||||
ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown,
|
||||
ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
|
||||
ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
|
||||
players_controller = {
|
||||
ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox,
|
||||
ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox,
|
||||
};
|
||||
|
||||
analog_map_buttons = {{
|
||||
{
|
||||
ui->buttonLStickUp,
|
||||
ui->buttonLStickDown,
|
||||
ui->buttonLStickLeft,
|
||||
ui->buttonLStickRight,
|
||||
ui->buttonLStickMod,
|
||||
},
|
||||
{
|
||||
ui->buttonRStickUp,
|
||||
ui->buttonRStickDown,
|
||||
ui->buttonRStickLeft,
|
||||
ui->buttonRStickRight,
|
||||
ui->buttonRStickMod,
|
||||
},
|
||||
}};
|
||||
players_configure = {
|
||||
ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure,
|
||||
ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure,
|
||||
};
|
||||
|
||||
analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
|
||||
|
||||
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
|
||||
if (!button_map[button_id])
|
||||
continue;
|
||||
button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(button_map[button_id], &QPushButton::released, [=]() {
|
||||
handleClick(
|
||||
button_map[button_id],
|
||||
[=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
connect(button_map[button_id], &QPushButton::customContextMenuRequested,
|
||||
[=](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
buttons_param[button_id].Clear();
|
||||
button_map[button_id]->setText(tr("[not set]"));
|
||||
});
|
||||
context_menu.addAction(tr("Restore Default"), [&] {
|
||||
buttons_param[button_id] = Common::ParamPackage{
|
||||
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
|
||||
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
|
||||
});
|
||||
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
|
||||
});
|
||||
for (auto* controller_box : players_controller) {
|
||||
controller_box->addItems({"None", "Pro Controller", "Dual Joycons", "Single Right Joycon",
|
||||
"Single Left Joycon"});
|
||||
}
|
||||
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
||||
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
|
||||
if (!analog_map_buttons[analog_id][sub_button_id])
|
||||
continue;
|
||||
analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
|
||||
Qt::CustomContextMenu);
|
||||
connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() {
|
||||
handleClick(analog_map_buttons[analog_id][sub_button_id],
|
||||
[=](const Common::ParamPackage& params) {
|
||||
SetAnalogButton(params, analogs_param[analog_id],
|
||||
analog_sub_buttons[sub_button_id]);
|
||||
},
|
||||
InputCommon::Polling::DeviceType::Button);
|
||||
});
|
||||
connect(analog_map_buttons[analog_id][sub_button_id],
|
||||
&QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
||||
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
|
||||
});
|
||||
context_menu.addAction(tr("Restore Default"), [&] {
|
||||
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
||||
Config::default_analogs[analog_id][sub_button_id])};
|
||||
SetAnalogButton(params, analogs_param[analog_id],
|
||||
analog_sub_buttons[sub_button_id]);
|
||||
analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
|
||||
analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
|
||||
});
|
||||
context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
|
||||
menu_location));
|
||||
});
|
||||
}
|
||||
connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
|
||||
QMessageBox::information(this, tr("Information"),
|
||||
tr("After pressing OK, first move your joystick horizontally, "
|
||||
"and then vertically."));
|
||||
handleClick(
|
||||
analog_map_stick[analog_id],
|
||||
[=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
|
||||
InputCommon::Polling::DeviceType::Analog);
|
||||
});
|
||||
}
|
||||
|
||||
connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
|
||||
connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
|
||||
|
||||
timeout_timer->setSingleShot(true);
|
||||
connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
|
||||
|
||||
connect(poll_timer.get(), &QTimer::timeout, [this]() {
|
||||
Common::ParamPackage params;
|
||||
for (auto& poller : device_pollers) {
|
||||
params = poller->GetNextInput();
|
||||
if (params.Has("engine")) {
|
||||
setPollingResult(params, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this->loadConfiguration();
|
||||
updateUIEnabled();
|
||||
|
||||
// TODO(wwylele): enable this when we actually emulate it
|
||||
ui->buttonHome->setEnabled(false);
|
||||
connect(ui->restore_defaults_button, &QPushButton::pressed, this,
|
||||
&ConfigureInput::restoreDefaults);
|
||||
|
||||
for (auto* enabled : players_controller)
|
||||
connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureInput::updateUIEnabled);
|
||||
connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
|
||||
connect(ui->handheld_connected, &QCheckBox::stateChanged, this,
|
||||
&ConfigureInput::updateUIEnabled);
|
||||
connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
|
||||
connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
|
||||
connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
|
||||
connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
|
||||
&ConfigureInput::updateUIEnabled);
|
||||
|
||||
for (std::size_t i = 0; i < players_configure.size(); ++i) {
|
||||
connect(players_configure[i], &QPushButton::pressed, this,
|
||||
[this, i]() { CallConfigureDialog<ConfigureInputPlayer>(i, false); });
|
||||
}
|
||||
|
||||
connect(ui->handheld_configure, &QPushButton::pressed, this,
|
||||
[this]() { CallConfigureDialog<ConfigureInputPlayer>(8, false); });
|
||||
|
||||
connect(ui->debug_configure, &QPushButton::pressed, this,
|
||||
[this]() { CallConfigureDialog<ConfigureInputPlayer>(9, true); });
|
||||
|
||||
connect(ui->mouse_advanced, &QPushButton::pressed, this,
|
||||
[this]() { CallConfigureDialog<ConfigureMouseAdvanced>(); });
|
||||
|
||||
connect(ui->touchscreen_advanced, &QPushButton::pressed, this,
|
||||
[this]() { CallConfigureDialog<ConfigureTouchscreenAdvanced>(); });
|
||||
}
|
||||
|
||||
template <typename Dialog, typename... Args>
|
||||
void ConfigureInput::CallConfigureDialog(Args&&... args) {
|
||||
this->applyConfiguration();
|
||||
Dialog dialog(this, std::forward<Args>(args)...);
|
||||
|
||||
const auto res = dialog.exec();
|
||||
if (res == QDialog::Accepted) {
|
||||
dialog.applyConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) {
|
||||
if (last_state == new_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
Core::System& system{Core::System::GetInstance()};
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||
|
||||
// Message queue is shared between these services, we just need to signal an operation
|
||||
// change to one and it will handle both automatically
|
||||
auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
|
||||
auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
|
||||
bool has_signalled = false;
|
||||
|
||||
if (applet_oe != nullptr) {
|
||||
applet_oe->GetMessageQueue()->OperationModeChanged();
|
||||
has_signalled = true;
|
||||
}
|
||||
|
||||
if (applet_ae != nullptr && !has_signalled) {
|
||||
applet_ae->GetMessageQueue()->OperationModeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureInput::applyConfiguration() {
|
||||
std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.buttons.begin(),
|
||||
[](const Common::ParamPackage& param) { return param.Serialize(); });
|
||||
std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(),
|
||||
[](const Common::ParamPackage& param) { return param.Serialize(); });
|
||||
for (std::size_t i = 0; i < players_controller.size(); ++i) {
|
||||
const auto controller_type_index = players_controller[i]->currentIndex();
|
||||
|
||||
Settings::values.players[i].connected = controller_type_index != 0;
|
||||
|
||||
if (controller_type_index > 0) {
|
||||
Settings::values.players[i].type =
|
||||
static_cast<Settings::ControllerType>(controller_type_index - 1);
|
||||
} else {
|
||||
Settings::values.players[i].type = Settings::ControllerType::DualJoycon;
|
||||
}
|
||||
}
|
||||
|
||||
const bool pre_docked_mode = Settings::values.use_docked_mode;
|
||||
Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
|
||||
OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
|
||||
Settings::values
|
||||
.players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
|
||||
.connected = ui->handheld_connected->isChecked();
|
||||
Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
|
||||
Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
|
||||
Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
|
||||
Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
|
||||
}
|
||||
|
||||
void ConfigureInput::updateUIEnabled() {
|
||||
bool hit_disabled = false;
|
||||
for (auto* player : players_controller) {
|
||||
player->setDisabled(hit_disabled);
|
||||
if (hit_disabled)
|
||||
player->setCurrentIndex(0);
|
||||
if (!hit_disabled && player->currentIndex() == 0)
|
||||
hit_disabled = true;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < players_controller.size(); ++i) {
|
||||
players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0);
|
||||
}
|
||||
|
||||
ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked());
|
||||
ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() &&
|
||||
!ui->use_docked_mode->isChecked());
|
||||
ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
|
||||
ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
|
||||
ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
|
||||
}
|
||||
|
||||
void ConfigureInput::loadConfiguration() {
|
||||
std::transform(Settings::values.buttons.begin(), Settings::values.buttons.end(),
|
||||
buttons_param.begin(),
|
||||
[](const std::string& str) { return Common::ParamPackage(str); });
|
||||
std::transform(Settings::values.analogs.begin(), Settings::values.analogs.end(),
|
||||
analogs_param.begin(),
|
||||
[](const std::string& str) { return Common::ParamPackage(str); });
|
||||
updateButtonLabels();
|
||||
std::stable_partition(
|
||||
Settings::values.players.begin(),
|
||||
Settings::values.players.begin() +
|
||||
Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
|
||||
[](const auto& player) { return player.connected; });
|
||||
|
||||
for (std::size_t i = 0; i < players_controller.size(); ++i) {
|
||||
const auto connected = Settings::values.players[i].connected;
|
||||
players_controller[i]->setCurrentIndex(
|
||||
connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
|
||||
}
|
||||
|
||||
ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
|
||||
ui->handheld_connected->setChecked(
|
||||
Settings::values
|
||||
.players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
|
||||
.connected);
|
||||
ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
|
||||
ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
|
||||
ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
|
||||
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
|
||||
|
||||
updateUIEnabled();
|
||||
}
|
||||
|
||||
void ConfigureInput::restoreDefaults() {
|
||||
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
|
||||
buttons_param[button_id] = Common::ParamPackage{
|
||||
InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
|
||||
players_controller[0]->setCurrentIndex(2);
|
||||
|
||||
for (std::size_t i = 1; i < players_controller.size(); ++i) {
|
||||
players_controller[i]->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
||||
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
|
||||
Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
|
||||
Config::default_analogs[analog_id][sub_button_id])};
|
||||
SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
|
||||
}
|
||||
}
|
||||
updateButtonLabels();
|
||||
}
|
||||
|
||||
void ConfigureInput::ClearAll() {
|
||||
for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
|
||||
if (button_map[button_id] && button_map[button_id]->isEnabled())
|
||||
buttons_param[button_id].Clear();
|
||||
}
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
||||
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
|
||||
if (analog_map_buttons[analog_id][sub_button_id] &&
|
||||
analog_map_buttons[analog_id][sub_button_id]->isEnabled())
|
||||
analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
|
||||
}
|
||||
}
|
||||
updateButtonLabels();
|
||||
}
|
||||
|
||||
void ConfigureInput::updateButtonLabels() {
|
||||
for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
|
||||
button_map[button]->setText(ButtonToText(buttons_param[button]));
|
||||
}
|
||||
|
||||
for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
|
||||
for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
|
||||
if (analog_map_buttons[analog_id][sub_button_id]) {
|
||||
analog_map_buttons[analog_id][sub_button_id]->setText(
|
||||
AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
|
||||
}
|
||||
}
|
||||
analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureInput::handleClick(QPushButton* button,
|
||||
std::function<void(const Common::ParamPackage&)> new_input_setter,
|
||||
InputCommon::Polling::DeviceType type) {
|
||||
button->setText(tr("[press key]"));
|
||||
button->setFocus();
|
||||
|
||||
input_setter = new_input_setter;
|
||||
|
||||
device_pollers = InputCommon::Polling::GetPollers(type);
|
||||
|
||||
// Keyboard keys can only be used as button devices
|
||||
want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
|
||||
|
||||
for (auto& poller : device_pollers) {
|
||||
poller->Start();
|
||||
}
|
||||
|
||||
grabKeyboard();
|
||||
grabMouse();
|
||||
timeout_timer->start(5000); // Cancel after 5 seconds
|
||||
poll_timer->start(200); // Check for new inputs every 200ms
|
||||
}
|
||||
|
||||
void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool abort) {
|
||||
releaseKeyboard();
|
||||
releaseMouse();
|
||||
timeout_timer->stop();
|
||||
poll_timer->stop();
|
||||
for (auto& poller : device_pollers) {
|
||||
poller->Stop();
|
||||
}
|
||||
|
||||
if (!abort) {
|
||||
(*input_setter)(params);
|
||||
}
|
||||
|
||||
updateButtonLabels();
|
||||
input_setter = {};
|
||||
}
|
||||
|
||||
void ConfigureInput::keyPressEvent(QKeyEvent* event) {
|
||||
if (!input_setter || !event)
|
||||
return;
|
||||
|
||||
if (event->key() != Qt::Key_Escape) {
|
||||
if (want_keyboard_keys) {
|
||||
setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
|
||||
false);
|
||||
} else {
|
||||
// Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
|
||||
return;
|
||||
}
|
||||
}
|
||||
setPollingResult({}, true);
|
||||
ui->use_docked_mode->setCheckState(Qt::Unchecked);
|
||||
ui->handheld_connected->setCheckState(Qt::Unchecked);
|
||||
ui->mouse_enabled->setCheckState(Qt::Unchecked);
|
||||
ui->keyboard_enabled->setCheckState(Qt::Unchecked);
|
||||
ui->debug_enabled->setCheckState(Qt::Unchecked);
|
||||
ui->touchscreen_enabled->setCheckState(Qt::Checked);
|
||||
updateUIEnabled();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "core/settings.h"
|
||||
#include "input_common/main.h"
|
||||
#include "ui_configure_input.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
|
||||
class QPushButton;
|
||||
class QString;
|
||||
@@ -37,57 +38,20 @@ public:
|
||||
void applyConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureInput> ui;
|
||||
void updateUIEnabled();
|
||||
|
||||
std::unique_ptr<QTimer> timeout_timer;
|
||||
std::unique_ptr<QTimer> poll_timer;
|
||||
template <typename Dialog, typename... Args>
|
||||
void CallConfigureDialog(Args&&... args);
|
||||
|
||||
/// This will be the the setting function when an input is awaiting configuration.
|
||||
std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
|
||||
|
||||
std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
|
||||
std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
|
||||
|
||||
static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
|
||||
|
||||
/// Each button input is represented by a QPushButton.
|
||||
std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
|
||||
|
||||
/// A group of five QPushButtons represent one analog input. The buttons each represent up,
|
||||
/// down, left, right, and modifier, respectively.
|
||||
std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
|
||||
analog_map_buttons;
|
||||
|
||||
/// Analog inputs are also represented each with a single button, used to configure with an
|
||||
/// actual analog stick
|
||||
std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
|
||||
|
||||
static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
|
||||
|
||||
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
|
||||
|
||||
/// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
|
||||
/// keyboard events are ignored.
|
||||
bool want_keyboard_keys = false;
|
||||
void OnDockedModeChanged(bool last_state, bool new_state);
|
||||
|
||||
/// Load configuration settings.
|
||||
void loadConfiguration();
|
||||
/// Restore all buttons to their default values.
|
||||
void restoreDefaults();
|
||||
/// Clear all input configuration
|
||||
void ClearAll();
|
||||
|
||||
/// Update UI to reflect current configuration.
|
||||
void updateButtonLabels();
|
||||
std::unique_ptr<Ui::ConfigureInput> ui;
|
||||
|
||||
/// Called when the button was pressed.
|
||||
void handleClick(QPushButton* button,
|
||||
std::function<void(const Common::ParamPackage&)> new_input_setter,
|
||||
InputCommon::Polling::DeviceType type);
|
||||
|
||||
/// Finish polling and configure input using the input_setter
|
||||
void setPollingResult(const Common::ParamPackage& params, bool abort);
|
||||
|
||||
/// Handle key press events.
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
std::array<QComboBox*, 8> players_controller;
|
||||
std::array<QPushButton*, 8> players_configure;
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user