diff options
70 files changed, 5563 insertions, 1804 deletions
diff --git a/.travis/clang-format/build.sh b/.travis/clang-format/build.sh index 3707be3c9..36c276c43 100755 --- a/.travis/clang-format/build.sh +++ b/.travis/clang-format/build.sh | |||
| @@ -1,3 +1,3 @@ | |||
| 1 | #!/bin/bash -ex | 1 | #!/bin/bash -ex |
| 2 | 2 | ||
| 3 | docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash -ex /yuzu/.travis/clang-format/docker.sh | 3 | 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 |
diff --git a/.travis/clang-format/deps.sh b/.travis/clang-format/deps.sh index 540bb934a..a15d164c7 100755 --- a/.travis/clang-format/deps.sh +++ b/.travis/clang-format/deps.sh | |||
| @@ -1,3 +1,3 @@ | |||
| 1 | #!/bin/sh -ex | 1 | #!/bin/sh -ex |
| 2 | 2 | ||
| 3 | docker pull ubuntu:18.04 | 3 | docker pull citraemu/build-environments:linux-clang-format |
diff --git a/.travis/clang-format/docker.sh b/.travis/clang-format/docker.sh index 05047e9b8..b519ab40e 100755 --- a/.travis/clang-format/docker.sh +++ b/.travis/clang-format/docker.sh | |||
| @@ -1,8 +1,5 @@ | |||
| 1 | #!/bin/bash -ex | 1 | #!/bin/bash -ex |
| 2 | 2 | ||
| 3 | apt-get update | ||
| 4 | apt-get install -y clang-format-6.0 | ||
| 5 | |||
| 6 | # Run clang-format | 3 | # Run clang-format |
| 7 | cd /yuzu | 4 | cd /yuzu |
| 8 | ./.travis/clang-format/script.sh | 5 | ./.travis/clang-format/script.sh |
diff --git a/externals/cmake-modules/GetGitRevisionDescription.cmake.in b/externals/cmake-modules/GetGitRevisionDescription.cmake.in index 888ce13aa..0d7eb3c26 100644 --- a/externals/cmake-modules/GetGitRevisionDescription.cmake.in +++ b/externals/cmake-modules/GetGitRevisionDescription.cmake.in | |||
| @@ -33,6 +33,10 @@ else() | |||
| 33 | endif() | 33 | endif() |
| 34 | 34 | ||
| 35 | if(NOT HEAD_HASH) | 35 | if(NOT HEAD_HASH) |
| 36 | file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) | 36 | if(EXISTS "@GIT_DATA@/head-ref") |
| 37 | string(STRIP "${HEAD_HASH}" HEAD_HASH) | 37 | file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) |
| 38 | string(STRIP "${HEAD_HASH}" HEAD_HASH) | ||
| 39 | else() | ||
| 40 | set(HEAD_HASH "Unknown") | ||
| 41 | endif() | ||
| 38 | endif() | 42 | endif() |
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 39bdf4e21..16fdcd376 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h | |||
| @@ -132,4 +132,11 @@ using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float> | |||
| 132 | */ | 132 | */ |
| 133 | using TouchDevice = InputDevice<std::tuple<float, float, bool>>; | 133 | using TouchDevice = InputDevice<std::tuple<float, float, bool>>; |
| 134 | 134 | ||
| 135 | /** | ||
| 136 | * A mouse device is an input device that returns a tuple of two floats and four ints. | ||
| 137 | * The first two floats are X and Y device coordinates of the mouse (from 0-1). | ||
| 138 | * The s32s are the mouse wheel. | ||
| 139 | */ | ||
| 140 | using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>; | ||
| 141 | |||
| 135 | } // namespace Input | 142 | } // namespace Input |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1fd4ba5d2..e441c5bc6 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -105,7 +105,7 @@ struct KernelCore::Impl { | |||
| 105 | void Initialize(KernelCore& kernel) { | 105 | void Initialize(KernelCore& kernel) { |
| 106 | Shutdown(); | 106 | Shutdown(); |
| 107 | 107 | ||
| 108 | InitializeResourceLimits(kernel); | 108 | InitializeSystemResourceLimit(kernel); |
| 109 | InitializeThreads(); | 109 | InitializeThreads(); |
| 110 | InitializeTimers(); | 110 | InitializeTimers(); |
| 111 | } | 111 | } |
| @@ -118,7 +118,7 @@ struct KernelCore::Impl { | |||
| 118 | process_list.clear(); | 118 | process_list.clear(); |
| 119 | current_process = nullptr; | 119 | current_process = nullptr; |
| 120 | 120 | ||
| 121 | resource_limits.fill(nullptr); | 121 | system_resource_limit = nullptr; |
| 122 | 122 | ||
| 123 | thread_wakeup_callback_handle_table.Clear(); | 123 | thread_wakeup_callback_handle_table.Clear(); |
| 124 | thread_wakeup_event_type = nullptr; | 124 | thread_wakeup_event_type = nullptr; |
| @@ -129,63 +129,17 @@ struct KernelCore::Impl { | |||
| 129 | named_ports.clear(); | 129 | named_ports.clear(); |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | void InitializeResourceLimits(KernelCore& kernel) { | 132 | // Creates the default system resource limit |
| 133 | // Create the four resource limits that the system uses | 133 | void InitializeSystemResourceLimit(KernelCore& kernel) { |
| 134 | // Create the APPLICATION resource limit | 134 | system_resource_limit = ResourceLimit::Create(kernel, "System"); |
| 135 | SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications"); | 135 | |
| 136 | resource_limit->max_priority = 0x18; | 136 | // If setting the default system values fails, then something seriously wrong has occurred. |
| 137 | resource_limit->max_commit = 0x4000000; | 137 | ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000) |
| 138 | resource_limit->max_threads = 0x20; | 138 | .IsSuccess()); |
| 139 | resource_limit->max_events = 0x20; | 139 | ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess()); |
| 140 | resource_limit->max_mutexes = 0x20; | 140 | ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess()); |
| 141 | resource_limit->max_semaphores = 0x8; | 141 | ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess()); |
| 142 | resource_limit->max_timers = 0x8; | 142 | ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess()); |
| 143 | resource_limit->max_shared_mems = 0x10; | ||
| 144 | resource_limit->max_address_arbiters = 0x2; | ||
| 145 | resource_limit->max_cpu_time = 0x1E; | ||
| 146 | resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit; | ||
| 147 | |||
| 148 | // Create the SYS_APPLET resource limit | ||
| 149 | resource_limit = ResourceLimit::Create(kernel, "System Applets"); | ||
| 150 | resource_limit->max_priority = 0x4; | ||
| 151 | resource_limit->max_commit = 0x5E00000; | ||
| 152 | resource_limit->max_threads = 0x1D; | ||
| 153 | resource_limit->max_events = 0xB; | ||
| 154 | resource_limit->max_mutexes = 0x8; | ||
| 155 | resource_limit->max_semaphores = 0x4; | ||
| 156 | resource_limit->max_timers = 0x4; | ||
| 157 | resource_limit->max_shared_mems = 0x8; | ||
| 158 | resource_limit->max_address_arbiters = 0x3; | ||
| 159 | resource_limit->max_cpu_time = 0x2710; | ||
| 160 | resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit; | ||
| 161 | |||
| 162 | // Create the LIB_APPLET resource limit | ||
| 163 | resource_limit = ResourceLimit::Create(kernel, "Library Applets"); | ||
| 164 | resource_limit->max_priority = 0x4; | ||
| 165 | resource_limit->max_commit = 0x600000; | ||
| 166 | resource_limit->max_threads = 0xE; | ||
| 167 | resource_limit->max_events = 0x8; | ||
| 168 | resource_limit->max_mutexes = 0x8; | ||
| 169 | resource_limit->max_semaphores = 0x4; | ||
| 170 | resource_limit->max_timers = 0x4; | ||
| 171 | resource_limit->max_shared_mems = 0x8; | ||
| 172 | resource_limit->max_address_arbiters = 0x1; | ||
| 173 | resource_limit->max_cpu_time = 0x2710; | ||
| 174 | resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit; | ||
| 175 | |||
| 176 | // Create the OTHER resource limit | ||
| 177 | resource_limit = ResourceLimit::Create(kernel, "Others"); | ||
| 178 | resource_limit->max_priority = 0x4; | ||
| 179 | resource_limit->max_commit = 0x2180000; | ||
| 180 | resource_limit->max_threads = 0xE1; | ||
| 181 | resource_limit->max_events = 0x108; | ||
| 182 | resource_limit->max_mutexes = 0x25; | ||
| 183 | resource_limit->max_semaphores = 0x43; | ||
| 184 | resource_limit->max_timers = 0x2C; | ||
| 185 | resource_limit->max_shared_mems = 0x1F; | ||
| 186 | resource_limit->max_address_arbiters = 0x2D; | ||
| 187 | resource_limit->max_cpu_time = 0x3E8; | ||
| 188 | resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit; | ||
| 189 | } | 143 | } |
| 190 | 144 | ||
| 191 | void InitializeThreads() { | 145 | void InitializeThreads() { |
| @@ -208,7 +162,7 @@ struct KernelCore::Impl { | |||
| 208 | std::vector<SharedPtr<Process>> process_list; | 162 | std::vector<SharedPtr<Process>> process_list; |
| 209 | Process* current_process = nullptr; | 163 | Process* current_process = nullptr; |
| 210 | 164 | ||
| 211 | std::array<SharedPtr<ResourceLimit>, 4> resource_limits; | 165 | SharedPtr<ResourceLimit> system_resource_limit; |
| 212 | 166 | ||
| 213 | /// The event type of the generic timer callback event | 167 | /// The event type of the generic timer callback event |
| 214 | CoreTiming::EventType* timer_callback_event_type = nullptr; | 168 | CoreTiming::EventType* timer_callback_event_type = nullptr; |
| @@ -239,9 +193,8 @@ void KernelCore::Shutdown() { | |||
| 239 | impl->Shutdown(); | 193 | impl->Shutdown(); |
| 240 | } | 194 | } |
| 241 | 195 | ||
| 242 | SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory( | 196 | SharedPtr<ResourceLimit> KernelCore::GetSystemResourceLimit() const { |
| 243 | ResourceLimitCategory category) const { | 197 | return impl->system_resource_limit; |
| 244 | return impl->resource_limits.at(static_cast<std::size_t>(category)); | ||
| 245 | } | 198 | } |
| 246 | 199 | ||
| 247 | SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const { | 200 | SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const { |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 7f822d524..ea00c89f5 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -24,8 +24,6 @@ class ResourceLimit; | |||
| 24 | class Thread; | 24 | class Thread; |
| 25 | class Timer; | 25 | class Timer; |
| 26 | 26 | ||
| 27 | enum class ResourceLimitCategory : u8; | ||
| 28 | |||
| 29 | /// Represents a single instance of the kernel. | 27 | /// Represents a single instance of the kernel. |
| 30 | class KernelCore { | 28 | class KernelCore { |
| 31 | private: | 29 | private: |
| @@ -47,8 +45,8 @@ public: | |||
| 47 | /// Clears all resources in use by the kernel instance. | 45 | /// Clears all resources in use by the kernel instance. |
| 48 | void Shutdown(); | 46 | void Shutdown(); |
| 49 | 47 | ||
| 50 | /// Retrieves a shared pointer to a ResourceLimit identified by the given category. | 48 | /// Retrieves a shared pointer to the system resource limit instance. |
| 51 | SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const; | 49 | SharedPtr<ResourceLimit> GetSystemResourceLimit() const; |
| 52 | 50 | ||
| 53 | /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table. | 51 | /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table. |
| 54 | SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const; | 52 | SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const; |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index f06b6bb55..1412257a0 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -28,7 +28,7 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { | |||
| 28 | process->name = std::move(name); | 28 | process->name = std::move(name); |
| 29 | process->flags.raw = 0; | 29 | process->flags.raw = 0; |
| 30 | process->flags.memory_region.Assign(MemoryRegion::APPLICATION); | 30 | process->flags.memory_region.Assign(MemoryRegion::APPLICATION); |
| 31 | process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION); | 31 | process->resource_limit = kernel.GetSystemResourceLimit(); |
| 32 | process->status = ProcessStatus::Created; | 32 | process->status = ProcessStatus::Created; |
| 33 | process->program_id = 0; | 33 | process->program_id = 0; |
| 34 | process->process_id = kernel.CreateNewProcessID(); | 34 | process->process_id = kernel.CreateNewProcessID(); |
| @@ -252,8 +252,8 @@ ResultCode Process::HeapFree(VAddr target, u32 size) { | |||
| 252 | return vm_manager.HeapFree(target, size); | 252 | return vm_manager.HeapFree(target, size); |
| 253 | } | 253 | } |
| 254 | 254 | ||
| 255 | ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | 255 | ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { |
| 256 | return vm_manager.MirrorMemory(dst_addr, src_addr, size); | 256 | return vm_manager.MirrorMemory(dst_addr, src_addr, size, state); |
| 257 | } | 257 | } |
| 258 | 258 | ||
| 259 | ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { | 259 | ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index cf48787ce..230e395ff 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -259,7 +259,8 @@ public: | |||
| 259 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); | 259 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); |
| 260 | ResultCode HeapFree(VAddr target, u32 size); | 260 | ResultCode HeapFree(VAddr target, u32 size); |
| 261 | 261 | ||
| 262 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); | 262 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, |
| 263 | MemoryState state = MemoryState::Mapped); | ||
| 263 | 264 | ||
| 264 | ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); | 265 | ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); |
| 265 | 266 | ||
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index b253a680f..2f9695005 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp | |||
| @@ -2,12 +2,16 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include "core/hle/kernel/errors.h" |
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/hle/kernel/resource_limit.h" | 6 | #include "core/hle/kernel/resource_limit.h" |
| 7 | #include "core/hle/result.h" | ||
| 9 | 8 | ||
| 10 | namespace Kernel { | 9 | namespace Kernel { |
| 10 | namespace { | ||
| 11 | constexpr std::size_t ResourceTypeToIndex(ResourceType type) { | ||
| 12 | return static_cast<std::size_t>(type); | ||
| 13 | } | ||
| 14 | } // Anonymous namespace | ||
| 11 | 15 | ||
| 12 | ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {} | 16 | ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {} |
| 13 | ResourceLimit::~ResourceLimit() = default; | 17 | ResourceLimit::~ResourceLimit() = default; |
| @@ -19,59 +23,22 @@ SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string n | |||
| 19 | return resource_limit; | 23 | return resource_limit; |
| 20 | } | 24 | } |
| 21 | 25 | ||
| 22 | s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { | 26 | s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { |
| 23 | switch (resource) { | 27 | return values.at(ResourceTypeToIndex(resource)); |
| 24 | case ResourceType::Commit: | 28 | } |
| 25 | return current_commit; | 29 | |
| 26 | case ResourceType::Thread: | 30 | s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const { |
| 27 | return current_threads; | 31 | return limits.at(ResourceTypeToIndex(resource)); |
| 28 | case ResourceType::Event: | ||
| 29 | return current_events; | ||
| 30 | case ResourceType::Mutex: | ||
| 31 | return current_mutexes; | ||
| 32 | case ResourceType::Semaphore: | ||
| 33 | return current_semaphores; | ||
| 34 | case ResourceType::Timer: | ||
| 35 | return current_timers; | ||
| 36 | case ResourceType::SharedMemory: | ||
| 37 | return current_shared_mems; | ||
| 38 | case ResourceType::AddressArbiter: | ||
| 39 | return current_address_arbiters; | ||
| 40 | case ResourceType::CPUTime: | ||
| 41 | return current_cpu_time; | ||
| 42 | default: | ||
| 43 | LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource)); | ||
| 44 | UNIMPLEMENTED(); | ||
| 45 | return 0; | ||
| 46 | } | ||
| 47 | } | 32 | } |
| 48 | 33 | ||
| 49 | u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const { | 34 | ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) { |
| 50 | switch (resource) { | 35 | const auto index = ResourceTypeToIndex(resource); |
| 51 | case ResourceType::Priority: | 36 | |
| 52 | return max_priority; | 37 | if (value < values[index]) { |
| 53 | case ResourceType::Commit: | 38 | return ERR_INVALID_STATE; |
| 54 | return max_commit; | ||
| 55 | case ResourceType::Thread: | ||
| 56 | return max_threads; | ||
| 57 | case ResourceType::Event: | ||
| 58 | return max_events; | ||
| 59 | case ResourceType::Mutex: | ||
| 60 | return max_mutexes; | ||
| 61 | case ResourceType::Semaphore: | ||
| 62 | return max_semaphores; | ||
| 63 | case ResourceType::Timer: | ||
| 64 | return max_timers; | ||
| 65 | case ResourceType::SharedMemory: | ||
| 66 | return max_shared_mems; | ||
| 67 | case ResourceType::AddressArbiter: | ||
| 68 | return max_address_arbiters; | ||
| 69 | case ResourceType::CPUTime: | ||
| 70 | return max_cpu_time; | ||
| 71 | default: | ||
| 72 | LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource)); | ||
| 73 | UNIMPLEMENTED(); | ||
| 74 | return 0; | ||
| 75 | } | 39 | } |
| 40 | |||
| 41 | values[index] = value; | ||
| 42 | return RESULT_SUCCESS; | ||
| 76 | } | 43 | } |
| 77 | } // namespace Kernel | 44 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 219e49562..bec065543 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h | |||
| @@ -4,31 +4,25 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | #include "core/hle/kernel/object.h" | 9 | #include "core/hle/kernel/object.h" |
| 9 | 10 | ||
| 11 | union ResultCode; | ||
| 12 | |||
| 10 | namespace Kernel { | 13 | namespace Kernel { |
| 11 | 14 | ||
| 12 | class KernelCore; | 15 | class KernelCore; |
| 13 | 16 | ||
| 14 | enum class ResourceLimitCategory : u8 { | ||
| 15 | APPLICATION = 0, | ||
| 16 | SYS_APPLET = 1, | ||
| 17 | LIB_APPLET = 2, | ||
| 18 | OTHER = 3 | ||
| 19 | }; | ||
| 20 | |||
| 21 | enum class ResourceType { | 17 | enum class ResourceType { |
| 22 | Priority = 0, | 18 | PhysicalMemory, |
| 23 | Commit = 1, | 19 | Threads, |
| 24 | Thread = 2, | 20 | Events, |
| 25 | Event = 3, | 21 | TransferMemory, |
| 26 | Mutex = 4, | 22 | Sessions, |
| 27 | Semaphore = 5, | 23 | |
| 28 | Timer = 6, | 24 | // Used as a count, not an actual type. |
| 29 | SharedMemory = 7, | 25 | ResourceTypeCount |
| 30 | AddressArbiter = 8, | ||
| 31 | CPUTime = 9, | ||
| 32 | }; | 26 | }; |
| 33 | 27 | ||
| 34 | class ResourceLimit final : public Object { | 28 | class ResourceLimit final : public Object { |
| @@ -55,61 +49,51 @@ public: | |||
| 55 | * @param resource Requested resource type | 49 | * @param resource Requested resource type |
| 56 | * @returns The current value of the resource type | 50 | * @returns The current value of the resource type |
| 57 | */ | 51 | */ |
| 58 | s32 GetCurrentResourceValue(ResourceType resource) const; | 52 | s64 GetCurrentResourceValue(ResourceType resource) const; |
| 59 | 53 | ||
| 60 | /** | 54 | /** |
| 61 | * Gets the max value for the specified resource. | 55 | * Gets the max value for the specified resource. |
| 62 | * @param resource Requested resource type | 56 | * @param resource Requested resource type |
| 63 | * @returns The max value of the resource type | 57 | * @returns The max value of the resource type |
| 64 | */ | 58 | */ |
| 65 | u32 GetMaxResourceValue(ResourceType resource) const; | 59 | s64 GetMaxResourceValue(ResourceType resource) const; |
| 66 | |||
| 67 | /// Name of resource limit object. | ||
| 68 | std::string name; | ||
| 69 | |||
| 70 | /// Max thread priority that a process in this category can create | ||
| 71 | s32 max_priority = 0; | ||
| 72 | |||
| 73 | /// Max memory that processes in this category can use | ||
| 74 | s32 max_commit = 0; | ||
| 75 | 60 | ||
| 76 | ///< Max number of objects that can be collectively created by the processes in this category | 61 | /** |
| 77 | s32 max_threads = 0; | 62 | * Sets the limit value for a given resource type. |
| 78 | s32 max_events = 0; | 63 | * |
| 79 | s32 max_mutexes = 0; | 64 | * @param resource The resource type to apply the limit to. |
| 80 | s32 max_semaphores = 0; | 65 | * @param value The limit to apply to the given resource type. |
| 81 | s32 max_timers = 0; | 66 | * |
| 82 | s32 max_shared_mems = 0; | 67 | * @return A result code indicating if setting the limit value |
| 83 | s32 max_address_arbiters = 0; | 68 | * was successful or not. |
| 69 | * | ||
| 70 | * @note The supplied limit value *must* be greater than or equal to | ||
| 71 | * the current resource value for the given resource type, | ||
| 72 | * otherwise ERR_INVALID_STATE will be returned. | ||
| 73 | */ | ||
| 74 | ResultCode SetLimitValue(ResourceType resource, s64 value); | ||
| 84 | 75 | ||
| 85 | /// Max CPU time that the processes in this category can utilize | 76 | private: |
| 86 | s32 max_cpu_time = 0; | 77 | explicit ResourceLimit(KernelCore& kernel); |
| 78 | ~ResourceLimit() override; | ||
| 87 | 79 | ||
| 88 | // TODO(Subv): Increment these in their respective Kernel::T::Create functions, keeping in mind | 80 | // TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create |
| 89 | // that APPLICATION resource limits should not be affected by the objects created by service | 81 | // functions |
| 90 | // modules. | 82 | // |
| 91 | // Currently we have no way of distinguishing if a Create was called by the running application, | 83 | // Currently we have no way of distinguishing if a Create was called by the running application, |
| 92 | // or by a service module. Approach this once we have separated the service modules into their | 84 | // or by a service module. Approach this once we have separated the service modules into their |
| 93 | // own processes | 85 | // own processes |
| 94 | 86 | ||
| 95 | /// Current memory that the processes in this category are using | 87 | using ResourceArray = |
| 96 | s32 current_commit = 0; | 88 | std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>; |
| 97 | 89 | ||
| 98 | ///< Current number of objects among all processes in this category | 90 | /// Maximum values a resource type may reach. |
| 99 | s32 current_threads = 0; | 91 | ResourceArray limits{}; |
| 100 | s32 current_events = 0; | 92 | /// Current resource limit values. |
| 101 | s32 current_mutexes = 0; | 93 | ResourceArray values{}; |
| 102 | s32 current_semaphores = 0; | ||
| 103 | s32 current_timers = 0; | ||
| 104 | s32 current_shared_mems = 0; | ||
| 105 | s32 current_address_arbiters = 0; | ||
| 106 | 94 | ||
| 107 | /// Current CPU time that the processes in this category are utilizing | 95 | /// Name of resource limit object. |
| 108 | s32 current_cpu_time = 0; | 96 | std::string name; |
| 109 | |||
| 110 | private: | ||
| 111 | explicit ResourceLimit(KernelCore& kernel); | ||
| 112 | ~ResourceLimit() override; | ||
| 113 | }; | 97 | }; |
| 114 | 98 | ||
| 115 | } // namespace Kernel | 99 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index f84b00a00..51c367de7 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -736,13 +736,6 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { | |||
| 736 | 736 | ||
| 737 | const auto* const current_process = Core::CurrentProcess(); | 737 | const auto* const current_process = Core::CurrentProcess(); |
| 738 | 738 | ||
| 739 | // Note: The kernel uses the current process's resource limit instead of | ||
| 740 | // the one from the thread owner's resource limit. | ||
| 741 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); | ||
| 742 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { | ||
| 743 | return ERR_INVALID_THREAD_PRIORITY; | ||
| 744 | } | ||
| 745 | |||
| 746 | SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); | 739 | SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); |
| 747 | if (!thread) { | 740 | if (!thread) { |
| 748 | return ERR_INVALID_HANDLE; | 741 | return ERR_INVALID_HANDLE; |
| @@ -885,10 +878,6 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 885 | } | 878 | } |
| 886 | 879 | ||
| 887 | auto* const current_process = Core::CurrentProcess(); | 880 | auto* const current_process = Core::CurrentProcess(); |
| 888 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); | ||
| 889 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { | ||
| 890 | return ERR_INVALID_THREAD_PRIORITY; | ||
| 891 | } | ||
| 892 | 881 | ||
| 893 | if (processor_id == THREADPROCESSORID_DEFAULT) { | 882 | if (processor_id == THREADPROCESSORID_DEFAULT) { |
| 894 | // Set the target CPU to the one specified in the process' exheader. | 883 | // Set the target CPU to the one specified in the process' exheader. |
| @@ -1181,7 +1170,7 @@ static ResultCode CloseHandle(Handle handle) { | |||
| 1181 | 1170 | ||
| 1182 | /// Reset an event | 1171 | /// Reset an event |
| 1183 | static ResultCode ResetSignal(Handle handle) { | 1172 | static ResultCode ResetSignal(Handle handle) { |
| 1184 | LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); | 1173 | LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); |
| 1185 | 1174 | ||
| 1186 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1175 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1187 | auto event = handle_table.Get<Event>(handle); | 1176 | auto event = handle_table.Get<Event>(handle); |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index ec7fd6150..100f8f6bf 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -298,7 +298,7 @@ ResultCode VMManager::HeapFree(VAddr target, u64 size) { | |||
| 298 | return RESULT_SUCCESS; | 298 | return RESULT_SUCCESS; |
| 299 | } | 299 | } |
| 300 | 300 | ||
| 301 | ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | 301 | ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { |
| 302 | const auto vma = FindVMA(src_addr); | 302 | const auto vma = FindVMA(src_addr); |
| 303 | 303 | ||
| 304 | ASSERT_MSG(vma != vma_map.end(), "Invalid memory address"); | 304 | ASSERT_MSG(vma != vma_map.end(), "Invalid memory address"); |
| @@ -312,8 +312,8 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 312 | const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; | 312 | const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; |
| 313 | const std::size_t backing_block_offset = vma->second.offset + vma_offset; | 313 | const std::size_t backing_block_offset = vma->second.offset + vma_offset; |
| 314 | 314 | ||
| 315 | CASCADE_RESULT(auto new_vma, MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, | 315 | CASCADE_RESULT(auto new_vma, |
| 316 | MemoryState::Mapped)); | 316 | MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, state)); |
| 317 | // Protect mirror with permissions from old region | 317 | // Protect mirror with permissions from old region |
| 318 | Reprotect(new_vma, vma->second.permissions); | 318 | Reprotect(new_vma, vma->second.permissions); |
| 319 | // Remove permissions from old region | 319 | // Remove permissions from old region |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 248cc46dc..d522404fe 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -189,7 +189,8 @@ public: | |||
| 189 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); | 189 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); |
| 190 | ResultCode HeapFree(VAddr target, u64 size); | 190 | ResultCode HeapFree(VAddr target, u64 size); |
| 191 | 191 | ||
| 192 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); | 192 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, |
| 193 | MemoryState state = MemoryState::Mapped); | ||
| 193 | 194 | ||
| 194 | /** | 195 | /** |
| 195 | * Scans all VMAs and updates the page table range of any that use the given vector as backing | 196 | * Scans all VMAs and updates the page table range of any that use the given vector as backing |
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index 3d100763f..e76c83aee 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp | |||
| @@ -6,9 +6,14 @@ | |||
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 8 | #include "core/hle/service/hid/controllers/debug_pad.h" | 8 | #include "core/hle/service/hid/controllers/debug_pad.h" |
| 9 | #include "core/settings.h" | ||
| 9 | 10 | ||
| 10 | namespace Service::HID { | 11 | namespace Service::HID { |
| 11 | 12 | ||
| 13 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | ||
| 14 | constexpr s32 HID_JOYSTICK_MIN = -0x7fff; | ||
| 15 | enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; | ||
| 16 | |||
| 12 | Controller_DebugPad::Controller_DebugPad() = default; | 17 | Controller_DebugPad::Controller_DebugPad() = default; |
| 13 | Controller_DebugPad::~Controller_DebugPad() = default; | 18 | Controller_DebugPad::~Controller_DebugPad() = default; |
| 14 | 19 | ||
| @@ -33,10 +38,43 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) { | |||
| 33 | 38 | ||
| 34 | cur_entry.sampling_number = last_entry.sampling_number + 1; | 39 | cur_entry.sampling_number = last_entry.sampling_number + 1; |
| 35 | cur_entry.sampling_number2 = cur_entry.sampling_number; | 40 | cur_entry.sampling_number2 = cur_entry.sampling_number; |
| 36 | // TODO(ogniK): Update debug pad states | 41 | cur_entry.attribute.connected.Assign(1); |
| 42 | auto& pad = cur_entry.pad_state; | ||
| 43 | |||
| 44 | using namespace Settings::NativeButton; | ||
| 45 | pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 46 | pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 47 | pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 48 | pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 49 | pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 50 | pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 51 | pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 52 | pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 53 | pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 54 | pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 55 | pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 56 | pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 57 | pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 58 | pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 59 | |||
| 60 | const auto [stick_l_x_f, stick_l_y_f] = | ||
| 61 | analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | ||
| 62 | const auto [stick_r_x_f, stick_r_y_f] = | ||
| 63 | analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | ||
| 64 | cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | ||
| 65 | cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | ||
| 66 | cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | ||
| 67 | cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); | ||
| 37 | 68 | ||
| 38 | std::memcpy(data, &shared_memory, sizeof(SharedMemory)); | 69 | std::memcpy(data, &shared_memory, sizeof(SharedMemory)); |
| 39 | } | 70 | } |
| 40 | 71 | ||
| 41 | void Controller_DebugPad::OnLoadInputDevices() {} | 72 | void Controller_DebugPad::OnLoadInputDevices() { |
| 73 | std::transform(Settings::values.debug_pad_buttons.begin(), | ||
| 74 | Settings::values.debug_pad_buttons.end(), buttons.begin(), | ||
| 75 | Input::CreateDevice<Input::ButtonDevice>); | ||
| 76 | std::transform(Settings::values.debug_pad_analogs.begin(), | ||
| 77 | Settings::values.debug_pad_analogs.end(), analogs.begin(), | ||
| 78 | Input::CreateDevice<Input::AnalogDevice>); | ||
| 79 | } | ||
| 42 | } // namespace Service::HID | 80 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index 62b4f2682..68b734248 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h | |||
| @@ -5,10 +5,13 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include "common/bit_field.h" | ||
| 8 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 12 | #include "core/frontend/input.h" | ||
| 11 | #include "core/hle/service/hid/controllers/controller_base.h" | 13 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 14 | #include "core/settings.h" | ||
| 12 | 15 | ||
| 13 | namespace Service::HID { | 16 | namespace Service::HID { |
| 14 | class Controller_DebugPad final : public ControllerBase { | 17 | class Controller_DebugPad final : public ControllerBase { |
| @@ -35,11 +38,40 @@ private: | |||
| 35 | }; | 38 | }; |
| 36 | static_assert(sizeof(AnalogStick) == 0x8); | 39 | static_assert(sizeof(AnalogStick) == 0x8); |
| 37 | 40 | ||
| 41 | struct PadState { | ||
| 42 | union { | ||
| 43 | u32_le raw{}; | ||
| 44 | BitField<0, 1, u32_le> a; | ||
| 45 | BitField<1, 1, u32_le> b; | ||
| 46 | BitField<2, 1, u32_le> x; | ||
| 47 | BitField<3, 1, u32_le> y; | ||
| 48 | BitField<4, 1, u32_le> l; | ||
| 49 | BitField<5, 1, u32_le> r; | ||
| 50 | BitField<6, 1, u32_le> zl; | ||
| 51 | BitField<7, 1, u32_le> zr; | ||
| 52 | BitField<8, 1, u32_le> plus; | ||
| 53 | BitField<9, 1, u32_le> minus; | ||
| 54 | BitField<10, 1, u32_le> d_left; | ||
| 55 | BitField<11, 1, u32_le> d_up; | ||
| 56 | BitField<12, 1, u32_le> d_right; | ||
| 57 | BitField<13, 1, u32_le> d_down; | ||
| 58 | }; | ||
| 59 | }; | ||
| 60 | static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size"); | ||
| 61 | |||
| 62 | struct Attributes { | ||
| 63 | union { | ||
| 64 | u32_le raw{}; | ||
| 65 | BitField<0, 1, u32_le> connected; | ||
| 66 | }; | ||
| 67 | }; | ||
| 68 | static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); | ||
| 69 | |||
| 38 | struct PadStates { | 70 | struct PadStates { |
| 39 | s64_le sampling_number; | 71 | s64_le sampling_number; |
| 40 | s64_le sampling_number2; | 72 | s64_le sampling_number2; |
| 41 | u32_le attribute; | 73 | Attributes attribute; |
| 42 | u32_le button_state; | 74 | PadState pad_state; |
| 43 | AnalogStick r_stick; | 75 | AnalogStick r_stick; |
| 44 | AnalogStick l_stick; | 76 | AnalogStick l_stick; |
| 45 | }; | 77 | }; |
| @@ -52,5 +84,10 @@ private: | |||
| 52 | }; | 84 | }; |
| 53 | static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); | 85 | static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); |
| 54 | SharedMemory shared_memory{}; | 86 | SharedMemory shared_memory{}; |
| 87 | |||
| 88 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> | ||
| 89 | buttons; | ||
| 90 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> | ||
| 91 | analogs; | ||
| 55 | }; | 92 | }; |
| 56 | } // namespace Service::HID | 93 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index ccfbce9ac..ca75adc2b 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp | |||
| @@ -6,9 +6,11 @@ | |||
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 8 | #include "core/hle/service/hid/controllers/keyboard.h" | 8 | #include "core/hle/service/hid/controllers/keyboard.h" |
| 9 | #include "core/settings.h" | ||
| 9 | 10 | ||
| 10 | namespace Service::HID { | 11 | namespace Service::HID { |
| 11 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; | 12 | constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; |
| 13 | constexpr u8 KEYS_PER_BYTE = 8; | ||
| 12 | 14 | ||
| 13 | Controller_Keyboard::Controller_Keyboard() = default; | 15 | Controller_Keyboard::Controller_Keyboard() = default; |
| 14 | Controller_Keyboard::~Controller_Keyboard() = default; | 16 | Controller_Keyboard::~Controller_Keyboard() = default; |
| @@ -34,10 +36,24 @@ void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) { | |||
| 34 | 36 | ||
| 35 | cur_entry.sampling_number = last_entry.sampling_number + 1; | 37 | cur_entry.sampling_number = last_entry.sampling_number + 1; |
| 36 | cur_entry.sampling_number2 = cur_entry.sampling_number; | 38 | cur_entry.sampling_number2 = cur_entry.sampling_number; |
| 37 | // TODO(ogniK): Update keyboard states | 39 | |
| 40 | for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { | ||
| 41 | for (std::size_t k = 0; k < KEYS_PER_BYTE; ++k) { | ||
| 42 | cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << k); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | for (std::size_t i = 0; i < keyboard_mods.size(); ++i) { | ||
| 47 | cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i); | ||
| 48 | } | ||
| 38 | 49 | ||
| 39 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); | 50 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); |
| 40 | } | 51 | } |
| 41 | 52 | ||
| 42 | void Controller_Keyboard::OnLoadInputDevices() {} | 53 | void Controller_Keyboard::OnLoadInputDevices() { |
| 54 | std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(), | ||
| 55 | keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>); | ||
| 56 | std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(), | ||
| 57 | keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>); | ||
| 58 | } | ||
| 43 | } // namespace Service::HID | 59 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index 493e68fce..f52775456 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h | |||
| @@ -8,7 +8,9 @@ | |||
| 8 | #include "common/common_funcs.h" | 8 | #include "common/common_funcs.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 11 | #include "core/frontend/input.h" | ||
| 11 | #include "core/hle/service/hid/controllers/controller_base.h" | 12 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 13 | #include "core/settings.h" | ||
| 12 | 14 | ||
| 13 | namespace Service::HID { | 15 | namespace Service::HID { |
| 14 | class Controller_Keyboard final : public ControllerBase { | 16 | class Controller_Keyboard final : public ControllerBase { |
| @@ -46,5 +48,10 @@ private: | |||
| 46 | }; | 48 | }; |
| 47 | static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); | 49 | static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); |
| 48 | SharedMemory shared_memory{}; | 50 | SharedMemory shared_memory{}; |
| 51 | |||
| 52 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys> | ||
| 53 | keyboard_keys; | ||
| 54 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods> | ||
| 55 | keyboard_mods; | ||
| 49 | }; | 56 | }; |
| 50 | } // namespace Service::HID | 57 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 4e246a57d..63391dbe9 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 8 | #include "core/frontend/emu_window.h" | ||
| 8 | #include "core/hle/service/hid/controllers/mouse.h" | 9 | #include "core/hle/service/hid/controllers/mouse.h" |
| 9 | 10 | ||
| 10 | namespace Service::HID { | 11 | namespace Service::HID { |
| @@ -14,7 +15,6 @@ Controller_Mouse::Controller_Mouse() = default; | |||
| 14 | Controller_Mouse::~Controller_Mouse() = default; | 15 | Controller_Mouse::~Controller_Mouse() = default; |
| 15 | 16 | ||
| 16 | void Controller_Mouse::OnInit() {} | 17 | void Controller_Mouse::OnInit() {} |
| 17 | |||
| 18 | void Controller_Mouse::OnRelease() {} | 18 | void Controller_Mouse::OnRelease() {} |
| 19 | 19 | ||
| 20 | void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { | 20 | void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { |
| @@ -34,10 +34,29 @@ void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { | |||
| 34 | 34 | ||
| 35 | cur_entry.sampling_number = last_entry.sampling_number + 1; | 35 | cur_entry.sampling_number = last_entry.sampling_number + 1; |
| 36 | cur_entry.sampling_number2 = cur_entry.sampling_number; | 36 | cur_entry.sampling_number2 = cur_entry.sampling_number; |
| 37 | // TODO(ogniK): Update mouse states | 37 | |
| 38 | if (Settings::values.mouse_enabled) { | ||
| 39 | const auto [px, py, sx, sy] = mouse_device->GetStatus(); | ||
| 40 | const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width); | ||
| 41 | const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height); | ||
| 42 | cur_entry.x = x; | ||
| 43 | cur_entry.y = y; | ||
| 44 | cur_entry.delta_x = x - last_entry.x; | ||
| 45 | cur_entry.delta_y = y - last_entry.y; | ||
| 46 | cur_entry.mouse_wheel_x = sx; | ||
| 47 | cur_entry.mouse_wheel_y = sy; | ||
| 48 | |||
| 49 | for (std::size_t i = 0; i < mouse_button_devices.size(); ++i) { | ||
| 50 | cur_entry.button |= (mouse_button_devices[i]->GetStatus() << i); | ||
| 51 | } | ||
| 52 | } | ||
| 38 | 53 | ||
| 39 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); | 54 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); |
| 40 | } | 55 | } |
| 41 | 56 | ||
| 42 | void Controller_Mouse::OnLoadInputDevices() {} | 57 | void Controller_Mouse::OnLoadInputDevices() { |
| 58 | mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device); | ||
| 59 | std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(), | ||
| 60 | mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>); | ||
| 61 | } | ||
| 43 | } // namespace Service::HID | 62 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 543b0b71f..70b654d07 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h | |||
| @@ -7,7 +7,9 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/swap.h" | 9 | #include "common/swap.h" |
| 10 | #include "core/frontend/input.h" | ||
| 10 | #include "core/hle/service/hid/controllers/controller_base.h" | 11 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 12 | #include "core/settings.h" | ||
| 11 | 13 | ||
| 12 | namespace Service::HID { | 14 | namespace Service::HID { |
| 13 | class Controller_Mouse final : public ControllerBase { | 15 | class Controller_Mouse final : public ControllerBase { |
| @@ -35,7 +37,8 @@ private: | |||
| 35 | s32_le y; | 37 | s32_le y; |
| 36 | s32_le delta_x; | 38 | s32_le delta_x; |
| 37 | s32_le delta_y; | 39 | s32_le delta_y; |
| 38 | s32_le mouse_wheel; | 40 | s32_le mouse_wheel_x; |
| 41 | s32_le mouse_wheel_y; | ||
| 39 | s32_le button; | 42 | s32_le button; |
| 40 | s32_le attribute; | 43 | s32_le attribute; |
| 41 | }; | 44 | }; |
| @@ -46,5 +49,9 @@ private: | |||
| 46 | std::array<MouseState, 17> mouse_states; | 49 | std::array<MouseState, 17> mouse_states; |
| 47 | }; | 50 | }; |
| 48 | SharedMemory shared_memory{}; | 51 | SharedMemory shared_memory{}; |
| 52 | |||
| 53 | std::unique_ptr<Input::MouseDevice> mouse_device; | ||
| 54 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> | ||
| 55 | mouse_button_devices; | ||
| 49 | }; | 56 | }; |
| 50 | } // namespace Service::HID | 57 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 205e4fd14..46604887c 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -17,22 +17,13 @@ | |||
| 17 | #include "core/settings.h" | 17 | #include "core/settings.h" |
| 18 | 18 | ||
| 19 | namespace Service::HID { | 19 | namespace Service::HID { |
| 20 | |||
| 21 | constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; | ||
| 22 | constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; | ||
| 23 | constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6; | ||
| 24 | constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E; | ||
| 25 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | 20 | constexpr s32 HID_JOYSTICK_MAX = 0x7fff; |
| 26 | constexpr s32 HID_JOYSTICK_MIN = -0x7fff; | 21 | constexpr s32 HID_JOYSTICK_MIN = -0x7fff; |
| 27 | constexpr std::size_t NPAD_OFFSET = 0x9A00; | 22 | constexpr std::size_t NPAD_OFFSET = 0x9A00; |
| 28 | constexpr u32 BATTERY_FULL = 2; | 23 | constexpr u32 BATTERY_FULL = 2; |
| 29 | constexpr u32 NPAD_HANDHELD = 32; | ||
| 30 | constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this? | ||
| 31 | constexpr u32 MAX_NPAD_ID = 7; | 24 | constexpr u32 MAX_NPAD_ID = 7; |
| 32 | constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER = | ||
| 33 | Controller_NPad::NPadControllerType::JoyDual; | ||
| 34 | constexpr std::array<u32, 10> npad_id_list{ | 25 | constexpr std::array<u32, 10> npad_id_list{ |
| 35 | 0, 1, 2, 3, 4, 5, 6, 7, 32, 16, | 26 | 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, |
| 36 | }; | 27 | }; |
| 37 | 28 | ||
| 38 | enum class JoystickId : std::size_t { | 29 | enum class JoystickId : std::size_t { |
| @@ -40,7 +31,23 @@ enum class JoystickId : std::size_t { | |||
| 40 | Joystick_Right, | 31 | Joystick_Right, |
| 41 | }; | 32 | }; |
| 42 | 33 | ||
| 43 | static std::size_t NPadIdToIndex(u32 npad_id) { | 34 | static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) { |
| 35 | switch (type) { | ||
| 36 | case Settings::ControllerType::ProController: | ||
| 37 | return Controller_NPad::NPadControllerType::ProController; | ||
| 38 | case Settings::ControllerType::DualJoycon: | ||
| 39 | return Controller_NPad::NPadControllerType::JoyDual; | ||
| 40 | case Settings::ControllerType::LeftJoycon: | ||
| 41 | return Controller_NPad::NPadControllerType::JoyLeft; | ||
| 42 | case Settings::ControllerType::RightJoycon: | ||
| 43 | return Controller_NPad::NPadControllerType::JoyRight; | ||
| 44 | default: | ||
| 45 | UNREACHABLE(); | ||
| 46 | return Controller_NPad::NPadControllerType::JoyDual; | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) { | ||
| 44 | switch (npad_id) { | 51 | switch (npad_id) { |
| 45 | case 0: | 52 | case 0: |
| 46 | case 1: | 53 | case 1: |
| @@ -63,6 +70,27 @@ static std::size_t NPadIdToIndex(u32 npad_id) { | |||
| 63 | } | 70 | } |
| 64 | } | 71 | } |
| 65 | 72 | ||
| 73 | u32 Controller_NPad::IndexToNPad(std::size_t index) { | ||
| 74 | switch (index) { | ||
| 75 | case 0: | ||
| 76 | case 1: | ||
| 77 | case 2: | ||
| 78 | case 3: | ||
| 79 | case 4: | ||
| 80 | case 5: | ||
| 81 | case 6: | ||
| 82 | case 7: | ||
| 83 | return static_cast<u32>(index); | ||
| 84 | case 8: | ||
| 85 | return NPAD_HANDHELD; | ||
| 86 | case 9: | ||
| 87 | return NPAD_UNKNOWN; | ||
| 88 | default: | ||
| 89 | UNIMPLEMENTED_MSG("Unknown npad index {}", index); | ||
| 90 | return 0; | ||
| 91 | }; | ||
| 92 | } | ||
| 93 | |||
| 66 | Controller_NPad::Controller_NPad() = default; | 94 | Controller_NPad::Controller_NPad() = default; |
| 67 | Controller_NPad::~Controller_NPad() = default; | 95 | Controller_NPad::~Controller_NPad() = default; |
| 68 | 96 | ||
| @@ -79,22 +107,32 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { | |||
| 79 | controller.joy_styles.handheld.Assign(1); | 107 | controller.joy_styles.handheld.Assign(1); |
| 80 | controller.device_type.handheld.Assign(1); | 108 | controller.device_type.handheld.Assign(1); |
| 81 | controller.pad_assignment = NPadAssignments::Dual; | 109 | controller.pad_assignment = NPadAssignments::Dual; |
| 110 | controller.properties.is_vertical.Assign(1); | ||
| 111 | controller.properties.use_plus.Assign(1); | ||
| 112 | controller.properties.use_minus.Assign(1); | ||
| 82 | break; | 113 | break; |
| 83 | case NPadControllerType::JoyDual: | 114 | case NPadControllerType::JoyDual: |
| 84 | controller.joy_styles.joycon_dual.Assign(1); | 115 | controller.joy_styles.joycon_dual.Assign(1); |
| 85 | controller.device_type.joycon_left.Assign(1); | 116 | controller.device_type.joycon_left.Assign(1); |
| 86 | controller.device_type.joycon_right.Assign(1); | 117 | controller.device_type.joycon_right.Assign(1); |
| 118 | controller.properties.is_vertical.Assign(1); | ||
| 119 | controller.properties.use_plus.Assign(1); | ||
| 120 | controller.properties.use_minus.Assign(1); | ||
| 87 | controller.pad_assignment = NPadAssignments::Dual; | 121 | controller.pad_assignment = NPadAssignments::Dual; |
| 88 | break; | 122 | break; |
| 89 | case NPadControllerType::JoyLeft: | 123 | case NPadControllerType::JoyLeft: |
| 90 | controller.joy_styles.joycon_left.Assign(1); | 124 | controller.joy_styles.joycon_left.Assign(1); |
| 91 | controller.device_type.joycon_left.Assign(1); | 125 | controller.device_type.joycon_left.Assign(1); |
| 92 | controller.pad_assignment = NPadAssignments::Dual; | 126 | controller.properties.is_horizontal.Assign(1); |
| 127 | controller.properties.use_minus.Assign(1); | ||
| 128 | controller.pad_assignment = NPadAssignments::Single; | ||
| 93 | break; | 129 | break; |
| 94 | case NPadControllerType::JoyRight: | 130 | case NPadControllerType::JoyRight: |
| 95 | controller.joy_styles.joycon_right.Assign(1); | 131 | controller.joy_styles.joycon_right.Assign(1); |
| 96 | controller.device_type.joycon_right.Assign(1); | 132 | controller.device_type.joycon_right.Assign(1); |
| 97 | controller.pad_assignment = NPadAssignments::Dual; | 133 | controller.properties.is_horizontal.Assign(1); |
| 134 | controller.properties.use_plus.Assign(1); | ||
| 135 | controller.pad_assignment = NPadAssignments::Single; | ||
| 98 | break; | 136 | break; |
| 99 | case NPadControllerType::Pokeball: | 137 | case NPadControllerType::Pokeball: |
| 100 | controller.joy_styles.pokeball.Assign(1); | 138 | controller.joy_styles.pokeball.Assign(1); |
| @@ -104,6 +142,9 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { | |||
| 104 | case NPadControllerType::ProController: | 142 | case NPadControllerType::ProController: |
| 105 | controller.joy_styles.pro_controller.Assign(1); | 143 | controller.joy_styles.pro_controller.Assign(1); |
| 106 | controller.device_type.pro_controller.Assign(1); | 144 | controller.device_type.pro_controller.Assign(1); |
| 145 | controller.properties.is_vertical.Assign(1); | ||
| 146 | controller.properties.use_plus.Assign(1); | ||
| 147 | controller.properties.use_minus.Assign(1); | ||
| 107 | controller.pad_assignment = NPadAssignments::Single; | 148 | controller.pad_assignment = NPadAssignments::Single; |
| 108 | break; | 149 | break; |
| 109 | } | 150 | } |
| @@ -113,14 +154,12 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { | |||
| 113 | controller.single_color.button_color = 0; | 154 | controller.single_color.button_color = 0; |
| 114 | 155 | ||
| 115 | controller.dual_color_error = ColorReadError::ReadOk; | 156 | controller.dual_color_error = ColorReadError::ReadOk; |
| 116 | controller.left_color.body_color = JOYCON_BODY_NEON_BLUE; | 157 | controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left; |
| 117 | controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE; | 158 | controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left; |
| 118 | controller.right_color.body_color = JOYCON_BODY_NEON_RED; | 159 | controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right; |
| 119 | controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED; | 160 | controller.right_color.button_color = |
| 120 | 161 | Settings::values.players[controller_idx].button_color_right; | |
| 121 | controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations | 162 | |
| 122 | controller.properties.use_plus.Assign(1); | ||
| 123 | controller.properties.use_minus.Assign(1); | ||
| 124 | controller.battery_level[0] = BATTERY_FULL; | 163 | controller.battery_level[0] = BATTERY_FULL; |
| 125 | controller.battery_level[1] = BATTERY_FULL; | 164 | controller.battery_level[1] = BATTERY_FULL; |
| 126 | controller.battery_level[2] = BATTERY_FULL; | 165 | controller.battery_level[2] = BATTERY_FULL; |
| @@ -144,26 +183,109 @@ void Controller_NPad::OnInit() { | |||
| 144 | style.pro_controller.Assign(1); | 183 | style.pro_controller.Assign(1); |
| 145 | style.pokeball.Assign(1); | 184 | style.pokeball.Assign(1); |
| 146 | } | 185 | } |
| 186 | |||
| 187 | std::transform( | ||
| 188 | Settings::values.players.begin(), Settings::values.players.end(), | ||
| 189 | connected_controllers.begin(), [](const Settings::PlayerInput& player) { | ||
| 190 | return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected}; | ||
| 191 | }); | ||
| 192 | |||
| 193 | std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8, | ||
| 194 | [](const ControllerHolder& holder) { return holder.is_connected; }); | ||
| 195 | |||
| 196 | // Account for handheld | ||
| 197 | if (connected_controllers[8].is_connected) | ||
| 198 | connected_controllers[8].type = NPadControllerType::Handheld; | ||
| 199 | |||
| 200 | supported_npad_id_types.resize(npad_id_list.size()); | ||
| 201 | std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), | ||
| 202 | npad_id_list.size() * sizeof(u32)); | ||
| 203 | |||
| 204 | // Add a default dual joycon controller if none are present. | ||
| 147 | if (std::none_of(connected_controllers.begin(), connected_controllers.end(), | 205 | if (std::none_of(connected_controllers.begin(), connected_controllers.end(), |
| 148 | [](const ControllerHolder& controller) { return controller.is_connected; })) { | 206 | [](const ControllerHolder& controller) { return controller.is_connected; })) { |
| 149 | supported_npad_id_types.resize(npad_id_list.size()); | 207 | supported_npad_id_types.resize(npad_id_list.size()); |
| 150 | std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), | 208 | std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), |
| 151 | npad_id_list.size() * sizeof(u32)); | 209 | npad_id_list.size() * sizeof(u32)); |
| 152 | AddNewController(PREFERRED_CONTROLLER); | 210 | AddNewController(NPadControllerType::JoyDual); |
| 211 | } | ||
| 212 | |||
| 213 | for (std::size_t i = 0; i < connected_controllers.size(); ++i) { | ||
| 214 | const auto& controller = connected_controllers[i]; | ||
| 215 | if (controller.is_connected) { | ||
| 216 | AddNewControllerAt(controller.type, IndexToNPad(i)); | ||
| 217 | } | ||
| 153 | } | 218 | } |
| 154 | } | 219 | } |
| 155 | 220 | ||
| 156 | void Controller_NPad::OnLoadInputDevices() { | 221 | void Controller_NPad::OnLoadInputDevices() { |
| 157 | std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, | 222 | const auto& players = Settings::values.players; |
| 158 | Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, | 223 | for (std::size_t i = 0; i < players.size(); ++i) { |
| 159 | buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); | 224 | std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, |
| 160 | std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, | 225 | players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END, |
| 161 | Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END, | 226 | buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>); |
| 162 | sticks.begin(), Input::CreateDevice<Input::AnalogDevice>); | 227 | std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, |
| 228 | players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, | ||
| 229 | sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); | ||
| 230 | } | ||
| 163 | } | 231 | } |
| 164 | 232 | ||
| 165 | void Controller_NPad::OnRelease() {} | 233 | void Controller_NPad::OnRelease() {} |
| 166 | 234 | ||
| 235 | void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { | ||
| 236 | const auto controller_idx = NPadIdToIndex(npad_id); | ||
| 237 | const auto controller_type = connected_controllers[controller_idx].type; | ||
| 238 | if (!connected_controllers[controller_idx].is_connected) { | ||
| 239 | return; | ||
| 240 | } | ||
| 241 | auto& pad_state = npad_pad_states[controller_idx].pad_states; | ||
| 242 | auto& lstick_entry = npad_pad_states[controller_idx].l_stick; | ||
| 243 | auto& rstick_entry = npad_pad_states[controller_idx].r_stick; | ||
| 244 | const auto& button_state = buttons[controller_idx]; | ||
| 245 | const auto& analog_state = sticks[controller_idx]; | ||
| 246 | |||
| 247 | using namespace Settings::NativeButton; | ||
| 248 | pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 249 | pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 250 | pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 251 | pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 252 | pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 253 | pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 254 | pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 255 | pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 256 | pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 257 | pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 258 | pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 259 | pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 260 | |||
| 261 | pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 262 | pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 263 | pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 264 | pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 265 | |||
| 266 | pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 267 | pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 268 | pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 269 | pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 270 | |||
| 271 | pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 272 | pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 273 | pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 274 | pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 275 | |||
| 276 | pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 277 | pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 278 | |||
| 279 | const auto [stick_l_x_f, stick_l_y_f] = | ||
| 280 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | ||
| 281 | const auto [stick_r_x_f, stick_r_y_f] = | ||
| 282 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | ||
| 283 | lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | ||
| 284 | lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | ||
| 285 | rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | ||
| 286 | rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); | ||
| 287 | } | ||
| 288 | |||
| 167 | void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { | 289 | void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { |
| 168 | if (!IsControllerActivated()) | 290 | if (!IsControllerActivated()) |
| 169 | return; | 291 | return; |
| @@ -199,97 +321,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { | |||
| 199 | if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { | 321 | if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { |
| 200 | continue; | 322 | continue; |
| 201 | } | 323 | } |
| 202 | 324 | const u32 npad_index = static_cast<u32>(i); | |
| 203 | // Pad states | 325 | RequestPadStateUpdate(npad_index); |
| 204 | ControllerPadState pad_state{}; | 326 | auto& pad_state = npad_pad_states[npad_index]; |
| 205 | using namespace Settings::NativeButton; | ||
| 206 | pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 207 | pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 208 | pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 209 | pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 210 | pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 211 | pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 212 | pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 213 | pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 214 | pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 215 | pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 216 | pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 217 | pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 218 | |||
| 219 | pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 220 | pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 221 | pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 222 | pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 223 | |||
| 224 | pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 225 | pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 226 | pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 227 | pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 228 | |||
| 229 | pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 230 | pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 231 | pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 232 | pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 233 | |||
| 234 | pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 235 | pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 236 | |||
| 237 | AnalogPosition lstick_entry{}; | ||
| 238 | AnalogPosition rstick_entry{}; | ||
| 239 | |||
| 240 | const auto [stick_l_x_f, stick_l_y_f] = | ||
| 241 | sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | ||
| 242 | const auto [stick_r_x_f, stick_r_y_f] = | ||
| 243 | sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | ||
| 244 | lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | ||
| 245 | lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | ||
| 246 | rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | ||
| 247 | rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); | ||
| 248 | |||
| 249 | if (controller_type == NPadControllerType::JoyLeft || | ||
| 250 | controller_type == NPadControllerType::JoyRight) { | ||
| 251 | if (npad.properties.is_horizontal) { | ||
| 252 | ControllerPadState state{}; | ||
| 253 | AnalogPosition temp_lstick_entry{}; | ||
| 254 | AnalogPosition temp_rstick_entry{}; | ||
| 255 | if (controller_type == NPadControllerType::JoyLeft) { | ||
| 256 | state.d_down.Assign(pad_state.d_left.Value()); | ||
| 257 | state.d_left.Assign(pad_state.d_up.Value()); | ||
| 258 | state.d_right.Assign(pad_state.d_down.Value()); | ||
| 259 | state.d_up.Assign(pad_state.d_right.Value()); | ||
| 260 | state.l.Assign(pad_state.l.Value() | pad_state.sl.Value()); | ||
| 261 | state.r.Assign(pad_state.r.Value() | pad_state.sr.Value()); | ||
| 262 | |||
| 263 | state.zl.Assign(pad_state.zl.Value()); | ||
| 264 | state.plus.Assign(pad_state.minus.Value()); | ||
| 265 | |||
| 266 | temp_lstick_entry = lstick_entry; | ||
| 267 | temp_rstick_entry = rstick_entry; | ||
| 268 | std::swap(temp_lstick_entry.x, temp_lstick_entry.y); | ||
| 269 | std::swap(temp_rstick_entry.x, temp_rstick_entry.y); | ||
| 270 | temp_lstick_entry.y *= -1; | ||
| 271 | } else if (controller_type == NPadControllerType::JoyRight) { | ||
| 272 | state.x.Assign(pad_state.a.Value()); | ||
| 273 | state.a.Assign(pad_state.b.Value()); | ||
| 274 | state.b.Assign(pad_state.y.Value()); | ||
| 275 | state.y.Assign(pad_state.b.Value()); | ||
| 276 | |||
| 277 | state.l.Assign(pad_state.l.Value() | pad_state.sl.Value()); | ||
| 278 | state.r.Assign(pad_state.r.Value() | pad_state.sr.Value()); | ||
| 279 | state.zr.Assign(pad_state.zr.Value()); | ||
| 280 | state.plus.Assign(pad_state.plus.Value()); | ||
| 281 | |||
| 282 | temp_lstick_entry = lstick_entry; | ||
| 283 | temp_rstick_entry = rstick_entry; | ||
| 284 | std::swap(temp_lstick_entry.x, temp_lstick_entry.y); | ||
| 285 | std::swap(temp_rstick_entry.x, temp_rstick_entry.y); | ||
| 286 | temp_rstick_entry.x *= -1; | ||
| 287 | } | ||
| 288 | pad_state.raw = state.raw; | ||
| 289 | lstick_entry = temp_lstick_entry; | ||
| 290 | rstick_entry = temp_rstick_entry; | ||
| 291 | } | ||
| 292 | } | ||
| 293 | 327 | ||
| 294 | auto& main_controller = | 328 | auto& main_controller = |
| 295 | npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index]; | 329 | npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index]; |
| @@ -304,8 +338,51 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { | |||
| 304 | auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; | 338 | auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; |
| 305 | 339 | ||
| 306 | if (hold_type == NpadHoldType::Horizontal) { | 340 | if (hold_type == NpadHoldType::Horizontal) { |
| 307 | // TODO(ogniK): Remap buttons for different orientations | 341 | ControllerPadState state{}; |
| 342 | AnalogPosition temp_lstick_entry{}; | ||
| 343 | AnalogPosition temp_rstick_entry{}; | ||
| 344 | if (controller_type == NPadControllerType::JoyLeft) { | ||
| 345 | state.d_down.Assign(pad_state.pad_states.d_left.Value()); | ||
| 346 | state.d_left.Assign(pad_state.pad_states.d_up.Value()); | ||
| 347 | state.d_right.Assign(pad_state.pad_states.d_down.Value()); | ||
| 348 | state.d_up.Assign(pad_state.pad_states.d_right.Value()); | ||
| 349 | state.l.Assign(pad_state.pad_states.l.Value() | | ||
| 350 | pad_state.pad_states.left_sl.Value()); | ||
| 351 | state.r.Assign(pad_state.pad_states.r.Value() | | ||
| 352 | pad_state.pad_states.left_sr.Value()); | ||
| 353 | |||
| 354 | state.zl.Assign(pad_state.pad_states.zl.Value()); | ||
| 355 | state.plus.Assign(pad_state.pad_states.minus.Value()); | ||
| 356 | |||
| 357 | temp_lstick_entry = pad_state.l_stick; | ||
| 358 | temp_rstick_entry = pad_state.r_stick; | ||
| 359 | std::swap(temp_lstick_entry.x, temp_lstick_entry.y); | ||
| 360 | std::swap(temp_rstick_entry.x, temp_rstick_entry.y); | ||
| 361 | temp_lstick_entry.y *= -1; | ||
| 362 | } else if (controller_type == NPadControllerType::JoyRight) { | ||
| 363 | state.x.Assign(pad_state.pad_states.a.Value()); | ||
| 364 | state.a.Assign(pad_state.pad_states.b.Value()); | ||
| 365 | state.b.Assign(pad_state.pad_states.y.Value()); | ||
| 366 | state.y.Assign(pad_state.pad_states.b.Value()); | ||
| 367 | |||
| 368 | state.l.Assign(pad_state.pad_states.l.Value() | | ||
| 369 | pad_state.pad_states.right_sl.Value()); | ||
| 370 | state.r.Assign(pad_state.pad_states.r.Value() | | ||
| 371 | pad_state.pad_states.right_sr.Value()); | ||
| 372 | state.zr.Assign(pad_state.pad_states.zr.Value()); | ||
| 373 | state.plus.Assign(pad_state.pad_states.plus.Value()); | ||
| 374 | |||
| 375 | temp_lstick_entry = pad_state.l_stick; | ||
| 376 | temp_rstick_entry = pad_state.r_stick; | ||
| 377 | std::swap(temp_lstick_entry.x, temp_lstick_entry.y); | ||
| 378 | std::swap(temp_rstick_entry.x, temp_rstick_entry.y); | ||
| 379 | temp_rstick_entry.x *= -1; | ||
| 380 | } | ||
| 381 | pad_state.pad_states.raw = state.raw; | ||
| 382 | pad_state.l_stick = temp_lstick_entry; | ||
| 383 | pad_state.r_stick = temp_rstick_entry; | ||
| 308 | } | 384 | } |
| 385 | |||
| 309 | libnx_entry.connection_status.raw = 0; | 386 | libnx_entry.connection_status.raw = 0; |
| 310 | 387 | ||
| 311 | switch (controller_type) { | 388 | switch (controller_type) { |
| @@ -316,9 +393,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { | |||
| 316 | handheld_entry.connection_status.IsRightJoyConnected.Assign(1); | 393 | handheld_entry.connection_status.IsRightJoyConnected.Assign(1); |
| 317 | handheld_entry.connection_status.IsLeftJoyWired.Assign(1); | 394 | handheld_entry.connection_status.IsLeftJoyWired.Assign(1); |
| 318 | handheld_entry.connection_status.IsRightJoyWired.Assign(1); | 395 | handheld_entry.connection_status.IsRightJoyWired.Assign(1); |
| 319 | handheld_entry.pad_states.raw = pad_state.raw; | 396 | handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; |
| 320 | handheld_entry.l_stick = lstick_entry; | 397 | handheld_entry.pad.l_stick = pad_state.l_stick; |
| 321 | handheld_entry.r_stick = rstick_entry; | 398 | handheld_entry.pad.r_stick = pad_state.r_stick; |
| 322 | break; | 399 | break; |
| 323 | case NPadControllerType::JoyDual: | 400 | case NPadControllerType::JoyDual: |
| 324 | dual_entry.connection_status.raw = 0; | 401 | dual_entry.connection_status.raw = 0; |
| @@ -331,25 +408,25 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { | |||
| 331 | libnx_entry.connection_status.IsRightJoyConnected.Assign(1); | 408 | libnx_entry.connection_status.IsRightJoyConnected.Assign(1); |
| 332 | libnx_entry.connection_status.IsConnected.Assign(1); | 409 | libnx_entry.connection_status.IsConnected.Assign(1); |
| 333 | 410 | ||
| 334 | dual_entry.pad_states.raw = pad_state.raw; | 411 | dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; |
| 335 | dual_entry.l_stick = lstick_entry; | 412 | dual_entry.pad.l_stick = pad_state.l_stick; |
| 336 | dual_entry.r_stick = rstick_entry; | 413 | dual_entry.pad.r_stick = pad_state.r_stick; |
| 337 | break; | 414 | break; |
| 338 | case NPadControllerType::JoyLeft: | 415 | case NPadControllerType::JoyLeft: |
| 339 | left_entry.connection_status.raw = 0; | 416 | left_entry.connection_status.raw = 0; |
| 340 | 417 | ||
| 341 | left_entry.connection_status.IsConnected.Assign(1); | 418 | left_entry.connection_status.IsConnected.Assign(1); |
| 342 | left_entry.pad_states.raw = pad_state.raw; | 419 | left_entry.pad.pad_states.raw = pad_state.pad_states.raw; |
| 343 | left_entry.l_stick = lstick_entry; | 420 | left_entry.pad.l_stick = pad_state.l_stick; |
| 344 | left_entry.r_stick = rstick_entry; | 421 | left_entry.pad.r_stick = pad_state.r_stick; |
| 345 | break; | 422 | break; |
| 346 | case NPadControllerType::JoyRight: | 423 | case NPadControllerType::JoyRight: |
| 347 | right_entry.connection_status.raw = 0; | 424 | right_entry.connection_status.raw = 0; |
| 348 | 425 | ||
| 349 | right_entry.connection_status.IsConnected.Assign(1); | 426 | right_entry.connection_status.IsConnected.Assign(1); |
| 350 | right_entry.pad_states.raw = pad_state.raw; | 427 | right_entry.pad.pad_states.raw = pad_state.pad_states.raw; |
| 351 | right_entry.l_stick = lstick_entry; | 428 | right_entry.pad.l_stick = pad_state.l_stick; |
| 352 | right_entry.r_stick = rstick_entry; | 429 | right_entry.pad.r_stick = pad_state.r_stick; |
| 353 | break; | 430 | break; |
| 354 | case NPadControllerType::Pokeball: | 431 | case NPadControllerType::Pokeball: |
| 355 | pokeball_entry.connection_status.raw = 0; | 432 | pokeball_entry.connection_status.raw = 0; |
| @@ -357,30 +434,30 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { | |||
| 357 | pokeball_entry.connection_status.IsConnected.Assign(1); | 434 | pokeball_entry.connection_status.IsConnected.Assign(1); |
| 358 | pokeball_entry.connection_status.IsWired.Assign(1); | 435 | pokeball_entry.connection_status.IsWired.Assign(1); |
| 359 | 436 | ||
| 360 | pokeball_entry.pad_states.raw = pad_state.raw; | 437 | pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; |
| 361 | pokeball_entry.l_stick = lstick_entry; | 438 | pokeball_entry.pad.l_stick = pad_state.l_stick; |
| 362 | pokeball_entry.r_stick = rstick_entry; | 439 | pokeball_entry.pad.r_stick = pad_state.r_stick; |
| 363 | break; | 440 | break; |
| 364 | case NPadControllerType::ProController: | 441 | case NPadControllerType::ProController: |
| 365 | main_controller.connection_status.raw = 0; | 442 | main_controller.connection_status.raw = 0; |
| 366 | 443 | ||
| 367 | main_controller.connection_status.IsConnected.Assign(1); | 444 | main_controller.connection_status.IsConnected.Assign(1); |
| 368 | main_controller.connection_status.IsWired.Assign(1); | 445 | main_controller.connection_status.IsWired.Assign(1); |
| 369 | main_controller.pad_states.raw = pad_state.raw; | 446 | main_controller.pad.pad_states.raw = pad_state.pad_states.raw; |
| 370 | main_controller.l_stick = lstick_entry; | 447 | main_controller.pad.l_stick = pad_state.l_stick; |
| 371 | main_controller.r_stick = rstick_entry; | 448 | main_controller.pad.r_stick = pad_state.r_stick; |
| 372 | break; | 449 | break; |
| 373 | } | 450 | } |
| 374 | 451 | ||
| 375 | // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate | 452 | // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate |
| 376 | // any controllers. | 453 | // any controllers. |
| 377 | libnx_entry.pad_states.raw = pad_state.raw; | 454 | libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw; |
| 378 | libnx_entry.l_stick = lstick_entry; | 455 | libnx_entry.pad.l_stick = pad_state.l_stick; |
| 379 | libnx_entry.r_stick = rstick_entry; | 456 | libnx_entry.pad.r_stick = pad_state.r_stick; |
| 380 | } | 457 | } |
| 381 | std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(), | 458 | std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(), |
| 382 | shared_memory_entries.size() * sizeof(NPadEntry)); | 459 | shared_memory_entries.size() * sizeof(NPadEntry)); |
| 383 | } // namespace Service::HID | 460 | } |
| 384 | 461 | ||
| 385 | void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { | 462 | void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { |
| 386 | style.raw = style_set.raw; | 463 | style.raw = style_set.raw; |
| @@ -401,23 +478,24 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { | |||
| 401 | if (!controller.is_connected) { | 478 | if (!controller.is_connected) { |
| 402 | continue; | 479 | continue; |
| 403 | } | 480 | } |
| 404 | if (!IsControllerSupported(PREFERRED_CONTROLLER)) { | 481 | const auto requested_controller = |
| 405 | const auto best_type = DecideBestController(PREFERRED_CONTROLLER); | 482 | i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type) |
| 406 | const bool is_handheld = (best_type == NPadControllerType::Handheld || | 483 | : NPadControllerType::Handheld; |
| 407 | PREFERRED_CONTROLLER == NPadControllerType::Handheld); | 484 | if (!IsControllerSupported(requested_controller)) { |
| 485 | const auto is_handheld = requested_controller == NPadControllerType::Handheld; | ||
| 408 | if (is_handheld) { | 486 | if (is_handheld) { |
| 409 | controller.type = NPadControllerType::None; | 487 | controller.type = NPadControllerType::None; |
| 410 | controller.is_connected = false; | 488 | controller.is_connected = false; |
| 411 | AddNewController(best_type); | 489 | AddNewController(requested_controller); |
| 412 | } else { | 490 | } else { |
| 413 | controller.type = best_type; | 491 | controller.type = requested_controller; |
| 414 | InitNewlyAddedControler(i); | 492 | InitNewlyAddedControler(i); |
| 415 | } | 493 | } |
| 416 | had_controller_update = true; | 494 | had_controller_update = true; |
| 417 | } | 495 | } |
| 418 | } | 496 | if (had_controller_update) { |
| 419 | if (had_controller_update) { | 497 | styleset_changed_event->Signal(); |
| 420 | styleset_changed_event->Signal(); | 498 | } |
| 421 | } | 499 | } |
| 422 | } | 500 | } |
| 423 | 501 | ||
| @@ -450,15 +528,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, | |||
| 450 | return; | 528 | return; |
| 451 | } | 529 | } |
| 452 | for (std::size_t i = 0; i < controller_ids.size(); i++) { | 530 | for (std::size_t i = 0; i < controller_ids.size(); i++) { |
| 453 | std::size_t controller_pos = i; | 531 | std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i)); |
| 454 | // Handheld controller conversion | ||
| 455 | if (controller_pos == NPAD_HANDHELD) { | ||
| 456 | controller_pos = 8; | ||
| 457 | } | ||
| 458 | // Unknown controller conversion | ||
| 459 | if (controller_pos == NPAD_UNKNOWN) { | ||
| 460 | controller_pos = 9; | ||
| 461 | } | ||
| 462 | if (connected_controllers[controller_pos].is_connected) { | 532 | if (connected_controllers[controller_pos].is_connected) { |
| 463 | // TODO(ogniK): Vibrate the physical controller | 533 | // TODO(ogniK): Vibrate the physical controller |
| 464 | } | 534 | } |
| @@ -477,7 +547,9 @@ Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() cons | |||
| 477 | Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { | 547 | Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { |
| 478 | return last_processed_vibration; | 548 | return last_processed_vibration; |
| 479 | } | 549 | } |
| 550 | |||
| 480 | void Controller_NPad::AddNewController(NPadControllerType controller) { | 551 | void Controller_NPad::AddNewController(NPadControllerType controller) { |
| 552 | controller = DecideBestController(controller); | ||
| 481 | if (controller == NPadControllerType::Handheld) { | 553 | if (controller == NPadControllerType::Handheld) { |
| 482 | connected_controllers[8] = {controller, true}; | 554 | connected_controllers[8] = {controller, true}; |
| 483 | InitNewlyAddedControler(8); | 555 | InitNewlyAddedControler(8); |
| @@ -495,6 +567,18 @@ void Controller_NPad::AddNewController(NPadControllerType controller) { | |||
| 495 | InitNewlyAddedControler(controller_id); | 567 | InitNewlyAddedControler(controller_id); |
| 496 | } | 568 | } |
| 497 | 569 | ||
| 570 | void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) { | ||
| 571 | controller = DecideBestController(controller); | ||
| 572 | if (controller == NPadControllerType::Handheld) { | ||
| 573 | connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true}; | ||
| 574 | InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD)); | ||
| 575 | return; | ||
| 576 | } | ||
| 577 | |||
| 578 | connected_controllers[npad_id] = {controller, true}; | ||
| 579 | InitNewlyAddedControler(npad_id); | ||
| 580 | } | ||
| 581 | |||
| 498 | void Controller_NPad::ConnectNPad(u32 npad_id) { | 582 | void Controller_NPad::ConnectNPad(u32 npad_id) { |
| 499 | connected_controllers[NPadIdToIndex(npad_id)].is_connected = true; | 583 | connected_controllers[NPadIdToIndex(npad_id)].is_connected = true; |
| 500 | } | 584 | } |
| @@ -503,6 +587,36 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) { | |||
| 503 | connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; | 587 | connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; |
| 504 | } | 588 | } |
| 505 | 589 | ||
| 590 | bool Controller_NPad::IsControllerSupported(NPadControllerType controller) { | ||
| 591 | if (controller == NPadControllerType::Handheld) { | ||
| 592 | // Handheld is not even a supported type, lets stop here | ||
| 593 | if (std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), | ||
| 594 | NPAD_HANDHELD) == supported_npad_id_types.end()) { | ||
| 595 | return false; | ||
| 596 | } | ||
| 597 | // Handheld should not be supported in docked mode | ||
| 598 | if (Settings::values.use_docked_mode) { | ||
| 599 | return false; | ||
| 600 | } | ||
| 601 | } | ||
| 602 | switch (controller) { | ||
| 603 | case NPadControllerType::ProController: | ||
| 604 | return style.pro_controller; | ||
| 605 | case NPadControllerType::Handheld: | ||
| 606 | return style.handheld; | ||
| 607 | case NPadControllerType::JoyDual: | ||
| 608 | return style.joycon_dual; | ||
| 609 | case NPadControllerType::JoyLeft: | ||
| 610 | return style.joycon_left; | ||
| 611 | case NPadControllerType::JoyRight: | ||
| 612 | return style.joycon_right; | ||
| 613 | case NPadControllerType::Pokeball: | ||
| 614 | return style.pokeball; | ||
| 615 | default: | ||
| 616 | return false; | ||
| 617 | } | ||
| 618 | } | ||
| 619 | |||
| 506 | Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { | 620 | Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { |
| 507 | if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) { | 621 | if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) { |
| 508 | // These are controllers without led patterns | 622 | // These are controllers without led patterns |
| @@ -534,6 +648,36 @@ void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { | |||
| 534 | can_controllers_vibrate = can_vibrate; | 648 | can_controllers_vibrate = can_vibrate; |
| 535 | } | 649 | } |
| 536 | 650 | ||
| 651 | void Controller_NPad::ClearAllConnectedControllers() { | ||
| 652 | for (auto& controller : connected_controllers) { | ||
| 653 | if (controller.is_connected && controller.type != NPadControllerType::None) { | ||
| 654 | controller.type = NPadControllerType::None; | ||
| 655 | controller.is_connected = false; | ||
| 656 | } | ||
| 657 | } | ||
| 658 | } | ||
| 659 | void Controller_NPad::DisconnectAllConnectedControllers() { | ||
| 660 | std::for_each(connected_controllers.begin(), connected_controllers.end(), | ||
| 661 | [](ControllerHolder& controller) { controller.is_connected = false; }); | ||
| 662 | } | ||
| 663 | |||
| 664 | void Controller_NPad::ConnectAllDisconnectedControllers() { | ||
| 665 | std::for_each(connected_controllers.begin(), connected_controllers.end(), | ||
| 666 | [](ControllerHolder& controller) { | ||
| 667 | if (controller.type != NPadControllerType::None && !controller.is_connected) { | ||
| 668 | controller.is_connected = false; | ||
| 669 | } | ||
| 670 | }); | ||
| 671 | } | ||
| 672 | |||
| 673 | void Controller_NPad::ClearAllControllers() { | ||
| 674 | std::for_each(connected_controllers.begin(), connected_controllers.end(), | ||
| 675 | [](ControllerHolder& controller) { | ||
| 676 | controller.type = NPadControllerType::None; | ||
| 677 | controller.is_connected = false; | ||
| 678 | }); | ||
| 679 | } | ||
| 680 | |||
| 537 | bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { | 681 | bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { |
| 538 | const bool support_handheld = | 682 | const bool support_handheld = |
| 539 | std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) != | 683 | std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) != |
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index ac86985ff..ea8057b80 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h | |||
| @@ -5,13 +5,18 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include "common/bit_field.h" | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "core/frontend/input.h" | 10 | #include "core/frontend/input.h" |
| 11 | #include "core/hle/kernel/event.h" | ||
| 10 | #include "core/hle/service/hid/controllers/controller_base.h" | 12 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 11 | #include "core/settings.h" | 13 | #include "core/settings.h" |
| 12 | 14 | ||
| 13 | namespace Service::HID { | 15 | namespace Service::HID { |
| 14 | 16 | ||
| 17 | constexpr u32 NPAD_HANDHELD = 32; | ||
| 18 | constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this? | ||
| 19 | |||
| 15 | class Controller_NPad final : public ControllerBase { | 20 | class Controller_NPad final : public ControllerBase { |
| 16 | public: | 21 | public: |
| 17 | Controller_NPad(); | 22 | Controller_NPad(); |
| @@ -107,11 +112,19 @@ public: | |||
| 107 | Vibration GetLastVibration() const; | 112 | Vibration GetLastVibration() const; |
| 108 | 113 | ||
| 109 | void AddNewController(NPadControllerType controller); | 114 | void AddNewController(NPadControllerType controller); |
| 115 | void AddNewControllerAt(NPadControllerType controller, u32 npad_id); | ||
| 110 | 116 | ||
| 111 | void ConnectNPad(u32 npad_id); | 117 | void ConnectNPad(u32 npad_id); |
| 112 | void DisconnectNPad(u32 npad_id); | 118 | void DisconnectNPad(u32 npad_id); |
| 113 | LedPattern GetLedPattern(u32 npad_id); | 119 | LedPattern GetLedPattern(u32 npad_id); |
| 114 | void SetVibrationEnabled(bool can_vibrate); | 120 | void SetVibrationEnabled(bool can_vibrate); |
| 121 | void ClearAllConnectedControllers(); | ||
| 122 | void DisconnectAllConnectedControllers(); | ||
| 123 | void ConnectAllDisconnectedControllers(); | ||
| 124 | void ClearAllControllers(); | ||
| 125 | |||
| 126 | static std::size_t NPadIdToIndex(u32 npad_id); | ||
| 127 | static u32 IndexToNPad(std::size_t index); | ||
| 115 | 128 | ||
| 116 | private: | 129 | private: |
| 117 | struct CommonHeader { | 130 | struct CommonHeader { |
| @@ -164,8 +177,11 @@ private: | |||
| 164 | BitField<23, 1, u64_le> r_stick_down; | 177 | BitField<23, 1, u64_le> r_stick_down; |
| 165 | 178 | ||
| 166 | // Not always active? | 179 | // Not always active? |
| 167 | BitField<24, 1, u64_le> sl; | 180 | BitField<24, 1, u64_le> left_sl; |
| 168 | BitField<25, 1, u64_le> sr; | 181 | BitField<25, 1, u64_le> left_sr; |
| 182 | |||
| 183 | BitField<26, 1, u64_le> right_sl; | ||
| 184 | BitField<27, 1, u64_le> right_sr; | ||
| 169 | }; | 185 | }; |
| 170 | }; | 186 | }; |
| 171 | static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size"); | 187 | static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size"); |
| @@ -189,12 +205,17 @@ private: | |||
| 189 | }; | 205 | }; |
| 190 | static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); | 206 | static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); |
| 191 | 207 | ||
| 192 | struct GenericStates { | 208 | struct ControllerPad { |
| 193 | s64_le timestamp; | ||
| 194 | s64_le timestamp2; | ||
| 195 | ControllerPadState pad_states; | 209 | ControllerPadState pad_states; |
| 196 | AnalogPosition l_stick; | 210 | AnalogPosition l_stick; |
| 197 | AnalogPosition r_stick; | 211 | AnalogPosition r_stick; |
| 212 | }; | ||
| 213 | static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size"); | ||
| 214 | |||
| 215 | struct GenericStates { | ||
| 216 | s64_le timestamp; | ||
| 217 | s64_le timestamp2; | ||
| 218 | ControllerPad pad; | ||
| 198 | ConnectionState connection_status; | 219 | ConnectionState connection_status; |
| 199 | }; | 220 | }; |
| 200 | static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size"); | 221 | static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size"); |
| @@ -266,15 +287,20 @@ private: | |||
| 266 | static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); | 287 | static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); |
| 267 | 288 | ||
| 268 | struct ControllerHolder { | 289 | struct ControllerHolder { |
| 269 | Controller_NPad::NPadControllerType type; | 290 | NPadControllerType type; |
| 270 | bool is_connected; | 291 | bool is_connected; |
| 271 | }; | 292 | }; |
| 272 | 293 | ||
| 273 | NPadType style{}; | 294 | NPadType style{}; |
| 274 | std::array<NPadEntry, 10> shared_memory_entries{}; | 295 | std::array<NPadEntry, 10> shared_memory_entries{}; |
| 275 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> | 296 | std::array< |
| 297 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, | ||
| 298 | 10> | ||
| 276 | buttons; | 299 | buttons; |
| 277 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks; | 300 | std::array< |
| 301 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, | ||
| 302 | 10> | ||
| 303 | sticks; | ||
| 278 | std::vector<u32> supported_npad_id_types{}; | 304 | std::vector<u32> supported_npad_id_types{}; |
| 279 | NpadHoldType hold_type{NpadHoldType::Vertical}; | 305 | NpadHoldType hold_type{NpadHoldType::Vertical}; |
| 280 | Kernel::SharedPtr<Kernel::Event> styleset_changed_event; | 306 | Kernel::SharedPtr<Kernel::Event> styleset_changed_event; |
| @@ -285,5 +311,8 @@ private: | |||
| 285 | void InitNewlyAddedControler(std::size_t controller_idx); | 311 | void InitNewlyAddedControler(std::size_t controller_idx); |
| 286 | bool IsControllerSupported(NPadControllerType controller) const; | 312 | bool IsControllerSupported(NPadControllerType controller) const; |
| 287 | NPadControllerType DecideBestController(NPadControllerType priority) const; | 313 | NPadControllerType DecideBestController(NPadControllerType priority) const; |
| 314 | void RequestPadStateUpdate(u32 npad_id); | ||
| 315 | std::array<ControllerPad, 10> npad_pad_states{}; | ||
| 316 | bool IsControllerSupported(NPadControllerType controller); | ||
| 288 | }; | 317 | }; |
| 289 | } // namespace Service::HID | 318 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 43efef803..f666b1bd8 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp | |||
| @@ -41,16 +41,17 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) { | |||
| 41 | 41 | ||
| 42 | const auto [x, y, pressed] = touch_device->GetStatus(); | 42 | const auto [x, y, pressed] = touch_device->GetStatus(); |
| 43 | auto& touch_entry = cur_entry.states[0]; | 43 | auto& touch_entry = cur_entry.states[0]; |
| 44 | if (pressed) { | 44 | touch_entry.attribute.raw = 0; |
| 45 | if (pressed && Settings::values.touchscreen.enabled) { | ||
| 45 | touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); | 46 | touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); |
| 46 | touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); | 47 | touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); |
| 47 | touch_entry.diameter_x = 15; | 48 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; |
| 48 | touch_entry.diameter_y = 15; | 49 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; |
| 49 | touch_entry.rotation_angle = 0; | 50 | touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; |
| 50 | const u64 tick = CoreTiming::GetTicks(); | 51 | const u64 tick = CoreTiming::GetTicks(); |
| 51 | touch_entry.delta_time = tick - last_touch; | 52 | touch_entry.delta_time = tick - last_touch; |
| 52 | last_touch = tick; | 53 | last_touch = tick; |
| 53 | touch_entry.finger = 0; | 54 | touch_entry.finger = Settings::values.touchscreen.finger; |
| 54 | cur_entry.entry_count = 1; | 55 | cur_entry.entry_count = 1; |
| 55 | } else { | 56 | } else { |
| 56 | cur_entry.entry_count = 0; | 57 | cur_entry.entry_count = 0; |
| @@ -60,6 +61,6 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) { | |||
| 60 | } | 61 | } |
| 61 | 62 | ||
| 62 | void Controller_Touchscreen::OnLoadInputDevices() { | 63 | void Controller_Touchscreen::OnLoadInputDevices() { |
| 63 | touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device); | 64 | touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device); |
| 64 | } | 65 | } |
| 65 | } // namespace Service::HID | 66 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index e5db6e6ba..94cd0eba9 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/bit_field.h" | ||
| 7 | #include "common/common_funcs.h" | 8 | #include "common/common_funcs.h" |
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| @@ -29,9 +30,18 @@ public: | |||
| 29 | void OnLoadInputDevices() override; | 30 | void OnLoadInputDevices() override; |
| 30 | 31 | ||
| 31 | private: | 32 | private: |
| 33 | struct Attributes { | ||
| 34 | union { | ||
| 35 | u32 raw{}; | ||
| 36 | BitField<0, 1, u32_le> start_touch; | ||
| 37 | BitField<1, 1, u32_le> end_touch; | ||
| 38 | }; | ||
| 39 | }; | ||
| 40 | static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size"); | ||
| 41 | |||
| 32 | struct TouchState { | 42 | struct TouchState { |
| 33 | u64_le delta_time; | 43 | u64_le delta_time; |
| 34 | u32_le attribute; | 44 | Attributes attribute; |
| 35 | u32_le finger; | 45 | u32_le finger; |
| 36 | u32_le x; | 46 | u32_le x; |
| 37 | u32_le y; | 47 | u32_le y; |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 39631b14f..7c0dac5dc 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -34,8 +34,8 @@ | |||
| 34 | namespace Service::HID { | 34 | namespace Service::HID { |
| 35 | 35 | ||
| 36 | // Updating period for each HID device. | 36 | // Updating period for each HID device. |
| 37 | // TODO(shinyquagsire23): These need better values. | 37 | // TODO(ogniK): Find actual polling rate of hid |
| 38 | constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; | 38 | constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66; |
| 39 | constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; | 39 | constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; |
| 40 | constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; | 40 | constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; |
| 41 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; | 41 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index d607d985e..b43f1f054 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -4,7 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <fmt/format.h> | 6 | #include <fmt/format.h> |
| 7 | #include <mbedtls/sha256.h> | ||
| 7 | 8 | ||
| 9 | #include "common/alignment.h" | ||
| 10 | #include "common/hex_util.h" | ||
| 8 | #include "core/hle/ipc_helpers.h" | 11 | #include "core/hle/ipc_helpers.h" |
| 9 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 10 | #include "core/hle/service/ldr/ldr.h" | 13 | #include "core/hle/service/ldr/ldr.h" |
| @@ -13,6 +16,38 @@ | |||
| 13 | 16 | ||
| 14 | namespace Service::LDR { | 17 | namespace Service::LDR { |
| 15 | 18 | ||
| 19 | namespace ErrCodes { | ||
| 20 | enum { | ||
| 21 | InvalidMemoryState = 51, | ||
| 22 | InvalidNRO = 52, | ||
| 23 | InvalidNRR = 53, | ||
| 24 | MissingNRRHash = 54, | ||
| 25 | MaximumNRO = 55, | ||
| 26 | MaximumNRR = 56, | ||
| 27 | AlreadyLoaded = 57, | ||
| 28 | InvalidAlignment = 81, | ||
| 29 | InvalidSize = 82, | ||
| 30 | InvalidNROAddress = 84, | ||
| 31 | InvalidNRRAddress = 85, | ||
| 32 | NotInitialized = 87, | ||
| 33 | }; | ||
| 34 | } | ||
| 35 | |||
| 36 | constexpr ResultCode ERROR_INVALID_MEMORY_STATE(ErrorModule::Loader, ErrCodes::InvalidMemoryState); | ||
| 37 | constexpr ResultCode ERROR_INVALID_NRO(ErrorModule::Loader, ErrCodes::InvalidNRO); | ||
| 38 | constexpr ResultCode ERROR_INVALID_NRR(ErrorModule::Loader, ErrCodes::InvalidNRR); | ||
| 39 | constexpr ResultCode ERROR_MISSING_NRR_HASH(ErrorModule::Loader, ErrCodes::MissingNRRHash); | ||
| 40 | constexpr ResultCode ERROR_MAXIMUM_NRO(ErrorModule::Loader, ErrCodes::MaximumNRO); | ||
| 41 | constexpr ResultCode ERROR_MAXIMUM_NRR(ErrorModule::Loader, ErrCodes::MaximumNRR); | ||
| 42 | constexpr ResultCode ERROR_ALREADY_LOADED(ErrorModule::Loader, ErrCodes::AlreadyLoaded); | ||
| 43 | constexpr ResultCode ERROR_INVALID_ALIGNMENT(ErrorModule::Loader, ErrCodes::InvalidAlignment); | ||
| 44 | constexpr ResultCode ERROR_INVALID_SIZE(ErrorModule::Loader, ErrCodes::InvalidSize); | ||
| 45 | constexpr ResultCode ERROR_INVALID_NRO_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNROAddress); | ||
| 46 | constexpr ResultCode ERROR_INVALID_NRR_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNRRAddress); | ||
| 47 | constexpr ResultCode ERROR_NOT_INITIALIZED(ErrorModule::Loader, ErrCodes::NotInitialized); | ||
| 48 | |||
| 49 | constexpr u64 MAXIMUM_LOADED_RO = 0x40; | ||
| 50 | |||
| 16 | class DebugMonitor final : public ServiceFramework<DebugMonitor> { | 51 | class DebugMonitor final : public ServiceFramework<DebugMonitor> { |
| 17 | public: | 52 | public: |
| 18 | explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} { | 53 | explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} { |
| @@ -64,9 +99,9 @@ public: | |||
| 64 | // clang-format off | 99 | // clang-format off |
| 65 | static const FunctionInfo functions[] = { | 100 | static const FunctionInfo functions[] = { |
| 66 | {0, &RelocatableObject::LoadNro, "LoadNro"}, | 101 | {0, &RelocatableObject::LoadNro, "LoadNro"}, |
| 67 | {1, nullptr, "UnloadNro"}, | 102 | {1, &RelocatableObject::UnloadNro, "UnloadNro"}, |
| 68 | {2, &RelocatableObject::LoadNrr, "LoadNrr"}, | 103 | {2, &RelocatableObject::LoadNrr, "LoadNrr"}, |
| 69 | {3, nullptr, "UnloadNrr"}, | 104 | {3, &RelocatableObject::UnloadNrr, "UnloadNrr"}, |
| 70 | {4, &RelocatableObject::Initialize, "Initialize"}, | 105 | {4, &RelocatableObject::Initialize, "Initialize"}, |
| 71 | }; | 106 | }; |
| 72 | // clang-format on | 107 | // clang-format on |
| @@ -75,9 +110,123 @@ public: | |||
| 75 | } | 110 | } |
| 76 | 111 | ||
| 77 | void LoadNrr(Kernel::HLERequestContext& ctx) { | 112 | void LoadNrr(Kernel::HLERequestContext& ctx) { |
| 113 | IPC::RequestParser rp{ctx}; | ||
| 114 | rp.Skip(2, false); | ||
| 115 | const VAddr nrr_addr{rp.Pop<VAddr>()}; | ||
| 116 | const u64 nrr_size{rp.Pop<u64>()}; | ||
| 117 | |||
| 118 | if (!initialized) { | ||
| 119 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | ||
| 120 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 121 | rb.Push(ERROR_NOT_INITIALIZED); | ||
| 122 | return; | ||
| 123 | } | ||
| 124 | |||
| 125 | if (nrr.size() >= MAXIMUM_LOADED_RO) { | ||
| 126 | LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs " | ||
| 127 | "(0x40)! Failing..."); | ||
| 128 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 129 | rb.Push(ERROR_MAXIMUM_NRR); | ||
| 130 | return; | ||
| 131 | } | ||
| 132 | |||
| 133 | // NRR Address does not fall on 0x1000 byte boundary | ||
| 134 | if (!Common::Is4KBAligned(nrr_addr)) { | ||
| 135 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); | ||
| 136 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 137 | rb.Push(ERROR_INVALID_ALIGNMENT); | ||
| 138 | return; | ||
| 139 | } | ||
| 140 | |||
| 141 | // NRR Size is zero or causes overflow | ||
| 142 | if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) { | ||
| 143 | LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", | ||
| 144 | nrr_addr, nrr_size); | ||
| 145 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 146 | rb.Push(ERROR_INVALID_SIZE); | ||
| 147 | return; | ||
| 148 | } | ||
| 149 | // Read NRR data from memory | ||
| 150 | std::vector<u8> nrr_data(nrr_size); | ||
| 151 | Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size); | ||
| 152 | NRRHeader header; | ||
| 153 | std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); | ||
| 154 | |||
| 155 | if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) { | ||
| 156 | LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic); | ||
| 157 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 158 | rb.Push(ERROR_INVALID_NRR); | ||
| 159 | return; | ||
| 160 | } | ||
| 161 | |||
| 162 | if (header.size != nrr_size) { | ||
| 163 | LOG_ERROR(Service_LDR, | ||
| 164 | "NRR header reported size did not match LoadNrr parameter size! " | ||
| 165 | "(header_size={:016X}, loadnrr_size={:016X})", | ||
| 166 | header.size, nrr_size); | ||
| 167 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 168 | rb.Push(ERROR_INVALID_SIZE); | ||
| 169 | return; | ||
| 170 | } | ||
| 171 | |||
| 172 | if (Core::CurrentProcess()->GetTitleID() != header.title_id) { | ||
| 173 | LOG_ERROR(Service_LDR, | ||
| 174 | "Attempting to load NRR with title ID other than current process. (actual " | ||
| 175 | "{:016X})!", | ||
| 176 | header.title_id); | ||
| 177 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 178 | rb.Push(ERROR_INVALID_NRR); | ||
| 179 | return; | ||
| 180 | } | ||
| 181 | |||
| 182 | std::vector<SHA256Hash> hashes; | ||
| 183 | |||
| 184 | // Copy all hashes in the NRR (specified by hash count/hash offset) into vector. | ||
| 185 | for (std::size_t i = header.hash_offset; | ||
| 186 | i < (header.hash_offset + (header.hash_count * sizeof(SHA256Hash))); i += 8) { | ||
| 187 | SHA256Hash hash; | ||
| 188 | std::memcpy(hash.data(), nrr_data.data() + i, sizeof(SHA256Hash)); | ||
| 189 | hashes.emplace_back(hash); | ||
| 190 | } | ||
| 191 | |||
| 192 | nrr.insert_or_assign(nrr_addr, std::move(hashes)); | ||
| 193 | |||
| 194 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 195 | rb.Push(RESULT_SUCCESS); | ||
| 196 | } | ||
| 197 | |||
| 198 | void UnloadNrr(Kernel::HLERequestContext& ctx) { | ||
| 199 | if (!initialized) { | ||
| 200 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | ||
| 201 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 202 | rb.Push(ERROR_NOT_INITIALIZED); | ||
| 203 | return; | ||
| 204 | } | ||
| 205 | |||
| 206 | IPC::RequestParser rp{ctx}; | ||
| 207 | rp.Skip(2, false); | ||
| 208 | const auto nrr_addr{rp.Pop<VAddr>()}; | ||
| 209 | |||
| 210 | if (!Common::Is4KBAligned(nrr_addr)) { | ||
| 211 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); | ||
| 212 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 213 | rb.Push(ERROR_INVALID_ALIGNMENT); | ||
| 214 | return; | ||
| 215 | } | ||
| 216 | |||
| 217 | const auto iter = nrr.find(nrr_addr); | ||
| 218 | if (iter == nrr.end()) { | ||
| 219 | LOG_ERROR(Service_LDR, | ||
| 220 | "Attempting to unload NRR which has not been loaded! (addr={:016X})", | ||
| 221 | nrr_addr); | ||
| 222 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 223 | rb.Push(ERROR_INVALID_NRR_ADDRESS); | ||
| 224 | return; | ||
| 225 | } | ||
| 226 | |||
| 227 | nrr.erase(iter); | ||
| 78 | IPC::ResponseBuilder rb{ctx, 2}; | 228 | IPC::ResponseBuilder rb{ctx, 2}; |
| 79 | rb.Push(RESULT_SUCCESS); | 229 | rb.Push(RESULT_SUCCESS); |
| 80 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | ||
| 81 | } | 230 | } |
| 82 | 231 | ||
| 83 | void LoadNro(Kernel::HLERequestContext& ctx) { | 232 | void LoadNro(Kernel::HLERequestContext& ctx) { |
| @@ -88,33 +237,253 @@ public: | |||
| 88 | const VAddr bss_addr{rp.Pop<VAddr>()}; | 237 | const VAddr bss_addr{rp.Pop<VAddr>()}; |
| 89 | const u64 bss_size{rp.Pop<u64>()}; | 238 | const u64 bss_size{rp.Pop<u64>()}; |
| 90 | 239 | ||
| 240 | if (!initialized) { | ||
| 241 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | ||
| 242 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 243 | rb.Push(ERROR_NOT_INITIALIZED); | ||
| 244 | return; | ||
| 245 | } | ||
| 246 | |||
| 247 | if (nro.size() >= MAXIMUM_LOADED_RO) { | ||
| 248 | LOG_ERROR(Service_LDR, "Loading new NRO would exceed the maximum number of loaded NROs " | ||
| 249 | "(0x40)! Failing..."); | ||
| 250 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 251 | rb.Push(ERROR_MAXIMUM_NRO); | ||
| 252 | return; | ||
| 253 | } | ||
| 254 | |||
| 255 | // NRO Address does not fall on 0x1000 byte boundary | ||
| 256 | if (!Common::Is4KBAligned(nro_addr)) { | ||
| 257 | LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr); | ||
| 258 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 259 | rb.Push(ERROR_INVALID_ALIGNMENT); | ||
| 260 | return; | ||
| 261 | } | ||
| 262 | |||
| 263 | // NRO Size or BSS Size is zero or causes overflow | ||
| 264 | const auto nro_size_valid = | ||
| 265 | nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size); | ||
| 266 | const auto bss_size_valid = | ||
| 267 | nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr); | ||
| 268 | |||
| 269 | if (!nro_size_valid || !bss_size_valid) { | ||
| 270 | LOG_ERROR(Service_LDR, | ||
| 271 | "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " | ||
| 272 | "bss_address={:016X}, bss_size={:016X})", | ||
| 273 | nro_addr, nro_size, bss_addr, bss_size); | ||
| 274 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 275 | rb.Push(ERROR_INVALID_SIZE); | ||
| 276 | return; | ||
| 277 | } | ||
| 278 | |||
| 91 | // Read NRO data from memory | 279 | // Read NRO data from memory |
| 92 | std::vector<u8> nro_data(nro_size); | 280 | std::vector<u8> nro_data(nro_size); |
| 93 | Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); | 281 | Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); |
| 94 | 282 | ||
| 283 | SHA256Hash hash{}; | ||
| 284 | mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); | ||
| 285 | |||
| 286 | // NRO Hash is already loaded | ||
| 287 | if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) { | ||
| 288 | return info.second.hash == hash; | ||
| 289 | })) { | ||
| 290 | LOG_ERROR(Service_LDR, "NRO is already loaded!"); | ||
| 291 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 292 | rb.Push(ERROR_ALREADY_LOADED); | ||
| 293 | return; | ||
| 294 | } | ||
| 295 | |||
| 296 | // NRO Hash is not in any loaded NRR | ||
| 297 | if (!IsValidNROHash(hash)) { | ||
| 298 | LOG_ERROR(Service_LDR, | ||
| 299 | "NRO hash is not present in any currently loaded NRRs (hash={})!", | ||
| 300 | Common::HexArrayToString(hash)); | ||
| 301 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 302 | rb.Push(ERROR_MISSING_NRR_HASH); | ||
| 303 | return; | ||
| 304 | } | ||
| 305 | |||
| 306 | NROHeader header; | ||
| 307 | std::memcpy(&header, nro_data.data(), sizeof(NROHeader)); | ||
| 308 | |||
| 309 | if (!IsValidNRO(header, nro_size, bss_size)) { | ||
| 310 | LOG_ERROR(Service_LDR, "NRO was invalid!"); | ||
| 311 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 312 | rb.Push(ERROR_INVALID_NRO); | ||
| 313 | return; | ||
| 314 | } | ||
| 315 | |||
| 95 | // Load NRO as new executable module | 316 | // Load NRO as new executable module |
| 96 | const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)}; | 317 | auto* process = Core::CurrentProcess(); |
| 97 | Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr); | 318 | auto& vm_manager = process->VMManager(); |
| 319 | auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size); | ||
| 320 | |||
| 321 | if (!map_address.Succeeded() || | ||
| 322 | *map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) { | ||
| 323 | |||
| 324 | LOG_ERROR(Service_LDR, | ||
| 325 | "General error while allocation memory or no available memory to allocate!"); | ||
| 326 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 327 | rb.Push(ERROR_INVALID_MEMORY_STATE); | ||
| 328 | return; | ||
| 329 | } | ||
| 330 | |||
| 331 | ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size, | ||
| 332 | Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); | ||
| 333 | ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS); | ||
| 334 | |||
| 335 | if (bss_size > 0) { | ||
| 336 | ASSERT(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size, | ||
| 337 | Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); | ||
| 338 | ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS); | ||
| 339 | } | ||
| 340 | |||
| 341 | vm_manager.ReprotectRange(*map_address, header.text_size, | ||
| 342 | Kernel::VMAPermission::ReadExecute); | ||
| 343 | vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size, | ||
| 344 | Kernel::VMAPermission::Read); | ||
| 345 | vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, | ||
| 346 | Kernel::VMAPermission::ReadWrite); | ||
| 98 | 347 | ||
| 99 | // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party. | 348 | Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); |
| 100 | // It is currently missing: | 349 | Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); |
| 101 | // - Signature checks with LoadNRR | 350 | Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); |
| 102 | // - Checking if a module has already been loaded | 351 | Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); |
| 103 | // - Using/validating BSS, etc. params (these are used from NRO header instead) | 352 | |
| 104 | // - Error checking | 353 | nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size}); |
| 105 | // - ...Probably other things | ||
| 106 | 354 | ||
| 107 | IPC::ResponseBuilder rb{ctx, 4}; | 355 | IPC::ResponseBuilder rb{ctx, 4}; |
| 108 | rb.Push(RESULT_SUCCESS); | 356 | rb.Push(RESULT_SUCCESS); |
| 109 | rb.Push(addr); | 357 | rb.Push(*map_address); |
| 110 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | 358 | } |
| 359 | |||
| 360 | void UnloadNro(Kernel::HLERequestContext& ctx) { | ||
| 361 | IPC::RequestParser rp{ctx}; | ||
| 362 | rp.Skip(2, false); | ||
| 363 | const VAddr mapped_addr{rp.PopRaw<VAddr>()}; | ||
| 364 | const VAddr heap_addr{rp.PopRaw<VAddr>()}; | ||
| 365 | |||
| 366 | if (!initialized) { | ||
| 367 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | ||
| 368 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 369 | rb.Push(ERROR_NOT_INITIALIZED); | ||
| 370 | return; | ||
| 371 | } | ||
| 372 | |||
| 373 | if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) { | ||
| 374 | LOG_ERROR(Service_LDR, | ||
| 375 | "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, " | ||
| 376 | "bss_addr={:016X})!", | ||
| 377 | mapped_addr, heap_addr); | ||
| 378 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 379 | rb.Push(ERROR_INVALID_ALIGNMENT); | ||
| 380 | return; | ||
| 381 | } | ||
| 382 | |||
| 383 | const auto iter = nro.find(mapped_addr); | ||
| 384 | if (iter == nro.end()) { | ||
| 385 | LOG_ERROR(Service_LDR, | ||
| 386 | "The NRO attempting to unmap was not mapped or has an invalid address " | ||
| 387 | "(actual {:016X})!", | ||
| 388 | mapped_addr); | ||
| 389 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 390 | rb.Push(ERROR_INVALID_NRO_ADDRESS); | ||
| 391 | return; | ||
| 392 | } | ||
| 393 | |||
| 394 | auto* process = Core::CurrentProcess(); | ||
| 395 | auto& vm_manager = process->VMManager(); | ||
| 396 | const auto& nro_size = iter->second.size; | ||
| 397 | |||
| 398 | ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size, | ||
| 399 | Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS); | ||
| 400 | ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS); | ||
| 401 | |||
| 402 | Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); | ||
| 403 | Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); | ||
| 404 | Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); | ||
| 405 | Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); | ||
| 406 | |||
| 407 | nro.erase(iter); | ||
| 408 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 409 | rb.Push(RESULT_SUCCESS); | ||
| 111 | } | 410 | } |
| 112 | 411 | ||
| 113 | void Initialize(Kernel::HLERequestContext& ctx) { | 412 | void Initialize(Kernel::HLERequestContext& ctx) { |
| 413 | initialized = true; | ||
| 414 | |||
| 114 | IPC::ResponseBuilder rb{ctx, 2}; | 415 | IPC::ResponseBuilder rb{ctx, 2}; |
| 115 | rb.Push(RESULT_SUCCESS); | 416 | rb.Push(RESULT_SUCCESS); |
| 116 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | 417 | LOG_WARNING(Service_LDR, "(STUBBED) called"); |
| 117 | } | 418 | } |
| 419 | |||
| 420 | private: | ||
| 421 | using SHA256Hash = std::array<u8, 0x20>; | ||
| 422 | |||
| 423 | struct NROHeader { | ||
| 424 | u32_le entrypoint_insn; | ||
| 425 | u32_le mod_offset; | ||
| 426 | INSERT_PADDING_WORDS(2); | ||
| 427 | u32_le magic; | ||
| 428 | INSERT_PADDING_WORDS(1); | ||
| 429 | u32_le nro_size; | ||
| 430 | INSERT_PADDING_WORDS(1); | ||
| 431 | u32_le text_offset; | ||
| 432 | u32_le text_size; | ||
| 433 | u32_le ro_offset; | ||
| 434 | u32_le ro_size; | ||
| 435 | u32_le rw_offset; | ||
| 436 | u32_le rw_size; | ||
| 437 | u32_le bss_size; | ||
| 438 | INSERT_PADDING_WORDS(1); | ||
| 439 | std::array<u8, 0x20> build_id; | ||
| 440 | INSERT_PADDING_BYTES(0x20); | ||
| 441 | }; | ||
| 442 | static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); | ||
| 443 | |||
| 444 | struct NRRHeader { | ||
| 445 | u32_le magic; | ||
| 446 | INSERT_PADDING_BYTES(0x1C); | ||
| 447 | u64_le title_id_mask; | ||
| 448 | u64_le title_id_pattern; | ||
| 449 | std::array<u8, 0x100> modulus; | ||
| 450 | std::array<u8, 0x100> signature_1; | ||
| 451 | std::array<u8, 0x100> signature_2; | ||
| 452 | u64_le title_id; | ||
| 453 | u32_le size; | ||
| 454 | INSERT_PADDING_BYTES(4); | ||
| 455 | u32_le hash_offset; | ||
| 456 | u32_le hash_count; | ||
| 457 | INSERT_PADDING_BYTES(8); | ||
| 458 | }; | ||
| 459 | static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size."); | ||
| 460 | |||
| 461 | struct NROInfo { | ||
| 462 | SHA256Hash hash; | ||
| 463 | u64 size; | ||
| 464 | }; | ||
| 465 | |||
| 466 | bool initialized = false; | ||
| 467 | |||
| 468 | std::map<VAddr, NROInfo> nro; | ||
| 469 | std::map<VAddr, std::vector<SHA256Hash>> nrr; | ||
| 470 | |||
| 471 | bool IsValidNROHash(const SHA256Hash& hash) { | ||
| 472 | return std::any_of( | ||
| 473 | nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) { | ||
| 474 | return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); | ||
| 475 | }); | ||
| 476 | } | ||
| 477 | |||
| 478 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { | ||
| 479 | return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && | ||
| 480 | header.nro_size == nro_size && header.bss_size == bss_size && | ||
| 481 | header.ro_offset == header.text_offset + header.text_size && | ||
| 482 | header.rw_offset == header.ro_offset + header.ro_size && | ||
| 483 | nro_size == header.rw_offset + header.rw_size && | ||
| 484 | Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && | ||
| 485 | Common::Is4KBAligned(header.rw_size); | ||
| 486 | } | ||
| 118 | }; | 487 | }; |
| 119 | 488 | ||
| 120 | void InstallInterfaces(SM::ServiceManager& sm) { | 489 | void InstallInterfaces(SM::ServiceManager& sm) { |
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index c89157a4d..4e5fdb16e 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp | |||
| @@ -18,7 +18,7 @@ public: | |||
| 18 | ILogger() : ServiceFramework("ILogger") { | 18 | ILogger() : ServiceFramework("ILogger") { |
| 19 | static const FunctionInfo functions[] = { | 19 | static const FunctionInfo functions[] = { |
| 20 | {0x00000000, &ILogger::Initialize, "Initialize"}, | 20 | {0x00000000, &ILogger::Initialize, "Initialize"}, |
| 21 | {0x00000001, nullptr, "SetDestination"}, | 21 | {0x00000001, &ILogger::SetDestination, "SetDestination"}, |
| 22 | }; | 22 | }; |
| 23 | RegisterHandlers(functions); | 23 | RegisterHandlers(functions); |
| 24 | } | 24 | } |
| @@ -178,6 +178,17 @@ private: | |||
| 178 | } | 178 | } |
| 179 | } | 179 | } |
| 180 | 180 | ||
| 181 | // This service function is intended to be used as a way to | ||
| 182 | // redirect logging output to different destinations, however, | ||
| 183 | // given we always want to see the logging output, it's sufficient | ||
| 184 | // to do nothing and return success here. | ||
| 185 | void SetDestination(Kernel::HLERequestContext& ctx) { | ||
| 186 | LOG_DEBUG(Service_LM, "called"); | ||
| 187 | |||
| 188 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 189 | rb.Push(RESULT_SUCCESS); | ||
| 190 | } | ||
| 191 | |||
| 181 | std::ostringstream log_stream; | 192 | std::ostringstream log_stream; |
| 182 | }; | 193 | }; |
| 183 | 194 | ||
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index 44accecb7..1066bf505 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp | |||
| @@ -351,6 +351,14 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { | |||
| 351 | font_sizes.push_back(region.size); | 351 | font_sizes.push_back(region.size); |
| 352 | } | 352 | } |
| 353 | 353 | ||
| 354 | // Resize buffers if game requests smaller size output. | ||
| 355 | font_codes.resize( | ||
| 356 | std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32))); | ||
| 357 | font_offsets.resize( | ||
| 358 | std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32))); | ||
| 359 | font_sizes.resize( | ||
| 360 | std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32))); | ||
| 361 | |||
| 354 | ctx.WriteBuffer(font_codes, 0); | 362 | ctx.WriteBuffer(font_codes, 0); |
| 355 | ctx.WriteBuffer(font_offsets, 1); | 363 | ctx.WriteBuffer(font_offsets, 1); |
| 356 | ctx.WriteBuffer(font_sizes, 2); | 364 | ctx.WriteBuffer(font_sizes, 2); |
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp index e3cbd7004..b3a196f65 100644 --- a/src/core/hle/service/time/interface.cpp +++ b/src/core/hle/service/time/interface.cpp | |||
| @@ -23,7 +23,8 @@ Time::Time(std::shared_ptr<Module> time, const char* name) | |||
| 23 | {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, | 23 | {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, |
| 24 | {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, | 24 | {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, |
| 25 | {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, | 25 | {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, |
| 26 | {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, | 26 | {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, |
| 27 | "CalculateStandardUserSystemClockDifferenceByUser"}, | ||
| 27 | {501, nullptr, "CalculateSpanBetween"}, | 28 | {501, nullptr, "CalculateSpanBetween"}, |
| 28 | }; | 29 | }; |
| 29 | RegisterHandlers(functions); | 30 | RegisterHandlers(functions); |
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 85e7b1195..e561a0c52 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp | |||
| @@ -299,6 +299,21 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { | |||
| 299 | ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot)); | 299 | ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot)); |
| 300 | } | 300 | } |
| 301 | 301 | ||
| 302 | void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser( | ||
| 303 | Kernel::HLERequestContext& ctx) { | ||
| 304 | LOG_DEBUG(Service_Time, "called"); | ||
| 305 | |||
| 306 | IPC::RequestParser rp{ctx}; | ||
| 307 | const auto snapshot_a = rp.PopRaw<ClockSnapshot>(); | ||
| 308 | const auto snapshot_b = rp.PopRaw<ClockSnapshot>(); | ||
| 309 | const u64 difference = | ||
| 310 | snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset; | ||
| 311 | |||
| 312 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 313 | rb.Push(RESULT_SUCCESS); | ||
| 314 | rb.PushRaw<u64>(difference); | ||
| 315 | } | ||
| 316 | |||
| 302 | Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) | 317 | Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) |
| 303 | : ServiceFramework(name), time(std::move(time)) {} | 318 | : ServiceFramework(name), time(std::move(time)) {} |
| 304 | 319 | ||
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index 77871ae07..ea43fbea7 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h | |||
| @@ -84,6 +84,7 @@ public: | |||
| 84 | void GetTimeZoneService(Kernel::HLERequestContext& ctx); | 84 | void GetTimeZoneService(Kernel::HLERequestContext& ctx); |
| 85 | void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); | 85 | void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); |
| 86 | void GetClockSnapshot(Kernel::HLERequestContext& ctx); | 86 | void GetClockSnapshot(Kernel::HLERequestContext& ctx); |
| 87 | void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx); | ||
| 87 | 88 | ||
| 88 | protected: | 89 | protected: |
| 89 | std::shared_ptr<Module> time; | 90 | std::shared_ptr<Module> time; |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index d764b2406..d25fdb1fe 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -237,6 +237,22 @@ private: | |||
| 237 | Data data{}; | 237 | Data data{}; |
| 238 | }; | 238 | }; |
| 239 | 239 | ||
| 240 | /// Represents a parcel containing one int '0' as its data | ||
| 241 | /// Used by DetachBuffer and Disconnect | ||
| 242 | class IGBPEmptyResponseParcel : public Parcel { | ||
| 243 | protected: | ||
| 244 | void SerializeData() override { | ||
| 245 | Write(data); | ||
| 246 | } | ||
| 247 | |||
| 248 | private: | ||
| 249 | struct Data { | ||
| 250 | u32_le unk_0; | ||
| 251 | }; | ||
| 252 | |||
| 253 | Data data{}; | ||
| 254 | }; | ||
| 255 | |||
| 240 | class IGBPSetPreallocatedBufferRequestParcel : public Parcel { | 256 | class IGBPSetPreallocatedBufferRequestParcel : public Parcel { |
| 241 | public: | 257 | public: |
| 242 | explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer) | 258 | explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer) |
| @@ -554,6 +570,12 @@ private: | |||
| 554 | ctx.WriteBuffer(response.Serialize()); | 570 | ctx.WriteBuffer(response.Serialize()); |
| 555 | } else if (transaction == TransactionId::CancelBuffer) { | 571 | } else if (transaction == TransactionId::CancelBuffer) { |
| 556 | LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); | 572 | LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); |
| 573 | } else if (transaction == TransactionId::Disconnect || | ||
| 574 | transaction == TransactionId::DetachBuffer) { | ||
| 575 | const auto buffer = ctx.ReadBuffer(); | ||
| 576 | |||
| 577 | IGBPEmptyResponseParcel response{}; | ||
| 578 | ctx.WriteBuffer(response.Serialize()); | ||
| 557 | } else { | 579 | } else { |
| 558 | ASSERT_MSG(false, "Unimplemented"); | 580 | ASSERT_MSG(false, "Unimplemented"); |
| 559 | } | 581 | } |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index c8e491fec..fbbd6b0de 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -170,17 +170,20 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 170 | arg_data.size()); | 170 | arg_data.size()); |
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | // Read MOD header | ||
| 174 | ModHeader mod_header{}; | ||
| 175 | // Default .bss to NRO header bss size if MOD0 section doesn't exist | 173 | // Default .bss to NRO header bss size if MOD0 section doesn't exist |
| 176 | u32 bss_size{PageAlignSize(nro_header.bss_size)}; | 174 | u32 bss_size{PageAlignSize(nro_header.bss_size)}; |
| 175 | |||
| 176 | // Read MOD header | ||
| 177 | ModHeader mod_header{}; | ||
| 177 | std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset, | 178 | std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset, |
| 178 | sizeof(ModHeader)); | 179 | sizeof(ModHeader)); |
| 180 | |||
| 179 | const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; | 181 | const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; |
| 180 | if (has_mod_header) { | 182 | if (has_mod_header) { |
| 181 | // Resize program image to include .bss section and page align each section | 183 | // Resize program image to include .bss section and page align each section |
| 182 | bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); | 184 | bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); |
| 183 | } | 185 | } |
| 186 | |||
| 184 | codeset.DataSegment().size += bss_size; | 187 | codeset.DataSegment().size += bss_size; |
| 185 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); | 188 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); |
| 186 | 189 | ||
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 0da159559..26fcd3405 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -10,6 +10,56 @@ | |||
| 10 | 10 | ||
| 11 | namespace Settings { | 11 | namespace Settings { |
| 12 | 12 | ||
| 13 | namespace NativeButton { | ||
| 14 | const std::array<const char*, NumButtons> mapping = {{ | ||
| 15 | "button_a", | ||
| 16 | "button_b", | ||
| 17 | "button_x", | ||
| 18 | "button_y", | ||
| 19 | "button_lstick", | ||
| 20 | "button_rstick", | ||
| 21 | "button_l", | ||
| 22 | "button_r", | ||
| 23 | "button_zl", | ||
| 24 | "button_zr", | ||
| 25 | "button_plus", | ||
| 26 | "button_minus", | ||
| 27 | "button_dleft", | ||
| 28 | "button_dup", | ||
| 29 | "button_dright", | ||
| 30 | "button_ddown", | ||
| 31 | "button_lstick_left", | ||
| 32 | "button_lstick_up", | ||
| 33 | "button_lstick_right", | ||
| 34 | "button_lstick_down", | ||
| 35 | "button_rstick_left", | ||
| 36 | "button_rstick_up", | ||
| 37 | "button_rstick_right", | ||
| 38 | "button_rstick_down", | ||
| 39 | "button_sl", | ||
| 40 | "button_sr", | ||
| 41 | "button_home", | ||
| 42 | "button_screenshot", | ||
| 43 | }}; | ||
| 44 | } | ||
| 45 | |||
| 46 | namespace NativeAnalog { | ||
| 47 | const std::array<const char*, NumAnalogs> mapping = {{ | ||
| 48 | "lstick", | ||
| 49 | "rstick", | ||
| 50 | }}; | ||
| 51 | } | ||
| 52 | |||
| 53 | namespace NativeMouseButton { | ||
| 54 | const std::array<const char*, NumMouseButtons> mapping = {{ | ||
| 55 | "left", | ||
| 56 | "right", | ||
| 57 | "middle", | ||
| 58 | "forward", | ||
| 59 | "back", | ||
| 60 | }}; | ||
| 61 | } | ||
| 62 | |||
| 13 | Values values = {}; | 63 | Values values = {}; |
| 14 | 64 | ||
| 15 | void Apply() { | 65 | void Apply() { |
diff --git a/src/core/settings.h b/src/core/settings.h index e424479f2..e63134f80 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -60,36 +60,7 @@ constexpr int BUTTON_NS_END = NumButtons; | |||
| 60 | constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; | 60 | constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; |
| 61 | constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; | 61 | constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; |
| 62 | 62 | ||
| 63 | static const std::array<const char*, NumButtons> mapping = {{ | 63 | extern const std::array<const char*, NumButtons> mapping; |
| 64 | "button_a", | ||
| 65 | "button_b", | ||
| 66 | "button_x", | ||
| 67 | "button_y", | ||
| 68 | "button_lstick", | ||
| 69 | "button_rstick", | ||
| 70 | "button_l", | ||
| 71 | "button_r", | ||
| 72 | "button_zl", | ||
| 73 | "button_zr", | ||
| 74 | "button_plus", | ||
| 75 | "button_minus", | ||
| 76 | "button_dleft", | ||
| 77 | "button_dup", | ||
| 78 | "button_dright", | ||
| 79 | "button_ddown", | ||
| 80 | "button_lstick_left", | ||
| 81 | "button_lstick_up", | ||
| 82 | "button_lstick_right", | ||
| 83 | "button_lstick_down", | ||
| 84 | "button_rstick_left", | ||
| 85 | "button_rstick_up", | ||
| 86 | "button_rstick_right", | ||
| 87 | "button_rstick_down", | ||
| 88 | "button_sl", | ||
| 89 | "button_sr", | ||
| 90 | "button_home", | ||
| 91 | "button_screenshot", | ||
| 92 | }}; | ||
| 93 | 64 | ||
| 94 | } // namespace NativeButton | 65 | } // namespace NativeButton |
| 95 | 66 | ||
| @@ -105,12 +76,273 @@ constexpr int STICK_HID_BEGIN = LStick; | |||
| 105 | constexpr int STICK_HID_END = NumAnalogs; | 76 | constexpr int STICK_HID_END = NumAnalogs; |
| 106 | constexpr int NUM_STICKS_HID = NumAnalogs; | 77 | constexpr int NUM_STICKS_HID = NumAnalogs; |
| 107 | 78 | ||
| 108 | static const std::array<const char*, NumAnalogs> mapping = {{ | 79 | extern const std::array<const char*, NumAnalogs> mapping; |
| 109 | "lstick", | ||
| 110 | "rstick", | ||
| 111 | }}; | ||
| 112 | } // namespace NativeAnalog | 80 | } // namespace NativeAnalog |
| 113 | 81 | ||
| 82 | namespace NativeMouseButton { | ||
| 83 | enum Values { | ||
| 84 | Left, | ||
| 85 | Right, | ||
| 86 | Middle, | ||
| 87 | Forward, | ||
| 88 | Back, | ||
| 89 | |||
| 90 | NumMouseButtons, | ||
| 91 | }; | ||
| 92 | |||
| 93 | constexpr int MOUSE_HID_BEGIN = Left; | ||
| 94 | constexpr int MOUSE_HID_END = NumMouseButtons; | ||
| 95 | constexpr int NUM_MOUSE_HID = NumMouseButtons; | ||
| 96 | |||
| 97 | extern const std::array<const char*, NumMouseButtons> mapping; | ||
| 98 | } // namespace NativeMouseButton | ||
| 99 | |||
| 100 | namespace NativeKeyboard { | ||
| 101 | enum Keys { | ||
| 102 | None, | ||
| 103 | Error, | ||
| 104 | |||
| 105 | A = 4, | ||
| 106 | B, | ||
| 107 | C, | ||
| 108 | D, | ||
| 109 | E, | ||
| 110 | F, | ||
| 111 | G, | ||
| 112 | H, | ||
| 113 | I, | ||
| 114 | J, | ||
| 115 | K, | ||
| 116 | L, | ||
| 117 | M, | ||
| 118 | N, | ||
| 119 | O, | ||
| 120 | P, | ||
| 121 | Q, | ||
| 122 | R, | ||
| 123 | S, | ||
| 124 | T, | ||
| 125 | U, | ||
| 126 | V, | ||
| 127 | W, | ||
| 128 | X, | ||
| 129 | Y, | ||
| 130 | Z, | ||
| 131 | N1, | ||
| 132 | N2, | ||
| 133 | N3, | ||
| 134 | N4, | ||
| 135 | N5, | ||
| 136 | N6, | ||
| 137 | N7, | ||
| 138 | N8, | ||
| 139 | N9, | ||
| 140 | N0, | ||
| 141 | Enter, | ||
| 142 | Escape, | ||
| 143 | Backspace, | ||
| 144 | Tab, | ||
| 145 | Space, | ||
| 146 | Minus, | ||
| 147 | Equal, | ||
| 148 | LeftBrace, | ||
| 149 | RightBrace, | ||
| 150 | Backslash, | ||
| 151 | Tilde, | ||
| 152 | Semicolon, | ||
| 153 | Apostrophe, | ||
| 154 | Grave, | ||
| 155 | Comma, | ||
| 156 | Dot, | ||
| 157 | Slash, | ||
| 158 | CapsLockKey, | ||
| 159 | |||
| 160 | F1, | ||
| 161 | F2, | ||
| 162 | F3, | ||
| 163 | F4, | ||
| 164 | F5, | ||
| 165 | F6, | ||
| 166 | F7, | ||
| 167 | F8, | ||
| 168 | F9, | ||
| 169 | F10, | ||
| 170 | F11, | ||
| 171 | F12, | ||
| 172 | |||
| 173 | SystemRequest, | ||
| 174 | ScrollLockKey, | ||
| 175 | Pause, | ||
| 176 | Insert, | ||
| 177 | Home, | ||
| 178 | PageUp, | ||
| 179 | Delete, | ||
| 180 | End, | ||
| 181 | PageDown, | ||
| 182 | Right, | ||
| 183 | Left, | ||
| 184 | Down, | ||
| 185 | Up, | ||
| 186 | |||
| 187 | NumLockKey, | ||
| 188 | KPSlash, | ||
| 189 | KPAsterisk, | ||
| 190 | KPMinus, | ||
| 191 | KPPlus, | ||
| 192 | KPEnter, | ||
| 193 | KP1, | ||
| 194 | KP2, | ||
| 195 | KP3, | ||
| 196 | KP4, | ||
| 197 | KP5, | ||
| 198 | KP6, | ||
| 199 | KP7, | ||
| 200 | KP8, | ||
| 201 | KP9, | ||
| 202 | KP0, | ||
| 203 | KPDot, | ||
| 204 | |||
| 205 | Key102, | ||
| 206 | Compose, | ||
| 207 | Power, | ||
| 208 | KPEqual, | ||
| 209 | |||
| 210 | F13, | ||
| 211 | F14, | ||
| 212 | F15, | ||
| 213 | F16, | ||
| 214 | F17, | ||
| 215 | F18, | ||
| 216 | F19, | ||
| 217 | F20, | ||
| 218 | F21, | ||
| 219 | F22, | ||
| 220 | F23, | ||
| 221 | F24, | ||
| 222 | |||
| 223 | Open, | ||
| 224 | Help, | ||
| 225 | Properties, | ||
| 226 | Front, | ||
| 227 | Stop, | ||
| 228 | Repeat, | ||
| 229 | Undo, | ||
| 230 | Cut, | ||
| 231 | Copy, | ||
| 232 | Paste, | ||
| 233 | Find, | ||
| 234 | Mute, | ||
| 235 | VolumeUp, | ||
| 236 | VolumeDown, | ||
| 237 | CapsLockActive, | ||
| 238 | NumLockActive, | ||
| 239 | ScrollLockActive, | ||
| 240 | KPComma, | ||
| 241 | |||
| 242 | KPLeftParenthesis, | ||
| 243 | KPRightParenthesis, | ||
| 244 | |||
| 245 | LeftControlKey = 0xE0, | ||
| 246 | LeftShiftKey, | ||
| 247 | LeftAltKey, | ||
| 248 | LeftMetaKey, | ||
| 249 | RightControlKey, | ||
| 250 | RightShiftKey, | ||
| 251 | RightAltKey, | ||
| 252 | RightMetaKey, | ||
| 253 | |||
| 254 | MediaPlayPause, | ||
| 255 | MediaStopCD, | ||
| 256 | MediaPrevious, | ||
| 257 | MediaNext, | ||
| 258 | MediaEject, | ||
| 259 | MediaVolumeUp, | ||
| 260 | MediaVolumeDown, | ||
| 261 | MediaMute, | ||
| 262 | MediaWebsite, | ||
| 263 | MediaBack, | ||
| 264 | MediaForward, | ||
| 265 | MediaStop, | ||
| 266 | MediaFind, | ||
| 267 | MediaScrollUp, | ||
| 268 | MediaScrollDown, | ||
| 269 | MediaEdit, | ||
| 270 | MediaSleep, | ||
| 271 | MediaCoffee, | ||
| 272 | MediaRefresh, | ||
| 273 | MediaCalculator, | ||
| 274 | |||
| 275 | NumKeyboardKeys, | ||
| 276 | }; | ||
| 277 | |||
| 278 | static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys."); | ||
| 279 | |||
| 280 | enum Modifiers { | ||
| 281 | LeftControl, | ||
| 282 | LeftShift, | ||
| 283 | LeftAlt, | ||
| 284 | LeftMeta, | ||
| 285 | RightControl, | ||
| 286 | RightShift, | ||
| 287 | RightAlt, | ||
| 288 | RightMeta, | ||
| 289 | CapsLock, | ||
| 290 | ScrollLock, | ||
| 291 | NumLock, | ||
| 292 | |||
| 293 | NumKeyboardMods, | ||
| 294 | }; | ||
| 295 | |||
| 296 | constexpr int KEYBOARD_KEYS_HID_BEGIN = None; | ||
| 297 | constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys; | ||
| 298 | constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys; | ||
| 299 | |||
| 300 | constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl; | ||
| 301 | constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods; | ||
| 302 | constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods; | ||
| 303 | |||
| 304 | } // namespace NativeKeyboard | ||
| 305 | |||
| 306 | using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; | ||
| 307 | using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; | ||
| 308 | using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; | ||
| 309 | using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; | ||
| 310 | using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; | ||
| 311 | |||
| 312 | constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; | ||
| 313 | constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; | ||
| 314 | constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6; | ||
| 315 | constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E; | ||
| 316 | |||
| 317 | enum class ControllerType { | ||
| 318 | ProController, | ||
| 319 | DualJoycon, | ||
| 320 | RightJoycon, | ||
| 321 | LeftJoycon, | ||
| 322 | }; | ||
| 323 | |||
| 324 | struct PlayerInput { | ||
| 325 | bool connected; | ||
| 326 | ControllerType type; | ||
| 327 | ButtonsRaw buttons; | ||
| 328 | AnalogsRaw analogs; | ||
| 329 | |||
| 330 | u32 body_color_right; | ||
| 331 | u32 button_color_right; | ||
| 332 | u32 body_color_left; | ||
| 333 | u32 button_color_left; | ||
| 334 | }; | ||
| 335 | |||
| 336 | struct TouchscreenInput { | ||
| 337 | bool enabled; | ||
| 338 | std::string device; | ||
| 339 | |||
| 340 | u32 finger; | ||
| 341 | u32 diameter_x; | ||
| 342 | u32 diameter_y; | ||
| 343 | u32 rotation_angle; | ||
| 344 | }; | ||
| 345 | |||
| 114 | struct Values { | 346 | struct Values { |
| 115 | // System | 347 | // System |
| 116 | bool use_docked_mode; | 348 | bool use_docked_mode; |
| @@ -120,10 +352,22 @@ struct Values { | |||
| 120 | s32 language_index; | 352 | s32 language_index; |
| 121 | 353 | ||
| 122 | // Controls | 354 | // Controls |
| 123 | std::array<std::string, NativeButton::NumButtons> buttons; | 355 | std::array<PlayerInput, 10> players; |
| 124 | std::array<std::string, NativeAnalog::NumAnalogs> analogs; | 356 | |
| 357 | bool mouse_enabled; | ||
| 358 | std::string mouse_device; | ||
| 359 | MouseButtonsRaw mouse_buttons; | ||
| 360 | |||
| 361 | bool keyboard_enabled; | ||
| 362 | KeyboardKeysRaw keyboard_keys; | ||
| 363 | KeyboardModsRaw keyboard_mods; | ||
| 364 | |||
| 365 | bool debug_pad_enabled; | ||
| 366 | ButtonsRaw debug_pad_buttons; | ||
| 367 | AnalogsRaw debug_pad_analogs; | ||
| 368 | |||
| 125 | std::string motion_device; | 369 | std::string motion_device; |
| 126 | std::string touch_device; | 370 | TouchscreenInput touchscreen; |
| 127 | std::atomic_bool is_device_reload_pending{true}; | 371 | std::atomic_bool is_device_reload_pending{true}; |
| 128 | 372 | ||
| 129 | // Core | 373 | // Core |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 6de07ea56..a04e00ecb 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -34,8 +34,8 @@ void Maxwell3D::InitializeRegisterDefaults() { | |||
| 34 | // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is | 34 | // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is |
| 35 | // needed for ARMS. | 35 | // needed for ARMS. |
| 36 | for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) { | 36 | for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) { |
| 37 | regs.viewport[viewport].depth_range_near = 0.0f; | 37 | regs.viewports[viewport].depth_range_near = 0.0f; |
| 38 | regs.viewport[viewport].depth_range_far = 1.0f; | 38 | regs.viewports[viewport].depth_range_far = 1.0f; |
| 39 | } | 39 | } |
| 40 | // Doom and Bomberman seems to use the uninitialized registers and just enable blend | 40 | // Doom and Bomberman seems to use the uninitialized registers and just enable blend |
| 41 | // so initialize blend registers with sane values | 41 | // so initialize blend registers with sane values |
| @@ -66,6 +66,9 @@ void Maxwell3D::InitializeRegisterDefaults() { | |||
| 66 | regs.stencil_back_func_func = Regs::ComparisonOp::Always; | 66 | regs.stencil_back_func_func = Regs::ComparisonOp::Always; |
| 67 | regs.stencil_back_func_mask = 0xFFFFFFFF; | 67 | regs.stencil_back_func_mask = 0xFFFFFFFF; |
| 68 | regs.stencil_back_mask = 0xFFFFFFFF; | 68 | regs.stencil_back_mask = 0xFFFFFFFF; |
| 69 | // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a | ||
| 70 | // register carrying a default value. Assume it's OpenGL's default (1). | ||
| 71 | regs.point_size = 1.0f; | ||
| 69 | } | 72 | } |
| 70 | 73 | ||
| 71 | void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { | 74 | void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 91ca57883..9e480dc39 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -480,6 +480,67 @@ public: | |||
| 480 | }; | 480 | }; |
| 481 | }; | 481 | }; |
| 482 | 482 | ||
| 483 | struct ViewportTransform { | ||
| 484 | f32 scale_x; | ||
| 485 | f32 scale_y; | ||
| 486 | f32 scale_z; | ||
| 487 | f32 translate_x; | ||
| 488 | f32 translate_y; | ||
| 489 | f32 translate_z; | ||
| 490 | INSERT_PADDING_WORDS(2); | ||
| 491 | |||
| 492 | MathUtil::Rectangle<s32> GetRect() const { | ||
| 493 | return { | ||
| 494 | GetX(), // left | ||
| 495 | GetY() + GetHeight(), // top | ||
| 496 | GetX() + GetWidth(), // right | ||
| 497 | GetY() // bottom | ||
| 498 | }; | ||
| 499 | }; | ||
| 500 | |||
| 501 | s32 GetX() const { | ||
| 502 | return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x))); | ||
| 503 | } | ||
| 504 | |||
| 505 | s32 GetY() const { | ||
| 506 | return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y))); | ||
| 507 | } | ||
| 508 | |||
| 509 | s32 GetWidth() const { | ||
| 510 | return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX(); | ||
| 511 | } | ||
| 512 | |||
| 513 | s32 GetHeight() const { | ||
| 514 | return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY(); | ||
| 515 | } | ||
| 516 | }; | ||
| 517 | |||
| 518 | struct ScissorTest { | ||
| 519 | u32 enable; | ||
| 520 | union { | ||
| 521 | BitField<0, 16, u32> min_x; | ||
| 522 | BitField<16, 16, u32> max_x; | ||
| 523 | }; | ||
| 524 | union { | ||
| 525 | BitField<0, 16, u32> min_y; | ||
| 526 | BitField<16, 16, u32> max_y; | ||
| 527 | }; | ||
| 528 | u32 fill; | ||
| 529 | }; | ||
| 530 | |||
| 531 | struct ViewPort { | ||
| 532 | union { | ||
| 533 | BitField<0, 16, u32> x; | ||
| 534 | BitField<16, 16, u32> width; | ||
| 535 | }; | ||
| 536 | union { | ||
| 537 | BitField<0, 16, u32> y; | ||
| 538 | BitField<16, 16, u32> height; | ||
| 539 | }; | ||
| 540 | float depth_range_near; | ||
| 541 | float depth_range_far; | ||
| 542 | }; | ||
| 543 | |||
| 483 | bool IsShaderConfigEnabled(std::size_t index) const { | 544 | bool IsShaderConfigEnabled(std::size_t index) const { |
| 484 | // The VertexB is always enabled. | 545 | // The VertexB is always enabled. |
| 485 | if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) { | 546 | if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) { |
| @@ -505,55 +566,11 @@ public: | |||
| 505 | 566 | ||
| 506 | INSERT_PADDING_WORDS(0x2E); | 567 | INSERT_PADDING_WORDS(0x2E); |
| 507 | 568 | ||
| 508 | RenderTargetConfig rt[NumRenderTargets]; | 569 | std::array<RenderTargetConfig, NumRenderTargets> rt; |
| 509 | |||
| 510 | struct { | ||
| 511 | f32 scale_x; | ||
| 512 | f32 scale_y; | ||
| 513 | f32 scale_z; | ||
| 514 | f32 translate_x; | ||
| 515 | f32 translate_y; | ||
| 516 | f32 translate_z; | ||
| 517 | INSERT_PADDING_WORDS(2); | ||
| 518 | |||
| 519 | MathUtil::Rectangle<s32> GetRect() const { | ||
| 520 | return { | ||
| 521 | GetX(), // left | ||
| 522 | GetY() + GetHeight(), // top | ||
| 523 | GetX() + GetWidth(), // right | ||
| 524 | GetY() // bottom | ||
| 525 | }; | ||
| 526 | }; | ||
| 527 | |||
| 528 | s32 GetX() const { | ||
| 529 | return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x))); | ||
| 530 | } | ||
| 531 | 570 | ||
| 532 | s32 GetY() const { | 571 | std::array<ViewportTransform, NumViewports> viewport_transform; |
| 533 | return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y))); | ||
| 534 | } | ||
| 535 | 572 | ||
| 536 | s32 GetWidth() const { | 573 | std::array<ViewPort, NumViewports> viewports; |
| 537 | return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX(); | ||
| 538 | } | ||
| 539 | |||
| 540 | s32 GetHeight() const { | ||
| 541 | return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY(); | ||
| 542 | } | ||
| 543 | } viewport_transform[NumViewports]; | ||
| 544 | |||
| 545 | struct { | ||
| 546 | union { | ||
| 547 | BitField<0, 16, u32> x; | ||
| 548 | BitField<16, 16, u32> width; | ||
| 549 | }; | ||
| 550 | union { | ||
| 551 | BitField<0, 16, u32> y; | ||
| 552 | BitField<16, 16, u32> height; | ||
| 553 | }; | ||
| 554 | float depth_range_near; | ||
| 555 | float depth_range_far; | ||
| 556 | } viewport[NumViewports]; | ||
| 557 | 574 | ||
| 558 | INSERT_PADDING_WORDS(0x1D); | 575 | INSERT_PADDING_WORDS(0x1D); |
| 559 | 576 | ||
| @@ -571,19 +588,9 @@ public: | |||
| 571 | 588 | ||
| 572 | INSERT_PADDING_WORDS(0x17); | 589 | INSERT_PADDING_WORDS(0x17); |
| 573 | 590 | ||
| 574 | struct { | 591 | std::array<ScissorTest, NumViewports> scissor_test; |
| 575 | u32 enable; | ||
| 576 | union { | ||
| 577 | BitField<0, 16, u32> min_x; | ||
| 578 | BitField<16, 16, u32> max_x; | ||
| 579 | }; | ||
| 580 | union { | ||
| 581 | BitField<0, 16, u32> min_y; | ||
| 582 | BitField<16, 16, u32> max_y; | ||
| 583 | }; | ||
| 584 | } scissor_test; | ||
| 585 | 592 | ||
| 586 | INSERT_PADDING_WORDS(0x52); | 593 | INSERT_PADDING_WORDS(0x15); |
| 587 | 594 | ||
| 588 | s32 stencil_back_func_ref; | 595 | s32 stencil_back_func_ref; |
| 589 | u32 stencil_back_mask; | 596 | u32 stencil_back_mask; |
| @@ -700,7 +707,9 @@ public: | |||
| 700 | u32 stencil_front_func_mask; | 707 | u32 stencil_front_func_mask; |
| 701 | u32 stencil_front_mask; | 708 | u32 stencil_front_mask; |
| 702 | 709 | ||
| 703 | INSERT_PADDING_WORDS(0x3); | 710 | INSERT_PADDING_WORDS(0x2); |
| 711 | |||
| 712 | u32 frag_color_clamp; | ||
| 704 | 713 | ||
| 705 | union { | 714 | union { |
| 706 | BitField<4, 1, u32> triangle_rast_flip; | 715 | BitField<4, 1, u32> triangle_rast_flip; |
| @@ -718,7 +727,12 @@ public: | |||
| 718 | 727 | ||
| 719 | u32 zeta_enable; | 728 | u32 zeta_enable; |
| 720 | 729 | ||
| 721 | INSERT_PADDING_WORDS(0x8); | 730 | union { |
| 731 | BitField<0, 1, u32> alpha_to_coverage; | ||
| 732 | BitField<4, 1, u32> alpha_to_one; | ||
| 733 | } multisample_control; | ||
| 734 | |||
| 735 | INSERT_PADDING_WORDS(0x7); | ||
| 722 | 736 | ||
| 723 | struct { | 737 | struct { |
| 724 | u32 tsc_address_high; | 738 | u32 tsc_address_high; |
| @@ -1100,8 +1114,8 @@ private: | |||
| 1100 | ASSERT_REG_POSITION(macros, 0x45); | 1114 | ASSERT_REG_POSITION(macros, 0x45); |
| 1101 | ASSERT_REG_POSITION(tfb_enabled, 0x1D1); | 1115 | ASSERT_REG_POSITION(tfb_enabled, 0x1D1); |
| 1102 | ASSERT_REG_POSITION(rt, 0x200); | 1116 | ASSERT_REG_POSITION(rt, 0x200); |
| 1103 | ASSERT_REG_POSITION(viewport_transform[0], 0x280); | 1117 | ASSERT_REG_POSITION(viewport_transform, 0x280); |
| 1104 | ASSERT_REG_POSITION(viewport, 0x300); | 1118 | ASSERT_REG_POSITION(viewports, 0x300); |
| 1105 | ASSERT_REG_POSITION(vertex_buffer, 0x35D); | 1119 | ASSERT_REG_POSITION(vertex_buffer, 0x35D); |
| 1106 | ASSERT_REG_POSITION(clear_color[0], 0x360); | 1120 | ASSERT_REG_POSITION(clear_color[0], 0x360); |
| 1107 | ASSERT_REG_POSITION(clear_depth, 0x364); | 1121 | ASSERT_REG_POSITION(clear_depth, 0x364); |
| @@ -1136,10 +1150,12 @@ ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4); | |||
| 1136 | ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5); | 1150 | ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5); |
| 1137 | ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6); | 1151 | ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6); |
| 1138 | ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); | 1152 | ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); |
| 1153 | ASSERT_REG_POSITION(frag_color_clamp, 0x4EA); | ||
| 1139 | ASSERT_REG_POSITION(screen_y_control, 0x4EB); | 1154 | ASSERT_REG_POSITION(screen_y_control, 0x4EB); |
| 1140 | ASSERT_REG_POSITION(vb_element_base, 0x50D); | 1155 | ASSERT_REG_POSITION(vb_element_base, 0x50D); |
| 1141 | ASSERT_REG_POSITION(point_size, 0x546); | 1156 | ASSERT_REG_POSITION(point_size, 0x546); |
| 1142 | ASSERT_REG_POSITION(zeta_enable, 0x54E); | 1157 | ASSERT_REG_POSITION(zeta_enable, 0x54E); |
| 1158 | ASSERT_REG_POSITION(multisample_control, 0x54F); | ||
| 1143 | ASSERT_REG_POSITION(tsc, 0x557); | 1159 | ASSERT_REG_POSITION(tsc, 0x557); |
| 1144 | ASSERT_REG_POSITION(tic, 0x55D); | 1160 | ASSERT_REG_POSITION(tic, 0x55D); |
| 1145 | ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); | 1161 | ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 54cc47a9b..ae6aaee4c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -107,8 +107,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo | |||
| 107 | 107 | ||
| 108 | ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported"); | 108 | ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported"); |
| 109 | OpenGLState::ApplyDefaultState(); | 109 | OpenGLState::ApplyDefaultState(); |
| 110 | // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 | ||
| 111 | state.clip_distance[0] = true; | ||
| 112 | 110 | ||
| 113 | // Create render framebuffer | 111 | // Create render framebuffer |
| 114 | framebuffer.Create(); | 112 | framebuffer.Create(); |
| @@ -582,6 +580,8 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 582 | 580 | ||
| 583 | ConfigureFramebuffers(state); | 581 | ConfigureFramebuffers(state); |
| 584 | SyncColorMask(); | 582 | SyncColorMask(); |
| 583 | SyncFragmentColorClampState(); | ||
| 584 | SyncMultiSampleState(); | ||
| 585 | SyncDepthTestState(); | 585 | SyncDepthTestState(); |
| 586 | SyncStencilTestState(); | 586 | SyncStencilTestState(); |
| 587 | SyncBlendState(); | 587 | SyncBlendState(); |
| @@ -642,7 +642,7 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 642 | params.DispatchDraw(); | 642 | params.DispatchDraw(); |
| 643 | 643 | ||
| 644 | // Disable scissor test | 644 | // Disable scissor test |
| 645 | state.scissor.enabled = false; | 645 | state.viewports[0].scissor.enabled = false; |
| 646 | 646 | ||
| 647 | accelerate_draw = AccelDraw::Disabled; | 647 | accelerate_draw = AccelDraw::Disabled; |
| 648 | 648 | ||
| @@ -733,9 +733,8 @@ void RasterizerOpenGL::SamplerInfo::Create() { | |||
| 733 | glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); | 733 | glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); |
| 734 | } | 734 | } |
| 735 | 735 | ||
| 736 | void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) { | 736 | void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { |
| 737 | const GLuint s = sampler.handle; | 737 | const GLuint s = sampler.handle; |
| 738 | const Tegra::Texture::TSCEntry& config = info.tsc; | ||
| 739 | if (mag_filter != config.mag_filter) { | 738 | if (mag_filter != config.mag_filter) { |
| 740 | mag_filter = config.mag_filter; | 739 | mag_filter = config.mag_filter; |
| 741 | glSamplerParameteri( | 740 | glSamplerParameteri( |
| @@ -777,30 +776,50 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTex | |||
| 777 | MaxwellToGL::DepthCompareFunc(depth_compare_func)); | 776 | MaxwellToGL::DepthCompareFunc(depth_compare_func)); |
| 778 | } | 777 | } |
| 779 | 778 | ||
| 780 | if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border || | 779 | GLvec4 new_border_color; |
| 781 | wrap_p == Tegra::Texture::WrapMode::Border) { | 780 | if (config.srgb_conversion) { |
| 782 | const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g, | 781 | new_border_color[0] = config.srgb_border_color_r / 255.0f; |
| 783 | config.border_color_b, config.border_color_a}}; | 782 | new_border_color[1] = config.srgb_border_color_g / 255.0f; |
| 784 | if (border_color != new_border_color) { | 783 | new_border_color[2] = config.srgb_border_color_g / 255.0f; |
| 785 | border_color = new_border_color; | 784 | } else { |
| 786 | glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data()); | 785 | new_border_color[0] = config.border_color_r; |
| 787 | } | 786 | new_border_color[1] = config.border_color_g; |
| 787 | new_border_color[2] = config.border_color_b; | ||
| 788 | } | 788 | } |
| 789 | if (info.tic.use_header_opt_control == 0) { | 789 | new_border_color[3] = config.border_color_a; |
| 790 | |||
| 791 | if (border_color != new_border_color) { | ||
| 792 | border_color = new_border_color; | ||
| 793 | glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data()); | ||
| 794 | } | ||
| 795 | |||
| 796 | const float anisotropic_max = static_cast<float>(1 << config.max_anisotropy.Value()); | ||
| 797 | if (anisotropic_max != max_anisotropic) { | ||
| 798 | max_anisotropic = anisotropic_max; | ||
| 790 | if (GLAD_GL_ARB_texture_filter_anisotropic) { | 799 | if (GLAD_GL_ARB_texture_filter_anisotropic) { |
| 791 | glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, | 800 | glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropic); |
| 792 | static_cast<float>(1 << info.tic.max_anisotropy.Value())); | ||
| 793 | } else if (GLAD_GL_EXT_texture_filter_anisotropic) { | 801 | } else if (GLAD_GL_EXT_texture_filter_anisotropic) { |
| 794 | glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, | 802 | glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropic); |
| 795 | static_cast<float>(1 << info.tic.max_anisotropy.Value())); | ||
| 796 | } | 803 | } |
| 797 | glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, | 804 | } |
| 798 | static_cast<float>(info.tic.res_min_mip_level.Value())); | 805 | const float lod_min = static_cast<float>(config.min_lod_clamp.Value()) / 256.0f; |
| 799 | glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, | 806 | if (lod_min != min_lod) { |
| 800 | static_cast<float>(info.tic.res_max_mip_level.Value() == 0 | 807 | min_lod = lod_min; |
| 801 | ? 16 | 808 | glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, min_lod); |
| 802 | : info.tic.res_max_mip_level.Value())); | 809 | } |
| 803 | glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, info.tic.mip_lod_bias.Value() / 256.f); | 810 | |
| 811 | const float lod_max = static_cast<float>(config.max_lod_clamp.Value()) / 256.0f; | ||
| 812 | if (lod_max != max_lod) { | ||
| 813 | max_lod = lod_max; | ||
| 814 | glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, max_lod); | ||
| 815 | } | ||
| 816 | const u32 bias = config.mip_lod_bias.Value(); | ||
| 817 | // Sign extend the 13-bit value. | ||
| 818 | const u32 mask = 1U << (13 - 1); | ||
| 819 | const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f; | ||
| 820 | if (lod_bias != bias_lod) { | ||
| 821 | lod_bias = bias_lod; | ||
| 822 | glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias); | ||
| 804 | } | 823 | } |
| 805 | } | 824 | } |
| 806 | 825 | ||
| @@ -899,7 +918,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, | |||
| 899 | continue; | 918 | continue; |
| 900 | } | 919 | } |
| 901 | 920 | ||
| 902 | texture_samplers[current_bindpoint].SyncWithConfig(texture); | 921 | texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); |
| 903 | Surface surface = res_cache.GetTextureSurface(texture, entry); | 922 | Surface surface = res_cache.GetTextureSurface(texture, entry); |
| 904 | if (surface != nullptr) { | 923 | if (surface != nullptr) { |
| 905 | state.texture_units[current_bindpoint].texture = surface->Texture().handle; | 924 | state.texture_units[current_bindpoint].texture = surface->Texture().handle; |
| @@ -923,15 +942,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, | |||
| 923 | 942 | ||
| 924 | void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { | 943 | void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { |
| 925 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 944 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 926 | for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { | 945 | for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) { |
| 927 | const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()}; | 946 | const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()}; |
| 928 | auto& viewport = current_state.viewports[i]; | 947 | auto& viewport = current_state.viewports[i]; |
| 929 | viewport.x = viewport_rect.left; | 948 | viewport.x = viewport_rect.left; |
| 930 | viewport.y = viewport_rect.bottom; | 949 | viewport.y = viewport_rect.bottom; |
| 931 | viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth()); | 950 | viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth()); |
| 932 | viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight()); | 951 | viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight()); |
| 933 | viewport.depth_range_far = regs.viewport[i].depth_range_far; | 952 | viewport.depth_range_far = regs.viewports[i].depth_range_far; |
| 934 | viewport.depth_range_near = regs.viewport[i].depth_range_near; | 953 | viewport.depth_range_near = regs.viewports[i].depth_range_near; |
| 935 | } | 954 | } |
| 936 | } | 955 | } |
| 937 | 956 | ||
| @@ -1022,7 +1041,9 @@ void RasterizerOpenGL::SyncStencilTestState() { | |||
| 1022 | 1041 | ||
| 1023 | void RasterizerOpenGL::SyncColorMask() { | 1042 | void RasterizerOpenGL::SyncColorMask() { |
| 1024 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 1043 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 1025 | for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { | 1044 | const std::size_t count = |
| 1045 | regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1; | ||
| 1046 | for (std::size_t i = 0; i < count; i++) { | ||
| 1026 | const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i]; | 1047 | const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i]; |
| 1027 | auto& dest = state.color_mask[i]; | 1048 | auto& dest = state.color_mask[i]; |
| 1028 | dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE; | 1049 | dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE; |
| @@ -1032,6 +1053,17 @@ void RasterizerOpenGL::SyncColorMask() { | |||
| 1032 | } | 1053 | } |
| 1033 | } | 1054 | } |
| 1034 | 1055 | ||
| 1056 | void RasterizerOpenGL::SyncMultiSampleState() { | ||
| 1057 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | ||
| 1058 | state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0; | ||
| 1059 | state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0; | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | void RasterizerOpenGL::SyncFragmentColorClampState() { | ||
| 1063 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | ||
| 1064 | state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0; | ||
| 1065 | } | ||
| 1066 | |||
| 1035 | void RasterizerOpenGL::SyncBlendState() { | 1067 | void RasterizerOpenGL::SyncBlendState() { |
| 1036 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 1068 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 1037 | 1069 | ||
| @@ -1043,43 +1075,40 @@ void RasterizerOpenGL::SyncBlendState() { | |||
| 1043 | state.independant_blend.enabled = regs.independent_blend_enable; | 1075 | state.independant_blend.enabled = regs.independent_blend_enable; |
| 1044 | if (!state.independant_blend.enabled) { | 1076 | if (!state.independant_blend.enabled) { |
| 1045 | auto& blend = state.blend[0]; | 1077 | auto& blend = state.blend[0]; |
| 1046 | blend.enabled = regs.blend.enable[0] != 0; | 1078 | const auto& src = regs.blend; |
| 1047 | blend.separate_alpha = regs.blend.separate_alpha; | 1079 | blend.enabled = src.enable[0] != 0; |
| 1048 | blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb); | 1080 | if (blend.enabled) { |
| 1049 | blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb); | 1081 | blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb); |
| 1050 | blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb); | 1082 | blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb); |
| 1051 | if (blend.separate_alpha) { | 1083 | blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb); |
| 1052 | blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a); | 1084 | blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a); |
| 1053 | blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a); | 1085 | blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a); |
| 1054 | blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a); | 1086 | blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a); |
| 1055 | } | 1087 | } |
| 1056 | for (size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { | 1088 | for (std::size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { |
| 1057 | state.blend[i].enabled = false; | 1089 | state.blend[i].enabled = false; |
| 1058 | } | 1090 | } |
| 1059 | return; | 1091 | return; |
| 1060 | } | 1092 | } |
| 1061 | 1093 | ||
| 1062 | for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { | 1094 | for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { |
| 1063 | auto& blend = state.blend[i]; | 1095 | auto& blend = state.blend[i]; |
| 1096 | const auto& src = regs.independent_blend[i]; | ||
| 1064 | blend.enabled = regs.blend.enable[i] != 0; | 1097 | blend.enabled = regs.blend.enable[i] != 0; |
| 1065 | if (!blend.enabled) | 1098 | if (!blend.enabled) |
| 1066 | continue; | 1099 | continue; |
| 1067 | blend.separate_alpha = regs.independent_blend[i].separate_alpha; | 1100 | blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb); |
| 1068 | blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_rgb); | 1101 | blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb); |
| 1069 | blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_rgb); | 1102 | blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb); |
| 1070 | blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_rgb); | 1103 | blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a); |
| 1071 | if (blend.separate_alpha) { | 1104 | blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a); |
| 1072 | blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_a); | 1105 | blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a); |
| 1073 | blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_a); | ||
| 1074 | blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_a); | ||
| 1075 | } | ||
| 1076 | } | 1106 | } |
| 1077 | } | 1107 | } |
| 1078 | 1108 | ||
| 1079 | void RasterizerOpenGL::SyncLogicOpState() { | 1109 | void RasterizerOpenGL::SyncLogicOpState() { |
| 1080 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 1110 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 1081 | 1111 | ||
| 1082 | // TODO(Subv): Support more than just render target 0. | ||
| 1083 | state.logic_op.enabled = regs.logic_op.enable != 0; | 1112 | state.logic_op.enabled = regs.logic_op.enable != 0; |
| 1084 | 1113 | ||
| 1085 | if (!state.logic_op.enabled) | 1114 | if (!state.logic_op.enabled) |
| @@ -1092,19 +1121,21 @@ void RasterizerOpenGL::SyncLogicOpState() { | |||
| 1092 | } | 1121 | } |
| 1093 | 1122 | ||
| 1094 | void RasterizerOpenGL::SyncScissorTest() { | 1123 | void RasterizerOpenGL::SyncScissorTest() { |
| 1095 | // TODO: what is the correct behavior here, a single scissor for all targets | ||
| 1096 | // or scissor disabled for the rest of the targets? | ||
| 1097 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 1124 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 1098 | state.scissor.enabled = (regs.scissor_test.enable != 0); | 1125 | for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) { |
| 1099 | if (regs.scissor_test.enable == 0) { | 1126 | const auto& src = regs.scissor_test[i]; |
| 1100 | return; | 1127 | auto& dst = state.viewports[i].scissor; |
| 1128 | dst.enabled = (src.enable != 0); | ||
| 1129 | if (dst.enabled == 0) { | ||
| 1130 | return; | ||
| 1131 | } | ||
| 1132 | const u32 width = src.max_x - src.min_x; | ||
| 1133 | const u32 height = src.max_y - src.min_y; | ||
| 1134 | dst.x = src.min_x; | ||
| 1135 | dst.y = src.min_y; | ||
| 1136 | dst.width = width; | ||
| 1137 | dst.height = height; | ||
| 1101 | } | 1138 | } |
| 1102 | const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x; | ||
| 1103 | const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y; | ||
| 1104 | state.scissor.x = regs.scissor_test.min_x; | ||
| 1105 | state.scissor.y = regs.scissor_test.min_y; | ||
| 1106 | state.scissor.width = width; | ||
| 1107 | state.scissor.height = height; | ||
| 1108 | } | 1139 | } |
| 1109 | 1140 | ||
| 1110 | void RasterizerOpenGL::SyncTransformFeedback() { | 1141 | void RasterizerOpenGL::SyncTransformFeedback() { |
| @@ -1118,11 +1149,7 @@ void RasterizerOpenGL::SyncTransformFeedback() { | |||
| 1118 | 1149 | ||
| 1119 | void RasterizerOpenGL::SyncPointState() { | 1150 | void RasterizerOpenGL::SyncPointState() { |
| 1120 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 1151 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 1121 | 1152 | state.point.size = regs.point_size; | |
| 1122 | // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a | ||
| 1123 | // register carrying a default value. For now, if the point size is zero, assume it's | ||
| 1124 | // OpenGL's default (1). | ||
| 1125 | state.point.size = regs.point_size == 0 ? 1 : regs.point_size; | ||
| 1126 | } | 1153 | } |
| 1127 | 1154 | ||
| 1128 | void RasterizerOpenGL::CheckAlphaTests() { | 1155 | void RasterizerOpenGL::CheckAlphaTests() { |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 8ef0f6c12..6e78ab4cd 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -88,7 +88,7 @@ private: | |||
| 88 | /// SamplerInfo struct. | 88 | /// SamplerInfo struct. |
| 89 | void Create(); | 89 | void Create(); |
| 90 | /// Syncs the sampler object with the config, updating any necessary state. | 90 | /// Syncs the sampler object with the config, updating any necessary state. |
| 91 | void SyncWithConfig(const Tegra::Texture::FullTextureInfo& info); | 91 | void SyncWithConfig(const Tegra::Texture::TSCEntry& info); |
| 92 | 92 | ||
| 93 | private: | 93 | private: |
| 94 | Tegra::Texture::TextureFilter mag_filter; | 94 | Tegra::Texture::TextureFilter mag_filter; |
| @@ -100,6 +100,10 @@ private: | |||
| 100 | bool uses_depth_compare; | 100 | bool uses_depth_compare; |
| 101 | Tegra::Texture::DepthCompareFunc depth_compare_func; | 101 | Tegra::Texture::DepthCompareFunc depth_compare_func; |
| 102 | GLvec4 border_color; | 102 | GLvec4 border_color; |
| 103 | float min_lod; | ||
| 104 | float max_lod; | ||
| 105 | float lod_bias; | ||
| 106 | float max_anisotropic; | ||
| 103 | }; | 107 | }; |
| 104 | 108 | ||
| 105 | /** | 109 | /** |
| @@ -160,6 +164,12 @@ private: | |||
| 160 | /// Syncs the LogicOp state to match the guest state | 164 | /// Syncs the LogicOp state to match the guest state |
| 161 | void SyncLogicOpState(); | 165 | void SyncLogicOpState(); |
| 162 | 166 | ||
| 167 | /// Syncs the the color clamp state | ||
| 168 | void SyncFragmentColorClampState(); | ||
| 169 | |||
| 170 | /// Syncs the alpha coverage and alpha to one | ||
| 171 | void SyncMultiSampleState(); | ||
| 172 | |||
| 163 | /// Syncs the scissor test state to match the guest state | 173 | /// Syncs the scissor test state to match the guest state |
| 164 | void SyncScissorTest(); | 174 | void SyncScissorTest(); |
| 165 | 175 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index b44ecfa1c..9ca82c06c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -381,11 +381,8 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d | |||
| 381 | const u32 tile_size_y{GetDefaultBlockHeight(format)}; | 381 | const u32 tile_size_y{GetDefaultBlockHeight(format)}; |
| 382 | 382 | ||
| 383 | if (morton_to_gl) { | 383 | if (morton_to_gl) { |
| 384 | const std::vector<u8> data = | 384 | Tegra::Texture::UnswizzleTexture(gl_buffer, addr, tile_size_x, tile_size_y, bytes_per_pixel, |
| 385 | Tegra::Texture::UnswizzleTexture(addr, tile_size_x, tile_size_y, bytes_per_pixel, | 385 | stride, height, depth, block_height, block_depth); |
| 386 | stride, height, depth, block_height, block_depth); | ||
| 387 | const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; | ||
| 388 | memcpy(gl_buffer, data.data(), size_to_copy); | ||
| 389 | } else { | 386 | } else { |
| 390 | Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x, | 387 | Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x, |
| 391 | (height + tile_size_y - 1) / tile_size_y, depth, | 388 | (height + tile_size_y - 1) / tile_size_y, depth, |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 2a069cdd8..9a5d7e289 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h | |||
| @@ -67,6 +67,7 @@ public: | |||
| 67 | glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs); | 67 | glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs); |
| 68 | state.draw.shader_program = 0; | 68 | state.draw.shader_program = 0; |
| 69 | state.draw.program_pipeline = pipeline.handle; | 69 | state.draw.program_pipeline = pipeline.handle; |
| 70 | state.geometry_shaders.enabled = (gs != 0); | ||
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | private: | 73 | private: |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 98622a058..d9910c6e8 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -14,7 +14,10 @@ OpenGLState OpenGLState::cur_state; | |||
| 14 | bool OpenGLState::s_rgb_used; | 14 | bool OpenGLState::s_rgb_used; |
| 15 | OpenGLState::OpenGLState() { | 15 | OpenGLState::OpenGLState() { |
| 16 | // These all match default OpenGL values | 16 | // These all match default OpenGL values |
| 17 | geometry_shaders.enabled = false; | ||
| 17 | framebuffer_srgb.enabled = false; | 18 | framebuffer_srgb.enabled = false; |
| 19 | multisample_control.alpha_to_coverage = false; | ||
| 20 | multisample_control.alpha_to_one = false; | ||
| 18 | cull.enabled = false; | 21 | cull.enabled = false; |
| 19 | cull.mode = GL_BACK; | 22 | cull.mode = GL_BACK; |
| 20 | cull.front_face = GL_CCW; | 23 | cull.front_face = GL_CCW; |
| @@ -50,12 +53,12 @@ OpenGLState::OpenGLState() { | |||
| 50 | item.height = 0; | 53 | item.height = 0; |
| 51 | item.depth_range_near = 0.0f; | 54 | item.depth_range_near = 0.0f; |
| 52 | item.depth_range_far = 1.0f; | 55 | item.depth_range_far = 1.0f; |
| 56 | item.scissor.enabled = false; | ||
| 57 | item.scissor.x = 0; | ||
| 58 | item.scissor.y = 0; | ||
| 59 | item.scissor.width = 0; | ||
| 60 | item.scissor.height = 0; | ||
| 53 | } | 61 | } |
| 54 | scissor.enabled = false; | ||
| 55 | scissor.x = 0; | ||
| 56 | scissor.y = 0; | ||
| 57 | scissor.width = 0; | ||
| 58 | scissor.height = 0; | ||
| 59 | for (auto& item : blend) { | 62 | for (auto& item : blend) { |
| 60 | item.enabled = true; | 63 | item.enabled = true; |
| 61 | item.rgb_equation = GL_FUNC_ADD; | 64 | item.rgb_equation = GL_FUNC_ADD; |
| @@ -88,6 +91,7 @@ OpenGLState::OpenGLState() { | |||
| 88 | clip_distance = {}; | 91 | clip_distance = {}; |
| 89 | 92 | ||
| 90 | point.size = 1; | 93 | point.size = 1; |
| 94 | fragment_color_clamp.enabled = false; | ||
| 91 | } | 95 | } |
| 92 | 96 | ||
| 93 | void OpenGLState::ApplyDefaultState() { | 97 | void OpenGLState::ApplyDefaultState() { |
| @@ -136,7 +140,7 @@ void OpenGLState::ApplyCulling() const { | |||
| 136 | } | 140 | } |
| 137 | 141 | ||
| 138 | void OpenGLState::ApplyColorMask() const { | 142 | void OpenGLState::ApplyColorMask() const { |
| 139 | if (GLAD_GL_ARB_viewport_array) { | 143 | if (GLAD_GL_ARB_viewport_array && independant_blend.enabled) { |
| 140 | for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { | 144 | for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { |
| 141 | const auto& updated = color_mask[i]; | 145 | const auto& updated = color_mask[i]; |
| 142 | const auto& current = cur_state.color_mask[i]; | 146 | const auto& current = cur_state.color_mask[i]; |
| @@ -230,26 +234,10 @@ void OpenGLState::ApplyStencilTest() const { | |||
| 230 | } | 234 | } |
| 231 | } | 235 | } |
| 232 | 236 | ||
| 233 | void OpenGLState::ApplyScissor() const { | ||
| 234 | const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled; | ||
| 235 | if (scissor_changed) { | ||
| 236 | if (scissor.enabled) { | ||
| 237 | glEnable(GL_SCISSOR_TEST); | ||
| 238 | } else { | ||
| 239 | glDisable(GL_SCISSOR_TEST); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | if (scissor.enabled && | ||
| 243 | (scissor_changed || scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y || | ||
| 244 | scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height)) { | ||
| 245 | glScissor(scissor.x, scissor.y, scissor.width, scissor.height); | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | void OpenGLState::ApplyViewport() const { | 237 | void OpenGLState::ApplyViewport() const { |
| 250 | if (GLAD_GL_ARB_viewport_array) { | 238 | if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) { |
| 251 | for (GLuint i = 0; | 239 | for (GLuint i = 0; i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumViewports); |
| 252 | i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); i++) { | 240 | i++) { |
| 253 | const auto& current = cur_state.viewports[i]; | 241 | const auto& current = cur_state.viewports[i]; |
| 254 | const auto& updated = viewports[i]; | 242 | const auto& updated = viewports[i]; |
| 255 | if (updated.x != current.x || updated.y != current.y || | 243 | if (updated.x != current.x || updated.y != current.y || |
| @@ -260,6 +248,22 @@ void OpenGLState::ApplyViewport() const { | |||
| 260 | updated.depth_range_far != current.depth_range_far) { | 248 | updated.depth_range_far != current.depth_range_far) { |
| 261 | glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far); | 249 | glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far); |
| 262 | } | 250 | } |
| 251 | const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled; | ||
| 252 | if (scissor_changed) { | ||
| 253 | if (updated.scissor.enabled) { | ||
| 254 | glEnablei(GL_SCISSOR_TEST, i); | ||
| 255 | } else { | ||
| 256 | glDisablei(GL_SCISSOR_TEST, i); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | if (updated.scissor.enabled && | ||
| 260 | (scissor_changed || updated.scissor.x != current.scissor.x || | ||
| 261 | updated.scissor.y != current.scissor.y || | ||
| 262 | updated.scissor.width != current.scissor.width || | ||
| 263 | updated.scissor.height != current.scissor.height)) { | ||
| 264 | glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width, | ||
| 265 | updated.scissor.height); | ||
| 266 | } | ||
| 263 | } | 267 | } |
| 264 | } else { | 268 | } else { |
| 265 | const auto& current = cur_state.viewports[0]; | 269 | const auto& current = cur_state.viewports[0]; |
| @@ -273,6 +277,21 @@ void OpenGLState::ApplyViewport() const { | |||
| 273 | updated.depth_range_far != current.depth_range_far) { | 277 | updated.depth_range_far != current.depth_range_far) { |
| 274 | glDepthRange(updated.depth_range_near, updated.depth_range_far); | 278 | glDepthRange(updated.depth_range_near, updated.depth_range_far); |
| 275 | } | 279 | } |
| 280 | const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled; | ||
| 281 | if (scissor_changed) { | ||
| 282 | if (updated.scissor.enabled) { | ||
| 283 | glEnable(GL_SCISSOR_TEST); | ||
| 284 | } else { | ||
| 285 | glDisable(GL_SCISSOR_TEST); | ||
| 286 | } | ||
| 287 | } | ||
| 288 | if (updated.scissor.enabled && (scissor_changed || updated.scissor.x != current.scissor.x || | ||
| 289 | updated.scissor.y != current.scissor.y || | ||
| 290 | updated.scissor.width != current.scissor.width || | ||
| 291 | updated.scissor.height != current.scissor.height)) { | ||
| 292 | glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width, | ||
| 293 | updated.scissor.height); | ||
| 294 | } | ||
| 276 | } | 295 | } |
| 277 | } | 296 | } |
| 278 | 297 | ||
| @@ -290,27 +309,16 @@ void OpenGLState::ApplyGlobalBlending() const { | |||
| 290 | if (!updated.enabled) { | 309 | if (!updated.enabled) { |
| 291 | return; | 310 | return; |
| 292 | } | 311 | } |
| 293 | if (updated.separate_alpha) { | 312 | if (blend_changed || updated.src_rgb_func != current.src_rgb_func || |
| 294 | if (blend_changed || updated.src_rgb_func != current.src_rgb_func || | 313 | updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func || |
| 295 | updated.dst_rgb_func != current.dst_rgb_func || | 314 | updated.dst_a_func != current.dst_a_func) { |
| 296 | updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) { | 315 | glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func, |
| 297 | glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func, | 316 | updated.dst_a_func); |
| 298 | updated.dst_a_func); | 317 | } |
| 299 | } | ||
| 300 | |||
| 301 | if (blend_changed || updated.rgb_equation != current.rgb_equation || | ||
| 302 | updated.a_equation != current.a_equation) { | ||
| 303 | glBlendEquationSeparate(updated.rgb_equation, updated.a_equation); | ||
| 304 | } | ||
| 305 | } else { | ||
| 306 | if (blend_changed || updated.src_rgb_func != current.src_rgb_func || | ||
| 307 | updated.dst_rgb_func != current.dst_rgb_func) { | ||
| 308 | glBlendFunc(updated.src_rgb_func, updated.dst_rgb_func); | ||
| 309 | } | ||
| 310 | 318 | ||
| 311 | if (blend_changed || updated.rgb_equation != current.rgb_equation) { | 319 | if (blend_changed || updated.rgb_equation != current.rgb_equation || |
| 312 | glBlendEquation(updated.rgb_equation); | 320 | updated.a_equation != current.a_equation) { |
| 313 | } | 321 | glBlendEquationSeparate(updated.rgb_equation, updated.a_equation); |
| 314 | } | 322 | } |
| 315 | } | 323 | } |
| 316 | 324 | ||
| @@ -328,29 +336,17 @@ void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const { | |||
| 328 | if (!updated.enabled) { | 336 | if (!updated.enabled) { |
| 329 | return; | 337 | return; |
| 330 | } | 338 | } |
| 331 | if (updated.separate_alpha) { | 339 | if (blend_changed || updated.src_rgb_func != current.src_rgb_func || |
| 332 | if (blend_changed || updated.src_rgb_func != current.src_rgb_func || | 340 | updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func || |
| 333 | updated.dst_rgb_func != current.dst_rgb_func || | 341 | updated.dst_a_func != current.dst_a_func) { |
| 334 | updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) { | 342 | glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func, |
| 335 | glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func, | 343 | updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); |
| 336 | updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func); | 344 | } |
| 337 | } | ||
| 338 | |||
| 339 | if (blend_changed || updated.rgb_equation != current.rgb_equation || | ||
| 340 | updated.a_equation != current.a_equation) { | ||
| 341 | glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation, | ||
| 342 | updated.a_equation); | ||
| 343 | } | ||
| 344 | } else { | ||
| 345 | if (blend_changed || updated.src_rgb_func != current.src_rgb_func || | ||
| 346 | updated.dst_rgb_func != current.dst_rgb_func) { | ||
| 347 | glBlendFunciARB(static_cast<GLuint>(target), updated.src_rgb_func, | ||
| 348 | updated.dst_rgb_func); | ||
| 349 | } | ||
| 350 | 345 | ||
| 351 | if (blend_changed || updated.rgb_equation != current.rgb_equation) { | 346 | if (blend_changed || updated.rgb_equation != current.rgb_equation || |
| 352 | glBlendEquationiARB(static_cast<GLuint>(target), updated.rgb_equation); | 347 | updated.a_equation != current.a_equation) { |
| 353 | } | 348 | glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation, |
| 349 | updated.a_equation); | ||
| 354 | } | 350 | } |
| 355 | } | 351 | } |
| 356 | 352 | ||
| @@ -481,9 +477,29 @@ void OpenGLState::Apply() const { | |||
| 481 | if (point.size != cur_state.point.size) { | 477 | if (point.size != cur_state.point.size) { |
| 482 | glPointSize(point.size); | 478 | glPointSize(point.size); |
| 483 | } | 479 | } |
| 480 | if (GLAD_GL_ARB_color_buffer_float) { | ||
| 481 | if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) { | ||
| 482 | glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB, | ||
| 483 | fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE); | ||
| 484 | } | ||
| 485 | } | ||
| 486 | if (multisample_control.alpha_to_coverage != cur_state.multisample_control.alpha_to_coverage) { | ||
| 487 | if (multisample_control.alpha_to_coverage) { | ||
| 488 | glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); | ||
| 489 | } else { | ||
| 490 | glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); | ||
| 491 | } | ||
| 492 | } | ||
| 493 | if (multisample_control.alpha_to_one != cur_state.multisample_control.alpha_to_one) { | ||
| 494 | if (multisample_control.alpha_to_one) { | ||
| 495 | glEnable(GL_SAMPLE_ALPHA_TO_ONE); | ||
| 496 | } else { | ||
| 497 | glDisable(GL_SAMPLE_ALPHA_TO_ONE); | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 484 | ApplyColorMask(); | 501 | ApplyColorMask(); |
| 485 | ApplyViewport(); | 502 | ApplyViewport(); |
| 486 | ApplyScissor(); | ||
| 487 | ApplyStencilTest(); | 503 | ApplyStencilTest(); |
| 488 | ApplySRgb(); | 504 | ApplySRgb(); |
| 489 | ApplyCulling(); | 505 | ApplyCulling(); |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index e5d1baae6..bdc743b0f 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -40,6 +40,19 @@ public: | |||
| 40 | } framebuffer_srgb; | 40 | } framebuffer_srgb; |
| 41 | 41 | ||
| 42 | struct { | 42 | struct { |
| 43 | bool alpha_to_coverage; // GL_ALPHA_TO_COVERAGE | ||
| 44 | bool alpha_to_one; // GL_ALPHA_TO_ONE | ||
| 45 | } multisample_control; | ||
| 46 | |||
| 47 | struct { | ||
| 48 | bool enabled; // GL_CLAMP_FRAGMENT_COLOR_ARB | ||
| 49 | } fragment_color_clamp; | ||
| 50 | |||
| 51 | struct { | ||
| 52 | bool enabled; // viewports arrays are only supported when geometry shaders are enabled. | ||
| 53 | } geometry_shaders; | ||
| 54 | |||
| 55 | struct { | ||
| 43 | bool enabled; // GL_CULL_FACE | 56 | bool enabled; // GL_CULL_FACE |
| 44 | GLenum mode; // GL_CULL_FACE_MODE | 57 | GLenum mode; // GL_CULL_FACE_MODE |
| 45 | GLenum front_face; // GL_FRONT_FACE | 58 | GLenum front_face; // GL_FRONT_FACE |
| @@ -79,7 +92,6 @@ public: | |||
| 79 | 92 | ||
| 80 | struct Blend { | 93 | struct Blend { |
| 81 | bool enabled; // GL_BLEND | 94 | bool enabled; // GL_BLEND |
| 82 | bool separate_alpha; // Independent blend enabled | ||
| 83 | GLenum rgb_equation; // GL_BLEND_EQUATION_RGB | 95 | GLenum rgb_equation; // GL_BLEND_EQUATION_RGB |
| 84 | GLenum a_equation; // GL_BLEND_EQUATION_ALPHA | 96 | GLenum a_equation; // GL_BLEND_EQUATION_ALPHA |
| 85 | GLenum src_rgb_func; // GL_BLEND_SRC_RGB | 97 | GLenum src_rgb_func; // GL_BLEND_SRC_RGB |
| @@ -150,16 +162,15 @@ public: | |||
| 150 | GLfloat height; | 162 | GLfloat height; |
| 151 | GLfloat depth_range_near; // GL_DEPTH_RANGE | 163 | GLfloat depth_range_near; // GL_DEPTH_RANGE |
| 152 | GLfloat depth_range_far; // GL_DEPTH_RANGE | 164 | GLfloat depth_range_far; // GL_DEPTH_RANGE |
| 165 | struct { | ||
| 166 | bool enabled; // GL_SCISSOR_TEST | ||
| 167 | GLint x; | ||
| 168 | GLint y; | ||
| 169 | GLsizei width; | ||
| 170 | GLsizei height; | ||
| 171 | } scissor; | ||
| 153 | }; | 172 | }; |
| 154 | std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> viewports; | 173 | std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports; |
| 155 | |||
| 156 | struct { | ||
| 157 | bool enabled; // GL_SCISSOR_TEST | ||
| 158 | GLint x; | ||
| 159 | GLint y; | ||
| 160 | GLsizei width; | ||
| 161 | GLsizei height; | ||
| 162 | } scissor; | ||
| 163 | 174 | ||
| 164 | struct { | 175 | struct { |
| 165 | float size; // GL_POINT_SIZE | 176 | float size; // GL_POINT_SIZE |
| @@ -214,7 +225,6 @@ private: | |||
| 214 | void ApplyLogicOp() const; | 225 | void ApplyLogicOp() const; |
| 215 | void ApplyTextures() const; | 226 | void ApplyTextures() const; |
| 216 | void ApplySamplers() const; | 227 | void ApplySamplers() const; |
| 217 | void ApplyScissor() const; | ||
| 218 | }; | 228 | }; |
| 219 | 229 | ||
| 220 | } // namespace OpenGL | 230 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 3ce2cc6d2..065b3929c 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -180,6 +180,12 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { | |||
| 180 | return GL_CLAMP_TO_BORDER; | 180 | return GL_CLAMP_TO_BORDER; |
| 181 | case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: | 181 | case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: |
| 182 | return GL_MIRROR_CLAMP_TO_EDGE; | 182 | return GL_MIRROR_CLAMP_TO_EDGE; |
| 183 | case Tegra::Texture::WrapMode::MirrorOnceBorder: | ||
| 184 | if (GL_EXT_texture_mirror_clamp) { | ||
| 185 | return GL_MIRROR_CLAMP_TO_BORDER_EXT; | ||
| 186 | } else { | ||
| 187 | return GL_MIRROR_CLAMP_TO_EDGE; | ||
| 188 | } | ||
| 183 | } | 189 | } |
| 184 | LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); | 190 | LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); |
| 185 | return GL_REPEAT; | 191 | return GL_REPEAT; |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 19f30b1b5..7eabd34f1 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -37,8 +37,14 @@ struct alignas(64) SwizzleTable { | |||
| 37 | std::array<std::array<u16, M>, N> values{}; | 37 | std::array<std::array<u16, M>, N> values{}; |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>(); | 40 | constexpr u32 gob_size_x = 64; |
| 41 | constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); | 41 | constexpr u32 gob_size_y = 8; |
| 42 | constexpr u32 gob_size_z = 1; | ||
| 43 | constexpr u32 gob_size = gob_size_x * gob_size_y * gob_size_z; | ||
| 44 | constexpr u32 fast_swizzle_align = 16; | ||
| 45 | |||
| 46 | constexpr auto legacy_swizzle_table = SwizzleTable<gob_size_y, gob_size_x, gob_size_z>(); | ||
| 47 | constexpr auto fast_swizzle_table = SwizzleTable<gob_size_y, 4, fast_swizzle_align>(); | ||
| 42 | 48 | ||
| 43 | /** | 49 | /** |
| 44 | * This function manages ALL the GOBs(Group of Bytes) Inside a single block. | 50 | * This function manages ALL the GOBs(Group of Bytes) Inside a single block. |
| @@ -52,10 +58,7 @@ void PreciseProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, con | |||
| 52 | const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) { | 58 | const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) { |
| 53 | std::array<u8*, 2> data_ptrs; | 59 | std::array<u8*, 2> data_ptrs; |
| 54 | u32 z_address = tile_offset; | 60 | u32 z_address = tile_offset; |
| 55 | const u32 gob_size_x = 64; | 61 | |
| 56 | const u32 gob_size_y = 8; | ||
| 57 | const u32 gob_size_z = 1; | ||
| 58 | const u32 gob_size = gob_size_x * gob_size_y * gob_size_z; | ||
| 59 | for (u32 z = z_start; z < z_end; z++) { | 62 | for (u32 z = z_start; z < z_end; z++) { |
| 60 | u32 y_address = z_address; | 63 | u32 y_address = z_address; |
| 61 | u32 pixel_base = layer_z * z + y_start * stride_x; | 64 | u32 pixel_base = layer_z * z + y_start * stride_x; |
| @@ -90,23 +93,19 @@ void FastProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const | |||
| 90 | u32 z_address = tile_offset; | 93 | u32 z_address = tile_offset; |
| 91 | const u32 x_startb = x_start * bytes_per_pixel; | 94 | const u32 x_startb = x_start * bytes_per_pixel; |
| 92 | const u32 x_endb = x_end * bytes_per_pixel; | 95 | const u32 x_endb = x_end * bytes_per_pixel; |
| 93 | constexpr u32 copy_size = 16; | 96 | |
| 94 | constexpr u32 gob_size_x = 64; | ||
| 95 | constexpr u32 gob_size_y = 8; | ||
| 96 | constexpr u32 gob_size_z = 1; | ||
| 97 | const u32 gob_size = gob_size_x * gob_size_y * gob_size_z; | ||
| 98 | for (u32 z = z_start; z < z_end; z++) { | 97 | for (u32 z = z_start; z < z_end; z++) { |
| 99 | u32 y_address = z_address; | 98 | u32 y_address = z_address; |
| 100 | u32 pixel_base = layer_z * z + y_start * stride_x; | 99 | u32 pixel_base = layer_z * z + y_start * stride_x; |
| 101 | for (u32 y = y_start; y < y_end; y++) { | 100 | for (u32 y = y_start; y < y_end; y++) { |
| 102 | const auto& table = fast_swizzle_table[y % gob_size_y]; | 101 | const auto& table = fast_swizzle_table[y % gob_size_y]; |
| 103 | for (u32 xb = x_startb; xb < x_endb; xb += copy_size) { | 102 | for (u32 xb = x_startb; xb < x_endb; xb += fast_swizzle_align) { |
| 104 | const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]}; | 103 | const u32 swizzle_offset{y_address + table[(xb / fast_swizzle_align) % 4]}; |
| 105 | const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel; | 104 | const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel; |
| 106 | const u32 pixel_index{out_x + pixel_base}; | 105 | const u32 pixel_index{out_x + pixel_base}; |
| 107 | data_ptrs[unswizzle] = swizzled_data + swizzle_offset; | 106 | data_ptrs[unswizzle] = swizzled_data + swizzle_offset; |
| 108 | data_ptrs[!unswizzle] = unswizzled_data + pixel_index; | 107 | data_ptrs[!unswizzle] = unswizzled_data + pixel_index; |
| 109 | std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); | 108 | std::memcpy(data_ptrs[0], data_ptrs[1], fast_swizzle_align); |
| 110 | } | 109 | } |
| 111 | pixel_base += stride_x; | 110 | pixel_base += stride_x; |
| 112 | if ((y + 1) % gob_size_y == 0) | 111 | if ((y + 1) % gob_size_y == 0) |
| @@ -132,17 +131,15 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool | |||
| 132 | auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); }; | 131 | auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); }; |
| 133 | const u32 stride_x = width * out_bytes_per_pixel; | 132 | const u32 stride_x = width * out_bytes_per_pixel; |
| 134 | const u32 layer_z = height * stride_x; | 133 | const u32 layer_z = height * stride_x; |
| 135 | constexpr u32 gob_x_bytes = 64; | 134 | const u32 gob_elements_x = gob_size_x / bytes_per_pixel; |
| 136 | const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel; | 135 | constexpr u32 gob_elements_y = gob_size_y; |
| 137 | constexpr u32 gob_elements_y = 8; | 136 | constexpr u32 gob_elements_z = gob_size_z; |
| 138 | constexpr u32 gob_elements_z = 1; | ||
| 139 | const u32 block_x_elements = gob_elements_x; | 137 | const u32 block_x_elements = gob_elements_x; |
| 140 | const u32 block_y_elements = gob_elements_y * block_height; | 138 | const u32 block_y_elements = gob_elements_y * block_height; |
| 141 | const u32 block_z_elements = gob_elements_z * block_depth; | 139 | const u32 block_z_elements = gob_elements_z * block_depth; |
| 142 | const u32 blocks_on_x = div_ceil(width, block_x_elements); | 140 | const u32 blocks_on_x = div_ceil(width, block_x_elements); |
| 143 | const u32 blocks_on_y = div_ceil(height, block_y_elements); | 141 | const u32 blocks_on_y = div_ceil(height, block_y_elements); |
| 144 | const u32 blocks_on_z = div_ceil(depth, block_z_elements); | 142 | const u32 blocks_on_z = div_ceil(depth, block_z_elements); |
| 145 | constexpr u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z; | ||
| 146 | const u32 xy_block_size = gob_size * block_height; | 143 | const u32 xy_block_size = gob_size * block_height; |
| 147 | const u32 block_size = xy_block_size * block_depth; | 144 | const u32 block_size = xy_block_size * block_depth; |
| 148 | u32 tile_offset = 0; | 145 | u32 tile_offset = 0; |
| @@ -173,7 +170,7 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool | |||
| 173 | void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, | 170 | void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, |
| 174 | u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data, | 171 | u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data, |
| 175 | bool unswizzle, u32 block_height, u32 block_depth) { | 172 | bool unswizzle, u32 block_height, u32 block_depth) { |
| 176 | if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { | 173 | if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % fast_swizzle_align == 0) { |
| 177 | SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth, | 174 | SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth, |
| 178 | bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth); | 175 | bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth); |
| 179 | } else { | 176 | } else { |
| @@ -229,29 +226,38 @@ u32 BytesPerPixel(TextureFormat format) { | |||
| 229 | } | 226 | } |
| 230 | } | 227 | } |
| 231 | 228 | ||
| 229 | void UnswizzleTexture(u8* const unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y, | ||
| 230 | u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, | ||
| 231 | u32 block_depth) { | ||
| 232 | CopySwizzledData((width + tile_size_x - 1) / tile_size_x, | ||
| 233 | (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel, | ||
| 234 | bytes_per_pixel, Memory::GetPointer(address), unswizzled_data, true, | ||
| 235 | block_height, block_depth); | ||
| 236 | } | ||
| 237 | |||
| 232 | std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, | 238 | std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, |
| 233 | u32 bytes_per_pixel, u32 width, u32 height, u32 depth, | 239 | u32 bytes_per_pixel, u32 width, u32 height, u32 depth, |
| 234 | u32 block_height, u32 block_depth) { | 240 | u32 block_height, u32 block_depth) { |
| 235 | std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel); | 241 | std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel); |
| 236 | CopySwizzledData((width + tile_size_x - 1) / tile_size_x, | 242 | UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel, |
| 237 | (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel, | 243 | width, height, depth, block_height, block_depth); |
| 238 | bytes_per_pixel, Memory::GetPointer(address), unswizzled_data.data(), true, | ||
| 239 | block_height, block_depth); | ||
| 240 | return unswizzled_data; | 244 | return unswizzled_data; |
| 241 | } | 245 | } |
| 242 | 246 | ||
| 243 | void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, | 247 | void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, |
| 244 | u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, | 248 | u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data, |
| 245 | u32 block_height) { | 249 | u32 block_height) { |
| 246 | const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64}; | 250 | const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) / |
| 251 | gob_size_x}; | ||
| 247 | for (u32 line = 0; line < subrect_height; ++line) { | 252 | for (u32 line = 0; line < subrect_height; ++line) { |
| 248 | const u32 gob_address_y = | 253 | const u32 gob_address_y = |
| 249 | (line / (8 * block_height)) * 512 * block_height * image_width_in_gobs + | 254 | (line / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs + |
| 250 | (line % (8 * block_height) / 8) * 512; | 255 | ((line % (gob_size_y * block_height)) / gob_size_y) * gob_size; |
| 251 | const auto& table = legacy_swizzle_table[line % 8]; | 256 | const auto& table = legacy_swizzle_table[line % gob_size_y]; |
| 252 | for (u32 x = 0; x < subrect_width; ++x) { | 257 | for (u32 x = 0; x < subrect_width; ++x) { |
| 253 | const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height; | 258 | const u32 gob_address = |
| 254 | const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64]; | 259 | gob_address_y + (x * bytes_per_pixel / gob_size_x) * gob_size * block_height; |
| 260 | const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % gob_size_x]; | ||
| 255 | const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel; | 261 | const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel; |
| 256 | const VAddr dest_addr = swizzled_data + swizzled_offset; | 262 | const VAddr dest_addr = swizzled_data + swizzled_offset; |
| 257 | 263 | ||
| @@ -265,13 +271,13 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 | |||
| 265 | u32 block_height, u32 offset_x, u32 offset_y) { | 271 | u32 block_height, u32 offset_x, u32 offset_y) { |
| 266 | for (u32 line = 0; line < subrect_height; ++line) { | 272 | for (u32 line = 0; line < subrect_height; ++line) { |
| 267 | const u32 y2 = line + offset_y; | 273 | const u32 y2 = line + offset_y; |
| 268 | const u32 gob_address_y = | 274 | const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height + |
| 269 | (y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512; | 275 | ((y2 % (gob_size_y * block_height)) / gob_size_y) * gob_size; |
| 270 | const auto& table = legacy_swizzle_table[y2 % 8]; | 276 | const auto& table = legacy_swizzle_table[y2 % gob_size_y]; |
| 271 | for (u32 x = 0; x < subrect_width; ++x) { | 277 | for (u32 x = 0; x < subrect_width; ++x) { |
| 272 | const u32 x2 = (x + offset_x) * bytes_per_pixel; | 278 | const u32 x2 = (x + offset_x) * bytes_per_pixel; |
| 273 | const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height; | 279 | const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height; |
| 274 | const u32 swizzled_offset = gob_address + table[x2 % 64]; | 280 | const u32 swizzled_offset = gob_address + table[x2 % gob_size_x]; |
| 275 | const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel; | 281 | const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel; |
| 276 | const VAddr source_addr = swizzled_data + swizzled_offset; | 282 | const VAddr source_addr = swizzled_data + swizzled_offset; |
| 277 | 283 | ||
| @@ -325,12 +331,9 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat | |||
| 325 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, | 331 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, |
| 326 | u32 block_height, u32 block_depth) { | 332 | u32 block_height, u32 block_depth) { |
| 327 | if (tiled) { | 333 | if (tiled) { |
| 328 | constexpr u32 gobs_in_x = 64; | 334 | const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gob_size_x); |
| 329 | constexpr u32 gobs_in_y = 8; | 335 | const u32 aligned_height = Common::AlignUp(height, gob_size_y * block_height); |
| 330 | constexpr u32 gobs_in_z = 1; | 336 | const u32 aligned_depth = Common::AlignUp(depth, gob_size_z * block_depth); |
| 331 | const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x); | ||
| 332 | const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); | ||
| 333 | const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); | ||
| 334 | return aligned_width * aligned_height * aligned_depth; | 337 | return aligned_width * aligned_height * aligned_depth; |
| 335 | } else { | 338 | } else { |
| 336 | return width * height * depth * bytes_per_pixel; | 339 | return width * height * depth * bytes_per_pixel; |
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index ba065510b..f4ef7c73e 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h | |||
| @@ -19,6 +19,13 @@ inline std::size_t GetGOBSize() { | |||
| 19 | /** | 19 | /** |
| 20 | * Unswizzles a swizzled texture without changing its format. | 20 | * Unswizzles a swizzled texture without changing its format. |
| 21 | */ | 21 | */ |
| 22 | void UnswizzleTexture(u8* unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y, | ||
| 23 | u32 bytes_per_pixel, u32 width, u32 height, u32 depth, | ||
| 24 | u32 block_height = TICEntry::DefaultBlockHeight, | ||
| 25 | u32 block_depth = TICEntry::DefaultBlockHeight); | ||
| 26 | /** | ||
| 27 | * Unswizzles a swizzled texture without changing its format. | ||
| 28 | */ | ||
| 22 | std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, | 29 | std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, |
| 23 | u32 bytes_per_pixel, u32 width, u32 height, u32 depth, | 30 | u32 bytes_per_pixel, u32 width, u32 height, u32 depth, |
| 24 | u32 block_height = TICEntry::DefaultBlockHeight, | 31 | u32 block_height = TICEntry::DefaultBlockHeight, |
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index e199d019a..ffa08f5c1 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h | |||
| @@ -190,6 +190,7 @@ struct TICEntry { | |||
| 190 | union { | 190 | union { |
| 191 | BitField<0, 4, u32> res_min_mip_level; | 191 | BitField<0, 4, u32> res_min_mip_level; |
| 192 | BitField<4, 4, u32> res_max_mip_level; | 192 | BitField<4, 4, u32> res_max_mip_level; |
| 193 | BitField<12, 12, u32> min_lod_clamp; | ||
| 193 | }; | 194 | }; |
| 194 | 195 | ||
| 195 | GPUVAddr Address() const { | 196 | GPUVAddr Address() const { |
| @@ -284,13 +285,25 @@ struct TSCEntry { | |||
| 284 | BitField<6, 3, WrapMode> wrap_p; | 285 | BitField<6, 3, WrapMode> wrap_p; |
| 285 | BitField<9, 1, u32> depth_compare_enabled; | 286 | BitField<9, 1, u32> depth_compare_enabled; |
| 286 | BitField<10, 3, DepthCompareFunc> depth_compare_func; | 287 | BitField<10, 3, DepthCompareFunc> depth_compare_func; |
| 288 | BitField<13, 1, u32> srgb_conversion; | ||
| 289 | BitField<20, 3, u32> max_anisotropy; | ||
| 287 | }; | 290 | }; |
| 288 | union { | 291 | union { |
| 289 | BitField<0, 2, TextureFilter> mag_filter; | 292 | BitField<0, 2, TextureFilter> mag_filter; |
| 290 | BitField<4, 2, TextureFilter> min_filter; | 293 | BitField<4, 2, TextureFilter> min_filter; |
| 291 | BitField<6, 2, TextureMipmapFilter> mip_filter; | 294 | BitField<6, 2, TextureMipmapFilter> mip_filter; |
| 295 | BitField<9, 1, u32> cubemap_interface_filtering; | ||
| 296 | BitField<12, 13, u32> mip_lod_bias; | ||
| 297 | }; | ||
| 298 | union { | ||
| 299 | BitField<0, 12, u32> min_lod_clamp; | ||
| 300 | BitField<12, 12, u32> max_lod_clamp; | ||
| 301 | BitField<24, 8, u32> srgb_border_color_r; | ||
| 302 | }; | ||
| 303 | union { | ||
| 304 | BitField<12, 8, u32> srgb_border_color_g; | ||
| 305 | BitField<20, 8, u32> srgb_border_color_b; | ||
| 292 | }; | 306 | }; |
| 293 | INSERT_PADDING_BYTES(8); | ||
| 294 | float border_color_r; | 307 | float border_color_r; |
| 295 | float border_color_g; | 308 | float border_color_g; |
| 296 | float border_color_b; | 309 | float border_color_b; |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 53c7f00d4..cfca8f4a8 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -29,8 +29,14 @@ add_executable(yuzu | |||
| 29 | configuration/configure_graphics.h | 29 | configuration/configure_graphics.h |
| 30 | configuration/configure_input.cpp | 30 | configuration/configure_input.cpp |
| 31 | configuration/configure_input.h | 31 | configuration/configure_input.h |
| 32 | configuration/configure_input_player.cpp | ||
| 33 | configuration/configure_input_player.h | ||
| 34 | configuration/configure_mouse_advanced.cpp | ||
| 35 | configuration/configure_mouse_advanced.h | ||
| 32 | configuration/configure_system.cpp | 36 | configuration/configure_system.cpp |
| 33 | configuration/configure_system.h | 37 | configuration/configure_system.h |
| 38 | configuration/configure_touchscreen_advanced.cpp | ||
| 39 | configuration/configure_touchscreen_advanced.h | ||
| 34 | configuration/configure_web.cpp | 40 | configuration/configure_web.cpp |
| 35 | configuration/configure_web.h | 41 | configuration/configure_web.h |
| 36 | debugger/graphics/graphics_breakpoint_observer.cpp | 42 | debugger/graphics/graphics_breakpoint_observer.cpp |
| @@ -78,7 +84,10 @@ set(UIS | |||
| 78 | configuration/configure_general.ui | 84 | configuration/configure_general.ui |
| 79 | configuration/configure_graphics.ui | 85 | configuration/configure_graphics.ui |
| 80 | configuration/configure_input.ui | 86 | configuration/configure_input.ui |
| 87 | configuration/configure_input_player.ui | ||
| 88 | configuration/configure_mouse_advanced.ui | ||
| 81 | configuration/configure_system.ui | 89 | configuration/configure_system.ui |
| 90 | configuration/configure_touchscreen_advanced.ui | ||
| 82 | configuration/configure_web.ui | 91 | configuration/configure_web.ui |
| 83 | hotkeys.ui | 92 | hotkeys.ui |
| 84 | main.ui | 93 | main.ui |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index be69fb831..e24ed5f2b 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <QSettings> | 5 | #include <QSettings> |
| 6 | #include "common/file_util.h" | 6 | #include "common/file_util.h" |
| 7 | #include "core/hle/service/acc/profile_manager.h" | 7 | #include "core/hle/service/acc/profile_manager.h" |
| 8 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 8 | #include "input_common/main.h" | 9 | #include "input_common/main.h" |
| 9 | #include "yuzu/configuration/config.h" | 10 | #include "yuzu/configuration/config.h" |
| 10 | #include "yuzu/ui_settings.h" | 11 | #include "yuzu/ui_settings.h" |
| @@ -47,40 +48,313 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config: | |||
| 47 | }, | 48 | }, |
| 48 | }}; | 49 | }}; |
| 49 | 50 | ||
| 50 | void Config::ReadValues() { | 51 | const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons = |
| 51 | qt_config->beginGroup("Controls"); | 52 | { |
| 53 | Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal, | ||
| 54 | }; | ||
| 55 | |||
| 56 | const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> Config::default_keyboard_keys = { | ||
| 57 | 0, | ||
| 58 | 0, | ||
| 59 | 0, | ||
| 60 | 0, | ||
| 61 | Qt::Key_A, | ||
| 62 | Qt::Key_B, | ||
| 63 | Qt::Key_C, | ||
| 64 | Qt::Key_D, | ||
| 65 | Qt::Key_E, | ||
| 66 | Qt::Key_F, | ||
| 67 | Qt::Key_G, | ||
| 68 | Qt::Key_H, | ||
| 69 | Qt::Key_I, | ||
| 70 | Qt::Key_J, | ||
| 71 | Qt::Key_K, | ||
| 72 | Qt::Key_L, | ||
| 73 | Qt::Key_M, | ||
| 74 | Qt::Key_N, | ||
| 75 | Qt::Key_O, | ||
| 76 | Qt::Key_P, | ||
| 77 | Qt::Key_Q, | ||
| 78 | Qt::Key_R, | ||
| 79 | Qt::Key_S, | ||
| 80 | Qt::Key_T, | ||
| 81 | Qt::Key_U, | ||
| 82 | Qt::Key_V, | ||
| 83 | Qt::Key_W, | ||
| 84 | Qt::Key_X, | ||
| 85 | Qt::Key_Y, | ||
| 86 | Qt::Key_Z, | ||
| 87 | Qt::Key_1, | ||
| 88 | Qt::Key_2, | ||
| 89 | Qt::Key_3, | ||
| 90 | Qt::Key_4, | ||
| 91 | Qt::Key_5, | ||
| 92 | Qt::Key_6, | ||
| 93 | Qt::Key_7, | ||
| 94 | Qt::Key_8, | ||
| 95 | Qt::Key_9, | ||
| 96 | Qt::Key_0, | ||
| 97 | Qt::Key_Enter, | ||
| 98 | Qt::Key_Escape, | ||
| 99 | Qt::Key_Backspace, | ||
| 100 | Qt::Key_Tab, | ||
| 101 | Qt::Key_Space, | ||
| 102 | Qt::Key_Minus, | ||
| 103 | Qt::Key_Equal, | ||
| 104 | Qt::Key_BracketLeft, | ||
| 105 | Qt::Key_BracketRight, | ||
| 106 | Qt::Key_Backslash, | ||
| 107 | Qt::Key_Dead_Tilde, | ||
| 108 | Qt::Key_Semicolon, | ||
| 109 | Qt::Key_Apostrophe, | ||
| 110 | Qt::Key_Dead_Grave, | ||
| 111 | Qt::Key_Comma, | ||
| 112 | Qt::Key_Period, | ||
| 113 | Qt::Key_Slash, | ||
| 114 | Qt::Key_CapsLock, | ||
| 115 | |||
| 116 | Qt::Key_F1, | ||
| 117 | Qt::Key_F2, | ||
| 118 | Qt::Key_F3, | ||
| 119 | Qt::Key_F4, | ||
| 120 | Qt::Key_F5, | ||
| 121 | Qt::Key_F6, | ||
| 122 | Qt::Key_F7, | ||
| 123 | Qt::Key_F8, | ||
| 124 | Qt::Key_F9, | ||
| 125 | Qt::Key_F10, | ||
| 126 | Qt::Key_F11, | ||
| 127 | Qt::Key_F12, | ||
| 128 | |||
| 129 | Qt::Key_SysReq, | ||
| 130 | Qt::Key_ScrollLock, | ||
| 131 | Qt::Key_Pause, | ||
| 132 | Qt::Key_Insert, | ||
| 133 | Qt::Key_Home, | ||
| 134 | Qt::Key_PageUp, | ||
| 135 | Qt::Key_Delete, | ||
| 136 | Qt::Key_End, | ||
| 137 | Qt::Key_PageDown, | ||
| 138 | Qt::Key_Right, | ||
| 139 | Qt::Key_Left, | ||
| 140 | Qt::Key_Down, | ||
| 141 | Qt::Key_Up, | ||
| 142 | |||
| 143 | Qt::Key_NumLock, | ||
| 144 | Qt::Key_Slash, | ||
| 145 | Qt::Key_Asterisk, | ||
| 146 | Qt::Key_Minus, | ||
| 147 | Qt::Key_Plus, | ||
| 148 | Qt::Key_Enter, | ||
| 149 | Qt::Key_1, | ||
| 150 | Qt::Key_2, | ||
| 151 | Qt::Key_3, | ||
| 152 | Qt::Key_4, | ||
| 153 | Qt::Key_5, | ||
| 154 | Qt::Key_6, | ||
| 155 | Qt::Key_7, | ||
| 156 | Qt::Key_8, | ||
| 157 | Qt::Key_9, | ||
| 158 | Qt::Key_0, | ||
| 159 | Qt::Key_Period, | ||
| 160 | |||
| 161 | 0, | ||
| 162 | 0, | ||
| 163 | Qt::Key_PowerOff, | ||
| 164 | Qt::Key_Equal, | ||
| 165 | |||
| 166 | Qt::Key_F13, | ||
| 167 | Qt::Key_F14, | ||
| 168 | Qt::Key_F15, | ||
| 169 | Qt::Key_F16, | ||
| 170 | Qt::Key_F17, | ||
| 171 | Qt::Key_F18, | ||
| 172 | Qt::Key_F19, | ||
| 173 | Qt::Key_F20, | ||
| 174 | Qt::Key_F21, | ||
| 175 | Qt::Key_F22, | ||
| 176 | Qt::Key_F23, | ||
| 177 | Qt::Key_F24, | ||
| 178 | |||
| 179 | Qt::Key_Open, | ||
| 180 | Qt::Key_Help, | ||
| 181 | Qt::Key_Menu, | ||
| 182 | 0, | ||
| 183 | Qt::Key_Stop, | ||
| 184 | Qt::Key_AudioRepeat, | ||
| 185 | Qt::Key_Undo, | ||
| 186 | Qt::Key_Cut, | ||
| 187 | Qt::Key_Copy, | ||
| 188 | Qt::Key_Paste, | ||
| 189 | Qt::Key_Find, | ||
| 190 | Qt::Key_VolumeMute, | ||
| 191 | Qt::Key_VolumeUp, | ||
| 192 | Qt::Key_VolumeDown, | ||
| 193 | Qt::Key_CapsLock, | ||
| 194 | Qt::Key_NumLock, | ||
| 195 | Qt::Key_ScrollLock, | ||
| 196 | Qt::Key_Comma, | ||
| 197 | |||
| 198 | Qt::Key_ParenLeft, | ||
| 199 | Qt::Key_ParenRight, | ||
| 200 | }; | ||
| 201 | |||
| 202 | const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default_keyboard_mods = { | ||
| 203 | Qt::Key_Control, Qt::Key_Shift, Qt::Key_Alt, Qt::Key_ApplicationLeft, | ||
| 204 | Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight, | ||
| 205 | }; | ||
| 206 | |||
| 207 | void Config::ReadPlayerValues() { | ||
| 208 | for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { | ||
| 209 | Settings::values.players[p].connected = | ||
| 210 | qt_config->value(QString("player_%1_connected").arg(p), false).toBool(); | ||
| 211 | |||
| 212 | Settings::values.players[p].type = static_cast<Settings::ControllerType>( | ||
| 213 | qt_config | ||
| 214 | ->value(QString("player_%1_type").arg(p), | ||
| 215 | static_cast<u8>(Settings::ControllerType::DualJoycon)) | ||
| 216 | .toUInt()); | ||
| 217 | |||
| 218 | Settings::values.players[p].body_color_left = | ||
| 219 | qt_config | ||
| 220 | ->value(QString("player_%1_body_color_left").arg(p), | ||
| 221 | Settings::JOYCON_BODY_NEON_BLUE) | ||
| 222 | .toUInt(); | ||
| 223 | Settings::values.players[p].body_color_right = | ||
| 224 | qt_config | ||
| 225 | ->value(QString("player_%1_body_color_right").arg(p), | ||
| 226 | Settings::JOYCON_BODY_NEON_RED) | ||
| 227 | .toUInt(); | ||
| 228 | Settings::values.players[p].button_color_left = | ||
| 229 | qt_config | ||
| 230 | ->value(QString("player_%1_button_color_left").arg(p), | ||
| 231 | Settings::JOYCON_BUTTONS_NEON_BLUE) | ||
| 232 | .toUInt(); | ||
| 233 | Settings::values.players[p].button_color_right = | ||
| 234 | qt_config | ||
| 235 | ->value(QString("player_%1_button_color_right").arg(p), | ||
| 236 | Settings::JOYCON_BUTTONS_NEON_RED) | ||
| 237 | .toUInt(); | ||
| 238 | |||
| 239 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 240 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 241 | Settings::values.players[p].buttons[i] = | ||
| 242 | qt_config | ||
| 243 | ->value(QString("player_%1_").arg(p) + Settings::NativeButton::mapping[i], | ||
| 244 | QString::fromStdString(default_param)) | ||
| 245 | .toString() | ||
| 246 | .toStdString(); | ||
| 247 | if (Settings::values.players[p].buttons[i].empty()) | ||
| 248 | Settings::values.players[p].buttons[i] = default_param; | ||
| 249 | } | ||
| 250 | |||
| 251 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 252 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 253 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 254 | default_analogs[i][3], default_analogs[i][4], 0.5f); | ||
| 255 | Settings::values.players[p].analogs[i] = | ||
| 256 | qt_config | ||
| 257 | ->value(QString("player_%1_").arg(p) + Settings::NativeAnalog::mapping[i], | ||
| 258 | QString::fromStdString(default_param)) | ||
| 259 | .toString() | ||
| 260 | .toStdString(); | ||
| 261 | if (Settings::values.players[p].analogs[i].empty()) | ||
| 262 | Settings::values.players[p].analogs[i] = default_param; | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | std::stable_partition( | ||
| 267 | Settings::values.players.begin(), | ||
| 268 | Settings::values.players.begin() + | ||
| 269 | Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD), | ||
| 270 | [](const auto& player) { return player.connected; }); | ||
| 271 | } | ||
| 272 | |||
| 273 | void Config::ReadDebugValues() { | ||
| 274 | Settings::values.debug_pad_enabled = qt_config->value("debug_pad_enabled", false).toBool(); | ||
| 52 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | 275 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |
| 53 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | 276 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); |
| 54 | Settings::values.buttons[i] = | 277 | Settings::values.debug_pad_buttons[i] = |
| 55 | qt_config | 278 | qt_config |
| 56 | ->value(Settings::NativeButton::mapping[i], QString::fromStdString(default_param)) | 279 | ->value(QString("debug_pad_") + Settings::NativeButton::mapping[i], |
| 280 | QString::fromStdString(default_param)) | ||
| 57 | .toString() | 281 | .toString() |
| 58 | .toStdString(); | 282 | .toStdString(); |
| 59 | if (Settings::values.buttons[i].empty()) | 283 | if (Settings::values.debug_pad_buttons[i].empty()) |
| 60 | Settings::values.buttons[i] = default_param; | 284 | Settings::values.debug_pad_buttons[i] = default_param; |
| 61 | } | 285 | } |
| 62 | 286 | ||
| 63 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | 287 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { |
| 64 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | 288 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( |
| 65 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | 289 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], |
| 66 | default_analogs[i][3], default_analogs[i][4], 0.5f); | 290 | default_analogs[i][3], default_analogs[i][4], 0.5f); |
| 67 | Settings::values.analogs[i] = | 291 | Settings::values.debug_pad_analogs[i] = |
| 68 | qt_config | 292 | qt_config |
| 69 | ->value(Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param)) | 293 | ->value(QString("debug_pad_") + Settings::NativeAnalog::mapping[i], |
| 294 | QString::fromStdString(default_param)) | ||
| 70 | .toString() | 295 | .toString() |
| 71 | .toStdString(); | 296 | .toStdString(); |
| 72 | if (Settings::values.analogs[i].empty()) | 297 | if (Settings::values.debug_pad_analogs[i].empty()) |
| 73 | Settings::values.analogs[i] = default_param; | 298 | Settings::values.debug_pad_analogs[i] = default_param; |
| 74 | } | 299 | } |
| 300 | } | ||
| 301 | |||
| 302 | void Config::ReadKeyboardValues() { | ||
| 303 | Settings::values.keyboard_enabled = qt_config->value("keyboard_enabled", false).toBool(); | ||
| 304 | |||
| 305 | std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(), | ||
| 306 | Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); | ||
| 307 | std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(), | ||
| 308 | Settings::values.keyboard_keys.begin() + | ||
| 309 | Settings::NativeKeyboard::LeftControlKey, | ||
| 310 | InputCommon::GenerateKeyboardParam); | ||
| 311 | std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(), | ||
| 312 | Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam); | ||
| 313 | } | ||
| 314 | |||
| 315 | void Config::ReadMouseValues() { | ||
| 316 | Settings::values.mouse_enabled = qt_config->value("mouse_enabled", false).toBool(); | ||
| 317 | |||
| 318 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | ||
| 319 | std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); | ||
| 320 | Settings::values.mouse_buttons[i] = | ||
| 321 | qt_config | ||
| 322 | ->value(QString("mouse_") + Settings::NativeMouseButton::mapping[i], | ||
| 323 | QString::fromStdString(default_param)) | ||
| 324 | .toString() | ||
| 325 | .toStdString(); | ||
| 326 | if (Settings::values.mouse_buttons[i].empty()) | ||
| 327 | Settings::values.mouse_buttons[i] = default_param; | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | void Config::ReadTouchscreenValues() { | ||
| 332 | Settings::values.touchscreen.enabled = qt_config->value("touchscreen_enabled", true).toBool(); | ||
| 333 | Settings::values.touchscreen.device = | ||
| 334 | qt_config->value("touchscreen_device", "engine:emu_window").toString().toStdString(); | ||
| 335 | |||
| 336 | Settings::values.touchscreen.finger = qt_config->value("touchscreen_finger", 0).toUInt(); | ||
| 337 | Settings::values.touchscreen.rotation_angle = qt_config->value("touchscreen_angle", 0).toUInt(); | ||
| 338 | Settings::values.touchscreen.diameter_x = | ||
| 339 | qt_config->value("touchscreen_diameter_x", 15).toUInt(); | ||
| 340 | Settings::values.touchscreen.diameter_y = | ||
| 341 | qt_config->value("touchscreen_diameter_y", 15).toUInt(); | ||
| 342 | qt_config->endGroup(); | ||
| 343 | } | ||
| 344 | |||
| 345 | void Config::ReadValues() { | ||
| 346 | qt_config->beginGroup("Controls"); | ||
| 347 | |||
| 348 | ReadPlayerValues(); | ||
| 349 | ReadDebugValues(); | ||
| 350 | ReadKeyboardValues(); | ||
| 351 | ReadMouseValues(); | ||
| 352 | ReadTouchscreenValues(); | ||
| 75 | 353 | ||
| 76 | Settings::values.motion_device = | 354 | Settings::values.motion_device = |
| 77 | qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") | 355 | qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") |
| 78 | .toString() | 356 | .toString() |
| 79 | .toStdString(); | 357 | .toStdString(); |
| 80 | Settings::values.touch_device = | ||
| 81 | qt_config->value("touch_device", "engine:emu_window").toString().toStdString(); | ||
| 82 | |||
| 83 | qt_config->endGroup(); | ||
| 84 | 358 | ||
| 85 | qt_config->beginGroup("Core"); | 359 | qt_config->beginGroup("Core"); |
| 86 | Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); | 360 | Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); |
| @@ -126,6 +400,11 @@ void Config::ReadValues() { | |||
| 126 | .toStdString()); | 400 | .toStdString()); |
| 127 | qt_config->endGroup(); | 401 | qt_config->endGroup(); |
| 128 | 402 | ||
| 403 | qt_config->beginGroup("Core"); | ||
| 404 | Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); | ||
| 405 | Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool(); | ||
| 406 | qt_config->endGroup(); | ||
| 407 | |||
| 129 | qt_config->beginGroup("System"); | 408 | qt_config->beginGroup("System"); |
| 130 | Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); | 409 | Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); |
| 131 | Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool(); | 410 | Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool(); |
| @@ -230,18 +509,81 @@ void Config::ReadValues() { | |||
| 230 | qt_config->endGroup(); | 509 | qt_config->endGroup(); |
| 231 | } | 510 | } |
| 232 | 511 | ||
| 233 | void Config::SaveValues() { | 512 | void Config::SavePlayerValues() { |
| 234 | qt_config->beginGroup("Controls"); | 513 | for (int p = 0; p < Settings::values.players.size(); ++p) { |
| 514 | qt_config->setValue(QString("player_%1_connected").arg(p), | ||
| 515 | Settings::values.players[p].connected); | ||
| 516 | qt_config->setValue(QString("player_%1_type").arg(p), | ||
| 517 | static_cast<u8>(Settings::values.players[p].type)); | ||
| 518 | |||
| 519 | qt_config->setValue(QString("player_%1_body_color_left").arg(p), | ||
| 520 | Settings::values.players[p].body_color_left); | ||
| 521 | qt_config->setValue(QString("player_%1_body_color_right").arg(p), | ||
| 522 | Settings::values.players[p].body_color_right); | ||
| 523 | qt_config->setValue(QString("player_%1_button_color_left").arg(p), | ||
| 524 | Settings::values.players[p].button_color_left); | ||
| 525 | qt_config->setValue(QString("player_%1_button_color_right").arg(p), | ||
| 526 | Settings::values.players[p].button_color_right); | ||
| 527 | |||
| 528 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 529 | qt_config->setValue(QString("player_%1_").arg(p) + | ||
| 530 | QString::fromStdString(Settings::NativeButton::mapping[i]), | ||
| 531 | QString::fromStdString(Settings::values.players[p].buttons[i])); | ||
| 532 | } | ||
| 533 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 534 | qt_config->setValue(QString("player_%1_").arg(p) + | ||
| 535 | QString::fromStdString(Settings::NativeAnalog::mapping[i]), | ||
| 536 | QString::fromStdString(Settings::values.players[p].analogs[i])); | ||
| 537 | } | ||
| 538 | } | ||
| 539 | } | ||
| 540 | |||
| 541 | void Config::SaveDebugValues() { | ||
| 542 | qt_config->setValue("debug_pad_enabled", Settings::values.debug_pad_enabled); | ||
| 235 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | 543 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |
| 236 | qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]), | 544 | qt_config->setValue(QString("debug_pad_") + |
| 237 | QString::fromStdString(Settings::values.buttons[i])); | 545 | QString::fromStdString(Settings::NativeButton::mapping[i]), |
| 546 | QString::fromStdString(Settings::values.debug_pad_buttons[i])); | ||
| 238 | } | 547 | } |
| 239 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | 548 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { |
| 240 | qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]), | 549 | qt_config->setValue(QString("debug_pad_") + |
| 241 | QString::fromStdString(Settings::values.analogs[i])); | 550 | QString::fromStdString(Settings::NativeAnalog::mapping[i]), |
| 551 | QString::fromStdString(Settings::values.debug_pad_analogs[i])); | ||
| 242 | } | 552 | } |
| 553 | } | ||
| 554 | |||
| 555 | void Config::SaveMouseValues() { | ||
| 556 | qt_config->setValue("mouse_enabled", Settings::values.mouse_enabled); | ||
| 557 | |||
| 558 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | ||
| 559 | qt_config->setValue(QString("mouse_") + | ||
| 560 | QString::fromStdString(Settings::NativeMouseButton::mapping[i]), | ||
| 561 | QString::fromStdString(Settings::values.mouse_buttons[i])); | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | void Config::SaveTouchscreenValues() { | ||
| 566 | qt_config->setValue("touchscreen_enabled", Settings::values.touchscreen.enabled); | ||
| 567 | qt_config->setValue("touchscreen_device", | ||
| 568 | QString::fromStdString(Settings::values.touchscreen.device)); | ||
| 569 | |||
| 570 | qt_config->setValue("touchscreen_finger", Settings::values.touchscreen.finger); | ||
| 571 | qt_config->setValue("touchscreen_angle", Settings::values.touchscreen.rotation_angle); | ||
| 572 | qt_config->setValue("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x); | ||
| 573 | qt_config->setValue("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y); | ||
| 574 | } | ||
| 575 | |||
| 576 | void Config::SaveValues() { | ||
| 577 | qt_config->beginGroup("Controls"); | ||
| 578 | |||
| 579 | SavePlayerValues(); | ||
| 580 | SaveDebugValues(); | ||
| 581 | SaveMouseValues(); | ||
| 582 | SaveTouchscreenValues(); | ||
| 583 | |||
| 243 | qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device)); | 584 | qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device)); |
| 244 | qt_config->setValue("touch_device", QString::fromStdString(Settings::values.touch_device)); | 585 | qt_config->setValue("keyboard_enabled", Settings::values.keyboard_enabled); |
| 586 | |||
| 245 | qt_config->endGroup(); | 587 | qt_config->endGroup(); |
| 246 | 588 | ||
| 247 | qt_config->beginGroup("Core"); | 589 | qt_config->beginGroup("Core"); |
| @@ -280,7 +622,6 @@ void Config::SaveValues() { | |||
| 280 | qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); | 622 | qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); |
| 281 | qt_config->setValue("enable_nfc", Settings::values.enable_nfc); | 623 | qt_config->setValue("enable_nfc", Settings::values.enable_nfc); |
| 282 | qt_config->setValue("current_user", Settings::values.current_user); | 624 | qt_config->setValue("current_user", Settings::values.current_user); |
| 283 | |||
| 284 | qt_config->setValue("language_index", Settings::values.language_index); | 625 | qt_config->setValue("language_index", Settings::values.language_index); |
| 285 | 626 | ||
| 286 | qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value()); | 627 | qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value()); |
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 9c99c1b75..a1c27bbf9 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -22,10 +22,24 @@ public: | |||
| 22 | 22 | ||
| 23 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; | 23 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; |
| 24 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; | 24 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; |
| 25 | static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> | ||
| 26 | default_mouse_buttons; | ||
| 27 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; | ||
| 28 | static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods; | ||
| 25 | 29 | ||
| 26 | private: | 30 | private: |
| 27 | void ReadValues(); | 31 | void ReadValues(); |
| 32 | void ReadPlayerValues(); | ||
| 33 | void ReadDebugValues(); | ||
| 34 | void ReadKeyboardValues(); | ||
| 35 | void ReadMouseValues(); | ||
| 36 | void ReadTouchscreenValues(); | ||
| 37 | |||
| 28 | void SaveValues(); | 38 | void SaveValues(); |
| 39 | void SavePlayerValues(); | ||
| 40 | void SaveDebugValues(); | ||
| 41 | void SaveMouseValues(); | ||
| 42 | void SaveTouchscreenValues(); | ||
| 29 | 43 | ||
| 30 | std::unique_ptr<QSettings> qt_config; | 44 | std::unique_ptr<QSettings> qt_config; |
| 31 | std::string qt_config_loc; | 45 | std::string qt_config_loc; |
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp index 639d5df0f..ae8cac243 100644 --- a/src/yuzu/configuration/configure_gamelist.cpp +++ b/src/yuzu/configuration/configure_gamelist.cpp | |||
| @@ -36,6 +36,16 @@ ConfigureGameList::ConfigureGameList(QWidget* parent) | |||
| 36 | InitializeRowComboBoxes(); | 36 | InitializeRowComboBoxes(); |
| 37 | 37 | ||
| 38 | this->setConfiguration(); | 38 | this->setConfiguration(); |
| 39 | |||
| 40 | // Force game list reload if any of the relevant settings are changed. | ||
| 41 | connect(ui->show_unknown, &QCheckBox::stateChanged, this, | ||
| 42 | &ConfigureGameList::RequestGameListUpdate); | ||
| 43 | connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | ||
| 44 | &ConfigureGameList::RequestGameListUpdate); | ||
| 45 | connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | ||
| 46 | &ConfigureGameList::RequestGameListUpdate); | ||
| 47 | connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | ||
| 48 | &ConfigureGameList::RequestGameListUpdate); | ||
| 39 | } | 49 | } |
| 40 | 50 | ||
| 41 | ConfigureGameList::~ConfigureGameList() = default; | 51 | ConfigureGameList::~ConfigureGameList() = default; |
| @@ -49,6 +59,10 @@ void ConfigureGameList::applyConfiguration() { | |||
| 49 | Settings::Apply(); | 59 | Settings::Apply(); |
| 50 | } | 60 | } |
| 51 | 61 | ||
| 62 | void ConfigureGameList::RequestGameListUpdate() { | ||
| 63 | UISettings::values.is_game_list_reload_pending.exchange(true); | ||
| 64 | } | ||
| 65 | |||
| 52 | void ConfigureGameList::setConfiguration() { | 66 | void ConfigureGameList::setConfiguration() { |
| 53 | ui->show_unknown->setChecked(UISettings::values.show_unknown); | 67 | ui->show_unknown->setChecked(UISettings::values.show_unknown); |
| 54 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons); | 68 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons); |
diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_gamelist.h index ff7406c60..bbf7e25f1 100644 --- a/src/yuzu/configuration/configure_gamelist.h +++ b/src/yuzu/configuration/configure_gamelist.h | |||
| @@ -21,6 +21,8 @@ public: | |||
| 21 | void applyConfiguration(); | 21 | void applyConfiguration(); |
| 22 | 22 | ||
| 23 | private: | 23 | private: |
| 24 | void RequestGameListUpdate(); | ||
| 25 | |||
| 24 | void setConfiguration(); | 26 | void setConfiguration(); |
| 25 | 27 | ||
| 26 | void changeEvent(QEvent*) override; | 28 | void changeEvent(QEvent*) override; |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 314f51203..92a441308 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -3,10 +3,6 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/core.h" | 5 | #include "core/core.h" |
| 6 | #include "core/hle/service/am/am.h" | ||
| 7 | #include "core/hle/service/am/applet_ae.h" | ||
| 8 | #include "core/hle/service/am/applet_oe.h" | ||
| 9 | #include "core/hle/service/sm/sm.h" | ||
| 10 | #include "core/settings.h" | 6 | #include "core/settings.h" |
| 11 | #include "ui_configure_general.h" | 7 | #include "ui_configure_general.h" |
| 12 | #include "yuzu/configuration/configure_general.h" | 8 | #include "yuzu/configuration/configure_general.h" |
| @@ -23,6 +19,9 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) | |||
| 23 | 19 | ||
| 24 | this->setConfiguration(); | 20 | this->setConfiguration(); |
| 25 | 21 | ||
| 22 | connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this, | ||
| 23 | [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); | ||
| 24 | |||
| 26 | ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | 25 | ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn()); |
| 27 | } | 26 | } |
| 28 | 27 | ||
| @@ -33,7 +32,6 @@ void ConfigureGeneral::setConfiguration() { | |||
| 33 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); | 32 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); |
| 34 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | 33 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); |
| 35 | ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); | 34 | ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); |
| 36 | ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); | ||
| 37 | ui->enable_nfc->setChecked(Settings::values.enable_nfc); | 35 | ui->enable_nfc->setChecked(Settings::values.enable_nfc); |
| 38 | } | 36 | } |
| 39 | 37 | ||
| @@ -41,33 +39,6 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { | |||
| 41 | ui->widget->Populate(registry); | 39 | ui->widget->Populate(registry); |
| 42 | } | 40 | } |
| 43 | 41 | ||
| 44 | void ConfigureGeneral::OnDockedModeChanged(bool last_state, bool new_state) { | ||
| 45 | if (last_state == new_state) { | ||
| 46 | return; | ||
| 47 | } | ||
| 48 | |||
| 49 | Core::System& system{Core::System::GetInstance()}; | ||
| 50 | if (!system.IsPoweredOn()) { | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | Service::SM::ServiceManager& sm = system.ServiceManager(); | ||
| 54 | |||
| 55 | // Message queue is shared between these services, we just need to signal an operation | ||
| 56 | // change to one and it will handle both automatically | ||
| 57 | auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE"); | ||
| 58 | auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); | ||
| 59 | bool has_signalled = false; | ||
| 60 | |||
| 61 | if (applet_oe != nullptr) { | ||
| 62 | applet_oe->GetMessageQueue()->OperationModeChanged(); | ||
| 63 | has_signalled = true; | ||
| 64 | } | ||
| 65 | |||
| 66 | if (applet_ae != nullptr && !has_signalled) { | ||
| 67 | applet_ae->GetMessageQueue()->OperationModeChanged(); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | void ConfigureGeneral::applyConfiguration() { | 42 | void ConfigureGeneral::applyConfiguration() { |
| 72 | UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); | 43 | UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); |
| 73 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); | 44 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); |
| @@ -75,9 +46,5 @@ void ConfigureGeneral::applyConfiguration() { | |||
| 75 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); | 46 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); |
| 76 | 47 | ||
| 77 | Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); | 48 | Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); |
| 78 | const bool pre_docked_mode = Settings::values.use_docked_mode; | ||
| 79 | Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); | ||
| 80 | OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); | ||
| 81 | |||
| 82 | Settings::values.enable_nfc = ui->enable_nfc->isChecked(); | 49 | Settings::values.enable_nfc = ui->enable_nfc->isChecked(); |
| 83 | } | 50 | } |
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h index 2210d48da..4770034cc 100644 --- a/src/yuzu/configuration/configure_general.h +++ b/src/yuzu/configuration/configure_general.h | |||
| @@ -25,7 +25,6 @@ public: | |||
| 25 | 25 | ||
| 26 | private: | 26 | private: |
| 27 | void setConfiguration(); | 27 | void setConfiguration(); |
| 28 | void OnDockedModeChanged(bool last_state, bool new_state); | ||
| 29 | 28 | ||
| 30 | std::unique_ptr<Ui::ConfigureGeneral> ui; | 29 | std::unique_ptr<Ui::ConfigureGeneral> ui; |
| 31 | }; | 30 | }; |
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index b82fffde8..bf37446c6 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>300</width> | 9 | <width>300</width> |
| 10 | <height>377</height> | 10 | <height>407</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -72,13 +72,6 @@ | |||
| 72 | <item> | 72 | <item> |
| 73 | <layout class="QVBoxLayout" name="EmulationVerticalLayout"> | 73 | <layout class="QVBoxLayout" name="EmulationVerticalLayout"> |
| 74 | <item> | 74 | <item> |
| 75 | <widget class="QCheckBox" name="use_docked_mode"> | ||
| 76 | <property name="text"> | ||
| 77 | <string>Enable docked mode</string> | ||
| 78 | </property> | ||
| 79 | </widget> | ||
| 80 | </item> | ||
| 81 | <item> | ||
| 82 | <widget class="QCheckBox" name="enable_nfc"> | 75 | <widget class="QCheckBox" name="enable_nfc"> |
| 83 | <property name="text"> | 76 | <property name="text"> |
| 84 | <string>Enable NFC</string> | 77 | <string>Enable NFC</string> |
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 42a7beac6..7ee572761 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp | |||
| @@ -9,334 +9,202 @@ | |||
| 9 | #include <QMessageBox> | 9 | #include <QMessageBox> |
| 10 | #include <QTimer> | 10 | #include <QTimer> |
| 11 | #include "common/param_package.h" | 11 | #include "common/param_package.h" |
| 12 | #include "configuration/configure_touchscreen_advanced.h" | ||
| 13 | #include "core/core.h" | ||
| 14 | #include "core/hle/service/am/am.h" | ||
| 15 | #include "core/hle/service/am/applet_ae.h" | ||
| 16 | #include "core/hle/service/am/applet_oe.h" | ||
| 17 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 18 | #include "core/hle/service/sm/sm.h" | ||
| 12 | #include "input_common/main.h" | 19 | #include "input_common/main.h" |
| 20 | #include "ui_configure_input.h" | ||
| 21 | #include "ui_configure_input_player.h" | ||
| 22 | #include "ui_configure_mouse_advanced.h" | ||
| 23 | #include "ui_configure_touchscreen_advanced.h" | ||
| 13 | #include "yuzu/configuration/config.h" | 24 | #include "yuzu/configuration/config.h" |
| 14 | #include "yuzu/configuration/configure_input.h" | 25 | #include "yuzu/configuration/configure_input.h" |
| 26 | #include "yuzu/configuration/configure_input_player.h" | ||
| 27 | #include "yuzu/configuration/configure_mouse_advanced.h" | ||
| 15 | 28 | ||
| 16 | const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM> | 29 | ConfigureInput::ConfigureInput(QWidget* parent) |
| 17 | ConfigureInput::analog_sub_buttons{{ | 30 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) { |
| 18 | "up", | 31 | ui->setupUi(this); |
| 19 | "down", | ||
| 20 | "left", | ||
| 21 | "right", | ||
| 22 | "modifier", | ||
| 23 | }}; | ||
| 24 | |||
| 25 | static QString getKeyName(int key_code) { | ||
| 26 | switch (key_code) { | ||
| 27 | case Qt::Key_Shift: | ||
| 28 | return QObject::tr("Shift"); | ||
| 29 | case Qt::Key_Control: | ||
| 30 | return QObject::tr("Ctrl"); | ||
| 31 | case Qt::Key_Alt: | ||
| 32 | return QObject::tr("Alt"); | ||
| 33 | case Qt::Key_Meta: | ||
| 34 | return ""; | ||
| 35 | default: | ||
| 36 | return QKeySequence(key_code).toString(); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | 32 | ||
| 40 | static void SetAnalogButton(const Common::ParamPackage& input_param, | 33 | players_controller = { |
| 41 | Common::ParamPackage& analog_param, const std::string& button_name) { | 34 | ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox, |
| 42 | if (analog_param.Get("engine", "") != "analog_from_button") { | 35 | ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox, |
| 43 | analog_param = { | 36 | }; |
| 44 | {"engine", "analog_from_button"}, | ||
| 45 | {"modifier_scale", "0.5"}, | ||
| 46 | }; | ||
| 47 | } | ||
| 48 | analog_param.Set(button_name, input_param.Serialize()); | ||
| 49 | } | ||
| 50 | 37 | ||
| 51 | static QString ButtonToText(const Common::ParamPackage& param) { | 38 | players_configure = { |
| 52 | if (!param.Has("engine")) { | 39 | ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure, |
| 53 | return QObject::tr("[not set]"); | 40 | ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure, |
| 54 | } else if (param.Get("engine", "") == "keyboard") { | 41 | }; |
| 55 | return getKeyName(param.Get("code", 0)); | ||
| 56 | } else if (param.Get("engine", "") == "sdl") { | ||
| 57 | if (param.Has("hat")) { | ||
| 58 | return QString(QObject::tr("Hat %1 %2")) | ||
| 59 | .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str()); | ||
| 60 | } | ||
| 61 | if (param.Has("axis")) { | ||
| 62 | return QString(QObject::tr("Axis %1%2")) | ||
| 63 | .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str()); | ||
| 64 | } | ||
| 65 | if (param.Has("button")) { | ||
| 66 | return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str()); | ||
| 67 | } | ||
| 68 | return QString(); | ||
| 69 | } else { | ||
| 70 | return QObject::tr("[unknown]"); | ||
| 71 | } | ||
| 72 | }; | ||
| 73 | |||
| 74 | static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { | ||
| 75 | if (!param.Has("engine")) { | ||
| 76 | return QObject::tr("[not set]"); | ||
| 77 | } else if (param.Get("engine", "") == "analog_from_button") { | ||
| 78 | return ButtonToText(Common::ParamPackage{param.Get(dir, "")}); | ||
| 79 | } else if (param.Get("engine", "") == "sdl") { | ||
| 80 | if (dir == "modifier") { | ||
| 81 | return QString(QObject::tr("[unused]")); | ||
| 82 | } | ||
| 83 | 42 | ||
| 84 | if (dir == "left" || dir == "right") { | 43 | for (auto* controller_box : players_controller) { |
| 85 | return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str()); | 44 | controller_box->addItems({"None", "Pro Controller", "Dual Joycons", "Single Right Joycon", |
| 86 | } else if (dir == "up" || dir == "down") { | 45 | "Single Left Joycon"}); |
| 87 | return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str()); | ||
| 88 | } | ||
| 89 | return QString(); | ||
| 90 | } else { | ||
| 91 | return QObject::tr("[unknown]"); | ||
| 92 | } | 46 | } |
| 93 | }; | ||
| 94 | 47 | ||
| 95 | ConfigureInput::ConfigureInput(QWidget* parent) | 48 | this->loadConfiguration(); |
| 96 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), | 49 | updateUIEnabled(); |
| 97 | timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) { | ||
| 98 | 50 | ||
| 99 | ui->setupUi(this); | 51 | connect(ui->restore_defaults_button, &QPushButton::pressed, this, |
| 100 | setFocusPolicy(Qt::ClickFocus); | 52 | &ConfigureInput::restoreDefaults); |
| 101 | |||
| 102 | button_map = { | ||
| 103 | ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, | ||
| 104 | ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, | ||
| 105 | ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, | ||
| 106 | ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, | ||
| 107 | ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown, | ||
| 108 | ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown, | ||
| 109 | ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot, | ||
| 110 | }; | ||
| 111 | 53 | ||
| 112 | analog_map_buttons = {{ | 54 | for (auto* enabled : players_controller) |
| 113 | { | 55 | connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this, |
| 114 | ui->buttonLStickUp, | 56 | &ConfigureInput::updateUIEnabled); |
| 115 | ui->buttonLStickDown, | 57 | connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled); |
| 116 | ui->buttonLStickLeft, | 58 | connect(ui->handheld_connected, &QCheckBox::stateChanged, this, |
| 117 | ui->buttonLStickRight, | 59 | &ConfigureInput::updateUIEnabled); |
| 118 | ui->buttonLStickMod, | 60 | connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled); |
| 119 | }, | 61 | connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled); |
| 120 | { | 62 | connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled); |
| 121 | ui->buttonRStickUp, | 63 | connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this, |
| 122 | ui->buttonRStickDown, | 64 | &ConfigureInput::updateUIEnabled); |
| 123 | ui->buttonRStickLeft, | ||
| 124 | ui->buttonRStickRight, | ||
| 125 | ui->buttonRStickMod, | ||
| 126 | }, | ||
| 127 | }}; | ||
| 128 | |||
| 129 | analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; | ||
| 130 | |||
| 131 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { | ||
| 132 | if (!button_map[button_id]) | ||
| 133 | continue; | ||
| 134 | button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu); | ||
| 135 | connect(button_map[button_id], &QPushButton::released, [=]() { | ||
| 136 | handleClick( | ||
| 137 | button_map[button_id], | ||
| 138 | [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, | ||
| 139 | InputCommon::Polling::DeviceType::Button); | ||
| 140 | }); | ||
| 141 | connect(button_map[button_id], &QPushButton::customContextMenuRequested, | ||
| 142 | [=](const QPoint& menu_location) { | ||
| 143 | QMenu context_menu; | ||
| 144 | context_menu.addAction(tr("Clear"), [&] { | ||
| 145 | buttons_param[button_id].Clear(); | ||
| 146 | button_map[button_id]->setText(tr("[not set]")); | ||
| 147 | }); | ||
| 148 | context_menu.addAction(tr("Restore Default"), [&] { | ||
| 149 | buttons_param[button_id] = Common::ParamPackage{ | ||
| 150 | InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; | ||
| 151 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); | ||
| 152 | }); | ||
| 153 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); | ||
| 154 | }); | ||
| 155 | } | ||
| 156 | 65 | ||
| 157 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | 66 | for (std::size_t i = 0; i < players_configure.size(); ++i) { |
| 158 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | 67 | connect(players_configure[i], &QPushButton::pressed, this, |
| 159 | if (!analog_map_buttons[analog_id][sub_button_id]) | 68 | [this, i]() { CallConfigureDialog<ConfigureInputPlayer>(i, false); }); |
| 160 | continue; | ||
| 161 | analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy( | ||
| 162 | Qt::CustomContextMenu); | ||
| 163 | connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() { | ||
| 164 | handleClick(analog_map_buttons[analog_id][sub_button_id], | ||
| 165 | [=](const Common::ParamPackage& params) { | ||
| 166 | SetAnalogButton(params, analogs_param[analog_id], | ||
| 167 | analog_sub_buttons[sub_button_id]); | ||
| 168 | }, | ||
| 169 | InputCommon::Polling::DeviceType::Button); | ||
| 170 | }); | ||
| 171 | connect(analog_map_buttons[analog_id][sub_button_id], | ||
| 172 | &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { | ||
| 173 | QMenu context_menu; | ||
| 174 | context_menu.addAction(tr("Clear"), [&] { | ||
| 175 | analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); | ||
| 176 | analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); | ||
| 177 | }); | ||
| 178 | context_menu.addAction(tr("Restore Default"), [&] { | ||
| 179 | Common::ParamPackage params{InputCommon::GenerateKeyboardParam( | ||
| 180 | Config::default_analogs[analog_id][sub_button_id])}; | ||
| 181 | SetAnalogButton(params, analogs_param[analog_id], | ||
| 182 | analog_sub_buttons[sub_button_id]); | ||
| 183 | analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText( | ||
| 184 | analogs_param[analog_id], analog_sub_buttons[sub_button_id])); | ||
| 185 | }); | ||
| 186 | context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal( | ||
| 187 | menu_location)); | ||
| 188 | }); | ||
| 189 | } | ||
| 190 | connect(analog_map_stick[analog_id], &QPushButton::released, [=]() { | ||
| 191 | QMessageBox::information(this, tr("Information"), | ||
| 192 | tr("After pressing OK, first move your joystick horizontally, " | ||
| 193 | "and then vertically.")); | ||
| 194 | handleClick( | ||
| 195 | analog_map_stick[analog_id], | ||
| 196 | [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; }, | ||
| 197 | InputCommon::Polling::DeviceType::Analog); | ||
| 198 | }); | ||
| 199 | } | 69 | } |
| 200 | 70 | ||
| 201 | connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); }); | 71 | connect(ui->handheld_configure, &QPushButton::pressed, this, |
| 202 | connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); | 72 | [this]() { CallConfigureDialog<ConfigureInputPlayer>(8, false); }); |
| 203 | |||
| 204 | timeout_timer->setSingleShot(true); | ||
| 205 | connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); }); | ||
| 206 | 73 | ||
| 207 | connect(poll_timer.get(), &QTimer::timeout, [this]() { | 74 | connect(ui->debug_configure, &QPushButton::pressed, this, |
| 208 | Common::ParamPackage params; | 75 | [this]() { CallConfigureDialog<ConfigureInputPlayer>(9, true); }); |
| 209 | for (auto& poller : device_pollers) { | ||
| 210 | params = poller->GetNextInput(); | ||
| 211 | if (params.Has("engine")) { | ||
| 212 | setPollingResult(params, false); | ||
| 213 | return; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | }); | ||
| 217 | 76 | ||
| 218 | this->loadConfiguration(); | 77 | connect(ui->mouse_advanced, &QPushButton::pressed, this, |
| 78 | [this]() { CallConfigureDialog<ConfigureMouseAdvanced>(); }); | ||
| 219 | 79 | ||
| 220 | // TODO(wwylele): enable this when we actually emulate it | 80 | connect(ui->touchscreen_advanced, &QPushButton::pressed, this, |
| 221 | ui->buttonHome->setEnabled(false); | 81 | [this]() { CallConfigureDialog<ConfigureTouchscreenAdvanced>(); }); |
| 222 | } | 82 | } |
| 223 | 83 | ||
| 224 | void ConfigureInput::applyConfiguration() { | 84 | template <typename Dialog, typename... Args> |
| 225 | std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.buttons.begin(), | 85 | void ConfigureInput::CallConfigureDialog(Args&&... args) { |
| 226 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | 86 | this->applyConfiguration(); |
| 227 | std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(), | 87 | Dialog dialog(this, std::forward<Args>(args)...); |
| 228 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | ||
| 229 | } | ||
| 230 | 88 | ||
| 231 | void ConfigureInput::loadConfiguration() { | 89 | const auto res = dialog.exec(); |
| 232 | std::transform(Settings::values.buttons.begin(), Settings::values.buttons.end(), | 90 | if (res == QDialog::Accepted) { |
| 233 | buttons_param.begin(), | 91 | dialog.applyConfiguration(); |
| 234 | [](const std::string& str) { return Common::ParamPackage(str); }); | 92 | } |
| 235 | std::transform(Settings::values.analogs.begin(), Settings::values.analogs.end(), | ||
| 236 | analogs_param.begin(), | ||
| 237 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 238 | updateButtonLabels(); | ||
| 239 | } | 93 | } |
| 240 | 94 | ||
| 241 | void ConfigureInput::restoreDefaults() { | 95 | void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) { |
| 242 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { | 96 | if (last_state == new_state) { |
| 243 | buttons_param[button_id] = Common::ParamPackage{ | 97 | return; |
| 244 | InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; | ||
| 245 | } | 98 | } |
| 246 | 99 | ||
| 247 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | 100 | Core::System& system{Core::System::GetInstance()}; |
| 248 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | 101 | if (!system.IsPoweredOn()) { |
| 249 | Common::ParamPackage params{InputCommon::GenerateKeyboardParam( | 102 | return; |
| 250 | Config::default_analogs[analog_id][sub_button_id])}; | ||
| 251 | SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); | ||
| 252 | } | ||
| 253 | } | 103 | } |
| 254 | updateButtonLabels(); | 104 | Service::SM::ServiceManager& sm = system.ServiceManager(); |
| 255 | } | ||
| 256 | 105 | ||
| 257 | void ConfigureInput::ClearAll() { | 106 | // Message queue is shared between these services, we just need to signal an operation |
| 258 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { | 107 | // change to one and it will handle both automatically |
| 259 | if (button_map[button_id] && button_map[button_id]->isEnabled()) | 108 | auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE"); |
| 260 | buttons_param[button_id].Clear(); | 109 | auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); |
| 261 | } | 110 | bool has_signalled = false; |
| 262 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | ||
| 263 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | ||
| 264 | if (analog_map_buttons[analog_id][sub_button_id] && | ||
| 265 | analog_map_buttons[analog_id][sub_button_id]->isEnabled()) | ||
| 266 | analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); | ||
| 267 | } | ||
| 268 | } | ||
| 269 | updateButtonLabels(); | ||
| 270 | } | ||
| 271 | 111 | ||
| 272 | void ConfigureInput::updateButtonLabels() { | 112 | if (applet_oe != nullptr) { |
| 273 | for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { | 113 | applet_oe->GetMessageQueue()->OperationModeChanged(); |
| 274 | button_map[button]->setText(ButtonToText(buttons_param[button])); | 114 | has_signalled = true; |
| 275 | } | 115 | } |
| 276 | 116 | ||
| 277 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | 117 | if (applet_ae != nullptr && !has_signalled) { |
| 278 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | 118 | applet_ae->GetMessageQueue()->OperationModeChanged(); |
| 279 | if (analog_map_buttons[analog_id][sub_button_id]) { | ||
| 280 | analog_map_buttons[analog_id][sub_button_id]->setText( | ||
| 281 | AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); | ||
| 282 | } | ||
| 283 | } | ||
| 284 | analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); | ||
| 285 | } | 119 | } |
| 286 | } | 120 | } |
| 287 | 121 | ||
| 288 | void ConfigureInput::handleClick(QPushButton* button, | 122 | void ConfigureInput::applyConfiguration() { |
| 289 | std::function<void(const Common::ParamPackage&)> new_input_setter, | 123 | for (std::size_t i = 0; i < players_controller.size(); ++i) { |
| 290 | InputCommon::Polling::DeviceType type) { | 124 | const auto controller_type_index = players_controller[i]->currentIndex(); |
| 291 | button->setText(tr("[press key]")); | ||
| 292 | button->setFocus(); | ||
| 293 | |||
| 294 | input_setter = new_input_setter; | ||
| 295 | |||
| 296 | device_pollers = InputCommon::Polling::GetPollers(type); | ||
| 297 | 125 | ||
| 298 | // Keyboard keys can only be used as button devices | 126 | Settings::values.players[i].connected = controller_type_index != 0; |
| 299 | want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; | ||
| 300 | 127 | ||
| 301 | for (auto& poller : device_pollers) { | 128 | if (controller_type_index > 0) { |
| 302 | poller->Start(); | 129 | Settings::values.players[i].type = |
| 130 | static_cast<Settings::ControllerType>(controller_type_index - 1); | ||
| 131 | } else { | ||
| 132 | Settings::values.players[i].type = Settings::ControllerType::DualJoycon; | ||
| 133 | } | ||
| 303 | } | 134 | } |
| 304 | 135 | ||
| 305 | grabKeyboard(); | 136 | const bool pre_docked_mode = Settings::values.use_docked_mode; |
| 306 | grabMouse(); | 137 | Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); |
| 307 | timeout_timer->start(5000); // Cancel after 5 seconds | 138 | OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); |
| 308 | poll_timer->start(200); // Check for new inputs every 200ms | 139 | Settings::values |
| 140 | .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)] | ||
| 141 | .connected = ui->handheld_connected->isChecked(); | ||
| 142 | Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked(); | ||
| 143 | Settings::values.mouse_enabled = ui->mouse_enabled->isChecked(); | ||
| 144 | Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked(); | ||
| 145 | Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked(); | ||
| 309 | } | 146 | } |
| 310 | 147 | ||
| 311 | void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool abort) { | 148 | void ConfigureInput::updateUIEnabled() { |
| 312 | releaseKeyboard(); | 149 | bool hit_disabled = false; |
| 313 | releaseMouse(); | 150 | for (auto* player : players_controller) { |
| 314 | timeout_timer->stop(); | 151 | player->setDisabled(hit_disabled); |
| 315 | poll_timer->stop(); | 152 | if (hit_disabled) |
| 316 | for (auto& poller : device_pollers) { | 153 | player->setCurrentIndex(0); |
| 317 | poller->Stop(); | 154 | if (!hit_disabled && player->currentIndex() == 0) |
| 155 | hit_disabled = true; | ||
| 318 | } | 156 | } |
| 319 | 157 | ||
| 320 | if (!abort) { | 158 | for (std::size_t i = 0; i < players_controller.size(); ++i) { |
| 321 | (*input_setter)(params); | 159 | players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0); |
| 322 | } | 160 | } |
| 323 | 161 | ||
| 324 | updateButtonLabels(); | 162 | ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked()); |
| 325 | input_setter = {}; | 163 | ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() && |
| 164 | !ui->use_docked_mode->isChecked()); | ||
| 165 | ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked()); | ||
| 166 | ui->debug_configure->setEnabled(ui->debug_enabled->isChecked()); | ||
| 167 | ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); | ||
| 326 | } | 168 | } |
| 327 | 169 | ||
| 328 | void ConfigureInput::keyPressEvent(QKeyEvent* event) { | 170 | void ConfigureInput::loadConfiguration() { |
| 329 | if (!input_setter || !event) | 171 | std::stable_partition( |
| 330 | return; | 172 | Settings::values.players.begin(), |
| 173 | Settings::values.players.begin() + | ||
| 174 | Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD), | ||
| 175 | [](const auto& player) { return player.connected; }); | ||
| 176 | |||
| 177 | for (std::size_t i = 0; i < players_controller.size(); ++i) { | ||
| 178 | const auto connected = Settings::values.players[i].connected; | ||
| 179 | players_controller[i]->setCurrentIndex( | ||
| 180 | connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0); | ||
| 181 | } | ||
| 182 | |||
| 183 | ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); | ||
| 184 | ui->handheld_connected->setChecked( | ||
| 185 | Settings::values | ||
| 186 | .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)] | ||
| 187 | .connected); | ||
| 188 | ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled); | ||
| 189 | ui->mouse_enabled->setChecked(Settings::values.mouse_enabled); | ||
| 190 | ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled); | ||
| 191 | ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); | ||
| 192 | |||
| 193 | updateUIEnabled(); | ||
| 194 | } | ||
| 331 | 195 | ||
| 332 | if (event->key() != Qt::Key_Escape) { | 196 | void ConfigureInput::restoreDefaults() { |
| 333 | if (want_keyboard_keys) { | 197 | players_controller[0]->setCurrentIndex(2); |
| 334 | setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, | 198 | |
| 335 | false); | 199 | for (std::size_t i = 1; i < players_controller.size(); ++i) { |
| 336 | } else { | 200 | players_controller[i]->setCurrentIndex(0); |
| 337 | // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling | ||
| 338 | return; | ||
| 339 | } | ||
| 340 | } | 201 | } |
| 341 | setPollingResult({}, true); | 202 | |
| 203 | ui->use_docked_mode->setCheckState(Qt::Unchecked); | ||
| 204 | ui->handheld_connected->setCheckState(Qt::Unchecked); | ||
| 205 | ui->mouse_enabled->setCheckState(Qt::Unchecked); | ||
| 206 | ui->keyboard_enabled->setCheckState(Qt::Unchecked); | ||
| 207 | ui->debug_enabled->setCheckState(Qt::Unchecked); | ||
| 208 | ui->touchscreen_enabled->setCheckState(Qt::Checked); | ||
| 209 | updateUIEnabled(); | ||
| 342 | } | 210 | } |
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index 32c7183f9..29a8a03f8 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include "core/settings.h" | 18 | #include "core/settings.h" |
| 19 | #include "input_common/main.h" | 19 | #include "input_common/main.h" |
| 20 | #include "ui_configure_input.h" | 20 | #include "ui_configure_input.h" |
| 21 | #include "yuzu/configuration/config.h" | ||
| 21 | 22 | ||
| 22 | class QPushButton; | 23 | class QPushButton; |
| 23 | class QString; | 24 | class QString; |
| @@ -37,57 +38,20 @@ public: | |||
| 37 | void applyConfiguration(); | 38 | void applyConfiguration(); |
| 38 | 39 | ||
| 39 | private: | 40 | private: |
| 40 | std::unique_ptr<Ui::ConfigureInput> ui; | 41 | void updateUIEnabled(); |
| 41 | |||
| 42 | std::unique_ptr<QTimer> timeout_timer; | ||
| 43 | std::unique_ptr<QTimer> poll_timer; | ||
| 44 | |||
| 45 | /// This will be the the setting function when an input is awaiting configuration. | ||
| 46 | std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; | ||
| 47 | |||
| 48 | std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; | ||
| 49 | std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; | ||
| 50 | |||
| 51 | static constexpr int ANALOG_SUB_BUTTONS_NUM = 5; | ||
| 52 | |||
| 53 | /// Each button input is represented by a QPushButton. | ||
| 54 | std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map; | ||
| 55 | 42 | ||
| 56 | /// A group of five QPushButtons represent one analog input. The buttons each represent up, | 43 | template <typename Dialog, typename... Args> |
| 57 | /// down, left, right, and modifier, respectively. | 44 | void CallConfigureDialog(Args&&... args); |
| 58 | std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs> | ||
| 59 | analog_map_buttons; | ||
| 60 | 45 | ||
| 61 | /// Analog inputs are also represented each with a single button, used to configure with an | 46 | void OnDockedModeChanged(bool last_state, bool new_state); |
| 62 | /// actual analog stick | ||
| 63 | std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; | ||
| 64 | |||
| 65 | static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; | ||
| 66 | |||
| 67 | std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; | ||
| 68 | |||
| 69 | /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, | ||
| 70 | /// keyboard events are ignored. | ||
| 71 | bool want_keyboard_keys = false; | ||
| 72 | 47 | ||
| 73 | /// Load configuration settings. | 48 | /// Load configuration settings. |
| 74 | void loadConfiguration(); | 49 | void loadConfiguration(); |
| 75 | /// Restore all buttons to their default values. | 50 | /// Restore all buttons to their default values. |
| 76 | void restoreDefaults(); | 51 | void restoreDefaults(); |
| 77 | /// Clear all input configuration | ||
| 78 | void ClearAll(); | ||
| 79 | 52 | ||
| 80 | /// Update UI to reflect current configuration. | 53 | std::unique_ptr<Ui::ConfigureInput> ui; |
| 81 | void updateButtonLabels(); | ||
| 82 | |||
| 83 | /// Called when the button was pressed. | ||
| 84 | void handleClick(QPushButton* button, | ||
| 85 | std::function<void(const Common::ParamPackage&)> new_input_setter, | ||
| 86 | InputCommon::Polling::DeviceType type); | ||
| 87 | |||
| 88 | /// Finish polling and configure input using the input_setter | ||
| 89 | void setPollingResult(const Common::ParamPackage& params, bool abort); | ||
| 90 | 54 | ||
| 91 | /// Handle key press events. | 55 | std::array<QComboBox*, 8> players_controller; |
| 92 | void keyPressEvent(QKeyEvent* event) override; | 56 | std::array<QPushButton*, 8> players_configure; |
| 93 | }; | 57 | }; |
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui index 8a019a693..dae8277bc 100644 --- a/src/yuzu/configuration/configure_input.ui +++ b/src/yuzu/configuration/configure_input.ui | |||
| @@ -6,8 +6,8 @@ | |||
| 6 | <rect> | 6 | <rect> |
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>343</width> | 9 | <width>473</width> |
| 10 | <height>677</height> | 10 | <height>685</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -15,740 +15,470 @@ | |||
| 15 | </property> | 15 | </property> |
| 16 | <layout class="QVBoxLayout" name="verticalLayout_5"> | 16 | <layout class="QVBoxLayout" name="verticalLayout_5"> |
| 17 | <item> | 17 | <item> |
| 18 | <layout class="QGridLayout" name="buttons"> | 18 | <layout class="QVBoxLayout" name="verticalLayout"> |
| 19 | <item row="3" column="1"> | 19 | <item> |
| 20 | <widget class="QGroupBox" name="misc"> | 20 | <widget class="QGroupBox" name="gridGroupBox"> |
| 21 | <property name="title"> | 21 | <property name="title"> |
| 22 | <string>Misc.</string> | 22 | <string>Players</string> |
| 23 | </property> | ||
| 24 | <property name="flat"> | ||
| 25 | <bool>false</bool> | ||
| 26 | </property> | ||
| 27 | <property name="checkable"> | ||
| 28 | <bool>false</bool> | ||
| 29 | </property> | 23 | </property> |
| 30 | <layout class="QGridLayout" name="gridLayout_6"> | 24 | <layout class="QGridLayout" name="gridLayout"> |
| 31 | <item row="0" column="0"> | 25 | <item row="1" column="2"> |
| 32 | <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout"> | 26 | <widget class="QComboBox" name="player1_combobox"> |
| 33 | <item> | 27 | <property name="minimumSize"> |
| 34 | <widget class="QLabel" name="labelMinus"> | 28 | <size> |
| 35 | <property name="text"> | 29 | <width>110</width> |
| 36 | <string>Minus:</string> | 30 | <height>0</height> |
| 37 | </property> | 31 | </size> |
| 38 | </widget> | 32 | </property> |
| 39 | </item> | 33 | </widget> |
| 40 | <item> | ||
| 41 | <widget class="QPushButton" name="buttonMinus"> | ||
| 42 | <property name="text"> | ||
| 43 | <string/> | ||
| 44 | </property> | ||
| 45 | </widget> | ||
| 46 | </item> | ||
| 47 | </layout> | ||
| 48 | </item> | 34 | </item> |
| 49 | <item row="0" column="1"> | 35 | <item row="1" column="3"> |
| 50 | <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout"> | 36 | <widget class="QPushButton" name="player1_configure"> |
| 51 | <item> | 37 | <property name="text"> |
| 52 | <widget class="QLabel" name="labelPlus"> | 38 | <string>Configure</string> |
| 53 | <property name="text"> | 39 | </property> |
| 54 | <string>Plus:</string> | 40 | </widget> |
| 55 | </property> | ||
| 56 | </widget> | ||
| 57 | </item> | ||
| 58 | <item> | ||
| 59 | <widget class="QPushButton" name="buttonPlus"> | ||
| 60 | <property name="text"> | ||
| 61 | <string/> | ||
| 62 | </property> | ||
| 63 | </widget> | ||
| 64 | </item> | ||
| 65 | </layout> | ||
| 66 | </item> | 41 | </item> |
| 67 | <item row="1" column="0"> | 42 | <item row="0" column="2"> |
| 68 | <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout"> | 43 | <widget class="QLabel" name="label"> |
| 69 | <item> | 44 | <property name="text"> |
| 70 | <widget class="QLabel" name="labelHome"> | 45 | <string>Controller Type</string> |
| 71 | <property name="text"> | 46 | </property> |
| 72 | <string>Home:</string> | 47 | <property name="alignment"> |
| 73 | </property> | 48 | <set>Qt::AlignCenter</set> |
| 74 | </widget> | 49 | </property> |
| 75 | </item> | 50 | </widget> |
| 76 | <item> | ||
| 77 | <widget class="QPushButton" name="buttonHome"> | ||
| 78 | <property name="text"> | ||
| 79 | <string/> | ||
| 80 | </property> | ||
| 81 | </widget> | ||
| 82 | </item> | ||
| 83 | </layout> | ||
| 84 | </item> | 51 | </item> |
| 85 | <item row="1" column="1"> | 52 | <item row="2" column="2"> |
| 86 | <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout"> | 53 | <widget class="QComboBox" name="player2_combobox"> |
| 87 | <item> | 54 | <property name="minimumSize"> |
| 88 | <widget class="QLabel" name="labelScrCap"> | 55 | <size> |
| 89 | <property name="text"> | 56 | <width>110</width> |
| 90 | <string>Screen | 57 | <height>0</height> |
| 91 | Capture:</string> | 58 | </size> |
| 92 | </property> | 59 | </property> |
| 93 | </widget> | 60 | </widget> |
| 94 | </item> | ||
| 95 | <item> | ||
| 96 | <widget class="QPushButton" name="buttonScreenshot"> | ||
| 97 | <property name="text"> | ||
| 98 | <string/> | ||
| 99 | </property> | ||
| 100 | </widget> | ||
| 101 | </item> | ||
| 102 | </layout> | ||
| 103 | </item> | 61 | </item> |
| 104 | <item row="2" column="1"> | 62 | <item row="3" column="2"> |
| 105 | <spacer name="verticalSpacer"> | 63 | <widget class="QComboBox" name="player3_combobox"> |
| 64 | <property name="minimumSize"> | ||
| 65 | <size> | ||
| 66 | <width>110</width> | ||
| 67 | <height>0</height> | ||
| 68 | </size> | ||
| 69 | </property> | ||
| 70 | </widget> | ||
| 71 | </item> | ||
| 72 | <item row="4" column="2"> | ||
| 73 | <widget class="QComboBox" name="player4_combobox"> | ||
| 74 | <property name="minimumSize"> | ||
| 75 | <size> | ||
| 76 | <width>110</width> | ||
| 77 | <height>0</height> | ||
| 78 | </size> | ||
| 79 | </property> | ||
| 80 | </widget> | ||
| 81 | </item> | ||
| 82 | <item row="5" column="2"> | ||
| 83 | <widget class="QComboBox" name="player5_combobox"> | ||
| 84 | <property name="minimumSize"> | ||
| 85 | <size> | ||
| 86 | <width>110</width> | ||
| 87 | <height>0</height> | ||
| 88 | </size> | ||
| 89 | </property> | ||
| 90 | </widget> | ||
| 91 | </item> | ||
| 92 | <item row="6" column="2"> | ||
| 93 | <widget class="QComboBox" name="player6_combobox"> | ||
| 94 | <property name="minimumSize"> | ||
| 95 | <size> | ||
| 96 | <width>110</width> | ||
| 97 | <height>0</height> | ||
| 98 | </size> | ||
| 99 | </property> | ||
| 100 | </widget> | ||
| 101 | </item> | ||
| 102 | <item row="7" column="2"> | ||
| 103 | <widget class="QComboBox" name="player7_combobox"> | ||
| 104 | <property name="minimumSize"> | ||
| 105 | <size> | ||
| 106 | <width>110</width> | ||
| 107 | <height>0</height> | ||
| 108 | </size> | ||
| 109 | </property> | ||
| 110 | </widget> | ||
| 111 | </item> | ||
| 112 | <item row="8" column="2"> | ||
| 113 | <widget class="QComboBox" name="player8_combobox"> | ||
| 114 | <property name="minimumSize"> | ||
| 115 | <size> | ||
| 116 | <width>110</width> | ||
| 117 | <height>0</height> | ||
| 118 | </size> | ||
| 119 | </property> | ||
| 120 | </widget> | ||
| 121 | </item> | ||
| 122 | <item row="2" column="3"> | ||
| 123 | <widget class="QPushButton" name="player2_configure"> | ||
| 124 | <property name="text"> | ||
| 125 | <string>Configure</string> | ||
| 126 | </property> | ||
| 127 | </widget> | ||
| 128 | </item> | ||
| 129 | <item row="3" column="3"> | ||
| 130 | <widget class="QPushButton" name="player3_configure"> | ||
| 131 | <property name="text"> | ||
| 132 | <string>Configure</string> | ||
| 133 | </property> | ||
| 134 | </widget> | ||
| 135 | </item> | ||
| 136 | <item row="4" column="3"> | ||
| 137 | <widget class="QPushButton" name="player4_configure"> | ||
| 138 | <property name="text"> | ||
| 139 | <string>Configure</string> | ||
| 140 | </property> | ||
| 141 | </widget> | ||
| 142 | </item> | ||
| 143 | <item row="5" column="3"> | ||
| 144 | <widget class="QPushButton" name="player5_configure"> | ||
| 145 | <property name="text"> | ||
| 146 | <string>Configure</string> | ||
| 147 | </property> | ||
| 148 | </widget> | ||
| 149 | </item> | ||
| 150 | <item row="6" column="3"> | ||
| 151 | <widget class="QPushButton" name="player6_configure"> | ||
| 152 | <property name="text"> | ||
| 153 | <string>Configure</string> | ||
| 154 | </property> | ||
| 155 | </widget> | ||
| 156 | </item> | ||
| 157 | <item row="7" column="3"> | ||
| 158 | <widget class="QPushButton" name="player7_configure"> | ||
| 159 | <property name="text"> | ||
| 160 | <string>Configure</string> | ||
| 161 | </property> | ||
| 162 | </widget> | ||
| 163 | </item> | ||
| 164 | <item row="8" column="3"> | ||
| 165 | <widget class="QPushButton" name="player8_configure"> | ||
| 166 | <property name="text"> | ||
| 167 | <string>Configure</string> | ||
| 168 | </property> | ||
| 169 | </widget> | ||
| 170 | </item> | ||
| 171 | <item row="0" column="0"> | ||
| 172 | <spacer name="horizontalSpacer"> | ||
| 106 | <property name="orientation"> | 173 | <property name="orientation"> |
| 107 | <enum>Qt::Vertical</enum> | 174 | <enum>Qt::Horizontal</enum> |
| 108 | </property> | 175 | </property> |
| 109 | <property name="sizeHint" stdset="0"> | 176 | <property name="sizeHint" stdset="0"> |
| 110 | <size> | 177 | <size> |
| 111 | <width>20</width> | 178 | <width>40</width> |
| 112 | <height>40</height> | 179 | <height>20</height> |
| 113 | </size> | 180 | </size> |
| 114 | </property> | 181 | </property> |
| 115 | </spacer> | 182 | </spacer> |
| 116 | </item> | 183 | </item> |
| 117 | </layout> | 184 | <item row="0" column="4"> |
| 118 | </widget> | 185 | <spacer name="horizontalSpacer_2"> |
| 119 | </item> | 186 | <property name="orientation"> |
| 120 | <item row="0" column="0"> | 187 | <enum>Qt::Horizontal</enum> |
| 121 | <widget class="QGroupBox" name="faceButtons"> | 188 | </property> |
| 122 | <property name="title"> | 189 | <property name="sizeHint" stdset="0"> |
| 123 | <string>Face Buttons</string> | 190 | <size> |
| 124 | </property> | 191 | <width>40</width> |
| 125 | <property name="flat"> | 192 | <height>20</height> |
| 126 | <bool>false</bool> | 193 | </size> |
| 127 | </property> | 194 | </property> |
| 128 | <property name="checkable"> | 195 | </spacer> |
| 129 | <bool>false</bool> | ||
| 130 | </property> | ||
| 131 | <layout class="QGridLayout" name="gridLayout"> | ||
| 132 | <item row="0" column="0"> | ||
| 133 | <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout"> | ||
| 134 | <item> | ||
| 135 | <widget class="QLabel" name="labelA"> | ||
| 136 | <property name="text"> | ||
| 137 | <string>A:</string> | ||
| 138 | </property> | ||
| 139 | </widget> | ||
| 140 | </item> | ||
| 141 | <item> | ||
| 142 | <widget class="QPushButton" name="buttonA"> | ||
| 143 | <property name="text"> | ||
| 144 | <string/> | ||
| 145 | </property> | ||
| 146 | </widget> | ||
| 147 | </item> | ||
| 148 | </layout> | ||
| 149 | </item> | 196 | </item> |
| 150 | <item row="0" column="1"> | 197 | <item row="1" column="1"> |
| 151 | <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout"> | 198 | <widget class="QLabel" name="label_3"> |
| 152 | <item> | 199 | <property name="minimumSize"> |
| 153 | <widget class="QLabel" name="labelB"> | 200 | <size> |
| 154 | <property name="text"> | 201 | <width>55</width> |
| 155 | <string>B:</string> | 202 | <height>0</height> |
| 156 | </property> | 203 | </size> |
| 157 | </widget> | 204 | </property> |
| 158 | </item> | 205 | <property name="text"> |
| 159 | <item> | 206 | <string>Player 1</string> |
| 160 | <widget class="QPushButton" name="buttonB"> | 207 | </property> |
| 161 | <property name="text"> | 208 | </widget> |
| 162 | <string/> | ||
| 163 | </property> | ||
| 164 | </widget> | ||
| 165 | </item> | ||
| 166 | </layout> | ||
| 167 | </item> | 209 | </item> |
| 168 | <item row="1" column="0"> | 210 | <item row="2" column="1"> |
| 169 | <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout"> | 211 | <widget class="QLabel" name="label_4"> |
| 170 | <item> | 212 | <property name="text"> |
| 171 | <widget class="QLabel" name="labelX"> | 213 | <string>Player 2</string> |
| 172 | <property name="text"> | 214 | </property> |
| 173 | <string>X:</string> | 215 | </widget> |
| 174 | </property> | ||
| 175 | </widget> | ||
| 176 | </item> | ||
| 177 | <item> | ||
| 178 | <widget class="QPushButton" name="buttonX"> | ||
| 179 | <property name="text"> | ||
| 180 | <string/> | ||
| 181 | </property> | ||
| 182 | </widget> | ||
| 183 | </item> | ||
| 184 | </layout> | ||
| 185 | </item> | 216 | </item> |
| 186 | <item row="1" column="1"> | 217 | <item row="3" column="1"> |
| 187 | <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout"> | 218 | <widget class="QLabel" name="label_5"> |
| 188 | <item> | 219 | <property name="text"> |
| 189 | <widget class="QLabel" name="labelY"> | 220 | <string>Player 3</string> |
| 190 | <property name="text"> | 221 | </property> |
| 191 | <string>Y:</string> | 222 | </widget> |
| 192 | </property> | ||
| 193 | </widget> | ||
| 194 | </item> | ||
| 195 | <item> | ||
| 196 | <widget class="QPushButton" name="buttonY"> | ||
| 197 | <property name="text"> | ||
| 198 | <string/> | ||
| 199 | </property> | ||
| 200 | </widget> | ||
| 201 | </item> | ||
| 202 | </layout> | ||
| 203 | </item> | 223 | </item> |
| 204 | </layout> | 224 | <item row="4" column="1"> |
| 205 | </widget> | 225 | <widget class="QLabel" name="label_6"> |
| 206 | </item> | 226 | <property name="text"> |
| 207 | <item row="0" column="1"> | 227 | <string>Player 4</string> |
| 208 | <widget class="QGroupBox" name="Dpad"> | 228 | </property> |
| 209 | <property name="title"> | 229 | </widget> |
| 210 | <string>Directional Pad</string> | ||
| 211 | </property> | ||
| 212 | <property name="flat"> | ||
| 213 | <bool>false</bool> | ||
| 214 | </property> | ||
| 215 | <property name="checkable"> | ||
| 216 | <bool>false</bool> | ||
| 217 | </property> | ||
| 218 | <layout class="QGridLayout" name="gridLayout_2"> | ||
| 219 | <item row="1" column="0"> | ||
| 220 | <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout"> | ||
| 221 | <item> | ||
| 222 | <widget class="QLabel" name="labelDpadUp"> | ||
| 223 | <property name="text"> | ||
| 224 | <string>Up:</string> | ||
| 225 | </property> | ||
| 226 | </widget> | ||
| 227 | </item> | ||
| 228 | <item> | ||
| 229 | <widget class="QPushButton" name="buttonDpadUp"> | ||
| 230 | <property name="text"> | ||
| 231 | <string/> | ||
| 232 | </property> | ||
| 233 | </widget> | ||
| 234 | </item> | ||
| 235 | </layout> | ||
| 236 | </item> | 230 | </item> |
| 237 | <item row="1" column="1"> | 231 | <item row="5" column="1"> |
| 238 | <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout"> | 232 | <widget class="QLabel" name="label_7"> |
| 239 | <item> | 233 | <property name="text"> |
| 240 | <widget class="QLabel" name="labelDpadDown"> | 234 | <string>Player 5</string> |
| 241 | <property name="text"> | 235 | </property> |
| 242 | <string>Down:</string> | 236 | </widget> |
| 243 | </property> | ||
| 244 | </widget> | ||
| 245 | </item> | ||
| 246 | <item> | ||
| 247 | <widget class="QPushButton" name="buttonDpadDown"> | ||
| 248 | <property name="text"> | ||
| 249 | <string/> | ||
| 250 | </property> | ||
| 251 | </widget> | ||
| 252 | </item> | ||
| 253 | </layout> | ||
| 254 | </item> | 237 | </item> |
| 255 | <item row="0" column="0"> | 238 | <item row="6" column="1"> |
| 256 | <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout"> | 239 | <widget class="QLabel" name="label_8"> |
| 257 | <item> | 240 | <property name="text"> |
| 258 | <widget class="QLabel" name="labelDpadLeft"> | 241 | <string>Player 6</string> |
| 259 | <property name="text"> | 242 | </property> |
| 260 | <string>Left:</string> | 243 | </widget> |
| 261 | </property> | ||
| 262 | </widget> | ||
| 263 | </item> | ||
| 264 | <item> | ||
| 265 | <widget class="QPushButton" name="buttonDpadLeft"> | ||
| 266 | <property name="text"> | ||
| 267 | <string/> | ||
| 268 | </property> | ||
| 269 | </widget> | ||
| 270 | </item> | ||
| 271 | </layout> | ||
| 272 | </item> | 244 | </item> |
| 273 | <item row="0" column="1"> | 245 | <item row="7" column="1"> |
| 274 | <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout"> | 246 | <widget class="QLabel" name="label_9"> |
| 275 | <item> | 247 | <property name="text"> |
| 276 | <widget class="QLabel" name="labelDpadRight"> | 248 | <string>Player 7</string> |
| 277 | <property name="text"> | 249 | </property> |
| 278 | <string>Right:</string> | 250 | </widget> |
| 279 | </property> | 251 | </item> |
| 280 | </widget> | 252 | <item row="8" column="1"> |
| 281 | </item> | 253 | <widget class="QLabel" name="label_10"> |
| 282 | <item> | 254 | <property name="text"> |
| 283 | <widget class="QPushButton" name="buttonDpadRight"> | 255 | <string>Player 8</string> |
| 284 | <property name="text"> | 256 | </property> |
| 285 | <string/> | 257 | </widget> |
| 286 | </property> | ||
| 287 | </widget> | ||
| 288 | </item> | ||
| 289 | </layout> | ||
| 290 | </item> | 258 | </item> |
| 291 | </layout> | 259 | </layout> |
| 292 | </widget> | 260 | </widget> |
| 293 | </item> | 261 | </item> |
| 294 | <item row="3" column="0"> | 262 | <item> |
| 295 | <widget class="QGroupBox" name="shoulderButtons"> | 263 | <widget class="QGroupBox" name="gridGroupBox"> |
| 296 | <property name="title"> | 264 | <property name="title"> |
| 297 | <string>Shoulder Buttons</string> | 265 | <string>Handheld</string> |
| 298 | </property> | ||
| 299 | <property name="flat"> | ||
| 300 | <bool>false</bool> | ||
| 301 | </property> | 266 | </property> |
| 302 | <property name="checkable"> | 267 | <layout class="QGridLayout" name="gridLayout_2"> |
| 303 | <bool>false</bool> | 268 | <item row="1" column="2"> |
| 304 | </property> | 269 | <spacer name="horizontalSpacer_5"> |
| 305 | <layout class="QGridLayout" name="gridLayout_3"> | 270 | <property name="orientation"> |
| 306 | <item row="0" column="0"> | 271 | <enum>Qt::Horizontal</enum> |
| 307 | <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout"> | 272 | </property> |
| 308 | <item> | 273 | <property name="sizeType"> |
| 309 | <widget class="QLabel" name="labelL"> | 274 | <enum>QSizePolicy::Fixed</enum> |
| 310 | <property name="text"> | 275 | </property> |
| 311 | <string>L:</string> | 276 | <property name="sizeHint" stdset="0"> |
| 312 | </property> | 277 | <size> |
| 313 | </widget> | 278 | <width>72</width> |
| 314 | </item> | 279 | <height>20</height> |
| 315 | <item> | 280 | </size> |
| 316 | <widget class="QPushButton" name="buttonL"> | 281 | </property> |
| 317 | <property name="text"> | 282 | </spacer> |
| 318 | <string/> | ||
| 319 | </property> | ||
| 320 | </widget> | ||
| 321 | </item> | ||
| 322 | </layout> | ||
| 323 | </item> | 283 | </item> |
| 324 | <item row="0" column="1"> | 284 | <item row="1" column="4"> |
| 325 | <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> | 285 | <spacer name="horizontalSpacer_4"> |
| 326 | <item> | 286 | <property name="orientation"> |
| 327 | <widget class="QLabel" name="labelR"> | 287 | <enum>Qt::Horizontal</enum> |
| 328 | <property name="text"> | 288 | </property> |
| 329 | <string>R:</string> | 289 | <property name="sizeHint" stdset="0"> |
| 330 | </property> | 290 | <size> |
| 331 | </widget> | 291 | <width>40</width> |
| 332 | </item> | 292 | <height>20</height> |
| 333 | <item> | 293 | </size> |
| 334 | <widget class="QPushButton" name="buttonR"> | 294 | </property> |
| 335 | <property name="text"> | 295 | </spacer> |
| 336 | <string/> | 296 | </item> |
| 337 | </property> | 297 | <item row="1" column="3"> |
| 338 | </widget> | 298 | <widget class="QPushButton" name="handheld_configure"> |
| 339 | </item> | 299 | <property name="text"> |
| 340 | </layout> | 300 | <string>Configure</string> |
| 301 | </property> | ||
| 302 | </widget> | ||
| 341 | </item> | 303 | </item> |
| 342 | <item row="1" column="0"> | 304 | <item row="1" column="0"> |
| 343 | <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout"> | 305 | <spacer name="horizontalSpacer_3"> |
| 344 | <item> | 306 | <property name="orientation"> |
| 345 | <widget class="QLabel" name="labelZL"> | 307 | <enum>Qt::Horizontal</enum> |
| 346 | <property name="text"> | 308 | </property> |
| 347 | <string>ZL:</string> | 309 | <property name="sizeHint" stdset="0"> |
| 348 | </property> | 310 | <size> |
| 349 | </widget> | 311 | <width>40</width> |
| 350 | </item> | 312 | <height>20</height> |
| 351 | <item> | 313 | </size> |
| 352 | <widget class="QPushButton" name="buttonZL"> | 314 | </property> |
| 353 | <property name="text"> | 315 | </spacer> |
| 354 | <string/> | ||
| 355 | </property> | ||
| 356 | </widget> | ||
| 357 | </item> | ||
| 358 | </layout> | ||
| 359 | </item> | 316 | </item> |
| 360 | <item row="1" column="1"> | 317 | <item row="1" column="1"> |
| 361 | <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout"> | 318 | <widget class="QCheckBox" name="handheld_connected"> |
| 362 | <item> | 319 | <property name="text"> |
| 363 | <widget class="QLabel" name="labelZR"> | 320 | <string>Joycons Docked</string> |
| 364 | <property name="text"> | 321 | </property> |
| 365 | <string>ZR:</string> | 322 | </widget> |
| 366 | </property> | ||
| 367 | </widget> | ||
| 368 | </item> | ||
| 369 | <item> | ||
| 370 | <widget class="QPushButton" name="buttonZR"> | ||
| 371 | <property name="text"> | ||
| 372 | <string/> | ||
| 373 | </property> | ||
| 374 | </widget> | ||
| 375 | </item> | ||
| 376 | </layout> | ||
| 377 | </item> | ||
| 378 | <item row="2" column="0"> | ||
| 379 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> | ||
| 380 | <item> | ||
| 381 | <widget class="QLabel" name="labelSL"> | ||
| 382 | <property name="text"> | ||
| 383 | <string>SL:</string> | ||
| 384 | </property> | ||
| 385 | </widget> | ||
| 386 | </item> | ||
| 387 | <item> | ||
| 388 | <widget class="QPushButton" name="buttonSL"> | ||
| 389 | <property name="text"> | ||
| 390 | <string/> | ||
| 391 | </property> | ||
| 392 | </widget> | ||
| 393 | </item> | ||
| 394 | </layout> | ||
| 395 | </item> | 323 | </item> |
| 396 | <item row="2" column="1"> | 324 | <item row="0" column="1"> |
| 397 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> | 325 | <widget class="QCheckBox" name="use_docked_mode"> |
| 398 | <item> | 326 | <property name="text"> |
| 399 | <widget class="QLabel" name="labelSR"> | 327 | <string>Use Docked Mode</string> |
| 400 | <property name="text"> | 328 | </property> |
| 401 | <string>SR:</string> | 329 | </widget> |
| 402 | </property> | ||
| 403 | </widget> | ||
| 404 | </item> | ||
| 405 | <item> | ||
| 406 | <widget class="QPushButton" name="buttonSR"> | ||
| 407 | <property name="text"> | ||
| 408 | <string/> | ||
| 409 | </property> | ||
| 410 | </widget> | ||
| 411 | </item> | ||
| 412 | </layout> | ||
| 413 | </item> | 330 | </item> |
| 414 | </layout> | 331 | </layout> |
| 415 | </widget> | 332 | </widget> |
| 416 | </item> | 333 | </item> |
| 417 | <item row="1" column="1"> | 334 | <item> |
| 418 | <widget class="QGroupBox" name="RStick"> | 335 | <widget class="QGroupBox" name="gridGroupBox"> |
| 419 | <property name="title"> | 336 | <property name="title"> |
| 420 | <string>Right Stick</string> | 337 | <string>Other</string> |
| 421 | </property> | ||
| 422 | <property name="alignment"> | ||
| 423 | <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> | ||
| 424 | </property> | ||
| 425 | <property name="flat"> | ||
| 426 | <bool>false</bool> | ||
| 427 | </property> | 338 | </property> |
| 428 | <property name="checkable"> | 339 | <layout class="QGridLayout" name="gridLayout_3"> |
| 429 | <bool>false</bool> | ||
| 430 | </property> | ||
| 431 | <layout class="QGridLayout" name="gridLayout_5"> | ||
| 432 | <item row="1" column="1"> | 340 | <item row="1" column="1"> |
| 433 | <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout"> | 341 | <widget class="QCheckBox" name="keyboard_enabled"> |
| 434 | <item> | 342 | <property name="minimumSize"> |
| 435 | <widget class="QLabel" name="labelRStickDown"> | 343 | <size> |
| 436 | <property name="text"> | 344 | <width>0</width> |
| 437 | <string>Down:</string> | 345 | <height>23</height> |
| 438 | </property> | 346 | </size> |
| 439 | </widget> | 347 | </property> |
| 440 | </item> | ||
| 441 | <item> | ||
| 442 | <widget class="QPushButton" name="buttonRStickDown"> | ||
| 443 | <property name="text"> | ||
| 444 | <string/> | ||
| 445 | </property> | ||
| 446 | </widget> | ||
| 447 | </item> | ||
| 448 | </layout> | ||
| 449 | </item> | ||
| 450 | <item row="0" column="1"> | ||
| 451 | <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout"> | ||
| 452 | <item> | ||
| 453 | <widget class="QLabel" name="labelRStickRight"> | ||
| 454 | <property name="text"> | ||
| 455 | <string>Right:</string> | ||
| 456 | </property> | ||
| 457 | </widget> | ||
| 458 | </item> | ||
| 459 | <item> | ||
| 460 | <widget class="QPushButton" name="buttonRStickRight"> | ||
| 461 | <property name="text"> | ||
| 462 | <string/> | ||
| 463 | </property> | ||
| 464 | </widget> | ||
| 465 | </item> | ||
| 466 | </layout> | ||
| 467 | </item> | ||
| 468 | <item row="3" column="0" colspan="2"> | ||
| 469 | <widget class="QPushButton" name="buttonRStickAnalog"> | ||
| 470 | <property name="text"> | 348 | <property name="text"> |
| 471 | <string>Set Analog Stick</string> | 349 | <string>Keyboard</string> |
| 472 | </property> | 350 | </property> |
| 473 | </widget> | 351 | </widget> |
| 474 | </item> | 352 | </item> |
| 475 | <item row="0" column="0"> | ||
| 476 | <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout"> | ||
| 477 | <item> | ||
| 478 | <widget class="QLabel" name="labelRStickLeft"> | ||
| 479 | <property name="text"> | ||
| 480 | <string>Left:</string> | ||
| 481 | </property> | ||
| 482 | </widget> | ||
| 483 | </item> | ||
| 484 | <item> | ||
| 485 | <widget class="QPushButton" name="buttonRStickLeft"> | ||
| 486 | <property name="text"> | ||
| 487 | <string/> | ||
| 488 | </property> | ||
| 489 | </widget> | ||
| 490 | </item> | ||
| 491 | </layout> | ||
| 492 | </item> | ||
| 493 | <item row="1" column="0"> | ||
| 494 | <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout"> | ||
| 495 | <item> | ||
| 496 | <widget class="QLabel" name="labelRStickUp"> | ||
| 497 | <property name="text"> | ||
| 498 | <string>Up:</string> | ||
| 499 | </property> | ||
| 500 | </widget> | ||
| 501 | </item> | ||
| 502 | <item> | ||
| 503 | <widget class="QPushButton" name="buttonRStickUp"> | ||
| 504 | <property name="text"> | ||
| 505 | <string/> | ||
| 506 | </property> | ||
| 507 | </widget> | ||
| 508 | </item> | ||
| 509 | </layout> | ||
| 510 | </item> | ||
| 511 | <item row="2" column="0"> | ||
| 512 | <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout"> | ||
| 513 | <item> | ||
| 514 | <widget class="QLabel" name="labelRStickPressed"> | ||
| 515 | <property name="text"> | ||
| 516 | <string>Pressed:</string> | ||
| 517 | </property> | ||
| 518 | </widget> | ||
| 519 | </item> | ||
| 520 | <item> | ||
| 521 | <widget class="QPushButton" name="buttonRStick"> | ||
| 522 | <property name="text"> | ||
| 523 | <string/> | ||
| 524 | </property> | ||
| 525 | </widget> | ||
| 526 | </item> | ||
| 527 | </layout> | ||
| 528 | </item> | ||
| 529 | <item row="2" column="1"> | 353 | <item row="2" column="1"> |
| 530 | <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout"> | 354 | <widget class="QCheckBox" name="debug_enabled"> |
| 531 | <item> | 355 | <property name="text"> |
| 532 | <widget class="QLabel" name="labelRStickMod"> | 356 | <string>Debug Controller</string> |
| 533 | <property name="text"> | 357 | </property> |
| 534 | <string>Modifier:</string> | 358 | </widget> |
| 535 | </property> | ||
| 536 | </widget> | ||
| 537 | </item> | ||
| 538 | <item> | ||
| 539 | <widget class="QPushButton" name="buttonRStickMod"> | ||
| 540 | <property name="text"> | ||
| 541 | <string/> | ||
| 542 | </property> | ||
| 543 | </widget> | ||
| 544 | </item> | ||
| 545 | </layout> | ||
| 546 | </item> | 359 | </item> |
| 547 | </layout> | 360 | <item row="3" column="1"> |
| 548 | </widget> | 361 | <widget class="QCheckBox" name="touchscreen_enabled"> |
| 549 | </item> | ||
| 550 | <item row="1" column="0"> | ||
| 551 | <widget class="QGroupBox" name="LStick"> | ||
| 552 | <property name="title"> | ||
| 553 | <string>Left Stick</string> | ||
| 554 | </property> | ||
| 555 | <property name="flat"> | ||
| 556 | <bool>false</bool> | ||
| 557 | </property> | ||
| 558 | <property name="checkable"> | ||
| 559 | <bool>false</bool> | ||
| 560 | </property> | ||
| 561 | <layout class="QGridLayout" name="gridLayout_4"> | ||
| 562 | <item row="1" column="1"> | ||
| 563 | <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout"> | ||
| 564 | <item> | ||
| 565 | <widget class="QLabel" name="labelLStickDown"> | ||
| 566 | <property name="text"> | ||
| 567 | <string>Down:</string> | ||
| 568 | </property> | ||
| 569 | </widget> | ||
| 570 | </item> | ||
| 571 | <item> | ||
| 572 | <widget class="QPushButton" name="buttonLStickDown"> | ||
| 573 | <property name="text"> | ||
| 574 | <string/> | ||
| 575 | </property> | ||
| 576 | </widget> | ||
| 577 | </item> | ||
| 578 | </layout> | ||
| 579 | </item> | ||
| 580 | <item row="4" column="0" colspan="2"> | ||
| 581 | <widget class="QPushButton" name="buttonLStickAnalog"> | ||
| 582 | <property name="text"> | 362 | <property name="text"> |
| 583 | <string>Set Analog Stick</string> | 363 | <string>Touchscreen</string> |
| 584 | </property> | 364 | </property> |
| 585 | </widget> | 365 | </widget> |
| 586 | </item> | 366 | </item> |
| 587 | <item row="0" column="1"> | 367 | <item row="0" column="1"> |
| 588 | <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout"> | 368 | <widget class="QCheckBox" name="mouse_enabled"> |
| 589 | <item> | 369 | <property name="minimumSize"> |
| 590 | <widget class="QLabel" name="labelLStickRight"> | 370 | <size> |
| 591 | <property name="text"> | 371 | <width>0</width> |
| 592 | <string>Right:</string> | 372 | <height>23</height> |
| 593 | </property> | 373 | </size> |
| 594 | </widget> | 374 | </property> |
| 595 | </item> | 375 | <property name="text"> |
| 596 | <item> | 376 | <string>Mouse</string> |
| 597 | <widget class="QPushButton" name="buttonLStickRight"> | 377 | </property> |
| 598 | <property name="text"> | 378 | </widget> |
| 599 | <string/> | 379 | </item> |
| 600 | </property> | 380 | <item row="0" column="4"> |
| 601 | </widget> | 381 | <spacer name="horizontalSpacer_7"> |
| 602 | </item> | 382 | <property name="orientation"> |
| 603 | </layout> | 383 | <enum>Qt::Horizontal</enum> |
| 384 | </property> | ||
| 385 | <property name="sizeHint" stdset="0"> | ||
| 386 | <size> | ||
| 387 | <width>40</width> | ||
| 388 | <height>20</height> | ||
| 389 | </size> | ||
| 390 | </property> | ||
| 391 | </spacer> | ||
| 392 | </item> | ||
| 393 | <item row="0" column="2"> | ||
| 394 | <spacer name="horizontalSpacer_8"> | ||
| 395 | <property name="orientation"> | ||
| 396 | <enum>Qt::Horizontal</enum> | ||
| 397 | </property> | ||
| 398 | <property name="sizeType"> | ||
| 399 | <enum>QSizePolicy::Fixed</enum> | ||
| 400 | </property> | ||
| 401 | <property name="sizeHint" stdset="0"> | ||
| 402 | <size> | ||
| 403 | <width>76</width> | ||
| 404 | <height>20</height> | ||
| 405 | </size> | ||
| 406 | </property> | ||
| 407 | </spacer> | ||
| 604 | </item> | 408 | </item> |
| 605 | <item row="0" column="0"> | 409 | <item row="0" column="0"> |
| 606 | <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout"> | 410 | <spacer name="horizontalSpacer_6"> |
| 607 | <item> | 411 | <property name="orientation"> |
| 608 | <widget class="QLabel" name="labelLStickLeft"> | 412 | <enum>Qt::Horizontal</enum> |
| 609 | <property name="text"> | 413 | </property> |
| 610 | <string>Left:</string> | 414 | <property name="sizeHint" stdset="0"> |
| 611 | </property> | 415 | <size> |
| 612 | </widget> | 416 | <width>40</width> |
| 613 | </item> | 417 | <height>20</height> |
| 614 | <item> | 418 | </size> |
| 615 | <widget class="QPushButton" name="buttonLStickLeft"> | 419 | </property> |
| 616 | <property name="text"> | 420 | </spacer> |
| 617 | <string/> | ||
| 618 | </property> | ||
| 619 | </widget> | ||
| 620 | </item> | ||
| 621 | </layout> | ||
| 622 | </item> | 421 | </item> |
| 623 | <item row="1" column="0"> | 422 | <item row="3" column="3"> |
| 624 | <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout"> | 423 | <widget class="QPushButton" name="touchscreen_advanced"> |
| 625 | <item> | 424 | <property name="text"> |
| 626 | <widget class="QLabel" name="labelLStickUp"> | 425 | <string>Advanced</string> |
| 627 | <property name="text"> | 426 | </property> |
| 628 | <string>Up:</string> | 427 | </widget> |
| 629 | </property> | ||
| 630 | </widget> | ||
| 631 | </item> | ||
| 632 | <item> | ||
| 633 | <widget class="QPushButton" name="buttonLStickUp"> | ||
| 634 | <property name="text"> | ||
| 635 | <string/> | ||
| 636 | </property> | ||
| 637 | </widget> | ||
| 638 | </item> | ||
| 639 | </layout> | ||
| 640 | </item> | ||
| 641 | <item row="3" column="0"> | ||
| 642 | <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout"> | ||
| 643 | <item> | ||
| 644 | <widget class="QLabel" name="labelLStickMod"> | ||
| 645 | <property name="text"> | ||
| 646 | <string>Modifier:</string> | ||
| 647 | </property> | ||
| 648 | </widget> | ||
| 649 | </item> | ||
| 650 | <item> | ||
| 651 | <widget class="QPushButton" name="buttonLStickMod"> | ||
| 652 | <property name="text"> | ||
| 653 | <string/> | ||
| 654 | </property> | ||
| 655 | </widget> | ||
| 656 | </item> | ||
| 657 | </layout> | ||
| 658 | </item> | 428 | </item> |
| 659 | <item row="3" column="1"> | 429 | <item row="2" column="3"> |
| 660 | <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0"> | 430 | <widget class="QPushButton" name="debug_configure"> |
| 661 | <item> | 431 | <property name="text"> |
| 662 | <widget class="QLabel" name="labelLStickPressed"> | 432 | <string>Configure</string> |
| 663 | <property name="text"> | 433 | </property> |
| 664 | <string>Pressed:</string> | 434 | </widget> |
| 665 | </property> | 435 | </item> |
| 666 | </widget> | 436 | <item row="0" column="3"> |
| 667 | </item> | 437 | <widget class="QPushButton" name="mouse_advanced"> |
| 668 | <item> | 438 | <property name="text"> |
| 669 | <widget class="QPushButton" name="buttonLStick"> | 439 | <string>Advanced</string> |
| 670 | <property name="text"> | 440 | </property> |
| 671 | <string/> | 441 | </widget> |
| 672 | </property> | ||
| 673 | </widget> | ||
| 674 | </item> | ||
| 675 | </layout> | ||
| 676 | </item> | 442 | </item> |
| 677 | </layout> | 443 | </layout> |
| 678 | </widget> | 444 | </widget> |
| 679 | </item> | 445 | </item> |
| 680 | </layout> | ||
| 681 | </item> | ||
| 682 | <item> | ||
| 683 | <layout class="QHBoxLayout" name="horizontalLayout"> | ||
| 684 | <item> | 446 | <item> |
| 685 | <spacer name="horizontalSpacer"> | 447 | <spacer name="verticalSpacer"> |
| 686 | <property name="orientation"> | 448 | <property name="orientation"> |
| 687 | <enum>Qt::Horizontal</enum> | 449 | <enum>Qt::Vertical</enum> |
| 688 | </property> | 450 | </property> |
| 689 | <property name="sizeHint" stdset="0"> | 451 | <property name="sizeHint" stdset="0"> |
| 690 | <size> | 452 | <size> |
| 691 | <width>40</width> | 453 | <width>20</width> |
| 692 | <height>20</height> | 454 | <height>40</height> |
| 693 | </size> | 455 | </size> |
| 694 | </property> | 456 | </property> |
| 695 | </spacer> | 457 | </spacer> |
| 696 | </item> | 458 | </item> |
| 697 | <item> | 459 | <item> |
| 698 | <widget class="QPushButton" name="buttonClearAll"> | 460 | <layout class="QHBoxLayout" name="horizontalLayout"> |
| 699 | <property name="sizePolicy"> | 461 | <item> |
| 700 | <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | 462 | <widget class="QPushButton" name="restore_defaults_button"> |
| 701 | <horstretch>0</horstretch> | 463 | <property name="text"> |
| 702 | <verstretch>0</verstretch> | 464 | <string>Restore Defaults</string> |
| 703 | </sizepolicy> | 465 | </property> |
| 704 | </property> | 466 | </widget> |
| 705 | <property name="sizeIncrement"> | 467 | </item> |
| 706 | <size> | 468 | <item> |
| 707 | <width>0</width> | 469 | <spacer name="horizontalSpacer_9"> |
| 708 | <height>0</height> | 470 | <property name="orientation"> |
| 709 | </size> | 471 | <enum>Qt::Horizontal</enum> |
| 710 | </property> | 472 | </property> |
| 711 | <property name="baseSize"> | 473 | <property name="sizeHint" stdset="0"> |
| 712 | <size> | 474 | <size> |
| 713 | <width>0</width> | 475 | <width>40</width> |
| 714 | <height>0</height> | 476 | <height>20</height> |
| 715 | </size> | 477 | </size> |
| 716 | </property> | 478 | </property> |
| 717 | <property name="layoutDirection"> | 479 | </spacer> |
| 718 | <enum>Qt::LeftToRight</enum> | 480 | </item> |
| 719 | </property> | 481 | </layout> |
| 720 | <property name="text"> | ||
| 721 | <string>Clear All</string> | ||
| 722 | </property> | ||
| 723 | </widget> | ||
| 724 | </item> | ||
| 725 | <item> | ||
| 726 | <widget class="QPushButton" name="buttonRestoreDefaults"> | ||
| 727 | <property name="sizePolicy"> | ||
| 728 | <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | ||
| 729 | <horstretch>0</horstretch> | ||
| 730 | <verstretch>0</verstretch> | ||
| 731 | </sizepolicy> | ||
| 732 | </property> | ||
| 733 | <property name="sizeIncrement"> | ||
| 734 | <size> | ||
| 735 | <width>0</width> | ||
| 736 | <height>0</height> | ||
| 737 | </size> | ||
| 738 | </property> | ||
| 739 | <property name="baseSize"> | ||
| 740 | <size> | ||
| 741 | <width>0</width> | ||
| 742 | <height>0</height> | ||
| 743 | </size> | ||
| 744 | </property> | ||
| 745 | <property name="layoutDirection"> | ||
| 746 | <enum>Qt::LeftToRight</enum> | ||
| 747 | </property> | ||
| 748 | <property name="text"> | ||
| 749 | <string>Restore Defaults</string> | ||
| 750 | </property> | ||
| 751 | </widget> | ||
| 752 | </item> | 482 | </item> |
| 753 | </layout> | 483 | </layout> |
| 754 | </item> | 484 | </item> |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp new file mode 100644 index 000000000..ba6e09368 --- /dev/null +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -0,0 +1,508 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <memory> | ||
| 7 | #include <utility> | ||
| 8 | #include <QColorDialog> | ||
| 9 | #include <QMenu> | ||
| 10 | #include <QMessageBox> | ||
| 11 | #include <QTimer> | ||
| 12 | #include "common/assert.h" | ||
| 13 | #include "common/param_package.h" | ||
| 14 | #include "input_common/main.h" | ||
| 15 | #include "ui_configure_input_player.h" | ||
| 16 | #include "yuzu/configuration/config.h" | ||
| 17 | #include "yuzu/configuration/configure_input_player.h" | ||
| 18 | |||
| 19 | const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> | ||
| 20 | ConfigureInputPlayer::analog_sub_buttons{{ | ||
| 21 | "up", | ||
| 22 | "down", | ||
| 23 | "left", | ||
| 24 | "right", | ||
| 25 | "modifier", | ||
| 26 | }}; | ||
| 27 | |||
| 28 | static void MoveGridElement(QGridLayout* grid, int row_old, int column_old, int row_new, | ||
| 29 | int column_new) { | ||
| 30 | const auto item = grid->itemAtPosition(row_old, column_old); | ||
| 31 | // grid->removeItem(item); | ||
| 32 | grid->addItem(item, row_new, column_new); | ||
| 33 | } | ||
| 34 | |||
| 35 | static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) { | ||
| 36 | const int index1 = grid->indexOf(item); | ||
| 37 | const int index2 = grid->indexOf(onTopOf); | ||
| 38 | int row, column, rowSpan, columnSpan; | ||
| 39 | grid->getItemPosition(index2, &row, &column, &rowSpan, &columnSpan); | ||
| 40 | grid->takeAt(index1); | ||
| 41 | grid->addWidget(item, row, column, rowSpan, columnSpan); | ||
| 42 | } | ||
| 43 | |||
| 44 | static QString GetKeyName(int key_code) { | ||
| 45 | switch (key_code) { | ||
| 46 | case Qt::Key_Shift: | ||
| 47 | return QObject::tr("Shift"); | ||
| 48 | case Qt::Key_Control: | ||
| 49 | return QObject::tr("Ctrl"); | ||
| 50 | case Qt::Key_Alt: | ||
| 51 | return QObject::tr("Alt"); | ||
| 52 | case Qt::Key_Meta: | ||
| 53 | return ""; | ||
| 54 | default: | ||
| 55 | return QKeySequence(key_code).toString(); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | static void SetAnalogButton(const Common::ParamPackage& input_param, | ||
| 60 | Common::ParamPackage& analog_param, const std::string& button_name) { | ||
| 61 | if (analog_param.Get("engine", "") != "analog_from_button") { | ||
| 62 | analog_param = { | ||
| 63 | {"engine", "analog_from_button"}, | ||
| 64 | {"modifier_scale", "0.5"}, | ||
| 65 | }; | ||
| 66 | } | ||
| 67 | analog_param.Set(button_name, input_param.Serialize()); | ||
| 68 | } | ||
| 69 | |||
| 70 | static QString ButtonToText(const Common::ParamPackage& param) { | ||
| 71 | if (!param.Has("engine")) { | ||
| 72 | return QObject::tr("[not set]"); | ||
| 73 | } else if (param.Get("engine", "") == "keyboard") { | ||
| 74 | return GetKeyName(param.Get("code", 0)); | ||
| 75 | } else if (param.Get("engine", "") == "sdl") { | ||
| 76 | if (param.Has("hat")) { | ||
| 77 | return QString(QObject::tr("Hat %1 %2")) | ||
| 78 | .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str()); | ||
| 79 | } | ||
| 80 | if (param.Has("axis")) { | ||
| 81 | return QString(QObject::tr("Axis %1%2")) | ||
| 82 | .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str()); | ||
| 83 | } | ||
| 84 | if (param.Has("button")) { | ||
| 85 | return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str()); | ||
| 86 | } | ||
| 87 | return QString(); | ||
| 88 | } else { | ||
| 89 | return QObject::tr("[unknown]"); | ||
| 90 | } | ||
| 91 | }; | ||
| 92 | |||
| 93 | static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) { | ||
| 94 | if (!param.Has("engine")) { | ||
| 95 | return QObject::tr("[not set]"); | ||
| 96 | } else if (param.Get("engine", "") == "analog_from_button") { | ||
| 97 | return ButtonToText(Common::ParamPackage{param.Get(dir, "")}); | ||
| 98 | } else if (param.Get("engine", "") == "sdl") { | ||
| 99 | if (dir == "modifier") { | ||
| 100 | return QString(QObject::tr("[unused]")); | ||
| 101 | } | ||
| 102 | |||
| 103 | if (dir == "left" || dir == "right") { | ||
| 104 | return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str()); | ||
| 105 | } else if (dir == "up" || dir == "down") { | ||
| 106 | return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str()); | ||
| 107 | } | ||
| 108 | return QString(); | ||
| 109 | } else { | ||
| 110 | return QObject::tr("[unknown]"); | ||
| 111 | } | ||
| 112 | }; | ||
| 113 | |||
| 114 | ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug) | ||
| 115 | : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), | ||
| 116 | timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()), | ||
| 117 | player_index(player_index), debug(debug) { | ||
| 118 | |||
| 119 | ui->setupUi(this); | ||
| 120 | setFocusPolicy(Qt::ClickFocus); | ||
| 121 | |||
| 122 | button_map = { | ||
| 123 | ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, | ||
| 124 | ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, | ||
| 125 | ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, | ||
| 126 | ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, | ||
| 127 | ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown, | ||
| 128 | ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown, | ||
| 129 | ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot, | ||
| 130 | }; | ||
| 131 | |||
| 132 | analog_map_buttons = {{ | ||
| 133 | { | ||
| 134 | ui->buttonLStickUp, | ||
| 135 | ui->buttonLStickDown, | ||
| 136 | ui->buttonLStickLeft, | ||
| 137 | ui->buttonLStickRight, | ||
| 138 | ui->buttonLStickMod, | ||
| 139 | }, | ||
| 140 | { | ||
| 141 | ui->buttonRStickUp, | ||
| 142 | ui->buttonRStickDown, | ||
| 143 | ui->buttonRStickLeft, | ||
| 144 | ui->buttonRStickRight, | ||
| 145 | ui->buttonRStickMod, | ||
| 146 | }, | ||
| 147 | }}; | ||
| 148 | |||
| 149 | debug_hidden = { | ||
| 150 | ui->buttonSL, ui->labelSL, | ||
| 151 | ui->buttonSR, ui->labelSR, | ||
| 152 | ui->buttonLStick, ui->labelLStickPressed, | ||
| 153 | ui->buttonRStick, ui->labelRStickPressed, | ||
| 154 | ui->buttonHome, ui->labelHome, | ||
| 155 | ui->buttonScreenshot, ui->labelScreenshot, | ||
| 156 | }; | ||
| 157 | |||
| 158 | auto layout = Settings::values.players[player_index].type; | ||
| 159 | if (debug) | ||
| 160 | layout = Settings::ControllerType::DualJoycon; | ||
| 161 | |||
| 162 | switch (layout) { | ||
| 163 | case Settings::ControllerType::ProController: | ||
| 164 | case Settings::ControllerType::DualJoycon: | ||
| 165 | layout_hidden = { | ||
| 166 | ui->buttonSL, | ||
| 167 | ui->labelSL, | ||
| 168 | ui->buttonSR, | ||
| 169 | ui->labelSR, | ||
| 170 | }; | ||
| 171 | break; | ||
| 172 | case Settings::ControllerType::LeftJoycon: | ||
| 173 | layout_hidden = { | ||
| 174 | ui->right_body_button, | ||
| 175 | ui->right_buttons_button, | ||
| 176 | ui->right_body_label, | ||
| 177 | ui->right_buttons_label, | ||
| 178 | ui->buttonR, | ||
| 179 | ui->labelR, | ||
| 180 | ui->buttonZR, | ||
| 181 | ui->labelZR, | ||
| 182 | ui->labelHome, | ||
| 183 | ui->buttonHome, | ||
| 184 | ui->buttonPlus, | ||
| 185 | ui->labelPlus, | ||
| 186 | ui->RStick, | ||
| 187 | ui->faceButtons, | ||
| 188 | }; | ||
| 189 | break; | ||
| 190 | case Settings::ControllerType::RightJoycon: | ||
| 191 | layout_hidden = { | ||
| 192 | ui->left_body_button, ui->left_buttons_button, | ||
| 193 | ui->left_body_label, ui->left_buttons_label, | ||
| 194 | ui->buttonL, ui->labelL, | ||
| 195 | ui->buttonZL, ui->labelZL, | ||
| 196 | ui->labelScreenshot, ui->buttonScreenshot, | ||
| 197 | ui->buttonMinus, ui->labelMinus, | ||
| 198 | ui->LStick, ui->Dpad, | ||
| 199 | }; | ||
| 200 | break; | ||
| 201 | } | ||
| 202 | |||
| 203 | if (debug || layout == Settings::ControllerType::ProController) { | ||
| 204 | ui->controller_color->hide(); | ||
| 205 | } else { | ||
| 206 | if (layout == Settings::ControllerType::LeftJoycon || | ||
| 207 | layout == Settings::ControllerType::RightJoycon) { | ||
| 208 | ui->horizontalSpacer_4->setGeometry({0, 0, 0, 0}); | ||
| 209 | |||
| 210 | LayerGridElements(ui->buttons, ui->shoulderButtons, ui->Dpad); | ||
| 211 | LayerGridElements(ui->buttons, ui->misc, ui->RStick); | ||
| 212 | LayerGridElements(ui->buttons, ui->Dpad, ui->faceButtons); | ||
| 213 | LayerGridElements(ui->buttons, ui->RStick, ui->LStick); | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | for (auto* widget : layout_hidden) | ||
| 218 | widget->setVisible(false); | ||
| 219 | |||
| 220 | analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; | ||
| 221 | |||
| 222 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { | ||
| 223 | if (!button_map[button_id]) | ||
| 224 | continue; | ||
| 225 | button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu); | ||
| 226 | connect(button_map[button_id], &QPushButton::released, [=]() { | ||
| 227 | handleClick( | ||
| 228 | button_map[button_id], | ||
| 229 | [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, | ||
| 230 | InputCommon::Polling::DeviceType::Button); | ||
| 231 | }); | ||
| 232 | connect(button_map[button_id], &QPushButton::customContextMenuRequested, | ||
| 233 | [=](const QPoint& menu_location) { | ||
| 234 | QMenu context_menu; | ||
| 235 | context_menu.addAction(tr("Clear"), [&] { | ||
| 236 | buttons_param[button_id].Clear(); | ||
| 237 | button_map[button_id]->setText(tr("[not set]")); | ||
| 238 | }); | ||
| 239 | context_menu.addAction(tr("Restore Default"), [&] { | ||
| 240 | buttons_param[button_id] = Common::ParamPackage{ | ||
| 241 | InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; | ||
| 242 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); | ||
| 243 | }); | ||
| 244 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); | ||
| 245 | }); | ||
| 246 | } | ||
| 247 | |||
| 248 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | ||
| 249 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | ||
| 250 | if (!analog_map_buttons[analog_id][sub_button_id]) | ||
| 251 | continue; | ||
| 252 | analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy( | ||
| 253 | Qt::CustomContextMenu); | ||
| 254 | connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() { | ||
| 255 | handleClick(analog_map_buttons[analog_id][sub_button_id], | ||
| 256 | [=](const Common::ParamPackage& params) { | ||
| 257 | SetAnalogButton(params, analogs_param[analog_id], | ||
| 258 | analog_sub_buttons[sub_button_id]); | ||
| 259 | }, | ||
| 260 | InputCommon::Polling::DeviceType::Button); | ||
| 261 | }); | ||
| 262 | connect(analog_map_buttons[analog_id][sub_button_id], | ||
| 263 | &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { | ||
| 264 | QMenu context_menu; | ||
| 265 | context_menu.addAction(tr("Clear"), [&] { | ||
| 266 | analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); | ||
| 267 | analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]")); | ||
| 268 | }); | ||
| 269 | context_menu.addAction(tr("Restore Default"), [&] { | ||
| 270 | Common::ParamPackage params{InputCommon::GenerateKeyboardParam( | ||
| 271 | Config::default_analogs[analog_id][sub_button_id])}; | ||
| 272 | SetAnalogButton(params, analogs_param[analog_id], | ||
| 273 | analog_sub_buttons[sub_button_id]); | ||
| 274 | analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText( | ||
| 275 | analogs_param[analog_id], analog_sub_buttons[sub_button_id])); | ||
| 276 | }); | ||
| 277 | context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal( | ||
| 278 | menu_location)); | ||
| 279 | }); | ||
| 280 | } | ||
| 281 | connect(analog_map_stick[analog_id], &QPushButton::released, [=]() { | ||
| 282 | QMessageBox::information(this, tr("Information"), | ||
| 283 | tr("After pressing OK, first move your joystick horizontally, " | ||
| 284 | "and then vertically.")); | ||
| 285 | handleClick( | ||
| 286 | analog_map_stick[analog_id], | ||
| 287 | [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; }, | ||
| 288 | InputCommon::Polling::DeviceType::Analog); | ||
| 289 | }); | ||
| 290 | } | ||
| 291 | |||
| 292 | connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); }); | ||
| 293 | connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); | ||
| 294 | |||
| 295 | timeout_timer->setSingleShot(true); | ||
| 296 | connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); }); | ||
| 297 | |||
| 298 | connect(poll_timer.get(), &QTimer::timeout, [this]() { | ||
| 299 | Common::ParamPackage params; | ||
| 300 | for (auto& poller : device_pollers) { | ||
| 301 | params = poller->GetNextInput(); | ||
| 302 | if (params.Has("engine")) { | ||
| 303 | setPollingResult(params, false); | ||
| 304 | return; | ||
| 305 | } | ||
| 306 | } | ||
| 307 | }); | ||
| 308 | |||
| 309 | controller_color_buttons = { | ||
| 310 | ui->left_body_button, | ||
| 311 | ui->left_buttons_button, | ||
| 312 | ui->right_body_button, | ||
| 313 | ui->right_buttons_button, | ||
| 314 | }; | ||
| 315 | |||
| 316 | for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) { | ||
| 317 | connect(controller_color_buttons[i], &QPushButton::clicked, this, | ||
| 318 | std::bind(&ConfigureInputPlayer::OnControllerButtonClick, this, i)); | ||
| 319 | } | ||
| 320 | |||
| 321 | this->loadConfiguration(); | ||
| 322 | this->resize(0, 0); | ||
| 323 | |||
| 324 | // TODO(wwylele): enable this when we actually emulate it | ||
| 325 | ui->buttonHome->setEnabled(false); | ||
| 326 | } | ||
| 327 | |||
| 328 | ConfigureInputPlayer::~ConfigureInputPlayer() = default; | ||
| 329 | |||
| 330 | void ConfigureInputPlayer::applyConfiguration() { | ||
| 331 | auto& buttons = | ||
| 332 | debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons; | ||
| 333 | auto& analogs = | ||
| 334 | debug ? Settings::values.debug_pad_analogs : Settings::values.players[player_index].analogs; | ||
| 335 | |||
| 336 | std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(), | ||
| 337 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | ||
| 338 | std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(), | ||
| 339 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | ||
| 340 | |||
| 341 | if (debug) | ||
| 342 | return; | ||
| 343 | |||
| 344 | std::array<u32, 4> colors{}; | ||
| 345 | std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(), | ||
| 346 | [](QColor color) { return color.rgb(); }); | ||
| 347 | |||
| 348 | Settings::values.players[player_index].body_color_left = colors[0]; | ||
| 349 | Settings::values.players[player_index].button_color_left = colors[1]; | ||
| 350 | Settings::values.players[player_index].body_color_right = colors[2]; | ||
| 351 | Settings::values.players[player_index].button_color_right = colors[3]; | ||
| 352 | } | ||
| 353 | |||
| 354 | void ConfigureInputPlayer::OnControllerButtonClick(int i) { | ||
| 355 | const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]); | ||
| 356 | if (!new_bg_color.isValid()) | ||
| 357 | return; | ||
| 358 | controller_colors[i] = new_bg_color; | ||
| 359 | controller_color_buttons[i]->setStyleSheet( | ||
| 360 | QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name())); | ||
| 361 | } | ||
| 362 | |||
| 363 | void ConfigureInputPlayer::loadConfiguration() { | ||
| 364 | if (debug) { | ||
| 365 | std::transform(Settings::values.debug_pad_buttons.begin(), | ||
| 366 | Settings::values.debug_pad_buttons.end(), buttons_param.begin(), | ||
| 367 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 368 | std::transform(Settings::values.debug_pad_analogs.begin(), | ||
| 369 | Settings::values.debug_pad_analogs.end(), analogs_param.begin(), | ||
| 370 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 371 | } else { | ||
| 372 | std::transform(Settings::values.players[player_index].buttons.begin(), | ||
| 373 | Settings::values.players[player_index].buttons.end(), buttons_param.begin(), | ||
| 374 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 375 | std::transform(Settings::values.players[player_index].analogs.begin(), | ||
| 376 | Settings::values.players[player_index].analogs.end(), analogs_param.begin(), | ||
| 377 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 378 | } | ||
| 379 | |||
| 380 | updateButtonLabels(); | ||
| 381 | |||
| 382 | if (debug) | ||
| 383 | return; | ||
| 384 | |||
| 385 | std::array<u32, 4> colors = { | ||
| 386 | Settings::values.players[player_index].body_color_left, | ||
| 387 | Settings::values.players[player_index].button_color_left, | ||
| 388 | Settings::values.players[player_index].body_color_right, | ||
| 389 | Settings::values.players[player_index].button_color_right, | ||
| 390 | }; | ||
| 391 | |||
| 392 | std::transform(colors.begin(), colors.end(), controller_colors.begin(), | ||
| 393 | [](u32 rgb) { return QColor::fromRgb(rgb); }); | ||
| 394 | |||
| 395 | for (std::size_t i = 0; i < colors.size(); ++i) { | ||
| 396 | controller_color_buttons[i]->setStyleSheet( | ||
| 397 | QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name())); | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | void ConfigureInputPlayer::restoreDefaults() { | ||
| 402 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { | ||
| 403 | buttons_param[button_id] = Common::ParamPackage{ | ||
| 404 | InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; | ||
| 405 | } | ||
| 406 | |||
| 407 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | ||
| 408 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | ||
| 409 | Common::ParamPackage params{InputCommon::GenerateKeyboardParam( | ||
| 410 | Config::default_analogs[analog_id][sub_button_id])}; | ||
| 411 | SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); | ||
| 412 | } | ||
| 413 | } | ||
| 414 | updateButtonLabels(); | ||
| 415 | } | ||
| 416 | |||
| 417 | void ConfigureInputPlayer::ClearAll() { | ||
| 418 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { | ||
| 419 | if (button_map[button_id] && button_map[button_id]->isEnabled()) | ||
| 420 | buttons_param[button_id].Clear(); | ||
| 421 | } | ||
| 422 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | ||
| 423 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | ||
| 424 | if (analog_map_buttons[analog_id][sub_button_id] && | ||
| 425 | analog_map_buttons[analog_id][sub_button_id]->isEnabled()) | ||
| 426 | analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | updateButtonLabels(); | ||
| 431 | } | ||
| 432 | |||
| 433 | void ConfigureInputPlayer::updateButtonLabels() { | ||
| 434 | for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { | ||
| 435 | button_map[button]->setText(ButtonToText(buttons_param[button])); | ||
| 436 | } | ||
| 437 | |||
| 438 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | ||
| 439 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | ||
| 440 | if (analog_map_buttons[analog_id][sub_button_id]) { | ||
| 441 | analog_map_buttons[analog_id][sub_button_id]->setText( | ||
| 442 | AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); | ||
| 443 | } | ||
| 444 | } | ||
| 445 | analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); | ||
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 449 | void ConfigureInputPlayer::handleClick( | ||
| 450 | QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, | ||
| 451 | InputCommon::Polling::DeviceType type) { | ||
| 452 | button->setText(tr("[press key]")); | ||
| 453 | button->setFocus(); | ||
| 454 | |||
| 455 | const auto iter = std::find(button_map.begin(), button_map.end(), button); | ||
| 456 | ASSERT(iter != button_map.end()); | ||
| 457 | const auto index = std::distance(button_map.begin(), iter); | ||
| 458 | ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); | ||
| 459 | |||
| 460 | input_setter = new_input_setter; | ||
| 461 | |||
| 462 | device_pollers = InputCommon::Polling::GetPollers(type); | ||
| 463 | |||
| 464 | // Keyboard keys can only be used as button devices | ||
| 465 | want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; | ||
| 466 | |||
| 467 | for (auto& poller : device_pollers) { | ||
| 468 | poller->Start(); | ||
| 469 | } | ||
| 470 | |||
| 471 | grabKeyboard(); | ||
| 472 | grabMouse(); | ||
| 473 | timeout_timer->start(5000); // Cancel after 5 seconds | ||
| 474 | poll_timer->start(200); // Check for new inputs every 200ms | ||
| 475 | } | ||
| 476 | |||
| 477 | void ConfigureInputPlayer::setPollingResult(const Common::ParamPackage& params, bool abort) { | ||
| 478 | releaseKeyboard(); | ||
| 479 | releaseMouse(); | ||
| 480 | timeout_timer->stop(); | ||
| 481 | poll_timer->stop(); | ||
| 482 | for (auto& poller : device_pollers) { | ||
| 483 | poller->Stop(); | ||
| 484 | } | ||
| 485 | |||
| 486 | if (!abort) { | ||
| 487 | (*input_setter)(params); | ||
| 488 | } | ||
| 489 | |||
| 490 | updateButtonLabels(); | ||
| 491 | input_setter = std::nullopt; | ||
| 492 | } | ||
| 493 | |||
| 494 | void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { | ||
| 495 | if (!input_setter || !event) | ||
| 496 | return; | ||
| 497 | |||
| 498 | if (event->key() != Qt::Key_Escape) { | ||
| 499 | if (want_keyboard_keys) { | ||
| 500 | setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, | ||
| 501 | false); | ||
| 502 | } else { | ||
| 503 | // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling | ||
| 504 | return; | ||
| 505 | } | ||
| 506 | } | ||
| 507 | setPollingResult({}, true); | ||
| 508 | } | ||
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h new file mode 100644 index 000000000..b0e5550c5 --- /dev/null +++ b/src/yuzu/configuration/configure_input_player.h | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <functional> | ||
| 9 | #include <memory> | ||
| 10 | #include <optional> | ||
| 11 | #include <string> | ||
| 12 | #include <unordered_map> | ||
| 13 | #include <QDialog> | ||
| 14 | #include <QKeyEvent> | ||
| 15 | #include "common/param_package.h" | ||
| 16 | #include "core/settings.h" | ||
| 17 | #include "input_common/main.h" | ||
| 18 | #include "ui_configure_input.h" | ||
| 19 | |||
| 20 | class QPushButton; | ||
| 21 | class QString; | ||
| 22 | class QTimer; | ||
| 23 | |||
| 24 | namespace Ui { | ||
| 25 | class ConfigureInputPlayer; | ||
| 26 | } | ||
| 27 | |||
| 28 | class ConfigureInputPlayer : public QDialog { | ||
| 29 | Q_OBJECT | ||
| 30 | |||
| 31 | public: | ||
| 32 | explicit ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug = false); | ||
| 33 | ~ConfigureInputPlayer() override; | ||
| 34 | |||
| 35 | /// Save all button configurations to settings file | ||
| 36 | void applyConfiguration(); | ||
| 37 | |||
| 38 | private: | ||
| 39 | std::unique_ptr<Ui::ConfigureInputPlayer> ui; | ||
| 40 | |||
| 41 | u8 player_index; | ||
| 42 | bool debug; | ||
| 43 | |||
| 44 | std::unique_ptr<QTimer> timeout_timer; | ||
| 45 | std::unique_ptr<QTimer> poll_timer; | ||
| 46 | |||
| 47 | /// This will be the the setting function when an input is awaiting configuration. | ||
| 48 | std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; | ||
| 49 | |||
| 50 | std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; | ||
| 51 | std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; | ||
| 52 | |||
| 53 | static constexpr int ANALOG_SUB_BUTTONS_NUM = 5; | ||
| 54 | |||
| 55 | /// Each button input is represented by a QPushButton. | ||
| 56 | std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map; | ||
| 57 | |||
| 58 | std::vector<QWidget*> debug_hidden; | ||
| 59 | std::vector<QWidget*> layout_hidden; | ||
| 60 | |||
| 61 | /// A group of five QPushButtons represent one analog input. The buttons each represent up, | ||
| 62 | /// down, left, right, and modifier, respectively. | ||
| 63 | std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs> | ||
| 64 | analog_map_buttons; | ||
| 65 | |||
| 66 | /// Analog inputs are also represented each with a single button, used to configure with an | ||
| 67 | /// actual analog stick | ||
| 68 | std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; | ||
| 69 | |||
| 70 | static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; | ||
| 71 | |||
| 72 | std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; | ||
| 73 | |||
| 74 | /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, | ||
| 75 | /// keyboard events are ignored. | ||
| 76 | bool want_keyboard_keys = false; | ||
| 77 | |||
| 78 | std::array<QPushButton*, 4> controller_color_buttons; | ||
| 79 | std::array<QColor, 4> controller_colors; | ||
| 80 | |||
| 81 | void OnControllerButtonClick(int i); | ||
| 82 | |||
| 83 | /// Load configuration settings. | ||
| 84 | void loadConfiguration(); | ||
| 85 | /// Restore all buttons to their default values. | ||
| 86 | void restoreDefaults(); | ||
| 87 | /// Clear all input configuration | ||
| 88 | void ClearAll(); | ||
| 89 | |||
| 90 | /// Update UI to reflect current configuration. | ||
| 91 | void updateButtonLabels(); | ||
| 92 | |||
| 93 | /// Called when the button was pressed. | ||
| 94 | void handleClick(QPushButton* button, | ||
| 95 | std::function<void(const Common::ParamPackage&)> new_input_setter, | ||
| 96 | InputCommon::Polling::DeviceType type); | ||
| 97 | |||
| 98 | /// Finish polling and configure input using the input_setter | ||
| 99 | void setPollingResult(const Common::ParamPackage& params, bool abort); | ||
| 100 | |||
| 101 | /// Handle key press events. | ||
| 102 | void keyPressEvent(QKeyEvent* event) override; | ||
| 103 | }; | ||
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui new file mode 100644 index 000000000..42db020be --- /dev/null +++ b/src/yuzu/configuration/configure_input_player.ui | |||
| @@ -0,0 +1,1164 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigureInputPlayer</class> | ||
| 4 | <widget class="QDialog" name="ConfigureInputPlayer"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>408</width> | ||
| 10 | <height>731</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>Configure Input</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QVBoxLayout" name="verticalLayout_5"> | ||
| 17 | <item> | ||
| 18 | <layout class="QGridLayout" name="buttons"> | ||
| 19 | <item row="1" column="1"> | ||
| 20 | <widget class="QGroupBox" name="RStick"> | ||
| 21 | <property name="title"> | ||
| 22 | <string>Right Stick</string> | ||
| 23 | </property> | ||
| 24 | <property name="alignment"> | ||
| 25 | <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> | ||
| 26 | </property> | ||
| 27 | <property name="flat"> | ||
| 28 | <bool>false</bool> | ||
| 29 | </property> | ||
| 30 | <property name="checkable"> | ||
| 31 | <bool>false</bool> | ||
| 32 | </property> | ||
| 33 | <layout class="QGridLayout" name="gridLayout_5"> | ||
| 34 | <item row="1" column="1"> | ||
| 35 | <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout"> | ||
| 36 | <item> | ||
| 37 | <layout class="QHBoxLayout" name="buttonRStickDownHorizontalLayout"> | ||
| 38 | <item> | ||
| 39 | <widget class="QLabel" name="labelRStickDown"> | ||
| 40 | <property name="text"> | ||
| 41 | <string>Down:</string> | ||
| 42 | </property> | ||
| 43 | </widget> | ||
| 44 | </item> | ||
| 45 | </layout> | ||
| 46 | </item> | ||
| 47 | <item> | ||
| 48 | <widget class="QPushButton" name="buttonRStickDown"> | ||
| 49 | <property name="text"> | ||
| 50 | <string/> | ||
| 51 | </property> | ||
| 52 | </widget> | ||
| 53 | </item> | ||
| 54 | </layout> | ||
| 55 | </item> | ||
| 56 | <item row="0" column="1"> | ||
| 57 | <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout"> | ||
| 58 | <item> | ||
| 59 | <layout class="QHBoxLayout" name="buttonRStickRightHorizontalLayout"> | ||
| 60 | <item> | ||
| 61 | <widget class="QLabel" name="labelRStickRight"> | ||
| 62 | <property name="text"> | ||
| 63 | <string>Right:</string> | ||
| 64 | </property> | ||
| 65 | </widget> | ||
| 66 | </item> | ||
| 67 | </layout> | ||
| 68 | </item> | ||
| 69 | <item> | ||
| 70 | <widget class="QPushButton" name="buttonRStickRight"> | ||
| 71 | <property name="text"> | ||
| 72 | <string/> | ||
| 73 | </property> | ||
| 74 | </widget> | ||
| 75 | </item> | ||
| 76 | </layout> | ||
| 77 | </item> | ||
| 78 | <item row="3" column="0" colspan="2"> | ||
| 79 | <widget class="QPushButton" name="buttonRStickAnalog"> | ||
| 80 | <property name="text"> | ||
| 81 | <string>Set Analog Stick</string> | ||
| 82 | </property> | ||
| 83 | </widget> | ||
| 84 | </item> | ||
| 85 | <item row="0" column="0"> | ||
| 86 | <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout"> | ||
| 87 | <item> | ||
| 88 | <layout class="QHBoxLayout" name="buttonRStickLeftHorizontalLayout"> | ||
| 89 | <item> | ||
| 90 | <widget class="QLabel" name="labelRStickLeft"> | ||
| 91 | <property name="text"> | ||
| 92 | <string>Left:</string> | ||
| 93 | </property> | ||
| 94 | </widget> | ||
| 95 | </item> | ||
| 96 | </layout> | ||
| 97 | </item> | ||
| 98 | <item> | ||
| 99 | <widget class="QPushButton" name="buttonRStickLeft"> | ||
| 100 | <property name="text"> | ||
| 101 | <string/> | ||
| 102 | </property> | ||
| 103 | </widget> | ||
| 104 | </item> | ||
| 105 | </layout> | ||
| 106 | </item> | ||
| 107 | <item row="1" column="0"> | ||
| 108 | <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout"> | ||
| 109 | <item> | ||
| 110 | <layout class="QHBoxLayout" name="buttonRStickUpHorizontalLayout"> | ||
| 111 | <item> | ||
| 112 | <widget class="QLabel" name="labelRStickUp"> | ||
| 113 | <property name="text"> | ||
| 114 | <string>Up:</string> | ||
| 115 | </property> | ||
| 116 | </widget> | ||
| 117 | </item> | ||
| 118 | </layout> | ||
| 119 | </item> | ||
| 120 | <item> | ||
| 121 | <widget class="QPushButton" name="buttonRStickUp"> | ||
| 122 | <property name="text"> | ||
| 123 | <string/> | ||
| 124 | </property> | ||
| 125 | </widget> | ||
| 126 | </item> | ||
| 127 | </layout> | ||
| 128 | </item> | ||
| 129 | <item row="2" column="0"> | ||
| 130 | <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout"> | ||
| 131 | <item> | ||
| 132 | <layout class="QHBoxLayout" name="buttonRStickPressedHorizontalLayout"> | ||
| 133 | <item> | ||
| 134 | <widget class="QLabel" name="labelRStickPressed"> | ||
| 135 | <property name="text"> | ||
| 136 | <string>Pressed:</string> | ||
| 137 | </property> | ||
| 138 | </widget> | ||
| 139 | </item> | ||
| 140 | </layout> | ||
| 141 | </item> | ||
| 142 | <item> | ||
| 143 | <widget class="QPushButton" name="buttonRStick"> | ||
| 144 | <property name="text"> | ||
| 145 | <string/> | ||
| 146 | </property> | ||
| 147 | </widget> | ||
| 148 | </item> | ||
| 149 | </layout> | ||
| 150 | </item> | ||
| 151 | <item row="2" column="1"> | ||
| 152 | <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout"> | ||
| 153 | <item> | ||
| 154 | <layout class="QHBoxLayout" name="buttonRStickModHorizontalLayout"> | ||
| 155 | <item> | ||
| 156 | <widget class="QLabel" name="labelRStickMod"> | ||
| 157 | <property name="text"> | ||
| 158 | <string>Modifier:</string> | ||
| 159 | </property> | ||
| 160 | </widget> | ||
| 161 | </item> | ||
| 162 | </layout> | ||
| 163 | </item> | ||
| 164 | <item> | ||
| 165 | <widget class="QPushButton" name="buttonRStickMod"> | ||
| 166 | <property name="text"> | ||
| 167 | <string/> | ||
| 168 | </property> | ||
| 169 | </widget> | ||
| 170 | </item> | ||
| 171 | </layout> | ||
| 172 | </item> | ||
| 173 | </layout> | ||
| 174 | </widget> | ||
| 175 | </item> | ||
| 176 | <item row="0" column="1"> | ||
| 177 | <widget class="QGroupBox" name="Dpad"> | ||
| 178 | <property name="title"> | ||
| 179 | <string>Directional Pad</string> | ||
| 180 | </property> | ||
| 181 | <property name="flat"> | ||
| 182 | <bool>false</bool> | ||
| 183 | </property> | ||
| 184 | <property name="checkable"> | ||
| 185 | <bool>false</bool> | ||
| 186 | </property> | ||
| 187 | <layout class="QGridLayout" name="gridLayout_2"> | ||
| 188 | <item row="1" column="0"> | ||
| 189 | <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout"> | ||
| 190 | <item> | ||
| 191 | <layout class="QHBoxLayout" name="buttonDpadUpHorizontalLayout"> | ||
| 192 | <item> | ||
| 193 | <widget class="QLabel" name="labelDpadUp"> | ||
| 194 | <property name="text"> | ||
| 195 | <string>Up:</string> | ||
| 196 | </property> | ||
| 197 | </widget> | ||
| 198 | </item> | ||
| 199 | </layout> | ||
| 200 | </item> | ||
| 201 | <item> | ||
| 202 | <widget class="QPushButton" name="buttonDpadUp"> | ||
| 203 | <property name="text"> | ||
| 204 | <string/> | ||
| 205 | </property> | ||
| 206 | </widget> | ||
| 207 | </item> | ||
| 208 | </layout> | ||
| 209 | </item> | ||
| 210 | <item row="1" column="1"> | ||
| 211 | <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout"> | ||
| 212 | <item> | ||
| 213 | <layout class="QHBoxLayout" name="buttonDpadDownHorizontalLayout"> | ||
| 214 | <item> | ||
| 215 | <widget class="QLabel" name="labelDpadDown"> | ||
| 216 | <property name="text"> | ||
| 217 | <string>Down:</string> | ||
| 218 | </property> | ||
| 219 | </widget> | ||
| 220 | </item> | ||
| 221 | </layout> | ||
| 222 | </item> | ||
| 223 | <item> | ||
| 224 | <widget class="QPushButton" name="buttonDpadDown"> | ||
| 225 | <property name="text"> | ||
| 226 | <string/> | ||
| 227 | </property> | ||
| 228 | </widget> | ||
| 229 | </item> | ||
| 230 | </layout> | ||
| 231 | </item> | ||
| 232 | <item row="0" column="0"> | ||
| 233 | <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout"> | ||
| 234 | <item> | ||
| 235 | <layout class="QHBoxLayout" name="buttonDpadLeftHorizontalLayout"> | ||
| 236 | <item> | ||
| 237 | <widget class="QLabel" name="labelDpadLeft"> | ||
| 238 | <property name="minimumSize"> | ||
| 239 | <size> | ||
| 240 | <width>80</width> | ||
| 241 | <height>0</height> | ||
| 242 | </size> | ||
| 243 | </property> | ||
| 244 | <property name="text"> | ||
| 245 | <string>Left:</string> | ||
| 246 | </property> | ||
| 247 | </widget> | ||
| 248 | </item> | ||
| 249 | </layout> | ||
| 250 | </item> | ||
| 251 | <item> | ||
| 252 | <widget class="QPushButton" name="buttonDpadLeft"> | ||
| 253 | <property name="text"> | ||
| 254 | <string/> | ||
| 255 | </property> | ||
| 256 | </widget> | ||
| 257 | </item> | ||
| 258 | </layout> | ||
| 259 | </item> | ||
| 260 | <item row="0" column="1"> | ||
| 261 | <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout"> | ||
| 262 | <item> | ||
| 263 | <layout class="QHBoxLayout" name="buttonDpadRightHorizontalLayout"> | ||
| 264 | <item> | ||
| 265 | <widget class="QLabel" name="labelDpadRight"> | ||
| 266 | <property name="minimumSize"> | ||
| 267 | <size> | ||
| 268 | <width>80</width> | ||
| 269 | <height>0</height> | ||
| 270 | </size> | ||
| 271 | </property> | ||
| 272 | <property name="text"> | ||
| 273 | <string>Right:</string> | ||
| 274 | </property> | ||
| 275 | </widget> | ||
| 276 | </item> | ||
| 277 | </layout> | ||
| 278 | </item> | ||
| 279 | <item> | ||
| 280 | <widget class="QPushButton" name="buttonDpadRight"> | ||
| 281 | <property name="text"> | ||
| 282 | <string/> | ||
| 283 | </property> | ||
| 284 | </widget> | ||
| 285 | </item> | ||
| 286 | </layout> | ||
| 287 | </item> | ||
| 288 | </layout> | ||
| 289 | </widget> | ||
| 290 | </item> | ||
| 291 | <item row="0" column="0"> | ||
| 292 | <widget class="QGroupBox" name="faceButtons"> | ||
| 293 | <property name="title"> | ||
| 294 | <string>Face Buttons</string> | ||
| 295 | </property> | ||
| 296 | <property name="flat"> | ||
| 297 | <bool>false</bool> | ||
| 298 | </property> | ||
| 299 | <property name="checkable"> | ||
| 300 | <bool>false</bool> | ||
| 301 | </property> | ||
| 302 | <layout class="QGridLayout" name="gridLayout"> | ||
| 303 | <item row="0" column="0"> | ||
| 304 | <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout"> | ||
| 305 | <item> | ||
| 306 | <layout class="QHBoxLayout" name="buttonFaceButtonsAHorizontalLayout"> | ||
| 307 | <item> | ||
| 308 | <widget class="QLabel" name="labelA"> | ||
| 309 | <property name="minimumSize"> | ||
| 310 | <size> | ||
| 311 | <width>80</width> | ||
| 312 | <height>0</height> | ||
| 313 | </size> | ||
| 314 | </property> | ||
| 315 | <property name="text"> | ||
| 316 | <string>A:</string> | ||
| 317 | </property> | ||
| 318 | </widget> | ||
| 319 | </item> | ||
| 320 | </layout> | ||
| 321 | </item> | ||
| 322 | <item> | ||
| 323 | <widget class="QPushButton" name="buttonA"> | ||
| 324 | <property name="text"> | ||
| 325 | <string/> | ||
| 326 | </property> | ||
| 327 | </widget> | ||
| 328 | </item> | ||
| 329 | </layout> | ||
| 330 | </item> | ||
| 331 | <item row="0" column="1"> | ||
| 332 | <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout"> | ||
| 333 | <item> | ||
| 334 | <layout class="QHBoxLayout" name="buttonFaceButtonsBHorizontalLayout"> | ||
| 335 | <item> | ||
| 336 | <widget class="QLabel" name="labelB"> | ||
| 337 | <property name="minimumSize"> | ||
| 338 | <size> | ||
| 339 | <width>80</width> | ||
| 340 | <height>0</height> | ||
| 341 | </size> | ||
| 342 | </property> | ||
| 343 | <property name="text"> | ||
| 344 | <string>B:</string> | ||
| 345 | </property> | ||
| 346 | </widget> | ||
| 347 | </item> | ||
| 348 | </layout> | ||
| 349 | </item> | ||
| 350 | <item> | ||
| 351 | <widget class="QPushButton" name="buttonB"> | ||
| 352 | <property name="text"> | ||
| 353 | <string/> | ||
| 354 | </property> | ||
| 355 | </widget> | ||
| 356 | </item> | ||
| 357 | </layout> | ||
| 358 | </item> | ||
| 359 | <item row="1" column="0"> | ||
| 360 | <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout"> | ||
| 361 | <item> | ||
| 362 | <layout class="QHBoxLayout" name="buttonFaceButtonsXHorizontalLayout"> | ||
| 363 | <item> | ||
| 364 | <widget class="QLabel" name="labelX"> | ||
| 365 | <property name="text"> | ||
| 366 | <string>X:</string> | ||
| 367 | </property> | ||
| 368 | </widget> | ||
| 369 | </item> | ||
| 370 | </layout> | ||
| 371 | </item> | ||
| 372 | <item> | ||
| 373 | <widget class="QPushButton" name="buttonX"> | ||
| 374 | <property name="text"> | ||
| 375 | <string/> | ||
| 376 | </property> | ||
| 377 | </widget> | ||
| 378 | </item> | ||
| 379 | </layout> | ||
| 380 | </item> | ||
| 381 | <item row="1" column="1"> | ||
| 382 | <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout"> | ||
| 383 | <item> | ||
| 384 | <layout class="QHBoxLayout" name="buttonFaceButtonsYHorizontalLayout"> | ||
| 385 | <item> | ||
| 386 | <widget class="QLabel" name="labelY"> | ||
| 387 | <property name="text"> | ||
| 388 | <string>Y:</string> | ||
| 389 | </property> | ||
| 390 | </widget> | ||
| 391 | </item> | ||
| 392 | </layout> | ||
| 393 | </item> | ||
| 394 | <item> | ||
| 395 | <widget class="QPushButton" name="buttonY"> | ||
| 396 | <property name="text"> | ||
| 397 | <string/> | ||
| 398 | </property> | ||
| 399 | </widget> | ||
| 400 | </item> | ||
| 401 | </layout> | ||
| 402 | </item> | ||
| 403 | </layout> | ||
| 404 | </widget> | ||
| 405 | </item> | ||
| 406 | <item row="5" column="0" colspan="2"> | ||
| 407 | <widget class="QGroupBox" name="controller_color"> | ||
| 408 | <property name="title"> | ||
| 409 | <string>Controller Color</string> | ||
| 410 | </property> | ||
| 411 | <layout class="QGridLayout" name="gridLayout_10" columnstretch="0,0,0,0,0,0,0"> | ||
| 412 | <item row="0" column="0"> | ||
| 413 | <spacer name="horizontalSpacer_2"> | ||
| 414 | <property name="orientation"> | ||
| 415 | <enum>Qt::Horizontal</enum> | ||
| 416 | </property> | ||
| 417 | <property name="sizeHint" stdset="0"> | ||
| 418 | <size> | ||
| 419 | <width>40</width> | ||
| 420 | <height>20</height> | ||
| 421 | </size> | ||
| 422 | </property> | ||
| 423 | </spacer> | ||
| 424 | </item> | ||
| 425 | <item row="0" column="1"> | ||
| 426 | <widget class="QLabel" name="left_body_label"> | ||
| 427 | <property name="text"> | ||
| 428 | <string>Left Body</string> | ||
| 429 | </property> | ||
| 430 | </widget> | ||
| 431 | </item> | ||
| 432 | <item row="0" column="6"> | ||
| 433 | <spacer name="horizontalSpacer_3"> | ||
| 434 | <property name="orientation"> | ||
| 435 | <enum>Qt::Horizontal</enum> | ||
| 436 | </property> | ||
| 437 | <property name="sizeHint" stdset="0"> | ||
| 438 | <size> | ||
| 439 | <width>40</width> | ||
| 440 | <height>20</height> | ||
| 441 | </size> | ||
| 442 | </property> | ||
| 443 | </spacer> | ||
| 444 | </item> | ||
| 445 | <item row="1" column="1"> | ||
| 446 | <widget class="QLabel" name="left_buttons_label"> | ||
| 447 | <property name="minimumSize"> | ||
| 448 | <size> | ||
| 449 | <width>90</width> | ||
| 450 | <height>0</height> | ||
| 451 | </size> | ||
| 452 | </property> | ||
| 453 | <property name="text"> | ||
| 454 | <string>Left Buttons</string> | ||
| 455 | </property> | ||
| 456 | </widget> | ||
| 457 | </item> | ||
| 458 | <item row="1" column="5"> | ||
| 459 | <widget class="QPushButton" name="right_buttons_button"> | ||
| 460 | <property name="sizePolicy"> | ||
| 461 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||
| 462 | <horstretch>0</horstretch> | ||
| 463 | <verstretch>0</verstretch> | ||
| 464 | </sizepolicy> | ||
| 465 | </property> | ||
| 466 | <property name="minimumSize"> | ||
| 467 | <size> | ||
| 468 | <width>32</width> | ||
| 469 | <height>0</height> | ||
| 470 | </size> | ||
| 471 | </property> | ||
| 472 | <property name="maximumSize"> | ||
| 473 | <size> | ||
| 474 | <width>40</width> | ||
| 475 | <height>16777215</height> | ||
| 476 | </size> | ||
| 477 | </property> | ||
| 478 | <property name="text"> | ||
| 479 | <string/> | ||
| 480 | </property> | ||
| 481 | </widget> | ||
| 482 | </item> | ||
| 483 | <item row="0" column="4"> | ||
| 484 | <widget class="QLabel" name="right_body_label"> | ||
| 485 | <property name="text"> | ||
| 486 | <string>Right Body</string> | ||
| 487 | </property> | ||
| 488 | </widget> | ||
| 489 | </item> | ||
| 490 | <item row="1" column="4"> | ||
| 491 | <widget class="QLabel" name="right_buttons_label"> | ||
| 492 | <property name="minimumSize"> | ||
| 493 | <size> | ||
| 494 | <width>90</width> | ||
| 495 | <height>0</height> | ||
| 496 | </size> | ||
| 497 | </property> | ||
| 498 | <property name="text"> | ||
| 499 | <string>Right Buttons</string> | ||
| 500 | </property> | ||
| 501 | </widget> | ||
| 502 | </item> | ||
| 503 | <item row="1" column="2"> | ||
| 504 | <widget class="QPushButton" name="left_buttons_button"> | ||
| 505 | <property name="sizePolicy"> | ||
| 506 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||
| 507 | <horstretch>0</horstretch> | ||
| 508 | <verstretch>0</verstretch> | ||
| 509 | </sizepolicy> | ||
| 510 | </property> | ||
| 511 | <property name="minimumSize"> | ||
| 512 | <size> | ||
| 513 | <width>32</width> | ||
| 514 | <height>0</height> | ||
| 515 | </size> | ||
| 516 | </property> | ||
| 517 | <property name="maximumSize"> | ||
| 518 | <size> | ||
| 519 | <width>40</width> | ||
| 520 | <height>16777215</height> | ||
| 521 | </size> | ||
| 522 | </property> | ||
| 523 | <property name="text"> | ||
| 524 | <string/> | ||
| 525 | </property> | ||
| 526 | </widget> | ||
| 527 | </item> | ||
| 528 | <item row="0" column="2"> | ||
| 529 | <widget class="QPushButton" name="left_body_button"> | ||
| 530 | <property name="sizePolicy"> | ||
| 531 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||
| 532 | <horstretch>0</horstretch> | ||
| 533 | <verstretch>0</verstretch> | ||
| 534 | </sizepolicy> | ||
| 535 | </property> | ||
| 536 | <property name="minimumSize"> | ||
| 537 | <size> | ||
| 538 | <width>32</width> | ||
| 539 | <height>0</height> | ||
| 540 | </size> | ||
| 541 | </property> | ||
| 542 | <property name="maximumSize"> | ||
| 543 | <size> | ||
| 544 | <width>40</width> | ||
| 545 | <height>16777215</height> | ||
| 546 | </size> | ||
| 547 | </property> | ||
| 548 | <property name="text"> | ||
| 549 | <string/> | ||
| 550 | </property> | ||
| 551 | </widget> | ||
| 552 | </item> | ||
| 553 | <item row="0" column="5"> | ||
| 554 | <widget class="QPushButton" name="right_body_button"> | ||
| 555 | <property name="sizePolicy"> | ||
| 556 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||
| 557 | <horstretch>0</horstretch> | ||
| 558 | <verstretch>0</verstretch> | ||
| 559 | </sizepolicy> | ||
| 560 | </property> | ||
| 561 | <property name="minimumSize"> | ||
| 562 | <size> | ||
| 563 | <width>32</width> | ||
| 564 | <height>0</height> | ||
| 565 | </size> | ||
| 566 | </property> | ||
| 567 | <property name="maximumSize"> | ||
| 568 | <size> | ||
| 569 | <width>40</width> | ||
| 570 | <height>16777215</height> | ||
| 571 | </size> | ||
| 572 | </property> | ||
| 573 | <property name="text"> | ||
| 574 | <string/> | ||
| 575 | </property> | ||
| 576 | </widget> | ||
| 577 | </item> | ||
| 578 | <item row="0" column="3"> | ||
| 579 | <spacer name="horizontalSpacer_4"> | ||
| 580 | <property name="orientation"> | ||
| 581 | <enum>Qt::Horizontal</enum> | ||
| 582 | </property> | ||
| 583 | <property name="sizeType"> | ||
| 584 | <enum>QSizePolicy::Fixed</enum> | ||
| 585 | </property> | ||
| 586 | <property name="sizeHint" stdset="0"> | ||
| 587 | <size> | ||
| 588 | <width>20</width> | ||
| 589 | <height>20</height> | ||
| 590 | </size> | ||
| 591 | </property> | ||
| 592 | </spacer> | ||
| 593 | </item> | ||
| 594 | </layout> | ||
| 595 | </widget> | ||
| 596 | </item> | ||
| 597 | <item row="1" column="0"> | ||
| 598 | <widget class="QGroupBox" name="LStick"> | ||
| 599 | <property name="title"> | ||
| 600 | <string>Left Stick</string> | ||
| 601 | </property> | ||
| 602 | <property name="flat"> | ||
| 603 | <bool>false</bool> | ||
| 604 | </property> | ||
| 605 | <property name="checkable"> | ||
| 606 | <bool>false</bool> | ||
| 607 | </property> | ||
| 608 | <layout class="QGridLayout" name="gridLayout_4"> | ||
| 609 | <item row="1" column="1"> | ||
| 610 | <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout"> | ||
| 611 | <item> | ||
| 612 | <layout class="QHBoxLayout" name="buttonLStickUpHorizontalLayout"> | ||
| 613 | <item> | ||
| 614 | <widget class="QLabel" name="labelLStickUp"> | ||
| 615 | <property name="text"> | ||
| 616 | <string>Up:</string> | ||
| 617 | </property> | ||
| 618 | </widget> | ||
| 619 | </item> | ||
| 620 | </layout> | ||
| 621 | </item> | ||
| 622 | <item> | ||
| 623 | <widget class="QPushButton" name="buttonLStickUp"> | ||
| 624 | <property name="text"> | ||
| 625 | <string/> | ||
| 626 | </property> | ||
| 627 | </widget> | ||
| 628 | </item> | ||
| 629 | </layout> | ||
| 630 | </item> | ||
| 631 | <item row="0" column="2"> | ||
| 632 | <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout"> | ||
| 633 | <item> | ||
| 634 | <layout class="QHBoxLayout" name="buttonLStickRightHorizontalLayout"> | ||
| 635 | <item> | ||
| 636 | <widget class="QLabel" name="labelLStickRight"> | ||
| 637 | <property name="text"> | ||
| 638 | <string>Right:</string> | ||
| 639 | </property> | ||
| 640 | </widget> | ||
| 641 | </item> | ||
| 642 | </layout> | ||
| 643 | </item> | ||
| 644 | <item> | ||
| 645 | <widget class="QPushButton" name="buttonLStickRight"> | ||
| 646 | <property name="text"> | ||
| 647 | <string/> | ||
| 648 | </property> | ||
| 649 | </widget> | ||
| 650 | </item> | ||
| 651 | </layout> | ||
| 652 | </item> | ||
| 653 | <item row="4" column="1" colspan="2"> | ||
| 654 | <widget class="QPushButton" name="buttonLStickAnalog"> | ||
| 655 | <property name="text"> | ||
| 656 | <string>Set Analog Stick</string> | ||
| 657 | </property> | ||
| 658 | </widget> | ||
| 659 | </item> | ||
| 660 | <item row="0" column="1"> | ||
| 661 | <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout"> | ||
| 662 | <item> | ||
| 663 | <layout class="QHBoxLayout" name="buttonLStickLeftHorizontalLayout_2"> | ||
| 664 | <item> | ||
| 665 | <widget class="QLabel" name="labelLStickLeft"> | ||
| 666 | <property name="text"> | ||
| 667 | <string>Left:</string> | ||
| 668 | </property> | ||
| 669 | </widget> | ||
| 670 | </item> | ||
| 671 | </layout> | ||
| 672 | </item> | ||
| 673 | <item> | ||
| 674 | <widget class="QPushButton" name="buttonLStickLeft"> | ||
| 675 | <property name="text"> | ||
| 676 | <string/> | ||
| 677 | </property> | ||
| 678 | </widget> | ||
| 679 | </item> | ||
| 680 | </layout> | ||
| 681 | </item> | ||
| 682 | <item row="1" column="2"> | ||
| 683 | <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout"> | ||
| 684 | <item> | ||
| 685 | <layout class="QHBoxLayout" name="buttonLStickDownHorizontalLayout"> | ||
| 686 | <item> | ||
| 687 | <widget class="QLabel" name="labelLStickDown"> | ||
| 688 | <property name="text"> | ||
| 689 | <string>Down:</string> | ||
| 690 | </property> | ||
| 691 | </widget> | ||
| 692 | </item> | ||
| 693 | </layout> | ||
| 694 | </item> | ||
| 695 | <item> | ||
| 696 | <widget class="QPushButton" name="buttonLStickDown"> | ||
| 697 | <property name="text"> | ||
| 698 | <string/> | ||
| 699 | </property> | ||
| 700 | </widget> | ||
| 701 | </item> | ||
| 702 | </layout> | ||
| 703 | </item> | ||
| 704 | <item row="3" column="2"> | ||
| 705 | <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout"> | ||
| 706 | <item> | ||
| 707 | <layout class="QHBoxLayout" name="buttonLStickModHorizontalLayout"> | ||
| 708 | <item> | ||
| 709 | <widget class="QLabel" name="labelLStickMod"> | ||
| 710 | <property name="text"> | ||
| 711 | <string>Modifier:</string> | ||
| 712 | </property> | ||
| 713 | </widget> | ||
| 714 | </item> | ||
| 715 | </layout> | ||
| 716 | </item> | ||
| 717 | <item> | ||
| 718 | <widget class="QPushButton" name="buttonLStickMod"> | ||
| 719 | <property name="text"> | ||
| 720 | <string/> | ||
| 721 | </property> | ||
| 722 | </widget> | ||
| 723 | </item> | ||
| 724 | </layout> | ||
| 725 | </item> | ||
| 726 | <item row="3" column="1"> | ||
| 727 | <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0"> | ||
| 728 | <item> | ||
| 729 | <layout class="QHBoxLayout" name="buttonLStickPressedHorizontalLayout"> | ||
| 730 | <item> | ||
| 731 | <widget class="QLabel" name="labelLStickPressed"> | ||
| 732 | <property name="text"> | ||
| 733 | <string>Pressed:</string> | ||
| 734 | </property> | ||
| 735 | </widget> | ||
| 736 | </item> | ||
| 737 | </layout> | ||
| 738 | </item> | ||
| 739 | <item> | ||
| 740 | <widget class="QPushButton" name="buttonLStick"> | ||
| 741 | <property name="text"> | ||
| 742 | <string/> | ||
| 743 | </property> | ||
| 744 | </widget> | ||
| 745 | </item> | ||
| 746 | </layout> | ||
| 747 | </item> | ||
| 748 | </layout> | ||
| 749 | </widget> | ||
| 750 | </item> | ||
| 751 | <item row="3" column="0"> | ||
| 752 | <widget class="QGroupBox" name="shoulderButtons"> | ||
| 753 | <property name="title"> | ||
| 754 | <string>Shoulder Buttons</string> | ||
| 755 | </property> | ||
| 756 | <property name="flat"> | ||
| 757 | <bool>false</bool> | ||
| 758 | </property> | ||
| 759 | <property name="checkable"> | ||
| 760 | <bool>false</bool> | ||
| 761 | </property> | ||
| 762 | <layout class="QGridLayout" name="gridLayout_3"> | ||
| 763 | <item row="3" column="0"> | ||
| 764 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> | ||
| 765 | <item> | ||
| 766 | <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout"> | ||
| 767 | <item> | ||
| 768 | <widget class="QLabel" name="labelSL"> | ||
| 769 | <property name="text"> | ||
| 770 | <string>SL:</string> | ||
| 771 | </property> | ||
| 772 | </widget> | ||
| 773 | </item> | ||
| 774 | </layout> | ||
| 775 | </item> | ||
| 776 | <item> | ||
| 777 | <widget class="QPushButton" name="buttonSL"> | ||
| 778 | <property name="text"> | ||
| 779 | <string/> | ||
| 780 | </property> | ||
| 781 | </widget> | ||
| 782 | </item> | ||
| 783 | </layout> | ||
| 784 | </item> | ||
| 785 | <item row="2" column="1"> | ||
| 786 | <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout"> | ||
| 787 | <item> | ||
| 788 | <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout"> | ||
| 789 | <item> | ||
| 790 | <widget class="QLabel" name="labelZR"> | ||
| 791 | <property name="text"> | ||
| 792 | <string>ZR:</string> | ||
| 793 | </property> | ||
| 794 | </widget> | ||
| 795 | </item> | ||
| 796 | </layout> | ||
| 797 | </item> | ||
| 798 | <item> | ||
| 799 | <widget class="QPushButton" name="buttonZR"> | ||
| 800 | <property name="text"> | ||
| 801 | <string/> | ||
| 802 | </property> | ||
| 803 | </widget> | ||
| 804 | </item> | ||
| 805 | </layout> | ||
| 806 | </item> | ||
| 807 | <item row="3" column="1"> | ||
| 808 | <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> | ||
| 809 | <item> | ||
| 810 | <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout"> | ||
| 811 | <item> | ||
| 812 | <widget class="QLabel" name="labelSR"> | ||
| 813 | <property name="text"> | ||
| 814 | <string>SR:</string> | ||
| 815 | </property> | ||
| 816 | </widget> | ||
| 817 | </item> | ||
| 818 | </layout> | ||
| 819 | </item> | ||
| 820 | <item> | ||
| 821 | <widget class="QPushButton" name="buttonSR"> | ||
| 822 | <property name="text"> | ||
| 823 | <string/> | ||
| 824 | </property> | ||
| 825 | </widget> | ||
| 826 | </item> | ||
| 827 | </layout> | ||
| 828 | </item> | ||
| 829 | <item row="0" column="1"> | ||
| 830 | <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout"> | ||
| 831 | <item> | ||
| 832 | <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout"> | ||
| 833 | <item> | ||
| 834 | <widget class="QLabel" name="labelZL"> | ||
| 835 | <property name="text"> | ||
| 836 | <string>ZL:</string> | ||
| 837 | </property> | ||
| 838 | </widget> | ||
| 839 | </item> | ||
| 840 | </layout> | ||
| 841 | </item> | ||
| 842 | <item> | ||
| 843 | <widget class="QPushButton" name="buttonZL"> | ||
| 844 | <property name="text"> | ||
| 845 | <string/> | ||
| 846 | </property> | ||
| 847 | </widget> | ||
| 848 | </item> | ||
| 849 | </layout> | ||
| 850 | </item> | ||
| 851 | <item row="0" column="0"> | ||
| 852 | <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout"> | ||
| 853 | <item> | ||
| 854 | <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout"> | ||
| 855 | <item> | ||
| 856 | <widget class="QLabel" name="labelL"> | ||
| 857 | <property name="text"> | ||
| 858 | <string>L:</string> | ||
| 859 | </property> | ||
| 860 | </widget> | ||
| 861 | </item> | ||
| 862 | </layout> | ||
| 863 | </item> | ||
| 864 | <item> | ||
| 865 | <widget class="QPushButton" name="buttonL"> | ||
| 866 | <property name="text"> | ||
| 867 | <string/> | ||
| 868 | </property> | ||
| 869 | </widget> | ||
| 870 | </item> | ||
| 871 | </layout> | ||
| 872 | </item> | ||
| 873 | <item row="2" column="0"> | ||
| 874 | <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> | ||
| 875 | <item> | ||
| 876 | <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout"> | ||
| 877 | <item> | ||
| 878 | <widget class="QLabel" name="labelR"> | ||
| 879 | <property name="text"> | ||
| 880 | <string>R:</string> | ||
| 881 | </property> | ||
| 882 | </widget> | ||
| 883 | </item> | ||
| 884 | </layout> | ||
| 885 | </item> | ||
| 886 | <item> | ||
| 887 | <widget class="QPushButton" name="buttonR"> | ||
| 888 | <property name="text"> | ||
| 889 | <string/> | ||
| 890 | </property> | ||
| 891 | </widget> | ||
| 892 | </item> | ||
| 893 | </layout> | ||
| 894 | </item> | ||
| 895 | </layout> | ||
| 896 | </widget> | ||
| 897 | </item> | ||
| 898 | <item row="3" column="1"> | ||
| 899 | <widget class="QGroupBox" name="misc"> | ||
| 900 | <property name="title"> | ||
| 901 | <string>Misc.</string> | ||
| 902 | </property> | ||
| 903 | <property name="flat"> | ||
| 904 | <bool>false</bool> | ||
| 905 | </property> | ||
| 906 | <property name="checkable"> | ||
| 907 | <bool>false</bool> | ||
| 908 | </property> | ||
| 909 | <layout class="QGridLayout" name="gridLayout_6"> | ||
| 910 | <item row="1" column="0"> | ||
| 911 | <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout"> | ||
| 912 | <item> | ||
| 913 | <layout class="QHBoxLayout" name="buttonMiscMinusHorizontalLayout"> | ||
| 914 | <item> | ||
| 915 | <widget class="QLabel" name="labelMinus"> | ||
| 916 | <property name="text"> | ||
| 917 | <string>Minus:</string> | ||
| 918 | </property> | ||
| 919 | </widget> | ||
| 920 | </item> | ||
| 921 | </layout> | ||
| 922 | </item> | ||
| 923 | <item> | ||
| 924 | <widget class="QPushButton" name="buttonMinus"> | ||
| 925 | <property name="text"> | ||
| 926 | <string/> | ||
| 927 | </property> | ||
| 928 | </widget> | ||
| 929 | </item> | ||
| 930 | </layout> | ||
| 931 | </item> | ||
| 932 | <item row="3" column="1"> | ||
| 933 | <spacer name="verticalSpacer_2"> | ||
| 934 | <property name="orientation"> | ||
| 935 | <enum>Qt::Vertical</enum> | ||
| 936 | </property> | ||
| 937 | <property name="sizeHint" stdset="0"> | ||
| 938 | <size> | ||
| 939 | <width>20</width> | ||
| 940 | <height>40</height> | ||
| 941 | </size> | ||
| 942 | </property> | ||
| 943 | </spacer> | ||
| 944 | </item> | ||
| 945 | <item row="0" column="0"> | ||
| 946 | <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout"> | ||
| 947 | <item> | ||
| 948 | <layout class="QHBoxLayout" name="buttonMiscPlusHorizontalLayout"> | ||
| 949 | <item> | ||
| 950 | <widget class="QLabel" name="labelPlus"> | ||
| 951 | <property name="text"> | ||
| 952 | <string>Plus:</string> | ||
| 953 | </property> | ||
| 954 | </widget> | ||
| 955 | </item> | ||
| 956 | </layout> | ||
| 957 | </item> | ||
| 958 | <item> | ||
| 959 | <widget class="QPushButton" name="buttonPlus"> | ||
| 960 | <property name="text"> | ||
| 961 | <string/> | ||
| 962 | </property> | ||
| 963 | </widget> | ||
| 964 | </item> | ||
| 965 | </layout> | ||
| 966 | </item> | ||
| 967 | <item row="0" column="1"> | ||
| 968 | <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout"> | ||
| 969 | <item> | ||
| 970 | <layout class="QHBoxLayout" name="buttonMiscHomeHorizontalLayout"> | ||
| 971 | <item> | ||
| 972 | <widget class="QLabel" name="labelHome"> | ||
| 973 | <property name="text"> | ||
| 974 | <string>Home:</string> | ||
| 975 | </property> | ||
| 976 | </widget> | ||
| 977 | </item> | ||
| 978 | </layout> | ||
| 979 | </item> | ||
| 980 | <item> | ||
| 981 | <widget class="QPushButton" name="buttonHome"> | ||
| 982 | <property name="text"> | ||
| 983 | <string/> | ||
| 984 | </property> | ||
| 985 | </widget> | ||
| 986 | </item> | ||
| 987 | </layout> | ||
| 988 | </item> | ||
| 989 | <item row="1" column="1"> | ||
| 990 | <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout"> | ||
| 991 | <item> | ||
| 992 | <layout class="QHBoxLayout" name="buttonMiscScrCapHorizontalLayout"> | ||
| 993 | <item> | ||
| 994 | <widget class="QLabel" name="labelScreenshot"> | ||
| 995 | <property name="text"> | ||
| 996 | <string>Screen Capture:</string> | ||
| 997 | </property> | ||
| 998 | <property name="wordWrap"> | ||
| 999 | <bool>false</bool> | ||
| 1000 | </property> | ||
| 1001 | </widget> | ||
| 1002 | </item> | ||
| 1003 | </layout> | ||
| 1004 | </item> | ||
| 1005 | <item> | ||
| 1006 | <widget class="QPushButton" name="buttonScreenshot"> | ||
| 1007 | <property name="sizePolicy"> | ||
| 1008 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||
| 1009 | <horstretch>0</horstretch> | ||
| 1010 | <verstretch>0</verstretch> | ||
| 1011 | </sizepolicy> | ||
| 1012 | </property> | ||
| 1013 | <property name="maximumSize"> | ||
| 1014 | <size> | ||
| 1015 | <width>80</width> | ||
| 1016 | <height>16777215</height> | ||
| 1017 | </size> | ||
| 1018 | </property> | ||
| 1019 | <property name="text"> | ||
| 1020 | <string/> | ||
| 1021 | </property> | ||
| 1022 | </widget> | ||
| 1023 | </item> | ||
| 1024 | </layout> | ||
| 1025 | </item> | ||
| 1026 | </layout> | ||
| 1027 | </widget> | ||
| 1028 | </item> | ||
| 1029 | </layout> | ||
| 1030 | </item> | ||
| 1031 | <item> | ||
| 1032 | <spacer name="verticalSpacer"> | ||
| 1033 | <property name="orientation"> | ||
| 1034 | <enum>Qt::Vertical</enum> | ||
| 1035 | </property> | ||
| 1036 | <property name="sizeHint" stdset="0"> | ||
| 1037 | <size> | ||
| 1038 | <width>20</width> | ||
| 1039 | <height>40</height> | ||
| 1040 | </size> | ||
| 1041 | </property> | ||
| 1042 | </spacer> | ||
| 1043 | </item> | ||
| 1044 | <item> | ||
| 1045 | <layout class="QHBoxLayout" name="horizontalLayout"/> | ||
| 1046 | </item> | ||
| 1047 | <item> | ||
| 1048 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||
| 1049 | <item> | ||
| 1050 | <widget class="QPushButton" name="buttonClearAll"> | ||
| 1051 | <property name="sizePolicy"> | ||
| 1052 | <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | ||
| 1053 | <horstretch>0</horstretch> | ||
| 1054 | <verstretch>0</verstretch> | ||
| 1055 | </sizepolicy> | ||
| 1056 | </property> | ||
| 1057 | <property name="sizeIncrement"> | ||
| 1058 | <size> | ||
| 1059 | <width>0</width> | ||
| 1060 | <height>0</height> | ||
| 1061 | </size> | ||
| 1062 | </property> | ||
| 1063 | <property name="baseSize"> | ||
| 1064 | <size> | ||
| 1065 | <width>0</width> | ||
| 1066 | <height>0</height> | ||
| 1067 | </size> | ||
| 1068 | </property> | ||
| 1069 | <property name="layoutDirection"> | ||
| 1070 | <enum>Qt::LeftToRight</enum> | ||
| 1071 | </property> | ||
| 1072 | <property name="text"> | ||
| 1073 | <string>Clear All</string> | ||
| 1074 | </property> | ||
| 1075 | </widget> | ||
| 1076 | </item> | ||
| 1077 | <item> | ||
| 1078 | <widget class="QPushButton" name="buttonRestoreDefaults"> | ||
| 1079 | <property name="sizePolicy"> | ||
| 1080 | <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | ||
| 1081 | <horstretch>0</horstretch> | ||
| 1082 | <verstretch>0</verstretch> | ||
| 1083 | </sizepolicy> | ||
| 1084 | </property> | ||
| 1085 | <property name="sizeIncrement"> | ||
| 1086 | <size> | ||
| 1087 | <width>0</width> | ||
| 1088 | <height>0</height> | ||
| 1089 | </size> | ||
| 1090 | </property> | ||
| 1091 | <property name="baseSize"> | ||
| 1092 | <size> | ||
| 1093 | <width>0</width> | ||
| 1094 | <height>0</height> | ||
| 1095 | </size> | ||
| 1096 | </property> | ||
| 1097 | <property name="layoutDirection"> | ||
| 1098 | <enum>Qt::LeftToRight</enum> | ||
| 1099 | </property> | ||
| 1100 | <property name="text"> | ||
| 1101 | <string>Restore Defaults</string> | ||
| 1102 | </property> | ||
| 1103 | </widget> | ||
| 1104 | </item> | ||
| 1105 | <item> | ||
| 1106 | <spacer name="horizontalSpacer"> | ||
| 1107 | <property name="orientation"> | ||
| 1108 | <enum>Qt::Horizontal</enum> | ||
| 1109 | </property> | ||
| 1110 | <property name="sizeHint" stdset="0"> | ||
| 1111 | <size> | ||
| 1112 | <width>40</width> | ||
| 1113 | <height>20</height> | ||
| 1114 | </size> | ||
| 1115 | </property> | ||
| 1116 | </spacer> | ||
| 1117 | </item> | ||
| 1118 | <item> | ||
| 1119 | <widget class="QDialogButtonBox" name="buttonBox"> | ||
| 1120 | <property name="standardButtons"> | ||
| 1121 | <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||
| 1122 | </property> | ||
| 1123 | </widget> | ||
| 1124 | </item> | ||
| 1125 | </layout> | ||
| 1126 | </item> | ||
| 1127 | </layout> | ||
| 1128 | </widget> | ||
| 1129 | <resources/> | ||
| 1130 | <connections> | ||
| 1131 | <connection> | ||
| 1132 | <sender>buttonBox</sender> | ||
| 1133 | <signal>accepted()</signal> | ||
| 1134 | <receiver>ConfigureInputPlayer</receiver> | ||
| 1135 | <slot>accept()</slot> | ||
| 1136 | <hints> | ||
| 1137 | <hint type="sourcelabel"> | ||
| 1138 | <x>371</x> | ||
| 1139 | <y>730</y> | ||
| 1140 | </hint> | ||
| 1141 | <hint type="destinationlabel"> | ||
| 1142 | <x>229</x> | ||
| 1143 | <y>375</y> | ||
| 1144 | </hint> | ||
| 1145 | </hints> | ||
| 1146 | </connection> | ||
| 1147 | <connection> | ||
| 1148 | <sender>buttonBox</sender> | ||
| 1149 | <signal>rejected()</signal> | ||
| 1150 | <receiver>ConfigureInputPlayer</receiver> | ||
| 1151 | <slot>reject()</slot> | ||
| 1152 | <hints> | ||
| 1153 | <hint type="sourcelabel"> | ||
| 1154 | <x>371</x> | ||
| 1155 | <y>730</y> | ||
| 1156 | </hint> | ||
| 1157 | <hint type="destinationlabel"> | ||
| 1158 | <x>229</x> | ||
| 1159 | <y>375</y> | ||
| 1160 | </hint> | ||
| 1161 | </hints> | ||
| 1162 | </connection> | ||
| 1163 | </connections> | ||
| 1164 | </ui> | ||
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp new file mode 100644 index 000000000..dab58fbaa --- /dev/null +++ b/src/yuzu/configuration/configure_mouse_advanced.cpp | |||
| @@ -0,0 +1,213 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <memory> | ||
| 7 | #include <utility> | ||
| 8 | #include <QKeyEvent> | ||
| 9 | #include <QMenu> | ||
| 10 | #include <QMessageBox> | ||
| 11 | #include <QTimer> | ||
| 12 | #include "common/assert.h" | ||
| 13 | #include "common/param_package.h" | ||
| 14 | #include "input_common/main.h" | ||
| 15 | #include "ui_configure_mouse_advanced.h" | ||
| 16 | #include "yuzu/configuration/config.h" | ||
| 17 | #include "yuzu/configuration/configure_mouse_advanced.h" | ||
| 18 | |||
| 19 | static QString GetKeyName(int key_code) { | ||
| 20 | switch (key_code) { | ||
| 21 | case Qt::Key_Shift: | ||
| 22 | return QObject::tr("Shift"); | ||
| 23 | case Qt::Key_Control: | ||
| 24 | return QObject::tr("Ctrl"); | ||
| 25 | case Qt::Key_Alt: | ||
| 26 | return QObject::tr("Alt"); | ||
| 27 | case Qt::Key_Meta: | ||
| 28 | return ""; | ||
| 29 | default: | ||
| 30 | return QKeySequence(key_code).toString(); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | static QString ButtonToText(const Common::ParamPackage& param) { | ||
| 35 | if (!param.Has("engine")) { | ||
| 36 | return QObject::tr("[not set]"); | ||
| 37 | } else if (param.Get("engine", "") == "keyboard") { | ||
| 38 | return GetKeyName(param.Get("code", 0)); | ||
| 39 | } else if (param.Get("engine", "") == "sdl") { | ||
| 40 | if (param.Has("hat")) { | ||
| 41 | return QString(QObject::tr("Hat %1 %2")) | ||
| 42 | .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str()); | ||
| 43 | } | ||
| 44 | if (param.Has("axis")) { | ||
| 45 | return QString(QObject::tr("Axis %1%2")) | ||
| 46 | .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str()); | ||
| 47 | } | ||
| 48 | if (param.Has("button")) { | ||
| 49 | return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str()); | ||
| 50 | } | ||
| 51 | return QString(); | ||
| 52 | } else { | ||
| 53 | return QObject::tr("[unknown]"); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) | ||
| 58 | : QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), | ||
| 59 | timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) { | ||
| 60 | ui->setupUi(this); | ||
| 61 | setFocusPolicy(Qt::ClickFocus); | ||
| 62 | |||
| 63 | button_map = { | ||
| 64 | ui->left_button, ui->right_button, ui->middle_button, ui->forward_button, ui->back_button, | ||
| 65 | }; | ||
| 66 | |||
| 67 | for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) { | ||
| 68 | if (!button_map[button_id]) | ||
| 69 | continue; | ||
| 70 | button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu); | ||
| 71 | connect(button_map[button_id], &QPushButton::released, [=]() { | ||
| 72 | handleClick( | ||
| 73 | button_map[button_id], | ||
| 74 | [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, | ||
| 75 | InputCommon::Polling::DeviceType::Button); | ||
| 76 | }); | ||
| 77 | connect(button_map[button_id], &QPushButton::customContextMenuRequested, | ||
| 78 | [=](const QPoint& menu_location) { | ||
| 79 | QMenu context_menu; | ||
| 80 | context_menu.addAction(tr("Clear"), [&] { | ||
| 81 | buttons_param[button_id].Clear(); | ||
| 82 | button_map[button_id]->setText(tr("[not set]")); | ||
| 83 | }); | ||
| 84 | context_menu.addAction(tr("Restore Default"), [&] { | ||
| 85 | buttons_param[button_id] = | ||
| 86 | Common::ParamPackage{InputCommon::GenerateKeyboardParam( | ||
| 87 | Config::default_mouse_buttons[button_id])}; | ||
| 88 | button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); | ||
| 89 | }); | ||
| 90 | context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); | ||
| 91 | }); | ||
| 92 | } | ||
| 93 | |||
| 94 | connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); }); | ||
| 95 | connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); | ||
| 96 | |||
| 97 | timeout_timer->setSingleShot(true); | ||
| 98 | connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); }); | ||
| 99 | |||
| 100 | connect(poll_timer.get(), &QTimer::timeout, [this]() { | ||
| 101 | Common::ParamPackage params; | ||
| 102 | for (auto& poller : device_pollers) { | ||
| 103 | params = poller->GetNextInput(); | ||
| 104 | if (params.Has("engine")) { | ||
| 105 | setPollingResult(params, false); | ||
| 106 | return; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | }); | ||
| 110 | |||
| 111 | loadConfiguration(); | ||
| 112 | resize(0, 0); | ||
| 113 | } | ||
| 114 | |||
| 115 | ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default; | ||
| 116 | |||
| 117 | void ConfigureMouseAdvanced::applyConfiguration() { | ||
| 118 | std::transform(buttons_param.begin(), buttons_param.end(), | ||
| 119 | Settings::values.mouse_buttons.begin(), | ||
| 120 | [](const Common::ParamPackage& param) { return param.Serialize(); }); | ||
| 121 | } | ||
| 122 | |||
| 123 | void ConfigureMouseAdvanced::loadConfiguration() { | ||
| 124 | std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(), | ||
| 125 | buttons_param.begin(), | ||
| 126 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 127 | updateButtonLabels(); | ||
| 128 | } | ||
| 129 | |||
| 130 | void ConfigureMouseAdvanced::restoreDefaults() { | ||
| 131 | for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) { | ||
| 132 | buttons_param[button_id] = Common::ParamPackage{ | ||
| 133 | InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])}; | ||
| 134 | } | ||
| 135 | |||
| 136 | updateButtonLabels(); | ||
| 137 | } | ||
| 138 | |||
| 139 | void ConfigureMouseAdvanced::ClearAll() { | ||
| 140 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | ||
| 141 | if (button_map[i] && button_map[i]->isEnabled()) | ||
| 142 | buttons_param[i].Clear(); | ||
| 143 | } | ||
| 144 | |||
| 145 | updateButtonLabels(); | ||
| 146 | } | ||
| 147 | |||
| 148 | void ConfigureMouseAdvanced::updateButtonLabels() { | ||
| 149 | for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) { | ||
| 150 | button_map[button]->setText(ButtonToText(buttons_param[button])); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | void ConfigureMouseAdvanced::handleClick( | ||
| 155 | QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, | ||
| 156 | InputCommon::Polling::DeviceType type) { | ||
| 157 | button->setText(tr("[press key]")); | ||
| 158 | button->setFocus(); | ||
| 159 | |||
| 160 | const auto iter = std::find(button_map.begin(), button_map.end(), button); | ||
| 161 | ASSERT(iter != button_map.end()); | ||
| 162 | const auto index = std::distance(button_map.begin(), iter); | ||
| 163 | ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); | ||
| 164 | |||
| 165 | input_setter = new_input_setter; | ||
| 166 | |||
| 167 | device_pollers = InputCommon::Polling::GetPollers(type); | ||
| 168 | |||
| 169 | // Keyboard keys can only be used as button devices | ||
| 170 | want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; | ||
| 171 | |||
| 172 | for (auto& poller : device_pollers) { | ||
| 173 | poller->Start(); | ||
| 174 | } | ||
| 175 | |||
| 176 | grabKeyboard(); | ||
| 177 | grabMouse(); | ||
| 178 | timeout_timer->start(5000); // Cancel after 5 seconds | ||
| 179 | poll_timer->start(200); // Check for new inputs every 200ms | ||
| 180 | } | ||
| 181 | |||
| 182 | void ConfigureMouseAdvanced::setPollingResult(const Common::ParamPackage& params, bool abort) { | ||
| 183 | releaseKeyboard(); | ||
| 184 | releaseMouse(); | ||
| 185 | timeout_timer->stop(); | ||
| 186 | poll_timer->stop(); | ||
| 187 | for (auto& poller : device_pollers) { | ||
| 188 | poller->Stop(); | ||
| 189 | } | ||
| 190 | |||
| 191 | if (!abort) { | ||
| 192 | (*input_setter)(params); | ||
| 193 | } | ||
| 194 | |||
| 195 | updateButtonLabels(); | ||
| 196 | input_setter = std::nullopt; | ||
| 197 | } | ||
| 198 | |||
| 199 | void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) { | ||
| 200 | if (!input_setter || !event) | ||
| 201 | return; | ||
| 202 | |||
| 203 | if (event->key() != Qt::Key_Escape) { | ||
| 204 | if (want_keyboard_keys) { | ||
| 205 | setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, | ||
| 206 | false); | ||
| 207 | } else { | ||
| 208 | // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling | ||
| 209 | return; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | setPollingResult({}, true); | ||
| 213 | } | ||
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h new file mode 100644 index 000000000..218df2bda --- /dev/null +++ b/src/yuzu/configuration/configure_mouse_advanced.h | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <optional> | ||
| 9 | #include <QDialog> | ||
| 10 | #include <QWidget> | ||
| 11 | #include "core/settings.h" | ||
| 12 | |||
| 13 | class QCheckBox; | ||
| 14 | class QPushButton; | ||
| 15 | class QTimer; | ||
| 16 | |||
| 17 | namespace Ui { | ||
| 18 | class ConfigureMouseAdvanced; | ||
| 19 | } | ||
| 20 | |||
| 21 | class ConfigureMouseAdvanced : public QDialog { | ||
| 22 | Q_OBJECT | ||
| 23 | |||
| 24 | public: | ||
| 25 | explicit ConfigureMouseAdvanced(QWidget* parent); | ||
| 26 | ~ConfigureMouseAdvanced() override; | ||
| 27 | |||
| 28 | void applyConfiguration(); | ||
| 29 | |||
| 30 | private: | ||
| 31 | std::unique_ptr<Ui::ConfigureMouseAdvanced> ui; | ||
| 32 | |||
| 33 | /// This will be the the setting function when an input is awaiting configuration. | ||
| 34 | std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; | ||
| 35 | |||
| 36 | std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map; | ||
| 37 | std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param; | ||
| 38 | |||
| 39 | std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; | ||
| 40 | |||
| 41 | std::unique_ptr<QTimer> timeout_timer; | ||
| 42 | std::unique_ptr<QTimer> poll_timer; | ||
| 43 | |||
| 44 | /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, | ||
| 45 | /// keyboard events are ignored. | ||
| 46 | bool want_keyboard_keys = false; | ||
| 47 | |||
| 48 | /// Load configuration settings. | ||
| 49 | void loadConfiguration(); | ||
| 50 | /// Restore all buttons to their default values. | ||
| 51 | void restoreDefaults(); | ||
| 52 | /// Clear all input configuration | ||
| 53 | void ClearAll(); | ||
| 54 | |||
| 55 | /// Update UI to reflect current configuration. | ||
| 56 | void updateButtonLabels(); | ||
| 57 | |||
| 58 | /// Called when the button was pressed. | ||
| 59 | void handleClick(QPushButton* button, | ||
| 60 | std::function<void(const Common::ParamPackage&)> new_input_setter, | ||
| 61 | InputCommon::Polling::DeviceType type); | ||
| 62 | |||
| 63 | /// Finish polling and configure input using the input_setter | ||
| 64 | void setPollingResult(const Common::ParamPackage& params, bool abort); | ||
| 65 | |||
| 66 | /// Handle key press events. | ||
| 67 | void keyPressEvent(QKeyEvent* event) override; | ||
| 68 | }; | ||
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui new file mode 100644 index 000000000..08245ecf0 --- /dev/null +++ b/src/yuzu/configuration/configure_mouse_advanced.ui | |||
| @@ -0,0 +1,261 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigureMouseAdvanced</class> | ||
| 4 | <widget class="QDialog" name="ConfigureMouseAdvanced"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>250</width> | ||
| 10 | <height>261</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>Configure Mouse</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QVBoxLayout" name="verticalLayout"> | ||
| 17 | <item> | ||
| 18 | <widget class="QGroupBox" name="gridGroupBox"> | ||
| 19 | <property name="title"> | ||
| 20 | <string>Mouse Buttons</string> | ||
| 21 | </property> | ||
| 22 | <layout class="QGridLayout" name="gridLayout"> | ||
| 23 | <item row="0" column="4"> | ||
| 24 | <spacer name="horizontalSpacer_2"> | ||
| 25 | <property name="orientation"> | ||
| 26 | <enum>Qt::Horizontal</enum> | ||
| 27 | </property> | ||
| 28 | <property name="sizeType"> | ||
| 29 | <enum>QSizePolicy::Fixed</enum> | ||
| 30 | </property> | ||
| 31 | <property name="sizeHint" stdset="0"> | ||
| 32 | <size> | ||
| 33 | <width>20</width> | ||
| 34 | <height>20</height> | ||
| 35 | </size> | ||
| 36 | </property> | ||
| 37 | </spacer> | ||
| 38 | </item> | ||
| 39 | <item row="0" column="3"> | ||
| 40 | <layout class="QVBoxLayout" name="verticalLayout_4"> | ||
| 41 | <item> | ||
| 42 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||
| 43 | <item> | ||
| 44 | <widget class="QLabel" name="label_3"> | ||
| 45 | <property name="text"> | ||
| 46 | <string>Right:</string> | ||
| 47 | </property> | ||
| 48 | </widget> | ||
| 49 | </item> | ||
| 50 | </layout> | ||
| 51 | </item> | ||
| 52 | <item> | ||
| 53 | <widget class="QPushButton" name="right_button"> | ||
| 54 | <property name="minimumSize"> | ||
| 55 | <size> | ||
| 56 | <width>75</width> | ||
| 57 | <height>0</height> | ||
| 58 | </size> | ||
| 59 | </property> | ||
| 60 | <property name="text"> | ||
| 61 | <string/> | ||
| 62 | </property> | ||
| 63 | </widget> | ||
| 64 | </item> | ||
| 65 | </layout> | ||
| 66 | </item> | ||
| 67 | <item row="0" column="0"> | ||
| 68 | <spacer name="horizontalSpacer"> | ||
| 69 | <property name="orientation"> | ||
| 70 | <enum>Qt::Horizontal</enum> | ||
| 71 | </property> | ||
| 72 | <property name="sizeType"> | ||
| 73 | <enum>QSizePolicy::Fixed</enum> | ||
| 74 | </property> | ||
| 75 | <property name="sizeHint" stdset="0"> | ||
| 76 | <size> | ||
| 77 | <width>20</width> | ||
| 78 | <height>20</height> | ||
| 79 | </size> | ||
| 80 | </property> | ||
| 81 | </spacer> | ||
| 82 | </item> | ||
| 83 | <item row="2" column="1"> | ||
| 84 | <layout class="QVBoxLayout" name="verticalLayout_3"> | ||
| 85 | <item> | ||
| 86 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||
| 87 | <item> | ||
| 88 | <widget class="QLabel" name="label_2"> | ||
| 89 | <property name="text"> | ||
| 90 | <string>Middle:</string> | ||
| 91 | </property> | ||
| 92 | </widget> | ||
| 93 | </item> | ||
| 94 | </layout> | ||
| 95 | </item> | ||
| 96 | <item> | ||
| 97 | <widget class="QPushButton" name="middle_button"> | ||
| 98 | <property name="text"> | ||
| 99 | <string/> | ||
| 100 | </property> | ||
| 101 | </widget> | ||
| 102 | </item> | ||
| 103 | </layout> | ||
| 104 | </item> | ||
| 105 | <item row="3" column="1"> | ||
| 106 | <layout class="QVBoxLayout" name="verticalLayout_5"> | ||
| 107 | <item> | ||
| 108 | <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
| 109 | <item> | ||
| 110 | <widget class="QLabel" name="label_4"> | ||
| 111 | <property name="minimumSize"> | ||
| 112 | <size> | ||
| 113 | <width>54</width> | ||
| 114 | <height>0</height> | ||
| 115 | </size> | ||
| 116 | </property> | ||
| 117 | <property name="text"> | ||
| 118 | <string>Back:</string> | ||
| 119 | </property> | ||
| 120 | </widget> | ||
| 121 | </item> | ||
| 122 | </layout> | ||
| 123 | </item> | ||
| 124 | <item> | ||
| 125 | <widget class="QPushButton" name="back_button"> | ||
| 126 | <property name="text"> | ||
| 127 | <string/> | ||
| 128 | </property> | ||
| 129 | </widget> | ||
| 130 | </item> | ||
| 131 | </layout> | ||
| 132 | </item> | ||
| 133 | <item row="0" column="1"> | ||
| 134 | <layout class="QVBoxLayout" name="verticalLayout_2"> | ||
| 135 | <item> | ||
| 136 | <layout class="QHBoxLayout" name="horizontalLayout"> | ||
| 137 | <item> | ||
| 138 | <widget class="QLabel" name="label"> | ||
| 139 | <property name="text"> | ||
| 140 | <string>Left:</string> | ||
| 141 | </property> | ||
| 142 | </widget> | ||
| 143 | </item> | ||
| 144 | </layout> | ||
| 145 | </item> | ||
| 146 | <item> | ||
| 147 | <widget class="QPushButton" name="left_button"> | ||
| 148 | <property name="minimumSize"> | ||
| 149 | <size> | ||
| 150 | <width>75</width> | ||
| 151 | <height>0</height> | ||
| 152 | </size> | ||
| 153 | </property> | ||
| 154 | <property name="text"> | ||
| 155 | <string/> | ||
| 156 | </property> | ||
| 157 | </widget> | ||
| 158 | </item> | ||
| 159 | </layout> | ||
| 160 | </item> | ||
| 161 | <item row="3" column="3"> | ||
| 162 | <layout class="QVBoxLayout" name="verticalLayout_6"> | ||
| 163 | <item> | ||
| 164 | <layout class="QHBoxLayout" name="horizontalLayout_5"> | ||
| 165 | <item> | ||
| 166 | <widget class="QLabel" name="label_5"> | ||
| 167 | <property name="text"> | ||
| 168 | <string>Forward:</string> | ||
| 169 | </property> | ||
| 170 | </widget> | ||
| 171 | </item> | ||
| 172 | </layout> | ||
| 173 | </item> | ||
| 174 | <item> | ||
| 175 | <widget class="QPushButton" name="forward_button"> | ||
| 176 | <property name="text"> | ||
| 177 | <string/> | ||
| 178 | </property> | ||
| 179 | </widget> | ||
| 180 | </item> | ||
| 181 | </layout> | ||
| 182 | </item> | ||
| 183 | </layout> | ||
| 184 | </widget> | ||
| 185 | </item> | ||
| 186 | <item> | ||
| 187 | <layout class="QHBoxLayout" name="horizontalLayout_6"> | ||
| 188 | <item> | ||
| 189 | <widget class="QPushButton" name="buttonClearAll"> | ||
| 190 | <property name="text"> | ||
| 191 | <string>Clear All</string> | ||
| 192 | </property> | ||
| 193 | </widget> | ||
| 194 | </item> | ||
| 195 | <item> | ||
| 196 | <widget class="QPushButton" name="buttonRestoreDefaults"> | ||
| 197 | <property name="text"> | ||
| 198 | <string>Restore Defaults</string> | ||
| 199 | </property> | ||
| 200 | </widget> | ||
| 201 | </item> | ||
| 202 | <item> | ||
| 203 | <spacer name="horizontalSpacer_3"> | ||
| 204 | <property name="orientation"> | ||
| 205 | <enum>Qt::Horizontal</enum> | ||
| 206 | </property> | ||
| 207 | <property name="sizeHint" stdset="0"> | ||
| 208 | <size> | ||
| 209 | <width>40</width> | ||
| 210 | <height>20</height> | ||
| 211 | </size> | ||
| 212 | </property> | ||
| 213 | </spacer> | ||
| 214 | </item> | ||
| 215 | </layout> | ||
| 216 | </item> | ||
| 217 | <item> | ||
| 218 | <widget class="QDialogButtonBox" name="buttonBox"> | ||
| 219 | <property name="standardButtons"> | ||
| 220 | <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||
| 221 | </property> | ||
| 222 | </widget> | ||
| 223 | </item> | ||
| 224 | </layout> | ||
| 225 | </widget> | ||
| 226 | <resources/> | ||
| 227 | <connections> | ||
| 228 | <connection> | ||
| 229 | <sender>buttonBox</sender> | ||
| 230 | <signal>accepted()</signal> | ||
| 231 | <receiver>ConfigureMouseAdvanced</receiver> | ||
| 232 | <slot>accept()</slot> | ||
| 233 | <hints> | ||
| 234 | <hint type="sourcelabel"> | ||
| 235 | <x>124</x> | ||
| 236 | <y>266</y> | ||
| 237 | </hint> | ||
| 238 | <hint type="destinationlabel"> | ||
| 239 | <x>124</x> | ||
| 240 | <y>143</y> | ||
| 241 | </hint> | ||
| 242 | </hints> | ||
| 243 | </connection> | ||
| 244 | <connection> | ||
| 245 | <sender>buttonBox</sender> | ||
| 246 | <signal>rejected()</signal> | ||
| 247 | <receiver>ConfigureMouseAdvanced</receiver> | ||
| 248 | <slot>reject()</slot> | ||
| 249 | <hints> | ||
| 250 | <hint type="sourcelabel"> | ||
| 251 | <x>124</x> | ||
| 252 | <y>266</y> | ||
| 253 | </hint> | ||
| 254 | <hint type="destinationlabel"> | ||
| 255 | <x>124</x> | ||
| 256 | <y>143</y> | ||
| 257 | </hint> | ||
| 258 | </hints> | ||
| 259 | </connection> | ||
| 260 | </connections> | ||
| 261 | </ui> | ||
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.cpp b/src/yuzu/configuration/configure_touchscreen_advanced.cpp new file mode 100644 index 000000000..9c1561e9d --- /dev/null +++ b/src/yuzu/configuration/configure_touchscreen_advanced.cpp | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include "ui_configure_touchscreen_advanced.h" | ||
| 7 | #include "yuzu/configuration/config.h" | ||
| 8 | #include "yuzu/configuration/configure_touchscreen_advanced.h" | ||
| 9 | |||
| 10 | ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent) | ||
| 11 | : QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchscreenAdvanced>()) { | ||
| 12 | ui->setupUi(this); | ||
| 13 | |||
| 14 | connect(ui->restore_defaults_button, &QPushButton::pressed, this, | ||
| 15 | &ConfigureTouchscreenAdvanced::restoreDefaults); | ||
| 16 | |||
| 17 | loadConfiguration(); | ||
| 18 | resize(0, 0); | ||
| 19 | } | ||
| 20 | |||
| 21 | ConfigureTouchscreenAdvanced::~ConfigureTouchscreenAdvanced() = default; | ||
| 22 | |||
| 23 | void ConfigureTouchscreenAdvanced::applyConfiguration() { | ||
| 24 | Settings::values.touchscreen.finger = ui->finger_box->value(); | ||
| 25 | Settings::values.touchscreen.diameter_x = ui->diameter_x_box->value(); | ||
| 26 | Settings::values.touchscreen.diameter_y = ui->diameter_y_box->value(); | ||
| 27 | Settings::values.touchscreen.rotation_angle = ui->angle_box->value(); | ||
| 28 | } | ||
| 29 | |||
| 30 | void ConfigureTouchscreenAdvanced::loadConfiguration() { | ||
| 31 | ui->finger_box->setValue(Settings::values.touchscreen.finger); | ||
| 32 | ui->diameter_x_box->setValue(Settings::values.touchscreen.diameter_x); | ||
| 33 | ui->diameter_y_box->setValue(Settings::values.touchscreen.diameter_y); | ||
| 34 | ui->angle_box->setValue(Settings::values.touchscreen.rotation_angle); | ||
| 35 | } | ||
| 36 | |||
| 37 | void ConfigureTouchscreenAdvanced::restoreDefaults() { | ||
| 38 | ui->finger_box->setValue(0); | ||
| 39 | ui->diameter_x_box->setValue(15); | ||
| 40 | ui->diameter_y_box->setValue(15); | ||
| 41 | ui->angle_box->setValue(0); | ||
| 42 | } | ||
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.h b/src/yuzu/configuration/configure_touchscreen_advanced.h new file mode 100644 index 000000000..41cd255fb --- /dev/null +++ b/src/yuzu/configuration/configure_touchscreen_advanced.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <QDialog> | ||
| 9 | #include <QWidget> | ||
| 10 | #include "yuzu/configuration/config.h" | ||
| 11 | |||
| 12 | namespace Ui { | ||
| 13 | class ConfigureTouchscreenAdvanced; | ||
| 14 | } | ||
| 15 | |||
| 16 | class ConfigureTouchscreenAdvanced : public QDialog { | ||
| 17 | Q_OBJECT | ||
| 18 | |||
| 19 | public: | ||
| 20 | explicit ConfigureTouchscreenAdvanced(QWidget* parent); | ||
| 21 | ~ConfigureTouchscreenAdvanced() override; | ||
| 22 | |||
| 23 | void applyConfiguration(); | ||
| 24 | |||
| 25 | private: | ||
| 26 | /// Load configuration settings. | ||
| 27 | void loadConfiguration(); | ||
| 28 | /// Restore all buttons to their default values. | ||
| 29 | void restoreDefaults(); | ||
| 30 | |||
| 31 | std::unique_ptr<Ui::ConfigureTouchscreenAdvanced> ui; | ||
| 32 | }; | ||
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.ui b/src/yuzu/configuration/configure_touchscreen_advanced.ui new file mode 100644 index 000000000..1171c2dd1 --- /dev/null +++ b/src/yuzu/configuration/configure_touchscreen_advanced.ui | |||
| @@ -0,0 +1,199 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigureTouchscreenAdvanced</class> | ||
| 4 | <widget class="QDialog" name="ConfigureTouchscreenAdvanced"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>298</width> | ||
| 10 | <height>339</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>Configure Touchscreen</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QVBoxLayout" name="verticalLayout"> | ||
| 17 | <item> | ||
| 18 | <widget class="QLabel" name="label_2"> | ||
| 19 | <property name="minimumSize"> | ||
| 20 | <size> | ||
| 21 | <width>280</width> | ||
| 22 | <height>0</height> | ||
| 23 | </size> | ||
| 24 | </property> | ||
| 25 | <property name="text"> | ||
| 26 | <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> | ||
| 27 | </property> | ||
| 28 | <property name="wordWrap"> | ||
| 29 | <bool>true</bool> | ||
| 30 | </property> | ||
| 31 | </widget> | ||
| 32 | </item> | ||
| 33 | <item> | ||
| 34 | <spacer name="verticalSpacer_2"> | ||
| 35 | <property name="orientation"> | ||
| 36 | <enum>Qt::Vertical</enum> | ||
| 37 | </property> | ||
| 38 | <property name="sizeType"> | ||
| 39 | <enum>QSizePolicy::Fixed</enum> | ||
| 40 | </property> | ||
| 41 | <property name="sizeHint" stdset="0"> | ||
| 42 | <size> | ||
| 43 | <width>20</width> | ||
| 44 | <height>20</height> | ||
| 45 | </size> | ||
| 46 | </property> | ||
| 47 | </spacer> | ||
| 48 | </item> | ||
| 49 | <item> | ||
| 50 | <widget class="QGroupBox" name="gridGroupBox"> | ||
| 51 | <property name="title"> | ||
| 52 | <string>Touch Parameters</string> | ||
| 53 | </property> | ||
| 54 | <layout class="QGridLayout" name="gridLayout"> | ||
| 55 | <item row="0" column="0"> | ||
| 56 | <spacer name="horizontalSpacer"> | ||
| 57 | <property name="orientation"> | ||
| 58 | <enum>Qt::Horizontal</enum> | ||
| 59 | </property> | ||
| 60 | <property name="sizeHint" stdset="0"> | ||
| 61 | <size> | ||
| 62 | <width>40</width> | ||
| 63 | <height>20</height> | ||
| 64 | </size> | ||
| 65 | </property> | ||
| 66 | </spacer> | ||
| 67 | </item> | ||
| 68 | <item row="2" column="1"> | ||
| 69 | <widget class="QLabel" name="label_4"> | ||
| 70 | <property name="text"> | ||
| 71 | <string>Touch Diameter Y</string> | ||
| 72 | </property> | ||
| 73 | </widget> | ||
| 74 | </item> | ||
| 75 | <item row="0" column="1"> | ||
| 76 | <widget class="QLabel" name="label"> | ||
| 77 | <property name="text"> | ||
| 78 | <string>Finger</string> | ||
| 79 | </property> | ||
| 80 | </widget> | ||
| 81 | </item> | ||
| 82 | <item row="0" column="3"> | ||
| 83 | <spacer name="horizontalSpacer_2"> | ||
| 84 | <property name="orientation"> | ||
| 85 | <enum>Qt::Horizontal</enum> | ||
| 86 | </property> | ||
| 87 | <property name="sizeHint" stdset="0"> | ||
| 88 | <size> | ||
| 89 | <width>40</width> | ||
| 90 | <height>20</height> | ||
| 91 | </size> | ||
| 92 | </property> | ||
| 93 | </spacer> | ||
| 94 | </item> | ||
| 95 | <item row="1" column="1"> | ||
| 96 | <widget class="QLabel" name="label_3"> | ||
| 97 | <property name="text"> | ||
| 98 | <string>Touch Diameter X</string> | ||
| 99 | </property> | ||
| 100 | </widget> | ||
| 101 | </item> | ||
| 102 | <item row="0" column="2"> | ||
| 103 | <widget class="QSpinBox" name="finger_box"> | ||
| 104 | <property name="minimumSize"> | ||
| 105 | <size> | ||
| 106 | <width>80</width> | ||
| 107 | <height>0</height> | ||
| 108 | </size> | ||
| 109 | </property> | ||
| 110 | </widget> | ||
| 111 | </item> | ||
| 112 | <item row="3" column="1"> | ||
| 113 | <widget class="QLabel" name="label_5"> | ||
| 114 | <property name="text"> | ||
| 115 | <string>Rotational Angle</string> | ||
| 116 | </property> | ||
| 117 | </widget> | ||
| 118 | </item> | ||
| 119 | <item row="1" column="2"> | ||
| 120 | <widget class="QSpinBox" name="diameter_x_box"/> | ||
| 121 | </item> | ||
| 122 | <item row="2" column="2"> | ||
| 123 | <widget class="QSpinBox" name="diameter_y_box"/> | ||
| 124 | </item> | ||
| 125 | <item row="3" column="2"> | ||
| 126 | <widget class="QSpinBox" name="angle_box"/> | ||
| 127 | </item> | ||
| 128 | </layout> | ||
| 129 | </widget> | ||
| 130 | </item> | ||
| 131 | <item> | ||
| 132 | <spacer name="verticalSpacer"> | ||
| 133 | <property name="orientation"> | ||
| 134 | <enum>Qt::Vertical</enum> | ||
| 135 | </property> | ||
| 136 | <property name="sizeHint" stdset="0"> | ||
| 137 | <size> | ||
| 138 | <width>20</width> | ||
| 139 | <height>40</height> | ||
| 140 | </size> | ||
| 141 | </property> | ||
| 142 | </spacer> | ||
| 143 | </item> | ||
| 144 | <item> | ||
| 145 | <layout class="QHBoxLayout" name="horizontalLayout"> | ||
| 146 | <item> | ||
| 147 | <widget class="QPushButton" name="restore_defaults_button"> | ||
| 148 | <property name="text"> | ||
| 149 | <string>Restore Defaults</string> | ||
| 150 | </property> | ||
| 151 | </widget> | ||
| 152 | </item> | ||
| 153 | <item> | ||
| 154 | <widget class="QDialogButtonBox" name="buttonBox"> | ||
| 155 | <property name="standardButtons"> | ||
| 156 | <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||
| 157 | </property> | ||
| 158 | </widget> | ||
| 159 | </item> | ||
| 160 | </layout> | ||
| 161 | </item> | ||
| 162 | </layout> | ||
| 163 | </widget> | ||
| 164 | <resources/> | ||
| 165 | <connections> | ||
| 166 | <connection> | ||
| 167 | <sender>buttonBox</sender> | ||
| 168 | <signal>accepted()</signal> | ||
| 169 | <receiver>ConfigureTouchscreenAdvanced</receiver> | ||
| 170 | <slot>accept()</slot> | ||
| 171 | <hints> | ||
| 172 | <hint type="sourcelabel"> | ||
| 173 | <x>140</x> | ||
| 174 | <y>318</y> | ||
| 175 | </hint> | ||
| 176 | <hint type="destinationlabel"> | ||
| 177 | <x>140</x> | ||
| 178 | <y>169</y> | ||
| 179 | </hint> | ||
| 180 | </hints> | ||
| 181 | </connection> | ||
| 182 | <connection> | ||
| 183 | <sender>buttonBox</sender> | ||
| 184 | <signal>rejected()</signal> | ||
| 185 | <receiver>ConfigureTouchscreenAdvanced</receiver> | ||
| 186 | <slot>reject()</slot> | ||
| 187 | <hints> | ||
| 188 | <hint type="sourcelabel"> | ||
| 189 | <x>140</x> | ||
| 190 | <y>318</y> | ||
| 191 | </hint> | ||
| 192 | <hint type="destinationlabel"> | ||
| 193 | <x>140</x> | ||
| 194 | <y>169</y> | ||
| 195 | </hint> | ||
| 196 | </hints> | ||
| 197 | </connection> | ||
| 198 | </connections> | ||
| 199 | </ui> | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4262bd0eb..9e13bbf7c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -1372,7 +1372,13 @@ void GMainWindow::OnConfigure() { | |||
| 1372 | UpdateUITheme(); | 1372 | UpdateUITheme(); |
| 1373 | if (UISettings::values.enable_discord_presence != old_discord_presence) | 1373 | if (UISettings::values.enable_discord_presence != old_discord_presence) |
| 1374 | SetDiscordEnabled(UISettings::values.enable_discord_presence); | 1374 | SetDiscordEnabled(UISettings::values.enable_discord_presence); |
| 1375 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 1375 | |
| 1376 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); | ||
| 1377 | if (reload) { | ||
| 1378 | game_list->PopulateAsync(UISettings::values.gamedir, | ||
| 1379 | UISettings::values.gamedir_deepscan); | ||
| 1380 | } | ||
| 1381 | |||
| 1376 | config->Save(); | 1382 | config->Save(); |
| 1377 | } | 1383 | } |
| 1378 | } | 1384 | } |
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h index 32a0d813c..e80aebc0a 100644 --- a/src/yuzu/ui_settings.h +++ b/src/yuzu/ui_settings.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <atomic> | ||
| 8 | #include <vector> | 9 | #include <vector> |
| 9 | #include <QByteArray> | 10 | #include <QByteArray> |
| 10 | #include <QString> | 11 | #include <QString> |
| @@ -63,6 +64,7 @@ struct Values { | |||
| 63 | uint32_t icon_size; | 64 | uint32_t icon_size; |
| 64 | uint8_t row_1_text_id; | 65 | uint8_t row_1_text_id; |
| 65 | uint8_t row_2_text_id; | 66 | uint8_t row_2_text_id; |
| 67 | std::atomic_bool is_game_list_reload_pending{false}; | ||
| 66 | }; | 68 | }; |
| 67 | 69 | ||
| 68 | extern Values values; | 70 | extern Values values; |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 9cc409fd5..c66353a65 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -65,54 +65,246 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> | |||
| 65 | }, | 65 | }, |
| 66 | }}; | 66 | }}; |
| 67 | 67 | ||
| 68 | static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> default_mouse_buttons = { | ||
| 69 | SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_APOSTROPHE, | ||
| 70 | SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, | ||
| 71 | }; | ||
| 72 | |||
| 73 | static const std::array<int, 0x8A> keyboard_keys = { | ||
| 74 | 0, | ||
| 75 | 0, | ||
| 76 | 0, | ||
| 77 | 0, | ||
| 78 | SDL_SCANCODE_A, | ||
| 79 | SDL_SCANCODE_B, | ||
| 80 | SDL_SCANCODE_C, | ||
| 81 | SDL_SCANCODE_D, | ||
| 82 | SDL_SCANCODE_E, | ||
| 83 | SDL_SCANCODE_F, | ||
| 84 | SDL_SCANCODE_G, | ||
| 85 | SDL_SCANCODE_H, | ||
| 86 | SDL_SCANCODE_I, | ||
| 87 | SDL_SCANCODE_J, | ||
| 88 | SDL_SCANCODE_K, | ||
| 89 | SDL_SCANCODE_L, | ||
| 90 | SDL_SCANCODE_M, | ||
| 91 | SDL_SCANCODE_N, | ||
| 92 | SDL_SCANCODE_O, | ||
| 93 | SDL_SCANCODE_P, | ||
| 94 | SDL_SCANCODE_Q, | ||
| 95 | SDL_SCANCODE_R, | ||
| 96 | SDL_SCANCODE_S, | ||
| 97 | SDL_SCANCODE_T, | ||
| 98 | SDL_SCANCODE_U, | ||
| 99 | SDL_SCANCODE_V, | ||
| 100 | SDL_SCANCODE_W, | ||
| 101 | SDL_SCANCODE_X, | ||
| 102 | SDL_SCANCODE_Y, | ||
| 103 | SDL_SCANCODE_Z, | ||
| 104 | SDL_SCANCODE_1, | ||
| 105 | SDL_SCANCODE_2, | ||
| 106 | SDL_SCANCODE_3, | ||
| 107 | SDL_SCANCODE_4, | ||
| 108 | SDL_SCANCODE_5, | ||
| 109 | SDL_SCANCODE_6, | ||
| 110 | SDL_SCANCODE_7, | ||
| 111 | SDL_SCANCODE_8, | ||
| 112 | SDL_SCANCODE_9, | ||
| 113 | SDL_SCANCODE_0, | ||
| 114 | SDL_SCANCODE_RETURN, | ||
| 115 | SDL_SCANCODE_ESCAPE, | ||
| 116 | SDL_SCANCODE_BACKSPACE, | ||
| 117 | SDL_SCANCODE_TAB, | ||
| 118 | SDL_SCANCODE_SPACE, | ||
| 119 | SDL_SCANCODE_MINUS, | ||
| 120 | SDL_SCANCODE_EQUALS, | ||
| 121 | SDL_SCANCODE_LEFTBRACKET, | ||
| 122 | SDL_SCANCODE_RIGHTBRACKET, | ||
| 123 | SDL_SCANCODE_BACKSLASH, | ||
| 124 | 0, | ||
| 125 | SDL_SCANCODE_SEMICOLON, | ||
| 126 | SDL_SCANCODE_APOSTROPHE, | ||
| 127 | SDL_SCANCODE_GRAVE, | ||
| 128 | SDL_SCANCODE_COMMA, | ||
| 129 | SDL_SCANCODE_PERIOD, | ||
| 130 | SDL_SCANCODE_SLASH, | ||
| 131 | SDL_SCANCODE_CAPSLOCK, | ||
| 132 | |||
| 133 | SDL_SCANCODE_F1, | ||
| 134 | SDL_SCANCODE_F2, | ||
| 135 | SDL_SCANCODE_F3, | ||
| 136 | SDL_SCANCODE_F4, | ||
| 137 | SDL_SCANCODE_F5, | ||
| 138 | SDL_SCANCODE_F6, | ||
| 139 | SDL_SCANCODE_F7, | ||
| 140 | SDL_SCANCODE_F8, | ||
| 141 | SDL_SCANCODE_F9, | ||
| 142 | SDL_SCANCODE_F10, | ||
| 143 | SDL_SCANCODE_F11, | ||
| 144 | SDL_SCANCODE_F12, | ||
| 145 | |||
| 146 | 0, | ||
| 147 | SDL_SCANCODE_SCROLLLOCK, | ||
| 148 | SDL_SCANCODE_PAUSE, | ||
| 149 | SDL_SCANCODE_INSERT, | ||
| 150 | SDL_SCANCODE_HOME, | ||
| 151 | SDL_SCANCODE_PAGEUP, | ||
| 152 | SDL_SCANCODE_DELETE, | ||
| 153 | SDL_SCANCODE_END, | ||
| 154 | SDL_SCANCODE_PAGEDOWN, | ||
| 155 | SDL_SCANCODE_RIGHT, | ||
| 156 | SDL_SCANCODE_LEFT, | ||
| 157 | SDL_SCANCODE_DOWN, | ||
| 158 | SDL_SCANCODE_UP, | ||
| 159 | |||
| 160 | SDL_SCANCODE_NUMLOCKCLEAR, | ||
| 161 | SDL_SCANCODE_KP_DIVIDE, | ||
| 162 | SDL_SCANCODE_KP_MULTIPLY, | ||
| 163 | SDL_SCANCODE_KP_MINUS, | ||
| 164 | SDL_SCANCODE_KP_PLUS, | ||
| 165 | SDL_SCANCODE_KP_ENTER, | ||
| 166 | SDL_SCANCODE_KP_1, | ||
| 167 | SDL_SCANCODE_KP_2, | ||
| 168 | SDL_SCANCODE_KP_3, | ||
| 169 | SDL_SCANCODE_KP_4, | ||
| 170 | SDL_SCANCODE_KP_5, | ||
| 171 | SDL_SCANCODE_KP_6, | ||
| 172 | SDL_SCANCODE_KP_7, | ||
| 173 | SDL_SCANCODE_KP_8, | ||
| 174 | SDL_SCANCODE_KP_9, | ||
| 175 | SDL_SCANCODE_KP_0, | ||
| 176 | SDL_SCANCODE_KP_PERIOD, | ||
| 177 | |||
| 178 | 0, | ||
| 179 | 0, | ||
| 180 | SDL_SCANCODE_POWER, | ||
| 181 | SDL_SCANCODE_KP_EQUALS, | ||
| 182 | |||
| 183 | SDL_SCANCODE_F13, | ||
| 184 | SDL_SCANCODE_F14, | ||
| 185 | SDL_SCANCODE_F15, | ||
| 186 | SDL_SCANCODE_F16, | ||
| 187 | SDL_SCANCODE_F17, | ||
| 188 | SDL_SCANCODE_F18, | ||
| 189 | SDL_SCANCODE_F19, | ||
| 190 | SDL_SCANCODE_F20, | ||
| 191 | SDL_SCANCODE_F21, | ||
| 192 | SDL_SCANCODE_F22, | ||
| 193 | SDL_SCANCODE_F23, | ||
| 194 | SDL_SCANCODE_F24, | ||
| 195 | |||
| 196 | 0, | ||
| 197 | SDL_SCANCODE_HELP, | ||
| 198 | SDL_SCANCODE_MENU, | ||
| 199 | 0, | ||
| 200 | 0, | ||
| 201 | 0, | ||
| 202 | 0, | ||
| 203 | 0, | ||
| 204 | 0, | ||
| 205 | 0, | ||
| 206 | 0, | ||
| 207 | 0, | ||
| 208 | 0, | ||
| 209 | 0, | ||
| 210 | 0, | ||
| 211 | SDL_SCANCODE_KP_COMMA, | ||
| 212 | SDL_SCANCODE_KP_LEFTPAREN, | ||
| 213 | SDL_SCANCODE_KP_RIGHTPAREN, | ||
| 214 | 0, | ||
| 215 | 0, | ||
| 216 | 0, | ||
| 217 | 0, | ||
| 218 | }; | ||
| 219 | |||
| 220 | static const std::array<int, 8> keyboard_mods{ | ||
| 221 | SDL_SCANCODE_LCTRL, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_LALT, SDL_SCANCODE_LGUI, | ||
| 222 | SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI, | ||
| 223 | }; | ||
| 224 | |||
| 68 | void Config::ReadValues() { | 225 | void Config::ReadValues() { |
| 69 | // Controls | 226 | // Controls |
| 227 | for (std::size_t p = 0; p < Settings::values.players.size(); ++p) { | ||
| 228 | const auto group = fmt::format("ControlsP{}", p); | ||
| 229 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | ||
| 230 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | ||
| 231 | Settings::values.players[p].buttons[i] = | ||
| 232 | sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param); | ||
| 233 | if (Settings::values.players[p].buttons[i].empty()) | ||
| 234 | Settings::values.players[p].buttons[i] = default_param; | ||
| 235 | } | ||
| 236 | |||
| 237 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 238 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 239 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 240 | default_analogs[i][3], default_analogs[i][4], 0.5f); | ||
| 241 | Settings::values.players[p].analogs[i] = | ||
| 242 | sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param); | ||
| 243 | if (Settings::values.players[p].analogs[i].empty()) | ||
| 244 | Settings::values.players[p].analogs[i] = default_param; | ||
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 | Settings::values.mouse_enabled = | ||
| 249 | sdl2_config->GetBoolean("ControlsGeneral", "mouse_enabled", false); | ||
| 250 | for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { | ||
| 251 | std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]); | ||
| 252 | Settings::values.mouse_buttons[i] = sdl2_config->Get( | ||
| 253 | "ControlsGeneral", std::string("mouse_") + Settings::NativeMouseButton::mapping[i], | ||
| 254 | default_param); | ||
| 255 | if (Settings::values.mouse_buttons[i].empty()) | ||
| 256 | Settings::values.mouse_buttons[i] = default_param; | ||
| 257 | } | ||
| 258 | |||
| 259 | Settings::values.motion_device = sdl2_config->Get( | ||
| 260 | "ControlsGeneral", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01"); | ||
| 261 | |||
| 262 | Settings::values.keyboard_enabled = | ||
| 263 | sdl2_config->GetBoolean("ControlsGeneral", "keyboard_enabled", false); | ||
| 264 | |||
| 265 | Settings::values.debug_pad_enabled = | ||
| 266 | sdl2_config->GetBoolean("ControlsGeneral", "debug_pad_enabled", false); | ||
| 70 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { | 267 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |
| 71 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); | 268 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); |
| 72 | Settings::values.buttons[i] = | 269 | Settings::values.debug_pad_buttons[i] = sdl2_config->Get( |
| 73 | sdl2_config->Get("Controls", Settings::NativeButton::mapping[i], default_param); | 270 | "ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i], |
| 74 | if (Settings::values.buttons[i].empty()) | 271 | default_param); |
| 75 | Settings::values.buttons[i] = default_param; | 272 | if (Settings::values.debug_pad_buttons[i].empty()) |
| 273 | Settings::values.debug_pad_buttons[i] = default_param; | ||
| 76 | } | 274 | } |
| 77 | 275 | ||
| 78 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | 276 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { |
| 79 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | 277 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( |
| 80 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | 278 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], |
| 81 | default_analogs[i][3], default_analogs[i][4], 0.5f); | 279 | default_analogs[i][3], default_analogs[i][4], 0.5f); |
| 82 | Settings::values.analogs[i] = | 280 | Settings::values.debug_pad_analogs[i] = sdl2_config->Get( |
| 83 | sdl2_config->Get("Controls", Settings::NativeAnalog::mapping[i], default_param); | 281 | "ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i], |
| 84 | if (Settings::values.analogs[i].empty()) | 282 | default_param); |
| 85 | Settings::values.analogs[i] = default_param; | 283 | if (Settings::values.debug_pad_analogs[i].empty()) |
| 284 | Settings::values.debug_pad_analogs[i] = default_param; | ||
| 86 | } | 285 | } |
| 87 | 286 | ||
| 88 | Settings::values.motion_device = sdl2_config->Get( | 287 | Settings::values.touchscreen.enabled = |
| 89 | "Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01"); | 288 | sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); |
| 90 | Settings::values.touch_device = | 289 | Settings::values.touchscreen.device = |
| 91 | sdl2_config->Get("Controls", "touch_device", "engine:emu_window"); | 290 | sdl2_config->Get("ControlsGeneral", "touch_device", "engine:emu_window"); |
| 92 | 291 | Settings::values.touchscreen.finger = | |
| 93 | // Core | 292 | sdl2_config->GetInteger("ControlsGeneral", "touch_finger", 0); |
| 94 | Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); | 293 | Settings::values.touchscreen.rotation_angle = |
| 95 | Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); | 294 | sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0); |
| 295 | Settings::values.touchscreen.diameter_x = | ||
| 296 | sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15); | ||
| 297 | Settings::values.touchscreen.diameter_y = | ||
| 298 | sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15); | ||
| 96 | 299 | ||
| 97 | // Renderer | 300 | std::transform(keyboard_keys.begin(), keyboard_keys.end(), |
| 98 | Settings::values.resolution_factor = | 301 | Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); |
| 99 | (float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0); | 302 | std::transform(keyboard_mods.begin(), keyboard_mods.end(), |
| 100 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); | 303 | Settings::values.keyboard_keys.begin() + |
| 101 | Settings::values.frame_limit = | 304 | Settings::NativeKeyboard::LeftControlKey, |
| 102 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); | 305 | InputCommon::GenerateKeyboardParam); |
| 103 | Settings::values.use_accurate_gpu_emulation = | 306 | std::transform(keyboard_mods.begin(), keyboard_mods.end(), |
| 104 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); | 307 | Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam); |
| 105 | |||
| 106 | Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); | ||
| 107 | Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); | ||
| 108 | Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0); | ||
| 109 | |||
| 110 | // Audio | ||
| 111 | Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); | ||
| 112 | Settings::values.enable_audio_stretching = | ||
| 113 | sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); | ||
| 114 | Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); | ||
| 115 | Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1); | ||
| 116 | 308 | ||
| 117 | // Data Storage | 309 | // Data Storage |
| 118 | Settings::values.use_virtual_sd = | 310 | Settings::values.use_virtual_sd = |
| @@ -139,6 +331,32 @@ void Config::ReadValues() { | |||
| 139 | Settings::values.rng_seed = std::nullopt; | 331 | Settings::values.rng_seed = std::nullopt; |
| 140 | } | 332 | } |
| 141 | 333 | ||
| 334 | // Core | ||
| 335 | Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); | ||
| 336 | Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); | ||
| 337 | |||
| 338 | // Renderer | ||
| 339 | Settings::values.resolution_factor = | ||
| 340 | (float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0); | ||
| 341 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); | ||
| 342 | Settings::values.frame_limit = | ||
| 343 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); | ||
| 344 | Settings::values.use_accurate_gpu_emulation = | ||
| 345 | sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); | ||
| 346 | |||
| 347 | Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); | ||
| 348 | Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); | ||
| 349 | Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0); | ||
| 350 | |||
| 351 | // Audio | ||
| 352 | Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); | ||
| 353 | Settings::values.enable_audio_stretching = | ||
| 354 | sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); | ||
| 355 | Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); | ||
| 356 | Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1); | ||
| 357 | |||
| 358 | Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1); | ||
| 359 | |||
| 142 | // Miscellaneous | 360 | // Miscellaneous |
| 143 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); | 361 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); |
| 144 | Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); | 362 | Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); |