summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/hid/input_converter.cpp2
-rw-r--r--src/core/hle/kernel/k_page_table.cpp65
-rw-r--r--src/core/hle/kernel/k_page_table.h15
-rw-r--r--src/input_common/drivers/sdl_driver.cpp5
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp30
-rw-r--r--src/input_common/helpers/touch_from_buttons.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h4
-rw-r--r--src/yuzu/applets/qt_controller.cpp2
-rw-r--r--src/yuzu/configuration/configure_input.cpp2
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp89
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.h2
-rw-r--r--src/yuzu/configuration/configure_vibration.cpp74
-rw-r--r--src/yuzu/configuration/configure_vibration.h18
-rw-r--r--src/yuzu/configuration/configure_vibration.ui7
15 files changed, 232 insertions, 90 deletions
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 860aab400..cd41607a7 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -28,7 +28,7 @@ Common::Input::BatteryStatus TransformToBattery(const Common::Input::CallbackSta
28 if (value > 0.8f) { 28 if (value > 0.8f) {
29 battery = Common::Input::BatteryLevel::Full; 29 battery = Common::Input::BatteryLevel::Full;
30 } 30 }
31 if (value >= 1.0f) { 31 if (value >= 0.95f) {
32 battery = Common::Input::BatteryLevel::Charging; 32 battery = Common::Input::BatteryLevel::Charging;
33 } 33 }
34 break; 34 break;
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 393214082..912853e5c 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -61,7 +61,8 @@ constexpr std::size_t GetSizeInRange(const KMemoryInfo& info, VAddr start, VAddr
61 61
62} // namespace 62} // namespace
63 63
64KPageTable::KPageTable(Core::System& system_) : system{system_} {} 64KPageTable::KPageTable(Core::System& system_)
65 : general_lock{system_.Kernel()}, map_physical_memory_lock{system_.Kernel()}, system{system_} {}
65 66
66KPageTable::~KPageTable() = default; 67KPageTable::~KPageTable() = default;
67 68
@@ -284,7 +285,7 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
284 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); 285 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
285 286
286 // Lock the table. 287 // Lock the table.
287 std::lock_guard lock{page_table_lock}; 288 KScopedLightLock lk(general_lock);
288 289
289 // Verify that the destination memory is unmapped. 290 // Verify that the destination memory is unmapped.
290 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, 291 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
@@ -302,7 +303,7 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
302} 303}
303 304
304ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 305ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
305 std::lock_guard lock{page_table_lock}; 306 KScopedLightLock lk(general_lock);
306 307
307 const std::size_t num_pages{size / PageSize}; 308 const std::size_t num_pages{size / PageSize};
308 309
@@ -339,7 +340,7 @@ ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t
339} 340}
340 341
341ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 342ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
342 std::lock_guard lock{page_table_lock}; 343 KScopedLightLock lk(general_lock);
343 344
344 if (!size) { 345 if (!size) {
345 return ResultSuccess; 346 return ResultSuccess;
@@ -373,7 +374,7 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size
373 374
374ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, 375ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
375 KPageTable& src_page_table, VAddr src_addr) { 376 KPageTable& src_page_table, VAddr src_addr) {
376 std::lock_guard lock{page_table_lock}; 377 KScopedLightLock lk(general_lock);
377 378
378 const std::size_t num_pages{size / PageSize}; 379 const std::size_t num_pages{size / PageSize};
379 380
@@ -401,10 +402,10 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
401 402
402ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { 403ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
403 // Lock the physical memory lock. 404 // Lock the physical memory lock.
404 std::lock_guard phys_lk(map_physical_memory_lock); 405 KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
405 406
406 // Lock the table. 407 // Lock the table.
407 std::lock_guard lock{page_table_lock}; 408 KScopedLightLock lk(general_lock);
408 409
409 std::size_t mapped_size{}; 410 std::size_t mapped_size{};
410 const VAddr end_addr{addr + size}; 411 const VAddr end_addr{addr + size};
@@ -480,7 +481,11 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
480} 481}
481 482
482ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) { 483ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
483 std::lock_guard lock{page_table_lock}; 484 // Lock the physical memory lock.
485 KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
486
487 // Lock the table.
488 KScopedLightLock lk(general_lock);
484 489
485 const VAddr end_addr{addr + size}; 490 const VAddr end_addr{addr + size};
486 ResultCode result{ResultSuccess}; 491 ResultCode result{ResultSuccess};
@@ -542,7 +547,7 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
542} 547}
543 548
544ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 549ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
545 std::lock_guard lock{page_table_lock}; 550 KScopedLightLock lk(general_lock);
546 551
547 KMemoryState src_state{}; 552 KMemoryState src_state{};
548 CASCADE_CODE(CheckMemoryState( 553 CASCADE_CODE(CheckMemoryState(
@@ -581,7 +586,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz
581} 586}
582 587
583ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 588ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
584 std::lock_guard lock{page_table_lock}; 589 KScopedLightLock lk(general_lock);
585 590
586 KMemoryState src_state{}; 591 KMemoryState src_state{};
587 CASCADE_CODE(CheckMemoryState( 592 CASCADE_CODE(CheckMemoryState(
@@ -624,6 +629,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s
624 629
625ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list, 630ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
626 KMemoryPermission perm) { 631 KMemoryPermission perm) {
632 ASSERT(this->IsLockedByCurrentThread());
633
627 VAddr cur_addr{addr}; 634 VAddr cur_addr{addr};
628 635
629 for (const auto& node : page_linked_list.Nodes()) { 636 for (const auto& node : page_linked_list.Nodes()) {
@@ -652,7 +659,7 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
652 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); 659 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
653 660
654 // Lock the table. 661 // Lock the table.
655 std::lock_guard lock{page_table_lock}; 662 KScopedLightLock lk(general_lock);
656 663
657 // Check the memory state. 664 // Check the memory state.
658 R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, 665 R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free,
@@ -669,6 +676,8 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
669} 676}
670 677
671ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { 678ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
679 ASSERT(this->IsLockedByCurrentThread());
680
672 VAddr cur_addr{addr}; 681 VAddr cur_addr{addr};
673 682
674 for (const auto& node : page_linked_list.Nodes()) { 683 for (const auto& node : page_linked_list.Nodes()) {
@@ -693,7 +702,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
693 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); 702 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
694 703
695 // Lock the table. 704 // Lock the table.
696 std::lock_guard lock{page_table_lock}; 705 KScopedLightLock lk(general_lock);
697 706
698 // Check the memory state. 707 // Check the memory state.
699 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None, 708 R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None,
@@ -714,7 +723,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
714 const size_t num_pages = size / PageSize; 723 const size_t num_pages = size / PageSize;
715 724
716 // Lock the table. 725 // Lock the table.
717 std::lock_guard lock{page_table_lock}; 726 KScopedLightLock lk(general_lock);
718 727
719 // Verify we can change the memory permission. 728 // Verify we can change the memory permission.
720 KMemoryState old_state; 729 KMemoryState old_state;
@@ -768,7 +777,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
768} 777}
769 778
770KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) { 779KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) {
771 std::lock_guard lock{page_table_lock}; 780 KScopedLightLock lk(general_lock);
772 781
773 return block_manager->FindBlock(addr).GetMemoryInfo(); 782 return block_manager->FindBlock(addr).GetMemoryInfo();
774} 783}
@@ -783,7 +792,7 @@ KMemoryInfo KPageTable::QueryInfo(VAddr addr) {
783} 792}
784 793
785ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) { 794ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) {
786 std::lock_guard lock{page_table_lock}; 795 KScopedLightLock lk(general_lock);
787 796
788 KMemoryState state{}; 797 KMemoryState state{};
789 KMemoryAttribute attribute{}; 798 KMemoryAttribute attribute{};
@@ -801,7 +810,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
801} 810}
802 811
803ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { 812ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
804 std::lock_guard lock{page_table_lock}; 813 KScopedLightLock lk(general_lock);
805 814
806 KMemoryState state{}; 815 KMemoryState state{};
807 816
@@ -820,7 +829,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
820 const size_t num_pages = size / PageSize; 829 const size_t num_pages = size / PageSize;
821 830
822 // Lock the table. 831 // Lock the table.
823 std::lock_guard lock{page_table_lock}; 832 KScopedLightLock lk(general_lock);
824 833
825 // Verify we can change the memory permission. 834 // Verify we can change the memory permission.
826 KMemoryState old_state; 835 KMemoryState old_state;
@@ -849,7 +858,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
849 KMemoryAttribute::SetMask); 858 KMemoryAttribute::SetMask);
850 859
851 // Lock the table. 860 // Lock the table.
852 std::lock_guard lock{page_table_lock}; 861 KScopedLightLock lk(general_lock);
853 862
854 // Verify we can change the memory attribute. 863 // Verify we can change the memory attribute.
855 KMemoryState old_state; 864 KMemoryState old_state;
@@ -880,7 +889,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
880 889
881ResultCode KPageTable::SetMaxHeapSize(std::size_t size) { 890ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
882 // Lock the table. 891 // Lock the table.
883 std::lock_guard lock{page_table_lock}; 892 KScopedLightLock lk(general_lock);
884 893
885 // Only process page tables are allowed to set heap size. 894 // Only process page tables are allowed to set heap size.
886 ASSERT(!this->IsKernel()); 895 ASSERT(!this->IsKernel());
@@ -891,15 +900,15 @@ ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
891} 900}
892 901
893ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { 902ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
894 // Lock the physical memory lock. 903 // Lock the physical memory mutex.
895 std::lock_guard phys_lk(map_physical_memory_lock); 904 KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
896 905
897 // Try to perform a reduction in heap, instead of an extension. 906 // Try to perform a reduction in heap, instead of an extension.
898 VAddr cur_address{}; 907 VAddr cur_address{};
899 std::size_t allocation_size{}; 908 std::size_t allocation_size{};
900 { 909 {
901 // Lock the table. 910 // Lock the table.
902 std::lock_guard lk(page_table_lock); 911 KScopedLightLock lk(general_lock);
903 912
904 // Validate that setting heap size is possible at all. 913 // Validate that setting heap size is possible at all.
905 R_UNLESS(!is_kernel, ResultOutOfMemory); 914 R_UNLESS(!is_kernel, ResultOutOfMemory);
@@ -964,7 +973,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
964 // Map the pages. 973 // Map the pages.
965 { 974 {
966 // Lock the table. 975 // Lock the table.
967 std::lock_guard lk(page_table_lock); 976 KScopedLightLock lk(general_lock);
968 977
969 // Ensure that the heap hasn't changed since we began executing. 978 // Ensure that the heap hasn't changed since we began executing.
970 ASSERT(cur_address == current_heap_end); 979 ASSERT(cur_address == current_heap_end);
@@ -1006,7 +1015,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
1006 bool is_map_only, VAddr region_start, 1015 bool is_map_only, VAddr region_start,
1007 std::size_t region_num_pages, KMemoryState state, 1016 std::size_t region_num_pages, KMemoryState state,
1008 KMemoryPermission perm, PAddr map_addr) { 1017 KMemoryPermission perm, PAddr map_addr) {
1009 std::lock_guard lock{page_table_lock}; 1018 KScopedLightLock lk(general_lock);
1010 1019
1011 if (!CanContain(region_start, region_num_pages * PageSize, state)) { 1020 if (!CanContain(region_start, region_num_pages * PageSize, state)) {
1012 return ResultInvalidCurrentMemory; 1021 return ResultInvalidCurrentMemory;
@@ -1037,7 +1046,7 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
1037} 1046}
1038 1047
1039ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { 1048ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
1040 std::lock_guard lock{page_table_lock}; 1049 KScopedLightLock lk(general_lock);
1041 1050
1042 KMemoryPermission perm{}; 1051 KMemoryPermission perm{};
1043 if (const ResultCode result{CheckMemoryState( 1052 if (const ResultCode result{CheckMemoryState(
@@ -1060,7 +1069,7 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
1060} 1069}
1061 1070
1062ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) { 1071ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
1063 std::lock_guard lock{page_table_lock}; 1072 KScopedLightLock lk(general_lock);
1064 1073
1065 KMemoryPermission perm{}; 1074 KMemoryPermission perm{};
1066 if (const ResultCode result{CheckMemoryState( 1075 if (const ResultCode result{CheckMemoryState(
@@ -1083,7 +1092,7 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
1083} 1092}
1084 1093
1085ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { 1094ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
1086 std::lock_guard lock{page_table_lock}; 1095 KScopedLightLock lk(general_lock);
1087 1096
1088 KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; 1097 KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
1089 1098
@@ -1110,7 +1119,7 @@ ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
1110} 1119}
1111 1120
1112ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { 1121ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
1113 std::lock_guard lock{page_table_lock}; 1122 KScopedLightLock lk(general_lock);
1114 1123
1115 KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; 1124 KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
1116 1125
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index ecae939a0..c98887d34 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -5,12 +5,12 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <mutex>
9 8
10#include "common/common_funcs.h" 9#include "common/common_funcs.h"
11#include "common/common_types.h" 10#include "common/common_types.h"
12#include "common/page_table.h" 11#include "common/page_table.h"
13#include "core/file_sys/program_metadata.h" 12#include "core/file_sys/program_metadata.h"
13#include "core/hle/kernel/k_light_lock.h"
14#include "core/hle/kernel/k_memory_block.h" 14#include "core/hle/kernel/k_memory_block.h"
15#include "core/hle/kernel/k_memory_manager.h" 15#include "core/hle/kernel/k_memory_manager.h"
16#include "core/hle/result.h" 16#include "core/hle/result.h"
@@ -147,11 +147,12 @@ private:
147 } 147 }
148 148
149 bool IsLockedByCurrentThread() const { 149 bool IsLockedByCurrentThread() const {
150 return true; 150 return general_lock.IsLockedByCurrentThread();
151 } 151 }
152 152
153 std::recursive_mutex page_table_lock; 153 mutable KLightLock general_lock;
154 std::mutex map_physical_memory_lock; 154 mutable KLightLock map_physical_memory_lock;
155
155 std::unique_ptr<KMemoryBlockManager> block_manager; 156 std::unique_ptr<KMemoryBlockManager> block_manager;
156 157
157public: 158public:
@@ -210,7 +211,7 @@ public:
210 return alias_code_region_end - alias_code_region_start; 211 return alias_code_region_end - alias_code_region_start;
211 } 212 }
212 size_t GetNormalMemorySize() { 213 size_t GetNormalMemorySize() {
213 std::lock_guard lk(page_table_lock); 214 KScopedLightLock lk(general_lock);
214 return GetHeapSize() + mapped_physical_memory_size; 215 return GetHeapSize() + mapped_physical_memory_size;
215 } 216 }
216 constexpr std::size_t GetAddressSpaceWidth() const { 217 constexpr std::size_t GetAddressSpaceWidth() const {
@@ -252,7 +253,9 @@ public:
252 constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { 253 constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const {
253 return !IsOutsideASLRRegion(address, size); 254 return !IsOutsideASLRRegion(address, size);
254 } 255 }
255 constexpr PAddr GetPhysicalAddr(VAddr addr) { 256
257 PAddr GetPhysicalAddr(VAddr addr) {
258 ASSERT(IsLockedByCurrentThread());
256 const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits]; 259 const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits];
257 ASSERT(backing_addr); 260 ASSERT(backing_addr);
258 return backing_addr + addr; 261 return backing_addr + addr;
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 577bf5c31..b031a8523 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -181,11 +181,10 @@ public:
181 case SDL_JOYSTICK_POWER_EMPTY: 181 case SDL_JOYSTICK_POWER_EMPTY:
182 return BatteryLevel::Empty; 182 return BatteryLevel::Empty;
183 case SDL_JOYSTICK_POWER_LOW: 183 case SDL_JOYSTICK_POWER_LOW:
184 return BatteryLevel::Critical;
185 case SDL_JOYSTICK_POWER_MEDIUM:
186 return BatteryLevel::Low; 184 return BatteryLevel::Low;
187 case SDL_JOYSTICK_POWER_FULL: 185 case SDL_JOYSTICK_POWER_MEDIUM:
188 return BatteryLevel::Medium; 186 return BatteryLevel::Medium;
187 case SDL_JOYSTICK_POWER_FULL:
189 case SDL_JOYSTICK_POWER_MAX: 188 case SDL_JOYSTICK_POWER_MAX:
190 return BatteryLevel::Full; 189 return BatteryLevel::Full;
191 case SDL_JOYSTICK_POWER_UNKNOWN: 190 case SDL_JOYSTICK_POWER_UNKNOWN:
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
index e23394f5f..31e6f62ab 100644
--- a/src/input_common/helpers/stick_from_buttons.cpp
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -167,12 +167,34 @@ public:
167 } 167 }
168 168
169 void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) { 169 void UpdateModButtonStatus(const Common::Input::CallbackStatus& button_callback) {
170 modifier_status = button_callback.button_status.value; 170 const auto& new_status = button_callback.button_status;
171 const bool new_button_value = new_status.inverted ? !new_status.value : new_status.value;
172 modifier_status.toggle = new_status.toggle;
173
174 // Update button status with current
175 if (!modifier_status.toggle) {
176 modifier_status.locked = false;
177 if (modifier_status.value != new_button_value) {
178 modifier_status.value = new_button_value;
179 }
180 } else {
181 // Toggle button and lock status
182 if (new_button_value && !modifier_status.locked) {
183 modifier_status.locked = true;
184 modifier_status.value = !modifier_status.value;
185 }
186
187 // Unlock button ready for next press
188 if (!new_button_value && modifier_status.locked) {
189 modifier_status.locked = false;
190 }
191 }
192
171 UpdateStatus(); 193 UpdateStatus();
172 } 194 }
173 195
174 void UpdateStatus() { 196 void UpdateStatus() {
175 const float coef = modifier_status ? modifier_scale : 1.0f; 197 const float coef = modifier_status.value ? modifier_scale : 1.0f;
176 198
177 bool r = right_status; 199 bool r = right_status;
178 bool l = left_status; 200 bool l = left_status;
@@ -266,7 +288,7 @@ public:
266 if (down_status) { 288 if (down_status) {
267 --y; 289 --y;
268 } 290 }
269 const float coef = modifier_status ? modifier_scale : 1.0f; 291 const float coef = modifier_status.value ? modifier_scale : 1.0f;
270 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF); 292 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
271 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF); 293 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
272 return status; 294 return status;
@@ -287,9 +309,9 @@ private:
287 bool down_status{}; 309 bool down_status{};
288 bool left_status{}; 310 bool left_status{};
289 bool right_status{}; 311 bool right_status{};
290 bool modifier_status{};
291 float last_x_axis_value{}; 312 float last_x_axis_value{};
292 float last_y_axis_value{}; 313 float last_y_axis_value{};
314 Common::Input::ButtonStatus modifier_status{};
293 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false}; 315 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
294 std::chrono::time_point<std::chrono::steady_clock> last_update; 316 std::chrono::time_point<std::chrono::steady_clock> last_update;
295}; 317};
diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp
index ece1e3b32..f1b57d03a 100644
--- a/src/input_common/helpers/touch_from_buttons.cpp
+++ b/src/input_common/helpers/touch_from_buttons.cpp
@@ -4,7 +4,6 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include "common/settings.h" 6#include "common/settings.h"
7#include "core/frontend/framebuffer_layout.h"
8#include "input_common/helpers/touch_from_buttons.h" 7#include "input_common/helpers/touch_from_buttons.h"
9 8
10namespace InputCommon { 9namespace InputCommon {
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 3bfdf41ba..7d9d4f7ba 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -140,12 +140,12 @@ bool VKScheduler::UpdateRescaling(bool is_rescaling) {
140void VKScheduler::WorkerThread(std::stop_token stop_token) { 140void VKScheduler::WorkerThread(std::stop_token stop_token) {
141 Common::SetCurrentThreadName("yuzu:VulkanWorker"); 141 Common::SetCurrentThreadName("yuzu:VulkanWorker");
142 do { 142 do {
143 if (work_queue.empty()) {
144 wait_cv.notify_all();
145 }
146 std::unique_ptr<CommandChunk> work; 143 std::unique_ptr<CommandChunk> work;
147 { 144 {
148 std::unique_lock lock{work_mutex}; 145 std::unique_lock lock{work_mutex};
146 if (work_queue.empty()) {
147 wait_cv.notify_all();
148 }
149 work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); }); 149 work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); });
150 if (stop_token.stop_requested()) { 150 if (stop_token.stop_requested()) {
151 continue; 151 continue;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 1b06c9296..e69aa136b 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -146,6 +146,7 @@ private:
146 using FuncType = TypedCommand<T>; 146 using FuncType = TypedCommand<T>;
147 static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); 147 static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large");
148 148
149 recorded_counts++;
149 command_offset = Common::AlignUp(command_offset, alignof(FuncType)); 150 command_offset = Common::AlignUp(command_offset, alignof(FuncType));
150 if (command_offset > sizeof(data) - sizeof(FuncType)) { 151 if (command_offset > sizeof(data) - sizeof(FuncType)) {
151 return false; 152 return false;
@@ -167,7 +168,7 @@ private:
167 } 168 }
168 169
169 bool Empty() const { 170 bool Empty() const {
170 return command_offset == 0; 171 return recorded_counts == 0;
171 } 172 }
172 173
173 bool HasSubmit() const { 174 bool HasSubmit() const {
@@ -178,6 +179,7 @@ private:
178 Command* first = nullptr; 179 Command* first = nullptr;
179 Command* last = nullptr; 180 Command* last = nullptr;
180 181
182 size_t recorded_counts = 0;
181 size_t command_offset = 0; 183 size_t command_offset = 0;
182 bool submit = false; 184 bool submit = false;
183 alignas(std::max_align_t) std::array<u8, 0x8000> data{}; 185 alignas(std::max_align_t) std::array<u8, 0x8000> data{};
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 4239c17f5..4104928d1 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -257,7 +257,7 @@ void QtControllerSelectorDialog::LoadConfiguration() {
257} 257}
258 258
259void QtControllerSelectorDialog::CallConfigureVibrationDialog() { 259void QtControllerSelectorDialog::CallConfigureVibrationDialog() {
260 ConfigureVibration dialog(this); 260 ConfigureVibration dialog(this, system.HIDCore());
261 261
262 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | 262 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
263 Qt::WindowSystemMenuHint); 263 Qt::WindowSystemMenuHint);
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index d53179dbb..7c5776189 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -164,7 +164,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
164 }); 164 });
165 165
166 connect(ui->vibrationButton, &QPushButton::clicked, 166 connect(ui->vibrationButton, &QPushButton::clicked,
167 [this] { CallConfigureDialog<ConfigureVibration>(*this); }); 167 [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
168 168
169 connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] { 169 connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] {
170 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); 170 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index 6630321cb..fb168b2ca 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -70,7 +70,6 @@ void PlayerControlPreview::UpdateColors() {
70 colors.slider_arrow = QColor(14, 15, 18); 70 colors.slider_arrow = QColor(14, 15, 18);
71 colors.font2 = QColor(255, 255, 255); 71 colors.font2 = QColor(255, 255, 255);
72 colors.indicator = QColor(170, 238, 255); 72 colors.indicator = QColor(170, 238, 255);
73 colors.indicator2 = QColor(100, 255, 100);
74 colors.deadzone = QColor(204, 136, 136); 73 colors.deadzone = QColor(204, 136, 136);
75 colors.slider_button = colors.button; 74 colors.slider_button = colors.button;
76 } 75 }
@@ -88,7 +87,6 @@ void PlayerControlPreview::UpdateColors() {
88 colors.slider_arrow = QColor(65, 68, 73); 87 colors.slider_arrow = QColor(65, 68, 73);
89 colors.font2 = QColor(0, 0, 0); 88 colors.font2 = QColor(0, 0, 0);
90 colors.indicator = QColor(0, 0, 200); 89 colors.indicator = QColor(0, 0, 200);
91 colors.indicator2 = QColor(0, 150, 0);
92 colors.deadzone = QColor(170, 0, 0); 90 colors.deadzone = QColor(170, 0, 0);
93 colors.slider_button = QColor(153, 149, 149); 91 colors.slider_button = QColor(153, 149, 149);
94 } 92 }
@@ -101,6 +99,8 @@ void PlayerControlPreview::UpdateColors() {
101 colors.font = QColor(255, 255, 255); 99 colors.font = QColor(255, 255, 255);
102 colors.led_on = QColor(255, 255, 0); 100 colors.led_on = QColor(255, 255, 0);
103 colors.led_off = QColor(170, 238, 255); 101 colors.led_off = QColor(170, 238, 255);
102 colors.indicator2 = QColor(59, 165, 93);
103 colors.charging = QColor(250, 168, 26);
104 104
105 colors.left = colors.primary; 105 colors.left = colors.primary;
106 colors.right = colors.primary; 106 colors.right = colors.primary;
@@ -357,7 +357,7 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
357 DrawCircle(p, center + QPoint(26, 71), 5); 357 DrawCircle(p, center + QPoint(26, 71), 5);
358 358
359 // Draw battery 359 // Draw battery
360 DrawBattery(p, center + QPoint(-170, -140), 360 DrawBattery(p, center + QPoint(-160, -140),
361 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); 361 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
362} 362}
363 363
@@ -484,7 +484,7 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
484 DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5); 484 DrawSymbol(p, center + QPoint(-26, 66), Symbol::House, 5);
485 485
486 // Draw battery 486 // Draw battery
487 DrawBattery(p, center + QPoint(110, -140), 487 DrawBattery(p, center + QPoint(120, -140),
488 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); 488 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
489} 489}
490 490
@@ -621,9 +621,9 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
621 DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f); 621 DrawSymbol(p, center + QPoint(50, 60), Symbol::House, 4.2f);
622 622
623 // Draw battery 623 // Draw battery
624 DrawBattery(p, center + QPoint(-100, -160), 624 DrawBattery(p, center + QPoint(-200, -10),
625 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); 625 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
626 DrawBattery(p, center + QPoint(40, -160), 626 DrawBattery(p, center + QPoint(160, -10),
627 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); 627 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
628} 628}
629 629
@@ -694,12 +694,12 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
694 694
695 // ZL and ZR buttons 695 // ZL and ZR buttons
696 p.setPen(colors.outline); 696 p.setPen(colors.outline);
697 DrawTriggerButton(p, center + QPoint(-210, -130), Direction::Left, button_values[ZL]); 697 DrawTriggerButton(p, center + QPoint(-210, -120), Direction::Left, button_values[ZL]);
698 DrawTriggerButton(p, center + QPoint(210, -130), Direction::Right, button_values[ZR]); 698 DrawTriggerButton(p, center + QPoint(210, -120), Direction::Right, button_values[ZR]);
699 p.setPen(colors.transparent); 699 p.setPen(colors.transparent);
700 p.setBrush(colors.font); 700 p.setBrush(colors.font);
701 DrawSymbol(p, center + QPoint(-210, -130), Symbol::ZL, 1.5f); 701 DrawSymbol(p, center + QPoint(-210, -120), Symbol::ZL, 1.5f);
702 DrawSymbol(p, center + QPoint(210, -130), Symbol::ZR, 1.5f); 702 DrawSymbol(p, center + QPoint(210, -120), Symbol::ZR, 1.5f);
703 703
704 // Minus and Plus button 704 // Minus and Plus button
705 p.setPen(colors.outline); 705 p.setPen(colors.outline);
@@ -725,9 +725,9 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
725 DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f); 725 DrawSymbol(p, center + QPoint(161, 37), Symbol::House, 2.75f);
726 726
727 // Draw battery 727 // Draw battery
728 DrawBattery(p, center + QPoint(-200, 110), 728 DrawBattery(p, center + QPoint(-188, 95),
729 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); 729 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
730 DrawBattery(p, center + QPoint(130, 110), 730 DrawBattery(p, center + QPoint(150, 95),
731 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]); 731 battery_values[Core::HID::EmulatedDeviceIndex::RightIndex]);
732} 732}
733 733
@@ -781,12 +781,12 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
781 781
782 // ZL and ZR buttons 782 // ZL and ZR buttons
783 p.setPen(colors.outline); 783 p.setPen(colors.outline);
784 DrawTriggerButton(p, center + QPoint(-210, -130), Direction::Left, button_values[ZL]); 784 DrawTriggerButton(p, center + QPoint(-210, -120), Direction::Left, button_values[ZL]);
785 DrawTriggerButton(p, center + QPoint(210, -130), Direction::Right, button_values[ZR]); 785 DrawTriggerButton(p, center + QPoint(210, -120), Direction::Right, button_values[ZR]);
786 p.setPen(colors.transparent); 786 p.setPen(colors.transparent);
787 p.setBrush(colors.font); 787 p.setBrush(colors.font);
788 DrawSymbol(p, center + QPoint(-210, -130), Symbol::ZL, 1.5f); 788 DrawSymbol(p, center + QPoint(-210, -120), Symbol::ZL, 1.5f);
789 DrawSymbol(p, center + QPoint(210, -130), Symbol::ZR, 1.5f); 789 DrawSymbol(p, center + QPoint(210, -120), Symbol::ZR, 1.5f);
790 790
791 // Minus and Plus buttons 791 // Minus and Plus buttons
792 p.setPen(colors.outline); 792 p.setPen(colors.outline);
@@ -818,7 +818,7 @@ void PlayerControlPreview::DrawProController(QPainter& p, const QPointF center)
818 DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f); 818 DrawSymbol(p, center + QPoint(29, -56), Symbol::House, 3.9f);
819 819
820 // Draw battery 820 // Draw battery
821 DrawBattery(p, center + QPoint(-30, -160), 821 DrawBattery(p, center + QPoint(-20, -160),
822 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); 822 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
823} 823}
824 824
@@ -875,7 +875,7 @@ void PlayerControlPreview::DrawGCController(QPainter& p, const QPointF center) {
875 DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8); 875 DrawCircleButton(p, center + QPoint(0, -44), button_values[Plus], 8);
876 876
877 // Draw battery 877 // Draw battery
878 DrawBattery(p, center + QPoint(-30, -165), 878 DrawBattery(p, center + QPoint(-20, 110),
879 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]); 879 battery_values[Core::HID::EmulatedDeviceIndex::LeftIndex]);
880} 880}
881 881
@@ -1030,6 +1030,10 @@ constexpr std::array<float, 30 * 2> symbol_c = {
1030 -2.37f, 5.64f, -0.65f, 6.44f, 1.25f, 6.47f, 3.06f, 5.89f, 4.63f, 4.92f, 4.63f, 6.83f, 1030 -2.37f, 5.64f, -0.65f, 6.44f, 1.25f, 6.47f, 3.06f, 5.89f, 4.63f, 4.92f, 4.63f, 6.83f,
1031}; 1031};
1032 1032
1033constexpr std::array<float, 6 * 2> symbol_charging = {
1034 6.5f, -1.0f, 1.0f, -1.0f, 1.0f, -3.0f, -6.5f, 1.0f, -1.0f, 1.0f, -1.0f, 3.0f,
1035};
1036
1033constexpr std::array<float, 12 * 2> house = { 1037constexpr std::array<float, 12 * 2> house = {
1034 -1.3f, 0.0f, -0.93f, 0.0f, -0.93f, 1.15f, 0.93f, 1.15f, 0.93f, 0.0f, 1.3f, 0.0f, 1038 -1.3f, 0.0f, -0.93f, 0.0f, -0.93f, 1.15f, 0.93f, 1.15f, 0.93f, 0.0f, 1.3f, 0.0f,
1035 0.0f, -1.2f, -1.3f, 0.0f, -0.43f, 0.0f, -0.43f, .73f, 0.43f, .73f, 0.43f, 0.0f, 1039 0.0f, -1.2f, -1.3f, 0.0f, -0.43f, 0.0f, -0.43f, .73f, 0.43f, .73f, 0.43f, 0.0f,
@@ -2674,36 +2678,43 @@ void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center,
2674 if (battery == Common::Input::BatteryLevel::None) { 2678 if (battery == Common::Input::BatteryLevel::None) {
2675 return; 2679 return;
2676 } 2680 }
2677 p.setPen(colors.outline); 2681 // Draw outline
2682 p.setPen(QPen(colors.button, 5));
2683 p.setBrush(colors.transparent);
2684 p.drawRoundedRect(center.x(), center.y(), 34, 16, 2, 2);
2685
2686 p.setPen(QPen(colors.button, 3));
2687 p.drawRect(center.x() + 35, center.y() + 4.5f, 4, 7);
2688
2689 // Draw Battery shape
2690 p.setPen(QPen(colors.indicator2, 3));
2678 p.setBrush(colors.transparent); 2691 p.setBrush(colors.transparent);
2679 p.drawRect(center.x(), center.y(), 56, 20); 2692 p.drawRoundedRect(center.x(), center.y(), 34, 16, 2, 2);
2680 p.drawRect(center.x() + 56, center.y() + 6, 3, 8); 2693
2681 p.setBrush(colors.deadzone); 2694 p.setPen(QPen(colors.indicator2, 1));
2695 p.setBrush(colors.indicator2);
2696 p.drawRect(center.x() + 35, center.y() + 4.5f, 4, 7);
2682 switch (battery) { 2697 switch (battery) {
2683 case Common::Input::BatteryLevel::Charging: 2698 case Common::Input::BatteryLevel::Charging:
2684 p.setBrush(colors.indicator2); 2699 p.drawRect(center.x(), center.y(), 34, 16);
2685 p.drawText(center + QPoint(2, 14), tr("Charging")); 2700 p.setPen(colors.slider);
2701 p.setBrush(colors.charging);
2702 DrawSymbol(p, center + QPointF(17.0f, 8.0f), Symbol::Charging, 2.1f);
2686 break; 2703 break;
2687 case Common::Input::BatteryLevel::Full: 2704 case Common::Input::BatteryLevel::Full:
2688 p.drawRect(center.x() + 42, center.y(), 14, 20); 2705 p.drawRect(center.x(), center.y(), 34, 16);
2689 p.drawRect(center.x() + 28, center.y(), 14, 20);
2690 p.drawRect(center.x() + 14, center.y(), 14, 20);
2691 p.drawRect(center.x(), center.y(), 14, 20);
2692 break; 2706 break;
2693 case Common::Input::BatteryLevel::Medium: 2707 case Common::Input::BatteryLevel::Medium:
2694 p.drawRect(center.x() + 28, center.y(), 14, 20); 2708 p.drawRect(center.x(), center.y(), 25, 16);
2695 p.drawRect(center.x() + 14, center.y(), 14, 20);
2696 p.drawRect(center.x(), center.y(), 14, 20);
2697 break; 2709 break;
2698 case Common::Input::BatteryLevel::Low: 2710 case Common::Input::BatteryLevel::Low:
2699 p.drawRect(center.x() + 14, center.y(), 14, 20); 2711 p.drawRect(center.x(), center.y(), 17, 16);
2700 p.drawRect(center.x(), center.y(), 14, 20);
2701 break; 2712 break;
2702 case Common::Input::BatteryLevel::Critical: 2713 case Common::Input::BatteryLevel::Critical:
2703 p.drawRect(center.x(), center.y(), 14, 20); 2714 p.drawRect(center.x(), center.y(), 6, 16);
2704 break; 2715 break;
2705 case Common::Input::BatteryLevel::Empty: 2716 case Common::Input::BatteryLevel::Empty:
2706 p.drawRect(center.x(), center.y(), 5, 20); 2717 p.drawRect(center.x(), center.y(), 3, 16);
2707 break; 2718 break;
2708 default: 2719 default:
2709 break; 2720 break;
@@ -2724,6 +2735,7 @@ void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol
2724 std::array<QPointF, symbol_sl.size() / 2> sl_icon; 2735 std::array<QPointF, symbol_sl.size() / 2> sl_icon;
2725 std::array<QPointF, symbol_zr.size() / 2> zr_icon; 2736 std::array<QPointF, symbol_zr.size() / 2> zr_icon;
2726 std::array<QPointF, symbol_sr.size() / 2> sr_icon; 2737 std::array<QPointF, symbol_sr.size() / 2> sr_icon;
2738 std::array<QPointF, symbol_charging.size() / 2> charging_icon;
2727 switch (symbol) { 2739 switch (symbol) {
2728 case Symbol::House: 2740 case Symbol::House:
2729 for (std::size_t point = 0; point < house.size() / 2; ++point) { 2741 for (std::size_t point = 0; point < house.size() / 2; ++point) {
@@ -2809,6 +2821,13 @@ void PlayerControlPreview::DrawSymbol(QPainter& p, const QPointF center, Symbol
2809 } 2821 }
2810 p.drawPolygon(sr_icon.data(), static_cast<int>(sr_icon.size())); 2822 p.drawPolygon(sr_icon.data(), static_cast<int>(sr_icon.size()));
2811 break; 2823 break;
2824 case Symbol::Charging:
2825 for (std::size_t point = 0; point < symbol_charging.size() / 2; ++point) {
2826 charging_icon[point] = center + QPointF(symbol_charging[point * 2] * icon_size,
2827 symbol_charging[point * 2 + 1] * icon_size);
2828 }
2829 p.drawPolygon(charging_icon.data(), static_cast<int>(charging_icon.size()));
2830 break;
2812 } 2831 }
2813} 2832}
2814 2833
diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h
index 4cd5c3be0..3582ef77a 100644
--- a/src/yuzu/configuration/configure_input_player_widget.h
+++ b/src/yuzu/configuration/configure_input_player_widget.h
@@ -72,6 +72,7 @@ private:
72 ZL, 72 ZL,
73 ZR, 73 ZR,
74 SR, 74 SR,
75 Charging,
75 }; 76 };
76 77
77 struct ColorMapping { 78 struct ColorMapping {
@@ -94,6 +95,7 @@ private:
94 QColor slider_button{}; 95 QColor slider_button{};
95 QColor slider_arrow{}; 96 QColor slider_arrow{};
96 QColor deadzone{}; 97 QColor deadzone{};
98 QColor charging{};
97 }; 99 };
98 100
99 void UpdateColors(); 101 void UpdateColors();
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp
index adce04b27..779b6401c 100644
--- a/src/yuzu/configuration/configure_vibration.cpp
+++ b/src/yuzu/configuration/configure_vibration.cpp
@@ -9,11 +9,14 @@
9 9
10#include "common/param_package.h" 10#include "common/param_package.h"
11#include "common/settings.h" 11#include "common/settings.h"
12#include "core/hid/emulated_controller.h"
13#include "core/hid/hid_core.h"
14#include "core/hid/hid_types.h"
12#include "ui_configure_vibration.h" 15#include "ui_configure_vibration.h"
13#include "yuzu/configuration/configure_vibration.h" 16#include "yuzu/configuration/configure_vibration.h"
14 17
15ConfigureVibration::ConfigureVibration(QWidget* parent) 18ConfigureVibration::ConfigureVibration(QWidget* parent, Core::HID::HIDCore& hid_core_)
16 : QDialog(parent), ui(std::make_unique<Ui::ConfigureVibration>()) { 19 : QDialog(parent), ui(std::make_unique<Ui::ConfigureVibration>()), hid_core{hid_core_} {
17 ui->setupUi(this); 20 ui->setupUi(this);
18 21
19 vibration_groupboxes = { 22 vibration_groupboxes = {
@@ -31,6 +34,13 @@ ConfigureVibration::ConfigureVibration(QWidget* parent)
31 const auto& players = Settings::values.players.GetValue(); 34 const auto& players = Settings::values.players.GetValue();
32 35
33 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { 36 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
37 auto controller = hid_core.GetEmulatedControllerByIndex(i);
38 Core::HID::ControllerUpdateCallback engine_callback{
39 .on_change = [this,
40 i](Core::HID::ControllerTriggerType type) { VibrateController(type, i); },
41 .is_npad_service = false,
42 };
43 controller_callback_key[i] = controller->SetCallback(engine_callback);
34 vibration_groupboxes[i]->setChecked(players[i].vibration_enabled); 44 vibration_groupboxes[i]->setChecked(players[i].vibration_enabled);
35 vibration_spinboxes[i]->setValue(players[i].vibration_strength); 45 vibration_spinboxes[i]->setValue(players[i].vibration_strength);
36 } 46 }
@@ -45,7 +55,14 @@ ConfigureVibration::ConfigureVibration(QWidget* parent)
45 RetranslateUI(); 55 RetranslateUI();
46} 56}
47 57
48ConfigureVibration::~ConfigureVibration() = default; 58ConfigureVibration::~ConfigureVibration() {
59 StopVibrations();
60
61 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
62 auto controller = hid_core.GetEmulatedControllerByIndex(i);
63 controller->DeleteCallback(controller_callback_key[i]);
64 }
65};
49 66
50void ConfigureVibration::ApplyConfiguration() { 67void ConfigureVibration::ApplyConfiguration() {
51 auto& players = Settings::values.players.GetValue(); 68 auto& players = Settings::values.players.GetValue();
@@ -70,3 +87,54 @@ void ConfigureVibration::changeEvent(QEvent* event) {
70void ConfigureVibration::RetranslateUI() { 87void ConfigureVibration::RetranslateUI() {
71 ui->retranslateUi(this); 88 ui->retranslateUi(this);
72} 89}
90
91void ConfigureVibration::VibrateController(Core::HID::ControllerTriggerType type,
92 std::size_t player_index) {
93 if (type != Core::HID::ControllerTriggerType::Button) {
94 return;
95 }
96
97 auto& player = Settings::values.players.GetValue()[player_index];
98 auto controller = hid_core.GetEmulatedControllerByIndex(player_index);
99 const int vibration_strenght = vibration_spinboxes[player_index]->value();
100 const auto& buttons = controller->GetButtonsValues();
101
102 bool button_is_pressed = false;
103 for (std::size_t i = 0; i < buttons.size(); ++i) {
104 if (buttons[i].value) {
105 button_is_pressed = true;
106 break;
107 }
108 }
109
110 if (!button_is_pressed) {
111 StopVibrations();
112 return;
113 }
114
115 const int old_vibration_enabled = player.vibration_enabled;
116 const bool old_vibration_strenght = player.vibration_strength;
117 player.vibration_enabled = true;
118 player.vibration_strength = vibration_strenght;
119
120 const Core::HID::VibrationValue vibration{
121 .low_amplitude = 1.0f,
122 .low_frequency = 160.0f,
123 .high_amplitude = 1.0f,
124 .high_frequency = 320.0f,
125 };
126 controller->SetVibration(0, vibration);
127 controller->SetVibration(1, vibration);
128
129 // Restore previous values
130 player.vibration_enabled = old_vibration_enabled;
131 player.vibration_strength = old_vibration_strenght;
132}
133
134void ConfigureVibration::StopVibrations() {
135 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
136 auto controller = hid_core.GetEmulatedControllerByIndex(i);
137 controller->SetVibration(0, Core::HID::DEFAULT_VIBRATION_VALUE);
138 controller->SetVibration(1, Core::HID::DEFAULT_VIBRATION_VALUE);
139 }
140}
diff --git a/src/yuzu/configuration/configure_vibration.h b/src/yuzu/configuration/configure_vibration.h
index 37bbc2653..50b8195fa 100644
--- a/src/yuzu/configuration/configure_vibration.h
+++ b/src/yuzu/configuration/configure_vibration.h
@@ -15,11 +15,16 @@ namespace Ui {
15class ConfigureVibration; 15class ConfigureVibration;
16} 16}
17 17
18namespace Core::HID {
19enum class ControllerTriggerType;
20class HIDCore;
21} // namespace Core::HID
22
18class ConfigureVibration : public QDialog { 23class ConfigureVibration : public QDialog {
19 Q_OBJECT 24 Q_OBJECT
20 25
21public: 26public:
22 explicit ConfigureVibration(QWidget* parent); 27 explicit ConfigureVibration(QWidget* parent, Core::HID::HIDCore& hid_core_);
23 ~ConfigureVibration() override; 28 ~ConfigureVibration() override;
24 29
25 void ApplyConfiguration(); 30 void ApplyConfiguration();
@@ -27,14 +32,21 @@ public:
27private: 32private:
28 void changeEvent(QEvent* event) override; 33 void changeEvent(QEvent* event) override;
29 void RetranslateUI(); 34 void RetranslateUI();
35 void VibrateController(Core::HID::ControllerTriggerType type, std::size_t player_index);
36 void StopVibrations();
30 37
31 std::unique_ptr<Ui::ConfigureVibration> ui; 38 std::unique_ptr<Ui::ConfigureVibration> ui;
32 39
33 static constexpr std::size_t NUM_PLAYERS = 8; 40 static constexpr std::size_t NUM_PLAYERS = 8;
34 41
35 // Groupboxes encapsulating the vibration strength spinbox. 42 /// Groupboxes encapsulating the vibration strength spinbox.
36 std::array<QGroupBox*, NUM_PLAYERS> vibration_groupboxes; 43 std::array<QGroupBox*, NUM_PLAYERS> vibration_groupboxes;
37 44
38 // Spinboxes representing the vibration strength percentage. 45 /// Spinboxes representing the vibration strength percentage.
39 std::array<QSpinBox*, NUM_PLAYERS> vibration_spinboxes; 46 std::array<QSpinBox*, NUM_PLAYERS> vibration_spinboxes;
47
48 /// Callback index to stop the controllers events
49 std::array<int, NUM_PLAYERS> controller_callback_key;
50
51 Core::HID::HIDCore& hid_core;
40}; 52};
diff --git a/src/yuzu/configuration/configure_vibration.ui b/src/yuzu/configuration/configure_vibration.ui
index efdf317a9..447a18eb1 100644
--- a/src/yuzu/configuration/configure_vibration.ui
+++ b/src/yuzu/configuration/configure_vibration.ui
@@ -17,6 +17,13 @@
17 <string notr="true"/> 17 <string notr="true"/>
18 </property> 18 </property>
19 <layout class="QVBoxLayout"> 19 <layout class="QVBoxLayout">
20 <item row="0" column="0" colspan="4">
21 <widget class="QLabel" name="label_1">
22 <property name="text">
23 <string>Press any controller button to vibrate the controller.</string>
24 </property>
25 </widget>
26 </item>
20 <item> 27 <item>
21 <widget class="QGroupBox" name="vibrationStrengthGroup"> 28 <widget class="QGroupBox" name="vibrationStrengthGroup">
22 <property name="title"> 29 <property name="title">