Compare commits
108 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34e4aaddd9 | ||
|
|
048da7240d | ||
|
|
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 | ||
|
|
f02b125ac8 | ||
|
|
6dc33fb812 | ||
|
|
5e6ad795cc | ||
|
|
997c3dc6ff | ||
|
|
11a1442229 | ||
|
|
3e93c30630 | ||
|
|
e34d47e6e3 | ||
|
|
f08b4cbbc8 | ||
|
|
9a47e40dd6 | ||
|
|
da238db6df | ||
|
|
611141e09f | ||
|
|
29e7c76d66 | ||
|
|
9a1bac840e | ||
|
|
13f79cc5bd | ||
|
|
4d1a0a24cc | ||
|
|
81a9c5fe6f | ||
|
|
b312cca756 | ||
|
|
5297495c87 | ||
|
|
e69eb3c760 | ||
|
|
53b4a1af0f | ||
|
|
8ed7e1af2c | ||
|
|
02c22a3440 | ||
|
|
1d60bb6544 | ||
|
|
6a2aa6dbdb | ||
|
|
1881e86c43 | ||
|
|
c91dc417d5 | ||
|
|
a819116154 | ||
|
|
03f274d8c1 | ||
|
|
0072275d25 | ||
|
|
6273a7aeee | ||
|
|
0149b4245c | ||
|
|
6552d75be1 | ||
|
|
224fcabe46 | ||
|
|
585e6fd426 | ||
|
|
c440e8b8e1 | ||
|
|
7a400e2191 | ||
|
|
3c7ba00d73 | ||
|
|
d21b2164e9 | ||
|
|
14cede1a0c | ||
|
|
405dd03dae | ||
|
|
241646f423 | ||
|
|
5b8f70ea2e | ||
|
|
0b701751da | ||
|
|
238bc4a077 | ||
|
|
e156834297 | ||
|
|
786995a81e | ||
|
|
e8899e7027 | ||
|
|
e658118aa9 | ||
|
|
d104478f1b | ||
|
|
4476fd29d6 | ||
|
|
e634ce847f | ||
|
|
84364c9d4f | ||
|
|
5c9c33e8ad | ||
|
|
edd5b6f12f | ||
|
|
b725d1fdf7 | ||
|
|
287ee1cd63 | ||
|
|
87eca5b209 | ||
|
|
75640c9c71 | ||
|
|
8cb2e7d881 | ||
|
|
f7319b0d3c | ||
|
|
97605e36f7 | ||
|
|
98060c6f7b | ||
|
|
95d3965f31 | ||
|
|
3844b5c0c5 | ||
|
|
3619b31fc0 | ||
|
|
f9db75fe40 | ||
|
|
9ea8eb6b2e | ||
|
|
9a0fb7d9fb | ||
|
|
ba2cdcdc5a | ||
|
|
52e7e8eed3 | ||
|
|
ab7472345b | ||
|
|
2d2ef05d8c | ||
|
|
8f183a47dd | ||
|
|
97d425c304 | ||
|
|
e20db909ee | ||
|
|
44c80e5d8b | ||
|
|
1c0226815d | ||
|
|
48eb3742b9 | ||
|
|
9078bb9854 | ||
|
|
34eaf3a366 |
@@ -1,3 +1,3 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash -ex /yuzu/.travis/clang-format/docker.sh
|
||||
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache citraemu/build-environments:linux-clang-format /bin/bash -ex /yuzu/.travis/clang-format/docker.sh
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
docker pull ubuntu:18.04
|
||||
docker pull citraemu/build-environments:linux-clang-format
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
apt-get update
|
||||
apt-get install -y clang-format-6.0
|
||||
|
||||
# Run clang-format
|
||||
cd /yuzu
|
||||
./.travis/clang-format/script.sh
|
||||
|
||||
@@ -57,3 +57,4 @@ done
|
||||
|
||||
pip3 install pefile
|
||||
python3 .travis/linux-mingw/scan_dll.py package/*.exe "package/"
|
||||
python3 .travis/linux-mingw/scan_dll.py package/imageformats/*.dll "package/"
|
||||
|
||||
23
appveyor.yml
23
appveyor.yml
@@ -125,17 +125,6 @@ after_build:
|
||||
Copy-Item -path "$CMAKE_SOURCE_DIR/license.txt" -destination $RELEASE_DIST
|
||||
Copy-Item -path "$CMAKE_SOURCE_DIR/README.md" -destination $RELEASE_DIST
|
||||
|
||||
# copy all the dll dependencies to the release folder
|
||||
. "./.appveyor/UtilityFunctions.ps1"
|
||||
$DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH"
|
||||
$MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe"
|
||||
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe"
|
||||
Write-Host "Detected the following dependencies:"
|
||||
Write-Host $MingwDLLs
|
||||
foreach ($file in $MingwDLLs) {
|
||||
Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
|
||||
}
|
||||
|
||||
# copy the qt windows plugin dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/platforms/qwindows.dll" -force -destination "$RELEASE_DIST/platforms"
|
||||
|
||||
@@ -145,6 +134,18 @@ after_build:
|
||||
# copy the qt jpeg imageformat dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
|
||||
|
||||
# copy all the dll dependencies to the release folder
|
||||
. "./.appveyor/UtilityFunctions.ps1"
|
||||
$DLLSearchPath = "C:\msys64\mingw64\bin;$env:PATH"
|
||||
$MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu.exe"
|
||||
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\yuzu_cmd.exe"
|
||||
$MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\imageformats\qjpeg.dll"
|
||||
Write-Host "Detected the following dependencies:"
|
||||
Write-Host $MingwDLLs
|
||||
foreach ($file in $MingwDLLs) {
|
||||
Copy-Item -path "$file" -force -destination "$RELEASE_DIST"
|
||||
}
|
||||
|
||||
7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
|
||||
7z a $MINGW_SEVENZIP $RELEASE_DIST
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@ else()
|
||||
endif()
|
||||
|
||||
if(NOT HEAD_HASH)
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
if(EXISTS "@GIT_DATA@/head-ref")
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
else()
|
||||
set(HEAD_HASH "Unknown")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -117,21 +117,21 @@ private:
|
||||
// We don't delete it because we want BitField to be trivially copyable.
|
||||
constexpr BitField& operator=(const BitField&) = default;
|
||||
|
||||
// StorageType is T for non-enum types and the underlying type of T if
|
||||
// UnderlyingType is T for non-enum types and the underlying type of T if
|
||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||
// former case to workaround compile errors which arise when using
|
||||
// std::underlying_type<T>::type directly.
|
||||
using StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>,
|
||||
std::enable_if<true, T>>::type;
|
||||
using UnderlyingType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
|
||||
std::enable_if<true, T>>::type;
|
||||
|
||||
// Unsigned version of StorageType
|
||||
using StorageTypeU = std::make_unsigned_t<StorageType>;
|
||||
// We store the value as the unsigned type to avoid undefined behaviour on value shifting
|
||||
using StorageType = std::make_unsigned_t<UnderlyingType>;
|
||||
|
||||
public:
|
||||
/// Constants to allow limited introspection of fields if needed
|
||||
static constexpr std::size_t position = Position;
|
||||
static constexpr std::size_t bits = Bits;
|
||||
static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
|
||||
static constexpr StorageType mask = (((StorageType)~0) >> (8 * sizeof(T) - bits)) << position;
|
||||
|
||||
/**
|
||||
* Formats a value by masking and shifting it according to the field parameters. A value
|
||||
@@ -148,11 +148,12 @@ public:
|
||||
* union in a constexpr context.
|
||||
*/
|
||||
static constexpr FORCE_INLINE T ExtractValue(const StorageType& storage) {
|
||||
if (std::numeric_limits<T>::is_signed) {
|
||||
if constexpr (std::numeric_limits<UnderlyingType>::is_signed) {
|
||||
std::size_t shift = 8 * sizeof(T) - bits;
|
||||
return (T)((storage << (shift - position)) >> shift);
|
||||
return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >>
|
||||
shift);
|
||||
} else {
|
||||
return (T)((storage & mask) >> position);
|
||||
return static_cast<T>((storage & mask) >> position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
|
||||
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_)
|
||||
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
|
||||
dump_root(std::move(dump_root_)),
|
||||
sysnand_cache(std::make_unique<RegisteredCache>(
|
||||
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
|
||||
usrnand_cache(std::make_unique<RegisteredCache>(
|
||||
@@ -32,4 +33,10 @@ VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
|
||||
return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
|
||||
}
|
||||
|
||||
VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
|
||||
if (title_id == 0)
|
||||
return nullptr;
|
||||
return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -17,17 +17,19 @@ class RegisteredCache;
|
||||
/// registered caches.
|
||||
class BISFactory {
|
||||
public:
|
||||
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
|
||||
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
|
||||
~BISFactory();
|
||||
|
||||
RegisteredCache* GetSystemNANDContents() const;
|
||||
RegisteredCache* GetUserNANDContents() const;
|
||||
|
||||
VirtualDir GetModificationLoadRoot(u64 title_id) const;
|
||||
VirtualDir GetModificationDumpRoot(u64 title_id) const;
|
||||
|
||||
private:
|
||||
VirtualDir nand_root;
|
||||
VirtualDir load_root;
|
||||
VirtualDir dump_root;
|
||||
|
||||
std::unique_ptr<RegisteredCache> sysnand_cache;
|
||||
std::unique_ptr<RegisteredCache> usrnand_cache;
|
||||
|
||||
@@ -176,7 +176,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
|
||||
if (file->GetExtension() != "nca")
|
||||
continue;
|
||||
auto nca = std::make_shared<NCA>(file);
|
||||
auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
|
||||
// TODO(DarkLordZach): Add proper Rev1+ Support
|
||||
if (nca->IsUpdate())
|
||||
continue;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace Loader {
|
||||
@@ -107,5 +108,7 @@ private:
|
||||
std::shared_ptr<NSP> secure_partition;
|
||||
std::shared_ptr<NCA> program;
|
||||
std::vector<std::shared_ptr<NCA>> ncas;
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
};
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -101,8 +101,9 @@ static bool IsValidNCA(const NCAHeader& header) {
|
||||
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
|
||||
}
|
||||
|
||||
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
|
||||
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
|
||||
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset,
|
||||
Core::Crypto::KeyManager keys_)
|
||||
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)), keys(std::move(keys_)) {
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
return;
|
||||
|
||||
@@ -79,7 +79,8 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
|
||||
class NCA : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
|
||||
u64 bktr_base_ivfc_offset = 0);
|
||||
u64 bktr_base_ivfc_offset = 0,
|
||||
Core::Crypto::KeyManager keys = Core::Crypto::KeyManager());
|
||||
~NCA() override;
|
||||
|
||||
Loader::ResultStatus GetStatus() const;
|
||||
|
||||
@@ -8,25 +8,10 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
NotFound = 1,
|
||||
TitleNotFound = 1002,
|
||||
SdCardNotFound = 2001,
|
||||
RomFSNotFound = 2520,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
|
||||
|
||||
// TODO(bunnei): Replace these with correct errors for Switch OS
|
||||
constexpr ResultCode ERROR_INVALID_PATH(-1);
|
||||
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
|
||||
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
|
||||
constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
|
||||
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
|
||||
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
|
||||
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
|
||||
constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
|
||||
constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
|
||||
constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
|
||||
constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
|
||||
constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
|
||||
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
@@ -119,6 +120,18 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
|
||||
const auto build_id_raw = Common::HexArrayToString(header.build_id);
|
||||
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
|
||||
|
||||
if (Settings::values.dump_nso) {
|
||||
LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id);
|
||||
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
|
||||
if (dump_dir != nullptr) {
|
||||
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
|
||||
const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id));
|
||||
|
||||
file->Resize(nso.size());
|
||||
file->WriteBytes(nso);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
|
||||
|
||||
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||
|
||||
@@ -106,9 +106,12 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
|
||||
|
||||
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
|
||||
std::string_view path) const {
|
||||
if (dir->GetFileRelative(path) != nullptr)
|
||||
return dir->GetFileRelative(path);
|
||||
if (dir->GetDirectoryRelative(path) != nullptr) {
|
||||
const auto file = dir->GetFileRelative(path);
|
||||
if (file != nullptr)
|
||||
return file;
|
||||
|
||||
const auto nca_dir = dir->GetDirectoryRelative(path);
|
||||
if (nca_dir != nullptr) {
|
||||
const auto nca_dir = dir->GetDirectoryRelative(path);
|
||||
VirtualFile file = nullptr;
|
||||
|
||||
@@ -225,7 +228,7 @@ void RegisteredCache::ProcessFiles(const std::vector<NcaID>& ids) {
|
||||
|
||||
if (file == nullptr)
|
||||
continue;
|
||||
const auto nca = std::make_shared<NCA>(parser(file, id));
|
||||
const auto nca = std::make_shared<NCA>(parser(file, id), nullptr, 0, keys);
|
||||
if (nca->GetStatus() != Loader::ResultStatus::Success ||
|
||||
nca->GetType() != NCAContentType::Meta) {
|
||||
continue;
|
||||
@@ -315,7 +318,7 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
|
||||
const auto raw = GetEntryRaw(title_id, type);
|
||||
if (raw == nullptr)
|
||||
return nullptr;
|
||||
return std::make_unique<NCA>(raw);
|
||||
return std::make_unique<NCA>(raw, nullptr, 0, keys);
|
||||
}
|
||||
|
||||
std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <vector>
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
@@ -133,6 +134,8 @@ private:
|
||||
|
||||
VirtualDir dir;
|
||||
RegisteredCacheParsingFunction parser;
|
||||
Core::Crypto::KeyManager keys;
|
||||
|
||||
// maps tid -> NcaID of meta
|
||||
boost::container::flat_map<u64, NcaID> meta_id;
|
||||
// maps tid -> meta
|
||||
|
||||
@@ -252,7 +252,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto next_nca = std::make_shared<NCA>(next_file);
|
||||
auto next_nca = std::make_shared<NCA>(next_file, nullptr, 0, keys);
|
||||
if (next_nca->GetType() == NCAContentType::Program)
|
||||
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
|
||||
if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
|
||||
|
||||
@@ -70,6 +70,8 @@ private:
|
||||
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
|
||||
std::vector<VirtualFile> ticket_files;
|
||||
|
||||
Core::Crypto::KeyManager keys;
|
||||
|
||||
VirtualFile romfs;
|
||||
VirtualDir exefs;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,58 +8,28 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
// Confirmed Switch OS error codes
|
||||
MaxConnectionsReached = 7,
|
||||
InvalidSize = 101,
|
||||
InvalidAddress = 102,
|
||||
HandleTableFull = 105,
|
||||
InvalidMemoryState = 106,
|
||||
InvalidMemoryPermissions = 108,
|
||||
InvalidMemoryRange = 110,
|
||||
InvalidThreadPriority = 112,
|
||||
InvalidProcessorId = 113,
|
||||
InvalidHandle = 114,
|
||||
InvalidPointer = 115,
|
||||
InvalidCombination = 116,
|
||||
Timeout = 117,
|
||||
SynchronizationCanceled = 118,
|
||||
TooLarge = 119,
|
||||
InvalidEnumValue = 120,
|
||||
NoSuchEntry = 121,
|
||||
AlreadyRegistered = 122,
|
||||
SessionClosed = 123,
|
||||
InvalidState = 125,
|
||||
ResourceLimitExceeded = 132,
|
||||
};
|
||||
}
|
||||
// Confirmed Switch kernel error codes
|
||||
|
||||
// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
|
||||
// double check that the code matches before re-using the constant.
|
||||
|
||||
constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
|
||||
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed);
|
||||
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
|
||||
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
|
||||
ErrCodes::MaxConnectionsReached);
|
||||
constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
|
||||
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
|
||||
ErrCodes::InvalidCombination);
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
|
||||
ErrCodes::InvalidMemoryPermissions);
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
|
||||
constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
|
||||
constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
|
||||
constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
|
||||
constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered);
|
||||
constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
|
||||
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
|
||||
ErrCodes::InvalidThreadPriority);
|
||||
constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
|
||||
constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
|
||||
constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
|
||||
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
|
||||
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
|
||||
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
|
||||
constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
|
||||
constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
|
||||
constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
|
||||
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112};
|
||||
constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114};
|
||||
constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
|
||||
constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
|
||||
constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
|
||||
constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
|
||||
constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
|
||||
constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
|
||||
constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
|
||||
constexpr ResultCode ERR_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
|
||||
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
|
||||
constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
|
||||
constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -123,6 +123,48 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
|
||||
LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (size == 0 || !Common::Is4KBAligned(size)) {
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(addr, size)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const auto permission = static_cast<MemoryPermission>(prot);
|
||||
if (permission != MemoryPermission::None && permission != MemoryPermission::Read &&
|
||||
permission != MemoryPermission::ReadWrite) {
|
||||
return ERR_INVALID_MEMORY_PERMISSIONS;
|
||||
}
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
auto& vm_manager = current_process->VMManager();
|
||||
|
||||
if (!IsInsideAddressSpace(vm_manager, addr, size)) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const VMManager::VMAHandle iter = vm_manager.FindVMA(addr);
|
||||
if (iter == vm_manager.vma_map.end()) {
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented.");
|
||||
// TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't
|
||||
// make sense to allow changing permissions on kernel memory itself, etc).
|
||||
|
||||
const auto converted_permissions = SharedMemory::ConvertPermissions(permission);
|
||||
|
||||
return vm_manager.ReprotectRange(addr, size, converted_permissions);
|
||||
}
|
||||
|
||||
static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
|
||||
LOG_WARNING(Kernel_SVC,
|
||||
"(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr,
|
||||
@@ -172,7 +214,7 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
|
||||
// Read 1 char beyond the max allowed port name to detect names that are too long.
|
||||
std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
|
||||
if (port_name.size() > PortNameMaxLength) {
|
||||
return ERR_PORT_NAME_TOO_LONG;
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
|
||||
@@ -268,8 +310,9 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
|
||||
|
||||
static constexpr u64 MaxHandles = 0x40;
|
||||
|
||||
if (handle_count > MaxHandles)
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge);
|
||||
if (handle_count > MaxHandles) {
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
auto* const thread = GetCurrentThread();
|
||||
|
||||
@@ -334,8 +377,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
|
||||
}
|
||||
|
||||
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
|
||||
thread->SetWaitSynchronizationResult(
|
||||
ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
|
||||
thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
|
||||
thread->ResumeFromWait();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@@ -564,7 +606,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
}
|
||||
|
||||
if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
|
||||
return ERR_INVALID_COMBINATION_KERNEL;
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
*result = current_process->GetRandomEntropy(info_sub_id);
|
||||
@@ -601,7 +643,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
case GetInfoType::ThreadTickCount: {
|
||||
constexpr u64 num_cpus = 4;
|
||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
|
||||
return ERR_INVALID_COMBINATION_KERNEL;
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
const auto thread =
|
||||
@@ -1139,7 +1181,7 @@ static ResultCode CloseHandle(Handle handle) {
|
||||
|
||||
/// Reset an event
|
||||
static ResultCode ResetSignal(Handle handle) {
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
|
||||
LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
auto event = handle_table.Get<Event>(handle);
|
||||
@@ -1194,7 +1236,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
}
|
||||
|
||||
if (mask == 0) {
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
/// This value is used to only change the affinity mask without changing the current ideal core.
|
||||
@@ -1203,12 +1245,12 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
if (core == OnlyChangeMask) {
|
||||
core = thread->GetIdealCore();
|
||||
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
|
||||
return ERR_INVALID_PROCESSOR_ID;
|
||||
}
|
||||
|
||||
// Error out if the input core isn't enabled in the input mask.
|
||||
if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
thread->ChangeCore(core, mask);
|
||||
@@ -1297,7 +1339,7 @@ struct FunctionDef {
|
||||
static const FunctionDef SVC_Table[] = {
|
||||
{0x00, nullptr, "Unknown"},
|
||||
{0x01, SvcWrap<SetHeapSize>, "SetHeapSize"},
|
||||
{0x02, nullptr, "SetMemoryPermission"},
|
||||
{0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"},
|
||||
{0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"},
|
||||
{0x04, SvcWrap<MapMemory>, "MapMemory"},
|
||||
{0x05, SvcWrap<UnmapMemory>, "UnmapMemory"},
|
||||
|
||||
@@ -121,6 +121,11 @@ void SvcWrap() {
|
||||
FuncReturn(func(Param(0), Param(1), Param(2)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64, u64, u32)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32, u64, u64, u32)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(
|
||||
|
||||
@@ -142,36 +142,7 @@ void Thread::ResumeFromWait() {
|
||||
|
||||
status = ThreadStatus::Ready;
|
||||
|
||||
std::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
}
|
||||
if (ideal_core != -1 &&
|
||||
Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
|
||||
new_processor_id = ideal_core;
|
||||
}
|
||||
|
||||
ASSERT(*new_processor_id < 4);
|
||||
|
||||
// Add thread to new core's scheduler
|
||||
auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
|
||||
|
||||
if (*new_processor_id != processor_id) {
|
||||
// Remove thread from previous core's scheduler
|
||||
scheduler->RemoveThread(this);
|
||||
next_scheduler->AddThread(this, current_priority);
|
||||
}
|
||||
|
||||
processor_id = *new_processor_id;
|
||||
|
||||
// If the thread was ready, unschedule from the previous core and schedule on the new core
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
next_scheduler->ScheduleThread(this, current_priority);
|
||||
|
||||
// Change thread's scheduler
|
||||
scheduler = next_scheduler;
|
||||
|
||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||
ChangeScheduler();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,42 +335,45 @@ void Thread::UpdatePriority() {
|
||||
void Thread::ChangeCore(u32 core, u64 mask) {
|
||||
ideal_core = core;
|
||||
affinity_mask = mask;
|
||||
ChangeScheduler();
|
||||
}
|
||||
|
||||
void Thread::ChangeScheduler() {
|
||||
if (status != ThreadStatus::Ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
|
||||
|
||||
if (!new_processor_id) {
|
||||
new_processor_id = processor_id;
|
||||
}
|
||||
if (ideal_core != -1 &&
|
||||
Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
|
||||
if (ideal_core != -1 && system.Scheduler(ideal_core).GetCurrentThread() == nullptr) {
|
||||
new_processor_id = ideal_core;
|
||||
}
|
||||
|
||||
ASSERT(*new_processor_id < 4);
|
||||
|
||||
// Add thread to new core's scheduler
|
||||
auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
|
||||
auto& next_scheduler = system.Scheduler(*new_processor_id);
|
||||
|
||||
if (*new_processor_id != processor_id) {
|
||||
// Remove thread from previous core's scheduler
|
||||
scheduler->RemoveThread(this);
|
||||
next_scheduler->AddThread(this, current_priority);
|
||||
next_scheduler.AddThread(this, current_priority);
|
||||
}
|
||||
|
||||
processor_id = *new_processor_id;
|
||||
|
||||
// If the thread was ready, unschedule from the previous core and schedule on the new core
|
||||
scheduler->UnscheduleThread(this, current_priority);
|
||||
next_scheduler->ScheduleThread(this, current_priority);
|
||||
next_scheduler.ScheduleThread(this, current_priority);
|
||||
|
||||
// Change thread's scheduler
|
||||
scheduler = next_scheduler;
|
||||
scheduler = &next_scheduler;
|
||||
|
||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||
system.CpuCore(processor_id).PrepareReschedule();
|
||||
}
|
||||
|
||||
bool Thread::AllWaitObjectsReady() {
|
||||
|
||||
@@ -374,6 +374,8 @@ private:
|
||||
explicit Thread(KernelCore& kernel);
|
||||
~Thread() override;
|
||||
|
||||
void ChangeScheduler();
|
||||
|
||||
Core::ARM_Interface::ThreadContext context{};
|
||||
|
||||
u32 thread_id = 0;
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
enum class ErrorDescription : u32 {
|
||||
Success = 0,
|
||||
RemoteProcessDead = 301,
|
||||
InvalidOffset = 6061,
|
||||
InvalidLength = 6062,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -203,8 +203,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
ISelfController::~ISelfController() = default;
|
||||
|
||||
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
|
||||
// Takes 3 input u8s with each field located immediately after the previous u8, these are
|
||||
// bool flags. No output.
|
||||
// Takes 3 input u8s with each field located immediately after the previous
|
||||
// u8, these are bool flags. No output.
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
@@ -258,8 +258,8 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
|
||||
}
|
||||
|
||||
void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
|
||||
// Takes 3 input u8s with each field located immediately after the previous u8, these are
|
||||
// bool flags. No output.
|
||||
// Takes 3 input u8s with each field located immediately after the previous
|
||||
// u8, these are bool flags. No output.
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
bool enabled = rp.Pop<bool>();
|
||||
@@ -302,8 +302,8 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
|
||||
}
|
||||
|
||||
void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just create the layer
|
||||
// in the Default display.
|
||||
// TODO(Subv): Find out how AM determines the display to use, for now just
|
||||
// create the layer in the Default display.
|
||||
u64 display_id = nvflinger->OpenDisplay("Default");
|
||||
u64 layer_id = nvflinger->CreateLayer(display_id);
|
||||
|
||||
@@ -733,7 +733,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
{70, nullptr, "RequestToShutdown"},
|
||||
{71, nullptr, "RequestToReboot"},
|
||||
{80, nullptr, "ExitAndRequestToShowThanksMessage"},
|
||||
{90, nullptr, "EnableApplicationCrashReport"},
|
||||
{90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
|
||||
{100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
|
||||
{101, nullptr, "SetApplicationCopyrightImage"},
|
||||
{102, nullptr, "SetApplicationCopyrightVisibility"},
|
||||
@@ -752,6 +752,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
|
||||
|
||||
IApplicationFunctions::~IApplicationFunctions() = default;
|
||||
|
||||
void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -821,7 +827,8 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
|
||||
// Takes an input u32 Result, no output.
|
||||
// For example, in some cases official apps use this with error 0x2A2 then uses svcBreak.
|
||||
// For example, in some cases official apps use this with error 0x2A2 then
|
||||
// uses svcBreak.
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 result = rp.Pop<u32>();
|
||||
@@ -884,8 +891,8 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager,
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
|
||||
auto message_queue = std::make_shared<AppletMessageQueue>();
|
||||
message_queue->PushMessage(
|
||||
AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on game boot
|
||||
message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
|
||||
// game boot
|
||||
|
||||
std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
|
||||
std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
|
||||
|
||||
@@ -185,6 +185,7 @@ private:
|
||||
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
|
||||
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||
void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
|
||||
void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
|
||||
|
||||
@@ -28,13 +28,13 @@ public:
|
||||
{1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
|
||||
{2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
|
||||
{3, &IAudioRenderer::GetState, "GetState"},
|
||||
{4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
|
||||
{4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
|
||||
{5, &IAudioRenderer::Start, "Start"},
|
||||
{6, &IAudioRenderer::Stop, "Stop"},
|
||||
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
|
||||
{8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
|
||||
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
|
||||
{10, nullptr, "RequestUpdateAuto"},
|
||||
{10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
|
||||
{11, nullptr, "ExecuteAudioRendererRendering"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -79,7 +79,7 @@ private:
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
}
|
||||
|
||||
void RequestUpdate(Kernel::HLERequestContext& ctx) {
|
||||
void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
|
||||
ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -77,8 +77,8 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(consumed);
|
||||
rb.Push<u64>(performance);
|
||||
rb.Push<u32>(sample_count);
|
||||
rb.Push<u64>(performance);
|
||||
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,49 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/btdrv/btdrv.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::BtDrv {
|
||||
|
||||
class Bt final : public ServiceFramework<Bt> {
|
||||
public:
|
||||
explicit Bt() : ServiceFramework{"bt"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Unknown0"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, &Bt::RegisterEvent, "RegisterEvent"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void RegisterEvent(Kernel::HLERequestContext& ctx) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
register_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "BT:RegisterEvent");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(register_event);
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
}
|
||||
Kernel::SharedPtr<Kernel::Event> register_event;
|
||||
};
|
||||
|
||||
class BtDrv final : public ServiceFramework<BtDrv> {
|
||||
public:
|
||||
explicit BtDrv() : ServiceFramework{"btdrv"} {
|
||||
@@ -67,6 +104,7 @@ public:
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<BtDrv>()->InstallAsService(sm);
|
||||
std::make_shared<Bt>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::BtDrv
|
||||
|
||||
@@ -6,13 +6,118 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/btm/btm.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::BTM {
|
||||
|
||||
class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
|
||||
public:
|
||||
explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IBtmUserCore::GetScanEvent, "GetScanEvent"},
|
||||
{1, nullptr, "Unknown1"},
|
||||
{2, nullptr, "Unknown2"},
|
||||
{3, nullptr, "Unknown3"},
|
||||
{4, nullptr, "Unknown4"},
|
||||
{5, nullptr, "Unknown5"},
|
||||
{6, nullptr, "Unknown6"},
|
||||
{7, nullptr, "Unknown7"},
|
||||
{8, nullptr, "Unknown8"},
|
||||
{9, nullptr, "Unknown9"},
|
||||
{10, nullptr, "Unknown10"},
|
||||
{17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"},
|
||||
{18, nullptr, "Unknown18"},
|
||||
{19, nullptr, "Unknown19"},
|
||||
{20, nullptr, "Unknown20"},
|
||||
{21, nullptr, "Unknown21"},
|
||||
{22, nullptr, "Unknown22"},
|
||||
{23, nullptr, "Unknown23"},
|
||||
{24, nullptr, "Unknown24"},
|
||||
{25, nullptr, "Unknown25"},
|
||||
{26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"},
|
||||
{27, nullptr, "Unknown27"},
|
||||
{28, nullptr, "Unknown28"},
|
||||
{29, nullptr, "Unknown29"},
|
||||
{30, nullptr, "Unknown30"},
|
||||
{31, nullptr, "Unknown31"},
|
||||
{32, nullptr, "Unknown32"},
|
||||
{33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"},
|
||||
{34, nullptr, "Unknown34"},
|
||||
{35, nullptr, "Unknown35"},
|
||||
{36, nullptr, "Unknown36"},
|
||||
{37, nullptr, "Unknown37"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetScanEvent(Kernel::HLERequestContext& ctx) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
scan_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ScanEvent");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(scan_event);
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
}
|
||||
void GetConnectionEvent(Kernel::HLERequestContext& ctx) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
connection_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"IBtmUserCore:ConnectionEvent");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(connection_event);
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
}
|
||||
void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
service_discovery =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(service_discovery);
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
}
|
||||
void GetConfigEvent(Kernel::HLERequestContext& ctx) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
config_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConfigEvent");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(config_event);
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
}
|
||||
Kernel::SharedPtr<Kernel::Event> scan_event;
|
||||
Kernel::SharedPtr<Kernel::Event> connection_event;
|
||||
Kernel::SharedPtr<Kernel::Event> service_discovery;
|
||||
Kernel::SharedPtr<Kernel::Event> config_event;
|
||||
};
|
||||
|
||||
class BTM_USR final : public ServiceFramework<BTM_USR> {
|
||||
public:
|
||||
explicit BTM_USR() : ServiceFramework{"btm:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &BTM_USR::GetCoreImpl, "GetCoreImpl"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void GetCoreImpl(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IBtmUserCore>();
|
||||
LOG_DEBUG(Service_BTM, "called");
|
||||
}
|
||||
};
|
||||
|
||||
class BTM final : public ServiceFramework<BTM> {
|
||||
public:
|
||||
explicit BTM() : ServiceFramework{"btm"} {
|
||||
@@ -116,6 +221,7 @@ void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<BTM>()->InstallAsService(sm);
|
||||
std::make_shared<BTM_DBG>()->InstallAsService(sm);
|
||||
std::make_shared<BTM_SYS>()->InstallAsService(sm);
|
||||
std::make_shared<BTM_USR>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
} // namespace Service::BTM
|
||||
|
||||
@@ -303,7 +303,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
static_cast<u8>(space), save_struct.DebugInfo());
|
||||
|
||||
if (save_data_factory == nullptr) {
|
||||
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound);
|
||||
return FileSys::ERROR_ENTITY_NOT_FOUND;
|
||||
}
|
||||
|
||||
return save_data_factory->Open(space, save_struct);
|
||||
@@ -313,7 +313,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space)
|
||||
LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
|
||||
|
||||
if (save_data_factory == nullptr) {
|
||||
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound);
|
||||
return FileSys::ERROR_ENTITY_NOT_FOUND;
|
||||
}
|
||||
|
||||
return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
|
||||
@@ -323,15 +323,22 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
|
||||
LOG_TRACE(Service_FS, "Opening SDMC");
|
||||
|
||||
if (sdmc_factory == nullptr) {
|
||||
return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound);
|
||||
return FileSys::ERROR_SD_CARD_NOT_FOUND;
|
||||
}
|
||||
|
||||
return sdmc_factory->Open();
|
||||
}
|
||||
|
||||
std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
|
||||
return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
|
||||
GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
|
||||
std::shared_ptr<FileSys::RegisteredCacheUnion> registered_cache_union;
|
||||
|
||||
std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
|
||||
if (registered_cache_union == nullptr) {
|
||||
registered_cache_union =
|
||||
std::make_shared<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
|
||||
GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
|
||||
}
|
||||
|
||||
return registered_cache_union;
|
||||
}
|
||||
|
||||
FileSys::RegisteredCache* GetSystemNANDContents() {
|
||||
@@ -370,6 +377,15 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
|
||||
return bis_factory->GetModificationLoadRoot(title_id);
|
||||
}
|
||||
|
||||
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
|
||||
LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return bis_factory->GetModificationDumpRoot(title_id);
|
||||
}
|
||||
|
||||
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
|
||||
if (overwrite) {
|
||||
bis_factory = nullptr;
|
||||
@@ -383,13 +399,21 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
|
||||
FileSys::Mode::ReadWrite);
|
||||
auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
|
||||
FileSys::Mode::ReadWrite);
|
||||
auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
|
||||
FileSys::Mode::ReadWrite);
|
||||
|
||||
if (bis_factory == nullptr)
|
||||
bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
|
||||
if (save_data_factory == nullptr)
|
||||
if (bis_factory == nullptr) {
|
||||
bis_factory =
|
||||
std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
|
||||
}
|
||||
|
||||
if (save_data_factory == nullptr) {
|
||||
save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
|
||||
if (sdmc_factory == nullptr)
|
||||
}
|
||||
|
||||
if (sdmc_factory == nullptr) {
|
||||
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
|
||||
}
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
|
||||
|
||||
@@ -48,13 +48,14 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC();
|
||||
|
||||
std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
|
||||
std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
|
||||
|
||||
FileSys::RegisteredCache* GetSystemNANDContents();
|
||||
FileSys::RegisteredCache* GetUserNANDContents();
|
||||
FileSys::RegisteredCache* GetSDMCContents();
|
||||
|
||||
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
|
||||
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
|
||||
|
||||
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
|
||||
// above is called.
|
||||
|
||||
@@ -63,12 +63,12 @@ private:
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
|
||||
rb.Push(FileSys::ERROR_INVALID_SIZE);
|
||||
return;
|
||||
}
|
||||
if (offset < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
|
||||
rb.Push(FileSys::ERROR_INVALID_OFFSET);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -108,12 +108,12 @@ private:
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
|
||||
rb.Push(FileSys::ERROR_INVALID_SIZE);
|
||||
return;
|
||||
}
|
||||
if (offset < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
|
||||
rb.Push(FileSys::ERROR_INVALID_OFFSET);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -139,12 +139,12 @@ private:
|
||||
// Error checking
|
||||
if (length < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
|
||||
rb.Push(FileSys::ERROR_INVALID_SIZE);
|
||||
return;
|
||||
}
|
||||
if (offset < 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
|
||||
rb.Push(FileSys::ERROR_INVALID_OFFSET);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -744,7 +744,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
if (dir.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
|
||||
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
|
||||
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -836,7 +836,7 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
|
||||
static_cast<u8>(storage_id), title_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
|
||||
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
|
||||
}
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
||||
@@ -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,43 @@ 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.end(), 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,6 +31,66 @@ enum class JoystickId : std::size_t {
|
||||
Joystick_Right,
|
||||
};
|
||||
|
||||
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:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
return npad_id;
|
||||
case 8:
|
||||
case NPAD_HANDHELD:
|
||||
return 8;
|
||||
case 9:
|
||||
case NPAD_UNKNOWN:
|
||||
return 9;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -56,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);
|
||||
@@ -81,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;
|
||||
}
|
||||
@@ -90,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;
|
||||
@@ -121,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;
|
||||
@@ -176,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];
|
||||
@@ -281,20 +338,64 @@ 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) {
|
||||
case NPadControllerType::Handheld:
|
||||
handheld_entry.connection_status.raw = 0;
|
||||
handheld_entry.connection_status.IsConnected.Assign(1);
|
||||
if (!Settings::values.use_docked_mode) {
|
||||
handheld_entry.connection_status.IsWired.Assign(1);
|
||||
}
|
||||
handheld_entry.pad_states.raw = pad_state.raw;
|
||||
handheld_entry.l_stick = lstick_entry;
|
||||
handheld_entry.r_stick = rstick_entry;
|
||||
handheld_entry.connection_status.IsWired.Assign(1);
|
||||
handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
|
||||
handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
|
||||
handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
|
||||
handheld_entry.connection_status.IsRightJoyWired.Assign(1);
|
||||
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;
|
||||
@@ -307,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;
|
||||
@@ -333,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;
|
||||
@@ -371,14 +472,29 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
|
||||
supported_npad_id_types.clear();
|
||||
supported_npad_id_types.resize(length / sizeof(u32));
|
||||
std::memcpy(supported_npad_id_types.data(), data, length);
|
||||
bool had_controller_update = false;
|
||||
for (std::size_t i = 0; i < connected_controllers.size(); i++) {
|
||||
auto& controller = connected_controllers[i];
|
||||
if (!controller.is_connected) {
|
||||
continue;
|
||||
}
|
||||
if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
|
||||
controller.type = DecideBestController(PREFERRED_CONTROLLER);
|
||||
InitNewlyAddedControler(i);
|
||||
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(requested_controller);
|
||||
} else {
|
||||
controller.type = requested_controller;
|
||||
InitNewlyAddedControler(i);
|
||||
}
|
||||
had_controller_update = true;
|
||||
}
|
||||
if (had_controller_update) {
|
||||
styleset_changed_event->Signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -412,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
|
||||
}
|
||||
@@ -439,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);
|
||||
@@ -457,16 +567,54 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
|
||||
InitNewlyAddedControler(controller_id);
|
||||
}
|
||||
|
||||
void Controller_NPad::ConnectNPad(u32 npad_id) {
|
||||
if (npad_id >= connected_controllers.size())
|
||||
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].is_connected = true;
|
||||
}
|
||||
|
||||
connected_controllers[npad_id] = {controller, true};
|
||||
InitNewlyAddedControler(npad_id);
|
||||
}
|
||||
|
||||
void Controller_NPad::ConnectNPad(u32 npad_id) {
|
||||
connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
|
||||
}
|
||||
|
||||
void Controller_NPad::DisconnectNPad(u32 npad_id) {
|
||||
if (npad_id >= connected_controllers.size())
|
||||
return;
|
||||
connected_controllers[npad_id].is_connected = false;
|
||||
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) {
|
||||
@@ -500,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;
|
||||
@@ -286,10 +286,10 @@ public:
|
||||
{519, nullptr, "GetPalmaOperationResult"},
|
||||
{520, nullptr, "ReadPalmaPlayLog"},
|
||||
{521, nullptr, "ResetPalmaPlayLog"},
|
||||
{522, nullptr, "SetIsPalmaAllConnectable"},
|
||||
{522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
|
||||
{523, nullptr, "SetIsPalmaPairedConnectable"},
|
||||
{524, nullptr, "PairPalma"},
|
||||
{525, nullptr, "SetPalmaBoostMode"},
|
||||
{525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
|
||||
{1000, nullptr, "SetNpadCommunicationMode"},
|
||||
{1001, nullptr, "GetNpadCommunicationMode"},
|
||||
};
|
||||
@@ -596,6 +596,18 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
}
|
||||
|
||||
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
}
|
||||
};
|
||||
|
||||
class HidDbg final : public ServiceFramework<HidDbg> {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -28,8 +28,9 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
std::size_t size = ctx.GetWriteBufferSize();
|
||||
|
||||
std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max());
|
||||
std::vector<u8> data(size);
|
||||
std::generate(data.begin(), data.end(), rng);
|
||||
std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); });
|
||||
|
||||
ctx.WriteBuffer(data);
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
|
||||
{300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
|
||||
{400, &Time::GetClockSnapshot, "GetClockSnapshot"},
|
||||
{401, nullptr, "GetClockSnapshotFromSystemClockContext"},
|
||||
{500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"},
|
||||
{500, &Time::CalculateStandardUserSystemClockDifferenceByUser,
|
||||
"CalculateStandardUserSystemClockDifferenceByUser"},
|
||||
{501, nullptr, "CalculateSpanBetween"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
|
||||
@@ -299,6 +299,21 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
|
||||
ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
|
||||
}
|
||||
|
||||
void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
|
||||
Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Time, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
|
||||
const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
|
||||
const u64 difference =
|
||||
snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<u64>(difference);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
|
||||
: ServiceFramework(name), time(std::move(time)) {}
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ public:
|
||||
void GetTimeZoneService(Kernel::HLERequestContext& ctx);
|
||||
void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
|
||||
void GetClockSnapshot(Kernel::HLERequestContext& ctx);
|
||||
void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> time;
|
||||
|
||||
@@ -237,6 +237,22 @@ private:
|
||||
Data data{};
|
||||
};
|
||||
|
||||
/// Represents a parcel containing one int '0' as its data
|
||||
/// Used by DetachBuffer and Disconnect
|
||||
class IGBPEmptyResponseParcel : public Parcel {
|
||||
protected:
|
||||
void SerializeData() override {
|
||||
Write(data);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Data {
|
||||
u32_le unk_0;
|
||||
};
|
||||
|
||||
Data data{};
|
||||
};
|
||||
|
||||
class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
|
||||
public:
|
||||
explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer)
|
||||
@@ -554,6 +570,12 @@ private:
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
} else if (transaction == TransactionId::CancelBuffer) {
|
||||
LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
|
||||
} else if (transaction == TransactionId::Disconnect ||
|
||||
transaction == TransactionId::DetachBuffer) {
|
||||
const auto buffer = ctx.ReadBuffer();
|
||||
|
||||
IGBPEmptyResponseParcel response{};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
} else {
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAd
|
||||
program_image.resize(image_size);
|
||||
|
||||
// Apply patches if necessary
|
||||
if (pm && pm->HasNSOPatch(nso_header.build_id)) {
|
||||
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
|
||||
std::vector<u8> pi_header(program_image.size() + 0x100);
|
||||
std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
|
||||
std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
|
||||
|
||||
@@ -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_nso;
|
||||
|
||||
// WebService
|
||||
bool enable_telemetry;
|
||||
|
||||
@@ -34,8 +34,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
// Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
|
||||
// needed for ARMS.
|
||||
for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
|
||||
regs.viewport[viewport].depth_range_near = 0.0f;
|
||||
regs.viewport[viewport].depth_range_far = 1.0f;
|
||||
regs.viewports[viewport].depth_range_near = 0.0f;
|
||||
regs.viewports[viewport].depth_range_far = 1.0f;
|
||||
}
|
||||
// Doom and Bomberman seems to use the uninitialized registers and just enable blend
|
||||
// so initialize blend registers with sane values
|
||||
@@ -66,6 +66,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
regs.stencil_back_func_func = Regs::ComparisonOp::Always;
|
||||
regs.stencil_back_func_mask = 0xFFFFFFFF;
|
||||
regs.stencil_back_mask = 0xFFFFFFFF;
|
||||
// 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;
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
|
||||
|
||||
@@ -480,6 +480,67 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
struct ViewportTransform {
|
||||
f32 scale_x;
|
||||
f32 scale_y;
|
||||
f32 scale_z;
|
||||
f32 translate_x;
|
||||
f32 translate_y;
|
||||
f32 translate_z;
|
||||
INSERT_PADDING_WORDS(2);
|
||||
|
||||
MathUtil::Rectangle<s32> GetRect() const {
|
||||
return {
|
||||
GetX(), // left
|
||||
GetY() + GetHeight(), // top
|
||||
GetX() + GetWidth(), // right
|
||||
GetY() // bottom
|
||||
};
|
||||
};
|
||||
|
||||
s32 GetX() const {
|
||||
return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
|
||||
}
|
||||
|
||||
s32 GetY() const {
|
||||
return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
|
||||
}
|
||||
|
||||
s32 GetWidth() const {
|
||||
return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
|
||||
}
|
||||
|
||||
s32 GetHeight() const {
|
||||
return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
|
||||
}
|
||||
};
|
||||
|
||||
struct ScissorTest {
|
||||
u32 enable;
|
||||
union {
|
||||
BitField<0, 16, u32> min_x;
|
||||
BitField<16, 16, u32> max_x;
|
||||
};
|
||||
union {
|
||||
BitField<0, 16, u32> min_y;
|
||||
BitField<16, 16, u32> max_y;
|
||||
};
|
||||
u32 fill;
|
||||
};
|
||||
|
||||
struct ViewPort {
|
||||
union {
|
||||
BitField<0, 16, u32> x;
|
||||
BitField<16, 16, u32> width;
|
||||
};
|
||||
union {
|
||||
BitField<0, 16, u32> y;
|
||||
BitField<16, 16, u32> height;
|
||||
};
|
||||
float depth_range_near;
|
||||
float depth_range_far;
|
||||
};
|
||||
|
||||
bool IsShaderConfigEnabled(std::size_t index) const {
|
||||
// The VertexB is always enabled.
|
||||
if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
|
||||
@@ -505,55 +566,11 @@ public:
|
||||
|
||||
INSERT_PADDING_WORDS(0x2E);
|
||||
|
||||
RenderTargetConfig rt[NumRenderTargets];
|
||||
std::array<RenderTargetConfig, NumRenderTargets> rt;
|
||||
|
||||
struct {
|
||||
f32 scale_x;
|
||||
f32 scale_y;
|
||||
f32 scale_z;
|
||||
f32 translate_x;
|
||||
f32 translate_y;
|
||||
f32 translate_z;
|
||||
INSERT_PADDING_WORDS(2);
|
||||
std::array<ViewportTransform, NumViewports> viewport_transform;
|
||||
|
||||
MathUtil::Rectangle<s32> GetRect() const {
|
||||
return {
|
||||
GetX(), // left
|
||||
GetY() + GetHeight(), // top
|
||||
GetX() + GetWidth(), // right
|
||||
GetY() // bottom
|
||||
};
|
||||
};
|
||||
|
||||
s32 GetX() const {
|
||||
return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
|
||||
}
|
||||
|
||||
s32 GetY() const {
|
||||
return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
|
||||
}
|
||||
|
||||
s32 GetWidth() const {
|
||||
return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
|
||||
}
|
||||
|
||||
s32 GetHeight() const {
|
||||
return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
|
||||
}
|
||||
} viewport_transform[NumViewports];
|
||||
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 16, u32> x;
|
||||
BitField<16, 16, u32> width;
|
||||
};
|
||||
union {
|
||||
BitField<0, 16, u32> y;
|
||||
BitField<16, 16, u32> height;
|
||||
};
|
||||
float depth_range_near;
|
||||
float depth_range_far;
|
||||
} viewport[NumViewports];
|
||||
std::array<ViewPort, NumViewports> viewports;
|
||||
|
||||
INSERT_PADDING_WORDS(0x1D);
|
||||
|
||||
@@ -571,19 +588,9 @@ public:
|
||||
|
||||
INSERT_PADDING_WORDS(0x17);
|
||||
|
||||
struct {
|
||||
u32 enable;
|
||||
union {
|
||||
BitField<0, 16, u32> min_x;
|
||||
BitField<16, 16, u32> max_x;
|
||||
};
|
||||
union {
|
||||
BitField<0, 16, u32> min_y;
|
||||
BitField<16, 16, u32> max_y;
|
||||
};
|
||||
} scissor_test;
|
||||
std::array<ScissorTest, NumViewports> scissor_test;
|
||||
|
||||
INSERT_PADDING_WORDS(0x52);
|
||||
INSERT_PADDING_WORDS(0x15);
|
||||
|
||||
s32 stencil_back_func_ref;
|
||||
u32 stencil_back_mask;
|
||||
@@ -700,7 +707,9 @@ public:
|
||||
u32 stencil_front_func_mask;
|
||||
u32 stencil_front_mask;
|
||||
|
||||
INSERT_PADDING_WORDS(0x3);
|
||||
INSERT_PADDING_WORDS(0x2);
|
||||
|
||||
u32 frag_color_clamp;
|
||||
|
||||
union {
|
||||
BitField<4, 1, u32> triangle_rast_flip;
|
||||
@@ -718,7 +727,12 @@ public:
|
||||
|
||||
u32 zeta_enable;
|
||||
|
||||
INSERT_PADDING_WORDS(0x8);
|
||||
union {
|
||||
BitField<0, 1, u32> alpha_to_coverage;
|
||||
BitField<4, 1, u32> alpha_to_one;
|
||||
} multisample_control;
|
||||
|
||||
INSERT_PADDING_WORDS(0x7);
|
||||
|
||||
struct {
|
||||
u32 tsc_address_high;
|
||||
@@ -1100,8 +1114,8 @@ private:
|
||||
ASSERT_REG_POSITION(macros, 0x45);
|
||||
ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
|
||||
ASSERT_REG_POSITION(rt, 0x200);
|
||||
ASSERT_REG_POSITION(viewport_transform[0], 0x280);
|
||||
ASSERT_REG_POSITION(viewport, 0x300);
|
||||
ASSERT_REG_POSITION(viewport_transform, 0x280);
|
||||
ASSERT_REG_POSITION(viewports, 0x300);
|
||||
ASSERT_REG_POSITION(vertex_buffer, 0x35D);
|
||||
ASSERT_REG_POSITION(clear_color[0], 0x360);
|
||||
ASSERT_REG_POSITION(clear_depth, 0x364);
|
||||
@@ -1136,10 +1150,12 @@ ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4);
|
||||
ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5);
|
||||
ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
|
||||
ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
|
||||
ASSERT_REG_POSITION(frag_color_clamp, 0x4EA);
|
||||
ASSERT_REG_POSITION(screen_y_control, 0x4EB);
|
||||
ASSERT_REG_POSITION(vb_element_base, 0x50D);
|
||||
ASSERT_REG_POSITION(point_size, 0x546);
|
||||
ASSERT_REG_POSITION(zeta_enable, 0x54E);
|
||||
ASSERT_REG_POSITION(multisample_control, 0x54F);
|
||||
ASSERT_REG_POSITION(tsc, 0x557);
|
||||
ASSERT_REG_POSITION(tic, 0x55D);
|
||||
ASSERT_REG_POSITION(stencil_two_side_enable, 0x565);
|
||||
|
||||
@@ -107,8 +107,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
|
||||
|
||||
ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
|
||||
OpenGLState::ApplyDefaultState();
|
||||
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
|
||||
state.clip_distance[0] = true;
|
||||
|
||||
// Create render framebuffer
|
||||
framebuffer.Create();
|
||||
@@ -582,6 +580,8 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
|
||||
ConfigureFramebuffers(state);
|
||||
SyncColorMask();
|
||||
SyncFragmentColorClampState();
|
||||
SyncMultiSampleState();
|
||||
SyncDepthTestState();
|
||||
SyncStencilTestState();
|
||||
SyncBlendState();
|
||||
@@ -642,7 +642,7 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
params.DispatchDraw();
|
||||
|
||||
// Disable scissor test
|
||||
state.scissor.enabled = false;
|
||||
state.viewports[0].scissor.enabled = false;
|
||||
|
||||
accelerate_draw = AccelDraw::Disabled;
|
||||
|
||||
@@ -733,9 +733,8 @@ void RasterizerOpenGL::SamplerInfo::Create() {
|
||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) {
|
||||
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
|
||||
const GLuint s = sampler.handle;
|
||||
const Tegra::Texture::TSCEntry& config = info.tsc;
|
||||
if (mag_filter != config.mag_filter) {
|
||||
mag_filter = config.mag_filter;
|
||||
glSamplerParameteri(
|
||||
@@ -777,30 +776,50 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTex
|
||||
MaxwellToGL::DepthCompareFunc(depth_compare_func));
|
||||
}
|
||||
|
||||
if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border ||
|
||||
wrap_p == Tegra::Texture::WrapMode::Border) {
|
||||
const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g,
|
||||
config.border_color_b, config.border_color_a}};
|
||||
if (border_color != new_border_color) {
|
||||
border_color = new_border_color;
|
||||
glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
|
||||
GLvec4 new_border_color;
|
||||
if (config.srgb_conversion) {
|
||||
new_border_color[0] = config.srgb_border_color_r / 255.0f;
|
||||
new_border_color[1] = config.srgb_border_color_g / 255.0f;
|
||||
new_border_color[2] = config.srgb_border_color_g / 255.0f;
|
||||
} else {
|
||||
new_border_color[0] = config.border_color_r;
|
||||
new_border_color[1] = config.border_color_g;
|
||||
new_border_color[2] = config.border_color_b;
|
||||
}
|
||||
new_border_color[3] = config.border_color_a;
|
||||
|
||||
if (border_color != new_border_color) {
|
||||
border_color = new_border_color;
|
||||
glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
|
||||
}
|
||||
|
||||
const float anisotropic_max = static_cast<float>(1 << config.max_anisotropy.Value());
|
||||
if (anisotropic_max != max_anisotropic) {
|
||||
max_anisotropic = anisotropic_max;
|
||||
if (GLAD_GL_ARB_texture_filter_anisotropic) {
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropic);
|
||||
} else if (GLAD_GL_EXT_texture_filter_anisotropic) {
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropic);
|
||||
}
|
||||
}
|
||||
if (info.tic.use_header_opt_control == 0) {
|
||||
if (GLAD_GL_ARB_texture_filter_anisotropic) {
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY,
|
||||
static_cast<float>(1 << info.tic.max_anisotropy.Value()));
|
||||
} else if (GLAD_GL_EXT_texture_filter_anisotropic) {
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
||||
static_cast<float>(1 << info.tic.max_anisotropy.Value()));
|
||||
}
|
||||
glSamplerParameterf(s, GL_TEXTURE_MIN_LOD,
|
||||
static_cast<float>(info.tic.res_min_mip_level.Value()));
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_LOD,
|
||||
static_cast<float>(info.tic.res_max_mip_level.Value() == 0
|
||||
? 16
|
||||
: info.tic.res_max_mip_level.Value()));
|
||||
glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, info.tic.mip_lod_bias.Value() / 256.f);
|
||||
const float lod_min = static_cast<float>(config.min_lod_clamp.Value()) / 256.0f;
|
||||
if (lod_min != min_lod) {
|
||||
min_lod = lod_min;
|
||||
glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, min_lod);
|
||||
}
|
||||
|
||||
const float lod_max = static_cast<float>(config.max_lod_clamp.Value()) / 256.0f;
|
||||
if (lod_max != max_lod) {
|
||||
max_lod = lod_max;
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, max_lod);
|
||||
}
|
||||
const u32 bias = config.mip_lod_bias.Value();
|
||||
// Sign extend the 13-bit value.
|
||||
const 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;
|
||||
glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -899,7 +918,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
|
||||
continue;
|
||||
}
|
||||
|
||||
texture_samplers[current_bindpoint].SyncWithConfig(texture);
|
||||
texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
|
||||
Surface surface = res_cache.GetTextureSurface(texture, entry);
|
||||
if (surface != nullptr) {
|
||||
state.texture_units[current_bindpoint].texture = surface->Texture().handle;
|
||||
@@ -923,15 +942,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
|
||||
|
||||
void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
|
||||
for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
|
||||
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
|
||||
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.depth_range_far = regs.viewport[i].depth_range_far;
|
||||
viewport.depth_range_near = regs.viewport[i].depth_range_near;
|
||||
viewport.depth_range_far = regs.viewports[i].depth_range_far;
|
||||
viewport.depth_range_near = regs.viewports[i].depth_range_near;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1022,7 +1041,9 @@ void RasterizerOpenGL::SyncStencilTestState() {
|
||||
|
||||
void RasterizerOpenGL::SyncColorMask() {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
|
||||
const std::size_t count =
|
||||
regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1;
|
||||
for (std::size_t i = 0; i < count; i++) {
|
||||
const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i];
|
||||
auto& dest = state.color_mask[i];
|
||||
dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE;
|
||||
@@ -1032,6 +1053,17 @@ void RasterizerOpenGL::SyncColorMask() {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncMultiSampleState() {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0;
|
||||
state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncFragmentColorClampState() {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendState() {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
@@ -1043,43 +1075,40 @@ void RasterizerOpenGL::SyncBlendState() {
|
||||
state.independant_blend.enabled = regs.independent_blend_enable;
|
||||
if (!state.independant_blend.enabled) {
|
||||
auto& blend = state.blend[0];
|
||||
blend.enabled = regs.blend.enable[0] != 0;
|
||||
blend.separate_alpha = regs.blend.separate_alpha;
|
||||
blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb);
|
||||
blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb);
|
||||
blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb);
|
||||
if (blend.separate_alpha) {
|
||||
blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a);
|
||||
blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a);
|
||||
blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a);
|
||||
const auto& src = regs.blend;
|
||||
blend.enabled = src.enable[0] != 0;
|
||||
if (blend.enabled) {
|
||||
blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
|
||||
blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
|
||||
blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
|
||||
blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
|
||||
blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
|
||||
blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
|
||||
}
|
||||
for (size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
|
||||
for (std::size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
|
||||
state.blend[i].enabled = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
|
||||
for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
|
||||
auto& blend = state.blend[i];
|
||||
const auto& src = regs.independent_blend[i];
|
||||
blend.enabled = regs.blend.enable[i] != 0;
|
||||
if (!blend.enabled)
|
||||
continue;
|
||||
blend.separate_alpha = regs.independent_blend[i].separate_alpha;
|
||||
blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_rgb);
|
||||
blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_rgb);
|
||||
blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_rgb);
|
||||
if (blend.separate_alpha) {
|
||||
blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_a);
|
||||
blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_a);
|
||||
blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_a);
|
||||
}
|
||||
blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
|
||||
blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
|
||||
blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
|
||||
blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
|
||||
blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
|
||||
blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncLogicOpState() {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
// TODO(Subv): Support more than just render target 0.
|
||||
state.logic_op.enabled = regs.logic_op.enable != 0;
|
||||
|
||||
if (!state.logic_op.enabled)
|
||||
@@ -1092,19 +1121,21 @@ void RasterizerOpenGL::SyncLogicOpState() {
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncScissorTest() {
|
||||
// TODO: what is the correct behavior here, a single scissor for all targets
|
||||
// or scissor disabled for the rest of the targets?
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
state.scissor.enabled = (regs.scissor_test.enable != 0);
|
||||
if (regs.scissor_test.enable == 0) {
|
||||
return;
|
||||
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;
|
||||
dst.enabled = (src.enable != 0);
|
||||
if (dst.enabled == 0) {
|
||||
return;
|
||||
}
|
||||
const u32 width = src.max_x - src.min_x;
|
||||
const u32 height = src.max_y - src.min_y;
|
||||
dst.x = src.min_x;
|
||||
dst.y = src.min_y;
|
||||
dst.width = width;
|
||||
dst.height = height;
|
||||
}
|
||||
const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
|
||||
const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
|
||||
state.scissor.x = regs.scissor_test.min_x;
|
||||
state.scissor.y = regs.scissor_test.min_y;
|
||||
state.scissor.width = width;
|
||||
state.scissor.height = height;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncTransformFeedback() {
|
||||
@@ -1118,11 +1149,7 @@ void RasterizerOpenGL::SyncTransformFeedback() {
|
||||
|
||||
void RasterizerOpenGL::SyncPointState() {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
// TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
|
||||
// register carrying a default value. For now, if the point size is zero, assume it's
|
||||
// OpenGL's default (1).
|
||||
state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
|
||||
state.point.size = regs.point_size;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::CheckAlphaTests() {
|
||||
|
||||
@@ -88,7 +88,7 @@ private:
|
||||
/// SamplerInfo struct.
|
||||
void Create();
|
||||
/// Syncs the sampler object with the config, updating any necessary state.
|
||||
void SyncWithConfig(const Tegra::Texture::FullTextureInfo& info);
|
||||
void SyncWithConfig(const Tegra::Texture::TSCEntry& info);
|
||||
|
||||
private:
|
||||
Tegra::Texture::TextureFilter mag_filter;
|
||||
@@ -100,6 +100,10 @@ private:
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -160,6 +164,12 @@ private:
|
||||
/// Syncs the LogicOp state to match the guest state
|
||||
void SyncLogicOpState();
|
||||
|
||||
/// Syncs the the color clamp state
|
||||
void SyncFragmentColorClampState();
|
||||
|
||||
/// Syncs the alpha coverage and alpha to one
|
||||
void SyncMultiSampleState();
|
||||
|
||||
/// Syncs the scissor test state to match the guest state
|
||||
void SyncScissorTest();
|
||||
|
||||
|
||||
@@ -381,11 +381,8 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d
|
||||
const u32 tile_size_y{GetDefaultBlockHeight(format)};
|
||||
|
||||
if (morton_to_gl) {
|
||||
const std::vector<u8> data =
|
||||
Tegra::Texture::UnswizzleTexture(addr, tile_size_x, tile_size_y, bytes_per_pixel,
|
||||
stride, height, depth, block_height, block_depth);
|
||||
const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
|
||||
memcpy(gl_buffer, data.data(), size_to_copy);
|
||||
Tegra::Texture::UnswizzleTexture(gl_buffer, addr, tile_size_x, tile_size_y, bytes_per_pixel,
|
||||
stride, height, depth, block_height, block_depth);
|
||||
} else {
|
||||
Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
|
||||
(height + tile_size_y - 1) / tile_size_y, depth,
|
||||
@@ -550,8 +547,8 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
|
||||
if (params.is_layered) {
|
||||
u64 offset = params.GetMipmapLevelOffset(mip_level);
|
||||
u64 offset_gl = 0;
|
||||
u64 layer_size = params.LayerMemorySize();
|
||||
u64 gl_size = params.LayerSizeGL(mip_level);
|
||||
const u64 layer_size = params.LayerMemorySize();
|
||||
const u64 gl_size = params.LayerSizeGL(mip_level);
|
||||
for (u32 i = 0; i < params.depth; i++) {
|
||||
functions[static_cast<std::size_t>(params.pixel_format)](
|
||||
params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
|
||||
@@ -561,7 +558,7 @@ void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params
|
||||
offset_gl += gl_size;
|
||||
}
|
||||
} else {
|
||||
u64 offset = params.GetMipmapLevelOffset(mip_level);
|
||||
const u64 offset = params.GetMipmapLevelOffset(mip_level);
|
||||
functions[static_cast<std::size_t>(params.pixel_format)](
|
||||
params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
|
||||
params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(),
|
||||
|
||||
@@ -67,6 +67,7 @@ public:
|
||||
glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs);
|
||||
state.draw.shader_program = 0;
|
||||
state.draw.program_pipeline = pipeline.handle;
|
||||
state.geometry_shaders.enabled = (gs != 0);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -14,7 +14,10 @@ OpenGLState OpenGLState::cur_state;
|
||||
bool OpenGLState::s_rgb_used;
|
||||
OpenGLState::OpenGLState() {
|
||||
// These all match default OpenGL values
|
||||
geometry_shaders.enabled = false;
|
||||
framebuffer_srgb.enabled = false;
|
||||
multisample_control.alpha_to_coverage = false;
|
||||
multisample_control.alpha_to_one = false;
|
||||
cull.enabled = false;
|
||||
cull.mode = GL_BACK;
|
||||
cull.front_face = GL_CCW;
|
||||
@@ -50,12 +53,12 @@ OpenGLState::OpenGLState() {
|
||||
item.height = 0;
|
||||
item.depth_range_near = 0.0f;
|
||||
item.depth_range_far = 1.0f;
|
||||
item.scissor.enabled = false;
|
||||
item.scissor.x = 0;
|
||||
item.scissor.y = 0;
|
||||
item.scissor.width = 0;
|
||||
item.scissor.height = 0;
|
||||
}
|
||||
scissor.enabled = false;
|
||||
scissor.x = 0;
|
||||
scissor.y = 0;
|
||||
scissor.width = 0;
|
||||
scissor.height = 0;
|
||||
for (auto& item : blend) {
|
||||
item.enabled = true;
|
||||
item.rgb_equation = GL_FUNC_ADD;
|
||||
@@ -88,6 +91,7 @@ OpenGLState::OpenGLState() {
|
||||
clip_distance = {};
|
||||
|
||||
point.size = 1;
|
||||
fragment_color_clamp.enabled = false;
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyDefaultState() {
|
||||
@@ -136,7 +140,7 @@ void OpenGLState::ApplyCulling() const {
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyColorMask() const {
|
||||
if (GLAD_GL_ARB_viewport_array) {
|
||||
if (GLAD_GL_ARB_viewport_array && independant_blend.enabled) {
|
||||
for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
|
||||
const auto& updated = color_mask[i];
|
||||
const auto& current = cur_state.color_mask[i];
|
||||
@@ -230,26 +234,10 @@ void OpenGLState::ApplyStencilTest() const {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyScissor() const {
|
||||
const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled;
|
||||
if (scissor_changed) {
|
||||
if (scissor.enabled) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
} else {
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
if (scissor.enabled &&
|
||||
(scissor_changed || scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
|
||||
scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height)) {
|
||||
glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyViewport() const {
|
||||
if (GLAD_GL_ARB_viewport_array) {
|
||||
for (GLuint i = 0;
|
||||
i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); i++) {
|
||||
if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) {
|
||||
for (GLuint i = 0; i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumViewports);
|
||||
i++) {
|
||||
const auto& current = cur_state.viewports[i];
|
||||
const auto& updated = viewports[i];
|
||||
if (updated.x != current.x || updated.y != current.y ||
|
||||
@@ -260,18 +248,50 @@ void OpenGLState::ApplyViewport() const {
|
||||
updated.depth_range_far != current.depth_range_far) {
|
||||
glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
|
||||
}
|
||||
const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
|
||||
if (scissor_changed) {
|
||||
if (updated.scissor.enabled) {
|
||||
glEnablei(GL_SCISSOR_TEST, i);
|
||||
} else {
|
||||
glDisablei(GL_SCISSOR_TEST, i);
|
||||
}
|
||||
}
|
||||
if (updated.scissor.enabled &&
|
||||
(scissor_changed || updated.scissor.x != current.scissor.x ||
|
||||
updated.scissor.y != current.scissor.y ||
|
||||
updated.scissor.width != current.scissor.width ||
|
||||
updated.scissor.height != current.scissor.height)) {
|
||||
glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
|
||||
updated.scissor.height);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const auto& current = cur_state.viewports[0];
|
||||
const auto& updated = viewports[0];
|
||||
if (updated.x != current.x || updated.y != current.y || updated.width != current.width ||
|
||||
updated.height != current.height) {
|
||||
glViewport(updated.x, updated.y, updated.width, updated.height);
|
||||
glViewport(static_cast<GLint>(updated.x), static_cast<GLint>(updated.y),
|
||||
static_cast<GLsizei>(updated.width), static_cast<GLsizei>(updated.height));
|
||||
}
|
||||
if (updated.depth_range_near != current.depth_range_near ||
|
||||
updated.depth_range_far != current.depth_range_far) {
|
||||
glDepthRange(updated.depth_range_near, updated.depth_range_far);
|
||||
}
|
||||
const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
|
||||
if (scissor_changed) {
|
||||
if (updated.scissor.enabled) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
} else {
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
if (updated.scissor.enabled && (scissor_changed || updated.scissor.x != current.scissor.x ||
|
||||
updated.scissor.y != current.scissor.y ||
|
||||
updated.scissor.width != current.scissor.width ||
|
||||
updated.scissor.height != current.scissor.height)) {
|
||||
glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width,
|
||||
updated.scissor.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,31 +309,20 @@ void OpenGLState::ApplyGlobalBlending() const {
|
||||
if (!updated.enabled) {
|
||||
return;
|
||||
}
|
||||
if (updated.separate_alpha) {
|
||||
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) {
|
||||
glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
|
||||
updated.dst_a_func);
|
||||
}
|
||||
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) {
|
||||
glBlendFuncSeparate(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) {
|
||||
glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
|
||||
}
|
||||
} else {
|
||||
if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
|
||||
updated.dst_rgb_func != current.dst_rgb_func) {
|
||||
glBlendFunc(updated.src_rgb_func, updated.dst_rgb_func);
|
||||
}
|
||||
|
||||
if (blend_changed || updated.rgb_equation != current.rgb_equation) {
|
||||
glBlendEquation(updated.rgb_equation);
|
||||
}
|
||||
if (blend_changed || updated.rgb_equation != current.rgb_equation ||
|
||||
updated.a_equation != current.a_equation) {
|
||||
glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyTargetBlending(int target, bool force) const {
|
||||
void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
|
||||
const Blend& updated = blend[target];
|
||||
const Blend& current = cur_state.blend[target];
|
||||
const bool blend_changed = updated.enabled != current.enabled || force;
|
||||
@@ -327,29 +336,17 @@ void OpenGLState::ApplyTargetBlending(int target, bool force) const {
|
||||
if (!updated.enabled) {
|
||||
return;
|
||||
}
|
||||
if (updated.separate_alpha) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
|
||||
updated.dst_rgb_func != current.dst_rgb_func) {
|
||||
glBlendFunciARB(static_cast<GLuint>(target), updated.src_rgb_func,
|
||||
updated.dst_rgb_func);
|
||||
}
|
||||
|
||||
if (blend_changed || updated.rgb_equation != current.rgb_equation) {
|
||||
glBlendEquationiARB(static_cast<GLuint>(target), updated.rgb_equation);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,9 +477,29 @@ void OpenGLState::Apply() const {
|
||||
if (point.size != cur_state.point.size) {
|
||||
glPointSize(point.size);
|
||||
}
|
||||
if (GLAD_GL_ARB_color_buffer_float) {
|
||||
if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) {
|
||||
glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
|
||||
fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
|
||||
}
|
||||
}
|
||||
if (multisample_control.alpha_to_coverage != cur_state.multisample_control.alpha_to_coverage) {
|
||||
if (multisample_control.alpha_to_coverage) {
|
||||
glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
|
||||
} else {
|
||||
glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
|
||||
}
|
||||
}
|
||||
if (multisample_control.alpha_to_one != cur_state.multisample_control.alpha_to_one) {
|
||||
if (multisample_control.alpha_to_one) {
|
||||
glEnable(GL_SAMPLE_ALPHA_TO_ONE);
|
||||
} else {
|
||||
glDisable(GL_SAMPLE_ALPHA_TO_ONE);
|
||||
}
|
||||
}
|
||||
|
||||
ApplyColorMask();
|
||||
ApplyViewport();
|
||||
ApplyScissor();
|
||||
ApplyStencilTest();
|
||||
ApplySRgb();
|
||||
ApplyCulling();
|
||||
|
||||
@@ -39,6 +39,19 @@ public:
|
||||
bool enabled; // GL_FRAMEBUFFER_SRGB
|
||||
} framebuffer_srgb;
|
||||
|
||||
struct {
|
||||
bool alpha_to_coverage; // GL_ALPHA_TO_COVERAGE
|
||||
bool alpha_to_one; // GL_ALPHA_TO_ONE
|
||||
} multisample_control;
|
||||
|
||||
struct {
|
||||
bool enabled; // GL_CLAMP_FRAGMENT_COLOR_ARB
|
||||
} fragment_color_clamp;
|
||||
|
||||
struct {
|
||||
bool enabled; // viewports arrays are only supported when geometry shaders are enabled.
|
||||
} geometry_shaders;
|
||||
|
||||
struct {
|
||||
bool enabled; // GL_CULL_FACE
|
||||
GLenum mode; // GL_CULL_FACE_MODE
|
||||
@@ -79,7 +92,6 @@ public:
|
||||
|
||||
struct Blend {
|
||||
bool enabled; // GL_BLEND
|
||||
bool separate_alpha; // Independent blend enabled
|
||||
GLenum rgb_equation; // GL_BLEND_EQUATION_RGB
|
||||
GLenum a_equation; // GL_BLEND_EQUATION_ALPHA
|
||||
GLenum src_rgb_func; // GL_BLEND_SRC_RGB
|
||||
@@ -150,16 +162,15 @@ public:
|
||||
GLfloat height;
|
||||
GLfloat depth_range_near; // GL_DEPTH_RANGE
|
||||
GLfloat depth_range_far; // GL_DEPTH_RANGE
|
||||
struct {
|
||||
bool enabled; // GL_SCISSOR_TEST
|
||||
GLint x;
|
||||
GLint y;
|
||||
GLsizei width;
|
||||
GLsizei height;
|
||||
} scissor;
|
||||
};
|
||||
std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> viewports;
|
||||
|
||||
struct {
|
||||
bool enabled; // GL_SCISSOR_TEST
|
||||
GLint x;
|
||||
GLint y;
|
||||
GLsizei width;
|
||||
GLsizei height;
|
||||
} scissor;
|
||||
std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
|
||||
|
||||
struct {
|
||||
float size; // GL_POINT_SIZE
|
||||
@@ -208,13 +219,12 @@ private:
|
||||
void ApplyPrimitiveRestart() const;
|
||||
void ApplyStencilTest() const;
|
||||
void ApplyViewport() const;
|
||||
void ApplyTargetBlending(int target, bool force) const;
|
||||
void ApplyTargetBlending(std::size_t target, bool force) const;
|
||||
void ApplyGlobalBlending() const;
|
||||
void ApplyBlending() const;
|
||||
void ApplyLogicOp() const;
|
||||
void ApplyTextures() const;
|
||||
void ApplySamplers() const;
|
||||
void ApplyScissor() const;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -180,6 +180,12 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
|
||||
return GL_CLAMP_TO_BORDER;
|
||||
case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
|
||||
return GL_MIRROR_CLAMP_TO_EDGE;
|
||||
case Tegra::Texture::WrapMode::MirrorOnceBorder:
|
||||
if (GL_EXT_texture_mirror_clamp) {
|
||||
return GL_MIRROR_CLAMP_TO_BORDER_EXT;
|
||||
} else {
|
||||
return GL_MIRROR_CLAMP_TO_EDGE;
|
||||
}
|
||||
}
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
|
||||
return GL_REPEAT;
|
||||
|
||||
@@ -37,25 +37,28 @@ struct alignas(64) SwizzleTable {
|
||||
std::array<std::array<u16, M>, N> values{};
|
||||
};
|
||||
|
||||
constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();
|
||||
constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
|
||||
constexpr u32 gob_size_x = 64;
|
||||
constexpr u32 gob_size_y = 8;
|
||||
constexpr u32 gob_size_z = 1;
|
||||
constexpr u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
|
||||
constexpr u32 fast_swizzle_align = 16;
|
||||
|
||||
constexpr auto legacy_swizzle_table = SwizzleTable<gob_size_y, gob_size_x, gob_size_z>();
|
||||
constexpr auto fast_swizzle_table = SwizzleTable<gob_size_y, 4, fast_swizzle_align>();
|
||||
|
||||
/**
|
||||
* This function manages ALL the GOBs(Group of Bytes) Inside a single block.
|
||||
* Instead of going gob by gob, we map the coordinates inside a block and manage from
|
||||
* those. Block_Width is assumed to be 1.
|
||||
*/
|
||||
void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
|
||||
void PreciseProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
|
||||
const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
|
||||
const u32 y_end, const u32 z_end, const u32 tile_offset,
|
||||
const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
|
||||
const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
|
||||
std::array<u8*, 2> data_ptrs;
|
||||
u32 z_address = tile_offset;
|
||||
const u32 gob_size_x = 64;
|
||||
const u32 gob_size_y = 8;
|
||||
const u32 gob_size_z = 1;
|
||||
const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
|
||||
|
||||
for (u32 z = z_start; z < z_end; z++) {
|
||||
u32 y_address = z_address;
|
||||
u32 pixel_base = layer_z * z + y_start * stride_x;
|
||||
@@ -81,7 +84,7 @@ void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unsw
|
||||
* Instead of going gob by gob, we map the coordinates inside a block and manage from
|
||||
* those. Block_Width is assumed to be 1.
|
||||
*/
|
||||
void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
|
||||
void FastProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
|
||||
const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
|
||||
const u32 y_end, const u32 z_end, const u32 tile_offset,
|
||||
const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
|
||||
@@ -90,23 +93,19 @@ void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizz
|
||||
u32 z_address = tile_offset;
|
||||
const u32 x_startb = x_start * bytes_per_pixel;
|
||||
const u32 x_endb = x_end * bytes_per_pixel;
|
||||
const u32 copy_size = 16;
|
||||
const u32 gob_size_x = 64;
|
||||
const u32 gob_size_y = 8;
|
||||
const u32 gob_size_z = 1;
|
||||
const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
|
||||
|
||||
for (u32 z = z_start; z < z_end; z++) {
|
||||
u32 y_address = z_address;
|
||||
u32 pixel_base = layer_z * z + y_start * stride_x;
|
||||
for (u32 y = y_start; y < y_end; y++) {
|
||||
const auto& table = fast_swizzle_table[y % gob_size_y];
|
||||
for (u32 xb = x_startb; xb < x_endb; xb += copy_size) {
|
||||
const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]};
|
||||
for (u32 xb = x_startb; xb < x_endb; xb += fast_swizzle_align) {
|
||||
const u32 swizzle_offset{y_address + table[(xb / fast_swizzle_align) % 4]};
|
||||
const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
|
||||
const u32 pixel_index{out_x + pixel_base};
|
||||
data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
|
||||
data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
|
||||
std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
|
||||
std::memcpy(data_ptrs[0], data_ptrs[1], fast_swizzle_align);
|
||||
}
|
||||
pixel_base += stride_x;
|
||||
if ((y + 1) % gob_size_y == 0)
|
||||
@@ -126,23 +125,21 @@ void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizz
|
||||
* https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces
|
||||
*/
|
||||
template <bool fast>
|
||||
void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 width,
|
||||
const u32 height, const u32 depth, const u32 bytes_per_pixel,
|
||||
void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool unswizzle,
|
||||
const u32 width, const u32 height, const u32 depth, const u32 bytes_per_pixel,
|
||||
const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) {
|
||||
auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
|
||||
const u32 stride_x = width * out_bytes_per_pixel;
|
||||
const u32 layer_z = height * stride_x;
|
||||
const u32 gob_x_bytes = 64;
|
||||
const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel;
|
||||
const u32 gob_elements_y = 8;
|
||||
const u32 gob_elements_z = 1;
|
||||
const u32 gob_elements_x = gob_size_x / bytes_per_pixel;
|
||||
constexpr u32 gob_elements_y = gob_size_y;
|
||||
constexpr u32 gob_elements_z = gob_size_z;
|
||||
const u32 block_x_elements = gob_elements_x;
|
||||
const u32 block_y_elements = gob_elements_y * block_height;
|
||||
const u32 block_z_elements = gob_elements_z * block_depth;
|
||||
const u32 blocks_on_x = div_ceil(width, block_x_elements);
|
||||
const u32 blocks_on_y = div_ceil(height, block_y_elements);
|
||||
const u32 blocks_on_z = div_ceil(depth, block_z_elements);
|
||||
const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
|
||||
const u32 xy_block_size = gob_size * block_height;
|
||||
const u32 block_size = xy_block_size * block_depth;
|
||||
u32 tile_offset = 0;
|
||||
@@ -171,9 +168,9 @@ void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
|
||||
}
|
||||
|
||||
void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
|
||||
u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
|
||||
u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data,
|
||||
bool unswizzle, u32 block_height, u32 block_depth) {
|
||||
if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
|
||||
if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % fast_swizzle_align == 0) {
|
||||
SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
|
||||
bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
|
||||
} else {
|
||||
@@ -229,29 +226,38 @@ u32 BytesPerPixel(TextureFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
void UnswizzleTexture(u8* const unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
|
||||
u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height,
|
||||
u32 block_depth) {
|
||||
CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
|
||||
(height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
|
||||
bytes_per_pixel, Memory::GetPointer(address), unswizzled_data, true,
|
||||
block_height, block_depth);
|
||||
}
|
||||
|
||||
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
|
||||
u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
|
||||
u32 block_height, u32 block_depth) {
|
||||
std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
|
||||
CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
|
||||
(height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
|
||||
bytes_per_pixel, Memory::GetPointer(address), unswizzled_data.data(), true,
|
||||
block_height, block_depth);
|
||||
UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel,
|
||||
width, height, depth, block_height, block_depth);
|
||||
return unswizzled_data;
|
||||
}
|
||||
|
||||
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
|
||||
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
|
||||
u32 block_height) {
|
||||
const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64};
|
||||
const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) /
|
||||
gob_size_x};
|
||||
for (u32 line = 0; line < subrect_height; ++line) {
|
||||
const u32 gob_address_y =
|
||||
(line / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
|
||||
(line % (8 * block_height) / 8) * 512;
|
||||
const auto& table = legacy_swizzle_table[line % 8];
|
||||
(line / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs +
|
||||
((line % (gob_size_y * block_height)) / gob_size_y) * gob_size;
|
||||
const auto& table = legacy_swizzle_table[line % gob_size_y];
|
||||
for (u32 x = 0; x < subrect_width; ++x) {
|
||||
const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height;
|
||||
const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64];
|
||||
const u32 gob_address =
|
||||
gob_address_y + (x * bytes_per_pixel / gob_size_x) * gob_size * block_height;
|
||||
const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % gob_size_x];
|
||||
const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
|
||||
const VAddr dest_addr = swizzled_data + swizzled_offset;
|
||||
|
||||
@@ -265,13 +271,13 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
|
||||
u32 block_height, u32 offset_x, u32 offset_y) {
|
||||
for (u32 line = 0; line < subrect_height; ++line) {
|
||||
const u32 y2 = line + offset_y;
|
||||
const u32 gob_address_y =
|
||||
(y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512;
|
||||
const auto& table = legacy_swizzle_table[y2 % 8];
|
||||
const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height +
|
||||
((y2 % (gob_size_y * block_height)) / gob_size_y) * gob_size;
|
||||
const auto& table = legacy_swizzle_table[y2 % gob_size_y];
|
||||
for (u32 x = 0; x < subrect_width; ++x) {
|
||||
const u32 x2 = (x + offset_x) * bytes_per_pixel;
|
||||
const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height;
|
||||
const u32 swizzled_offset = gob_address + table[x2 % 64];
|
||||
const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height;
|
||||
const u32 swizzled_offset = gob_address + table[x2 % gob_size_x];
|
||||
const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
|
||||
const VAddr source_addr = swizzled_data + swizzled_offset;
|
||||
|
||||
@@ -325,12 +331,9 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
|
||||
std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
|
||||
u32 block_height, u32 block_depth) {
|
||||
if (tiled) {
|
||||
const u32 gobs_in_x = 64;
|
||||
const u32 gobs_in_y = 8;
|
||||
const u32 gobs_in_z = 1;
|
||||
const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x);
|
||||
const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
|
||||
const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
|
||||
const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gob_size_x);
|
||||
const u32 aligned_height = Common::AlignUp(height, gob_size_y * block_height);
|
||||
const u32 aligned_depth = Common::AlignUp(depth, gob_size_z * block_depth);
|
||||
return aligned_width * aligned_height * aligned_depth;
|
||||
} else {
|
||||
return width * height * depth * bytes_per_pixel;
|
||||
|
||||
@@ -16,6 +16,13 @@ inline std::size_t GetGOBSize() {
|
||||
return 512;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unswizzles a swizzled texture without changing its format.
|
||||
*/
|
||||
void UnswizzleTexture(u8* unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
|
||||
u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
|
||||
u32 block_height = TICEntry::DefaultBlockHeight,
|
||||
u32 block_depth = TICEntry::DefaultBlockHeight);
|
||||
/**
|
||||
* Unswizzles a swizzled texture without changing its format.
|
||||
*/
|
||||
|
||||
@@ -190,6 +190,7 @@ struct TICEntry {
|
||||
union {
|
||||
BitField<0, 4, u32> res_min_mip_level;
|
||||
BitField<4, 4, u32> res_max_mip_level;
|
||||
BitField<12, 12, u32> min_lod_clamp;
|
||||
};
|
||||
|
||||
GPUVAddr Address() const {
|
||||
@@ -284,13 +285,25 @@ struct TSCEntry {
|
||||
BitField<6, 3, WrapMode> wrap_p;
|
||||
BitField<9, 1, u32> depth_compare_enabled;
|
||||
BitField<10, 3, DepthCompareFunc> depth_compare_func;
|
||||
BitField<13, 1, u32> srgb_conversion;
|
||||
BitField<20, 3, u32> max_anisotropy;
|
||||
};
|
||||
union {
|
||||
BitField<0, 2, TextureFilter> mag_filter;
|
||||
BitField<4, 2, TextureFilter> min_filter;
|
||||
BitField<6, 2, TextureMipmapFilter> mip_filter;
|
||||
BitField<9, 1, u32> cubemap_interface_filtering;
|
||||
BitField<12, 13, u32> mip_lod_bias;
|
||||
};
|
||||
union {
|
||||
BitField<0, 12, u32> min_lod_clamp;
|
||||
BitField<12, 12, u32> max_lod_clamp;
|
||||
BitField<24, 8, u32> srgb_border_color_r;
|
||||
};
|
||||
union {
|
||||
BitField<12, 8, u32> srgb_border_color_g;
|
||||
BitField<20, 8, u32> srgb_border_color_b;
|
||||
};
|
||||
INSERT_PADDING_BYTES(8);
|
||||
float border_color_r;
|
||||
float border_color_g;
|
||||
float border_color_b;
|
||||
|
||||
@@ -27,8 +27,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 +82,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
|
||||
|
||||
@@ -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_nso = qt_config->value("dump_nso", false).toBool();
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("WebService");
|
||||
@@ -170,6 +450,7 @@ void Config::ReadValues() {
|
||||
|
||||
qt_config->beginGroup("UIGameList");
|
||||
UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
|
||||
UISettings::values.show_add_ons = qt_config->value("show_add_ons", true).toBool();
|
||||
UISettings::values.icon_size = qt_config->value("icon_size", 64).toUInt();
|
||||
UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 3).toUInt();
|
||||
UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 2).toUInt();
|
||||
@@ -228,18 +509,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");
|
||||
@@ -278,7 +622,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());
|
||||
@@ -295,6 +638,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_nso", Settings::values.dump_nso);
|
||||
qt_config->endGroup();
|
||||
|
||||
qt_config->beginGroup("WebService");
|
||||
@@ -310,6 +654,7 @@ void Config::SaveValues() {
|
||||
|
||||
qt_config->beginGroup("UIGameList");
|
||||
qt_config->setValue("show_unknown", UISettings::values.show_unknown);
|
||||
qt_config->setValue("show_add_ons", UISettings::values.show_add_ons);
|
||||
qt_config->setValue("icon_size", UISettings::values.icon_size);
|
||||
qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id);
|
||||
qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id);
|
||||
|
||||
@@ -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_decompressed_nso->setChecked(Settings::values.dump_nso);
|
||||
}
|
||||
|
||||
void ConfigureDebug::applyConfiguration() {
|
||||
@@ -42,6 +43,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_nso = ui->dump_decompressed_nso->isChecked();
|
||||
Debugger::ToggleConsole();
|
||||
Log::Filter filter;
|
||||
filter.ParseFilterString(Settings::values.log_filter);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
<height>357</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -129,6 +129,25 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<property name="title">
|
||||
<string>Dump</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="dump_decompressed_nso">
|
||||
<property name="whatsThis">
|
||||
<string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dump Decompressed NSOs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
|
||||
@@ -36,20 +36,36 @@ ConfigureGameList::ConfigureGameList(QWidget* parent)
|
||||
InitializeRowComboBoxes();
|
||||
|
||||
this->setConfiguration();
|
||||
|
||||
// Force game list reload if any of the relevant settings are changed.
|
||||
connect(ui->show_unknown, &QCheckBox::stateChanged, this,
|
||||
&ConfigureGameList::RequestGameListUpdate);
|
||||
connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGameList::RequestGameListUpdate);
|
||||
connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGameList::RequestGameListUpdate);
|
||||
connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGameList::RequestGameListUpdate);
|
||||
}
|
||||
|
||||
ConfigureGameList::~ConfigureGameList() = default;
|
||||
|
||||
void ConfigureGameList::applyConfiguration() {
|
||||
UISettings::values.show_unknown = ui->show_unknown->isChecked();
|
||||
UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
|
||||
UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
|
||||
UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
|
||||
UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
void ConfigureGameList::RequestGameListUpdate() {
|
||||
UISettings::values.is_game_list_reload_pending.exchange(true);
|
||||
}
|
||||
|
||||
void ConfigureGameList::setConfiguration() {
|
||||
ui->show_unknown->setChecked(UISettings::values.show_unknown);
|
||||
ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
|
||||
ui->icon_size_combobox->setCurrentIndex(
|
||||
ui->icon_size_combobox->findData(UISettings::values.icon_size));
|
||||
ui->row_1_text_combobox->setCurrentIndex(
|
||||
|
||||
@@ -21,6 +21,8 @@ public:
|
||||
void applyConfiguration();
|
||||
|
||||
private:
|
||||
void RequestGameListUpdate();
|
||||
|
||||
void setConfiguration();
|
||||
|
||||
void changeEvent(QEvent*) override;
|
||||
|
||||
@@ -1,126 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureGameList</class>
|
||||
<widget class="QWidget" name="ConfigureGeneral">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>300</width>
|
||||
<height>377</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="HorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="VerticalLayout">
|
||||
<widget class="QWidget" name="ConfigureGameList">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>300</width>
|
||||
<height>377</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="HorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="VerticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="GeneralGroupBox">
|
||||
<property name="title">
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="GeneralHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="GeneralVerticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="GeneralGroupBox">
|
||||
<property name="title">
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="GeneralHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="GeneralVerticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="show_unknown">
|
||||
<property name="text">
|
||||
<string>Show files with type 'Unknown'</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="show_unknown">
|
||||
<property name="text">
|
||||
<string>Show files with type 'Unknown'</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="IconSizeGroupBox">
|
||||
<property name="title">
|
||||
<string>Icon Size</string>
|
||||
<widget class="QCheckBox" name="show_add_ons">
|
||||
<property name="text">
|
||||
<string>Show Add-Ons Column</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="IconSizeGroupBox">
|
||||
<property name="title">
|
||||
<string>Icon Size</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="icon_size_qhbox_layout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="icon_size_qvbox_layout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon_size_label">
|
||||
<property name="text">
|
||||
<string>Icon Size:</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="icon_size_qhbox_layout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="icon_size_qvbox_layout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="icon_size_label">
|
||||
<property name="text">
|
||||
<string>Icon Size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="icon_size_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="icon_size_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="RowGroupBox">
|
||||
<property name="title">
|
||||
<string>Row Text</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="RowHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="RowVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="row_1_qhbox_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="row_1_label">
|
||||
<property name="text">
|
||||
<string>Row 1 Text:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="row_1_text_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="RowGroupBox">
|
||||
<property name="title">
|
||||
<string>Row Text</string>
|
||||
<layout class="QHBoxLayout" name="row_2_qhbox_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="row_2_label">
|
||||
<property name="text">
|
||||
<string>Row 2 Text:</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="RowHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="RowVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="row_1_qhbox_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="row_1_label">
|
||||
<property name="text">
|
||||
<string>Row 1 Text:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="row_1_text_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="row_2_qhbox_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="row_2_label">
|
||||
<property name="text">
|
||||
<string>Row 2 Text:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="row_2_text_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="row_2_text_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -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"
|
||||
@@ -23,6 +19,9 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
|
||||
this->setConfiguration();
|
||||
|
||||
connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this,
|
||||
[] { UISettings::values.is_game_list_reload_pending.exchange(true); });
|
||||
|
||||
ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
}
|
||||
|
||||
@@ -33,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);
|
||||
}
|
||||
|
||||
@@ -41,30 +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()};
|
||||
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();
|
||||
@@ -72,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">
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
508
src/yuzu/configuration/configure_input_player.cpp
Normal file
508
src/yuzu/configuration/configure_input_player.cpp
Normal file
@@ -0,0 +1,508 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <QColorDialog>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
#include "common/assert.h"
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/main.h"
|
||||
#include "ui_configure_input_player.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_input_player.h"
|
||||
|
||||
const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
|
||||
ConfigureInputPlayer::analog_sub_buttons{{
|
||||
"up",
|
||||
"down",
|
||||
"left",
|
||||
"right",
|
||||
"modifier",
|
||||
}};
|
||||
|
||||
static void MoveGridElement(QGridLayout* grid, int row_old, int column_old, int row_new,
|
||||
int column_new) {
|
||||
const auto item = grid->itemAtPosition(row_old, column_old);
|
||||
// grid->removeItem(item);
|
||||
grid->addItem(item, row_new, column_new);
|
||||
}
|
||||
|
||||
static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) {
|
||||
const int index1 = grid->indexOf(item);
|
||||
const int index2 = grid->indexOf(onTopOf);
|
||||
int row, column, rowSpan, columnSpan;
|
||||
grid->getItemPosition(index2, &row, &column, &rowSpan, &columnSpan);
|
||||
grid->takeAt(index1);
|
||||
grid->addWidget(item, row, column, rowSpan, columnSpan);
|
||||
}
|
||||
|
||||
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]");
|
||||
}
|
||||
};
|
||||
|
||||
ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()),
|
||||
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
|
||||
player_index(player_index), debug(debug) {
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
analog_map_buttons = {{
|
||||
{
|
||||
ui->buttonLStickUp,
|
||||
ui->buttonLStickDown,
|
||||
ui->buttonLStickLeft,
|
||||
ui->buttonLStickRight,
|
||||
ui->buttonLStickMod,
|
||||
},
|
||||
{
|
||||
ui->buttonRStickUp,
|
||||
ui->buttonRStickDown,
|
||||
ui->buttonRStickLeft,
|
||||
ui->buttonRStickRight,
|
||||
ui->buttonRStickMod,
|
||||
},
|
||||
}};
|
||||
|
||||
debug_hidden = {
|
||||
ui->buttonSL, ui->labelSL,
|
||||
ui->buttonSR, ui->labelSR,
|
||||
ui->buttonLStick, ui->labelLStickPressed,
|
||||
ui->buttonRStick, ui->labelRStickPressed,
|
||||
ui->buttonHome, ui->labelHome,
|
||||
ui->buttonScreenshot, ui->labelScreenshot,
|
||||
};
|
||||
|
||||
auto layout = Settings::values.players[player_index].type;
|
||||
if (debug)
|
||||
layout = Settings::ControllerType::DualJoycon;
|
||||
|
||||
switch (layout) {
|
||||
case Settings::ControllerType::ProController:
|
||||
case Settings::ControllerType::DualJoycon:
|
||||
layout_hidden = {
|
||||
ui->buttonSL,
|
||||
ui->labelSL,
|
||||
ui->buttonSR,
|
||||
ui->labelSR,
|
||||
};
|
||||
break;
|
||||
case Settings::ControllerType::LeftJoycon:
|
||||
layout_hidden = {
|
||||
ui->right_body_button,
|
||||
ui->right_buttons_button,
|
||||
ui->right_body_label,
|
||||
ui->right_buttons_label,
|
||||
ui->buttonR,
|
||||
ui->labelR,
|
||||
ui->buttonZR,
|
||||
ui->labelZR,
|
||||
ui->labelHome,
|
||||
ui->buttonHome,
|
||||
ui->buttonPlus,
|
||||
ui->labelPlus,
|
||||
ui->RStick,
|
||||
ui->faceButtons,
|
||||
};
|
||||
break;
|
||||
case Settings::ControllerType::RightJoycon:
|
||||
layout_hidden = {
|
||||
ui->left_body_button, ui->left_buttons_button,
|
||||
ui->left_body_label, ui->left_buttons_label,
|
||||
ui->buttonL, ui->labelL,
|
||||
ui->buttonZL, ui->labelZL,
|
||||
ui->labelScreenshot, ui->buttonScreenshot,
|
||||
ui->buttonMinus, ui->labelMinus,
|
||||
ui->LStick, ui->Dpad,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
if (debug || layout == Settings::ControllerType::ProController) {
|
||||
ui->controller_color->hide();
|
||||
} else {
|
||||
if (layout == Settings::ControllerType::LeftJoycon ||
|
||||
layout == Settings::ControllerType::RightJoycon) {
|
||||
ui->horizontalSpacer_4->setGeometry({0, 0, 0, 0});
|
||||
|
||||
LayerGridElements(ui->buttons, ui->shoulderButtons, ui->Dpad);
|
||||
LayerGridElements(ui->buttons, ui->misc, ui->RStick);
|
||||
LayerGridElements(ui->buttons, ui->Dpad, ui->faceButtons);
|
||||
LayerGridElements(ui->buttons, ui->RStick, ui->LStick);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* widget : layout_hidden)
|
||||
widget->setVisible(false);
|
||||
|
||||
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 (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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
controller_color_buttons = {
|
||||
ui->left_body_button,
|
||||
ui->left_buttons_button,
|
||||
ui->right_body_button,
|
||||
ui->right_buttons_button,
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) {
|
||||
connect(controller_color_buttons[i], &QPushButton::clicked, this,
|
||||
std::bind(&ConfigureInputPlayer::OnControllerButtonClick, this, i));
|
||||
}
|
||||
|
||||
this->loadConfiguration();
|
||||
this->resize(0, 0);
|
||||
|
||||
// TODO(wwylele): enable this when we actually emulate it
|
||||
ui->buttonHome->setEnabled(false);
|
||||
}
|
||||
|
||||
ConfigureInputPlayer::~ConfigureInputPlayer() = default;
|
||||
|
||||
void ConfigureInputPlayer::applyConfiguration() {
|
||||
auto& buttons =
|
||||
debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons;
|
||||
auto& analogs =
|
||||
debug ? Settings::values.debug_pad_analogs : Settings::values.players[player_index].analogs;
|
||||
|
||||
std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
|
||||
[](const Common::ParamPackage& param) { return param.Serialize(); });
|
||||
std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
|
||||
[](const Common::ParamPackage& param) { return param.Serialize(); });
|
||||
|
||||
if (debug)
|
||||
return;
|
||||
|
||||
std::array<u32, 4> colors{};
|
||||
std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(),
|
||||
[](QColor color) { return color.rgb(); });
|
||||
|
||||
Settings::values.players[player_index].body_color_left = colors[0];
|
||||
Settings::values.players[player_index].button_color_left = colors[1];
|
||||
Settings::values.players[player_index].body_color_right = colors[2];
|
||||
Settings::values.players[player_index].button_color_right = colors[3];
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::OnControllerButtonClick(int i) {
|
||||
const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]);
|
||||
if (!new_bg_color.isValid())
|
||||
return;
|
||||
controller_colors[i] = new_bg_color;
|
||||
controller_color_buttons[i]->setStyleSheet(
|
||||
QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::loadConfiguration() {
|
||||
if (debug) {
|
||||
std::transform(Settings::values.debug_pad_buttons.begin(),
|
||||
Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
|
||||
[](const std::string& str) { return Common::ParamPackage(str); });
|
||||
std::transform(Settings::values.debug_pad_analogs.begin(),
|
||||
Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
|
||||
[](const std::string& str) { return Common::ParamPackage(str); });
|
||||
} else {
|
||||
std::transform(Settings::values.players[player_index].buttons.begin(),
|
||||
Settings::values.players[player_index].buttons.end(), buttons_param.begin(),
|
||||
[](const std::string& str) { return Common::ParamPackage(str); });
|
||||
std::transform(Settings::values.players[player_index].analogs.begin(),
|
||||
Settings::values.players[player_index].analogs.end(), analogs_param.begin(),
|
||||
[](const std::string& str) { return Common::ParamPackage(str); });
|
||||
}
|
||||
|
||||
updateButtonLabels();
|
||||
|
||||
if (debug)
|
||||
return;
|
||||
|
||||
std::array<u32, 4> colors = {
|
||||
Settings::values.players[player_index].body_color_left,
|
||||
Settings::values.players[player_index].button_color_left,
|
||||
Settings::values.players[player_index].body_color_right,
|
||||
Settings::values.players[player_index].button_color_right,
|
||||
};
|
||||
|
||||
std::transform(colors.begin(), colors.end(), controller_colors.begin(),
|
||||
[](u32 rgb) { return QColor::fromRgb(rgb); });
|
||||
|
||||
for (std::size_t i = 0; i < colors.size(); ++i) {
|
||||
controller_color_buttons[i]->setStyleSheet(
|
||||
QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::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])};
|
||||
}
|
||||
|
||||
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 ConfigureInputPlayer::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 ConfigureInputPlayer::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 ConfigureInputPlayer::handleClick(
|
||||
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
|
||||
InputCommon::Polling::DeviceType type) {
|
||||
button->setText(tr("[press key]"));
|
||||
button->setFocus();
|
||||
|
||||
const auto iter = std::find(button_map.begin(), button_map.end(), button);
|
||||
ASSERT(iter != button_map.end());
|
||||
const auto index = std::distance(button_map.begin(), iter);
|
||||
ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
|
||||
|
||||
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 ConfigureInputPlayer::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 = std::nullopt;
|
||||
}
|
||||
|
||||
void ConfigureInputPlayer::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);
|
||||
}
|
||||
103
src/yuzu/configuration/configure_input_player.h
Normal file
103
src/yuzu/configuration/configure_input_player.h
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <QDialog>
|
||||
#include <QKeyEvent>
|
||||
#include "common/param_package.h"
|
||||
#include "core/settings.h"
|
||||
#include "input_common/main.h"
|
||||
#include "ui_configure_input.h"
|
||||
|
||||
class QPushButton;
|
||||
class QString;
|
||||
class QTimer;
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureInputPlayer;
|
||||
}
|
||||
|
||||
class ConfigureInputPlayer : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug = false);
|
||||
~ConfigureInputPlayer() override;
|
||||
|
||||
/// Save all button configurations to settings file
|
||||
void applyConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureInputPlayer> ui;
|
||||
|
||||
u8 player_index;
|
||||
bool debug;
|
||||
|
||||
std::unique_ptr<QTimer> timeout_timer;
|
||||
std::unique_ptr<QTimer> poll_timer;
|
||||
|
||||
/// 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;
|
||||
|
||||
std::vector<QWidget*> debug_hidden;
|
||||
std::vector<QWidget*> layout_hidden;
|
||||
|
||||
/// 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;
|
||||
|
||||
std::array<QPushButton*, 4> controller_color_buttons;
|
||||
std::array<QColor, 4> controller_colors;
|
||||
|
||||
void OnControllerButtonClick(int i);
|
||||
|
||||
/// 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();
|
||||
|
||||
/// 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;
|
||||
};
|
||||
1164
src/yuzu/configuration/configure_input_player.ui
Normal file
1164
src/yuzu/configuration/configure_input_player.ui
Normal file
File diff suppressed because it is too large
Load Diff
213
src/yuzu/configuration/configure_mouse_advanced.cpp
Normal file
213
src/yuzu/configuration/configure_mouse_advanced.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
#include "common/assert.h"
|
||||
#include "common/param_package.h"
|
||||
#include "input_common/main.h"
|
||||
#include "ui_configure_mouse_advanced.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_mouse_advanced.h"
|
||||
|
||||
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 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]");
|
||||
}
|
||||
}
|
||||
|
||||
ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()),
|
||||
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
|
||||
ui->setupUi(this);
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
|
||||
button_map = {
|
||||
ui->left_button, ui->right_button, ui->middle_button, ui->forward_button, ui->back_button,
|
||||
};
|
||||
|
||||
for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; 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_mouse_buttons[button_id])};
|
||||
button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
|
||||
});
|
||||
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
loadConfiguration();
|
||||
resize(0, 0);
|
||||
}
|
||||
|
||||
ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default;
|
||||
|
||||
void ConfigureMouseAdvanced::applyConfiguration() {
|
||||
std::transform(buttons_param.begin(), buttons_param.end(),
|
||||
Settings::values.mouse_buttons.begin(),
|
||||
[](const Common::ParamPackage& param) { return param.Serialize(); });
|
||||
}
|
||||
|
||||
void ConfigureMouseAdvanced::loadConfiguration() {
|
||||
std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
|
||||
buttons_param.begin(),
|
||||
[](const std::string& str) { return Common::ParamPackage(str); });
|
||||
updateButtonLabels();
|
||||
}
|
||||
|
||||
void ConfigureMouseAdvanced::restoreDefaults() {
|
||||
for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
|
||||
buttons_param[button_id] = Common::ParamPackage{
|
||||
InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])};
|
||||
}
|
||||
|
||||
updateButtonLabels();
|
||||
}
|
||||
|
||||
void ConfigureMouseAdvanced::ClearAll() {
|
||||
for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
|
||||
if (button_map[i] && button_map[i]->isEnabled())
|
||||
buttons_param[i].Clear();
|
||||
}
|
||||
|
||||
updateButtonLabels();
|
||||
}
|
||||
|
||||
void ConfigureMouseAdvanced::updateButtonLabels() {
|
||||
for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) {
|
||||
button_map[button]->setText(ButtonToText(buttons_param[button]));
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureMouseAdvanced::handleClick(
|
||||
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
|
||||
InputCommon::Polling::DeviceType type) {
|
||||
button->setText(tr("[press key]"));
|
||||
button->setFocus();
|
||||
|
||||
const auto iter = std::find(button_map.begin(), button_map.end(), button);
|
||||
ASSERT(iter != button_map.end());
|
||||
const auto index = std::distance(button_map.begin(), iter);
|
||||
ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
|
||||
|
||||
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 ConfigureMouseAdvanced::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 = std::nullopt;
|
||||
}
|
||||
|
||||
void ConfigureMouseAdvanced::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);
|
||||
}
|
||||
68
src/yuzu/configuration/configure_mouse_advanced.h
Normal file
68
src/yuzu/configuration/configure_mouse_advanced.h
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
#include "core/settings.h"
|
||||
|
||||
class QCheckBox;
|
||||
class QPushButton;
|
||||
class QTimer;
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureMouseAdvanced;
|
||||
}
|
||||
|
||||
class ConfigureMouseAdvanced : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureMouseAdvanced(QWidget* parent);
|
||||
~ConfigureMouseAdvanced() override;
|
||||
|
||||
void applyConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
|
||||
|
||||
/// 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<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map;
|
||||
std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param;
|
||||
|
||||
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
|
||||
|
||||
std::unique_ptr<QTimer> timeout_timer;
|
||||
std::unique_ptr<QTimer> poll_timer;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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();
|
||||
|
||||
/// 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;
|
||||
};
|
||||
261
src/yuzu/configuration/configure_mouse_advanced.ui
Normal file
261
src/yuzu/configuration/configure_mouse_advanced.ui
Normal file
@@ -0,0 +1,261 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureMouseAdvanced</class>
|
||||
<widget class="QDialog" name="ConfigureMouseAdvanced">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>250</width>
|
||||
<height>261</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Configure Mouse</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gridGroupBox">
|
||||
<property name="title">
|
||||
<string>Mouse Buttons</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="4">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Right:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="right_button">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>75</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Middle:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="middle_button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>54</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Back:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="back_button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Left:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="left_button">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>75</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Forward:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="forward_button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonClearAll">
|
||||
<property name="text">
|
||||
<string>Clear All</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonRestoreDefaults">
|
||||
<property name="text">
|
||||
<string>Restore Defaults</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ConfigureMouseAdvanced</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>124</x>
|
||||
<y>266</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>124</x>
|
||||
<y>143</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ConfigureMouseAdvanced</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>124</x>
|
||||
<y>266</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>124</x>
|
||||
<y>143</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
42
src/yuzu/configuration/configure_touchscreen_advanced.cpp
Normal file
42
src/yuzu/configuration/configure_touchscreen_advanced.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include "ui_configure_touchscreen_advanced.h"
|
||||
#include "yuzu/configuration/config.h"
|
||||
#include "yuzu/configuration/configure_touchscreen_advanced.h"
|
||||
|
||||
ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent)
|
||||
: QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchscreenAdvanced>()) {
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->restore_defaults_button, &QPushButton::pressed, this,
|
||||
&ConfigureTouchscreenAdvanced::restoreDefaults);
|
||||
|
||||
loadConfiguration();
|
||||
resize(0, 0);
|
||||
}
|
||||
|
||||
ConfigureTouchscreenAdvanced::~ConfigureTouchscreenAdvanced() = default;
|
||||
|
||||
void ConfigureTouchscreenAdvanced::applyConfiguration() {
|
||||
Settings::values.touchscreen.finger = ui->finger_box->value();
|
||||
Settings::values.touchscreen.diameter_x = ui->diameter_x_box->value();
|
||||
Settings::values.touchscreen.diameter_y = ui->diameter_y_box->value();
|
||||
Settings::values.touchscreen.rotation_angle = ui->angle_box->value();
|
||||
}
|
||||
|
||||
void ConfigureTouchscreenAdvanced::loadConfiguration() {
|
||||
ui->finger_box->setValue(Settings::values.touchscreen.finger);
|
||||
ui->diameter_x_box->setValue(Settings::values.touchscreen.diameter_x);
|
||||
ui->diameter_y_box->setValue(Settings::values.touchscreen.diameter_y);
|
||||
ui->angle_box->setValue(Settings::values.touchscreen.rotation_angle);
|
||||
}
|
||||
|
||||
void ConfigureTouchscreenAdvanced::restoreDefaults() {
|
||||
ui->finger_box->setValue(0);
|
||||
ui->diameter_x_box->setValue(15);
|
||||
ui->diameter_y_box->setValue(15);
|
||||
ui->angle_box->setValue(0);
|
||||
}
|
||||
32
src/yuzu/configuration/configure_touchscreen_advanced.h
Normal file
32
src/yuzu/configuration/configure_touchscreen_advanced.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
#include "yuzu/configuration/config.h"
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureTouchscreenAdvanced;
|
||||
}
|
||||
|
||||
class ConfigureTouchscreenAdvanced : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureTouchscreenAdvanced(QWidget* parent);
|
||||
~ConfigureTouchscreenAdvanced() override;
|
||||
|
||||
void applyConfiguration();
|
||||
|
||||
private:
|
||||
/// Load configuration settings.
|
||||
void loadConfiguration();
|
||||
/// Restore all buttons to their default values.
|
||||
void restoreDefaults();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureTouchscreenAdvanced> ui;
|
||||
};
|
||||
199
src/yuzu/configuration/configure_touchscreen_advanced.ui
Normal file
199
src/yuzu/configuration/configure_touchscreen_advanced.ui
Normal file
@@ -0,0 +1,199 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureTouchscreenAdvanced</class>
|
||||
<widget class="QDialog" name="ConfigureTouchscreenAdvanced">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>298</width>
|
||||
<height>339</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Configure Touchscreen</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>280</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Warning: The settings in this page affect the inner workings of yuzu's emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gridGroupBox">
|
||||
<property name="title">
|
||||
<string>Touch Parameters</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Touch Diameter Y</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Finger</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Touch Diameter X</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QSpinBox" name="finger_box">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Rotational Angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QSpinBox" name="diameter_x_box"/>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QSpinBox" name="diameter_y_box"/>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QSpinBox" name="angle_box"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="restore_defaults_button">
|
||||
<property name="text">
|
||||
<string>Restore Defaults</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ConfigureTouchscreenAdvanced</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>140</x>
|
||||
<y>318</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>140</x>
|
||||
<y>169</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ConfigureTouchscreenAdvanced</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>140</x>
|
||||
<y>318</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>140</x>
|
||||
<y>169</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -215,12 +215,18 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
|
||||
tree_view->setUniformRowHeights(true);
|
||||
tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
item_model->insertColumns(0, COLUMN_COUNT);
|
||||
item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
|
||||
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
|
||||
item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
|
||||
item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
|
||||
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
|
||||
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
|
||||
|
||||
if (UISettings::values.show_add_ons) {
|
||||
item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
|
||||
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
|
||||
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
|
||||
} else {
|
||||
item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type"));
|
||||
item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size"));
|
||||
}
|
||||
|
||||
connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
|
||||
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
|
||||
@@ -394,6 +400,25 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
|
||||
}
|
||||
|
||||
tree_view->setEnabled(false);
|
||||
|
||||
// Update the columns in case UISettings has changed
|
||||
item_model->removeColumns(0, item_model->columnCount());
|
||||
item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
|
||||
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
|
||||
item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
|
||||
|
||||
if (UISettings::values.show_add_ons) {
|
||||
item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
|
||||
item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
|
||||
item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
|
||||
} else {
|
||||
item_model->setHeaderData(COLUMN_FILE_TYPE - 1, Qt::Horizontal, tr("File type"));
|
||||
item_model->setHeaderData(COLUMN_SIZE - 1, Qt::Horizontal, tr("Size"));
|
||||
item_model->removeColumns(COLUMN_COUNT - 1, 1);
|
||||
}
|
||||
|
||||
LoadInterfaceLayout();
|
||||
|
||||
// Delete any rows that might already exist if we're repopulating
|
||||
item_model->removeRows(0, item_model->rowCount());
|
||||
|
||||
|
||||
@@ -123,17 +123,22 @@ void GameListWorker::AddInstalledTitlesToGameList() {
|
||||
if (it != compatibility_list.end())
|
||||
compatibility = it->second.first;
|
||||
|
||||
emit EntryReady({
|
||||
QList<QStandardItem*> list{
|
||||
new GameListItemPath(
|
||||
FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name),
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
|
||||
program_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItem(FormatPatchNameVersions(patch, *loader)),
|
||||
new GameListItem(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
||||
new GameListItemSize(file->GetSize()),
|
||||
});
|
||||
};
|
||||
|
||||
if (UISettings::values.show_add_ons) {
|
||||
list.insert(2, new GameListItem(FormatPatchNameVersions(patch, *loader)));
|
||||
}
|
||||
|
||||
emit EntryReady(list);
|
||||
}
|
||||
|
||||
const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application,
|
||||
@@ -216,18 +221,23 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||
if (it != compatibility_list.end())
|
||||
compatibility = it->second.first;
|
||||
|
||||
emit EntryReady({
|
||||
QList<QStandardItem*> list{
|
||||
new GameListItemPath(
|
||||
FormatGameName(physical_name), icon, QString::fromStdString(name),
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
|
||||
program_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItem(
|
||||
FormatPatchNameVersions(patch, *loader, loader->IsRomFSUpdatable())),
|
||||
new GameListItem(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
||||
new GameListItemSize(FileUtil::GetSize(physical_name)),
|
||||
});
|
||||
};
|
||||
|
||||
if (UISettings::values.show_add_ons) {
|
||||
list.insert(2, new GameListItem(FormatPatchNameVersions(
|
||||
patch, *loader, loader->IsRomFSUpdatable())));
|
||||
}
|
||||
|
||||
emit EntryReady(std::move(list));
|
||||
} else if (is_dir && recursion > 0) {
|
||||
watch_list.append(QString::fromStdString(physical_name));
|
||||
AddFstEntriesToGameList(physical_name, recursion - 1);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "core/file_sys/vfs_real.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
|
||||
// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows
|
||||
// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
|
||||
// defines.
|
||||
static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
|
||||
const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) {
|
||||
@@ -308,6 +308,8 @@ void GMainWindow::InitializeHotkeys() {
|
||||
Qt::ApplicationShortcut);
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
|
||||
Qt::ApplicationShortcut);
|
||||
hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
|
||||
Qt::ApplicationShortcut);
|
||||
hotkey_registry.LoadHotkeys();
|
||||
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
|
||||
@@ -361,6 +363,12 @@ void GMainWindow::InitializeHotkeys() {
|
||||
UpdateStatusBar();
|
||||
}
|
||||
});
|
||||
connect(hotkey_registry.GetHotkey("Main Window", "Load Amiibo", this), &QShortcut::activated,
|
||||
this, [&] {
|
||||
if (ui.action_Load_Amiibo->isEnabled()) {
|
||||
OnLoadAmiibo();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void GMainWindow::SetDefaultUIGeometry() {
|
||||
@@ -1333,7 +1341,13 @@ void GMainWindow::OnConfigure() {
|
||||
UpdateUITheme();
|
||||
if (UISettings::values.enable_discord_presence != old_discord_presence)
|
||||
SetDiscordEnabled(UISettings::values.enable_discord_presence);
|
||||
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
|
||||
|
||||
const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
|
||||
if (reload) {
|
||||
game_list->PopulateAsync(UISettings::values.gamedir,
|
||||
UISettings::values.gamedir_deepscan);
|
||||
}
|
||||
|
||||
config->Save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
@@ -59,9 +60,11 @@ struct Values {
|
||||
|
||||
// Game List
|
||||
bool show_unknown;
|
||||
bool show_add_ons;
|
||||
uint32_t icon_size;
|
||||
uint8_t row_1_text_id;
|
||||
uint8_t row_2_text_id;
|
||||
std::atomic_bool is_game_list_reload_pending{false};
|
||||
};
|
||||
|
||||
extern Values values;
|
||||
|
||||
@@ -65,54 +65,246 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs>
|
||||
},
|
||||
}};
|
||||
|
||||
static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> default_mouse_buttons = {
|
||||
SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_APOSTROPHE,
|
||||
SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS,
|
||||
};
|
||||
|
||||
static const std::array<int, 0x8A> keyboard_keys = {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
SDL_SCANCODE_A,
|
||||
SDL_SCANCODE_B,
|
||||
SDL_SCANCODE_C,
|
||||
SDL_SCANCODE_D,
|
||||
SDL_SCANCODE_E,
|
||||
SDL_SCANCODE_F,
|
||||
SDL_SCANCODE_G,
|
||||
SDL_SCANCODE_H,
|
||||
SDL_SCANCODE_I,
|
||||
SDL_SCANCODE_J,
|
||||
SDL_SCANCODE_K,
|
||||
SDL_SCANCODE_L,
|
||||
SDL_SCANCODE_M,
|
||||
SDL_SCANCODE_N,
|
||||
SDL_SCANCODE_O,
|
||||
SDL_SCANCODE_P,
|
||||
SDL_SCANCODE_Q,
|
||||
SDL_SCANCODE_R,
|
||||
SDL_SCANCODE_S,
|
||||
SDL_SCANCODE_T,
|
||||
SDL_SCANCODE_U,
|
||||
SDL_SCANCODE_V,
|
||||
SDL_SCANCODE_W,
|
||||
SDL_SCANCODE_X,
|
||||
SDL_SCANCODE_Y,
|
||||
SDL_SCANCODE_Z,
|
||||
SDL_SCANCODE_1,
|
||||
SDL_SCANCODE_2,
|
||||
SDL_SCANCODE_3,
|
||||
SDL_SCANCODE_4,
|
||||
SDL_SCANCODE_5,
|
||||
SDL_SCANCODE_6,
|
||||
SDL_SCANCODE_7,
|
||||
SDL_SCANCODE_8,
|
||||
SDL_SCANCODE_9,
|
||||
SDL_SCANCODE_0,
|
||||
SDL_SCANCODE_RETURN,
|
||||
SDL_SCANCODE_ESCAPE,
|
||||
SDL_SCANCODE_BACKSPACE,
|
||||
SDL_SCANCODE_TAB,
|
||||
SDL_SCANCODE_SPACE,
|
||||
SDL_SCANCODE_MINUS,
|
||||
SDL_SCANCODE_EQUALS,
|
||||
SDL_SCANCODE_LEFTBRACKET,
|
||||
SDL_SCANCODE_RIGHTBRACKET,
|
||||
SDL_SCANCODE_BACKSLASH,
|
||||
0,
|
||||
SDL_SCANCODE_SEMICOLON,
|
||||
SDL_SCANCODE_APOSTROPHE,
|
||||
SDL_SCANCODE_GRAVE,
|
||||
SDL_SCANCODE_COMMA,
|
||||
SDL_SCANCODE_PERIOD,
|
||||
SDL_SCANCODE_SLASH,
|
||||
SDL_SCANCODE_CAPSLOCK,
|
||||
|
||||
SDL_SCANCODE_F1,
|
||||
SDL_SCANCODE_F2,
|
||||
SDL_SCANCODE_F3,
|
||||
SDL_SCANCODE_F4,
|
||||
SDL_SCANCODE_F5,
|
||||
SDL_SCANCODE_F6,
|
||||
SDL_SCANCODE_F7,
|
||||
SDL_SCANCODE_F8,
|
||||
SDL_SCANCODE_F9,
|
||||
SDL_SCANCODE_F10,
|
||||
SDL_SCANCODE_F11,
|
||||
SDL_SCANCODE_F12,
|
||||
|
||||
0,
|
||||
SDL_SCANCODE_SCROLLLOCK,
|
||||
SDL_SCANCODE_PAUSE,
|
||||
SDL_SCANCODE_INSERT,
|
||||
SDL_SCANCODE_HOME,
|
||||
SDL_SCANCODE_PAGEUP,
|
||||
SDL_SCANCODE_DELETE,
|
||||
SDL_SCANCODE_END,
|
||||
SDL_SCANCODE_PAGEDOWN,
|
||||
SDL_SCANCODE_RIGHT,
|
||||
SDL_SCANCODE_LEFT,
|
||||
SDL_SCANCODE_DOWN,
|
||||
SDL_SCANCODE_UP,
|
||||
|
||||
SDL_SCANCODE_NUMLOCKCLEAR,
|
||||
SDL_SCANCODE_KP_DIVIDE,
|
||||
SDL_SCANCODE_KP_MULTIPLY,
|
||||
SDL_SCANCODE_KP_MINUS,
|
||||
SDL_SCANCODE_KP_PLUS,
|
||||
SDL_SCANCODE_KP_ENTER,
|
||||
SDL_SCANCODE_KP_1,
|
||||
SDL_SCANCODE_KP_2,
|
||||
SDL_SCANCODE_KP_3,
|
||||
SDL_SCANCODE_KP_4,
|
||||
SDL_SCANCODE_KP_5,
|
||||
SDL_SCANCODE_KP_6,
|
||||
SDL_SCANCODE_KP_7,
|
||||
SDL_SCANCODE_KP_8,
|
||||
SDL_SCANCODE_KP_9,
|
||||
SDL_SCANCODE_KP_0,
|
||||
SDL_SCANCODE_KP_PERIOD,
|
||||
|
||||
0,
|
||||
0,
|
||||
SDL_SCANCODE_POWER,
|
||||
SDL_SCANCODE_KP_EQUALS,
|
||||
|
||||
SDL_SCANCODE_F13,
|
||||
SDL_SCANCODE_F14,
|
||||
SDL_SCANCODE_F15,
|
||||
SDL_SCANCODE_F16,
|
||||
SDL_SCANCODE_F17,
|
||||
SDL_SCANCODE_F18,
|
||||
SDL_SCANCODE_F19,
|
||||
SDL_SCANCODE_F20,
|
||||
SDL_SCANCODE_F21,
|
||||
SDL_SCANCODE_F22,
|
||||
SDL_SCANCODE_F23,
|
||||
SDL_SCANCODE_F24,
|
||||
|
||||
0,
|
||||
SDL_SCANCODE_HELP,
|
||||
SDL_SCANCODE_MENU,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
SDL_SCANCODE_KP_COMMA,
|
||||
SDL_SCANCODE_KP_LEFTPAREN,
|
||||
SDL_SCANCODE_KP_RIGHTPAREN,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
static const std::array<int, 8> keyboard_mods{
|
||||
SDL_SCANCODE_LCTRL, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_LALT, SDL_SCANCODE_LGUI,
|
||||
SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI,
|
||||
};
|
||||
|
||||
void Config::ReadValues() {
|
||||
// Controls
|
||||
for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
|
||||
const auto group = fmt::format("ControlsP{}", p);
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||
Settings::values.players[p].buttons[i] =
|
||||
sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
|
||||
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] =
|
||||
sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
|
||||
if (Settings::values.players[p].analogs[i].empty())
|
||||
Settings::values.players[p].analogs[i] = default_param;
|
||||
}
|
||||
}
|
||||
|
||||
Settings::values.mouse_enabled =
|
||||
sdl2_config->GetBoolean("ControlsGeneral", "mouse_enabled", false);
|
||||
for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
|
||||
Settings::values.mouse_buttons[i] = sdl2_config->Get(
|
||||
"ControlsGeneral", std::string("mouse_") + Settings::NativeMouseButton::mapping[i],
|
||||
default_param);
|
||||
if (Settings::values.mouse_buttons[i].empty())
|
||||
Settings::values.mouse_buttons[i] = default_param;
|
||||
}
|
||||
|
||||
Settings::values.motion_device = sdl2_config->Get(
|
||||
"ControlsGeneral", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
|
||||
|
||||
Settings::values.keyboard_enabled =
|
||||
sdl2_config->GetBoolean("ControlsGeneral", "keyboard_enabled", false);
|
||||
|
||||
Settings::values.debug_pad_enabled =
|
||||
sdl2_config->GetBoolean("ControlsGeneral", "debug_pad_enabled", false);
|
||||
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
|
||||
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
|
||||
Settings::values.buttons[i] =
|
||||
sdl2_config->Get("Controls", Settings::NativeButton::mapping[i], default_param);
|
||||
if (Settings::values.buttons[i].empty())
|
||||
Settings::values.buttons[i] = default_param;
|
||||
Settings::values.debug_pad_buttons[i] = sdl2_config->Get(
|
||||
"ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[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] =
|
||||
sdl2_config->Get("Controls", Settings::NativeAnalog::mapping[i], default_param);
|
||||
if (Settings::values.analogs[i].empty())
|
||||
Settings::values.analogs[i] = default_param;
|
||||
Settings::values.debug_pad_analogs[i] = sdl2_config->Get(
|
||||
"ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i],
|
||||
default_param);
|
||||
if (Settings::values.debug_pad_analogs[i].empty())
|
||||
Settings::values.debug_pad_analogs[i] = default_param;
|
||||
}
|
||||
|
||||
Settings::values.motion_device = sdl2_config->Get(
|
||||
"Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
|
||||
Settings::values.touch_device =
|
||||
sdl2_config->Get("Controls", "touch_device", "engine:emu_window");
|
||||
Settings::values.touchscreen.enabled =
|
||||
sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
|
||||
Settings::values.touchscreen.device =
|
||||
sdl2_config->Get("ControlsGeneral", "touch_device", "engine:emu_window");
|
||||
Settings::values.touchscreen.finger =
|
||||
sdl2_config->GetInteger("ControlsGeneral", "touch_finger", 0);
|
||||
Settings::values.touchscreen.rotation_angle =
|
||||
sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0);
|
||||
Settings::values.touchscreen.diameter_x =
|
||||
sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
|
||||
Settings::values.touchscreen.diameter_y =
|
||||
sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
|
||||
|
||||
// Core
|
||||
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
|
||||
Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
|
||||
|
||||
// Renderer
|
||||
Settings::values.resolution_factor =
|
||||
(float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0);
|
||||
Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
|
||||
Settings::values.frame_limit =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
|
||||
Settings::values.use_accurate_gpu_emulation =
|
||||
sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
|
||||
|
||||
Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
|
||||
Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
|
||||
Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
|
||||
|
||||
// Audio
|
||||
Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
|
||||
Settings::values.enable_audio_stretching =
|
||||
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
|
||||
Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
|
||||
Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);
|
||||
std::transform(keyboard_keys.begin(), keyboard_keys.end(),
|
||||
Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
|
||||
std::transform(keyboard_mods.begin(), keyboard_mods.end(),
|
||||
Settings::values.keyboard_keys.begin() +
|
||||
Settings::NativeKeyboard::LeftControlKey,
|
||||
InputCommon::GenerateKeyboardParam);
|
||||
std::transform(keyboard_mods.begin(), keyboard_mods.end(),
|
||||
Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
|
||||
|
||||
// Data Storage
|
||||
Settings::values.use_virtual_sd =
|
||||
@@ -139,6 +331,32 @@ void Config::ReadValues() {
|
||||
Settings::values.rng_seed = std::nullopt;
|
||||
}
|
||||
|
||||
// Core
|
||||
Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
|
||||
Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
|
||||
|
||||
// Renderer
|
||||
Settings::values.resolution_factor =
|
||||
(float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0);
|
||||
Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
|
||||
Settings::values.frame_limit =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
|
||||
Settings::values.use_accurate_gpu_emulation =
|
||||
sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
|
||||
|
||||
Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
|
||||
Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
|
||||
Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
|
||||
|
||||
// Audio
|
||||
Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
|
||||
Settings::values.enable_audio_stretching =
|
||||
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
|
||||
Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
|
||||
Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);
|
||||
|
||||
Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
|
||||
|
||||
// Miscellaneous
|
||||
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
|
||||
Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
|
||||
@@ -148,6 +366,7 @@ void Config::ReadValues() {
|
||||
Settings::values.gdbstub_port =
|
||||
static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
|
||||
Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
|
||||
Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
|
||||
|
||||
// Web Service
|
||||
Settings::values.enable_telemetry =
|
||||
|
||||
@@ -206,6 +206,8 @@ log_filter = *:Trace
|
||||
# Port for listening to GDB connections.
|
||||
use_gdbstub=false
|
||||
gdbstub_port=24689
|
||||
# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
|
||||
dump_nso=false
|
||||
|
||||
[WebService]
|
||||
# Whether or not to enable telemetry
|
||||
|
||||
Reference in New Issue
Block a user