summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.travis/clang-format/build.sh2
-rwxr-xr-x.travis/clang-format/deps.sh2
-rwxr-xr-x.travis/clang-format/docker.sh3
-rw-r--r--externals/cmake-modules/GetGitRevisionDescription.cmake.in8
-rw-r--r--src/core/frontend/input.h7
-rw-r--r--src/core/hle/kernel/kernel.cpp79
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/process.cpp6
-rw-r--r--src/core/hle/kernel/process.h3
-rw-r--r--src/core/hle/kernel/resource_limit.cpp75
-rw-r--r--src/core/hle/kernel/resource_limit.h98
-rw-r--r--src/core/hle/kernel/svc.cpp13
-rw-r--r--src/core/hle/kernel/vm_manager.cpp6
-rw-r--r--src/core/hle/kernel/vm_manager.h3
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp42
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h41
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp20
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h7
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp25
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h9
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp464
-rw-r--r--src/core/hle/service/hid/controllers/npad.h45
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h12
-rw-r--r--src/core/hle/service/hid/hid.cpp4
-rw-r--r--src/core/hle/service/ldr/ldr.cpp397
-rw-r--r--src/core/hle/service/lm/lm.cpp13
-rw-r--r--src/core/hle/service/ns/pl_u.cpp8
-rw-r--r--src/core/hle/service/time/interface.cpp3
-rw-r--r--src/core/hle/service/time/time.cpp15
-rw-r--r--src/core/hle/service/time/time.h1
-rw-r--r--src/core/hle/service/vi/vi.cpp22
-rw-r--r--src/core/loader/nro.cpp7
-rw-r--r--src/core/settings.cpp50
-rw-r--r--src/core/settings.h318
-rw-r--r--src/video_core/engines/maxwell_3d.cpp7
-rw-r--r--src/video_core/engines/maxwell_3d.h142
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp161
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h12
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h1
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp152
-rw-r--r--src/video_core/renderer_opengl/gl_state.h32
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h6
-rw-r--r--src/video_core/textures/decoders.cpp85
-rw-r--r--src/video_core/textures/decoders.h7
-rw-r--r--src/video_core/textures/texture.h15
-rw-r--r--src/yuzu/CMakeLists.txt9
-rw-r--r--src/yuzu/configuration/config.cpp385
-rw-r--r--src/yuzu/configuration/config.h14
-rw-r--r--src/yuzu/configuration/configure_gamelist.cpp14
-rw-r--r--src/yuzu/configuration/configure_gamelist.h2
-rw-r--r--src/yuzu/configuration/configure_general.cpp39
-rw-r--r--src/yuzu/configuration/configure_general.h1
-rw-r--r--src/yuzu/configuration/configure_general.ui9
-rw-r--r--src/yuzu/configuration/configure_input.cpp436
-rw-r--r--src/yuzu/configuration/configure_input.h52
-rw-r--r--src/yuzu/configuration/configure_input.ui1064
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp508
-rw-r--r--src/yuzu/configuration/configure_input_player.h103
-rw-r--r--src/yuzu/configuration/configure_input_player.ui1164
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp213
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.h68
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.ui261
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.cpp42
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.h32
-rw-r--r--src/yuzu/configuration/configure_touchscreen_advanced.ui199
-rw-r--r--src/yuzu/main.cpp8
-rw-r--r--src/yuzu/ui_settings.h2
-rw-r--r--src/yuzu_cmd/config.cpp288
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
3docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash -ex /yuzu/.travis/clang-format/docker.sh 3docker 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
3docker pull ubuntu:18.04 3docker 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
3apt-get update
4apt-get install -y clang-format-6.0
5
6# Run clang-format 3# Run clang-format
7cd /yuzu 4cd /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()
33endif() 33endif()
34 34
35if(NOT HEAD_HASH) 35if(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()
38endif() 42endif()
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 */
133using TouchDevice = InputDevice<std::tuple<float, float, bool>>; 133using 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 */
140using 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
242SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory( 196SharedPtr<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
247SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const { 200SharedPtr<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;
24class Thread; 24class Thread;
25class Timer; 25class Timer;
26 26
27enum class ResourceLimitCategory : u8;
28
29/// Represents a single instance of the kernel. 27/// Represents a single instance of the kernel.
30class KernelCore { 28class KernelCore {
31private: 29private:
@@ -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
255ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 255ResultCode 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
259ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { 259ResultCode 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
10namespace Kernel { 9namespace Kernel {
10namespace {
11constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
12 return static_cast<std::size_t>(type);
13}
14} // Anonymous namespace
11 15
12ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {} 16ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
13ResourceLimit::~ResourceLimit() = default; 17ResourceLimit::~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
22s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { 26s64 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: 30s64 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
49u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const { 34ResultCode 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
11union ResultCode;
12
10namespace Kernel { 13namespace Kernel {
11 14
12class KernelCore; 15class KernelCore;
13 16
14enum class ResourceLimitCategory : u8 {
15 APPLICATION = 0,
16 SYS_APPLET = 1,
17 LIB_APPLET = 2,
18 OTHER = 3
19};
20
21enum class ResourceType { 17enum 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
34class ResourceLimit final : public Object { 28class 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 76private:
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
110private:
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
1183static ResultCode ResetSignal(Handle handle) { 1172static 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
301ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 301ResultCode 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
10namespace Service::HID { 11namespace Service::HID {
11 12
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
14constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
16
12Controller_DebugPad::Controller_DebugPad() = default; 17Controller_DebugPad::Controller_DebugPad() = default;
13Controller_DebugPad::~Controller_DebugPad() = default; 18Controller_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
41void Controller_DebugPad::OnLoadInputDevices() {} 72void 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
13namespace Service::HID { 16namespace Service::HID {
14class Controller_DebugPad final : public ControllerBase { 17class 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
10namespace Service::HID { 11namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
13constexpr u8 KEYS_PER_BYTE = 8;
12 14
13Controller_Keyboard::Controller_Keyboard() = default; 15Controller_Keyboard::Controller_Keyboard() = default;
14Controller_Keyboard::~Controller_Keyboard() = default; 16Controller_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
42void Controller_Keyboard::OnLoadInputDevices() {} 53void 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
13namespace Service::HID { 15namespace Service::HID {
14class Controller_Keyboard final : public ControllerBase { 16class 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
10namespace Service::HID { 11namespace Service::HID {
@@ -14,7 +15,6 @@ Controller_Mouse::Controller_Mouse() = default;
14Controller_Mouse::~Controller_Mouse() = default; 15Controller_Mouse::~Controller_Mouse() = default;
15 16
16void Controller_Mouse::OnInit() {} 17void Controller_Mouse::OnInit() {}
17
18void Controller_Mouse::OnRelease() {} 18void Controller_Mouse::OnRelease() {}
19 19
20void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { 20void 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
42void Controller_Mouse::OnLoadInputDevices() {} 57void 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
12namespace Service::HID { 14namespace Service::HID {
13class Controller_Mouse final : public ControllerBase { 15class 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
19namespace Service::HID { 19namespace Service::HID {
20
21constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
22constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
23constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
24constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
25constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 20constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
26constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 21constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
27constexpr std::size_t NPAD_OFFSET = 0x9A00; 22constexpr std::size_t NPAD_OFFSET = 0x9A00;
28constexpr u32 BATTERY_FULL = 2; 23constexpr u32 BATTERY_FULL = 2;
29constexpr u32 NPAD_HANDHELD = 32;
30constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
31constexpr u32 MAX_NPAD_ID = 7; 24constexpr u32 MAX_NPAD_ID = 7;
32constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER =
33 Controller_NPad::NPadControllerType::JoyDual;
34constexpr std::array<u32, 10> npad_id_list{ 25constexpr 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
38enum class JoystickId : std::size_t { 29enum 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
43static std::size_t NPadIdToIndex(u32 npad_id) { 34static 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
50std::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
73u32 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
66Controller_NPad::Controller_NPad() = default; 94Controller_NPad::Controller_NPad() = default;
67Controller_NPad::~Controller_NPad() = default; 95Controller_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
156void Controller_NPad::OnLoadInputDevices() { 221void 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
165void Controller_NPad::OnRelease() {} 233void Controller_NPad::OnRelease() {}
166 234
235void 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
167void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { 289void 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
385void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { 462void 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
477Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { 547Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
478 return last_processed_vibration; 548 return last_processed_vibration;
479} 549}
550
480void Controller_NPad::AddNewController(NPadControllerType controller) { 551void 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
570void 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
498void Controller_NPad::ConnectNPad(u32 npad_id) { 582void 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
590bool 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
506Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { 620Controller_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
651void 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}
659void Controller_NPad::DisconnectAllConnectedControllers() {
660 std::for_each(connected_controllers.begin(), connected_controllers.end(),
661 [](ControllerHolder& controller) { controller.is_connected = false; });
662}
663
664void 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
673void 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
537bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { 681bool 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
13namespace Service::HID { 15namespace Service::HID {
14 16
17constexpr u32 NPAD_HANDHELD = 32;
18constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
19
15class Controller_NPad final : public ControllerBase { 20class Controller_NPad final : public ControllerBase {
16public: 21public:
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
116private: 129private:
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
62void Controller_Touchscreen::OnLoadInputDevices() { 63void 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
31private: 32private:
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 @@
34namespace Service::HID { 34namespace 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
38constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 38constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66;
39constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 39constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
40constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 40constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 41constexpr 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
14namespace Service::LDR { 17namespace Service::LDR {
15 18
19namespace ErrCodes {
20enum {
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
36constexpr ResultCode ERROR_INVALID_MEMORY_STATE(ErrorModule::Loader, ErrCodes::InvalidMemoryState);
37constexpr ResultCode ERROR_INVALID_NRO(ErrorModule::Loader, ErrCodes::InvalidNRO);
38constexpr ResultCode ERROR_INVALID_NRR(ErrorModule::Loader, ErrCodes::InvalidNRR);
39constexpr ResultCode ERROR_MISSING_NRR_HASH(ErrorModule::Loader, ErrCodes::MissingNRRHash);
40constexpr ResultCode ERROR_MAXIMUM_NRO(ErrorModule::Loader, ErrCodes::MaximumNRO);
41constexpr ResultCode ERROR_MAXIMUM_NRR(ErrorModule::Loader, ErrCodes::MaximumNRR);
42constexpr ResultCode ERROR_ALREADY_LOADED(ErrorModule::Loader, ErrCodes::AlreadyLoaded);
43constexpr ResultCode ERROR_INVALID_ALIGNMENT(ErrorModule::Loader, ErrCodes::InvalidAlignment);
44constexpr ResultCode ERROR_INVALID_SIZE(ErrorModule::Loader, ErrCodes::InvalidSize);
45constexpr ResultCode ERROR_INVALID_NRO_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNROAddress);
46constexpr ResultCode ERROR_INVALID_NRR_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNRRAddress);
47constexpr ResultCode ERROR_NOT_INITIALIZED(ErrorModule::Loader, ErrCodes::NotInitialized);
48
49constexpr u64 MAXIMUM_LOADED_RO = 0x40;
50
16class DebugMonitor final : public ServiceFramework<DebugMonitor> { 51class DebugMonitor final : public ServiceFramework<DebugMonitor> {
17public: 52public:
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
420private:
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
120void InstallInterfaces(SM::ServiceManager& sm) { 489void 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
302void 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
302Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) 317Module::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
242class IGBPEmptyResponseParcel : public Parcel {
243protected:
244 void SerializeData() override {
245 Write(data);
246 }
247
248private:
249 struct Data {
250 u32_le unk_0;
251 };
252
253 Data data{};
254};
255
240class IGBPSetPreallocatedBufferRequestParcel : public Parcel { 256class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
241public: 257public:
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
11namespace Settings { 11namespace Settings {
12 12
13namespace NativeButton {
14const 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
46namespace NativeAnalog {
47const std::array<const char*, NumAnalogs> mapping = {{
48 "lstick",
49 "rstick",
50}};
51}
52
53namespace NativeMouseButton {
54const std::array<const char*, NumMouseButtons> mapping = {{
55 "left",
56 "right",
57 "middle",
58 "forward",
59 "back",
60}};
61}
62
13Values values = {}; 63Values values = {};
14 64
15void Apply() { 65void 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;
60constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; 60constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
61constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; 61constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
62 62
63static const std::array<const char*, NumButtons> mapping = {{ 63extern 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;
105constexpr int STICK_HID_END = NumAnalogs; 76constexpr int STICK_HID_END = NumAnalogs;
106constexpr int NUM_STICKS_HID = NumAnalogs; 77constexpr int NUM_STICKS_HID = NumAnalogs;
107 78
108static const std::array<const char*, NumAnalogs> mapping = {{ 79extern const std::array<const char*, NumAnalogs> mapping;
109 "lstick",
110 "rstick",
111}};
112} // namespace NativeAnalog 80} // namespace NativeAnalog
113 81
82namespace NativeMouseButton {
83enum Values {
84 Left,
85 Right,
86 Middle,
87 Forward,
88 Back,
89
90 NumMouseButtons,
91};
92
93constexpr int MOUSE_HID_BEGIN = Left;
94constexpr int MOUSE_HID_END = NumMouseButtons;
95constexpr int NUM_MOUSE_HID = NumMouseButtons;
96
97extern const std::array<const char*, NumMouseButtons> mapping;
98} // namespace NativeMouseButton
99
100namespace NativeKeyboard {
101enum 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
278static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
279
280enum 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
296constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
297constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
298constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
299
300constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
301constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
302constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
303
304} // namespace NativeKeyboard
305
306using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
307using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
308using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
309using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
310using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
311
312constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
313constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
314constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
315constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
316
317enum class ControllerType {
318 ProController,
319 DualJoycon,
320 RightJoycon,
321 LeftJoycon,
322};
323
324struct 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
336struct 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
114struct Values { 346struct 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
71void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { 74void 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:
1100ASSERT_REG_POSITION(macros, 0x45); 1114ASSERT_REG_POSITION(macros, 0x45);
1101ASSERT_REG_POSITION(tfb_enabled, 0x1D1); 1115ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
1102ASSERT_REG_POSITION(rt, 0x200); 1116ASSERT_REG_POSITION(rt, 0x200);
1103ASSERT_REG_POSITION(viewport_transform[0], 0x280); 1117ASSERT_REG_POSITION(viewport_transform, 0x280);
1104ASSERT_REG_POSITION(viewport, 0x300); 1118ASSERT_REG_POSITION(viewports, 0x300);
1105ASSERT_REG_POSITION(vertex_buffer, 0x35D); 1119ASSERT_REG_POSITION(vertex_buffer, 0x35D);
1106ASSERT_REG_POSITION(clear_color[0], 0x360); 1120ASSERT_REG_POSITION(clear_color[0], 0x360);
1107ASSERT_REG_POSITION(clear_depth, 0x364); 1121ASSERT_REG_POSITION(clear_depth, 0x364);
@@ -1136,10 +1150,12 @@ ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4);
1136ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5); 1150ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5);
1137ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6); 1151ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
1138ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); 1152ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
1153ASSERT_REG_POSITION(frag_color_clamp, 0x4EA);
1139ASSERT_REG_POSITION(screen_y_control, 0x4EB); 1154ASSERT_REG_POSITION(screen_y_control, 0x4EB);
1140ASSERT_REG_POSITION(vb_element_base, 0x50D); 1155ASSERT_REG_POSITION(vb_element_base, 0x50D);
1141ASSERT_REG_POSITION(point_size, 0x546); 1156ASSERT_REG_POSITION(point_size, 0x546);
1142ASSERT_REG_POSITION(zeta_enable, 0x54E); 1157ASSERT_REG_POSITION(zeta_enable, 0x54E);
1158ASSERT_REG_POSITION(multisample_control, 0x54F);
1143ASSERT_REG_POSITION(tsc, 0x557); 1159ASSERT_REG_POSITION(tsc, 0x557);
1144ASSERT_REG_POSITION(tic, 0x55D); 1160ASSERT_REG_POSITION(tic, 0x55D);
1145ASSERT_REG_POSITION(stencil_two_side_enable, 0x565); 1161ASSERT_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
736void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) { 736void 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
924void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { 943void 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
1023void RasterizerOpenGL::SyncColorMask() { 1042void 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
1056void 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
1062void 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
1035void RasterizerOpenGL::SyncBlendState() { 1067void 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
1079void RasterizerOpenGL::SyncLogicOpState() { 1109void 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
1094void RasterizerOpenGL::SyncScissorTest() { 1123void 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
1110void RasterizerOpenGL::SyncTransformFeedback() { 1141void RasterizerOpenGL::SyncTransformFeedback() {
@@ -1118,11 +1149,7 @@ void RasterizerOpenGL::SyncTransformFeedback() {
1118 1149
1119void RasterizerOpenGL::SyncPointState() { 1150void 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
1128void RasterizerOpenGL::CheckAlphaTests() { 1155void 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
72private: 73private:
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;
14bool OpenGLState::s_rgb_used; 14bool OpenGLState::s_rgb_used;
15OpenGLState::OpenGLState() { 15OpenGLState::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
93void OpenGLState::ApplyDefaultState() { 97void OpenGLState::ApplyDefaultState() {
@@ -136,7 +140,7 @@ void OpenGLState::ApplyCulling() const {
136} 140}
137 141
138void OpenGLState::ApplyColorMask() const { 142void 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
233void 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
249void OpenGLState::ApplyViewport() const { 237void 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
40constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>(); 40constexpr u32 gob_size_x = 64;
41constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); 41constexpr u32 gob_size_y = 8;
42constexpr u32 gob_size_z = 1;
43constexpr u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
44constexpr u32 fast_swizzle_align = 16;
45
46constexpr auto legacy_swizzle_table = SwizzleTable<gob_size_y, gob_size_x, gob_size_z>();
47constexpr 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
173void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, 170void 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
229void 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
232std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, 238std::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
243void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, 247void 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
325std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 331std::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 */
22void 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 */
22std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y, 29std::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
50void Config::ReadValues() { 51const 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
56const 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
202const 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
207void 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
273void 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
302void 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
315void 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
331void 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
345void 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
233void Config::SaveValues() { 512void 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
541void 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
555void 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
565void 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
576void 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
26private: 30private:
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
41ConfigureGameList::~ConfigureGameList() = default; 51ConfigureGameList::~ConfigureGameList() = default;
@@ -49,6 +59,10 @@ void ConfigureGameList::applyConfiguration() {
49 Settings::Apply(); 59 Settings::Apply();
50} 60}
51 61
62void ConfigureGameList::RequestGameListUpdate() {
63 UISettings::values.is_game_list_reload_pending.exchange(true);
64}
65
52void ConfigureGameList::setConfiguration() { 66void 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
23private: 23private:
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
44void 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
71void ConfigureGeneral::applyConfiguration() { 42void 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
26private: 26private:
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
16const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM> 29ConfigureInput::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
25static 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
40static 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
51static 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
74static 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
95ConfigureInput::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
224void ConfigureInput::applyConfiguration() { 84template <typename Dialog, typename... Args>
225 std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.buttons.begin(), 85void 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
231void 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
241void ConfigureInput::restoreDefaults() { 95void 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
257void 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
272void 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
288void ConfigureInput::handleClick(QPushButton* button, 122void 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
311void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool abort) { 148void 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
328void ConfigureInput::keyPressEvent(QKeyEvent* event) { 170void 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) { 196void 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
22class QPushButton; 23class QPushButton;
23class QString; 24class QString;
@@ -37,57 +38,20 @@ public:
37 void applyConfiguration(); 38 void applyConfiguration();
38 39
39private: 40private:
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>
91Capture:</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
19const 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
28static 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
35static 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
44static 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
59static 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
70static 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
93static 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
114ConfigureInputPlayer::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
328ConfigureInputPlayer::~ConfigureInputPlayer() = default;
329
330void 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
354void 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
363void 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
401void 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
417void 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
433void 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
449void 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
477void 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
494void 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
20class QPushButton;
21class QString;
22class QTimer;
23
24namespace Ui {
25class ConfigureInputPlayer;
26}
27
28class ConfigureInputPlayer : public QDialog {
29 Q_OBJECT
30
31public:
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
38private:
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
19static 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
34static 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
57ConfigureMouseAdvanced::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
115ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default;
116
117void 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
123void 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
130void 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
139void 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
148void ConfigureMouseAdvanced::updateButtonLabels() {
149 for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) {
150 button_map[button]->setText(ButtonToText(buttons_param[button]));
151 }
152}
153
154void 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
182void 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
199void 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
13class QCheckBox;
14class QPushButton;
15class QTimer;
16
17namespace Ui {
18class ConfigureMouseAdvanced;
19}
20
21class ConfigureMouseAdvanced : public QDialog {
22 Q_OBJECT
23
24public:
25 explicit ConfigureMouseAdvanced(QWidget* parent);
26 ~ConfigureMouseAdvanced() override;
27
28 void applyConfiguration();
29
30private:
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
10ConfigureTouchscreenAdvanced::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
21ConfigureTouchscreenAdvanced::~ConfigureTouchscreenAdvanced() = default;
22
23void 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
30void 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
37void 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
12namespace Ui {
13class ConfigureTouchscreenAdvanced;
14}
15
16class ConfigureTouchscreenAdvanced : public QDialog {
17 Q_OBJECT
18
19public:
20 explicit ConfigureTouchscreenAdvanced(QWidget* parent);
21 ~ConfigureTouchscreenAdvanced() override;
22
23 void applyConfiguration();
24
25private:
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
68extern Values values; 70extern 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
68static 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
73static 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
220static 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
68void Config::ReadValues() { 225void 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);