summaryrefslogtreecommitdiffstats
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 @@
#!/bin/bash -ex
-docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash -ex /yuzu/.travis/clang-format/docker.sh
+docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache citraemu/build-environments:linux-clang-format /bin/bash -ex /yuzu/.travis/clang-format/docker.sh
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 @@
#!/bin/sh -ex
-docker pull ubuntu:18.04
+docker pull citraemu/build-environments:linux-clang-format
diff --git a/.travis/clang-format/docker.sh b/.travis/clang-format/docker.sh
index 05047e9b8..b519ab40e 100755
--- a/.travis/clang-format/docker.sh
+++ b/.travis/clang-format/docker.sh
@@ -1,8 +1,5 @@
#!/bin/bash -ex
-apt-get update
-apt-get install -y clang-format-6.0
-
# Run clang-format
cd /yuzu
./.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()
endif()
if(NOT HEAD_HASH)
- file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
- string(STRIP "${HEAD_HASH}" HEAD_HASH)
+ if(EXISTS "@GIT_DATA@/head-ref")
+ file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
+ string(STRIP "${HEAD_HASH}" HEAD_HASH)
+ else()
+ set(HEAD_HASH "Unknown")
+ endif()
endif()
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>
*/
using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
+/**
+ * A mouse device is an input device that returns a tuple of two floats and four ints.
+ * The first two floats are X and Y device coordinates of the mouse (from 0-1).
+ * The s32s are the mouse wheel.
+ */
+using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>;
+
} // namespace Input
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 {
void Initialize(KernelCore& kernel) {
Shutdown();
- InitializeResourceLimits(kernel);
+ InitializeSystemResourceLimit(kernel);
InitializeThreads();
InitializeTimers();
}
@@ -118,7 +118,7 @@ struct KernelCore::Impl {
process_list.clear();
current_process = nullptr;
- resource_limits.fill(nullptr);
+ system_resource_limit = nullptr;
thread_wakeup_callback_handle_table.Clear();
thread_wakeup_event_type = nullptr;
@@ -129,63 +129,17 @@ struct KernelCore::Impl {
named_ports.clear();
}
- void InitializeResourceLimits(KernelCore& kernel) {
- // Create the four resource limits that the system uses
- // Create the APPLICATION resource limit
- SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications");
- resource_limit->max_priority = 0x18;
- resource_limit->max_commit = 0x4000000;
- resource_limit->max_threads = 0x20;
- resource_limit->max_events = 0x20;
- resource_limit->max_mutexes = 0x20;
- resource_limit->max_semaphores = 0x8;
- resource_limit->max_timers = 0x8;
- resource_limit->max_shared_mems = 0x10;
- resource_limit->max_address_arbiters = 0x2;
- resource_limit->max_cpu_time = 0x1E;
- resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
-
- // Create the SYS_APPLET resource limit
- resource_limit = ResourceLimit::Create(kernel, "System Applets");
- resource_limit->max_priority = 0x4;
- resource_limit->max_commit = 0x5E00000;
- resource_limit->max_threads = 0x1D;
- resource_limit->max_events = 0xB;
- resource_limit->max_mutexes = 0x8;
- resource_limit->max_semaphores = 0x4;
- resource_limit->max_timers = 0x4;
- resource_limit->max_shared_mems = 0x8;
- resource_limit->max_address_arbiters = 0x3;
- resource_limit->max_cpu_time = 0x2710;
- resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
-
- // Create the LIB_APPLET resource limit
- resource_limit = ResourceLimit::Create(kernel, "Library Applets");
- resource_limit->max_priority = 0x4;
- resource_limit->max_commit = 0x600000;
- resource_limit->max_threads = 0xE;
- resource_limit->max_events = 0x8;
- resource_limit->max_mutexes = 0x8;
- resource_limit->max_semaphores = 0x4;
- resource_limit->max_timers = 0x4;
- resource_limit->max_shared_mems = 0x8;
- resource_limit->max_address_arbiters = 0x1;
- resource_limit->max_cpu_time = 0x2710;
- resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
-
- // Create the OTHER resource limit
- resource_limit = ResourceLimit::Create(kernel, "Others");
- resource_limit->max_priority = 0x4;
- resource_limit->max_commit = 0x2180000;
- resource_limit->max_threads = 0xE1;
- resource_limit->max_events = 0x108;
- resource_limit->max_mutexes = 0x25;
- resource_limit->max_semaphores = 0x43;
- resource_limit->max_timers = 0x2C;
- resource_limit->max_shared_mems = 0x1F;
- resource_limit->max_address_arbiters = 0x2D;
- resource_limit->max_cpu_time = 0x3E8;
- resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
+ // Creates the default system resource limit
+ void InitializeSystemResourceLimit(KernelCore& kernel) {
+ system_resource_limit = ResourceLimit::Create(kernel, "System");
+
+ // If setting the default system values fails, then something seriously wrong has occurred.
+ ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
+ .IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
}
void InitializeThreads() {
@@ -208,7 +162,7 @@ struct KernelCore::Impl {
std::vector<SharedPtr<Process>> process_list;
Process* current_process = nullptr;
- std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
+ SharedPtr<ResourceLimit> system_resource_limit;
/// The event type of the generic timer callback event
CoreTiming::EventType* timer_callback_event_type = nullptr;
@@ -239,9 +193,8 @@ void KernelCore::Shutdown() {
impl->Shutdown();
}
-SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
- ResourceLimitCategory category) const {
- return impl->resource_limits.at(static_cast<std::size_t>(category));
+SharedPtr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
+ return impl->system_resource_limit;
}
SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {
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;
class Thread;
class Timer;
-enum class ResourceLimitCategory : u8;
-
/// Represents a single instance of the kernel.
class KernelCore {
private:
@@ -47,8 +45,8 @@ public:
/// Clears all resources in use by the kernel instance.
void Shutdown();
- /// Retrieves a shared pointer to a ResourceLimit identified by the given category.
- SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;
+ /// Retrieves a shared pointer to the system resource limit instance.
+ SharedPtr<ResourceLimit> GetSystemResourceLimit() const;
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
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) {
process->name = std::move(name);
process->flags.raw = 0;
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
- process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION);
+ process->resource_limit = kernel.GetSystemResourceLimit();
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = kernel.CreateNewProcessID();
@@ -252,8 +252,8 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
return vm_manager.HeapFree(target, size);
}
-ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
- return vm_manager.MirrorMemory(dst_addr, src_addr, size);
+ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
+ return vm_manager.MirrorMemory(dst_addr, src_addr, size, state);
}
ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index cf48787ce..230e395ff 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -259,7 +259,8 @@ public:
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
- ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
+ ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
+ MemoryState state = MemoryState::Mapped);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cstring>
-#include "common/assert.h"
-#include "common/logging/log.h"
+#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/resource_limit.h"
+#include "core/hle/result.h"
namespace Kernel {
+namespace {
+constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
+ return static_cast<std::size_t>(type);
+}
+} // Anonymous namespace
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
ResourceLimit::~ResourceLimit() = default;
@@ -19,59 +23,22 @@ SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string n
return resource_limit;
}
-s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
- switch (resource) {
- case ResourceType::Commit:
- return current_commit;
- case ResourceType::Thread:
- return current_threads;
- case ResourceType::Event:
- return current_events;
- case ResourceType::Mutex:
- return current_mutexes;
- case ResourceType::Semaphore:
- return current_semaphores;
- case ResourceType::Timer:
- return current_timers;
- case ResourceType::SharedMemory:
- return current_shared_mems;
- case ResourceType::AddressArbiter:
- return current_address_arbiters;
- case ResourceType::CPUTime:
- return current_cpu_time;
- default:
- LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
- UNIMPLEMENTED();
- return 0;
- }
+s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
+ return values.at(ResourceTypeToIndex(resource));
+}
+
+s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
+ return limits.at(ResourceTypeToIndex(resource));
}
-u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
- switch (resource) {
- case ResourceType::Priority:
- return max_priority;
- case ResourceType::Commit:
- return max_commit;
- case ResourceType::Thread:
- return max_threads;
- case ResourceType::Event:
- return max_events;
- case ResourceType::Mutex:
- return max_mutexes;
- case ResourceType::Semaphore:
- return max_semaphores;
- case ResourceType::Timer:
- return max_timers;
- case ResourceType::SharedMemory:
- return max_shared_mems;
- case ResourceType::AddressArbiter:
- return max_address_arbiters;
- case ResourceType::CPUTime:
- return max_cpu_time;
- default:
- LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
- UNIMPLEMENTED();
- return 0;
+ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) {
+ const auto index = ResourceTypeToIndex(resource);
+
+ if (value < values[index]) {
+ return ERR_INVALID_STATE;
}
+
+ values[index] = value;
+ return RESULT_SUCCESS;
}
} // namespace Kernel
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 @@
#pragma once
+#include <array>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
+union ResultCode;
+
namespace Kernel {
class KernelCore;
-enum class ResourceLimitCategory : u8 {
- APPLICATION = 0,
- SYS_APPLET = 1,
- LIB_APPLET = 2,
- OTHER = 3
-};
-
enum class ResourceType {
- Priority = 0,
- Commit = 1,
- Thread = 2,
- Event = 3,
- Mutex = 4,
- Semaphore = 5,
- Timer = 6,
- SharedMemory = 7,
- AddressArbiter = 8,
- CPUTime = 9,
+ PhysicalMemory,
+ Threads,
+ Events,
+ TransferMemory,
+ Sessions,
+
+ // Used as a count, not an actual type.
+ ResourceTypeCount
};
class ResourceLimit final : public Object {
@@ -55,61 +49,51 @@ public:
* @param resource Requested resource type
* @returns The current value of the resource type
*/
- s32 GetCurrentResourceValue(ResourceType resource) const;
+ s64 GetCurrentResourceValue(ResourceType resource) const;
/**
* Gets the max value for the specified resource.
* @param resource Requested resource type
* @returns The max value of the resource type
*/
- u32 GetMaxResourceValue(ResourceType resource) const;
-
- /// Name of resource limit object.
- std::string name;
-
- /// Max thread priority that a process in this category can create
- s32 max_priority = 0;
-
- /// Max memory that processes in this category can use
- s32 max_commit = 0;
+ s64 GetMaxResourceValue(ResourceType resource) const;
- ///< Max number of objects that can be collectively created by the processes in this category
- s32 max_threads = 0;
- s32 max_events = 0;
- s32 max_mutexes = 0;
- s32 max_semaphores = 0;
- s32 max_timers = 0;
- s32 max_shared_mems = 0;
- s32 max_address_arbiters = 0;
+ /**
+ * Sets the limit value for a given resource type.
+ *
+ * @param resource The resource type to apply the limit to.
+ * @param value The limit to apply to the given resource type.
+ *
+ * @return A result code indicating if setting the limit value
+ * was successful or not.
+ *
+ * @note The supplied limit value *must* be greater than or equal to
+ * the current resource value for the given resource type,
+ * otherwise ERR_INVALID_STATE will be returned.
+ */
+ ResultCode SetLimitValue(ResourceType resource, s64 value);
- /// Max CPU time that the processes in this category can utilize
- s32 max_cpu_time = 0;
+private:
+ explicit ResourceLimit(KernelCore& kernel);
+ ~ResourceLimit() override;
- // TODO(Subv): Increment these in their respective Kernel::T::Create functions, keeping in mind
- // that APPLICATION resource limits should not be affected by the objects created by service
- // modules.
+ // TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create
+ // functions
+ //
// Currently we have no way of distinguishing if a Create was called by the running application,
// or by a service module. Approach this once we have separated the service modules into their
// own processes
- /// Current memory that the processes in this category are using
- s32 current_commit = 0;
+ using ResourceArray =
+ std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>;
- ///< Current number of objects among all processes in this category
- s32 current_threads = 0;
- s32 current_events = 0;
- s32 current_mutexes = 0;
- s32 current_semaphores = 0;
- s32 current_timers = 0;
- s32 current_shared_mems = 0;
- s32 current_address_arbiters = 0;
+ /// Maximum values a resource type may reach.
+ ResourceArray limits{};
+ /// Current resource limit values.
+ ResourceArray values{};
- /// Current CPU time that the processes in this category are utilizing
- s32 current_cpu_time = 0;
-
-private:
- explicit ResourceLimit(KernelCore& kernel);
- ~ResourceLimit() override;
+ /// Name of resource limit object.
+ std::string name;
};
} // 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) {
const auto* const current_process = Core::CurrentProcess();
- // Note: The kernel uses the current process's resource limit instead of
- // the one from the thread owner's resource limit.
- const ResourceLimit& resource_limit = current_process->GetResourceLimit();
- if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
- return ERR_INVALID_THREAD_PRIORITY;
- }
-
SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
return ERR_INVALID_HANDLE;
@@ -885,10 +878,6 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
}
auto* const current_process = Core::CurrentProcess();
- const ResourceLimit& resource_limit = current_process->GetResourceLimit();
- if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
- return ERR_INVALID_THREAD_PRIORITY;
- }
if (processor_id == THREADPROCESSORID_DEFAULT) {
// Set the target CPU to the one specified in the process' exheader.
@@ -1181,7 +1170,7 @@ static ResultCode CloseHandle(Handle handle) {
/// Reset an event
static ResultCode ResetSignal(Handle handle) {
- LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
+ LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
auto event = handle_table.Get<Event>(handle);
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) {
return RESULT_SUCCESS;
}
-ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
+ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
const auto vma = FindVMA(src_addr);
ASSERT_MSG(vma != vma_map.end(), "Invalid memory address");
@@ -312,8 +312,8 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
const std::size_t backing_block_offset = vma->second.offset + vma_offset;
- CASCADE_RESULT(auto new_vma, MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
- MemoryState::Mapped));
+ CASCADE_RESULT(auto new_vma,
+ MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, state));
// Protect mirror with permissions from old region
Reprotect(new_vma, vma->second.permissions);
// 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:
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u64 size);
- ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
+ ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size,
+ MemoryState state = MemoryState::Mapped);
/**
* 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 @@
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
+#include "core/settings.h"
namespace Service::HID {
+constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
+constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
+enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
+
Controller_DebugPad::Controller_DebugPad() = default;
Controller_DebugPad::~Controller_DebugPad() = default;
@@ -33,10 +38,43 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
- // TODO(ogniK): Update debug pad states
+ cur_entry.attribute.connected.Assign(1);
+ auto& pad = cur_entry.pad_state;
+
+ using namespace Settings::NativeButton;
+ pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
+ pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
+ pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
+ pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
+ pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
+ pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
+ pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
+ pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
+ pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
+ pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
+ pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
+ pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
+ pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
+ pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
+
+ const auto [stick_l_x_f, stick_l_y_f] =
+ analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
+ const auto [stick_r_x_f, stick_r_y_f] =
+ analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
+ cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
+ cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
+ cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
+ cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
std::memcpy(data, &shared_memory, sizeof(SharedMemory));
}
-void Controller_DebugPad::OnLoadInputDevices() {}
+void Controller_DebugPad::OnLoadInputDevices() {
+ std::transform(Settings::values.debug_pad_buttons.begin(),
+ Settings::values.debug_pad_buttons.end(), buttons.begin(),
+ Input::CreateDevice<Input::ButtonDevice>);
+ std::transform(Settings::values.debug_pad_analogs.begin(),
+ Settings::values.debug_pad_analogs.end(), analogs.begin(),
+ Input::CreateDevice<Input::AnalogDevice>);
+}
} // namespace Service::HID
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 @@
#pragma once
#include <array>
+#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/settings.h"
namespace Service::HID {
class Controller_DebugPad final : public ControllerBase {
@@ -35,11 +38,40 @@ private:
};
static_assert(sizeof(AnalogStick) == 0x8);
+ struct PadState {
+ union {
+ u32_le raw{};
+ BitField<0, 1, u32_le> a;
+ BitField<1, 1, u32_le> b;
+ BitField<2, 1, u32_le> x;
+ BitField<3, 1, u32_le> y;
+ BitField<4, 1, u32_le> l;
+ BitField<5, 1, u32_le> r;
+ BitField<6, 1, u32_le> zl;
+ BitField<7, 1, u32_le> zr;
+ BitField<8, 1, u32_le> plus;
+ BitField<9, 1, u32_le> minus;
+ BitField<10, 1, u32_le> d_left;
+ BitField<11, 1, u32_le> d_up;
+ BitField<12, 1, u32_le> d_right;
+ BitField<13, 1, u32_le> d_down;
+ };
+ };
+ static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
+
+ struct Attributes {
+ union {
+ u32_le raw{};
+ BitField<0, 1, u32_le> connected;
+ };
+ };
+ static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
+
struct PadStates {
s64_le sampling_number;
s64_le sampling_number2;
- u32_le attribute;
- u32_le button_state;
+ Attributes attribute;
+ PadState pad_state;
AnalogStick r_stick;
AnalogStick l_stick;
};
@@ -52,5 +84,10 @@ private:
};
static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
SharedMemory shared_memory{};
+
+ std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
+ buttons;
+ std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
+ analogs;
};
} // namespace Service::HID
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 @@
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hle/service/hid/controllers/keyboard.h"
+#include "core/settings.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
+constexpr u8 KEYS_PER_BYTE = 8;
Controller_Keyboard::Controller_Keyboard() = default;
Controller_Keyboard::~Controller_Keyboard() = default;
@@ -34,10 +36,24 @@ void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
- // TODO(ogniK): Update keyboard states
+
+ for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
+ for (std::size_t k = 0; k < KEYS_PER_BYTE; ++k) {
+ cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << k);
+ }
+ }
+
+ for (std::size_t i = 0; i < keyboard_mods.size(); ++i) {
+ cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i);
+ }
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
-void Controller_Keyboard::OnLoadInputDevices() {}
+void Controller_Keyboard::OnLoadInputDevices() {
+ std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(),
+ keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
+ std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
+ keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
+}
} // namespace Service::HID
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 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/settings.h"
namespace Service::HID {
class Controller_Keyboard final : public ControllerBase {
@@ -46,5 +48,10 @@ private:
};
static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
SharedMemory shared_memory{};
+
+ std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys>
+ keyboard_keys;
+ std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
+ keyboard_mods;
};
} // namespace Service::HID
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 @@
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
+#include "core/frontend/emu_window.h"
#include "core/hle/service/hid/controllers/mouse.h"
namespace Service::HID {
@@ -14,7 +15,6 @@ Controller_Mouse::Controller_Mouse() = default;
Controller_Mouse::~Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}
-
void Controller_Mouse::OnRelease() {}
void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
@@ -34,10 +34,29 @@ void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
- // TODO(ogniK): Update mouse states
+
+ if (Settings::values.mouse_enabled) {
+ const auto [px, py, sx, sy] = mouse_device->GetStatus();
+ const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width);
+ const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height);
+ cur_entry.x = x;
+ cur_entry.y = y;
+ cur_entry.delta_x = x - last_entry.x;
+ cur_entry.delta_y = y - last_entry.y;
+ cur_entry.mouse_wheel_x = sx;
+ cur_entry.mouse_wheel_y = sy;
+
+ for (std::size_t i = 0; i < mouse_button_devices.size(); ++i) {
+ cur_entry.button |= (mouse_button_devices[i]->GetStatus() << i);
+ }
+ }
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
-void Controller_Mouse::OnLoadInputDevices() {}
+void Controller_Mouse::OnLoadInputDevices() {
+ mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
+ std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
+ mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
+}
} // namespace Service::HID
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 @@
#include <array>
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/settings.h"
namespace Service::HID {
class Controller_Mouse final : public ControllerBase {
@@ -35,7 +37,8 @@ private:
s32_le y;
s32_le delta_x;
s32_le delta_y;
- s32_le mouse_wheel;
+ s32_le mouse_wheel_x;
+ s32_le mouse_wheel_y;
s32_le button;
s32_le attribute;
};
@@ -46,5 +49,9 @@ private:
std::array<MouseState, 17> mouse_states;
};
SharedMemory shared_memory{};
+
+ std::unique_ptr<Input::MouseDevice> mouse_device;
+ std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
+ mouse_button_devices;
};
} // namespace Service::HID
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 @@
#include "core/settings.h"
namespace Service::HID {
-
-constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
-constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
-constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
-constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
-constexpr u32 NPAD_HANDHELD = 32;
-constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
constexpr u32 MAX_NPAD_ID = 7;
-constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER =
- Controller_NPad::NPadControllerType::JoyDual;
constexpr std::array<u32, 10> npad_id_list{
- 0, 1, 2, 3, 4, 5, 6, 7, 32, 16,
+ 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
};
enum class JoystickId : std::size_t {
@@ -40,7 +31,23 @@ enum class JoystickId : std::size_t {
Joystick_Right,
};
-static std::size_t NPadIdToIndex(u32 npad_id) {
+static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
+ switch (type) {
+ case Settings::ControllerType::ProController:
+ return Controller_NPad::NPadControllerType::ProController;
+ case Settings::ControllerType::DualJoycon:
+ return Controller_NPad::NPadControllerType::JoyDual;
+ case Settings::ControllerType::LeftJoycon:
+ return Controller_NPad::NPadControllerType::JoyLeft;
+ case Settings::ControllerType::RightJoycon:
+ return Controller_NPad::NPadControllerType::JoyRight;
+ default:
+ UNREACHABLE();
+ return Controller_NPad::NPadControllerType::JoyDual;
+ }
+}
+
+std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
switch (npad_id) {
case 0:
case 1:
@@ -63,6 +70,27 @@ static std::size_t NPadIdToIndex(u32 npad_id) {
}
}
+u32 Controller_NPad::IndexToNPad(std::size_t index) {
+ switch (index) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ return static_cast<u32>(index);
+ case 8:
+ return NPAD_HANDHELD;
+ case 9:
+ return NPAD_UNKNOWN;
+ default:
+ UNIMPLEMENTED_MSG("Unknown npad index {}", index);
+ return 0;
+ };
+}
+
Controller_NPad::Controller_NPad() = default;
Controller_NPad::~Controller_NPad() = default;
@@ -79,22 +107,32 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
+ controller.properties.is_vertical.Assign(1);
+ controller.properties.use_plus.Assign(1);
+ controller.properties.use_minus.Assign(1);
break;
case NPadControllerType::JoyDual:
controller.joy_styles.joycon_dual.Assign(1);
controller.device_type.joycon_left.Assign(1);
controller.device_type.joycon_right.Assign(1);
+ controller.properties.is_vertical.Assign(1);
+ controller.properties.use_plus.Assign(1);
+ controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
break;
case NPadControllerType::JoyLeft:
controller.joy_styles.joycon_left.Assign(1);
controller.device_type.joycon_left.Assign(1);
- controller.pad_assignment = NPadAssignments::Dual;
+ controller.properties.is_horizontal.Assign(1);
+ controller.properties.use_minus.Assign(1);
+ controller.pad_assignment = NPadAssignments::Single;
break;
case NPadControllerType::JoyRight:
controller.joy_styles.joycon_right.Assign(1);
controller.device_type.joycon_right.Assign(1);
- controller.pad_assignment = NPadAssignments::Dual;
+ controller.properties.is_horizontal.Assign(1);
+ controller.properties.use_plus.Assign(1);
+ controller.pad_assignment = NPadAssignments::Single;
break;
case NPadControllerType::Pokeball:
controller.joy_styles.pokeball.Assign(1);
@@ -104,6 +142,9 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
case NPadControllerType::ProController:
controller.joy_styles.pro_controller.Assign(1);
controller.device_type.pro_controller.Assign(1);
+ controller.properties.is_vertical.Assign(1);
+ controller.properties.use_plus.Assign(1);
+ controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
break;
}
@@ -113,14 +154,12 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.single_color.button_color = 0;
controller.dual_color_error = ColorReadError::ReadOk;
- controller.left_color.body_color = JOYCON_BODY_NEON_BLUE;
- controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE;
- controller.right_color.body_color = JOYCON_BODY_NEON_RED;
- controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED;
-
- controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations
- controller.properties.use_plus.Assign(1);
- controller.properties.use_minus.Assign(1);
+ controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left;
+ controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left;
+ controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right;
+ controller.right_color.button_color =
+ Settings::values.players[controller_idx].button_color_right;
+
controller.battery_level[0] = BATTERY_FULL;
controller.battery_level[1] = BATTERY_FULL;
controller.battery_level[2] = BATTERY_FULL;
@@ -144,26 +183,109 @@ void Controller_NPad::OnInit() {
style.pro_controller.Assign(1);
style.pokeball.Assign(1);
}
+
+ std::transform(
+ Settings::values.players.begin(), Settings::values.players.end(),
+ connected_controllers.begin(), [](const Settings::PlayerInput& player) {
+ return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected};
+ });
+
+ std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
+ [](const ControllerHolder& holder) { return holder.is_connected; });
+
+ // Account for handheld
+ if (connected_controllers[8].is_connected)
+ connected_controllers[8].type = NPadControllerType::Handheld;
+
+ supported_npad_id_types.resize(npad_id_list.size());
+ std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
+ npad_id_list.size() * sizeof(u32));
+
+ // Add a default dual joycon controller if none are present.
if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
[](const ControllerHolder& controller) { return controller.is_connected; })) {
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(u32));
- AddNewController(PREFERRED_CONTROLLER);
+ AddNewController(NPadControllerType::JoyDual);
+ }
+
+ for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
+ const auto& controller = connected_controllers[i];
+ if (controller.is_connected) {
+ AddNewControllerAt(controller.type, IndexToNPad(i));
+ }
}
}
void Controller_NPad::OnLoadInputDevices() {
- std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
- Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
- buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
- std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
- Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
- sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
+ const auto& players = Settings::values.players;
+ for (std::size_t i = 0; i < players.size(); ++i) {
+ std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
+ players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
+ buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>);
+ std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
+ players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
+ sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
+ }
}
void Controller_NPad::OnRelease() {}
+void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
+ const auto controller_idx = NPadIdToIndex(npad_id);
+ const auto controller_type = connected_controllers[controller_idx].type;
+ if (!connected_controllers[controller_idx].is_connected) {
+ return;
+ }
+ auto& pad_state = npad_pad_states[controller_idx].pad_states;
+ auto& lstick_entry = npad_pad_states[controller_idx].l_stick;
+ auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
+ const auto& button_state = buttons[controller_idx];
+ const auto& analog_state = sticks[controller_idx];
+
+ using namespace Settings::NativeButton;
+ pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
+
+ const auto [stick_l_x_f, stick_l_y_f] =
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
+ const auto [stick_r_x_f, stick_r_y_f] =
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
+ lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
+ lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
+ rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
+ rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
+}
+
void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
if (!IsControllerActivated())
return;
@@ -199,97 +321,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
continue;
}
-
- // Pad states
- ControllerPadState pad_state{};
- using namespace Settings::NativeButton;
- pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
-
- AnalogPosition lstick_entry{};
- AnalogPosition rstick_entry{};
-
- const auto [stick_l_x_f, stick_l_y_f] =
- sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
- const auto [stick_r_x_f, stick_r_y_f] =
- sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
- lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
- lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
- rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
- rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
-
- if (controller_type == NPadControllerType::JoyLeft ||
- controller_type == NPadControllerType::JoyRight) {
- if (npad.properties.is_horizontal) {
- ControllerPadState state{};
- AnalogPosition temp_lstick_entry{};
- AnalogPosition temp_rstick_entry{};
- if (controller_type == NPadControllerType::JoyLeft) {
- state.d_down.Assign(pad_state.d_left.Value());
- state.d_left.Assign(pad_state.d_up.Value());
- state.d_right.Assign(pad_state.d_down.Value());
- state.d_up.Assign(pad_state.d_right.Value());
- state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
- state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
-
- state.zl.Assign(pad_state.zl.Value());
- state.plus.Assign(pad_state.minus.Value());
-
- temp_lstick_entry = lstick_entry;
- temp_rstick_entry = rstick_entry;
- std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
- std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
- temp_lstick_entry.y *= -1;
- } else if (controller_type == NPadControllerType::JoyRight) {
- state.x.Assign(pad_state.a.Value());
- state.a.Assign(pad_state.b.Value());
- state.b.Assign(pad_state.y.Value());
- state.y.Assign(pad_state.b.Value());
-
- state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
- state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
- state.zr.Assign(pad_state.zr.Value());
- state.plus.Assign(pad_state.plus.Value());
-
- temp_lstick_entry = lstick_entry;
- temp_rstick_entry = rstick_entry;
- std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
- std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
- temp_rstick_entry.x *= -1;
- }
- pad_state.raw = state.raw;
- lstick_entry = temp_lstick_entry;
- rstick_entry = temp_rstick_entry;
- }
- }
+ const u32 npad_index = static_cast<u32>(i);
+ RequestPadStateUpdate(npad_index);
+ auto& pad_state = npad_pad_states[npad_index];
auto& main_controller =
npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
@@ -304,8 +338,51 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
if (hold_type == NpadHoldType::Horizontal) {
- // TODO(ogniK): Remap buttons for different orientations
+ ControllerPadState state{};
+ AnalogPosition temp_lstick_entry{};
+ AnalogPosition temp_rstick_entry{};
+ if (controller_type == NPadControllerType::JoyLeft) {
+ state.d_down.Assign(pad_state.pad_states.d_left.Value());
+ state.d_left.Assign(pad_state.pad_states.d_up.Value());
+ state.d_right.Assign(pad_state.pad_states.d_down.Value());
+ state.d_up.Assign(pad_state.pad_states.d_right.Value());
+ state.l.Assign(pad_state.pad_states.l.Value() |
+ pad_state.pad_states.left_sl.Value());
+ state.r.Assign(pad_state.pad_states.r.Value() |
+ pad_state.pad_states.left_sr.Value());
+
+ state.zl.Assign(pad_state.pad_states.zl.Value());
+ state.plus.Assign(pad_state.pad_states.minus.Value());
+
+ temp_lstick_entry = pad_state.l_stick;
+ temp_rstick_entry = pad_state.r_stick;
+ std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
+ std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
+ temp_lstick_entry.y *= -1;
+ } else if (controller_type == NPadControllerType::JoyRight) {
+ state.x.Assign(pad_state.pad_states.a.Value());
+ state.a.Assign(pad_state.pad_states.b.Value());
+ state.b.Assign(pad_state.pad_states.y.Value());
+ state.y.Assign(pad_state.pad_states.b.Value());
+
+ state.l.Assign(pad_state.pad_states.l.Value() |
+ pad_state.pad_states.right_sl.Value());
+ state.r.Assign(pad_state.pad_states.r.Value() |
+ pad_state.pad_states.right_sr.Value());
+ state.zr.Assign(pad_state.pad_states.zr.Value());
+ state.plus.Assign(pad_state.pad_states.plus.Value());
+
+ temp_lstick_entry = pad_state.l_stick;
+ temp_rstick_entry = pad_state.r_stick;
+ std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
+ std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
+ temp_rstick_entry.x *= -1;
+ }
+ pad_state.pad_states.raw = state.raw;
+ pad_state.l_stick = temp_lstick_entry;
+ pad_state.r_stick = temp_rstick_entry;
}
+
libnx_entry.connection_status.raw = 0;
switch (controller_type) {
@@ -316,9 +393,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
handheld_entry.connection_status.IsRightJoyWired.Assign(1);
- handheld_entry.pad_states.raw = pad_state.raw;
- handheld_entry.l_stick = lstick_entry;
- handheld_entry.r_stick = rstick_entry;
+ handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+ handheld_entry.pad.l_stick = pad_state.l_stick;
+ handheld_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::JoyDual:
dual_entry.connection_status.raw = 0;
@@ -331,25 +408,25 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
libnx_entry.connection_status.IsConnected.Assign(1);
- dual_entry.pad_states.raw = pad_state.raw;
- dual_entry.l_stick = lstick_entry;
- dual_entry.r_stick = rstick_entry;
+ dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+ dual_entry.pad.l_stick = pad_state.l_stick;
+ dual_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::JoyLeft:
left_entry.connection_status.raw = 0;
left_entry.connection_status.IsConnected.Assign(1);
- left_entry.pad_states.raw = pad_state.raw;
- left_entry.l_stick = lstick_entry;
- left_entry.r_stick = rstick_entry;
+ left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+ left_entry.pad.l_stick = pad_state.l_stick;
+ left_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::JoyRight:
right_entry.connection_status.raw = 0;
right_entry.connection_status.IsConnected.Assign(1);
- right_entry.pad_states.raw = pad_state.raw;
- right_entry.l_stick = lstick_entry;
- right_entry.r_stick = rstick_entry;
+ right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+ right_entry.pad.l_stick = pad_state.l_stick;
+ right_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::Pokeball:
pokeball_entry.connection_status.raw = 0;
@@ -357,30 +434,30 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
pokeball_entry.connection_status.IsConnected.Assign(1);
pokeball_entry.connection_status.IsWired.Assign(1);
- pokeball_entry.pad_states.raw = pad_state.raw;
- pokeball_entry.l_stick = lstick_entry;
- pokeball_entry.r_stick = rstick_entry;
+ pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+ pokeball_entry.pad.l_stick = pad_state.l_stick;
+ pokeball_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::ProController:
main_controller.connection_status.raw = 0;
main_controller.connection_status.IsConnected.Assign(1);
main_controller.connection_status.IsWired.Assign(1);
- main_controller.pad_states.raw = pad_state.raw;
- main_controller.l_stick = lstick_entry;
- main_controller.r_stick = rstick_entry;
+ main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
+ main_controller.pad.l_stick = pad_state.l_stick;
+ main_controller.pad.r_stick = pad_state.r_stick;
break;
}
// LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
// any controllers.
- libnx_entry.pad_states.raw = pad_state.raw;
- libnx_entry.l_stick = lstick_entry;
- libnx_entry.r_stick = rstick_entry;
+ libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+ libnx_entry.pad.l_stick = pad_state.l_stick;
+ libnx_entry.pad.r_stick = pad_state.r_stick;
}
std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
shared_memory_entries.size() * sizeof(NPadEntry));
-} // namespace Service::HID
+}
void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
style.raw = style_set.raw;
@@ -401,23 +478,24 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
if (!controller.is_connected) {
continue;
}
- if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
- const auto best_type = DecideBestController(PREFERRED_CONTROLLER);
- const bool is_handheld = (best_type == NPadControllerType::Handheld ||
- PREFERRED_CONTROLLER == NPadControllerType::Handheld);
+ const auto requested_controller =
+ i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
+ : NPadControllerType::Handheld;
+ if (!IsControllerSupported(requested_controller)) {
+ const auto is_handheld = requested_controller == NPadControllerType::Handheld;
if (is_handheld) {
controller.type = NPadControllerType::None;
controller.is_connected = false;
- AddNewController(best_type);
+ AddNewController(requested_controller);
} else {
- controller.type = best_type;
+ controller.type = requested_controller;
InitNewlyAddedControler(i);
}
had_controller_update = true;
}
- }
- if (had_controller_update) {
- styleset_changed_event->Signal();
+ if (had_controller_update) {
+ styleset_changed_event->Signal();
+ }
}
}
@@ -450,15 +528,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
return;
}
for (std::size_t i = 0; i < controller_ids.size(); i++) {
- std::size_t controller_pos = i;
- // Handheld controller conversion
- if (controller_pos == NPAD_HANDHELD) {
- controller_pos = 8;
- }
- // Unknown controller conversion
- if (controller_pos == NPAD_UNKNOWN) {
- controller_pos = 9;
- }
+ std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i));
if (connected_controllers[controller_pos].is_connected) {
// TODO(ogniK): Vibrate the physical controller
}
@@ -477,7 +547,9 @@ Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() cons
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
return last_processed_vibration;
}
+
void Controller_NPad::AddNewController(NPadControllerType controller) {
+ controller = DecideBestController(controller);
if (controller == NPadControllerType::Handheld) {
connected_controllers[8] = {controller, true};
InitNewlyAddedControler(8);
@@ -495,6 +567,18 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
InitNewlyAddedControler(controller_id);
}
+void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
+ controller = DecideBestController(controller);
+ if (controller == NPadControllerType::Handheld) {
+ connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
+ InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
+ return;
+ }
+
+ connected_controllers[npad_id] = {controller, true};
+ InitNewlyAddedControler(npad_id);
+}
+
void Controller_NPad::ConnectNPad(u32 npad_id) {
connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
}
@@ -503,6 +587,36 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) {
connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
}
+bool Controller_NPad::IsControllerSupported(NPadControllerType controller) {
+ if (controller == NPadControllerType::Handheld) {
+ // Handheld is not even a supported type, lets stop here
+ if (std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
+ NPAD_HANDHELD) == supported_npad_id_types.end()) {
+ return false;
+ }
+ // Handheld should not be supported in docked mode
+ if (Settings::values.use_docked_mode) {
+ return false;
+ }
+ }
+ switch (controller) {
+ case NPadControllerType::ProController:
+ return style.pro_controller;
+ case NPadControllerType::Handheld:
+ return style.handheld;
+ case NPadControllerType::JoyDual:
+ return style.joycon_dual;
+ case NPadControllerType::JoyLeft:
+ return style.joycon_left;
+ case NPadControllerType::JoyRight:
+ return style.joycon_right;
+ case NPadControllerType::Pokeball:
+ return style.pokeball;
+ default:
+ return false;
+ }
+}
+
Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
// These are controllers without led patterns
@@ -534,6 +648,36 @@ void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
can_controllers_vibrate = can_vibrate;
}
+void Controller_NPad::ClearAllConnectedControllers() {
+ for (auto& controller : connected_controllers) {
+ if (controller.is_connected && controller.type != NPadControllerType::None) {
+ controller.type = NPadControllerType::None;
+ controller.is_connected = false;
+ }
+ }
+}
+void Controller_NPad::DisconnectAllConnectedControllers() {
+ std::for_each(connected_controllers.begin(), connected_controllers.end(),
+ [](ControllerHolder& controller) { controller.is_connected = false; });
+}
+
+void Controller_NPad::ConnectAllDisconnectedControllers() {
+ std::for_each(connected_controllers.begin(), connected_controllers.end(),
+ [](ControllerHolder& controller) {
+ if (controller.type != NPadControllerType::None && !controller.is_connected) {
+ controller.is_connected = false;
+ }
+ });
+}
+
+void Controller_NPad::ClearAllControllers() {
+ std::for_each(connected_controllers.begin(), connected_controllers.end(),
+ [](ControllerHolder& controller) {
+ controller.type = NPadControllerType::None;
+ controller.is_connected = false;
+ });
+}
+
bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
const bool support_handheld =
std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
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 @@
#pragma once
#include <array>
+#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/frontend/input.h"
+#include "core/hle/kernel/event.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/settings.h"
namespace Service::HID {
+constexpr u32 NPAD_HANDHELD = 32;
+constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
+
class Controller_NPad final : public ControllerBase {
public:
Controller_NPad();
@@ -107,11 +112,19 @@ public:
Vibration GetLastVibration() const;
void AddNewController(NPadControllerType controller);
+ void AddNewControllerAt(NPadControllerType controller, u32 npad_id);
void ConnectNPad(u32 npad_id);
void DisconnectNPad(u32 npad_id);
LedPattern GetLedPattern(u32 npad_id);
void SetVibrationEnabled(bool can_vibrate);
+ void ClearAllConnectedControllers();
+ void DisconnectAllConnectedControllers();
+ void ConnectAllDisconnectedControllers();
+ void ClearAllControllers();
+
+ static std::size_t NPadIdToIndex(u32 npad_id);
+ static u32 IndexToNPad(std::size_t index);
private:
struct CommonHeader {
@@ -164,8 +177,11 @@ private:
BitField<23, 1, u64_le> r_stick_down;
// Not always active?
- BitField<24, 1, u64_le> sl;
- BitField<25, 1, u64_le> sr;
+ BitField<24, 1, u64_le> left_sl;
+ BitField<25, 1, u64_le> left_sr;
+
+ BitField<26, 1, u64_le> right_sl;
+ BitField<27, 1, u64_le> right_sr;
};
};
static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
@@ -189,12 +205,17 @@ private:
};
static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
- struct GenericStates {
- s64_le timestamp;
- s64_le timestamp2;
+ struct ControllerPad {
ControllerPadState pad_states;
AnalogPosition l_stick;
AnalogPosition r_stick;
+ };
+ static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size");
+
+ struct GenericStates {
+ s64_le timestamp;
+ s64_le timestamp2;
+ ControllerPad pad;
ConnectionState connection_status;
};
static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
@@ -266,15 +287,20 @@ private:
static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
struct ControllerHolder {
- Controller_NPad::NPadControllerType type;
+ NPadControllerType type;
bool is_connected;
};
NPadType style{};
std::array<NPadEntry, 10> shared_memory_entries{};
- std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
+ std::array<
+ std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
+ 10>
buttons;
- std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
+ std::array<
+ std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
+ 10>
+ sticks;
std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical};
Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
@@ -285,5 +311,8 @@ private:
void InitNewlyAddedControler(std::size_t controller_idx);
bool IsControllerSupported(NPadControllerType controller) const;
NPadControllerType DecideBestController(NPadControllerType priority) const;
+ void RequestPadStateUpdate(u32 npad_id);
+ std::array<ControllerPad, 10> npad_pad_states{};
+ bool IsControllerSupported(NPadControllerType controller);
};
} // namespace Service::HID
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) {
const auto [x, y, pressed] = touch_device->GetStatus();
auto& touch_entry = cur_entry.states[0];
- if (pressed) {
+ touch_entry.attribute.raw = 0;
+ if (pressed && Settings::values.touchscreen.enabled) {
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
- touch_entry.diameter_x = 15;
- touch_entry.diameter_y = 15;
- touch_entry.rotation_angle = 0;
+ touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
+ touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
+ touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
const u64 tick = CoreTiming::GetTicks();
touch_entry.delta_time = tick - last_touch;
last_touch = tick;
- touch_entry.finger = 0;
+ touch_entry.finger = Settings::values.touchscreen.finger;
cur_entry.entry_count = 1;
} else {
cur_entry.entry_count = 0;
@@ -60,6 +61,6 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
}
void Controller_Touchscreen::OnLoadInputDevices() {
- touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
+ touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
}
} // namespace Service::HID
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 @@
#pragma once
+#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -29,9 +30,18 @@ public:
void OnLoadInputDevices() override;
private:
+ struct Attributes {
+ union {
+ u32 raw{};
+ BitField<0, 1, u32_le> start_touch;
+ BitField<1, 1, u32_le> end_touch;
+ };
+ };
+ static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
+
struct TouchState {
u64_le delta_time;
- u32_le attribute;
+ Attributes attribute;
u32_le finger;
u32_le x;
u32_le y;
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 @@
namespace Service::HID {
// Updating period for each HID device.
-// TODO(shinyquagsire23): These need better values.
-constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
+// TODO(ogniK): Find actual polling rate of hid
+constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66;
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
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 @@
#include <memory>
#include <fmt/format.h>
+#include <mbedtls/sha256.h>
+#include "common/alignment.h"
+#include "common/hex_util.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/ldr/ldr.h"
@@ -13,6 +16,38 @@
namespace Service::LDR {
+namespace ErrCodes {
+enum {
+ InvalidMemoryState = 51,
+ InvalidNRO = 52,
+ InvalidNRR = 53,
+ MissingNRRHash = 54,
+ MaximumNRO = 55,
+ MaximumNRR = 56,
+ AlreadyLoaded = 57,
+ InvalidAlignment = 81,
+ InvalidSize = 82,
+ InvalidNROAddress = 84,
+ InvalidNRRAddress = 85,
+ NotInitialized = 87,
+};
+}
+
+constexpr ResultCode ERROR_INVALID_MEMORY_STATE(ErrorModule::Loader, ErrCodes::InvalidMemoryState);
+constexpr ResultCode ERROR_INVALID_NRO(ErrorModule::Loader, ErrCodes::InvalidNRO);
+constexpr ResultCode ERROR_INVALID_NRR(ErrorModule::Loader, ErrCodes::InvalidNRR);
+constexpr ResultCode ERROR_MISSING_NRR_HASH(ErrorModule::Loader, ErrCodes::MissingNRRHash);
+constexpr ResultCode ERROR_MAXIMUM_NRO(ErrorModule::Loader, ErrCodes::MaximumNRO);
+constexpr ResultCode ERROR_MAXIMUM_NRR(ErrorModule::Loader, ErrCodes::MaximumNRR);
+constexpr ResultCode ERROR_ALREADY_LOADED(ErrorModule::Loader, ErrCodes::AlreadyLoaded);
+constexpr ResultCode ERROR_INVALID_ALIGNMENT(ErrorModule::Loader, ErrCodes::InvalidAlignment);
+constexpr ResultCode ERROR_INVALID_SIZE(ErrorModule::Loader, ErrCodes::InvalidSize);
+constexpr ResultCode ERROR_INVALID_NRO_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNROAddress);
+constexpr ResultCode ERROR_INVALID_NRR_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNRRAddress);
+constexpr ResultCode ERROR_NOT_INITIALIZED(ErrorModule::Loader, ErrCodes::NotInitialized);
+
+constexpr u64 MAXIMUM_LOADED_RO = 0x40;
+
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
public:
explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} {
@@ -64,9 +99,9 @@ public:
// clang-format off
static const FunctionInfo functions[] = {
{0, &RelocatableObject::LoadNro, "LoadNro"},
- {1, nullptr, "UnloadNro"},
+ {1, &RelocatableObject::UnloadNro, "UnloadNro"},
{2, &RelocatableObject::LoadNrr, "LoadNrr"},
- {3, nullptr, "UnloadNrr"},
+ {3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
{4, &RelocatableObject::Initialize, "Initialize"},
};
// clang-format on
@@ -75,9 +110,123 @@ public:
}
void LoadNrr(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ rp.Skip(2, false);
+ const VAddr nrr_addr{rp.Pop<VAddr>()};
+ const u64 nrr_size{rp.Pop<u64>()};
+
+ if (!initialized) {
+ LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NOT_INITIALIZED);
+ return;
+ }
+
+ if (nrr.size() >= MAXIMUM_LOADED_RO) {
+ LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs "
+ "(0x40)! Failing...");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_MAXIMUM_NRR);
+ return;
+ }
+
+ // NRR Address does not fall on 0x1000 byte boundary
+ if (!Common::Is4KBAligned(nrr_addr)) {
+ LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ALIGNMENT);
+ return;
+ }
+
+ // NRR Size is zero or causes overflow
+ if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) {
+ LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
+ nrr_addr, nrr_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_SIZE);
+ return;
+ }
+ // Read NRR data from memory
+ std::vector<u8> nrr_data(nrr_size);
+ Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size);
+ NRRHeader header;
+ std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
+
+ if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) {
+ LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_NRR);
+ return;
+ }
+
+ if (header.size != nrr_size) {
+ LOG_ERROR(Service_LDR,
+ "NRR header reported size did not match LoadNrr parameter size! "
+ "(header_size={:016X}, loadnrr_size={:016X})",
+ header.size, nrr_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_SIZE);
+ return;
+ }
+
+ if (Core::CurrentProcess()->GetTitleID() != header.title_id) {
+ LOG_ERROR(Service_LDR,
+ "Attempting to load NRR with title ID other than current process. (actual "
+ "{:016X})!",
+ header.title_id);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_NRR);
+ return;
+ }
+
+ std::vector<SHA256Hash> hashes;
+
+ // Copy all hashes in the NRR (specified by hash count/hash offset) into vector.
+ for (std::size_t i = header.hash_offset;
+ i < (header.hash_offset + (header.hash_count * sizeof(SHA256Hash))); i += 8) {
+ SHA256Hash hash;
+ std::memcpy(hash.data(), nrr_data.data() + i, sizeof(SHA256Hash));
+ hashes.emplace_back(hash);
+ }
+
+ nrr.insert_or_assign(nrr_addr, std::move(hashes));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void UnloadNrr(Kernel::HLERequestContext& ctx) {
+ if (!initialized) {
+ LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NOT_INITIALIZED);
+ return;
+ }
+
+ IPC::RequestParser rp{ctx};
+ rp.Skip(2, false);
+ const auto nrr_addr{rp.Pop<VAddr>()};
+
+ if (!Common::Is4KBAligned(nrr_addr)) {
+ LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ALIGNMENT);
+ return;
+ }
+
+ const auto iter = nrr.find(nrr_addr);
+ if (iter == nrr.end()) {
+ LOG_ERROR(Service_LDR,
+ "Attempting to unload NRR which has not been loaded! (addr={:016X})",
+ nrr_addr);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_NRR_ADDRESS);
+ return;
+ }
+
+ nrr.erase(iter);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_LDR, "(STUBBED) called");
}
void LoadNro(Kernel::HLERequestContext& ctx) {
@@ -88,33 +237,253 @@ public:
const VAddr bss_addr{rp.Pop<VAddr>()};
const u64 bss_size{rp.Pop<u64>()};
+ if (!initialized) {
+ LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NOT_INITIALIZED);
+ return;
+ }
+
+ if (nro.size() >= MAXIMUM_LOADED_RO) {
+ LOG_ERROR(Service_LDR, "Loading new NRO would exceed the maximum number of loaded NROs "
+ "(0x40)! Failing...");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_MAXIMUM_NRO);
+ return;
+ }
+
+ // NRO Address does not fall on 0x1000 byte boundary
+ if (!Common::Is4KBAligned(nro_addr)) {
+ LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ALIGNMENT);
+ return;
+ }
+
+ // NRO Size or BSS Size is zero or causes overflow
+ const auto nro_size_valid =
+ nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size);
+ const auto bss_size_valid =
+ nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr);
+
+ if (!nro_size_valid || !bss_size_valid) {
+ LOG_ERROR(Service_LDR,
+ "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
+ "bss_address={:016X}, bss_size={:016X})",
+ nro_addr, nro_size, bss_addr, bss_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_SIZE);
+ return;
+ }
+
// Read NRO data from memory
std::vector<u8> nro_data(nro_size);
Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
+ SHA256Hash hash{};
+ mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0);
+
+ // NRO Hash is already loaded
+ if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) {
+ return info.second.hash == hash;
+ })) {
+ LOG_ERROR(Service_LDR, "NRO is already loaded!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_ALREADY_LOADED);
+ return;
+ }
+
+ // NRO Hash is not in any loaded NRR
+ if (!IsValidNROHash(hash)) {
+ LOG_ERROR(Service_LDR,
+ "NRO hash is not present in any currently loaded NRRs (hash={})!",
+ Common::HexArrayToString(hash));
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_MISSING_NRR_HASH);
+ return;
+ }
+
+ NROHeader header;
+ std::memcpy(&header, nro_data.data(), sizeof(NROHeader));
+
+ if (!IsValidNRO(header, nro_size, bss_size)) {
+ LOG_ERROR(Service_LDR, "NRO was invalid!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_NRO);
+ return;
+ }
+
// Load NRO as new executable module
- const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)};
- Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr);
+ auto* process = Core::CurrentProcess();
+ auto& vm_manager = process->VMManager();
+ auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
+
+ if (!map_address.Succeeded() ||
+ *map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) {
+
+ LOG_ERROR(Service_LDR,
+ "General error while allocation memory or no available memory to allocate!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_MEMORY_STATE);
+ return;
+ }
+
+ ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size,
+ Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
+ ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS);
+
+ if (bss_size > 0) {
+ ASSERT(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
+ Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
+ ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS);
+ }
+
+ vm_manager.ReprotectRange(*map_address, header.text_size,
+ Kernel::VMAPermission::ReadExecute);
+ vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size,
+ Kernel::VMAPermission::Read);
+ vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
+ Kernel::VMAPermission::ReadWrite);
- // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party.
- // It is currently missing:
- // - Signature checks with LoadNRR
- // - Checking if a module has already been loaded
- // - Using/validating BSS, etc. params (these are used from NRO header instead)
- // - Error checking
- // - ...Probably other things
+ Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
+ Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
+ Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
+ Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
+
+ nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size});
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push(addr);
- LOG_WARNING(Service_LDR, "(STUBBED) called");
+ rb.Push(*map_address);
+ }
+
+ void UnloadNro(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ rp.Skip(2, false);
+ const VAddr mapped_addr{rp.PopRaw<VAddr>()};
+ const VAddr heap_addr{rp.PopRaw<VAddr>()};
+
+ if (!initialized) {
+ LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NOT_INITIALIZED);
+ return;
+ }
+
+ if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) {
+ LOG_ERROR(Service_LDR,
+ "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, "
+ "bss_addr={:016X})!",
+ mapped_addr, heap_addr);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ALIGNMENT);
+ return;
+ }
+
+ const auto iter = nro.find(mapped_addr);
+ if (iter == nro.end()) {
+ LOG_ERROR(Service_LDR,
+ "The NRO attempting to unmap was not mapped or has an invalid address "
+ "(actual {:016X})!",
+ mapped_addr);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_NRO_ADDRESS);
+ return;
+ }
+
+ auto* process = Core::CurrentProcess();
+ auto& vm_manager = process->VMManager();
+ const auto& nro_size = iter->second.size;
+
+ ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size,
+ Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
+ ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS);
+
+ Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
+ Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
+ Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
+ Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
+
+ nro.erase(iter);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
}
void Initialize(Kernel::HLERequestContext& ctx) {
+ initialized = true;
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_LDR, "(STUBBED) called");
}
+
+private:
+ using SHA256Hash = std::array<u8, 0x20>;
+
+ struct NROHeader {
+ u32_le entrypoint_insn;
+ u32_le mod_offset;
+ INSERT_PADDING_WORDS(2);
+ u32_le magic;
+ INSERT_PADDING_WORDS(1);
+ u32_le nro_size;
+ INSERT_PADDING_WORDS(1);
+ u32_le text_offset;
+ u32_le text_size;
+ u32_le ro_offset;
+ u32_le ro_size;
+ u32_le rw_offset;
+ u32_le rw_size;
+ u32_le bss_size;
+ INSERT_PADDING_WORDS(1);
+ std::array<u8, 0x20> build_id;
+ INSERT_PADDING_BYTES(0x20);
+ };
+ static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size.");
+
+ struct NRRHeader {
+ u32_le magic;
+ INSERT_PADDING_BYTES(0x1C);
+ u64_le title_id_mask;
+ u64_le title_id_pattern;
+ std::array<u8, 0x100> modulus;
+ std::array<u8, 0x100> signature_1;
+ std::array<u8, 0x100> signature_2;
+ u64_le title_id;
+ u32_le size;
+ INSERT_PADDING_BYTES(4);
+ u32_le hash_offset;
+ u32_le hash_count;
+ INSERT_PADDING_BYTES(8);
+ };
+ static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size.");
+
+ struct NROInfo {
+ SHA256Hash hash;
+ u64 size;
+ };
+
+ bool initialized = false;
+
+ std::map<VAddr, NROInfo> nro;
+ std::map<VAddr, std::vector<SHA256Hash>> nrr;
+
+ bool IsValidNROHash(const SHA256Hash& hash) {
+ return std::any_of(
+ nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) {
+ return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
+ });
+ }
+
+ static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
+ return header.magic == Common::MakeMagic('N', 'R', 'O', '0') &&
+ header.nro_size == nro_size && header.bss_size == bss_size &&
+ header.ro_offset == header.text_offset + header.text_size &&
+ header.rw_offset == header.ro_offset + header.ro_size &&
+ nro_size == header.rw_offset + header.rw_size &&
+ Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) &&
+ Common::Is4KBAligned(header.rw_size);
+ }
};
void InstallInterfaces(SM::ServiceManager& sm) {
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index c89157a4d..4e5fdb16e 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -18,7 +18,7 @@ public:
ILogger() : ServiceFramework("ILogger") {
static const FunctionInfo functions[] = {
{0x00000000, &ILogger::Initialize, "Initialize"},
- {0x00000001, nullptr, "SetDestination"},
+ {0x00000001, &ILogger::SetDestination, "SetDestination"},
};
RegisterHandlers(functions);
}
@@ -178,6 +178,17 @@ private:
}
}
+ // This service function is intended to be used as a way to
+ // redirect logging output to different destinations, however,
+ // given we always want to see the logging output, it's sufficient
+ // to do nothing and return success here.
+ void SetDestination(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
std::ostringstream log_stream;
};
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) {
font_sizes.push_back(region.size);
}
+ // Resize buffers if game requests smaller size output.
+ font_codes.resize(
+ std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32)));
+ font_offsets.resize(
+ std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32)));
+ font_sizes.resize(
+ std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32)));
+
ctx.WriteBuffer(font_codes, 0);
ctx.WriteBuffer(font_offsets, 1);
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)
{300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
{400, &Time::GetClockSnapshot, "GetClockSnapshot"},
{401, nullptr, "GetClockSnapshotFromSystemClockContext"},
- {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"},
+ {500, &Time::CalculateStandardUserSystemClockDifferenceByUser,
+ "CalculateStandardUserSystemClockDifferenceByUser"},
{501, nullptr, "CalculateSpanBetween"},
};
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) {
ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
}
+void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
+ Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
+
+ IPC::RequestParser rp{ctx};
+ const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
+ const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
+ const u64 difference =
+ snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u64>(difference);
+}
+
Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
: ServiceFramework(name), time(std::move(time)) {}
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:
void GetTimeZoneService(Kernel::HLERequestContext& ctx);
void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
void GetClockSnapshot(Kernel::HLERequestContext& ctx);
+ void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> time;
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:
Data data{};
};
+/// Represents a parcel containing one int '0' as its data
+/// Used by DetachBuffer and Disconnect
+class IGBPEmptyResponseParcel : public Parcel {
+protected:
+ void SerializeData() override {
+ Write(data);
+ }
+
+private:
+ struct Data {
+ u32_le unk_0;
+ };
+
+ Data data{};
+};
+
class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
public:
explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer)
@@ -554,6 +570,12 @@ private:
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::CancelBuffer) {
LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
+ } else if (transaction == TransactionId::Disconnect ||
+ transaction == TransactionId::DetachBuffer) {
+ const auto buffer = ctx.ReadBuffer();
+
+ IGBPEmptyResponseParcel response{};
+ ctx.WriteBuffer(response.Serialize());
} else {
ASSERT_MSG(false, "Unimplemented");
}
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) {
arg_data.size());
}
- // Read MOD header
- ModHeader mod_header{};
// Default .bss to NRO header bss size if MOD0 section doesn't exist
u32 bss_size{PageAlignSize(nro_header.bss_size)};
+
+ // Read MOD header
+ ModHeader mod_header{};
std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset,
sizeof(ModHeader));
+
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
if (has_mod_header) {
// Resize program image to include .bss section and page align each section
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
}
+
codeset.DataSegment().size += bss_size;
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
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 @@
namespace Settings {
+namespace NativeButton {
+const std::array<const char*, NumButtons> mapping = {{
+ "button_a",
+ "button_b",
+ "button_x",
+ "button_y",
+ "button_lstick",
+ "button_rstick",
+ "button_l",
+ "button_r",
+ "button_zl",
+ "button_zr",
+ "button_plus",
+ "button_minus",
+ "button_dleft",
+ "button_dup",
+ "button_dright",
+ "button_ddown",
+ "button_lstick_left",
+ "button_lstick_up",
+ "button_lstick_right",
+ "button_lstick_down",
+ "button_rstick_left",
+ "button_rstick_up",
+ "button_rstick_right",
+ "button_rstick_down",
+ "button_sl",
+ "button_sr",
+ "button_home",
+ "button_screenshot",
+}};
+}
+
+namespace NativeAnalog {
+const std::array<const char*, NumAnalogs> mapping = {{
+ "lstick",
+ "rstick",
+}};
+}
+
+namespace NativeMouseButton {
+const std::array<const char*, NumMouseButtons> mapping = {{
+ "left",
+ "right",
+ "middle",
+ "forward",
+ "back",
+}};
+}
+
Values values = {};
void Apply() {
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;
constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
-static const std::array<const char*, NumButtons> mapping = {{
- "button_a",
- "button_b",
- "button_x",
- "button_y",
- "button_lstick",
- "button_rstick",
- "button_l",
- "button_r",
- "button_zl",
- "button_zr",
- "button_plus",
- "button_minus",
- "button_dleft",
- "button_dup",
- "button_dright",
- "button_ddown",
- "button_lstick_left",
- "button_lstick_up",
- "button_lstick_right",
- "button_lstick_down",
- "button_rstick_left",
- "button_rstick_up",
- "button_rstick_right",
- "button_rstick_down",
- "button_sl",
- "button_sr",
- "button_home",
- "button_screenshot",
-}};
+extern const std::array<const char*, NumButtons> mapping;
} // namespace NativeButton
@@ -105,12 +76,273 @@ constexpr int STICK_HID_BEGIN = LStick;
constexpr int STICK_HID_END = NumAnalogs;
constexpr int NUM_STICKS_HID = NumAnalogs;
-static const std::array<const char*, NumAnalogs> mapping = {{
- "lstick",
- "rstick",
-}};
+extern const std::array<const char*, NumAnalogs> mapping;
} // namespace NativeAnalog
+namespace NativeMouseButton {
+enum Values {
+ Left,
+ Right,
+ Middle,
+ Forward,
+ Back,
+
+ NumMouseButtons,
+};
+
+constexpr int MOUSE_HID_BEGIN = Left;
+constexpr int MOUSE_HID_END = NumMouseButtons;
+constexpr int NUM_MOUSE_HID = NumMouseButtons;
+
+extern const std::array<const char*, NumMouseButtons> mapping;
+} // namespace NativeMouseButton
+
+namespace NativeKeyboard {
+enum Keys {
+ None,
+ Error,
+
+ A = 4,
+ B,
+ C,
+ D,
+ E,
+ F,
+ G,
+ H,
+ I,
+ J,
+ K,
+ L,
+ M,
+ N,
+ O,
+ P,
+ Q,
+ R,
+ S,
+ T,
+ U,
+ V,
+ W,
+ X,
+ Y,
+ Z,
+ N1,
+ N2,
+ N3,
+ N4,
+ N5,
+ N6,
+ N7,
+ N8,
+ N9,
+ N0,
+ Enter,
+ Escape,
+ Backspace,
+ Tab,
+ Space,
+ Minus,
+ Equal,
+ LeftBrace,
+ RightBrace,
+ Backslash,
+ Tilde,
+ Semicolon,
+ Apostrophe,
+ Grave,
+ Comma,
+ Dot,
+ Slash,
+ CapsLockKey,
+
+ F1,
+ F2,
+ F3,
+ F4,
+ F5,
+ F6,
+ F7,
+ F8,
+ F9,
+ F10,
+ F11,
+ F12,
+
+ SystemRequest,
+ ScrollLockKey,
+ Pause,
+ Insert,
+ Home,
+ PageUp,
+ Delete,
+ End,
+ PageDown,
+ Right,
+ Left,
+ Down,
+ Up,
+
+ NumLockKey,
+ KPSlash,
+ KPAsterisk,
+ KPMinus,
+ KPPlus,
+ KPEnter,
+ KP1,
+ KP2,
+ KP3,
+ KP4,
+ KP5,
+ KP6,
+ KP7,
+ KP8,
+ KP9,
+ KP0,
+ KPDot,
+
+ Key102,
+ Compose,
+ Power,
+ KPEqual,
+
+ F13,
+ F14,
+ F15,
+ F16,
+ F17,
+ F18,
+ F19,
+ F20,
+ F21,
+ F22,
+ F23,
+ F24,
+
+ Open,
+ Help,
+ Properties,
+ Front,
+ Stop,
+ Repeat,
+ Undo,
+ Cut,
+ Copy,
+ Paste,
+ Find,
+ Mute,
+ VolumeUp,
+ VolumeDown,
+ CapsLockActive,
+ NumLockActive,
+ ScrollLockActive,
+ KPComma,
+
+ KPLeftParenthesis,
+ KPRightParenthesis,
+
+ LeftControlKey = 0xE0,
+ LeftShiftKey,
+ LeftAltKey,
+ LeftMetaKey,
+ RightControlKey,
+ RightShiftKey,
+ RightAltKey,
+ RightMetaKey,
+
+ MediaPlayPause,
+ MediaStopCD,
+ MediaPrevious,
+ MediaNext,
+ MediaEject,
+ MediaVolumeUp,
+ MediaVolumeDown,
+ MediaMute,
+ MediaWebsite,
+ MediaBack,
+ MediaForward,
+ MediaStop,
+ MediaFind,
+ MediaScrollUp,
+ MediaScrollDown,
+ MediaEdit,
+ MediaSleep,
+ MediaCoffee,
+ MediaRefresh,
+ MediaCalculator,
+
+ NumKeyboardKeys,
+};
+
+static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
+
+enum Modifiers {
+ LeftControl,
+ LeftShift,
+ LeftAlt,
+ LeftMeta,
+ RightControl,
+ RightShift,
+ RightAlt,
+ RightMeta,
+ CapsLock,
+ ScrollLock,
+ NumLock,
+
+ NumKeyboardMods,
+};
+
+constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
+constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
+constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
+
+constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
+constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
+constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
+
+} // namespace NativeKeyboard
+
+using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
+using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
+using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
+using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
+using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
+
+constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
+constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
+constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
+constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
+
+enum class ControllerType {
+ ProController,
+ DualJoycon,
+ RightJoycon,
+ LeftJoycon,
+};
+
+struct PlayerInput {
+ bool connected;
+ ControllerType type;
+ ButtonsRaw buttons;
+ AnalogsRaw analogs;
+
+ u32 body_color_right;
+ u32 button_color_right;
+ u32 body_color_left;
+ u32 button_color_left;
+};
+
+struct TouchscreenInput {
+ bool enabled;
+ std::string device;
+
+ u32 finger;
+ u32 diameter_x;
+ u32 diameter_y;
+ u32 rotation_angle;
+};
+
struct Values {
// System
bool use_docked_mode;
@@ -120,10 +352,22 @@ struct Values {
s32 language_index;
// Controls
- std::array<std::string, NativeButton::NumButtons> buttons;
- std::array<std::string, NativeAnalog::NumAnalogs> analogs;
+ std::array<PlayerInput, 10> players;
+
+ bool mouse_enabled;
+ std::string mouse_device;
+ MouseButtonsRaw mouse_buttons;
+
+ bool keyboard_enabled;
+ KeyboardKeysRaw keyboard_keys;
+ KeyboardModsRaw keyboard_mods;
+
+ bool debug_pad_enabled;
+ ButtonsRaw debug_pad_buttons;
+ AnalogsRaw debug_pad_analogs;
+
std::string motion_device;
- std::string touch_device;
+ TouchscreenInput touchscreen;
std::atomic_bool is_device_reload_pending{true};
// Core
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() {
// Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
// needed for ARMS.
for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
- regs.viewport[viewport].depth_range_near = 0.0f;
- regs.viewport[viewport].depth_range_far = 1.0f;
+ regs.viewports[viewport].depth_range_near = 0.0f;
+ regs.viewports[viewport].depth_range_far = 1.0f;
}
// Doom and Bomberman seems to use the uninitialized registers and just enable blend
// so initialize blend registers with sane values
@@ -66,6 +66,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
regs.stencil_back_func_func = Regs::ComparisonOp::Always;
regs.stencil_back_func_mask = 0xFFFFFFFF;
regs.stencil_back_mask = 0xFFFFFFFF;
+ // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
+ // register carrying a default value. Assume it's OpenGL's default (1).
+ regs.point_size = 1.0f;
}
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
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:
};
};
+ struct ViewportTransform {
+ f32 scale_x;
+ f32 scale_y;
+ f32 scale_z;
+ f32 translate_x;
+ f32 translate_y;
+ f32 translate_z;
+ INSERT_PADDING_WORDS(2);
+
+ MathUtil::Rectangle<s32> GetRect() const {
+ return {
+ GetX(), // left
+ GetY() + GetHeight(), // top
+ GetX() + GetWidth(), // right
+ GetY() // bottom
+ };
+ };
+
+ s32 GetX() const {
+ return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
+ }
+
+ s32 GetY() const {
+ return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
+ }
+
+ s32 GetWidth() const {
+ return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
+ }
+
+ s32 GetHeight() const {
+ return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
+ }
+ };
+
+ struct ScissorTest {
+ u32 enable;
+ union {
+ BitField<0, 16, u32> min_x;
+ BitField<16, 16, u32> max_x;
+ };
+ union {
+ BitField<0, 16, u32> min_y;
+ BitField<16, 16, u32> max_y;
+ };
+ u32 fill;
+ };
+
+ struct ViewPort {
+ union {
+ BitField<0, 16, u32> x;
+ BitField<16, 16, u32> width;
+ };
+ union {
+ BitField<0, 16, u32> y;
+ BitField<16, 16, u32> height;
+ };
+ float depth_range_near;
+ float depth_range_far;
+ };
+
bool IsShaderConfigEnabled(std::size_t index) const {
// The VertexB is always enabled.
if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) {
@@ -505,55 +566,11 @@ public:
INSERT_PADDING_WORDS(0x2E);
- RenderTargetConfig rt[NumRenderTargets];
-
- struct {
- f32 scale_x;
- f32 scale_y;
- f32 scale_z;
- f32 translate_x;
- f32 translate_y;
- f32 translate_z;
- INSERT_PADDING_WORDS(2);
-
- MathUtil::Rectangle<s32> GetRect() const {
- return {
- GetX(), // left
- GetY() + GetHeight(), // top
- GetX() + GetWidth(), // right
- GetY() // bottom
- };
- };
-
- s32 GetX() const {
- return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
- }
+ std::array<RenderTargetConfig, NumRenderTargets> rt;
- s32 GetY() const {
- return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
- }
+ std::array<ViewportTransform, NumViewports> viewport_transform;
- s32 GetWidth() const {
- return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
- }
-
- s32 GetHeight() const {
- return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
- }
- } viewport_transform[NumViewports];
-
- struct {
- union {
- BitField<0, 16, u32> x;
- BitField<16, 16, u32> width;
- };
- union {
- BitField<0, 16, u32> y;
- BitField<16, 16, u32> height;
- };
- float depth_range_near;
- float depth_range_far;
- } viewport[NumViewports];
+ std::array<ViewPort, NumViewports> viewports;
INSERT_PADDING_WORDS(0x1D);
@@ -571,19 +588,9 @@ public:
INSERT_PADDING_WORDS(0x17);
- struct {
- u32 enable;
- union {
- BitField<0, 16, u32> min_x;
- BitField<16, 16, u32> max_x;
- };
- union {
- BitField<0, 16, u32> min_y;
- BitField<16, 16, u32> max_y;
- };
- } scissor_test;
+ std::array<ScissorTest, NumViewports> scissor_test;
- INSERT_PADDING_WORDS(0x52);
+ INSERT_PADDING_WORDS(0x15);
s32 stencil_back_func_ref;
u32 stencil_back_mask;
@@ -700,7 +707,9 @@ public:
u32 stencil_front_func_mask;
u32 stencil_front_mask;
- INSERT_PADDING_WORDS(0x3);
+ INSERT_PADDING_WORDS(0x2);
+
+ u32 frag_color_clamp;
union {
BitField<4, 1, u32> triangle_rast_flip;
@@ -718,7 +727,12 @@ public:
u32 zeta_enable;
- INSERT_PADDING_WORDS(0x8);
+ union {
+ BitField<0, 1, u32> alpha_to_coverage;
+ BitField<4, 1, u32> alpha_to_one;
+ } multisample_control;
+
+ INSERT_PADDING_WORDS(0x7);
struct {
u32 tsc_address_high;
@@ -1100,8 +1114,8 @@ private:
ASSERT_REG_POSITION(macros, 0x45);
ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
ASSERT_REG_POSITION(rt, 0x200);
-ASSERT_REG_POSITION(viewport_transform[0], 0x280);
-ASSERT_REG_POSITION(viewport, 0x300);
+ASSERT_REG_POSITION(viewport_transform, 0x280);
+ASSERT_REG_POSITION(viewports, 0x300);
ASSERT_REG_POSITION(vertex_buffer, 0x35D);
ASSERT_REG_POSITION(clear_color[0], 0x360);
ASSERT_REG_POSITION(clear_depth, 0x364);
@@ -1136,10 +1150,12 @@ ASSERT_REG_POSITION(stencil_front_func_func, 0x4E4);
ASSERT_REG_POSITION(stencil_front_func_ref, 0x4E5);
ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
+ASSERT_REG_POSITION(frag_color_clamp, 0x4EA);
ASSERT_REG_POSITION(screen_y_control, 0x4EB);
ASSERT_REG_POSITION(vb_element_base, 0x50D);
ASSERT_REG_POSITION(point_size, 0x546);
ASSERT_REG_POSITION(zeta_enable, 0x54E);
+ASSERT_REG_POSITION(multisample_control, 0x54F);
ASSERT_REG_POSITION(tsc, 0x557);
ASSERT_REG_POSITION(tic, 0x55D);
ASSERT_REG_POSITION(stencil_two_side_enable, 0x565);
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
ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
OpenGLState::ApplyDefaultState();
- // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
- state.clip_distance[0] = true;
// Create render framebuffer
framebuffer.Create();
@@ -582,6 +580,8 @@ void RasterizerOpenGL::DrawArrays() {
ConfigureFramebuffers(state);
SyncColorMask();
+ SyncFragmentColorClampState();
+ SyncMultiSampleState();
SyncDepthTestState();
SyncStencilTestState();
SyncBlendState();
@@ -642,7 +642,7 @@ void RasterizerOpenGL::DrawArrays() {
params.DispatchDraw();
// Disable scissor test
- state.scissor.enabled = false;
+ state.viewports[0].scissor.enabled = false;
accelerate_draw = AccelDraw::Disabled;
@@ -733,9 +733,8 @@ void RasterizerOpenGL::SamplerInfo::Create() {
glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
}
-void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) {
+void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
const GLuint s = sampler.handle;
- const Tegra::Texture::TSCEntry& config = info.tsc;
if (mag_filter != config.mag_filter) {
mag_filter = config.mag_filter;
glSamplerParameteri(
@@ -777,30 +776,50 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTex
MaxwellToGL::DepthCompareFunc(depth_compare_func));
}
- if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border ||
- wrap_p == Tegra::Texture::WrapMode::Border) {
- const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g,
- config.border_color_b, config.border_color_a}};
- if (border_color != new_border_color) {
- border_color = new_border_color;
- glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
- }
+ GLvec4 new_border_color;
+ if (config.srgb_conversion) {
+ new_border_color[0] = config.srgb_border_color_r / 255.0f;
+ new_border_color[1] = config.srgb_border_color_g / 255.0f;
+ new_border_color[2] = config.srgb_border_color_g / 255.0f;
+ } else {
+ new_border_color[0] = config.border_color_r;
+ new_border_color[1] = config.border_color_g;
+ new_border_color[2] = config.border_color_b;
}
- if (info.tic.use_header_opt_control == 0) {
+ new_border_color[3] = config.border_color_a;
+
+ if (border_color != new_border_color) {
+ border_color = new_border_color;
+ glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
+ }
+
+ const float anisotropic_max = static_cast<float>(1 << config.max_anisotropy.Value());
+ if (anisotropic_max != max_anisotropic) {
+ max_anisotropic = anisotropic_max;
if (GLAD_GL_ARB_texture_filter_anisotropic) {
- glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY,
- static_cast<float>(1 << info.tic.max_anisotropy.Value()));
+ glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropic);
} else if (GLAD_GL_EXT_texture_filter_anisotropic) {
- glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT,
- static_cast<float>(1 << info.tic.max_anisotropy.Value()));
+ glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropic);
}
- glSamplerParameterf(s, GL_TEXTURE_MIN_LOD,
- static_cast<float>(info.tic.res_min_mip_level.Value()));
- glSamplerParameterf(s, GL_TEXTURE_MAX_LOD,
- static_cast<float>(info.tic.res_max_mip_level.Value() == 0
- ? 16
- : info.tic.res_max_mip_level.Value()));
- glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, info.tic.mip_lod_bias.Value() / 256.f);
+ }
+ const float lod_min = static_cast<float>(config.min_lod_clamp.Value()) / 256.0f;
+ if (lod_min != min_lod) {
+ min_lod = lod_min;
+ glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, min_lod);
+ }
+
+ const float lod_max = static_cast<float>(config.max_lod_clamp.Value()) / 256.0f;
+ if (lod_max != max_lod) {
+ max_lod = lod_max;
+ glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, max_lod);
+ }
+ const u32 bias = config.mip_lod_bias.Value();
+ // Sign extend the 13-bit value.
+ const u32 mask = 1U << (13 - 1);
+ const float bias_lod = static_cast<s32>((bias ^ mask) - mask) / 256.f;
+ if (lod_bias != bias_lod) {
+ lod_bias = bias_lod;
+ glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias);
}
}
@@ -899,7 +918,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
continue;
}
- texture_samplers[current_bindpoint].SyncWithConfig(texture);
+ texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
Surface surface = res_cache.GetTextureSurface(texture, entry);
if (surface != nullptr) {
state.texture_units[current_bindpoint].texture = surface->Texture().handle;
@@ -923,15 +942,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
- for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
+ for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
auto& viewport = current_state.viewports[i];
viewport.x = viewport_rect.left;
viewport.y = viewport_rect.bottom;
viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth());
viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight());
- viewport.depth_range_far = regs.viewport[i].depth_range_far;
- viewport.depth_range_near = regs.viewport[i].depth_range_near;
+ viewport.depth_range_far = regs.viewports[i].depth_range_far;
+ viewport.depth_range_near = regs.viewports[i].depth_range_near;
}
}
@@ -1022,7 +1041,9 @@ void RasterizerOpenGL::SyncStencilTestState() {
void RasterizerOpenGL::SyncColorMask() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
- for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
+ const std::size_t count =
+ regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1;
+ for (std::size_t i = 0; i < count; i++) {
const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i];
auto& dest = state.color_mask[i];
dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE;
@@ -1032,6 +1053,17 @@ void RasterizerOpenGL::SyncColorMask() {
}
}
+void RasterizerOpenGL::SyncMultiSampleState() {
+ const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
+ state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0;
+ state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0;
+}
+
+void RasterizerOpenGL::SyncFragmentColorClampState() {
+ const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
+ state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0;
+}
+
void RasterizerOpenGL::SyncBlendState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
@@ -1043,43 +1075,40 @@ void RasterizerOpenGL::SyncBlendState() {
state.independant_blend.enabled = regs.independent_blend_enable;
if (!state.independant_blend.enabled) {
auto& blend = state.blend[0];
- blend.enabled = regs.blend.enable[0] != 0;
- blend.separate_alpha = regs.blend.separate_alpha;
- blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb);
- blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb);
- blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb);
- if (blend.separate_alpha) {
- blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a);
- blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a);
- blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a);
+ const auto& src = regs.blend;
+ blend.enabled = src.enable[0] != 0;
+ if (blend.enabled) {
+ blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
+ blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
+ blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
+ blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
+ blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
+ blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
}
- for (size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
+ for (std::size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
state.blend[i].enabled = false;
}
return;
}
- for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
+ for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
auto& blend = state.blend[i];
+ const auto& src = regs.independent_blend[i];
blend.enabled = regs.blend.enable[i] != 0;
if (!blend.enabled)
continue;
- blend.separate_alpha = regs.independent_blend[i].separate_alpha;
- blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_rgb);
- blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_rgb);
- blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_rgb);
- if (blend.separate_alpha) {
- blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[i].equation_a);
- blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_source_a);
- blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[i].factor_dest_a);
- }
+ blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
+ blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
+ blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
+ blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
+ blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
+ blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
}
}
void RasterizerOpenGL::SyncLogicOpState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
- // TODO(Subv): Support more than just render target 0.
state.logic_op.enabled = regs.logic_op.enable != 0;
if (!state.logic_op.enabled)
@@ -1092,19 +1121,21 @@ void RasterizerOpenGL::SyncLogicOpState() {
}
void RasterizerOpenGL::SyncScissorTest() {
- // TODO: what is the correct behavior here, a single scissor for all targets
- // or scissor disabled for the rest of the targets?
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
- state.scissor.enabled = (regs.scissor_test.enable != 0);
- if (regs.scissor_test.enable == 0) {
- return;
+ for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumViewports; i++) {
+ const auto& src = regs.scissor_test[i];
+ auto& dst = state.viewports[i].scissor;
+ dst.enabled = (src.enable != 0);
+ if (dst.enabled == 0) {
+ return;
+ }
+ const u32 width = src.max_x - src.min_x;
+ const u32 height = src.max_y - src.min_y;
+ dst.x = src.min_x;
+ dst.y = src.min_y;
+ dst.width = width;
+ dst.height = height;
}
- const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
- const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
- state.scissor.x = regs.scissor_test.min_x;
- state.scissor.y = regs.scissor_test.min_y;
- state.scissor.width = width;
- state.scissor.height = height;
}
void RasterizerOpenGL::SyncTransformFeedback() {
@@ -1118,11 +1149,7 @@ void RasterizerOpenGL::SyncTransformFeedback() {
void RasterizerOpenGL::SyncPointState() {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
-
- // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
- // register carrying a default value. For now, if the point size is zero, assume it's
- // OpenGL's default (1).
- state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
+ state.point.size = regs.point_size;
}
void RasterizerOpenGL::CheckAlphaTests() {
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:
/// SamplerInfo struct.
void Create();
/// Syncs the sampler object with the config, updating any necessary state.
- void SyncWithConfig(const Tegra::Texture::FullTextureInfo& info);
+ void SyncWithConfig(const Tegra::Texture::TSCEntry& info);
private:
Tegra::Texture::TextureFilter mag_filter;
@@ -100,6 +100,10 @@ private:
bool uses_depth_compare;
Tegra::Texture::DepthCompareFunc depth_compare_func;
GLvec4 border_color;
+ float min_lod;
+ float max_lod;
+ float lod_bias;
+ float max_anisotropic;
};
/**
@@ -160,6 +164,12 @@ private:
/// Syncs the LogicOp state to match the guest state
void SyncLogicOpState();
+ /// Syncs the the color clamp state
+ void SyncFragmentColorClampState();
+
+ /// Syncs the alpha coverage and alpha to one
+ void SyncMultiSampleState();
+
/// Syncs the scissor test state to match the guest state
void SyncScissorTest();
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
const u32 tile_size_y{GetDefaultBlockHeight(format)};
if (morton_to_gl) {
- const std::vector<u8> data =
- Tegra::Texture::UnswizzleTexture(addr, tile_size_x, tile_size_y, bytes_per_pixel,
- stride, height, depth, block_height, block_depth);
- const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
- memcpy(gl_buffer, data.data(), size_to_copy);
+ Tegra::Texture::UnswizzleTexture(gl_buffer, addr, tile_size_x, tile_size_y, bytes_per_pixel,
+ stride, height, depth, block_height, block_depth);
} else {
Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
(height + tile_size_y - 1) / tile_size_y, depth,
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:
glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs);
state.draw.shader_program = 0;
state.draw.program_pipeline = pipeline.handle;
+ state.geometry_shaders.enabled = (gs != 0);
}
private:
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;
bool OpenGLState::s_rgb_used;
OpenGLState::OpenGLState() {
// These all match default OpenGL values
+ geometry_shaders.enabled = false;
framebuffer_srgb.enabled = false;
+ multisample_control.alpha_to_coverage = false;
+ multisample_control.alpha_to_one = false;
cull.enabled = false;
cull.mode = GL_BACK;
cull.front_face = GL_CCW;
@@ -50,12 +53,12 @@ OpenGLState::OpenGLState() {
item.height = 0;
item.depth_range_near = 0.0f;
item.depth_range_far = 1.0f;
+ item.scissor.enabled = false;
+ item.scissor.x = 0;
+ item.scissor.y = 0;
+ item.scissor.width = 0;
+ item.scissor.height = 0;
}
- scissor.enabled = false;
- scissor.x = 0;
- scissor.y = 0;
- scissor.width = 0;
- scissor.height = 0;
for (auto& item : blend) {
item.enabled = true;
item.rgb_equation = GL_FUNC_ADD;
@@ -88,6 +91,7 @@ OpenGLState::OpenGLState() {
clip_distance = {};
point.size = 1;
+ fragment_color_clamp.enabled = false;
}
void OpenGLState::ApplyDefaultState() {
@@ -136,7 +140,7 @@ void OpenGLState::ApplyCulling() const {
}
void OpenGLState::ApplyColorMask() const {
- if (GLAD_GL_ARB_viewport_array) {
+ if (GLAD_GL_ARB_viewport_array && independant_blend.enabled) {
for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
const auto& updated = color_mask[i];
const auto& current = cur_state.color_mask[i];
@@ -230,26 +234,10 @@ void OpenGLState::ApplyStencilTest() const {
}
}
-void OpenGLState::ApplyScissor() const {
- const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled;
- if (scissor_changed) {
- if (scissor.enabled) {
- glEnable(GL_SCISSOR_TEST);
- } else {
- glDisable(GL_SCISSOR_TEST);
- }
- }
- if (scissor.enabled &&
- (scissor_changed || scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
- scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height)) {
- glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
- }
-}
-
void OpenGLState::ApplyViewport() const {
- if (GLAD_GL_ARB_viewport_array) {
- for (GLuint i = 0;
- i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); i++) {
+ if (GLAD_GL_ARB_viewport_array && geometry_shaders.enabled) {
+ for (GLuint i = 0; i < static_cast<GLuint>(Tegra::Engines::Maxwell3D::Regs::NumViewports);
+ i++) {
const auto& current = cur_state.viewports[i];
const auto& updated = viewports[i];
if (updated.x != current.x || updated.y != current.y ||
@@ -260,6 +248,22 @@ void OpenGLState::ApplyViewport() const {
updated.depth_range_far != current.depth_range_far) {
glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
}
+ const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
+ if (scissor_changed) {
+ if (updated.scissor.enabled) {
+ glEnablei(GL_SCISSOR_TEST, i);
+ } else {
+ glDisablei(GL_SCISSOR_TEST, i);
+ }
+ }
+ if (updated.scissor.enabled &&
+ (scissor_changed || updated.scissor.x != current.scissor.x ||
+ updated.scissor.y != current.scissor.y ||
+ updated.scissor.width != current.scissor.width ||
+ updated.scissor.height != current.scissor.height)) {
+ glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
+ updated.scissor.height);
+ }
}
} else {
const auto& current = cur_state.viewports[0];
@@ -273,6 +277,21 @@ void OpenGLState::ApplyViewport() const {
updated.depth_range_far != current.depth_range_far) {
glDepthRange(updated.depth_range_near, updated.depth_range_far);
}
+ const bool scissor_changed = updated.scissor.enabled != current.scissor.enabled;
+ if (scissor_changed) {
+ if (updated.scissor.enabled) {
+ glEnable(GL_SCISSOR_TEST);
+ } else {
+ glDisable(GL_SCISSOR_TEST);
+ }
+ }
+ if (updated.scissor.enabled && (scissor_changed || updated.scissor.x != current.scissor.x ||
+ updated.scissor.y != current.scissor.y ||
+ updated.scissor.width != current.scissor.width ||
+ updated.scissor.height != current.scissor.height)) {
+ glScissor(updated.scissor.x, updated.scissor.y, updated.scissor.width,
+ updated.scissor.height);
+ }
}
}
@@ -290,27 +309,16 @@ void OpenGLState::ApplyGlobalBlending() const {
if (!updated.enabled) {
return;
}
- if (updated.separate_alpha) {
- if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
- updated.dst_rgb_func != current.dst_rgb_func ||
- updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) {
- glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
- updated.dst_a_func);
- }
-
- if (blend_changed || updated.rgb_equation != current.rgb_equation ||
- updated.a_equation != current.a_equation) {
- glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
- }
- } else {
- if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
- updated.dst_rgb_func != current.dst_rgb_func) {
- glBlendFunc(updated.src_rgb_func, updated.dst_rgb_func);
- }
+ if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
+ updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
+ updated.dst_a_func != current.dst_a_func) {
+ glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
+ updated.dst_a_func);
+ }
- if (blend_changed || updated.rgb_equation != current.rgb_equation) {
- glBlendEquation(updated.rgb_equation);
- }
+ if (blend_changed || updated.rgb_equation != current.rgb_equation ||
+ updated.a_equation != current.a_equation) {
+ glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
}
}
@@ -328,29 +336,17 @@ void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) const {
if (!updated.enabled) {
return;
}
- if (updated.separate_alpha) {
- if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
- updated.dst_rgb_func != current.dst_rgb_func ||
- updated.src_a_func != current.src_a_func || updated.dst_a_func != current.dst_a_func) {
- glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func,
- updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
- }
-
- if (blend_changed || updated.rgb_equation != current.rgb_equation ||
- updated.a_equation != current.a_equation) {
- glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
- updated.a_equation);
- }
- } else {
- if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
- updated.dst_rgb_func != current.dst_rgb_func) {
- glBlendFunciARB(static_cast<GLuint>(target), updated.src_rgb_func,
- updated.dst_rgb_func);
- }
+ if (blend_changed || updated.src_rgb_func != current.src_rgb_func ||
+ updated.dst_rgb_func != current.dst_rgb_func || updated.src_a_func != current.src_a_func ||
+ updated.dst_a_func != current.dst_a_func) {
+ glBlendFuncSeparateiARB(static_cast<GLuint>(target), updated.src_rgb_func,
+ updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
+ }
- if (blend_changed || updated.rgb_equation != current.rgb_equation) {
- glBlendEquationiARB(static_cast<GLuint>(target), updated.rgb_equation);
- }
+ if (blend_changed || updated.rgb_equation != current.rgb_equation ||
+ updated.a_equation != current.a_equation) {
+ glBlendEquationSeparateiARB(static_cast<GLuint>(target), updated.rgb_equation,
+ updated.a_equation);
}
}
@@ -481,9 +477,29 @@ void OpenGLState::Apply() const {
if (point.size != cur_state.point.size) {
glPointSize(point.size);
}
+ if (GLAD_GL_ARB_color_buffer_float) {
+ if (fragment_color_clamp.enabled != cur_state.fragment_color_clamp.enabled) {
+ glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
+ fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
+ }
+ }
+ if (multisample_control.alpha_to_coverage != cur_state.multisample_control.alpha_to_coverage) {
+ if (multisample_control.alpha_to_coverage) {
+ glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+ } else {
+ glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+ }
+ }
+ if (multisample_control.alpha_to_one != cur_state.multisample_control.alpha_to_one) {
+ if (multisample_control.alpha_to_one) {
+ glEnable(GL_SAMPLE_ALPHA_TO_ONE);
+ } else {
+ glDisable(GL_SAMPLE_ALPHA_TO_ONE);
+ }
+ }
+
ApplyColorMask();
ApplyViewport();
- ApplyScissor();
ApplyStencilTest();
ApplySRgb();
ApplyCulling();
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:
} framebuffer_srgb;
struct {
+ bool alpha_to_coverage; // GL_ALPHA_TO_COVERAGE
+ bool alpha_to_one; // GL_ALPHA_TO_ONE
+ } multisample_control;
+
+ struct {
+ bool enabled; // GL_CLAMP_FRAGMENT_COLOR_ARB
+ } fragment_color_clamp;
+
+ struct {
+ bool enabled; // viewports arrays are only supported when geometry shaders are enabled.
+ } geometry_shaders;
+
+ struct {
bool enabled; // GL_CULL_FACE
GLenum mode; // GL_CULL_FACE_MODE
GLenum front_face; // GL_FRONT_FACE
@@ -79,7 +92,6 @@ public:
struct Blend {
bool enabled; // GL_BLEND
- bool separate_alpha; // Independent blend enabled
GLenum rgb_equation; // GL_BLEND_EQUATION_RGB
GLenum a_equation; // GL_BLEND_EQUATION_ALPHA
GLenum src_rgb_func; // GL_BLEND_SRC_RGB
@@ -150,16 +162,15 @@ public:
GLfloat height;
GLfloat depth_range_near; // GL_DEPTH_RANGE
GLfloat depth_range_far; // GL_DEPTH_RANGE
+ struct {
+ bool enabled; // GL_SCISSOR_TEST
+ GLint x;
+ GLint y;
+ GLsizei width;
+ GLsizei height;
+ } scissor;
};
- std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> viewports;
-
- struct {
- bool enabled; // GL_SCISSOR_TEST
- GLint x;
- GLint y;
- GLsizei width;
- GLsizei height;
- } scissor;
+ std::array<viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
struct {
float size; // GL_POINT_SIZE
@@ -214,7 +225,6 @@ private:
void ApplyLogicOp() const;
void ApplyTextures() const;
void ApplySamplers() const;
- void ApplyScissor() const;
};
} // 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) {
return GL_CLAMP_TO_BORDER;
case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
return GL_MIRROR_CLAMP_TO_EDGE;
+ case Tegra::Texture::WrapMode::MirrorOnceBorder:
+ if (GL_EXT_texture_mirror_clamp) {
+ return GL_MIRROR_CLAMP_TO_BORDER_EXT;
+ } else {
+ return GL_MIRROR_CLAMP_TO_EDGE;
+ }
}
LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
return GL_REPEAT;
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 {
std::array<std::array<u16, M>, N> values{};
};
-constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();
-constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
+constexpr u32 gob_size_x = 64;
+constexpr u32 gob_size_y = 8;
+constexpr u32 gob_size_z = 1;
+constexpr u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
+constexpr u32 fast_swizzle_align = 16;
+
+constexpr auto legacy_swizzle_table = SwizzleTable<gob_size_y, gob_size_x, gob_size_z>();
+constexpr auto fast_swizzle_table = SwizzleTable<gob_size_y, 4, fast_swizzle_align>();
/**
* This function manages ALL the GOBs(Group of Bytes) Inside a single block.
@@ -52,10 +58,7 @@ void PreciseProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, con
const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
std::array<u8*, 2> data_ptrs;
u32 z_address = tile_offset;
- const u32 gob_size_x = 64;
- const u32 gob_size_y = 8;
- const u32 gob_size_z = 1;
- const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
+
for (u32 z = z_start; z < z_end; z++) {
u32 y_address = z_address;
u32 pixel_base = layer_z * z + y_start * stride_x;
@@ -90,23 +93,19 @@ void FastProcessBlock(u8* const swizzled_data, u8* const unswizzled_data, const
u32 z_address = tile_offset;
const u32 x_startb = x_start * bytes_per_pixel;
const u32 x_endb = x_end * bytes_per_pixel;
- constexpr u32 copy_size = 16;
- constexpr u32 gob_size_x = 64;
- constexpr u32 gob_size_y = 8;
- constexpr u32 gob_size_z = 1;
- const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
+
for (u32 z = z_start; z < z_end; z++) {
u32 y_address = z_address;
u32 pixel_base = layer_z * z + y_start * stride_x;
for (u32 y = y_start; y < y_end; y++) {
const auto& table = fast_swizzle_table[y % gob_size_y];
- for (u32 xb = x_startb; xb < x_endb; xb += copy_size) {
- const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]};
+ for (u32 xb = x_startb; xb < x_endb; xb += fast_swizzle_align) {
+ const u32 swizzle_offset{y_address + table[(xb / fast_swizzle_align) % 4]};
const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
const u32 pixel_index{out_x + pixel_base};
data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
- std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
+ std::memcpy(data_ptrs[0], data_ptrs[1], fast_swizzle_align);
}
pixel_base += stride_x;
if ((y + 1) % gob_size_y == 0)
@@ -132,17 +131,15 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool
auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
const u32 stride_x = width * out_bytes_per_pixel;
const u32 layer_z = height * stride_x;
- constexpr u32 gob_x_bytes = 64;
- const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel;
- constexpr u32 gob_elements_y = 8;
- constexpr u32 gob_elements_z = 1;
+ const u32 gob_elements_x = gob_size_x / bytes_per_pixel;
+ constexpr u32 gob_elements_y = gob_size_y;
+ constexpr u32 gob_elements_z = gob_size_z;
const u32 block_x_elements = gob_elements_x;
const u32 block_y_elements = gob_elements_y * block_height;
const u32 block_z_elements = gob_elements_z * block_depth;
const u32 blocks_on_x = div_ceil(width, block_x_elements);
const u32 blocks_on_y = div_ceil(height, block_y_elements);
const u32 blocks_on_z = div_ceil(depth, block_z_elements);
- constexpr u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
const u32 xy_block_size = gob_size * block_height;
const u32 block_size = xy_block_size * block_depth;
u32 tile_offset = 0;
@@ -173,7 +170,7 @@ void SwizzledData(u8* const swizzled_data, u8* const unswizzled_data, const bool
void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
u32 out_bytes_per_pixel, u8* const swizzled_data, u8* const unswizzled_data,
bool unswizzle, u32 block_height, u32 block_depth) {
- if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
+ if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % fast_swizzle_align == 0) {
SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
} else {
@@ -229,29 +226,38 @@ u32 BytesPerPixel(TextureFormat format) {
}
}
+void UnswizzleTexture(u8* const unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
+ u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height,
+ u32 block_depth) {
+ CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
+ (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
+ bytes_per_pixel, Memory::GetPointer(address), unswizzled_data, true,
+ block_height, block_depth);
+}
+
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height, u32 block_depth) {
std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
- CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
- (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
- bytes_per_pixel, Memory::GetPointer(address), unswizzled_data.data(), true,
- block_height, block_depth);
+ UnswizzleTexture(unswizzled_data.data(), address, tile_size_x, tile_size_y, bytes_per_pixel,
+ width, height, depth, block_height, block_depth);
return unswizzled_data;
}
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
u32 block_height) {
- const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64};
+ const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + (gob_size_x - 1)) /
+ gob_size_x};
for (u32 line = 0; line < subrect_height; ++line) {
const u32 gob_address_y =
- (line / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
- (line % (8 * block_height) / 8) * 512;
- const auto& table = legacy_swizzle_table[line % 8];
+ (line / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs +
+ ((line % (gob_size_y * block_height)) / gob_size_y) * gob_size;
+ const auto& table = legacy_swizzle_table[line % gob_size_y];
for (u32 x = 0; x < subrect_width; ++x) {
- const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height;
- const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64];
+ const u32 gob_address =
+ gob_address_y + (x * bytes_per_pixel / gob_size_x) * gob_size * block_height;
+ const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % gob_size_x];
const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
const VAddr dest_addr = swizzled_data + swizzled_offset;
@@ -265,13 +271,13 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32
u32 block_height, u32 offset_x, u32 offset_y) {
for (u32 line = 0; line < subrect_height; ++line) {
const u32 y2 = line + offset_y;
- const u32 gob_address_y =
- (y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512;
- const auto& table = legacy_swizzle_table[y2 % 8];
+ const u32 gob_address_y = (y2 / (gob_size_y * block_height)) * gob_size * block_height +
+ ((y2 % (gob_size_y * block_height)) / gob_size_y) * gob_size;
+ const auto& table = legacy_swizzle_table[y2 % gob_size_y];
for (u32 x = 0; x < subrect_width; ++x) {
const u32 x2 = (x + offset_x) * bytes_per_pixel;
- const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height;
- const u32 swizzled_offset = gob_address + table[x2 % 64];
+ const u32 gob_address = gob_address_y + (x2 / gob_size_x) * gob_size * block_height;
+ const u32 swizzled_offset = gob_address + table[x2 % gob_size_x];
const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
const VAddr source_addr = swizzled_data + swizzled_offset;
@@ -325,12 +331,9 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height, u32 block_depth) {
if (tiled) {
- constexpr u32 gobs_in_x = 64;
- constexpr u32 gobs_in_y = 8;
- constexpr u32 gobs_in_z = 1;
- const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x);
- const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
- const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
+ const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gob_size_x);
+ const u32 aligned_height = Common::AlignUp(height, gob_size_y * block_height);
+ const u32 aligned_depth = Common::AlignUp(depth, gob_size_z * block_depth);
return aligned_width * aligned_height * aligned_depth;
} else {
return width * height * depth * bytes_per_pixel;
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() {
/**
* Unswizzles a swizzled texture without changing its format.
*/
+void UnswizzleTexture(u8* unswizzled_data, VAddr address, u32 tile_size_x, u32 tile_size_y,
+ u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
+ u32 block_height = TICEntry::DefaultBlockHeight,
+ u32 block_depth = TICEntry::DefaultBlockHeight);
+/**
+ * Unswizzles a swizzled texture without changing its format.
+ */
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
u32 block_height = 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 {
union {
BitField<0, 4, u32> res_min_mip_level;
BitField<4, 4, u32> res_max_mip_level;
+ BitField<12, 12, u32> min_lod_clamp;
};
GPUVAddr Address() const {
@@ -284,13 +285,25 @@ struct TSCEntry {
BitField<6, 3, WrapMode> wrap_p;
BitField<9, 1, u32> depth_compare_enabled;
BitField<10, 3, DepthCompareFunc> depth_compare_func;
+ BitField<13, 1, u32> srgb_conversion;
+ BitField<20, 3, u32> max_anisotropy;
};
union {
BitField<0, 2, TextureFilter> mag_filter;
BitField<4, 2, TextureFilter> min_filter;
BitField<6, 2, TextureMipmapFilter> mip_filter;
+ BitField<9, 1, u32> cubemap_interface_filtering;
+ BitField<12, 13, u32> mip_lod_bias;
+ };
+ union {
+ BitField<0, 12, u32> min_lod_clamp;
+ BitField<12, 12, u32> max_lod_clamp;
+ BitField<24, 8, u32> srgb_border_color_r;
+ };
+ union {
+ BitField<12, 8, u32> srgb_border_color_g;
+ BitField<20, 8, u32> srgb_border_color_b;
};
- INSERT_PADDING_BYTES(8);
float border_color_r;
float border_color_g;
float border_color_b;
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
configuration/configure_graphics.h
configuration/configure_input.cpp
configuration/configure_input.h
+ configuration/configure_input_player.cpp
+ configuration/configure_input_player.h
+ configuration/configure_mouse_advanced.cpp
+ configuration/configure_mouse_advanced.h
configuration/configure_system.cpp
configuration/configure_system.h
+ configuration/configure_touchscreen_advanced.cpp
+ configuration/configure_touchscreen_advanced.h
configuration/configure_web.cpp
configuration/configure_web.h
debugger/graphics/graphics_breakpoint_observer.cpp
@@ -78,7 +84,10 @@ set(UIS
configuration/configure_general.ui
configuration/configure_graphics.ui
configuration/configure_input.ui
+ configuration/configure_input_player.ui
+ configuration/configure_mouse_advanced.ui
configuration/configure_system.ui
+ configuration/configure_touchscreen_advanced.ui
configuration/configure_web.ui
hotkeys.ui
main.ui
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 @@
#include <QSettings>
#include "common/file_util.h"
#include "core/hle/service/acc/profile_manager.h"
+#include "core/hle/service/hid/controllers/npad.h"
#include "input_common/main.h"
#include "yuzu/configuration/config.h"
#include "yuzu/ui_settings.h"
@@ -47,40 +48,313 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
},
}};
-void Config::ReadValues() {
- qt_config->beginGroup("Controls");
+const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
+ {
+ Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
+};
+
+const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> Config::default_keyboard_keys = {
+ 0,
+ 0,
+ 0,
+ 0,
+ Qt::Key_A,
+ Qt::Key_B,
+ Qt::Key_C,
+ Qt::Key_D,
+ Qt::Key_E,
+ Qt::Key_F,
+ Qt::Key_G,
+ Qt::Key_H,
+ Qt::Key_I,
+ Qt::Key_J,
+ Qt::Key_K,
+ Qt::Key_L,
+ Qt::Key_M,
+ Qt::Key_N,
+ Qt::Key_O,
+ Qt::Key_P,
+ Qt::Key_Q,
+ Qt::Key_R,
+ Qt::Key_S,
+ Qt::Key_T,
+ Qt::Key_U,
+ Qt::Key_V,
+ Qt::Key_W,
+ Qt::Key_X,
+ Qt::Key_Y,
+ Qt::Key_Z,
+ Qt::Key_1,
+ Qt::Key_2,
+ Qt::Key_3,
+ Qt::Key_4,
+ Qt::Key_5,
+ Qt::Key_6,
+ Qt::Key_7,
+ Qt::Key_8,
+ Qt::Key_9,
+ Qt::Key_0,
+ Qt::Key_Enter,
+ Qt::Key_Escape,
+ Qt::Key_Backspace,
+ Qt::Key_Tab,
+ Qt::Key_Space,
+ Qt::Key_Minus,
+ Qt::Key_Equal,
+ Qt::Key_BracketLeft,
+ Qt::Key_BracketRight,
+ Qt::Key_Backslash,
+ Qt::Key_Dead_Tilde,
+ Qt::Key_Semicolon,
+ Qt::Key_Apostrophe,
+ Qt::Key_Dead_Grave,
+ Qt::Key_Comma,
+ Qt::Key_Period,
+ Qt::Key_Slash,
+ Qt::Key_CapsLock,
+
+ Qt::Key_F1,
+ Qt::Key_F2,
+ Qt::Key_F3,
+ Qt::Key_F4,
+ Qt::Key_F5,
+ Qt::Key_F6,
+ Qt::Key_F7,
+ Qt::Key_F8,
+ Qt::Key_F9,
+ Qt::Key_F10,
+ Qt::Key_F11,
+ Qt::Key_F12,
+
+ Qt::Key_SysReq,
+ Qt::Key_ScrollLock,
+ Qt::Key_Pause,
+ Qt::Key_Insert,
+ Qt::Key_Home,
+ Qt::Key_PageUp,
+ Qt::Key_Delete,
+ Qt::Key_End,
+ Qt::Key_PageDown,
+ Qt::Key_Right,
+ Qt::Key_Left,
+ Qt::Key_Down,
+ Qt::Key_Up,
+
+ Qt::Key_NumLock,
+ Qt::Key_Slash,
+ Qt::Key_Asterisk,
+ Qt::Key_Minus,
+ Qt::Key_Plus,
+ Qt::Key_Enter,
+ Qt::Key_1,
+ Qt::Key_2,
+ Qt::Key_3,
+ Qt::Key_4,
+ Qt::Key_5,
+ Qt::Key_6,
+ Qt::Key_7,
+ Qt::Key_8,
+ Qt::Key_9,
+ Qt::Key_0,
+ Qt::Key_Period,
+
+ 0,
+ 0,
+ Qt::Key_PowerOff,
+ Qt::Key_Equal,
+
+ Qt::Key_F13,
+ Qt::Key_F14,
+ Qt::Key_F15,
+ Qt::Key_F16,
+ Qt::Key_F17,
+ Qt::Key_F18,
+ Qt::Key_F19,
+ Qt::Key_F20,
+ Qt::Key_F21,
+ Qt::Key_F22,
+ Qt::Key_F23,
+ Qt::Key_F24,
+
+ Qt::Key_Open,
+ Qt::Key_Help,
+ Qt::Key_Menu,
+ 0,
+ Qt::Key_Stop,
+ Qt::Key_AudioRepeat,
+ Qt::Key_Undo,
+ Qt::Key_Cut,
+ Qt::Key_Copy,
+ Qt::Key_Paste,
+ Qt::Key_Find,
+ Qt::Key_VolumeMute,
+ Qt::Key_VolumeUp,
+ Qt::Key_VolumeDown,
+ Qt::Key_CapsLock,
+ Qt::Key_NumLock,
+ Qt::Key_ScrollLock,
+ Qt::Key_Comma,
+
+ Qt::Key_ParenLeft,
+ Qt::Key_ParenRight,
+};
+
+const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default_keyboard_mods = {
+ Qt::Key_Control, Qt::Key_Shift, Qt::Key_Alt, Qt::Key_ApplicationLeft,
+ Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight,
+};
+
+void Config::ReadPlayerValues() {
+ for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
+ Settings::values.players[p].connected =
+ qt_config->value(QString("player_%1_connected").arg(p), false).toBool();
+
+ Settings::values.players[p].type = static_cast<Settings::ControllerType>(
+ qt_config
+ ->value(QString("player_%1_type").arg(p),
+ static_cast<u8>(Settings::ControllerType::DualJoycon))
+ .toUInt());
+
+ Settings::values.players[p].body_color_left =
+ qt_config
+ ->value(QString("player_%1_body_color_left").arg(p),
+ Settings::JOYCON_BODY_NEON_BLUE)
+ .toUInt();
+ Settings::values.players[p].body_color_right =
+ qt_config
+ ->value(QString("player_%1_body_color_right").arg(p),
+ Settings::JOYCON_BODY_NEON_RED)
+ .toUInt();
+ Settings::values.players[p].button_color_left =
+ qt_config
+ ->value(QString("player_%1_button_color_left").arg(p),
+ Settings::JOYCON_BUTTONS_NEON_BLUE)
+ .toUInt();
+ Settings::values.players[p].button_color_right =
+ qt_config
+ ->value(QString("player_%1_button_color_right").arg(p),
+ Settings::JOYCON_BUTTONS_NEON_RED)
+ .toUInt();
+
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
+ Settings::values.players[p].buttons[i] =
+ qt_config
+ ->value(QString("player_%1_").arg(p) + Settings::NativeButton::mapping[i],
+ QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (Settings::values.players[p].buttons[i].empty())
+ Settings::values.players[p].buttons[i] = default_param;
+ }
+
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+ default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
+ default_analogs[i][3], default_analogs[i][4], 0.5f);
+ Settings::values.players[p].analogs[i] =
+ qt_config
+ ->value(QString("player_%1_").arg(p) + Settings::NativeAnalog::mapping[i],
+ QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (Settings::values.players[p].analogs[i].empty())
+ Settings::values.players[p].analogs[i] = default_param;
+ }
+ }
+
+ std::stable_partition(
+ Settings::values.players.begin(),
+ Settings::values.players.begin() +
+ Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
+ [](const auto& player) { return player.connected; });
+}
+
+void Config::ReadDebugValues() {
+ Settings::values.debug_pad_enabled = qt_config->value("debug_pad_enabled", false).toBool();
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
- Settings::values.buttons[i] =
+ Settings::values.debug_pad_buttons[i] =
qt_config
- ->value(Settings::NativeButton::mapping[i], QString::fromStdString(default_param))
+ ->value(QString("debug_pad_") + Settings::NativeButton::mapping[i],
+ QString::fromStdString(default_param))
.toString()
.toStdString();
- if (Settings::values.buttons[i].empty())
- Settings::values.buttons[i] = default_param;
+ if (Settings::values.debug_pad_buttons[i].empty())
+ Settings::values.debug_pad_buttons[i] = default_param;
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_analogs[i][4], 0.5f);
- Settings::values.analogs[i] =
+ Settings::values.debug_pad_analogs[i] =
qt_config
- ->value(Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param))
+ ->value(QString("debug_pad_") + Settings::NativeAnalog::mapping[i],
+ QString::fromStdString(default_param))
.toString()
.toStdString();
- if (Settings::values.analogs[i].empty())
- Settings::values.analogs[i] = default_param;
+ if (Settings::values.debug_pad_analogs[i].empty())
+ Settings::values.debug_pad_analogs[i] = default_param;
}
+}
+
+void Config::ReadKeyboardValues() {
+ Settings::values.keyboard_enabled = qt_config->value("keyboard_enabled", false).toBool();
+
+ std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
+ Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
+ std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
+ Settings::values.keyboard_keys.begin() +
+ Settings::NativeKeyboard::LeftControlKey,
+ InputCommon::GenerateKeyboardParam);
+ std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
+ Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
+}
+
+void Config::ReadMouseValues() {
+ Settings::values.mouse_enabled = qt_config->value("mouse_enabled", false).toBool();
+
+ for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
+ std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
+ Settings::values.mouse_buttons[i] =
+ qt_config
+ ->value(QString("mouse_") + Settings::NativeMouseButton::mapping[i],
+ QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (Settings::values.mouse_buttons[i].empty())
+ Settings::values.mouse_buttons[i] = default_param;
+ }
+}
+
+void Config::ReadTouchscreenValues() {
+ Settings::values.touchscreen.enabled = qt_config->value("touchscreen_enabled", true).toBool();
+ Settings::values.touchscreen.device =
+ qt_config->value("touchscreen_device", "engine:emu_window").toString().toStdString();
+
+ Settings::values.touchscreen.finger = qt_config->value("touchscreen_finger", 0).toUInt();
+ Settings::values.touchscreen.rotation_angle = qt_config->value("touchscreen_angle", 0).toUInt();
+ Settings::values.touchscreen.diameter_x =
+ qt_config->value("touchscreen_diameter_x", 15).toUInt();
+ Settings::values.touchscreen.diameter_y =
+ qt_config->value("touchscreen_diameter_y", 15).toUInt();
+ qt_config->endGroup();
+}
+
+void Config::ReadValues() {
+ qt_config->beginGroup("Controls");
+
+ ReadPlayerValues();
+ ReadDebugValues();
+ ReadKeyboardValues();
+ ReadMouseValues();
+ ReadTouchscreenValues();
Settings::values.motion_device =
qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
.toString()
.toStdString();
- Settings::values.touch_device =
- qt_config->value("touch_device", "engine:emu_window").toString().toStdString();
-
- qt_config->endGroup();
qt_config->beginGroup("Core");
Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
@@ -126,6 +400,11 @@ void Config::ReadValues() {
.toStdString());
qt_config->endGroup();
+ qt_config->beginGroup("Core");
+ Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
+ Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool();
+ qt_config->endGroup();
+
qt_config->beginGroup("System");
Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
@@ -230,18 +509,81 @@ void Config::ReadValues() {
qt_config->endGroup();
}
-void Config::SaveValues() {
- qt_config->beginGroup("Controls");
+void Config::SavePlayerValues() {
+ for (int p = 0; p < Settings::values.players.size(); ++p) {
+ qt_config->setValue(QString("player_%1_connected").arg(p),
+ Settings::values.players[p].connected);
+ qt_config->setValue(QString("player_%1_type").arg(p),
+ static_cast<u8>(Settings::values.players[p].type));
+
+ qt_config->setValue(QString("player_%1_body_color_left").arg(p),
+ Settings::values.players[p].body_color_left);
+ qt_config->setValue(QString("player_%1_body_color_right").arg(p),
+ Settings::values.players[p].body_color_right);
+ qt_config->setValue(QString("player_%1_button_color_left").arg(p),
+ Settings::values.players[p].button_color_left);
+ qt_config->setValue(QString("player_%1_button_color_right").arg(p),
+ Settings::values.players[p].button_color_right);
+
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ qt_config->setValue(QString("player_%1_").arg(p) +
+ QString::fromStdString(Settings::NativeButton::mapping[i]),
+ QString::fromStdString(Settings::values.players[p].buttons[i]));
+ }
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ qt_config->setValue(QString("player_%1_").arg(p) +
+ QString::fromStdString(Settings::NativeAnalog::mapping[i]),
+ QString::fromStdString(Settings::values.players[p].analogs[i]));
+ }
+ }
+}
+
+void Config::SaveDebugValues() {
+ qt_config->setValue("debug_pad_enabled", Settings::values.debug_pad_enabled);
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
- qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]),
- QString::fromStdString(Settings::values.buttons[i]));
+ qt_config->setValue(QString("debug_pad_") +
+ QString::fromStdString(Settings::NativeButton::mapping[i]),
+ QString::fromStdString(Settings::values.debug_pad_buttons[i]));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
- qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]),
- QString::fromStdString(Settings::values.analogs[i]));
+ qt_config->setValue(QString("debug_pad_") +
+ QString::fromStdString(Settings::NativeAnalog::mapping[i]),
+ QString::fromStdString(Settings::values.debug_pad_analogs[i]));
}
+}
+
+void Config::SaveMouseValues() {
+ qt_config->setValue("mouse_enabled", Settings::values.mouse_enabled);
+
+ for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
+ qt_config->setValue(QString("mouse_") +
+ QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
+ QString::fromStdString(Settings::values.mouse_buttons[i]));
+ }
+}
+
+void Config::SaveTouchscreenValues() {
+ qt_config->setValue("touchscreen_enabled", Settings::values.touchscreen.enabled);
+ qt_config->setValue("touchscreen_device",
+ QString::fromStdString(Settings::values.touchscreen.device));
+
+ qt_config->setValue("touchscreen_finger", Settings::values.touchscreen.finger);
+ qt_config->setValue("touchscreen_angle", Settings::values.touchscreen.rotation_angle);
+ qt_config->setValue("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x);
+ qt_config->setValue("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y);
+}
+
+void Config::SaveValues() {
+ qt_config->beginGroup("Controls");
+
+ SavePlayerValues();
+ SaveDebugValues();
+ SaveMouseValues();
+ SaveTouchscreenValues();
+
qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device));
- qt_config->setValue("touch_device", QString::fromStdString(Settings::values.touch_device));
+ qt_config->setValue("keyboard_enabled", Settings::values.keyboard_enabled);
+
qt_config->endGroup();
qt_config->beginGroup("Core");
@@ -280,7 +622,6 @@ void Config::SaveValues() {
qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
qt_config->setValue("current_user", Settings::values.current_user);
-
qt_config->setValue("language_index", Settings::values.language_index);
qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value());
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:
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
+ static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
+ default_mouse_buttons;
+ static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
+ static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
private:
void ReadValues();
+ void ReadPlayerValues();
+ void ReadDebugValues();
+ void ReadKeyboardValues();
+ void ReadMouseValues();
+ void ReadTouchscreenValues();
+
void SaveValues();
+ void SavePlayerValues();
+ void SaveDebugValues();
+ void SaveMouseValues();
+ void SaveTouchscreenValues();
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
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)
InitializeRowComboBoxes();
this->setConfiguration();
+
+ // Force game list reload if any of the relevant settings are changed.
+ connect(ui->show_unknown, &QCheckBox::stateChanged, this,
+ &ConfigureGameList::RequestGameListUpdate);
+ connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
+ &ConfigureGameList::RequestGameListUpdate);
+ connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
+ &ConfigureGameList::RequestGameListUpdate);
+ connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
+ &ConfigureGameList::RequestGameListUpdate);
}
ConfigureGameList::~ConfigureGameList() = default;
@@ -49,6 +59,10 @@ void ConfigureGameList::applyConfiguration() {
Settings::Apply();
}
+void ConfigureGameList::RequestGameListUpdate() {
+ UISettings::values.is_game_list_reload_pending.exchange(true);
+}
+
void ConfigureGameList::setConfiguration() {
ui->show_unknown->setChecked(UISettings::values.show_unknown);
ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
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:
void applyConfiguration();
private:
+ void RequestGameListUpdate();
+
void setConfiguration();
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 @@
// Refer to the license.txt file included.
#include "core/core.h"
-#include "core/hle/service/am/am.h"
-#include "core/hle/service/am/applet_ae.h"
-#include "core/hle/service/am/applet_oe.h"
-#include "core/hle/service/sm/sm.h"
#include "core/settings.h"
#include "ui_configure_general.h"
#include "yuzu/configuration/configure_general.h"
@@ -23,6 +19,9 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
this->setConfiguration();
+ connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this,
+ [] { UISettings::values.is_game_list_reload_pending.exchange(true); });
+
ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
}
@@ -33,7 +32,6 @@ void ConfigureGeneral::setConfiguration() {
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
- ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
ui->enable_nfc->setChecked(Settings::values.enable_nfc);
}
@@ -41,33 +39,6 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
ui->widget->Populate(registry);
}
-void ConfigureGeneral::OnDockedModeChanged(bool last_state, bool new_state) {
- if (last_state == new_state) {
- return;
- }
-
- Core::System& system{Core::System::GetInstance()};
- if (!system.IsPoweredOn()) {
- return;
- }
- Service::SM::ServiceManager& sm = system.ServiceManager();
-
- // Message queue is shared between these services, we just need to signal an operation
- // change to one and it will handle both automatically
- auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
- auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
- bool has_signalled = false;
-
- if (applet_oe != nullptr) {
- applet_oe->GetMessageQueue()->OperationModeChanged();
- has_signalled = true;
- }
-
- if (applet_ae != nullptr && !has_signalled) {
- applet_ae->GetMessageQueue()->OperationModeChanged();
- }
-}
-
void ConfigureGeneral::applyConfiguration() {
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
@@ -75,9 +46,5 @@ void ConfigureGeneral::applyConfiguration() {
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
- const bool pre_docked_mode = Settings::values.use_docked_mode;
- Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
- OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
-
Settings::values.enable_nfc = ui->enable_nfc->isChecked();
}
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:
private:
void setConfiguration();
- void OnDockedModeChanged(bool last_state, bool new_state);
std::unique_ptr<Ui::ConfigureGeneral> ui;
};
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 @@
<x>0</x>
<y>0</y>
<width>300</width>
- <height>377</height>
+ <height>407</height>
</rect>
</property>
<property name="windowTitle">
@@ -72,13 +72,6 @@
<item>
<layout class="QVBoxLayout" name="EmulationVerticalLayout">
<item>
- <widget class="QCheckBox" name="use_docked_mode">
- <property name="text">
- <string>Enable docked mode</string>
- </property>
- </widget>
- </item>
- <item>
<widget class="QCheckBox" name="enable_nfc">
<property name="text">
<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 @@
#include <QMessageBox>
#include <QTimer>
#include "common/param_package.h"
+#include "configuration/configure_touchscreen_advanced.h"
+#include "core/core.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applet_ae.h"
+#include "core/hle/service/am/applet_oe.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/sm/sm.h"
#include "input_common/main.h"
+#include "ui_configure_input.h"
+#include "ui_configure_input_player.h"
+#include "ui_configure_mouse_advanced.h"
+#include "ui_configure_touchscreen_advanced.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input.h"
+#include "yuzu/configuration/configure_input_player.h"
+#include "yuzu/configuration/configure_mouse_advanced.h"
-const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM>
- ConfigureInput::analog_sub_buttons{{
- "up",
- "down",
- "left",
- "right",
- "modifier",
- }};
-
-static QString getKeyName(int key_code) {
- switch (key_code) {
- case Qt::Key_Shift:
- return QObject::tr("Shift");
- case Qt::Key_Control:
- return QObject::tr("Ctrl");
- case Qt::Key_Alt:
- return QObject::tr("Alt");
- case Qt::Key_Meta:
- return "";
- default:
- return QKeySequence(key_code).toString();
- }
-}
+ConfigureInput::ConfigureInput(QWidget* parent)
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
+ ui->setupUi(this);
-static void SetAnalogButton(const Common::ParamPackage& input_param,
- Common::ParamPackage& analog_param, const std::string& button_name) {
- if (analog_param.Get("engine", "") != "analog_from_button") {
- analog_param = {
- {"engine", "analog_from_button"},
- {"modifier_scale", "0.5"},
- };
- }
- analog_param.Set(button_name, input_param.Serialize());
-}
+ players_controller = {
+ ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox,
+ ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox,
+ };
-static QString ButtonToText(const Common::ParamPackage& param) {
- if (!param.Has("engine")) {
- return QObject::tr("[not set]");
- } else if (param.Get("engine", "") == "keyboard") {
- return getKeyName(param.Get("code", 0));
- } else if (param.Get("engine", "") == "sdl") {
- if (param.Has("hat")) {
- return QString(QObject::tr("Hat %1 %2"))
- .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
- }
- if (param.Has("axis")) {
- return QString(QObject::tr("Axis %1%2"))
- .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
- }
- if (param.Has("button")) {
- return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
- }
- return QString();
- } else {
- return QObject::tr("[unknown]");
- }
-};
-
-static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
- if (!param.Has("engine")) {
- return QObject::tr("[not set]");
- } else if (param.Get("engine", "") == "analog_from_button") {
- return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
- } else if (param.Get("engine", "") == "sdl") {
- if (dir == "modifier") {
- return QString(QObject::tr("[unused]"));
- }
+ players_configure = {
+ ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure,
+ ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure,
+ };
- if (dir == "left" || dir == "right") {
- return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str());
- } else if (dir == "up" || dir == "down") {
- return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str());
- }
- return QString();
- } else {
- return QObject::tr("[unknown]");
+ for (auto* controller_box : players_controller) {
+ controller_box->addItems({"None", "Pro Controller", "Dual Joycons", "Single Right Joycon",
+ "Single Left Joycon"});
}
-};
-ConfigureInput::ConfigureInput(QWidget* parent)
- : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
- timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
+ this->loadConfiguration();
+ updateUIEnabled();
- ui->setupUi(this);
- setFocusPolicy(Qt::ClickFocus);
-
- button_map = {
- ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
- ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
- ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
- ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
- ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown,
- ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
- ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
- };
+ connect(ui->restore_defaults_button, &QPushButton::pressed, this,
+ &ConfigureInput::restoreDefaults);
- analog_map_buttons = {{
- {
- ui->buttonLStickUp,
- ui->buttonLStickDown,
- ui->buttonLStickLeft,
- ui->buttonLStickRight,
- ui->buttonLStickMod,
- },
- {
- ui->buttonRStickUp,
- ui->buttonRStickDown,
- ui->buttonRStickLeft,
- ui->buttonRStickRight,
- ui->buttonRStickMod,
- },
- }};
-
- analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
-
- for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
- if (!button_map[button_id])
- continue;
- button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(button_map[button_id], &QPushButton::released, [=]() {
- handleClick(
- button_map[button_id],
- [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
- InputCommon::Polling::DeviceType::Button);
- });
- connect(button_map[button_id], &QPushButton::customContextMenuRequested,
- [=](const QPoint& menu_location) {
- QMenu context_menu;
- context_menu.addAction(tr("Clear"), [&] {
- buttons_param[button_id].Clear();
- button_map[button_id]->setText(tr("[not set]"));
- });
- context_menu.addAction(tr("Restore Default"), [&] {
- buttons_param[button_id] = Common::ParamPackage{
- InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
- button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
- });
- context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
- });
- }
+ for (auto* enabled : players_controller)
+ connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
+ &ConfigureInput::updateUIEnabled);
+ connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
+ connect(ui->handheld_connected, &QCheckBox::stateChanged, this,
+ &ConfigureInput::updateUIEnabled);
+ connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
+ connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
+ connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
+ connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
+ &ConfigureInput::updateUIEnabled);
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
- if (!analog_map_buttons[analog_id][sub_button_id])
- continue;
- analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
- Qt::CustomContextMenu);
- connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() {
- handleClick(analog_map_buttons[analog_id][sub_button_id],
- [=](const Common::ParamPackage& params) {
- SetAnalogButton(params, analogs_param[analog_id],
- analog_sub_buttons[sub_button_id]);
- },
- InputCommon::Polling::DeviceType::Button);
- });
- connect(analog_map_buttons[analog_id][sub_button_id],
- &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
- QMenu context_menu;
- context_menu.addAction(tr("Clear"), [&] {
- analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
- analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
- });
- context_menu.addAction(tr("Restore Default"), [&] {
- Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
- Config::default_analogs[analog_id][sub_button_id])};
- SetAnalogButton(params, analogs_param[analog_id],
- analog_sub_buttons[sub_button_id]);
- analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
- analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
- });
- context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
- menu_location));
- });
- }
- connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
- QMessageBox::information(this, tr("Information"),
- tr("After pressing OK, first move your joystick horizontally, "
- "and then vertically."));
- handleClick(
- analog_map_stick[analog_id],
- [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
- InputCommon::Polling::DeviceType::Analog);
- });
+ for (std::size_t i = 0; i < players_configure.size(); ++i) {
+ connect(players_configure[i], &QPushButton::pressed, this,
+ [this, i]() { CallConfigureDialog<ConfigureInputPlayer>(i, false); });
}
- connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
- connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
-
- timeout_timer->setSingleShot(true);
- connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
+ connect(ui->handheld_configure, &QPushButton::pressed, this,
+ [this]() { CallConfigureDialog<ConfigureInputPlayer>(8, false); });
- connect(poll_timer.get(), &QTimer::timeout, [this]() {
- Common::ParamPackage params;
- for (auto& poller : device_pollers) {
- params = poller->GetNextInput();
- if (params.Has("engine")) {
- setPollingResult(params, false);
- return;
- }
- }
- });
+ connect(ui->debug_configure, &QPushButton::pressed, this,
+ [this]() { CallConfigureDialog<ConfigureInputPlayer>(9, true); });
- this->loadConfiguration();
+ connect(ui->mouse_advanced, &QPushButton::pressed, this,
+ [this]() { CallConfigureDialog<ConfigureMouseAdvanced>(); });
- // TODO(wwylele): enable this when we actually emulate it
- ui->buttonHome->setEnabled(false);
+ connect(ui->touchscreen_advanced, &QPushButton::pressed, this,
+ [this]() { CallConfigureDialog<ConfigureTouchscreenAdvanced>(); });
}
-void ConfigureInput::applyConfiguration() {
- std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.buttons.begin(),
- [](const Common::ParamPackage& param) { return param.Serialize(); });
- std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(),
- [](const Common::ParamPackage& param) { return param.Serialize(); });
-}
+template <typename Dialog, typename... Args>
+void ConfigureInput::CallConfigureDialog(Args&&... args) {
+ this->applyConfiguration();
+ Dialog dialog(this, std::forward<Args>(args)...);
-void ConfigureInput::loadConfiguration() {
- std::transform(Settings::values.buttons.begin(), Settings::values.buttons.end(),
- buttons_param.begin(),
- [](const std::string& str) { return Common::ParamPackage(str); });
- std::transform(Settings::values.analogs.begin(), Settings::values.analogs.end(),
- analogs_param.begin(),
- [](const std::string& str) { return Common::ParamPackage(str); });
- updateButtonLabels();
+ const auto res = dialog.exec();
+ if (res == QDialog::Accepted) {
+ dialog.applyConfiguration();
+ }
}
-void ConfigureInput::restoreDefaults() {
- for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
- buttons_param[button_id] = Common::ParamPackage{
- InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
+void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) {
+ if (last_state == new_state) {
+ return;
}
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
- Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
- Config::default_analogs[analog_id][sub_button_id])};
- SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
- }
+ Core::System& system{Core::System::GetInstance()};
+ if (!system.IsPoweredOn()) {
+ return;
}
- updateButtonLabels();
-}
+ Service::SM::ServiceManager& sm = system.ServiceManager();
-void ConfigureInput::ClearAll() {
- for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
- if (button_map[button_id] && button_map[button_id]->isEnabled())
- buttons_param[button_id].Clear();
- }
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
- if (analog_map_buttons[analog_id][sub_button_id] &&
- analog_map_buttons[analog_id][sub_button_id]->isEnabled())
- analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
- }
- }
- updateButtonLabels();
-}
+ // Message queue is shared between these services, we just need to signal an operation
+ // change to one and it will handle both automatically
+ auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
+ auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
+ bool has_signalled = false;
-void ConfigureInput::updateButtonLabels() {
- for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
- button_map[button]->setText(ButtonToText(buttons_param[button]));
+ if (applet_oe != nullptr) {
+ applet_oe->GetMessageQueue()->OperationModeChanged();
+ has_signalled = true;
}
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
- if (analog_map_buttons[analog_id][sub_button_id]) {
- analog_map_buttons[analog_id][sub_button_id]->setText(
- AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
- }
- }
- analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
+ if (applet_ae != nullptr && !has_signalled) {
+ applet_ae->GetMessageQueue()->OperationModeChanged();
}
}
-void ConfigureInput::handleClick(QPushButton* button,
- std::function<void(const Common::ParamPackage&)> new_input_setter,
- InputCommon::Polling::DeviceType type) {
- button->setText(tr("[press key]"));
- button->setFocus();
-
- input_setter = new_input_setter;
-
- device_pollers = InputCommon::Polling::GetPollers(type);
+void ConfigureInput::applyConfiguration() {
+ for (std::size_t i = 0; i < players_controller.size(); ++i) {
+ const auto controller_type_index = players_controller[i]->currentIndex();
- // Keyboard keys can only be used as button devices
- want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
+ Settings::values.players[i].connected = controller_type_index != 0;
- for (auto& poller : device_pollers) {
- poller->Start();
+ if (controller_type_index > 0) {
+ Settings::values.players[i].type =
+ static_cast<Settings::ControllerType>(controller_type_index - 1);
+ } else {
+ Settings::values.players[i].type = Settings::ControllerType::DualJoycon;
+ }
}
- grabKeyboard();
- grabMouse();
- timeout_timer->start(5000); // Cancel after 5 seconds
- poll_timer->start(200); // Check for new inputs every 200ms
+ const bool pre_docked_mode = Settings::values.use_docked_mode;
+ Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
+ OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
+ Settings::values
+ .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
+ .connected = ui->handheld_connected->isChecked();
+ Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
+ Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
+ Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
+ Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
}
-void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool abort) {
- releaseKeyboard();
- releaseMouse();
- timeout_timer->stop();
- poll_timer->stop();
- for (auto& poller : device_pollers) {
- poller->Stop();
+void ConfigureInput::updateUIEnabled() {
+ bool hit_disabled = false;
+ for (auto* player : players_controller) {
+ player->setDisabled(hit_disabled);
+ if (hit_disabled)
+ player->setCurrentIndex(0);
+ if (!hit_disabled && player->currentIndex() == 0)
+ hit_disabled = true;
}
- if (!abort) {
- (*input_setter)(params);
+ for (std::size_t i = 0; i < players_controller.size(); ++i) {
+ players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0);
}
- updateButtonLabels();
- input_setter = {};
+ ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked());
+ ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() &&
+ !ui->use_docked_mode->isChecked());
+ ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
+ ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
+ ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
}
-void ConfigureInput::keyPressEvent(QKeyEvent* event) {
- if (!input_setter || !event)
- return;
+void ConfigureInput::loadConfiguration() {
+ std::stable_partition(
+ Settings::values.players.begin(),
+ Settings::values.players.begin() +
+ Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
+ [](const auto& player) { return player.connected; });
+
+ for (std::size_t i = 0; i < players_controller.size(); ++i) {
+ const auto connected = Settings::values.players[i].connected;
+ players_controller[i]->setCurrentIndex(
+ connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
+ }
+
+ ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
+ ui->handheld_connected->setChecked(
+ Settings::values
+ .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
+ .connected);
+ ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
+ ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
+ ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
+ ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
+
+ updateUIEnabled();
+}
- if (event->key() != Qt::Key_Escape) {
- if (want_keyboard_keys) {
- setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
- false);
- } else {
- // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
- return;
- }
+void ConfigureInput::restoreDefaults() {
+ players_controller[0]->setCurrentIndex(2);
+
+ for (std::size_t i = 1; i < players_controller.size(); ++i) {
+ players_controller[i]->setCurrentIndex(0);
}
- setPollingResult({}, true);
+
+ ui->use_docked_mode->setCheckState(Qt::Unchecked);
+ ui->handheld_connected->setCheckState(Qt::Unchecked);
+ ui->mouse_enabled->setCheckState(Qt::Unchecked);
+ ui->keyboard_enabled->setCheckState(Qt::Unchecked);
+ ui->debug_enabled->setCheckState(Qt::Unchecked);
+ ui->touchscreen_enabled->setCheckState(Qt::Checked);
+ updateUIEnabled();
}
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 @@
#include "core/settings.h"
#include "input_common/main.h"
#include "ui_configure_input.h"
+#include "yuzu/configuration/config.h"
class QPushButton;
class QString;
@@ -37,57 +38,20 @@ public:
void applyConfiguration();
private:
- std::unique_ptr<Ui::ConfigureInput> ui;
-
- std::unique_ptr<QTimer> timeout_timer;
- std::unique_ptr<QTimer> poll_timer;
-
- /// This will be the the setting function when an input is awaiting configuration.
- std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
-
- std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
- std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
-
- static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
-
- /// Each button input is represented by a QPushButton.
- std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
+ void updateUIEnabled();
- /// A group of five QPushButtons represent one analog input. The buttons each represent up,
- /// down, left, right, and modifier, respectively.
- std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
- analog_map_buttons;
+ template <typename Dialog, typename... Args>
+ void CallConfigureDialog(Args&&... args);
- /// Analog inputs are also represented each with a single button, used to configure with an
- /// actual analog stick
- std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
-
- static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
-
- std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
-
- /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
- /// keyboard events are ignored.
- bool want_keyboard_keys = false;
+ void OnDockedModeChanged(bool last_state, bool new_state);
/// Load configuration settings.
void loadConfiguration();
/// Restore all buttons to their default values.
void restoreDefaults();
- /// Clear all input configuration
- void ClearAll();
- /// Update UI to reflect current configuration.
- void updateButtonLabels();
-
- /// Called when the button was pressed.
- void handleClick(QPushButton* button,
- std::function<void(const Common::ParamPackage&)> new_input_setter,
- InputCommon::Polling::DeviceType type);
-
- /// Finish polling and configure input using the input_setter
- void setPollingResult(const Common::ParamPackage& params, bool abort);
+ std::unique_ptr<Ui::ConfigureInput> ui;
- /// Handle key press events.
- void keyPressEvent(QKeyEvent* event) override;
+ std::array<QComboBox*, 8> players_controller;
+ std::array<QPushButton*, 8> players_configure;
};
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 @@
<rect>
<x>0</x>
<y>0</y>
- <width>343</width>
- <height>677</height>
+ <width>473</width>
+ <height>685</height>
</rect>
</property>
<property name="windowTitle">
@@ -15,740 +15,470 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
- <layout class="QGridLayout" name="buttons">
- <item row="3" column="1">
- <widget class="QGroupBox" name="misc">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="gridGroupBox">
<property name="title">
- <string>Misc.</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
+ <string>Players</string>
</property>
- <layout class="QGridLayout" name="gridLayout_6">
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout">
- <item>
- <widget class="QLabel" name="labelMinus">
- <property name="text">
- <string>Minus:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonMinus">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="2">
+ <widget class="QComboBox" name="player1_combobox">
+ <property name="minimumSize">
+ <size>
+ <width>110</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
</item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
- <item>
- <widget class="QLabel" name="labelPlus">
- <property name="text">
- <string>Plus:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonPlus">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="1" column="3">
+ <widget class="QPushButton" name="player1_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
</item>
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
- <item>
- <widget class="QLabel" name="labelHome">
- <property name="text">
- <string>Home:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonHome">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Controller Type</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
</item>
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
- <item>
- <widget class="QLabel" name="labelScrCap">
- <property name="text">
- <string>Screen
-Capture:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonScreenshot">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="2" column="2">
+ <widget class="QComboBox" name="player2_combobox">
+ <property name="minimumSize">
+ <size>
+ <width>110</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
</item>
- <item row="2" column="1">
- <spacer name="verticalSpacer">
+ <item row="3" column="2">
+ <widget class="QComboBox" name="player3_combobox">
+ <property name="minimumSize">
+ <size>
+ <width>110</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QComboBox" name="player4_combobox">
+ <property name="minimumSize">
+ <size>
+ <width>110</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="2">
+ <widget class="QComboBox" name="player5_combobox">
+ <property name="minimumSize">
+ <size>
+ <width>110</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="2">
+ <widget class="QComboBox" name="player6_combobox">
+ <property name="minimumSize">
+ <size>
+ <width>110</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="2">
+ <widget class="QComboBox" name="player7_combobox">
+ <property name="minimumSize">
+ <size>
+ <width>110</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="2">
+ <widget class="QComboBox" name="player8_combobox">
+ <property name="minimumSize">
+ <size>
+ <width>110</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QPushButton" name="player2_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="3">
+ <widget class="QPushButton" name="player3_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3">
+ <widget class="QPushButton" name="player4_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="3">
+ <widget class="QPushButton" name="player5_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="3">
+ <widget class="QPushButton" name="player6_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="3">
+ <widget class="QPushButton" name="player7_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="3">
+ <widget class="QPushButton" name="player8_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <spacer name="horizontalSpacer">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
- <width>20</width>
- <height>40</height>
+ <width>40</width>
+ <height>20</height>
</size>
</property>
</spacer>
</item>
- </layout>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QGroupBox" name="faceButtons">
- <property name="title">
- <string>Face Buttons</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
- <item>
- <widget class="QLabel" name="labelA">
- <property name="text">
- <string>A:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonA">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="0" column="4">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
</item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
- <item>
- <widget class="QLabel" name="labelB">
- <property name="text">
- <string>B:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonB">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_3">
+ <property name="minimumSize">
+ <size>
+ <width>55</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Player 1</string>
+ </property>
+ </widget>
</item>
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
- <item>
- <widget class="QLabel" name="labelX">
- <property name="text">
- <string>X:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonX">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="2" column="1">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Player 2</string>
+ </property>
+ </widget>
</item>
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
- <item>
- <widget class="QLabel" name="labelY">
- <property name="text">
- <string>Y:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonY">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="3" column="1">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Player 3</string>
+ </property>
+ </widget>
</item>
- </layout>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QGroupBox" name="Dpad">
- <property name="title">
- <string>Directional Pad</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
- <item>
- <widget class="QLabel" name="labelDpadUp">
- <property name="text">
- <string>Up:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonDpadUp">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="4" column="1">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Player 4</string>
+ </property>
+ </widget>
</item>
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
- <item>
- <widget class="QLabel" name="labelDpadDown">
- <property name="text">
- <string>Down:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonDpadDown">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="5" column="1">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Player 5</string>
+ </property>
+ </widget>
</item>
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
- <item>
- <widget class="QLabel" name="labelDpadLeft">
- <property name="text">
- <string>Left:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonDpadLeft">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="6" column="1">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Player 6</string>
+ </property>
+ </widget>
</item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
- <item>
- <widget class="QLabel" name="labelDpadRight">
- <property name="text">
- <string>Right:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonDpadRight">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="7" column="1">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Player 7</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="1">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>Player 8</string>
+ </property>
+ </widget>
</item>
</layout>
</widget>
</item>
- <item row="3" column="0">
- <widget class="QGroupBox" name="shoulderButtons">
+ <item>
+ <widget class="QGroupBox" name="gridGroupBox">
<property name="title">
- <string>Shoulder Buttons</string>
- </property>
- <property name="flat">
- <bool>false</bool>
+ <string>Handheld</string>
</property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
- <item>
- <widget class="QLabel" name="labelL">
- <property name="text">
- <string>L:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonL">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="2">
+ <spacer name="horizontalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>72</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
</item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
- <item>
- <widget class="QLabel" name="labelR">
- <property name="text">
- <string>R:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonR">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="1" column="4">
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="3">
+ <widget class="QPushButton" name="handheld_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
</item>
<item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
- <item>
- <widget class="QLabel" name="labelZL">
- <property name="text">
- <string>ZL:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonZL">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
</item>
<item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
- <item>
- <widget class="QLabel" name="labelZR">
- <property name="text">
- <string>ZR:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonZR">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="2" column="0">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
- <item>
- <widget class="QLabel" name="labelSL">
- <property name="text">
- <string>SL:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonSL">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <widget class="QCheckBox" name="handheld_connected">
+ <property name="text">
+ <string>Joycons Docked</string>
+ </property>
+ </widget>
</item>
- <item row="2" column="1">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
- <item>
- <widget class="QLabel" name="labelSR">
- <property name="text">
- <string>SR:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonSR">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="use_docked_mode">
+ <property name="text">
+ <string>Use Docked Mode</string>
+ </property>
+ </widget>
</item>
</layout>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QGroupBox" name="RStick">
+ <item>
+ <widget class="QGroupBox" name="gridGroupBox">
<property name="title">
- <string>Right Stick</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
- </property>
- <property name="flat">
- <bool>false</bool>
+ <string>Other</string>
</property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_5">
+ <layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout">
- <item>
- <widget class="QLabel" name="labelRStickDown">
- <property name="text">
- <string>Down:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickDown">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
- <item>
- <widget class="QLabel" name="labelRStickRight">
- <property name="text">
- <string>Right:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickRight">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="3" column="0" colspan="2">
- <widget class="QPushButton" name="buttonRStickAnalog">
+ <widget class="QCheckBox" name="keyboard_enabled">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>23</height>
+ </size>
+ </property>
<property name="text">
- <string>Set Analog Stick</string>
+ <string>Keyboard</string>
</property>
</widget>
</item>
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
- <item>
- <widget class="QLabel" name="labelRStickLeft">
- <property name="text">
- <string>Left:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickLeft">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
- <item>
- <widget class="QLabel" name="labelRStickUp">
- <property name="text">
- <string>Up:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickUp">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="2" column="0">
- <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
- <item>
- <widget class="QLabel" name="labelRStickPressed">
- <property name="text">
- <string>Pressed:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStick">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
<item row="2" column="1">
- <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout">
- <item>
- <widget class="QLabel" name="labelRStickMod">
- <property name="text">
- <string>Modifier:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickMod">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <widget class="QCheckBox" name="debug_enabled">
+ <property name="text">
+ <string>Debug Controller</string>
+ </property>
+ </widget>
</item>
- </layout>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QGroupBox" name="LStick">
- <property name="title">
- <string>Left Stick</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_4">
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
- <item>
- <widget class="QLabel" name="labelLStickDown">
- <property name="text">
- <string>Down:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickDown">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="4" column="0" colspan="2">
- <widget class="QPushButton" name="buttonLStickAnalog">
+ <item row="3" column="1">
+ <widget class="QCheckBox" name="touchscreen_enabled">
<property name="text">
- <string>Set Analog Stick</string>
+ <string>Touchscreen</string>
</property>
</widget>
</item>
<item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
- <item>
- <widget class="QLabel" name="labelLStickRight">
- <property name="text">
- <string>Right:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickRight">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <widget class="QCheckBox" name="mouse_enabled">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Mouse</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <spacer name="horizontalSpacer_7">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="2">
+ <spacer name="horizontalSpacer_8">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>76</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
</item>
<item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
- <item>
- <widget class="QLabel" name="labelLStickLeft">
- <property name="text">
- <string>Left:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickLeft">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <spacer name="horizontalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
</item>
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout">
- <item>
- <widget class="QLabel" name="labelLStickUp">
- <property name="text">
- <string>Up:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickUp">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="3" column="0">
- <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
- <item>
- <widget class="QLabel" name="labelLStickMod">
- <property name="text">
- <string>Modifier:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickMod">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="3" column="3">
+ <widget class="QPushButton" name="touchscreen_advanced">
+ <property name="text">
+ <string>Advanced</string>
+ </property>
+ </widget>
</item>
- <item row="3" column="1">
- <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0">
- <item>
- <widget class="QLabel" name="labelLStickPressed">
- <property name="text">
- <string>Pressed:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStick">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item row="2" column="3">
+ <widget class="QPushButton" name="debug_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QPushButton" name="mouse_advanced">
+ <property name="text">
+ <string>Advanced</string>
+ </property>
+ </widget>
</item>
</layout>
</widget>
</item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout">
<item>
- <spacer name="horizontalSpacer">
+ <spacer name="verticalSpacer">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
- <width>40</width>
- <height>20</height>
+ <width>20</width>
+ <height>40</height>
</size>
</property>
</spacer>
</item>
<item>
- <widget class="QPushButton" name="buttonClearAll">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="sizeIncrement">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="baseSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
- </property>
- <property name="text">
- <string>Clear All</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRestoreDefaults">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="sizeIncrement">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="baseSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
- </property>
- <property name="text">
- <string>Restore Defaults</string>
- </property>
- </widget>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="restore_defaults_button">
+ <property name="text">
+ <string>Restore Defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_9">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
</item>
</layout>
</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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <QColorDialog>
+#include <QMenu>
+#include <QMessageBox>
+#include <QTimer>
+#include "common/assert.h"
+#include "common/param_package.h"
+#include "input_common/main.h"
+#include "ui_configure_input_player.h"
+#include "yuzu/configuration/config.h"
+#include "yuzu/configuration/configure_input_player.h"
+
+const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
+ ConfigureInputPlayer::analog_sub_buttons{{
+ "up",
+ "down",
+ "left",
+ "right",
+ "modifier",
+ }};
+
+static void MoveGridElement(QGridLayout* grid, int row_old, int column_old, int row_new,
+ int column_new) {
+ const auto item = grid->itemAtPosition(row_old, column_old);
+ // grid->removeItem(item);
+ grid->addItem(item, row_new, column_new);
+}
+
+static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) {
+ const int index1 = grid->indexOf(item);
+ const int index2 = grid->indexOf(onTopOf);
+ int row, column, rowSpan, columnSpan;
+ grid->getItemPosition(index2, &row, &column, &rowSpan, &columnSpan);
+ grid->takeAt(index1);
+ grid->addWidget(item, row, column, rowSpan, columnSpan);
+}
+
+static QString GetKeyName(int key_code) {
+ switch (key_code) {
+ case Qt::Key_Shift:
+ return QObject::tr("Shift");
+ case Qt::Key_Control:
+ return QObject::tr("Ctrl");
+ case Qt::Key_Alt:
+ return QObject::tr("Alt");
+ case Qt::Key_Meta:
+ return "";
+ default:
+ return QKeySequence(key_code).toString();
+ }
+}
+
+static void SetAnalogButton(const Common::ParamPackage& input_param,
+ Common::ParamPackage& analog_param, const std::string& button_name) {
+ if (analog_param.Get("engine", "") != "analog_from_button") {
+ analog_param = {
+ {"engine", "analog_from_button"},
+ {"modifier_scale", "0.5"},
+ };
+ }
+ analog_param.Set(button_name, input_param.Serialize());
+}
+
+static QString ButtonToText(const Common::ParamPackage& param) {
+ if (!param.Has("engine")) {
+ return QObject::tr("[not set]");
+ } else if (param.Get("engine", "") == "keyboard") {
+ return GetKeyName(param.Get("code", 0));
+ } else if (param.Get("engine", "") == "sdl") {
+ if (param.Has("hat")) {
+ return QString(QObject::tr("Hat %1 %2"))
+ .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
+ }
+ if (param.Has("axis")) {
+ return QString(QObject::tr("Axis %1%2"))
+ .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
+ }
+ if (param.Has("button")) {
+ return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
+ }
+ return QString();
+ } else {
+ return QObject::tr("[unknown]");
+ }
+};
+
+static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
+ if (!param.Has("engine")) {
+ return QObject::tr("[not set]");
+ } else if (param.Get("engine", "") == "analog_from_button") {
+ return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
+ } else if (param.Get("engine", "") == "sdl") {
+ if (dir == "modifier") {
+ return QString(QObject::tr("[unused]"));
+ }
+
+ if (dir == "left" || dir == "right") {
+ return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str());
+ } else if (dir == "up" || dir == "down") {
+ return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str());
+ }
+ return QString();
+ } else {
+ return QObject::tr("[unknown]");
+ }
+};
+
+ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()),
+ timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
+ player_index(player_index), debug(debug) {
+
+ ui->setupUi(this);
+ setFocusPolicy(Qt::ClickFocus);
+
+ button_map = {
+ ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
+ ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
+ ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
+ ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
+ ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown,
+ ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
+ ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
+ };
+
+ analog_map_buttons = {{
+ {
+ ui->buttonLStickUp,
+ ui->buttonLStickDown,
+ ui->buttonLStickLeft,
+ ui->buttonLStickRight,
+ ui->buttonLStickMod,
+ },
+ {
+ ui->buttonRStickUp,
+ ui->buttonRStickDown,
+ ui->buttonRStickLeft,
+ ui->buttonRStickRight,
+ ui->buttonRStickMod,
+ },
+ }};
+
+ debug_hidden = {
+ ui->buttonSL, ui->labelSL,
+ ui->buttonSR, ui->labelSR,
+ ui->buttonLStick, ui->labelLStickPressed,
+ ui->buttonRStick, ui->labelRStickPressed,
+ ui->buttonHome, ui->labelHome,
+ ui->buttonScreenshot, ui->labelScreenshot,
+ };
+
+ auto layout = Settings::values.players[player_index].type;
+ if (debug)
+ layout = Settings::ControllerType::DualJoycon;
+
+ switch (layout) {
+ case Settings::ControllerType::ProController:
+ case Settings::ControllerType::DualJoycon:
+ layout_hidden = {
+ ui->buttonSL,
+ ui->labelSL,
+ ui->buttonSR,
+ ui->labelSR,
+ };
+ break;
+ case Settings::ControllerType::LeftJoycon:
+ layout_hidden = {
+ ui->right_body_button,
+ ui->right_buttons_button,
+ ui->right_body_label,
+ ui->right_buttons_label,
+ ui->buttonR,
+ ui->labelR,
+ ui->buttonZR,
+ ui->labelZR,
+ ui->labelHome,
+ ui->buttonHome,
+ ui->buttonPlus,
+ ui->labelPlus,
+ ui->RStick,
+ ui->faceButtons,
+ };
+ break;
+ case Settings::ControllerType::RightJoycon:
+ layout_hidden = {
+ ui->left_body_button, ui->left_buttons_button,
+ ui->left_body_label, ui->left_buttons_label,
+ ui->buttonL, ui->labelL,
+ ui->buttonZL, ui->labelZL,
+ ui->labelScreenshot, ui->buttonScreenshot,
+ ui->buttonMinus, ui->labelMinus,
+ ui->LStick, ui->Dpad,
+ };
+ break;
+ }
+
+ if (debug || layout == Settings::ControllerType::ProController) {
+ ui->controller_color->hide();
+ } else {
+ if (layout == Settings::ControllerType::LeftJoycon ||
+ layout == Settings::ControllerType::RightJoycon) {
+ ui->horizontalSpacer_4->setGeometry({0, 0, 0, 0});
+
+ LayerGridElements(ui->buttons, ui->shoulderButtons, ui->Dpad);
+ LayerGridElements(ui->buttons, ui->misc, ui->RStick);
+ LayerGridElements(ui->buttons, ui->Dpad, ui->faceButtons);
+ LayerGridElements(ui->buttons, ui->RStick, ui->LStick);
+ }
+ }
+
+ for (auto* widget : layout_hidden)
+ widget->setVisible(false);
+
+ analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
+
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
+ if (!button_map[button_id])
+ continue;
+ button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(button_map[button_id], &QPushButton::released, [=]() {
+ handleClick(
+ button_map[button_id],
+ [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
+ InputCommon::Polling::DeviceType::Button);
+ });
+ connect(button_map[button_id], &QPushButton::customContextMenuRequested,
+ [=](const QPoint& menu_location) {
+ QMenu context_menu;
+ context_menu.addAction(tr("Clear"), [&] {
+ buttons_param[button_id].Clear();
+ button_map[button_id]->setText(tr("[not set]"));
+ });
+ context_menu.addAction(tr("Restore Default"), [&] {
+ buttons_param[button_id] = Common::ParamPackage{
+ InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
+ button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
+ });
+ context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
+ });
+ }
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ if (!analog_map_buttons[analog_id][sub_button_id])
+ continue;
+ analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
+ Qt::CustomContextMenu);
+ connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() {
+ handleClick(analog_map_buttons[analog_id][sub_button_id],
+ [=](const Common::ParamPackage& params) {
+ SetAnalogButton(params, analogs_param[analog_id],
+ analog_sub_buttons[sub_button_id]);
+ },
+ InputCommon::Polling::DeviceType::Button);
+ });
+ connect(analog_map_buttons[analog_id][sub_button_id],
+ &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
+ QMenu context_menu;
+ context_menu.addAction(tr("Clear"), [&] {
+ analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
+ analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
+ });
+ context_menu.addAction(tr("Restore Default"), [&] {
+ Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
+ Config::default_analogs[analog_id][sub_button_id])};
+ SetAnalogButton(params, analogs_param[analog_id],
+ analog_sub_buttons[sub_button_id]);
+ analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
+ analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
+ });
+ context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
+ menu_location));
+ });
+ }
+ connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
+ QMessageBox::information(this, tr("Information"),
+ tr("After pressing OK, first move your joystick horizontally, "
+ "and then vertically."));
+ handleClick(
+ analog_map_stick[analog_id],
+ [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
+ InputCommon::Polling::DeviceType::Analog);
+ });
+ }
+
+ connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
+ connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
+
+ timeout_timer->setSingleShot(true);
+ connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
+
+ connect(poll_timer.get(), &QTimer::timeout, [this]() {
+ Common::ParamPackage params;
+ for (auto& poller : device_pollers) {
+ params = poller->GetNextInput();
+ if (params.Has("engine")) {
+ setPollingResult(params, false);
+ return;
+ }
+ }
+ });
+
+ controller_color_buttons = {
+ ui->left_body_button,
+ ui->left_buttons_button,
+ ui->right_body_button,
+ ui->right_buttons_button,
+ };
+
+ for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) {
+ connect(controller_color_buttons[i], &QPushButton::clicked, this,
+ std::bind(&ConfigureInputPlayer::OnControllerButtonClick, this, i));
+ }
+
+ this->loadConfiguration();
+ this->resize(0, 0);
+
+ // TODO(wwylele): enable this when we actually emulate it
+ ui->buttonHome->setEnabled(false);
+}
+
+ConfigureInputPlayer::~ConfigureInputPlayer() = default;
+
+void ConfigureInputPlayer::applyConfiguration() {
+ auto& buttons =
+ debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons;
+ auto& analogs =
+ debug ? Settings::values.debug_pad_analogs : Settings::values.players[player_index].analogs;
+
+ std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
+ [](const Common::ParamPackage& param) { return param.Serialize(); });
+ std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
+ [](const Common::ParamPackage& param) { return param.Serialize(); });
+
+ if (debug)
+ return;
+
+ std::array<u32, 4> colors{};
+ std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(),
+ [](QColor color) { return color.rgb(); });
+
+ Settings::values.players[player_index].body_color_left = colors[0];
+ Settings::values.players[player_index].button_color_left = colors[1];
+ Settings::values.players[player_index].body_color_right = colors[2];
+ Settings::values.players[player_index].button_color_right = colors[3];
+}
+
+void ConfigureInputPlayer::OnControllerButtonClick(int i) {
+ const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]);
+ if (!new_bg_color.isValid())
+ return;
+ controller_colors[i] = new_bg_color;
+ controller_color_buttons[i]->setStyleSheet(
+ QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
+}
+
+void ConfigureInputPlayer::loadConfiguration() {
+ if (debug) {
+ std::transform(Settings::values.debug_pad_buttons.begin(),
+ Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
+ [](const std::string& str) { return Common::ParamPackage(str); });
+ std::transform(Settings::values.debug_pad_analogs.begin(),
+ Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
+ [](const std::string& str) { return Common::ParamPackage(str); });
+ } else {
+ std::transform(Settings::values.players[player_index].buttons.begin(),
+ Settings::values.players[player_index].buttons.end(), buttons_param.begin(),
+ [](const std::string& str) { return Common::ParamPackage(str); });
+ std::transform(Settings::values.players[player_index].analogs.begin(),
+ Settings::values.players[player_index].analogs.end(), analogs_param.begin(),
+ [](const std::string& str) { return Common::ParamPackage(str); });
+ }
+
+ updateButtonLabels();
+
+ if (debug)
+ return;
+
+ std::array<u32, 4> colors = {
+ Settings::values.players[player_index].body_color_left,
+ Settings::values.players[player_index].button_color_left,
+ Settings::values.players[player_index].body_color_right,
+ Settings::values.players[player_index].button_color_right,
+ };
+
+ std::transform(colors.begin(), colors.end(), controller_colors.begin(),
+ [](u32 rgb) { return QColor::fromRgb(rgb); });
+
+ for (std::size_t i = 0; i < colors.size(); ++i) {
+ controller_color_buttons[i]->setStyleSheet(
+ QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
+ }
+}
+
+void ConfigureInputPlayer::restoreDefaults() {
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
+ buttons_param[button_id] = Common::ParamPackage{
+ InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
+ }
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
+ Config::default_analogs[analog_id][sub_button_id])};
+ SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
+ }
+ }
+ updateButtonLabels();
+}
+
+void ConfigureInputPlayer::ClearAll() {
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
+ if (button_map[button_id] && button_map[button_id]->isEnabled())
+ buttons_param[button_id].Clear();
+ }
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ if (analog_map_buttons[analog_id][sub_button_id] &&
+ analog_map_buttons[analog_id][sub_button_id]->isEnabled())
+ analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
+ }
+ }
+
+ updateButtonLabels();
+}
+
+void ConfigureInputPlayer::updateButtonLabels() {
+ for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
+ button_map[button]->setText(ButtonToText(buttons_param[button]));
+ }
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ if (analog_map_buttons[analog_id][sub_button_id]) {
+ analog_map_buttons[analog_id][sub_button_id]->setText(
+ AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
+ }
+ }
+ analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
+ }
+}
+
+void ConfigureInputPlayer::handleClick(
+ QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
+ InputCommon::Polling::DeviceType type) {
+ button->setText(tr("[press key]"));
+ button->setFocus();
+
+ const auto iter = std::find(button_map.begin(), button_map.end(), button);
+ ASSERT(iter != button_map.end());
+ const auto index = std::distance(button_map.begin(), iter);
+ ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
+
+ input_setter = new_input_setter;
+
+ device_pollers = InputCommon::Polling::GetPollers(type);
+
+ // Keyboard keys can only be used as button devices
+ want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
+
+ for (auto& poller : device_pollers) {
+ poller->Start();
+ }
+
+ grabKeyboard();
+ grabMouse();
+ timeout_timer->start(5000); // Cancel after 5 seconds
+ poll_timer->start(200); // Check for new inputs every 200ms
+}
+
+void ConfigureInputPlayer::setPollingResult(const Common::ParamPackage& params, bool abort) {
+ releaseKeyboard();
+ releaseMouse();
+ timeout_timer->stop();
+ poll_timer->stop();
+ for (auto& poller : device_pollers) {
+ poller->Stop();
+ }
+
+ if (!abort) {
+ (*input_setter)(params);
+ }
+
+ updateButtonLabels();
+ input_setter = std::nullopt;
+}
+
+void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
+ if (!input_setter || !event)
+ return;
+
+ if (event->key() != Qt::Key_Escape) {
+ if (want_keyboard_keys) {
+ setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
+ false);
+ } else {
+ // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
+ return;
+ }
+ }
+ setPollingResult({}, true);
+}
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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <QDialog>
+#include <QKeyEvent>
+#include "common/param_package.h"
+#include "core/settings.h"
+#include "input_common/main.h"
+#include "ui_configure_input.h"
+
+class QPushButton;
+class QString;
+class QTimer;
+
+namespace Ui {
+class ConfigureInputPlayer;
+}
+
+class ConfigureInputPlayer : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug = false);
+ ~ConfigureInputPlayer() override;
+
+ /// Save all button configurations to settings file
+ void applyConfiguration();
+
+private:
+ std::unique_ptr<Ui::ConfigureInputPlayer> ui;
+
+ u8 player_index;
+ bool debug;
+
+ std::unique_ptr<QTimer> timeout_timer;
+ std::unique_ptr<QTimer> poll_timer;
+
+ /// This will be the the setting function when an input is awaiting configuration.
+ std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
+
+ std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
+ std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
+
+ static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
+
+ /// Each button input is represented by a QPushButton.
+ std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
+
+ std::vector<QWidget*> debug_hidden;
+ std::vector<QWidget*> layout_hidden;
+
+ /// A group of five QPushButtons represent one analog input. The buttons each represent up,
+ /// down, left, right, and modifier, respectively.
+ std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
+ analog_map_buttons;
+
+ /// Analog inputs are also represented each with a single button, used to configure with an
+ /// actual analog stick
+ std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
+
+ static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
+
+ std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
+
+ /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
+ /// keyboard events are ignored.
+ bool want_keyboard_keys = false;
+
+ std::array<QPushButton*, 4> controller_color_buttons;
+ std::array<QColor, 4> controller_colors;
+
+ void OnControllerButtonClick(int i);
+
+ /// Load configuration settings.
+ void loadConfiguration();
+ /// Restore all buttons to their default values.
+ void restoreDefaults();
+ /// Clear all input configuration
+ void ClearAll();
+
+ /// Update UI to reflect current configuration.
+ void updateButtonLabels();
+
+ /// Called when the button was pressed.
+ void handleClick(QPushButton* button,
+ std::function<void(const Common::ParamPackage&)> new_input_setter,
+ InputCommon::Polling::DeviceType type);
+
+ /// Finish polling and configure input using the input_setter
+ void setPollingResult(const Common::ParamPackage& params, bool abort);
+
+ /// Handle key press events.
+ void keyPressEvent(QKeyEvent* event) override;
+};
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureInputPlayer</class>
+ <widget class="QDialog" name="ConfigureInputPlayer">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>408</width>
+ <height>731</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Input</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <layout class="QGridLayout" name="buttons">
+ <item row="1" column="1">
+ <widget class="QGroupBox" name="RStick">
+ <property name="title">
+ <string>Right Stick</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonRStickDownHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelRStickDown">
+ <property name="text">
+ <string>Down:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonRStickDown">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonRStickRightHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelRStickRight">
+ <property name="text">
+ <string>Right:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonRStickRight">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="QPushButton" name="buttonRStickAnalog">
+ <property name="text">
+ <string>Set Analog Stick</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonRStickLeftHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelRStickLeft">
+ <property name="text">
+ <string>Left:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonRStickLeft">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonRStickUpHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelRStickUp">
+ <property name="text">
+ <string>Up:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonRStickUp">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0">
+ <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonRStickPressedHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelRStickPressed">
+ <property name="text">
+ <string>Pressed:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonRStick">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="1">
+ <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonRStickModHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelRStickMod">
+ <property name="text">
+ <string>Modifier:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonRStickMod">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QGroupBox" name="Dpad">
+ <property name="title">
+ <string>Directional Pad</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="0">
+ <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonDpadUpHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelDpadUp">
+ <property name="text">
+ <string>Up:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonDpadUp">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonDpadDownHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelDpadDown">
+ <property name="text">
+ <string>Down:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonDpadDown">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0">
+ <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonDpadLeftHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelDpadLeft">
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Left:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonDpadLeft">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonDpadRightHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelDpadRight">
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Right:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonDpadRight">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="faceButtons">
+ <property name="title">
+ <string>Face Buttons</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonFaceButtonsAHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelA">
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>A:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonA">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonFaceButtonsBHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelB">
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>B:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonB">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonFaceButtonsXHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelX">
+ <property name="text">
+ <string>X:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonX">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonFaceButtonsYHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelY">
+ <property name="text">
+ <string>Y:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonY">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="5" column="0" colspan="2">
+ <widget class="QGroupBox" name="controller_color">
+ <property name="title">
+ <string>Controller Color</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_10" columnstretch="0,0,0,0,0,0,0">
+ <item row="0" column="0">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="left_body_label">
+ <property name="text">
+ <string>Left Body</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="6">
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="left_buttons_label">
+ <property name="minimumSize">
+ <size>
+ <width>90</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Left Buttons</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="5">
+ <widget class="QPushButton" name="right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>32</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>40</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QLabel" name="right_body_label">
+ <property name="text">
+ <string>Right Body</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QLabel" name="right_buttons_label">
+ <property name="minimumSize">
+ <size>
+ <width>90</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Right Buttons</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QPushButton" name="left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>32</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>40</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QPushButton" name="left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>32</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>40</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="5">
+ <widget class="QPushButton" name="right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>32</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>40</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="LStick">
+ <property name="title">
+ <string>Left Stick</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonLStickUpHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelLStickUp">
+ <property name="text">
+ <string>Up:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonLStickUp">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="2">
+ <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonLStickRightHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelLStickRight">
+ <property name="text">
+ <string>Right:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonLStickRight">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="4" column="1" colspan="2">
+ <widget class="QPushButton" name="buttonLStickAnalog">
+ <property name="text">
+ <string>Set Analog Stick</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonLStickLeftHorizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="labelLStickLeft">
+ <property name="text">
+ <string>Left:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonLStickLeft">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="2">
+ <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonLStickDownHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelLStickDown">
+ <property name="text">
+ <string>Down:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonLStickDown">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="2">
+ <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonLStickModHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelLStickMod">
+ <property name="text">
+ <string>Modifier:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonLStickMod">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="1">
+ <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0">
+ <item>
+ <layout class="QHBoxLayout" name="buttonLStickPressedHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelLStickPressed">
+ <property name="text">
+ <string>Pressed:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonLStick">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QGroupBox" name="shoulderButtons">
+ <property name="title">
+ <string>Shoulder Buttons</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="3" column="0">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelSL">
+ <property name="text">
+ <string>SL:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonSL">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="1">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelZR">
+ <property name="text">
+ <string>ZR:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonZR">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="1">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelSR">
+ <property name="text">
+ <string>SR:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonSR">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelZL">
+ <property name="text">
+ <string>ZL:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonZL">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelL">
+ <property name="text">
+ <string>L:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonL">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelR">
+ <property name="text">
+ <string>R:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonR">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QGroupBox" name="misc">
+ <property name="title">
+ <string>Misc.</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_6">
+ <item row="1" column="0">
+ <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonMiscMinusHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelMinus">
+ <property name="text">
+ <string>Minus:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonMinus">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="1">
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="0">
+ <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonMiscPlusHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelPlus">
+ <property name="text">
+ <string>Plus:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonPlus">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonMiscHomeHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelHome">
+ <property name="text">
+ <string>Home:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonHome">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="buttonMiscScrCapHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelScreenshot">
+ <property name="text">
+ <string>Screen Capture:</string>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonScreenshot">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>80</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QPushButton" name="buttonClearAll">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="sizeIncrement">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string>Clear All</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonRestoreDefaults">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="sizeIncrement">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string>Restore Defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureInputPlayer</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>371</x>
+ <y>730</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>229</x>
+ <y>375</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigureInputPlayer</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>371</x>
+ <y>730</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>229</x>
+ <y>375</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <QKeyEvent>
+#include <QMenu>
+#include <QMessageBox>
+#include <QTimer>
+#include "common/assert.h"
+#include "common/param_package.h"
+#include "input_common/main.h"
+#include "ui_configure_mouse_advanced.h"
+#include "yuzu/configuration/config.h"
+#include "yuzu/configuration/configure_mouse_advanced.h"
+
+static QString GetKeyName(int key_code) {
+ switch (key_code) {
+ case Qt::Key_Shift:
+ return QObject::tr("Shift");
+ case Qt::Key_Control:
+ return QObject::tr("Ctrl");
+ case Qt::Key_Alt:
+ return QObject::tr("Alt");
+ case Qt::Key_Meta:
+ return "";
+ default:
+ return QKeySequence(key_code).toString();
+ }
+}
+
+static QString ButtonToText(const Common::ParamPackage& param) {
+ if (!param.Has("engine")) {
+ return QObject::tr("[not set]");
+ } else if (param.Get("engine", "") == "keyboard") {
+ return GetKeyName(param.Get("code", 0));
+ } else if (param.Get("engine", "") == "sdl") {
+ if (param.Has("hat")) {
+ return QString(QObject::tr("Hat %1 %2"))
+ .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
+ }
+ if (param.Has("axis")) {
+ return QString(QObject::tr("Axis %1%2"))
+ .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
+ }
+ if (param.Has("button")) {
+ return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
+ }
+ return QString();
+ } else {
+ return QObject::tr("[unknown]");
+ }
+}
+
+ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()),
+ timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
+ ui->setupUi(this);
+ setFocusPolicy(Qt::ClickFocus);
+
+ button_map = {
+ ui->left_button, ui->right_button, ui->middle_button, ui->forward_button, ui->back_button,
+ };
+
+ for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
+ if (!button_map[button_id])
+ continue;
+ button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(button_map[button_id], &QPushButton::released, [=]() {
+ handleClick(
+ button_map[button_id],
+ [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
+ InputCommon::Polling::DeviceType::Button);
+ });
+ connect(button_map[button_id], &QPushButton::customContextMenuRequested,
+ [=](const QPoint& menu_location) {
+ QMenu context_menu;
+ context_menu.addAction(tr("Clear"), [&] {
+ buttons_param[button_id].Clear();
+ button_map[button_id]->setText(tr("[not set]"));
+ });
+ context_menu.addAction(tr("Restore Default"), [&] {
+ buttons_param[button_id] =
+ Common::ParamPackage{InputCommon::GenerateKeyboardParam(
+ Config::default_mouse_buttons[button_id])};
+ button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
+ });
+ context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
+ });
+ }
+
+ connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
+ connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
+
+ timeout_timer->setSingleShot(true);
+ connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
+
+ connect(poll_timer.get(), &QTimer::timeout, [this]() {
+ Common::ParamPackage params;
+ for (auto& poller : device_pollers) {
+ params = poller->GetNextInput();
+ if (params.Has("engine")) {
+ setPollingResult(params, false);
+ return;
+ }
+ }
+ });
+
+ loadConfiguration();
+ resize(0, 0);
+}
+
+ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default;
+
+void ConfigureMouseAdvanced::applyConfiguration() {
+ std::transform(buttons_param.begin(), buttons_param.end(),
+ Settings::values.mouse_buttons.begin(),
+ [](const Common::ParamPackage& param) { return param.Serialize(); });
+}
+
+void ConfigureMouseAdvanced::loadConfiguration() {
+ std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
+ buttons_param.begin(),
+ [](const std::string& str) { return Common::ParamPackage(str); });
+ updateButtonLabels();
+}
+
+void ConfigureMouseAdvanced::restoreDefaults() {
+ for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
+ buttons_param[button_id] = Common::ParamPackage{
+ InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])};
+ }
+
+ updateButtonLabels();
+}
+
+void ConfigureMouseAdvanced::ClearAll() {
+ for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
+ if (button_map[i] && button_map[i]->isEnabled())
+ buttons_param[i].Clear();
+ }
+
+ updateButtonLabels();
+}
+
+void ConfigureMouseAdvanced::updateButtonLabels() {
+ for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) {
+ button_map[button]->setText(ButtonToText(buttons_param[button]));
+ }
+}
+
+void ConfigureMouseAdvanced::handleClick(
+ QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
+ InputCommon::Polling::DeviceType type) {
+ button->setText(tr("[press key]"));
+ button->setFocus();
+
+ const auto iter = std::find(button_map.begin(), button_map.end(), button);
+ ASSERT(iter != button_map.end());
+ const auto index = std::distance(button_map.begin(), iter);
+ ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
+
+ input_setter = new_input_setter;
+
+ device_pollers = InputCommon::Polling::GetPollers(type);
+
+ // Keyboard keys can only be used as button devices
+ want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
+
+ for (auto& poller : device_pollers) {
+ poller->Start();
+ }
+
+ grabKeyboard();
+ grabMouse();
+ timeout_timer->start(5000); // Cancel after 5 seconds
+ poll_timer->start(200); // Check for new inputs every 200ms
+}
+
+void ConfigureMouseAdvanced::setPollingResult(const Common::ParamPackage& params, bool abort) {
+ releaseKeyboard();
+ releaseMouse();
+ timeout_timer->stop();
+ poll_timer->stop();
+ for (auto& poller : device_pollers) {
+ poller->Stop();
+ }
+
+ if (!abort) {
+ (*input_setter)(params);
+ }
+
+ updateButtonLabels();
+ input_setter = std::nullopt;
+}
+
+void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
+ if (!input_setter || !event)
+ return;
+
+ if (event->key() != Qt::Key_Escape) {
+ if (want_keyboard_keys) {
+ setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
+ false);
+ } else {
+ // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
+ return;
+ }
+ }
+ setPollingResult({}, true);
+}
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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <optional>
+#include <QDialog>
+#include <QWidget>
+#include "core/settings.h"
+
+class QCheckBox;
+class QPushButton;
+class QTimer;
+
+namespace Ui {
+class ConfigureMouseAdvanced;
+}
+
+class ConfigureMouseAdvanced : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigureMouseAdvanced(QWidget* parent);
+ ~ConfigureMouseAdvanced() override;
+
+ void applyConfiguration();
+
+private:
+ std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
+
+ /// This will be the the setting function when an input is awaiting configuration.
+ std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
+
+ std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map;
+ std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param;
+
+ std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
+
+ std::unique_ptr<QTimer> timeout_timer;
+ std::unique_ptr<QTimer> poll_timer;
+
+ /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
+ /// keyboard events are ignored.
+ bool want_keyboard_keys = false;
+
+ /// Load configuration settings.
+ void loadConfiguration();
+ /// Restore all buttons to their default values.
+ void restoreDefaults();
+ /// Clear all input configuration
+ void ClearAll();
+
+ /// Update UI to reflect current configuration.
+ void updateButtonLabels();
+
+ /// Called when the button was pressed.
+ void handleClick(QPushButton* button,
+ std::function<void(const Common::ParamPackage&)> new_input_setter,
+ InputCommon::Polling::DeviceType type);
+
+ /// Finish polling and configure input using the input_setter
+ void setPollingResult(const Common::ParamPackage& params, bool abort);
+
+ /// Handle key press events.
+ void keyPressEvent(QKeyEvent* event) override;
+};
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureMouseAdvanced</class>
+ <widget class="QDialog" name="ConfigureMouseAdvanced">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>250</width>
+ <height>261</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Mouse</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="gridGroupBox">
+ <property name="title">
+ <string>Mouse Buttons</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="4">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="3">
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Right:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="right_button">
+ <property name="minimumSize">
+ <size>
+ <width>75</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Middle:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="middle_button">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="minimumSize">
+ <size>
+ <width>54</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Back:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="back_button">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Left:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="left_button">
+ <property name="minimumSize">
+ <size>
+ <width>75</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="3">
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <item>
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Forward:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="forward_button">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <item>
+ <widget class="QPushButton" name="buttonClearAll">
+ <property name="text">
+ <string>Clear All</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonRestoreDefaults">
+ <property name="text">
+ <string>Restore Defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureMouseAdvanced</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>124</x>
+ <y>266</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>124</x>
+ <y>143</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigureMouseAdvanced</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>124</x>
+ <y>266</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>124</x>
+ <y>143</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include "ui_configure_touchscreen_advanced.h"
+#include "yuzu/configuration/config.h"
+#include "yuzu/configuration/configure_touchscreen_advanced.h"
+
+ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchscreenAdvanced>()) {
+ ui->setupUi(this);
+
+ connect(ui->restore_defaults_button, &QPushButton::pressed, this,
+ &ConfigureTouchscreenAdvanced::restoreDefaults);
+
+ loadConfiguration();
+ resize(0, 0);
+}
+
+ConfigureTouchscreenAdvanced::~ConfigureTouchscreenAdvanced() = default;
+
+void ConfigureTouchscreenAdvanced::applyConfiguration() {
+ Settings::values.touchscreen.finger = ui->finger_box->value();
+ Settings::values.touchscreen.diameter_x = ui->diameter_x_box->value();
+ Settings::values.touchscreen.diameter_y = ui->diameter_y_box->value();
+ Settings::values.touchscreen.rotation_angle = ui->angle_box->value();
+}
+
+void ConfigureTouchscreenAdvanced::loadConfiguration() {
+ ui->finger_box->setValue(Settings::values.touchscreen.finger);
+ ui->diameter_x_box->setValue(Settings::values.touchscreen.diameter_x);
+ ui->diameter_y_box->setValue(Settings::values.touchscreen.diameter_y);
+ ui->angle_box->setValue(Settings::values.touchscreen.rotation_angle);
+}
+
+void ConfigureTouchscreenAdvanced::restoreDefaults() {
+ ui->finger_box->setValue(0);
+ ui->diameter_x_box->setValue(15);
+ ui->diameter_y_box->setValue(15);
+ ui->angle_box->setValue(0);
+}
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 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QDialog>
+#include <QWidget>
+#include "yuzu/configuration/config.h"
+
+namespace Ui {
+class ConfigureTouchscreenAdvanced;
+}
+
+class ConfigureTouchscreenAdvanced : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigureTouchscreenAdvanced(QWidget* parent);
+ ~ConfigureTouchscreenAdvanced() override;
+
+ void applyConfiguration();
+
+private:
+ /// Load configuration settings.
+ void loadConfiguration();
+ /// Restore all buttons to their default values.
+ void restoreDefaults();
+
+ std::unique_ptr<Ui::ConfigureTouchscreenAdvanced> ui;
+};
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureTouchscreenAdvanced</class>
+ <widget class="QDialog" name="ConfigureTouchscreenAdvanced">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>298</width>
+ <height>339</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Touchscreen</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="minimumSize">
+ <size>
+ <width>280</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Warning: The settings in this page affect the inner workings of yuzu's emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="gridGroupBox">
+ <property name="title">
+ <string>Touch Parameters</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Touch Diameter Y</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Finger</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Touch Diameter X</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QSpinBox" name="finger_box">
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Rotational Angle</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QSpinBox" name="diameter_x_box"/>
+ </item>
+ <item row="2" column="2">
+ <widget class="QSpinBox" name="diameter_y_box"/>
+ </item>
+ <item row="3" column="2">
+ <widget class="QSpinBox" name="angle_box"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="restore_defaults_button">
+ <property name="text">
+ <string>Restore Defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureTouchscreenAdvanced</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>140</x>
+ <y>318</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>140</x>
+ <y>169</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigureTouchscreenAdvanced</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>140</x>
+ <y>318</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>140</x>
+ <y>169</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
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() {
UpdateUITheme();
if (UISettings::values.enable_discord_presence != old_discord_presence)
SetDiscordEnabled(UISettings::values.enable_discord_presence);
- game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
+
+ const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
+ if (reload) {
+ game_list->PopulateAsync(UISettings::values.gamedir,
+ UISettings::values.gamedir_deepscan);
+ }
+
config->Save();
}
}
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 @@
#pragma once
#include <array>
+#include <atomic>
#include <vector>
#include <QByteArray>
#include <QString>
@@ -63,6 +64,7 @@ struct Values {
uint32_t icon_size;
uint8_t row_1_text_id;
uint8_t row_2_text_id;
+ std::atomic_bool is_game_list_reload_pending{false};
};
extern Values values;
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>
},
}};
+static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> default_mouse_buttons = {
+ SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_APOSTROPHE,
+ SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS,
+};
+
+static const std::array<int, 0x8A> keyboard_keys = {
+ 0,
+ 0,
+ 0,
+ 0,
+ SDL_SCANCODE_A,
+ SDL_SCANCODE_B,
+ SDL_SCANCODE_C,
+ SDL_SCANCODE_D,
+ SDL_SCANCODE_E,
+ SDL_SCANCODE_F,
+ SDL_SCANCODE_G,
+ SDL_SCANCODE_H,
+ SDL_SCANCODE_I,
+ SDL_SCANCODE_J,
+ SDL_SCANCODE_K,
+ SDL_SCANCODE_L,
+ SDL_SCANCODE_M,
+ SDL_SCANCODE_N,
+ SDL_SCANCODE_O,
+ SDL_SCANCODE_P,
+ SDL_SCANCODE_Q,
+ SDL_SCANCODE_R,
+ SDL_SCANCODE_S,
+ SDL_SCANCODE_T,
+ SDL_SCANCODE_U,
+ SDL_SCANCODE_V,
+ SDL_SCANCODE_W,
+ SDL_SCANCODE_X,
+ SDL_SCANCODE_Y,
+ SDL_SCANCODE_Z,
+ SDL_SCANCODE_1,
+ SDL_SCANCODE_2,
+ SDL_SCANCODE_3,
+ SDL_SCANCODE_4,
+ SDL_SCANCODE_5,
+ SDL_SCANCODE_6,
+ SDL_SCANCODE_7,
+ SDL_SCANCODE_8,
+ SDL_SCANCODE_9,
+ SDL_SCANCODE_0,
+ SDL_SCANCODE_RETURN,
+ SDL_SCANCODE_ESCAPE,
+ SDL_SCANCODE_BACKSPACE,
+ SDL_SCANCODE_TAB,
+ SDL_SCANCODE_SPACE,
+ SDL_SCANCODE_MINUS,
+ SDL_SCANCODE_EQUALS,
+ SDL_SCANCODE_LEFTBRACKET,
+ SDL_SCANCODE_RIGHTBRACKET,
+ SDL_SCANCODE_BACKSLASH,
+ 0,
+ SDL_SCANCODE_SEMICOLON,
+ SDL_SCANCODE_APOSTROPHE,
+ SDL_SCANCODE_GRAVE,
+ SDL_SCANCODE_COMMA,
+ SDL_SCANCODE_PERIOD,
+ SDL_SCANCODE_SLASH,
+ SDL_SCANCODE_CAPSLOCK,
+
+ SDL_SCANCODE_F1,
+ SDL_SCANCODE_F2,
+ SDL_SCANCODE_F3,
+ SDL_SCANCODE_F4,
+ SDL_SCANCODE_F5,
+ SDL_SCANCODE_F6,
+ SDL_SCANCODE_F7,
+ SDL_SCANCODE_F8,
+ SDL_SCANCODE_F9,
+ SDL_SCANCODE_F10,
+ SDL_SCANCODE_F11,
+ SDL_SCANCODE_F12,
+
+ 0,
+ SDL_SCANCODE_SCROLLLOCK,
+ SDL_SCANCODE_PAUSE,
+ SDL_SCANCODE_INSERT,
+ SDL_SCANCODE_HOME,
+ SDL_SCANCODE_PAGEUP,
+ SDL_SCANCODE_DELETE,
+ SDL_SCANCODE_END,
+ SDL_SCANCODE_PAGEDOWN,
+ SDL_SCANCODE_RIGHT,
+ SDL_SCANCODE_LEFT,
+ SDL_SCANCODE_DOWN,
+ SDL_SCANCODE_UP,
+
+ SDL_SCANCODE_NUMLOCKCLEAR,
+ SDL_SCANCODE_KP_DIVIDE,
+ SDL_SCANCODE_KP_MULTIPLY,
+ SDL_SCANCODE_KP_MINUS,
+ SDL_SCANCODE_KP_PLUS,
+ SDL_SCANCODE_KP_ENTER,
+ SDL_SCANCODE_KP_1,
+ SDL_SCANCODE_KP_2,
+ SDL_SCANCODE_KP_3,
+ SDL_SCANCODE_KP_4,
+ SDL_SCANCODE_KP_5,
+ SDL_SCANCODE_KP_6,
+ SDL_SCANCODE_KP_7,
+ SDL_SCANCODE_KP_8,
+ SDL_SCANCODE_KP_9,
+ SDL_SCANCODE_KP_0,
+ SDL_SCANCODE_KP_PERIOD,
+
+ 0,
+ 0,
+ SDL_SCANCODE_POWER,
+ SDL_SCANCODE_KP_EQUALS,
+
+ SDL_SCANCODE_F13,
+ SDL_SCANCODE_F14,
+ SDL_SCANCODE_F15,
+ SDL_SCANCODE_F16,
+ SDL_SCANCODE_F17,
+ SDL_SCANCODE_F18,
+ SDL_SCANCODE_F19,
+ SDL_SCANCODE_F20,
+ SDL_SCANCODE_F21,
+ SDL_SCANCODE_F22,
+ SDL_SCANCODE_F23,
+ SDL_SCANCODE_F24,
+
+ 0,
+ SDL_SCANCODE_HELP,
+ SDL_SCANCODE_MENU,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ SDL_SCANCODE_KP_COMMA,
+ SDL_SCANCODE_KP_LEFTPAREN,
+ SDL_SCANCODE_KP_RIGHTPAREN,
+ 0,
+ 0,
+ 0,
+ 0,
+};
+
+static const std::array<int, 8> keyboard_mods{
+ SDL_SCANCODE_LCTRL, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_LALT, SDL_SCANCODE_LGUI,
+ SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI,
+};
+
void Config::ReadValues() {
// Controls
+ for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
+ const auto group = fmt::format("ControlsP{}", p);
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
+ Settings::values.players[p].buttons[i] =
+ sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
+ if (Settings::values.players[p].buttons[i].empty())
+ Settings::values.players[p].buttons[i] = default_param;
+ }
+
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+ default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
+ default_analogs[i][3], default_analogs[i][4], 0.5f);
+ Settings::values.players[p].analogs[i] =
+ sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
+ if (Settings::values.players[p].analogs[i].empty())
+ Settings::values.players[p].analogs[i] = default_param;
+ }
+ }
+
+ Settings::values.mouse_enabled =
+ sdl2_config->GetBoolean("ControlsGeneral", "mouse_enabled", false);
+ for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
+ std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
+ Settings::values.mouse_buttons[i] = sdl2_config->Get(
+ "ControlsGeneral", std::string("mouse_") + Settings::NativeMouseButton::mapping[i],
+ default_param);
+ if (Settings::values.mouse_buttons[i].empty())
+ Settings::values.mouse_buttons[i] = default_param;
+ }
+
+ Settings::values.motion_device = sdl2_config->Get(
+ "ControlsGeneral", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
+
+ Settings::values.keyboard_enabled =
+ sdl2_config->GetBoolean("ControlsGeneral", "keyboard_enabled", false);
+
+ Settings::values.debug_pad_enabled =
+ sdl2_config->GetBoolean("ControlsGeneral", "debug_pad_enabled", false);
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
- Settings::values.buttons[i] =
- sdl2_config->Get("Controls", Settings::NativeButton::mapping[i], default_param);
- if (Settings::values.buttons[i].empty())
- Settings::values.buttons[i] = default_param;
+ Settings::values.debug_pad_buttons[i] = sdl2_config->Get(
+ "ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i],
+ default_param);
+ if (Settings::values.debug_pad_buttons[i].empty())
+ Settings::values.debug_pad_buttons[i] = default_param;
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_analogs[i][4], 0.5f);
- Settings::values.analogs[i] =
- sdl2_config->Get("Controls", Settings::NativeAnalog::mapping[i], default_param);
- if (Settings::values.analogs[i].empty())
- Settings::values.analogs[i] = default_param;
+ Settings::values.debug_pad_analogs[i] = sdl2_config->Get(
+ "ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i],
+ default_param);
+ if (Settings::values.debug_pad_analogs[i].empty())
+ Settings::values.debug_pad_analogs[i] = default_param;
}
- Settings::values.motion_device = sdl2_config->Get(
- "Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
- Settings::values.touch_device =
- sdl2_config->Get("Controls", "touch_device", "engine:emu_window");
-
- // Core
- Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
- Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
+ Settings::values.touchscreen.enabled =
+ sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
+ Settings::values.touchscreen.device =
+ sdl2_config->Get("ControlsGeneral", "touch_device", "engine:emu_window");
+ Settings::values.touchscreen.finger =
+ sdl2_config->GetInteger("ControlsGeneral", "touch_finger", 0);
+ Settings::values.touchscreen.rotation_angle =
+ sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0);
+ Settings::values.touchscreen.diameter_x =
+ sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
+ Settings::values.touchscreen.diameter_y =
+ sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
- // Renderer
- Settings::values.resolution_factor =
- (float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0);
- Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
- Settings::values.frame_limit =
- static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
- Settings::values.use_accurate_gpu_emulation =
- sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
-
- Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
- Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
- Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
-
- // Audio
- Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
- Settings::values.enable_audio_stretching =
- sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
- Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
- Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);
+ std::transform(keyboard_keys.begin(), keyboard_keys.end(),
+ Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
+ std::transform(keyboard_mods.begin(), keyboard_mods.end(),
+ Settings::values.keyboard_keys.begin() +
+ Settings::NativeKeyboard::LeftControlKey,
+ InputCommon::GenerateKeyboardParam);
+ std::transform(keyboard_mods.begin(), keyboard_mods.end(),
+ Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
// Data Storage
Settings::values.use_virtual_sd =
@@ -139,6 +331,32 @@ void Config::ReadValues() {
Settings::values.rng_seed = std::nullopt;
}
+ // Core
+ Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
+ Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
+
+ // Renderer
+ Settings::values.resolution_factor =
+ (float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0);
+ Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
+ Settings::values.frame_limit =
+ static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
+ Settings::values.use_accurate_gpu_emulation =
+ sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
+
+ Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
+ Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
+ Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
+
+ // Audio
+ Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
+ Settings::values.enable_audio_stretching =
+ sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
+ Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
+ Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);
+
+ Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
+
// Miscellaneous
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);