summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/frontend/input.h7
-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/settings.cpp50
-rw-r--r--src/core/settings.h318
-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_general.cpp36
-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_cmd/config.cpp286
33 files changed, 4618 insertions, 1321 deletions
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 39bdf4e21..16fdcd376 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -132,4 +132,11 @@ using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>
132 */ 132 */
133using TouchDevice = InputDevice<std::tuple<float, float, bool>>; 133using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
134 134
135/**
136 * A mouse device is an input device that returns a tuple of two floats and four ints.
137 * The first two floats are X and Y device coordinates of the mouse (from 0-1).
138 * The s32s are the mouse wheel.
139 */
140using MouseDevice = InputDevice<std::tuple<float, float, s32, s32>>;
141
135} // namespace Input 142} // namespace Input
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 3d100763f..e76c83aee 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -6,9 +6,14 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/debug_pad.h" 8#include "core/hle/service/hid/controllers/debug_pad.h"
9#include "core/settings.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
11 12
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
14constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
16
12Controller_DebugPad::Controller_DebugPad() = default; 17Controller_DebugPad::Controller_DebugPad() = default;
13Controller_DebugPad::~Controller_DebugPad() = default; 18Controller_DebugPad::~Controller_DebugPad() = default;
14 19
@@ -33,10 +38,43 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
33 38
34 cur_entry.sampling_number = last_entry.sampling_number + 1; 39 cur_entry.sampling_number = last_entry.sampling_number + 1;
35 cur_entry.sampling_number2 = cur_entry.sampling_number; 40 cur_entry.sampling_number2 = cur_entry.sampling_number;
36 // TODO(ogniK): Update debug pad states 41 cur_entry.attribute.connected.Assign(1);
42 auto& pad = cur_entry.pad_state;
43
44 using namespace Settings::NativeButton;
45 pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
46 pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
47 pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
48 pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
49 pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
50 pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
51 pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
52 pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
53 pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
54 pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
55 pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
56 pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
57 pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
58 pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
59
60 const auto [stick_l_x_f, stick_l_y_f] =
61 analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
62 const auto [stick_r_x_f, stick_r_y_f] =
63 analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
64 cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
65 cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
66 cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
67 cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
37 68
38 std::memcpy(data, &shared_memory, sizeof(SharedMemory)); 69 std::memcpy(data, &shared_memory, sizeof(SharedMemory));
39} 70}
40 71
41void Controller_DebugPad::OnLoadInputDevices() {} 72void Controller_DebugPad::OnLoadInputDevices() {
73 std::transform(Settings::values.debug_pad_buttons.begin(),
74 Settings::values.debug_pad_buttons.end(), buttons.begin(),
75 Input::CreateDevice<Input::ButtonDevice>);
76 std::transform(Settings::values.debug_pad_analogs.begin(),
77 Settings::values.debug_pad_analogs.end(), analogs.begin(),
78 Input::CreateDevice<Input::AnalogDevice>);
79}
42} // namespace Service::HID 80} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 62b4f2682..68b734248 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -5,10 +5,13 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/bit_field.h"
8#include "common/common_funcs.h" 9#include "common/common_funcs.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/swap.h" 11#include "common/swap.h"
12#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 13#include "core/hle/service/hid/controllers/controller_base.h"
14#include "core/settings.h"
12 15
13namespace Service::HID { 16namespace Service::HID {
14class Controller_DebugPad final : public ControllerBase { 17class Controller_DebugPad final : public ControllerBase {
@@ -35,11 +38,40 @@ private:
35 }; 38 };
36 static_assert(sizeof(AnalogStick) == 0x8); 39 static_assert(sizeof(AnalogStick) == 0x8);
37 40
41 struct PadState {
42 union {
43 u32_le raw{};
44 BitField<0, 1, u32_le> a;
45 BitField<1, 1, u32_le> b;
46 BitField<2, 1, u32_le> x;
47 BitField<3, 1, u32_le> y;
48 BitField<4, 1, u32_le> l;
49 BitField<5, 1, u32_le> r;
50 BitField<6, 1, u32_le> zl;
51 BitField<7, 1, u32_le> zr;
52 BitField<8, 1, u32_le> plus;
53 BitField<9, 1, u32_le> minus;
54 BitField<10, 1, u32_le> d_left;
55 BitField<11, 1, u32_le> d_up;
56 BitField<12, 1, u32_le> d_right;
57 BitField<13, 1, u32_le> d_down;
58 };
59 };
60 static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
61
62 struct Attributes {
63 union {
64 u32_le raw{};
65 BitField<0, 1, u32_le> connected;
66 };
67 };
68 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
69
38 struct PadStates { 70 struct PadStates {
39 s64_le sampling_number; 71 s64_le sampling_number;
40 s64_le sampling_number2; 72 s64_le sampling_number2;
41 u32_le attribute; 73 Attributes attribute;
42 u32_le button_state; 74 PadState pad_state;
43 AnalogStick r_stick; 75 AnalogStick r_stick;
44 AnalogStick l_stick; 76 AnalogStick l_stick;
45 }; 77 };
@@ -52,5 +84,10 @@ private:
52 }; 84 };
53 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); 85 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
54 SharedMemory shared_memory{}; 86 SharedMemory shared_memory{};
87
88 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
89 buttons;
90 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
91 analogs;
55}; 92};
56} // namespace Service::HID 93} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index ccfbce9ac..ca75adc2b 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -6,9 +6,11 @@
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/keyboard.h" 8#include "core/hle/service/hid/controllers/keyboard.h"
9#include "core/settings.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
13constexpr u8 KEYS_PER_BYTE = 8;
12 14
13Controller_Keyboard::Controller_Keyboard() = default; 15Controller_Keyboard::Controller_Keyboard() = default;
14Controller_Keyboard::~Controller_Keyboard() = default; 16Controller_Keyboard::~Controller_Keyboard() = default;
@@ -34,10 +36,24 @@ void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
34 36
35 cur_entry.sampling_number = last_entry.sampling_number + 1; 37 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number; 38 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update keyboard states 39
40 for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
41 for (std::size_t k = 0; k < KEYS_PER_BYTE; ++k) {
42 cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << k);
43 }
44 }
45
46 for (std::size_t i = 0; i < keyboard_mods.size(); ++i) {
47 cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i);
48 }
38 49
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 50 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40} 51}
41 52
42void Controller_Keyboard::OnLoadInputDevices() {} 53void Controller_Keyboard::OnLoadInputDevices() {
54 std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(),
55 keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
56 std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
57 keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
58}
43} // namespace Service::HID 59} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 493e68fce..f52775456 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -8,7 +8,9 @@
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/settings.h"
12 14
13namespace Service::HID { 15namespace Service::HID {
14class Controller_Keyboard final : public ControllerBase { 16class Controller_Keyboard final : public ControllerBase {
@@ -46,5 +48,10 @@ private:
46 }; 48 };
47 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size"); 49 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
48 SharedMemory shared_memory{}; 50 SharedMemory shared_memory{};
51
52 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys>
53 keyboard_keys;
54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
55 keyboard_mods;
49}; 56};
50} // namespace Service::HID 57} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 4e246a57d..63391dbe9 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -5,6 +5,7 @@
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h"
8#include "core/hle/service/hid/controllers/mouse.h" 9#include "core/hle/service/hid/controllers/mouse.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
@@ -14,7 +15,6 @@ Controller_Mouse::Controller_Mouse() = default;
14Controller_Mouse::~Controller_Mouse() = default; 15Controller_Mouse::~Controller_Mouse() = default;
15 16
16void Controller_Mouse::OnInit() {} 17void Controller_Mouse::OnInit() {}
17
18void Controller_Mouse::OnRelease() {} 18void Controller_Mouse::OnRelease() {}
19 19
20void Controller_Mouse::OnUpdate(u8* data, std::size_t size) { 20void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
@@ -34,10 +34,29 @@ void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
34 34
35 cur_entry.sampling_number = last_entry.sampling_number + 1; 35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number; 36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update mouse states 37
38 if (Settings::values.mouse_enabled) {
39 const auto [px, py, sx, sy] = mouse_device->GetStatus();
40 const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width);
41 const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height);
42 cur_entry.x = x;
43 cur_entry.y = y;
44 cur_entry.delta_x = x - last_entry.x;
45 cur_entry.delta_y = y - last_entry.y;
46 cur_entry.mouse_wheel_x = sx;
47 cur_entry.mouse_wheel_y = sy;
48
49 for (std::size_t i = 0; i < mouse_button_devices.size(); ++i) {
50 cur_entry.button |= (mouse_button_devices[i]->GetStatus() << i);
51 }
52 }
38 53
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); 54 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40} 55}
41 56
42void Controller_Mouse::OnLoadInputDevices() {} 57void Controller_Mouse::OnLoadInputDevices() {
58 mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
59 std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
60 mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
61}
43} // namespace Service::HID 62} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 543b0b71f..70b654d07 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -7,7 +7,9 @@
7#include <array> 7#include <array>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/swap.h" 9#include "common/swap.h"
10#include "core/frontend/input.h"
10#include "core/hle/service/hid/controllers/controller_base.h" 11#include "core/hle/service/hid/controllers/controller_base.h"
12#include "core/settings.h"
11 13
12namespace Service::HID { 14namespace Service::HID {
13class Controller_Mouse final : public ControllerBase { 15class Controller_Mouse final : public ControllerBase {
@@ -35,7 +37,8 @@ private:
35 s32_le y; 37 s32_le y;
36 s32_le delta_x; 38 s32_le delta_x;
37 s32_le delta_y; 39 s32_le delta_y;
38 s32_le mouse_wheel; 40 s32_le mouse_wheel_x;
41 s32_le mouse_wheel_y;
39 s32_le button; 42 s32_le button;
40 s32_le attribute; 43 s32_le attribute;
41 }; 44 };
@@ -46,5 +49,9 @@ private:
46 std::array<MouseState, 17> mouse_states; 49 std::array<MouseState, 17> mouse_states;
47 }; 50 };
48 SharedMemory shared_memory{}; 51 SharedMemory shared_memory{};
52
53 std::unique_ptr<Input::MouseDevice> mouse_device;
54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
55 mouse_button_devices;
49}; 56};
50} // namespace Service::HID 57} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 205e4fd14..46604887c 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -17,22 +17,13 @@
17#include "core/settings.h" 17#include "core/settings.h"
18 18
19namespace Service::HID { 19namespace Service::HID {
20
21constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
22constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
23constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
24constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
25constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 20constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
26constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 21constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
27constexpr std::size_t NPAD_OFFSET = 0x9A00; 22constexpr std::size_t NPAD_OFFSET = 0x9A00;
28constexpr u32 BATTERY_FULL = 2; 23constexpr u32 BATTERY_FULL = 2;
29constexpr u32 NPAD_HANDHELD = 32;
30constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
31constexpr u32 MAX_NPAD_ID = 7; 24constexpr u32 MAX_NPAD_ID = 7;
32constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER =
33 Controller_NPad::NPadControllerType::JoyDual;
34constexpr std::array<u32, 10> npad_id_list{ 25constexpr std::array<u32, 10> npad_id_list{
35 0, 1, 2, 3, 4, 5, 6, 7, 32, 16, 26 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
36}; 27};
37 28
38enum class JoystickId : std::size_t { 29enum class JoystickId : std::size_t {
@@ -40,7 +31,23 @@ enum class JoystickId : std::size_t {
40 Joystick_Right, 31 Joystick_Right,
41}; 32};
42 33
43static std::size_t NPadIdToIndex(u32 npad_id) { 34static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
35 switch (type) {
36 case Settings::ControllerType::ProController:
37 return Controller_NPad::NPadControllerType::ProController;
38 case Settings::ControllerType::DualJoycon:
39 return Controller_NPad::NPadControllerType::JoyDual;
40 case Settings::ControllerType::LeftJoycon:
41 return Controller_NPad::NPadControllerType::JoyLeft;
42 case Settings::ControllerType::RightJoycon:
43 return Controller_NPad::NPadControllerType::JoyRight;
44 default:
45 UNREACHABLE();
46 return Controller_NPad::NPadControllerType::JoyDual;
47 }
48}
49
50std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
44 switch (npad_id) { 51 switch (npad_id) {
45 case 0: 52 case 0:
46 case 1: 53 case 1:
@@ -63,6 +70,27 @@ static std::size_t NPadIdToIndex(u32 npad_id) {
63 } 70 }
64} 71}
65 72
73u32 Controller_NPad::IndexToNPad(std::size_t index) {
74 switch (index) {
75 case 0:
76 case 1:
77 case 2:
78 case 3:
79 case 4:
80 case 5:
81 case 6:
82 case 7:
83 return static_cast<u32>(index);
84 case 8:
85 return NPAD_HANDHELD;
86 case 9:
87 return NPAD_UNKNOWN;
88 default:
89 UNIMPLEMENTED_MSG("Unknown npad index {}", index);
90 return 0;
91 };
92}
93
66Controller_NPad::Controller_NPad() = default; 94Controller_NPad::Controller_NPad() = default;
67Controller_NPad::~Controller_NPad() = default; 95Controller_NPad::~Controller_NPad() = default;
68 96
@@ -79,22 +107,32 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
79 controller.joy_styles.handheld.Assign(1); 107 controller.joy_styles.handheld.Assign(1);
80 controller.device_type.handheld.Assign(1); 108 controller.device_type.handheld.Assign(1);
81 controller.pad_assignment = NPadAssignments::Dual; 109 controller.pad_assignment = NPadAssignments::Dual;
110 controller.properties.is_vertical.Assign(1);
111 controller.properties.use_plus.Assign(1);
112 controller.properties.use_minus.Assign(1);
82 break; 113 break;
83 case NPadControllerType::JoyDual: 114 case NPadControllerType::JoyDual:
84 controller.joy_styles.joycon_dual.Assign(1); 115 controller.joy_styles.joycon_dual.Assign(1);
85 controller.device_type.joycon_left.Assign(1); 116 controller.device_type.joycon_left.Assign(1);
86 controller.device_type.joycon_right.Assign(1); 117 controller.device_type.joycon_right.Assign(1);
118 controller.properties.is_vertical.Assign(1);
119 controller.properties.use_plus.Assign(1);
120 controller.properties.use_minus.Assign(1);
87 controller.pad_assignment = NPadAssignments::Dual; 121 controller.pad_assignment = NPadAssignments::Dual;
88 break; 122 break;
89 case NPadControllerType::JoyLeft: 123 case NPadControllerType::JoyLeft:
90 controller.joy_styles.joycon_left.Assign(1); 124 controller.joy_styles.joycon_left.Assign(1);
91 controller.device_type.joycon_left.Assign(1); 125 controller.device_type.joycon_left.Assign(1);
92 controller.pad_assignment = NPadAssignments::Dual; 126 controller.properties.is_horizontal.Assign(1);
127 controller.properties.use_minus.Assign(1);
128 controller.pad_assignment = NPadAssignments::Single;
93 break; 129 break;
94 case NPadControllerType::JoyRight: 130 case NPadControllerType::JoyRight:
95 controller.joy_styles.joycon_right.Assign(1); 131 controller.joy_styles.joycon_right.Assign(1);
96 controller.device_type.joycon_right.Assign(1); 132 controller.device_type.joycon_right.Assign(1);
97 controller.pad_assignment = NPadAssignments::Dual; 133 controller.properties.is_horizontal.Assign(1);
134 controller.properties.use_plus.Assign(1);
135 controller.pad_assignment = NPadAssignments::Single;
98 break; 136 break;
99 case NPadControllerType::Pokeball: 137 case NPadControllerType::Pokeball:
100 controller.joy_styles.pokeball.Assign(1); 138 controller.joy_styles.pokeball.Assign(1);
@@ -104,6 +142,9 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
104 case NPadControllerType::ProController: 142 case NPadControllerType::ProController:
105 controller.joy_styles.pro_controller.Assign(1); 143 controller.joy_styles.pro_controller.Assign(1);
106 controller.device_type.pro_controller.Assign(1); 144 controller.device_type.pro_controller.Assign(1);
145 controller.properties.is_vertical.Assign(1);
146 controller.properties.use_plus.Assign(1);
147 controller.properties.use_minus.Assign(1);
107 controller.pad_assignment = NPadAssignments::Single; 148 controller.pad_assignment = NPadAssignments::Single;
108 break; 149 break;
109 } 150 }
@@ -113,14 +154,12 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
113 controller.single_color.button_color = 0; 154 controller.single_color.button_color = 0;
114 155
115 controller.dual_color_error = ColorReadError::ReadOk; 156 controller.dual_color_error = ColorReadError::ReadOk;
116 controller.left_color.body_color = JOYCON_BODY_NEON_BLUE; 157 controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left;
117 controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE; 158 controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left;
118 controller.right_color.body_color = JOYCON_BODY_NEON_RED; 159 controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right;
119 controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED; 160 controller.right_color.button_color =
120 161 Settings::values.players[controller_idx].button_color_right;
121 controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations 162
122 controller.properties.use_plus.Assign(1);
123 controller.properties.use_minus.Assign(1);
124 controller.battery_level[0] = BATTERY_FULL; 163 controller.battery_level[0] = BATTERY_FULL;
125 controller.battery_level[1] = BATTERY_FULL; 164 controller.battery_level[1] = BATTERY_FULL;
126 controller.battery_level[2] = BATTERY_FULL; 165 controller.battery_level[2] = BATTERY_FULL;
@@ -144,26 +183,109 @@ void Controller_NPad::OnInit() {
144 style.pro_controller.Assign(1); 183 style.pro_controller.Assign(1);
145 style.pokeball.Assign(1); 184 style.pokeball.Assign(1);
146 } 185 }
186
187 std::transform(
188 Settings::values.players.begin(), Settings::values.players.end(),
189 connected_controllers.begin(), [](const Settings::PlayerInput& player) {
190 return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected};
191 });
192
193 std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
194 [](const ControllerHolder& holder) { return holder.is_connected; });
195
196 // Account for handheld
197 if (connected_controllers[8].is_connected)
198 connected_controllers[8].type = NPadControllerType::Handheld;
199
200 supported_npad_id_types.resize(npad_id_list.size());
201 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
202 npad_id_list.size() * sizeof(u32));
203
204 // Add a default dual joycon controller if none are present.
147 if (std::none_of(connected_controllers.begin(), connected_controllers.end(), 205 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
148 [](const ControllerHolder& controller) { return controller.is_connected; })) { 206 [](const ControllerHolder& controller) { return controller.is_connected; })) {
149 supported_npad_id_types.resize(npad_id_list.size()); 207 supported_npad_id_types.resize(npad_id_list.size());
150 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 208 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
151 npad_id_list.size() * sizeof(u32)); 209 npad_id_list.size() * sizeof(u32));
152 AddNewController(PREFERRED_CONTROLLER); 210 AddNewController(NPadControllerType::JoyDual);
211 }
212
213 for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
214 const auto& controller = connected_controllers[i];
215 if (controller.is_connected) {
216 AddNewControllerAt(controller.type, IndexToNPad(i));
217 }
153 } 218 }
154} 219}
155 220
156void Controller_NPad::OnLoadInputDevices() { 221void Controller_NPad::OnLoadInputDevices() {
157 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 222 const auto& players = Settings::values.players;
158 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, 223 for (std::size_t i = 0; i < players.size(); ++i) {
159 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); 224 std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
160 std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 225 players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
161 Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END, 226 buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>);
162 sticks.begin(), Input::CreateDevice<Input::AnalogDevice>); 227 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
228 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
229 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
230 }
163} 231}
164 232
165void Controller_NPad::OnRelease() {} 233void Controller_NPad::OnRelease() {}
166 234
235void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
236 const auto controller_idx = NPadIdToIndex(npad_id);
237 const auto controller_type = connected_controllers[controller_idx].type;
238 if (!connected_controllers[controller_idx].is_connected) {
239 return;
240 }
241 auto& pad_state = npad_pad_states[controller_idx].pad_states;
242 auto& lstick_entry = npad_pad_states[controller_idx].l_stick;
243 auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
244 const auto& button_state = buttons[controller_idx];
245 const auto& analog_state = sticks[controller_idx];
246
247 using namespace Settings::NativeButton;
248 pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
249 pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
250 pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
251 pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
252 pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
253 pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
254 pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
255 pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
256 pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
257 pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
258 pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
259 pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
260
261 pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
262 pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
263 pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
264 pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
265
266 pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
267 pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
268 pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
269 pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
270
271 pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
272 pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
273 pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
274 pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
275
276 pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
277 pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
278
279 const auto [stick_l_x_f, stick_l_y_f] =
280 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
281 const auto [stick_r_x_f, stick_r_y_f] =
282 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
283 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
284 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
285 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
286 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
287}
288
167void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { 289void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
168 if (!IsControllerActivated()) 290 if (!IsControllerActivated())
169 return; 291 return;
@@ -199,97 +321,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
199 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { 321 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
200 continue; 322 continue;
201 } 323 }
202 324 const u32 npad_index = static_cast<u32>(i);
203 // Pad states 325 RequestPadStateUpdate(npad_index);
204 ControllerPadState pad_state{}; 326 auto& pad_state = npad_pad_states[npad_index];
205 using namespace Settings::NativeButton;
206 pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
207 pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
208 pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
209 pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
210 pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
211 pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
212 pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
213 pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
214 pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
215 pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
216 pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
217 pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
218
219 pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
220 pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
221 pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
222 pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
223
224 pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
225 pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
226 pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
227 pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
228
229 pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
230 pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
231 pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
232 pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
233
234 pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
235 pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
236
237 AnalogPosition lstick_entry{};
238 AnalogPosition rstick_entry{};
239
240 const auto [stick_l_x_f, stick_l_y_f] =
241 sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
242 const auto [stick_r_x_f, stick_r_y_f] =
243 sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
244 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
245 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
246 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
247 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
248
249 if (controller_type == NPadControllerType::JoyLeft ||
250 controller_type == NPadControllerType::JoyRight) {
251 if (npad.properties.is_horizontal) {
252 ControllerPadState state{};
253 AnalogPosition temp_lstick_entry{};
254 AnalogPosition temp_rstick_entry{};
255 if (controller_type == NPadControllerType::JoyLeft) {
256 state.d_down.Assign(pad_state.d_left.Value());
257 state.d_left.Assign(pad_state.d_up.Value());
258 state.d_right.Assign(pad_state.d_down.Value());
259 state.d_up.Assign(pad_state.d_right.Value());
260 state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
261 state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
262
263 state.zl.Assign(pad_state.zl.Value());
264 state.plus.Assign(pad_state.minus.Value());
265
266 temp_lstick_entry = lstick_entry;
267 temp_rstick_entry = rstick_entry;
268 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
269 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
270 temp_lstick_entry.y *= -1;
271 } else if (controller_type == NPadControllerType::JoyRight) {
272 state.x.Assign(pad_state.a.Value());
273 state.a.Assign(pad_state.b.Value());
274 state.b.Assign(pad_state.y.Value());
275 state.y.Assign(pad_state.b.Value());
276
277 state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
278 state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
279 state.zr.Assign(pad_state.zr.Value());
280 state.plus.Assign(pad_state.plus.Value());
281
282 temp_lstick_entry = lstick_entry;
283 temp_rstick_entry = rstick_entry;
284 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
285 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
286 temp_rstick_entry.x *= -1;
287 }
288 pad_state.raw = state.raw;
289 lstick_entry = temp_lstick_entry;
290 rstick_entry = temp_rstick_entry;
291 }
292 }
293 327
294 auto& main_controller = 328 auto& main_controller =
295 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index]; 329 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
@@ -304,8 +338,51 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
304 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; 338 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
305 339
306 if (hold_type == NpadHoldType::Horizontal) { 340 if (hold_type == NpadHoldType::Horizontal) {
307 // TODO(ogniK): Remap buttons for different orientations 341 ControllerPadState state{};
342 AnalogPosition temp_lstick_entry{};
343 AnalogPosition temp_rstick_entry{};
344 if (controller_type == NPadControllerType::JoyLeft) {
345 state.d_down.Assign(pad_state.pad_states.d_left.Value());
346 state.d_left.Assign(pad_state.pad_states.d_up.Value());
347 state.d_right.Assign(pad_state.pad_states.d_down.Value());
348 state.d_up.Assign(pad_state.pad_states.d_right.Value());
349 state.l.Assign(pad_state.pad_states.l.Value() |
350 pad_state.pad_states.left_sl.Value());
351 state.r.Assign(pad_state.pad_states.r.Value() |
352 pad_state.pad_states.left_sr.Value());
353
354 state.zl.Assign(pad_state.pad_states.zl.Value());
355 state.plus.Assign(pad_state.pad_states.minus.Value());
356
357 temp_lstick_entry = pad_state.l_stick;
358 temp_rstick_entry = pad_state.r_stick;
359 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
360 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
361 temp_lstick_entry.y *= -1;
362 } else if (controller_type == NPadControllerType::JoyRight) {
363 state.x.Assign(pad_state.pad_states.a.Value());
364 state.a.Assign(pad_state.pad_states.b.Value());
365 state.b.Assign(pad_state.pad_states.y.Value());
366 state.y.Assign(pad_state.pad_states.b.Value());
367
368 state.l.Assign(pad_state.pad_states.l.Value() |
369 pad_state.pad_states.right_sl.Value());
370 state.r.Assign(pad_state.pad_states.r.Value() |
371 pad_state.pad_states.right_sr.Value());
372 state.zr.Assign(pad_state.pad_states.zr.Value());
373 state.plus.Assign(pad_state.pad_states.plus.Value());
374
375 temp_lstick_entry = pad_state.l_stick;
376 temp_rstick_entry = pad_state.r_stick;
377 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
378 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
379 temp_rstick_entry.x *= -1;
380 }
381 pad_state.pad_states.raw = state.raw;
382 pad_state.l_stick = temp_lstick_entry;
383 pad_state.r_stick = temp_rstick_entry;
308 } 384 }
385
309 libnx_entry.connection_status.raw = 0; 386 libnx_entry.connection_status.raw = 0;
310 387
311 switch (controller_type) { 388 switch (controller_type) {
@@ -316,9 +393,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
316 handheld_entry.connection_status.IsRightJoyConnected.Assign(1); 393 handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
317 handheld_entry.connection_status.IsLeftJoyWired.Assign(1); 394 handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
318 handheld_entry.connection_status.IsRightJoyWired.Assign(1); 395 handheld_entry.connection_status.IsRightJoyWired.Assign(1);
319 handheld_entry.pad_states.raw = pad_state.raw; 396 handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
320 handheld_entry.l_stick = lstick_entry; 397 handheld_entry.pad.l_stick = pad_state.l_stick;
321 handheld_entry.r_stick = rstick_entry; 398 handheld_entry.pad.r_stick = pad_state.r_stick;
322 break; 399 break;
323 case NPadControllerType::JoyDual: 400 case NPadControllerType::JoyDual:
324 dual_entry.connection_status.raw = 0; 401 dual_entry.connection_status.raw = 0;
@@ -331,25 +408,25 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
331 libnx_entry.connection_status.IsRightJoyConnected.Assign(1); 408 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
332 libnx_entry.connection_status.IsConnected.Assign(1); 409 libnx_entry.connection_status.IsConnected.Assign(1);
333 410
334 dual_entry.pad_states.raw = pad_state.raw; 411 dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
335 dual_entry.l_stick = lstick_entry; 412 dual_entry.pad.l_stick = pad_state.l_stick;
336 dual_entry.r_stick = rstick_entry; 413 dual_entry.pad.r_stick = pad_state.r_stick;
337 break; 414 break;
338 case NPadControllerType::JoyLeft: 415 case NPadControllerType::JoyLeft:
339 left_entry.connection_status.raw = 0; 416 left_entry.connection_status.raw = 0;
340 417
341 left_entry.connection_status.IsConnected.Assign(1); 418 left_entry.connection_status.IsConnected.Assign(1);
342 left_entry.pad_states.raw = pad_state.raw; 419 left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
343 left_entry.l_stick = lstick_entry; 420 left_entry.pad.l_stick = pad_state.l_stick;
344 left_entry.r_stick = rstick_entry; 421 left_entry.pad.r_stick = pad_state.r_stick;
345 break; 422 break;
346 case NPadControllerType::JoyRight: 423 case NPadControllerType::JoyRight:
347 right_entry.connection_status.raw = 0; 424 right_entry.connection_status.raw = 0;
348 425
349 right_entry.connection_status.IsConnected.Assign(1); 426 right_entry.connection_status.IsConnected.Assign(1);
350 right_entry.pad_states.raw = pad_state.raw; 427 right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
351 right_entry.l_stick = lstick_entry; 428 right_entry.pad.l_stick = pad_state.l_stick;
352 right_entry.r_stick = rstick_entry; 429 right_entry.pad.r_stick = pad_state.r_stick;
353 break; 430 break;
354 case NPadControllerType::Pokeball: 431 case NPadControllerType::Pokeball:
355 pokeball_entry.connection_status.raw = 0; 432 pokeball_entry.connection_status.raw = 0;
@@ -357,30 +434,30 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
357 pokeball_entry.connection_status.IsConnected.Assign(1); 434 pokeball_entry.connection_status.IsConnected.Assign(1);
358 pokeball_entry.connection_status.IsWired.Assign(1); 435 pokeball_entry.connection_status.IsWired.Assign(1);
359 436
360 pokeball_entry.pad_states.raw = pad_state.raw; 437 pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
361 pokeball_entry.l_stick = lstick_entry; 438 pokeball_entry.pad.l_stick = pad_state.l_stick;
362 pokeball_entry.r_stick = rstick_entry; 439 pokeball_entry.pad.r_stick = pad_state.r_stick;
363 break; 440 break;
364 case NPadControllerType::ProController: 441 case NPadControllerType::ProController:
365 main_controller.connection_status.raw = 0; 442 main_controller.connection_status.raw = 0;
366 443
367 main_controller.connection_status.IsConnected.Assign(1); 444 main_controller.connection_status.IsConnected.Assign(1);
368 main_controller.connection_status.IsWired.Assign(1); 445 main_controller.connection_status.IsWired.Assign(1);
369 main_controller.pad_states.raw = pad_state.raw; 446 main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
370 main_controller.l_stick = lstick_entry; 447 main_controller.pad.l_stick = pad_state.l_stick;
371 main_controller.r_stick = rstick_entry; 448 main_controller.pad.r_stick = pad_state.r_stick;
372 break; 449 break;
373 } 450 }
374 451
375 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate 452 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
376 // any controllers. 453 // any controllers.
377 libnx_entry.pad_states.raw = pad_state.raw; 454 libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw;
378 libnx_entry.l_stick = lstick_entry; 455 libnx_entry.pad.l_stick = pad_state.l_stick;
379 libnx_entry.r_stick = rstick_entry; 456 libnx_entry.pad.r_stick = pad_state.r_stick;
380 } 457 }
381 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(), 458 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
382 shared_memory_entries.size() * sizeof(NPadEntry)); 459 shared_memory_entries.size() * sizeof(NPadEntry));
383} // namespace Service::HID 460}
384 461
385void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { 462void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
386 style.raw = style_set.raw; 463 style.raw = style_set.raw;
@@ -401,23 +478,24 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
401 if (!controller.is_connected) { 478 if (!controller.is_connected) {
402 continue; 479 continue;
403 } 480 }
404 if (!IsControllerSupported(PREFERRED_CONTROLLER)) { 481 const auto requested_controller =
405 const auto best_type = DecideBestController(PREFERRED_CONTROLLER); 482 i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
406 const bool is_handheld = (best_type == NPadControllerType::Handheld || 483 : NPadControllerType::Handheld;
407 PREFERRED_CONTROLLER == NPadControllerType::Handheld); 484 if (!IsControllerSupported(requested_controller)) {
485 const auto is_handheld = requested_controller == NPadControllerType::Handheld;
408 if (is_handheld) { 486 if (is_handheld) {
409 controller.type = NPadControllerType::None; 487 controller.type = NPadControllerType::None;
410 controller.is_connected = false; 488 controller.is_connected = false;
411 AddNewController(best_type); 489 AddNewController(requested_controller);
412 } else { 490 } else {
413 controller.type = best_type; 491 controller.type = requested_controller;
414 InitNewlyAddedControler(i); 492 InitNewlyAddedControler(i);
415 } 493 }
416 had_controller_update = true; 494 had_controller_update = true;
417 } 495 }
418 } 496 if (had_controller_update) {
419 if (had_controller_update) { 497 styleset_changed_event->Signal();
420 styleset_changed_event->Signal(); 498 }
421 } 499 }
422} 500}
423 501
@@ -450,15 +528,7 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
450 return; 528 return;
451 } 529 }
452 for (std::size_t i = 0; i < controller_ids.size(); i++) { 530 for (std::size_t i = 0; i < controller_ids.size(); i++) {
453 std::size_t controller_pos = i; 531 std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i));
454 // Handheld controller conversion
455 if (controller_pos == NPAD_HANDHELD) {
456 controller_pos = 8;
457 }
458 // Unknown controller conversion
459 if (controller_pos == NPAD_UNKNOWN) {
460 controller_pos = 9;
461 }
462 if (connected_controllers[controller_pos].is_connected) { 532 if (connected_controllers[controller_pos].is_connected) {
463 // TODO(ogniK): Vibrate the physical controller 533 // TODO(ogniK): Vibrate the physical controller
464 } 534 }
@@ -477,7 +547,9 @@ Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() cons
477Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { 547Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
478 return last_processed_vibration; 548 return last_processed_vibration;
479} 549}
550
480void Controller_NPad::AddNewController(NPadControllerType controller) { 551void Controller_NPad::AddNewController(NPadControllerType controller) {
552 controller = DecideBestController(controller);
481 if (controller == NPadControllerType::Handheld) { 553 if (controller == NPadControllerType::Handheld) {
482 connected_controllers[8] = {controller, true}; 554 connected_controllers[8] = {controller, true};
483 InitNewlyAddedControler(8); 555 InitNewlyAddedControler(8);
@@ -495,6 +567,18 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
495 InitNewlyAddedControler(controller_id); 567 InitNewlyAddedControler(controller_id);
496} 568}
497 569
570void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
571 controller = DecideBestController(controller);
572 if (controller == NPadControllerType::Handheld) {
573 connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
574 InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
575 return;
576 }
577
578 connected_controllers[npad_id] = {controller, true};
579 InitNewlyAddedControler(npad_id);
580}
581
498void Controller_NPad::ConnectNPad(u32 npad_id) { 582void Controller_NPad::ConnectNPad(u32 npad_id) {
499 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true; 583 connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
500} 584}
@@ -503,6 +587,36 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) {
503 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; 587 connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
504} 588}
505 589
590bool Controller_NPad::IsControllerSupported(NPadControllerType controller) {
591 if (controller == NPadControllerType::Handheld) {
592 // Handheld is not even a supported type, lets stop here
593 if (std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
594 NPAD_HANDHELD) == supported_npad_id_types.end()) {
595 return false;
596 }
597 // Handheld should not be supported in docked mode
598 if (Settings::values.use_docked_mode) {
599 return false;
600 }
601 }
602 switch (controller) {
603 case NPadControllerType::ProController:
604 return style.pro_controller;
605 case NPadControllerType::Handheld:
606 return style.handheld;
607 case NPadControllerType::JoyDual:
608 return style.joycon_dual;
609 case NPadControllerType::JoyLeft:
610 return style.joycon_left;
611 case NPadControllerType::JoyRight:
612 return style.joycon_right;
613 case NPadControllerType::Pokeball:
614 return style.pokeball;
615 default:
616 return false;
617 }
618}
619
506Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { 620Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
507 if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) { 621 if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
508 // These are controllers without led patterns 622 // These are controllers without led patterns
@@ -534,6 +648,36 @@ void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
534 can_controllers_vibrate = can_vibrate; 648 can_controllers_vibrate = can_vibrate;
535} 649}
536 650
651void Controller_NPad::ClearAllConnectedControllers() {
652 for (auto& controller : connected_controllers) {
653 if (controller.is_connected && controller.type != NPadControllerType::None) {
654 controller.type = NPadControllerType::None;
655 controller.is_connected = false;
656 }
657 }
658}
659void Controller_NPad::DisconnectAllConnectedControllers() {
660 std::for_each(connected_controllers.begin(), connected_controllers.end(),
661 [](ControllerHolder& controller) { controller.is_connected = false; });
662}
663
664void Controller_NPad::ConnectAllDisconnectedControllers() {
665 std::for_each(connected_controllers.begin(), connected_controllers.end(),
666 [](ControllerHolder& controller) {
667 if (controller.type != NPadControllerType::None && !controller.is_connected) {
668 controller.is_connected = false;
669 }
670 });
671}
672
673void Controller_NPad::ClearAllControllers() {
674 std::for_each(connected_controllers.begin(), connected_controllers.end(),
675 [](ControllerHolder& controller) {
676 controller.type = NPadControllerType::None;
677 controller.is_connected = false;
678 });
679}
680
537bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { 681bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
538 const bool support_handheld = 682 const bool support_handheld =
539 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) != 683 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index ac86985ff..ea8057b80 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -5,13 +5,18 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/bit_field.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "core/frontend/input.h" 10#include "core/frontend/input.h"
11#include "core/hle/kernel/event.h"
10#include "core/hle/service/hid/controllers/controller_base.h" 12#include "core/hle/service/hid/controllers/controller_base.h"
11#include "core/settings.h" 13#include "core/settings.h"
12 14
13namespace Service::HID { 15namespace Service::HID {
14 16
17constexpr u32 NPAD_HANDHELD = 32;
18constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
19
15class Controller_NPad final : public ControllerBase { 20class Controller_NPad final : public ControllerBase {
16public: 21public:
17 Controller_NPad(); 22 Controller_NPad();
@@ -107,11 +112,19 @@ public:
107 Vibration GetLastVibration() const; 112 Vibration GetLastVibration() const;
108 113
109 void AddNewController(NPadControllerType controller); 114 void AddNewController(NPadControllerType controller);
115 void AddNewControllerAt(NPadControllerType controller, u32 npad_id);
110 116
111 void ConnectNPad(u32 npad_id); 117 void ConnectNPad(u32 npad_id);
112 void DisconnectNPad(u32 npad_id); 118 void DisconnectNPad(u32 npad_id);
113 LedPattern GetLedPattern(u32 npad_id); 119 LedPattern GetLedPattern(u32 npad_id);
114 void SetVibrationEnabled(bool can_vibrate); 120 void SetVibrationEnabled(bool can_vibrate);
121 void ClearAllConnectedControllers();
122 void DisconnectAllConnectedControllers();
123 void ConnectAllDisconnectedControllers();
124 void ClearAllControllers();
125
126 static std::size_t NPadIdToIndex(u32 npad_id);
127 static u32 IndexToNPad(std::size_t index);
115 128
116private: 129private:
117 struct CommonHeader { 130 struct CommonHeader {
@@ -164,8 +177,11 @@ private:
164 BitField<23, 1, u64_le> r_stick_down; 177 BitField<23, 1, u64_le> r_stick_down;
165 178
166 // Not always active? 179 // Not always active?
167 BitField<24, 1, u64_le> sl; 180 BitField<24, 1, u64_le> left_sl;
168 BitField<25, 1, u64_le> sr; 181 BitField<25, 1, u64_le> left_sr;
182
183 BitField<26, 1, u64_le> right_sl;
184 BitField<27, 1, u64_le> right_sr;
169 }; 185 };
170 }; 186 };
171 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size"); 187 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
@@ -189,12 +205,17 @@ private:
189 }; 205 };
190 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size"); 206 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
191 207
192 struct GenericStates { 208 struct ControllerPad {
193 s64_le timestamp;
194 s64_le timestamp2;
195 ControllerPadState pad_states; 209 ControllerPadState pad_states;
196 AnalogPosition l_stick; 210 AnalogPosition l_stick;
197 AnalogPosition r_stick; 211 AnalogPosition r_stick;
212 };
213 static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size");
214
215 struct GenericStates {
216 s64_le timestamp;
217 s64_le timestamp2;
218 ControllerPad pad;
198 ConnectionState connection_status; 219 ConnectionState connection_status;
199 }; 220 };
200 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size"); 221 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
@@ -266,15 +287,20 @@ private:
266 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); 287 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
267 288
268 struct ControllerHolder { 289 struct ControllerHolder {
269 Controller_NPad::NPadControllerType type; 290 NPadControllerType type;
270 bool is_connected; 291 bool is_connected;
271 }; 292 };
272 293
273 NPadType style{}; 294 NPadType style{};
274 std::array<NPadEntry, 10> shared_memory_entries{}; 295 std::array<NPadEntry, 10> shared_memory_entries{};
275 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 296 std::array<
297 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
298 10>
276 buttons; 299 buttons;
277 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks; 300 std::array<
301 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
302 10>
303 sticks;
278 std::vector<u32> supported_npad_id_types{}; 304 std::vector<u32> supported_npad_id_types{};
279 NpadHoldType hold_type{NpadHoldType::Vertical}; 305 NpadHoldType hold_type{NpadHoldType::Vertical};
280 Kernel::SharedPtr<Kernel::Event> styleset_changed_event; 306 Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
@@ -285,5 +311,8 @@ private:
285 void InitNewlyAddedControler(std::size_t controller_idx); 311 void InitNewlyAddedControler(std::size_t controller_idx);
286 bool IsControllerSupported(NPadControllerType controller) const; 312 bool IsControllerSupported(NPadControllerType controller) const;
287 NPadControllerType DecideBestController(NPadControllerType priority) const; 313 NPadControllerType DecideBestController(NPadControllerType priority) const;
314 void RequestPadStateUpdate(u32 npad_id);
315 std::array<ControllerPad, 10> npad_pad_states{};
316 bool IsControllerSupported(NPadControllerType controller);
288}; 317};
289} // namespace Service::HID 318} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 43efef803..f666b1bd8 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -41,16 +41,17 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
41 41
42 const auto [x, y, pressed] = touch_device->GetStatus(); 42 const auto [x, y, pressed] = touch_device->GetStatus();
43 auto& touch_entry = cur_entry.states[0]; 43 auto& touch_entry = cur_entry.states[0];
44 if (pressed) { 44 touch_entry.attribute.raw = 0;
45 if (pressed && Settings::values.touchscreen.enabled) {
45 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); 46 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
46 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); 47 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
47 touch_entry.diameter_x = 15; 48 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
48 touch_entry.diameter_y = 15; 49 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
49 touch_entry.rotation_angle = 0; 50 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
50 const u64 tick = CoreTiming::GetTicks(); 51 const u64 tick = CoreTiming::GetTicks();
51 touch_entry.delta_time = tick - last_touch; 52 touch_entry.delta_time = tick - last_touch;
52 last_touch = tick; 53 last_touch = tick;
53 touch_entry.finger = 0; 54 touch_entry.finger = Settings::values.touchscreen.finger;
54 cur_entry.entry_count = 1; 55 cur_entry.entry_count = 1;
55 } else { 56 } else {
56 cur_entry.entry_count = 0; 57 cur_entry.entry_count = 0;
@@ -60,6 +61,6 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
60} 61}
61 62
62void Controller_Touchscreen::OnLoadInputDevices() { 63void Controller_Touchscreen::OnLoadInputDevices() {
63 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device); 64 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
64} 65}
65} // namespace Service::HID 66} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index e5db6e6ba..94cd0eba9 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/bit_field.h"
7#include "common/common_funcs.h" 8#include "common/common_funcs.h"
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/swap.h" 10#include "common/swap.h"
@@ -29,9 +30,18 @@ public:
29 void OnLoadInputDevices() override; 30 void OnLoadInputDevices() override;
30 31
31private: 32private:
33 struct Attributes {
34 union {
35 u32 raw{};
36 BitField<0, 1, u32_le> start_touch;
37 BitField<1, 1, u32_le> end_touch;
38 };
39 };
40 static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
41
32 struct TouchState { 42 struct TouchState {
33 u64_le delta_time; 43 u64_le delta_time;
34 u32_le attribute; 44 Attributes attribute;
35 u32_le finger; 45 u32_le finger;
36 u32_le x; 46 u32_le x;
37 u32_le y; 47 u32_le y;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 39631b14f..7c0dac5dc 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -34,8 +34,8 @@
34namespace Service::HID { 34namespace Service::HID {
35 35
36// Updating period for each HID device. 36// Updating period for each HID device.
37// TODO(shinyquagsire23): These need better values. 37// TODO(ogniK): Find actual polling rate of hid
38constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 38constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66;
39constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 39constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
40constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 40constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 0da159559..26fcd3405 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -10,6 +10,56 @@
10 10
11namespace Settings { 11namespace Settings {
12 12
13namespace NativeButton {
14const std::array<const char*, NumButtons> mapping = {{
15 "button_a",
16 "button_b",
17 "button_x",
18 "button_y",
19 "button_lstick",
20 "button_rstick",
21 "button_l",
22 "button_r",
23 "button_zl",
24 "button_zr",
25 "button_plus",
26 "button_minus",
27 "button_dleft",
28 "button_dup",
29 "button_dright",
30 "button_ddown",
31 "button_lstick_left",
32 "button_lstick_up",
33 "button_lstick_right",
34 "button_lstick_down",
35 "button_rstick_left",
36 "button_rstick_up",
37 "button_rstick_right",
38 "button_rstick_down",
39 "button_sl",
40 "button_sr",
41 "button_home",
42 "button_screenshot",
43}};
44}
45
46namespace NativeAnalog {
47const std::array<const char*, NumAnalogs> mapping = {{
48 "lstick",
49 "rstick",
50}};
51}
52
53namespace NativeMouseButton {
54const std::array<const char*, NumMouseButtons> mapping = {{
55 "left",
56 "right",
57 "middle",
58 "forward",
59 "back",
60}};
61}
62
13Values values = {}; 63Values values = {};
14 64
15void Apply() { 65void Apply() {
diff --git a/src/core/settings.h b/src/core/settings.h
index e424479f2..e63134f80 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -60,36 +60,7 @@ constexpr int BUTTON_NS_END = NumButtons;
60constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; 60constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
61constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; 61constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
62 62
63static const std::array<const char*, NumButtons> mapping = {{ 63extern const std::array<const char*, NumButtons> mapping;
64 "button_a",
65 "button_b",
66 "button_x",
67 "button_y",
68 "button_lstick",
69 "button_rstick",
70 "button_l",
71 "button_r",
72 "button_zl",
73 "button_zr",
74 "button_plus",
75 "button_minus",
76 "button_dleft",
77 "button_dup",
78 "button_dright",
79 "button_ddown",
80 "button_lstick_left",
81 "button_lstick_up",
82 "button_lstick_right",
83 "button_lstick_down",
84 "button_rstick_left",
85 "button_rstick_up",
86 "button_rstick_right",
87 "button_rstick_down",
88 "button_sl",
89 "button_sr",
90 "button_home",
91 "button_screenshot",
92}};
93 64
94} // namespace NativeButton 65} // namespace NativeButton
95 66
@@ -105,12 +76,273 @@ constexpr int STICK_HID_BEGIN = LStick;
105constexpr int STICK_HID_END = NumAnalogs; 76constexpr int STICK_HID_END = NumAnalogs;
106constexpr int NUM_STICKS_HID = NumAnalogs; 77constexpr int NUM_STICKS_HID = NumAnalogs;
107 78
108static const std::array<const char*, NumAnalogs> mapping = {{ 79extern const std::array<const char*, NumAnalogs> mapping;
109 "lstick",
110 "rstick",
111}};
112} // namespace NativeAnalog 80} // namespace NativeAnalog
113 81
82namespace NativeMouseButton {
83enum Values {
84 Left,
85 Right,
86 Middle,
87 Forward,
88 Back,
89
90 NumMouseButtons,
91};
92
93constexpr int MOUSE_HID_BEGIN = Left;
94constexpr int MOUSE_HID_END = NumMouseButtons;
95constexpr int NUM_MOUSE_HID = NumMouseButtons;
96
97extern const std::array<const char*, NumMouseButtons> mapping;
98} // namespace NativeMouseButton
99
100namespace NativeKeyboard {
101enum Keys {
102 None,
103 Error,
104
105 A = 4,
106 B,
107 C,
108 D,
109 E,
110 F,
111 G,
112 H,
113 I,
114 J,
115 K,
116 L,
117 M,
118 N,
119 O,
120 P,
121 Q,
122 R,
123 S,
124 T,
125 U,
126 V,
127 W,
128 X,
129 Y,
130 Z,
131 N1,
132 N2,
133 N3,
134 N4,
135 N5,
136 N6,
137 N7,
138 N8,
139 N9,
140 N0,
141 Enter,
142 Escape,
143 Backspace,
144 Tab,
145 Space,
146 Minus,
147 Equal,
148 LeftBrace,
149 RightBrace,
150 Backslash,
151 Tilde,
152 Semicolon,
153 Apostrophe,
154 Grave,
155 Comma,
156 Dot,
157 Slash,
158 CapsLockKey,
159
160 F1,
161 F2,
162 F3,
163 F4,
164 F5,
165 F6,
166 F7,
167 F8,
168 F9,
169 F10,
170 F11,
171 F12,
172
173 SystemRequest,
174 ScrollLockKey,
175 Pause,
176 Insert,
177 Home,
178 PageUp,
179 Delete,
180 End,
181 PageDown,
182 Right,
183 Left,
184 Down,
185 Up,
186
187 NumLockKey,
188 KPSlash,
189 KPAsterisk,
190 KPMinus,
191 KPPlus,
192 KPEnter,
193 KP1,
194 KP2,
195 KP3,
196 KP4,
197 KP5,
198 KP6,
199 KP7,
200 KP8,
201 KP9,
202 KP0,
203 KPDot,
204
205 Key102,
206 Compose,
207 Power,
208 KPEqual,
209
210 F13,
211 F14,
212 F15,
213 F16,
214 F17,
215 F18,
216 F19,
217 F20,
218 F21,
219 F22,
220 F23,
221 F24,
222
223 Open,
224 Help,
225 Properties,
226 Front,
227 Stop,
228 Repeat,
229 Undo,
230 Cut,
231 Copy,
232 Paste,
233 Find,
234 Mute,
235 VolumeUp,
236 VolumeDown,
237 CapsLockActive,
238 NumLockActive,
239 ScrollLockActive,
240 KPComma,
241
242 KPLeftParenthesis,
243 KPRightParenthesis,
244
245 LeftControlKey = 0xE0,
246 LeftShiftKey,
247 LeftAltKey,
248 LeftMetaKey,
249 RightControlKey,
250 RightShiftKey,
251 RightAltKey,
252 RightMetaKey,
253
254 MediaPlayPause,
255 MediaStopCD,
256 MediaPrevious,
257 MediaNext,
258 MediaEject,
259 MediaVolumeUp,
260 MediaVolumeDown,
261 MediaMute,
262 MediaWebsite,
263 MediaBack,
264 MediaForward,
265 MediaStop,
266 MediaFind,
267 MediaScrollUp,
268 MediaScrollDown,
269 MediaEdit,
270 MediaSleep,
271 MediaCoffee,
272 MediaRefresh,
273 MediaCalculator,
274
275 NumKeyboardKeys,
276};
277
278static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
279
280enum Modifiers {
281 LeftControl,
282 LeftShift,
283 LeftAlt,
284 LeftMeta,
285 RightControl,
286 RightShift,
287 RightAlt,
288 RightMeta,
289 CapsLock,
290 ScrollLock,
291 NumLock,
292
293 NumKeyboardMods,
294};
295
296constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
297constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
298constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
299
300constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
301constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
302constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
303
304} // namespace NativeKeyboard
305
306using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
307using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
308using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
309using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
310using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
311
312constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
313constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
314constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
315constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
316
317enum class ControllerType {
318 ProController,
319 DualJoycon,
320 RightJoycon,
321 LeftJoycon,
322};
323
324struct PlayerInput {
325 bool connected;
326 ControllerType type;
327 ButtonsRaw buttons;
328 AnalogsRaw analogs;
329
330 u32 body_color_right;
331 u32 button_color_right;
332 u32 body_color_left;
333 u32 button_color_left;
334};
335
336struct TouchscreenInput {
337 bool enabled;
338 std::string device;
339
340 u32 finger;
341 u32 diameter_x;
342 u32 diameter_y;
343 u32 rotation_angle;
344};
345
114struct Values { 346struct Values {
115 // System 347 // System
116 bool use_docked_mode; 348 bool use_docked_mode;
@@ -120,10 +352,22 @@ struct Values {
120 s32 language_index; 352 s32 language_index;
121 353
122 // Controls 354 // Controls
123 std::array<std::string, NativeButton::NumButtons> buttons; 355 std::array<PlayerInput, 10> players;
124 std::array<std::string, NativeAnalog::NumAnalogs> analogs; 356
357 bool mouse_enabled;
358 std::string mouse_device;
359 MouseButtonsRaw mouse_buttons;
360
361 bool keyboard_enabled;
362 KeyboardKeysRaw keyboard_keys;
363 KeyboardModsRaw keyboard_mods;
364
365 bool debug_pad_enabled;
366 ButtonsRaw debug_pad_buttons;
367 AnalogsRaw debug_pad_analogs;
368
125 std::string motion_device; 369 std::string motion_device;
126 std::string touch_device; 370 TouchscreenInput touchscreen;
127 std::atomic_bool is_device_reload_pending{true}; 371 std::atomic_bool is_device_reload_pending{true};
128 372
129 // Core 373 // Core
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index f9ca2948e..34f36f06b 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -27,8 +27,14 @@ add_executable(yuzu
27 configuration/configure_graphics.h 27 configuration/configure_graphics.h
28 configuration/configure_input.cpp 28 configuration/configure_input.cpp
29 configuration/configure_input.h 29 configuration/configure_input.h
30 configuration/configure_input_player.cpp
31 configuration/configure_input_player.h
32 configuration/configure_mouse_advanced.cpp
33 configuration/configure_mouse_advanced.h
30 configuration/configure_system.cpp 34 configuration/configure_system.cpp
31 configuration/configure_system.h 35 configuration/configure_system.h
36 configuration/configure_touchscreen_advanced.cpp
37 configuration/configure_touchscreen_advanced.h
32 configuration/configure_web.cpp 38 configuration/configure_web.cpp
33 configuration/configure_web.h 39 configuration/configure_web.h
34 debugger/graphics/graphics_breakpoint_observer.cpp 40 debugger/graphics/graphics_breakpoint_observer.cpp
@@ -76,7 +82,10 @@ set(UIS
76 configuration/configure_general.ui 82 configuration/configure_general.ui
77 configuration/configure_graphics.ui 83 configuration/configure_graphics.ui
78 configuration/configure_input.ui 84 configuration/configure_input.ui
85 configuration/configure_input_player.ui
86 configuration/configure_mouse_advanced.ui
79 configuration/configure_system.ui 87 configuration/configure_system.ui
88 configuration/configure_touchscreen_advanced.ui
80 configuration/configure_web.ui 89 configuration/configure_web.ui
81 hotkeys.ui 90 hotkeys.ui
82 main.ui 91 main.ui
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index be69fb831..e24ed5f2b 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -5,6 +5,7 @@
5#include <QSettings> 5#include <QSettings>
6#include "common/file_util.h" 6#include "common/file_util.h"
7#include "core/hle/service/acc/profile_manager.h" 7#include "core/hle/service/acc/profile_manager.h"
8#include "core/hle/service/hid/controllers/npad.h"
8#include "input_common/main.h" 9#include "input_common/main.h"
9#include "yuzu/configuration/config.h" 10#include "yuzu/configuration/config.h"
10#include "yuzu/ui_settings.h" 11#include "yuzu/ui_settings.h"
@@ -47,40 +48,313 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
47 }, 48 },
48}}; 49}};
49 50
50void Config::ReadValues() { 51const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
51 qt_config->beginGroup("Controls"); 52 {
53 Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
54};
55
56const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> Config::default_keyboard_keys = {
57 0,
58 0,
59 0,
60 0,
61 Qt::Key_A,
62 Qt::Key_B,
63 Qt::Key_C,
64 Qt::Key_D,
65 Qt::Key_E,
66 Qt::Key_F,
67 Qt::Key_G,
68 Qt::Key_H,
69 Qt::Key_I,
70 Qt::Key_J,
71 Qt::Key_K,
72 Qt::Key_L,
73 Qt::Key_M,
74 Qt::Key_N,
75 Qt::Key_O,
76 Qt::Key_P,
77 Qt::Key_Q,
78 Qt::Key_R,
79 Qt::Key_S,
80 Qt::Key_T,
81 Qt::Key_U,
82 Qt::Key_V,
83 Qt::Key_W,
84 Qt::Key_X,
85 Qt::Key_Y,
86 Qt::Key_Z,
87 Qt::Key_1,
88 Qt::Key_2,
89 Qt::Key_3,
90 Qt::Key_4,
91 Qt::Key_5,
92 Qt::Key_6,
93 Qt::Key_7,
94 Qt::Key_8,
95 Qt::Key_9,
96 Qt::Key_0,
97 Qt::Key_Enter,
98 Qt::Key_Escape,
99 Qt::Key_Backspace,
100 Qt::Key_Tab,
101 Qt::Key_Space,
102 Qt::Key_Minus,
103 Qt::Key_Equal,
104 Qt::Key_BracketLeft,
105 Qt::Key_BracketRight,
106 Qt::Key_Backslash,
107 Qt::Key_Dead_Tilde,
108 Qt::Key_Semicolon,
109 Qt::Key_Apostrophe,
110 Qt::Key_Dead_Grave,
111 Qt::Key_Comma,
112 Qt::Key_Period,
113 Qt::Key_Slash,
114 Qt::Key_CapsLock,
115
116 Qt::Key_F1,
117 Qt::Key_F2,
118 Qt::Key_F3,
119 Qt::Key_F4,
120 Qt::Key_F5,
121 Qt::Key_F6,
122 Qt::Key_F7,
123 Qt::Key_F8,
124 Qt::Key_F9,
125 Qt::Key_F10,
126 Qt::Key_F11,
127 Qt::Key_F12,
128
129 Qt::Key_SysReq,
130 Qt::Key_ScrollLock,
131 Qt::Key_Pause,
132 Qt::Key_Insert,
133 Qt::Key_Home,
134 Qt::Key_PageUp,
135 Qt::Key_Delete,
136 Qt::Key_End,
137 Qt::Key_PageDown,
138 Qt::Key_Right,
139 Qt::Key_Left,
140 Qt::Key_Down,
141 Qt::Key_Up,
142
143 Qt::Key_NumLock,
144 Qt::Key_Slash,
145 Qt::Key_Asterisk,
146 Qt::Key_Minus,
147 Qt::Key_Plus,
148 Qt::Key_Enter,
149 Qt::Key_1,
150 Qt::Key_2,
151 Qt::Key_3,
152 Qt::Key_4,
153 Qt::Key_5,
154 Qt::Key_6,
155 Qt::Key_7,
156 Qt::Key_8,
157 Qt::Key_9,
158 Qt::Key_0,
159 Qt::Key_Period,
160
161 0,
162 0,
163 Qt::Key_PowerOff,
164 Qt::Key_Equal,
165
166 Qt::Key_F13,
167 Qt::Key_F14,
168 Qt::Key_F15,
169 Qt::Key_F16,
170 Qt::Key_F17,
171 Qt::Key_F18,
172 Qt::Key_F19,
173 Qt::Key_F20,
174 Qt::Key_F21,
175 Qt::Key_F22,
176 Qt::Key_F23,
177 Qt::Key_F24,
178
179 Qt::Key_Open,
180 Qt::Key_Help,
181 Qt::Key_Menu,
182 0,
183 Qt::Key_Stop,
184 Qt::Key_AudioRepeat,
185 Qt::Key_Undo,
186 Qt::Key_Cut,
187 Qt::Key_Copy,
188 Qt::Key_Paste,
189 Qt::Key_Find,
190 Qt::Key_VolumeMute,
191 Qt::Key_VolumeUp,
192 Qt::Key_VolumeDown,
193 Qt::Key_CapsLock,
194 Qt::Key_NumLock,
195 Qt::Key_ScrollLock,
196 Qt::Key_Comma,
197
198 Qt::Key_ParenLeft,
199 Qt::Key_ParenRight,
200};
201
202const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default_keyboard_mods = {
203 Qt::Key_Control, Qt::Key_Shift, Qt::Key_Alt, Qt::Key_ApplicationLeft,
204 Qt::Key_Control, Qt::Key_Shift, Qt::Key_AltGr, Qt::Key_ApplicationRight,
205};
206
207void Config::ReadPlayerValues() {
208 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
209 Settings::values.players[p].connected =
210 qt_config->value(QString("player_%1_connected").arg(p), false).toBool();
211
212 Settings::values.players[p].type = static_cast<Settings::ControllerType>(
213 qt_config
214 ->value(QString("player_%1_type").arg(p),
215 static_cast<u8>(Settings::ControllerType::DualJoycon))
216 .toUInt());
217
218 Settings::values.players[p].body_color_left =
219 qt_config
220 ->value(QString("player_%1_body_color_left").arg(p),
221 Settings::JOYCON_BODY_NEON_BLUE)
222 .toUInt();
223 Settings::values.players[p].body_color_right =
224 qt_config
225 ->value(QString("player_%1_body_color_right").arg(p),
226 Settings::JOYCON_BODY_NEON_RED)
227 .toUInt();
228 Settings::values.players[p].button_color_left =
229 qt_config
230 ->value(QString("player_%1_button_color_left").arg(p),
231 Settings::JOYCON_BUTTONS_NEON_BLUE)
232 .toUInt();
233 Settings::values.players[p].button_color_right =
234 qt_config
235 ->value(QString("player_%1_button_color_right").arg(p),
236 Settings::JOYCON_BUTTONS_NEON_RED)
237 .toUInt();
238
239 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
240 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
241 Settings::values.players[p].buttons[i] =
242 qt_config
243 ->value(QString("player_%1_").arg(p) + Settings::NativeButton::mapping[i],
244 QString::fromStdString(default_param))
245 .toString()
246 .toStdString();
247 if (Settings::values.players[p].buttons[i].empty())
248 Settings::values.players[p].buttons[i] = default_param;
249 }
250
251 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
252 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
253 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
254 default_analogs[i][3], default_analogs[i][4], 0.5f);
255 Settings::values.players[p].analogs[i] =
256 qt_config
257 ->value(QString("player_%1_").arg(p) + Settings::NativeAnalog::mapping[i],
258 QString::fromStdString(default_param))
259 .toString()
260 .toStdString();
261 if (Settings::values.players[p].analogs[i].empty())
262 Settings::values.players[p].analogs[i] = default_param;
263 }
264 }
265
266 std::stable_partition(
267 Settings::values.players.begin(),
268 Settings::values.players.begin() +
269 Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
270 [](const auto& player) { return player.connected; });
271}
272
273void Config::ReadDebugValues() {
274 Settings::values.debug_pad_enabled = qt_config->value("debug_pad_enabled", false).toBool();
52 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 275 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
53 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 276 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
54 Settings::values.buttons[i] = 277 Settings::values.debug_pad_buttons[i] =
55 qt_config 278 qt_config
56 ->value(Settings::NativeButton::mapping[i], QString::fromStdString(default_param)) 279 ->value(QString("debug_pad_") + Settings::NativeButton::mapping[i],
280 QString::fromStdString(default_param))
57 .toString() 281 .toString()
58 .toStdString(); 282 .toStdString();
59 if (Settings::values.buttons[i].empty()) 283 if (Settings::values.debug_pad_buttons[i].empty())
60 Settings::values.buttons[i] = default_param; 284 Settings::values.debug_pad_buttons[i] = default_param;
61 } 285 }
62 286
63 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 287 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
64 std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 288 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
65 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 289 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
66 default_analogs[i][3], default_analogs[i][4], 0.5f); 290 default_analogs[i][3], default_analogs[i][4], 0.5f);
67 Settings::values.analogs[i] = 291 Settings::values.debug_pad_analogs[i] =
68 qt_config 292 qt_config
69 ->value(Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param)) 293 ->value(QString("debug_pad_") + Settings::NativeAnalog::mapping[i],
294 QString::fromStdString(default_param))
70 .toString() 295 .toString()
71 .toStdString(); 296 .toStdString();
72 if (Settings::values.analogs[i].empty()) 297 if (Settings::values.debug_pad_analogs[i].empty())
73 Settings::values.analogs[i] = default_param; 298 Settings::values.debug_pad_analogs[i] = default_param;
74 } 299 }
300}
301
302void Config::ReadKeyboardValues() {
303 Settings::values.keyboard_enabled = qt_config->value("keyboard_enabled", false).toBool();
304
305 std::transform(default_keyboard_keys.begin(), default_keyboard_keys.end(),
306 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
307 std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
308 Settings::values.keyboard_keys.begin() +
309 Settings::NativeKeyboard::LeftControlKey,
310 InputCommon::GenerateKeyboardParam);
311 std::transform(default_keyboard_mods.begin(), default_keyboard_mods.end(),
312 Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
313}
314
315void Config::ReadMouseValues() {
316 Settings::values.mouse_enabled = qt_config->value("mouse_enabled", false).toBool();
317
318 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
319 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
320 Settings::values.mouse_buttons[i] =
321 qt_config
322 ->value(QString("mouse_") + Settings::NativeMouseButton::mapping[i],
323 QString::fromStdString(default_param))
324 .toString()
325 .toStdString();
326 if (Settings::values.mouse_buttons[i].empty())
327 Settings::values.mouse_buttons[i] = default_param;
328 }
329}
330
331void Config::ReadTouchscreenValues() {
332 Settings::values.touchscreen.enabled = qt_config->value("touchscreen_enabled", true).toBool();
333 Settings::values.touchscreen.device =
334 qt_config->value("touchscreen_device", "engine:emu_window").toString().toStdString();
335
336 Settings::values.touchscreen.finger = qt_config->value("touchscreen_finger", 0).toUInt();
337 Settings::values.touchscreen.rotation_angle = qt_config->value("touchscreen_angle", 0).toUInt();
338 Settings::values.touchscreen.diameter_x =
339 qt_config->value("touchscreen_diameter_x", 15).toUInt();
340 Settings::values.touchscreen.diameter_y =
341 qt_config->value("touchscreen_diameter_y", 15).toUInt();
342 qt_config->endGroup();
343}
344
345void Config::ReadValues() {
346 qt_config->beginGroup("Controls");
347
348 ReadPlayerValues();
349 ReadDebugValues();
350 ReadKeyboardValues();
351 ReadMouseValues();
352 ReadTouchscreenValues();
75 353
76 Settings::values.motion_device = 354 Settings::values.motion_device =
77 qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") 355 qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
78 .toString() 356 .toString()
79 .toStdString(); 357 .toStdString();
80 Settings::values.touch_device =
81 qt_config->value("touch_device", "engine:emu_window").toString().toStdString();
82
83 qt_config->endGroup();
84 358
85 qt_config->beginGroup("Core"); 359 qt_config->beginGroup("Core");
86 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool(); 360 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
@@ -126,6 +400,11 @@ void Config::ReadValues() {
126 .toStdString()); 400 .toStdString());
127 qt_config->endGroup(); 401 qt_config->endGroup();
128 402
403 qt_config->beginGroup("Core");
404 Settings::values.use_cpu_jit = qt_config->value("use_cpu_jit", true).toBool();
405 Settings::values.use_multi_core = qt_config->value("use_multi_core", false).toBool();
406 qt_config->endGroup();
407
129 qt_config->beginGroup("System"); 408 qt_config->beginGroup("System");
130 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); 409 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
131 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool(); 410 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
@@ -230,18 +509,81 @@ void Config::ReadValues() {
230 qt_config->endGroup(); 509 qt_config->endGroup();
231} 510}
232 511
233void Config::SaveValues() { 512void Config::SavePlayerValues() {
234 qt_config->beginGroup("Controls"); 513 for (int p = 0; p < Settings::values.players.size(); ++p) {
514 qt_config->setValue(QString("player_%1_connected").arg(p),
515 Settings::values.players[p].connected);
516 qt_config->setValue(QString("player_%1_type").arg(p),
517 static_cast<u8>(Settings::values.players[p].type));
518
519 qt_config->setValue(QString("player_%1_body_color_left").arg(p),
520 Settings::values.players[p].body_color_left);
521 qt_config->setValue(QString("player_%1_body_color_right").arg(p),
522 Settings::values.players[p].body_color_right);
523 qt_config->setValue(QString("player_%1_button_color_left").arg(p),
524 Settings::values.players[p].button_color_left);
525 qt_config->setValue(QString("player_%1_button_color_right").arg(p),
526 Settings::values.players[p].button_color_right);
527
528 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
529 qt_config->setValue(QString("player_%1_").arg(p) +
530 QString::fromStdString(Settings::NativeButton::mapping[i]),
531 QString::fromStdString(Settings::values.players[p].buttons[i]));
532 }
533 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
534 qt_config->setValue(QString("player_%1_").arg(p) +
535 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
536 QString::fromStdString(Settings::values.players[p].analogs[i]));
537 }
538 }
539}
540
541void Config::SaveDebugValues() {
542 qt_config->setValue("debug_pad_enabled", Settings::values.debug_pad_enabled);
235 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 543 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
236 qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]), 544 qt_config->setValue(QString("debug_pad_") +
237 QString::fromStdString(Settings::values.buttons[i])); 545 QString::fromStdString(Settings::NativeButton::mapping[i]),
546 QString::fromStdString(Settings::values.debug_pad_buttons[i]));
238 } 547 }
239 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 548 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
240 qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]), 549 qt_config->setValue(QString("debug_pad_") +
241 QString::fromStdString(Settings::values.analogs[i])); 550 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
551 QString::fromStdString(Settings::values.debug_pad_analogs[i]));
242 } 552 }
553}
554
555void Config::SaveMouseValues() {
556 qt_config->setValue("mouse_enabled", Settings::values.mouse_enabled);
557
558 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
559 qt_config->setValue(QString("mouse_") +
560 QString::fromStdString(Settings::NativeMouseButton::mapping[i]),
561 QString::fromStdString(Settings::values.mouse_buttons[i]));
562 }
563}
564
565void Config::SaveTouchscreenValues() {
566 qt_config->setValue("touchscreen_enabled", Settings::values.touchscreen.enabled);
567 qt_config->setValue("touchscreen_device",
568 QString::fromStdString(Settings::values.touchscreen.device));
569
570 qt_config->setValue("touchscreen_finger", Settings::values.touchscreen.finger);
571 qt_config->setValue("touchscreen_angle", Settings::values.touchscreen.rotation_angle);
572 qt_config->setValue("touchscreen_diameter_x", Settings::values.touchscreen.diameter_x);
573 qt_config->setValue("touchscreen_diameter_y", Settings::values.touchscreen.diameter_y);
574}
575
576void Config::SaveValues() {
577 qt_config->beginGroup("Controls");
578
579 SavePlayerValues();
580 SaveDebugValues();
581 SaveMouseValues();
582 SaveTouchscreenValues();
583
243 qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device)); 584 qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device));
244 qt_config->setValue("touch_device", QString::fromStdString(Settings::values.touch_device)); 585 qt_config->setValue("keyboard_enabled", Settings::values.keyboard_enabled);
586
245 qt_config->endGroup(); 587 qt_config->endGroup();
246 588
247 qt_config->beginGroup("Core"); 589 qt_config->beginGroup("Core");
@@ -280,7 +622,6 @@ void Config::SaveValues() {
280 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); 622 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
281 qt_config->setValue("enable_nfc", Settings::values.enable_nfc); 623 qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
282 qt_config->setValue("current_user", Settings::values.current_user); 624 qt_config->setValue("current_user", Settings::values.current_user);
283
284 qt_config->setValue("language_index", Settings::values.language_index); 625 qt_config->setValue("language_index", Settings::values.language_index);
285 626
286 qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value()); 627 qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value());
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 9c99c1b75..a1c27bbf9 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -22,10 +22,24 @@ public:
22 22
23 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 23 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
24 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; 24 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
25 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
26 default_mouse_buttons;
27 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
28 static const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> default_keyboard_mods;
25 29
26private: 30private:
27 void ReadValues(); 31 void ReadValues();
32 void ReadPlayerValues();
33 void ReadDebugValues();
34 void ReadKeyboardValues();
35 void ReadMouseValues();
36 void ReadTouchscreenValues();
37
28 void SaveValues(); 38 void SaveValues();
39 void SavePlayerValues();
40 void SaveDebugValues();
41 void SaveMouseValues();
42 void SaveTouchscreenValues();
29 43
30 std::unique_ptr<QSettings> qt_config; 44 std::unique_ptr<QSettings> qt_config;
31 std::string qt_config_loc; 45 std::string qt_config_loc;
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index c22742007..92a441308 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -3,10 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core.h" 5#include "core/core.h"
6#include "core/hle/service/am/am.h"
7#include "core/hle/service/am/applet_ae.h"
8#include "core/hle/service/am/applet_oe.h"
9#include "core/hle/service/sm/sm.h"
10#include "core/settings.h" 6#include "core/settings.h"
11#include "ui_configure_general.h" 7#include "ui_configure_general.h"
12#include "yuzu/configuration/configure_general.h" 8#include "yuzu/configuration/configure_general.h"
@@ -36,7 +32,6 @@ void ConfigureGeneral::setConfiguration() {
36 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); 32 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
37 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); 33 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
38 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); 34 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
39 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
40 ui->enable_nfc->setChecked(Settings::values.enable_nfc); 35 ui->enable_nfc->setChecked(Settings::values.enable_nfc);
41} 36}
42 37
@@ -44,33 +39,6 @@ void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
44 ui->widget->Populate(registry); 39 ui->widget->Populate(registry);
45} 40}
46 41
47void ConfigureGeneral::OnDockedModeChanged(bool last_state, bool new_state) {
48 if (last_state == new_state) {
49 return;
50 }
51
52 Core::System& system{Core::System::GetInstance()};
53 if (!system.IsPoweredOn()) {
54 return;
55 }
56 Service::SM::ServiceManager& sm = system.ServiceManager();
57
58 // Message queue is shared between these services, we just need to signal an operation
59 // change to one and it will handle both automatically
60 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
61 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
62 bool has_signalled = false;
63
64 if (applet_oe != nullptr) {
65 applet_oe->GetMessageQueue()->OperationModeChanged();
66 has_signalled = true;
67 }
68
69 if (applet_ae != nullptr && !has_signalled) {
70 applet_ae->GetMessageQueue()->OperationModeChanged();
71 }
72}
73
74void ConfigureGeneral::applyConfiguration() { 42void ConfigureGeneral::applyConfiguration() {
75 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); 43 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
76 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 44 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
@@ -78,9 +46,5 @@ void ConfigureGeneral::applyConfiguration() {
78 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 46 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
79 47
80 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); 48 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
81 const bool pre_docked_mode = Settings::values.use_docked_mode;
82 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
83 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
84
85 Settings::values.enable_nfc = ui->enable_nfc->isChecked(); 49 Settings::values.enable_nfc = ui->enable_nfc->isChecked();
86} 50}
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 2210d48da..4770034cc 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -25,7 +25,6 @@ public:
25 25
26private: 26private:
27 void setConfiguration(); 27 void setConfiguration();
28 void OnDockedModeChanged(bool last_state, bool new_state);
29 28
30 std::unique_ptr<Ui::ConfigureGeneral> ui; 29 std::unique_ptr<Ui::ConfigureGeneral> ui;
31}; 30};
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index b82fffde8..bf37446c6 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>300</width> 9 <width>300</width>
10 <height>377</height> 10 <height>407</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -72,13 +72,6 @@
72 <item> 72 <item>
73 <layout class="QVBoxLayout" name="EmulationVerticalLayout"> 73 <layout class="QVBoxLayout" name="EmulationVerticalLayout">
74 <item> 74 <item>
75 <widget class="QCheckBox" name="use_docked_mode">
76 <property name="text">
77 <string>Enable docked mode</string>
78 </property>
79 </widget>
80 </item>
81 <item>
82 <widget class="QCheckBox" name="enable_nfc"> 75 <widget class="QCheckBox" name="enable_nfc">
83 <property name="text"> 76 <property name="text">
84 <string>Enable NFC</string> 77 <string>Enable NFC</string>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 42a7beac6..7ee572761 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -9,334 +9,202 @@
9#include <QMessageBox> 9#include <QMessageBox>
10#include <QTimer> 10#include <QTimer>
11#include "common/param_package.h" 11#include "common/param_package.h"
12#include "configuration/configure_touchscreen_advanced.h"
13#include "core/core.h"
14#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applet_ae.h"
16#include "core/hle/service/am/applet_oe.h"
17#include "core/hle/service/hid/controllers/npad.h"
18#include "core/hle/service/sm/sm.h"
12#include "input_common/main.h" 19#include "input_common/main.h"
20#include "ui_configure_input.h"
21#include "ui_configure_input_player.h"
22#include "ui_configure_mouse_advanced.h"
23#include "ui_configure_touchscreen_advanced.h"
13#include "yuzu/configuration/config.h" 24#include "yuzu/configuration/config.h"
14#include "yuzu/configuration/configure_input.h" 25#include "yuzu/configuration/configure_input.h"
26#include "yuzu/configuration/configure_input_player.h"
27#include "yuzu/configuration/configure_mouse_advanced.h"
15 28
16const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM> 29ConfigureInput::ConfigureInput(QWidget* parent)
17 ConfigureInput::analog_sub_buttons{{ 30 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
18 "up", 31 ui->setupUi(this);
19 "down",
20 "left",
21 "right",
22 "modifier",
23 }};
24
25static QString getKeyName(int key_code) {
26 switch (key_code) {
27 case Qt::Key_Shift:
28 return QObject::tr("Shift");
29 case Qt::Key_Control:
30 return QObject::tr("Ctrl");
31 case Qt::Key_Alt:
32 return QObject::tr("Alt");
33 case Qt::Key_Meta:
34 return "";
35 default:
36 return QKeySequence(key_code).toString();
37 }
38}
39 32
40static void SetAnalogButton(const Common::ParamPackage& input_param, 33 players_controller = {
41 Common::ParamPackage& analog_param, const std::string& button_name) { 34 ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox,
42 if (analog_param.Get("engine", "") != "analog_from_button") { 35 ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox,
43 analog_param = { 36 };
44 {"engine", "analog_from_button"},
45 {"modifier_scale", "0.5"},
46 };
47 }
48 analog_param.Set(button_name, input_param.Serialize());
49}
50 37
51static QString ButtonToText(const Common::ParamPackage& param) { 38 players_configure = {
52 if (!param.Has("engine")) { 39 ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure,
53 return QObject::tr("[not set]"); 40 ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure,
54 } else if (param.Get("engine", "") == "keyboard") { 41 };
55 return getKeyName(param.Get("code", 0));
56 } else if (param.Get("engine", "") == "sdl") {
57 if (param.Has("hat")) {
58 return QString(QObject::tr("Hat %1 %2"))
59 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
60 }
61 if (param.Has("axis")) {
62 return QString(QObject::tr("Axis %1%2"))
63 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
64 }
65 if (param.Has("button")) {
66 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
67 }
68 return QString();
69 } else {
70 return QObject::tr("[unknown]");
71 }
72};
73
74static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
75 if (!param.Has("engine")) {
76 return QObject::tr("[not set]");
77 } else if (param.Get("engine", "") == "analog_from_button") {
78 return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
79 } else if (param.Get("engine", "") == "sdl") {
80 if (dir == "modifier") {
81 return QString(QObject::tr("[unused]"));
82 }
83 42
84 if (dir == "left" || dir == "right") { 43 for (auto* controller_box : players_controller) {
85 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str()); 44 controller_box->addItems({"None", "Pro Controller", "Dual Joycons", "Single Right Joycon",
86 } else if (dir == "up" || dir == "down") { 45 "Single Left Joycon"});
87 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str());
88 }
89 return QString();
90 } else {
91 return QObject::tr("[unknown]");
92 } 46 }
93};
94 47
95ConfigureInput::ConfigureInput(QWidget* parent) 48 this->loadConfiguration();
96 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), 49 updateUIEnabled();
97 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
98 50
99 ui->setupUi(this); 51 connect(ui->restore_defaults_button, &QPushButton::pressed, this,
100 setFocusPolicy(Qt::ClickFocus); 52 &ConfigureInput::restoreDefaults);
101
102 button_map = {
103 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
104 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
105 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
106 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
107 ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown,
108 ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
109 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
110 };
111 53
112 analog_map_buttons = {{ 54 for (auto* enabled : players_controller)
113 { 55 connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
114 ui->buttonLStickUp, 56 &ConfigureInput::updateUIEnabled);
115 ui->buttonLStickDown, 57 connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
116 ui->buttonLStickLeft, 58 connect(ui->handheld_connected, &QCheckBox::stateChanged, this,
117 ui->buttonLStickRight, 59 &ConfigureInput::updateUIEnabled);
118 ui->buttonLStickMod, 60 connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
119 }, 61 connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
120 { 62 connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::updateUIEnabled);
121 ui->buttonRStickUp, 63 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
122 ui->buttonRStickDown, 64 &ConfigureInput::updateUIEnabled);
123 ui->buttonRStickLeft,
124 ui->buttonRStickRight,
125 ui->buttonRStickMod,
126 },
127 }};
128
129 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
130
131 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
132 if (!button_map[button_id])
133 continue;
134 button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
135 connect(button_map[button_id], &QPushButton::released, [=]() {
136 handleClick(
137 button_map[button_id],
138 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
139 InputCommon::Polling::DeviceType::Button);
140 });
141 connect(button_map[button_id], &QPushButton::customContextMenuRequested,
142 [=](const QPoint& menu_location) {
143 QMenu context_menu;
144 context_menu.addAction(tr("Clear"), [&] {
145 buttons_param[button_id].Clear();
146 button_map[button_id]->setText(tr("[not set]"));
147 });
148 context_menu.addAction(tr("Restore Default"), [&] {
149 buttons_param[button_id] = Common::ParamPackage{
150 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
151 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
152 });
153 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
154 });
155 }
156 65
157 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 66 for (std::size_t i = 0; i < players_configure.size(); ++i) {
158 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 67 connect(players_configure[i], &QPushButton::pressed, this,
159 if (!analog_map_buttons[analog_id][sub_button_id]) 68 [this, i]() { CallConfigureDialog<ConfigureInputPlayer>(i, false); });
160 continue;
161 analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
162 Qt::CustomContextMenu);
163 connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() {
164 handleClick(analog_map_buttons[analog_id][sub_button_id],
165 [=](const Common::ParamPackage& params) {
166 SetAnalogButton(params, analogs_param[analog_id],
167 analog_sub_buttons[sub_button_id]);
168 },
169 InputCommon::Polling::DeviceType::Button);
170 });
171 connect(analog_map_buttons[analog_id][sub_button_id],
172 &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
173 QMenu context_menu;
174 context_menu.addAction(tr("Clear"), [&] {
175 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
176 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
177 });
178 context_menu.addAction(tr("Restore Default"), [&] {
179 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
180 Config::default_analogs[analog_id][sub_button_id])};
181 SetAnalogButton(params, analogs_param[analog_id],
182 analog_sub_buttons[sub_button_id]);
183 analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
184 analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
185 });
186 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
187 menu_location));
188 });
189 }
190 connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
191 QMessageBox::information(this, tr("Information"),
192 tr("After pressing OK, first move your joystick horizontally, "
193 "and then vertically."));
194 handleClick(
195 analog_map_stick[analog_id],
196 [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
197 InputCommon::Polling::DeviceType::Analog);
198 });
199 } 69 }
200 70
201 connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); }); 71 connect(ui->handheld_configure, &QPushButton::pressed, this,
202 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); 72 [this]() { CallConfigureDialog<ConfigureInputPlayer>(8, false); });
203
204 timeout_timer->setSingleShot(true);
205 connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
206 73
207 connect(poll_timer.get(), &QTimer::timeout, [this]() { 74 connect(ui->debug_configure, &QPushButton::pressed, this,
208 Common::ParamPackage params; 75 [this]() { CallConfigureDialog<ConfigureInputPlayer>(9, true); });
209 for (auto& poller : device_pollers) {
210 params = poller->GetNextInput();
211 if (params.Has("engine")) {
212 setPollingResult(params, false);
213 return;
214 }
215 }
216 });
217 76
218 this->loadConfiguration(); 77 connect(ui->mouse_advanced, &QPushButton::pressed, this,
78 [this]() { CallConfigureDialog<ConfigureMouseAdvanced>(); });
219 79
220 // TODO(wwylele): enable this when we actually emulate it 80 connect(ui->touchscreen_advanced, &QPushButton::pressed, this,
221 ui->buttonHome->setEnabled(false); 81 [this]() { CallConfigureDialog<ConfigureTouchscreenAdvanced>(); });
222} 82}
223 83
224void ConfigureInput::applyConfiguration() { 84template <typename Dialog, typename... Args>
225 std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.buttons.begin(), 85void ConfigureInput::CallConfigureDialog(Args&&... args) {
226 [](const Common::ParamPackage& param) { return param.Serialize(); }); 86 this->applyConfiguration();
227 std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(), 87 Dialog dialog(this, std::forward<Args>(args)...);
228 [](const Common::ParamPackage& param) { return param.Serialize(); });
229}
230 88
231void ConfigureInput::loadConfiguration() { 89 const auto res = dialog.exec();
232 std::transform(Settings::values.buttons.begin(), Settings::values.buttons.end(), 90 if (res == QDialog::Accepted) {
233 buttons_param.begin(), 91 dialog.applyConfiguration();
234 [](const std::string& str) { return Common::ParamPackage(str); }); 92 }
235 std::transform(Settings::values.analogs.begin(), Settings::values.analogs.end(),
236 analogs_param.begin(),
237 [](const std::string& str) { return Common::ParamPackage(str); });
238 updateButtonLabels();
239} 93}
240 94
241void ConfigureInput::restoreDefaults() { 95void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) {
242 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 96 if (last_state == new_state) {
243 buttons_param[button_id] = Common::ParamPackage{ 97 return;
244 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
245 } 98 }
246 99
247 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 100 Core::System& system{Core::System::GetInstance()};
248 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 101 if (!system.IsPoweredOn()) {
249 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 102 return;
250 Config::default_analogs[analog_id][sub_button_id])};
251 SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
252 }
253 } 103 }
254 updateButtonLabels(); 104 Service::SM::ServiceManager& sm = system.ServiceManager();
255}
256 105
257void ConfigureInput::ClearAll() { 106 // Message queue is shared between these services, we just need to signal an operation
258 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 107 // change to one and it will handle both automatically
259 if (button_map[button_id] && button_map[button_id]->isEnabled()) 108 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
260 buttons_param[button_id].Clear(); 109 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
261 } 110 bool has_signalled = false;
262 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
263 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
264 if (analog_map_buttons[analog_id][sub_button_id] &&
265 analog_map_buttons[analog_id][sub_button_id]->isEnabled())
266 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
267 }
268 }
269 updateButtonLabels();
270}
271 111
272void ConfigureInput::updateButtonLabels() { 112 if (applet_oe != nullptr) {
273 for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { 113 applet_oe->GetMessageQueue()->OperationModeChanged();
274 button_map[button]->setText(ButtonToText(buttons_param[button])); 114 has_signalled = true;
275 } 115 }
276 116
277 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 117 if (applet_ae != nullptr && !has_signalled) {
278 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 118 applet_ae->GetMessageQueue()->OperationModeChanged();
279 if (analog_map_buttons[analog_id][sub_button_id]) {
280 analog_map_buttons[analog_id][sub_button_id]->setText(
281 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
282 }
283 }
284 analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
285 } 119 }
286} 120}
287 121
288void ConfigureInput::handleClick(QPushButton* button, 122void ConfigureInput::applyConfiguration() {
289 std::function<void(const Common::ParamPackage&)> new_input_setter, 123 for (std::size_t i = 0; i < players_controller.size(); ++i) {
290 InputCommon::Polling::DeviceType type) { 124 const auto controller_type_index = players_controller[i]->currentIndex();
291 button->setText(tr("[press key]"));
292 button->setFocus();
293
294 input_setter = new_input_setter;
295
296 device_pollers = InputCommon::Polling::GetPollers(type);
297 125
298 // Keyboard keys can only be used as button devices 126 Settings::values.players[i].connected = controller_type_index != 0;
299 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
300 127
301 for (auto& poller : device_pollers) { 128 if (controller_type_index > 0) {
302 poller->Start(); 129 Settings::values.players[i].type =
130 static_cast<Settings::ControllerType>(controller_type_index - 1);
131 } else {
132 Settings::values.players[i].type = Settings::ControllerType::DualJoycon;
133 }
303 } 134 }
304 135
305 grabKeyboard(); 136 const bool pre_docked_mode = Settings::values.use_docked_mode;
306 grabMouse(); 137 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
307 timeout_timer->start(5000); // Cancel after 5 seconds 138 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
308 poll_timer->start(200); // Check for new inputs every 200ms 139 Settings::values
140 .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
141 .connected = ui->handheld_connected->isChecked();
142 Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
143 Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
144 Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
145 Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
309} 146}
310 147
311void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool abort) { 148void ConfigureInput::updateUIEnabled() {
312 releaseKeyboard(); 149 bool hit_disabled = false;
313 releaseMouse(); 150 for (auto* player : players_controller) {
314 timeout_timer->stop(); 151 player->setDisabled(hit_disabled);
315 poll_timer->stop(); 152 if (hit_disabled)
316 for (auto& poller : device_pollers) { 153 player->setCurrentIndex(0);
317 poller->Stop(); 154 if (!hit_disabled && player->currentIndex() == 0)
155 hit_disabled = true;
318 } 156 }
319 157
320 if (!abort) { 158 for (std::size_t i = 0; i < players_controller.size(); ++i) {
321 (*input_setter)(params); 159 players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0);
322 } 160 }
323 161
324 updateButtonLabels(); 162 ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked());
325 input_setter = {}; 163 ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() &&
164 !ui->use_docked_mode->isChecked());
165 ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
166 ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
167 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
326} 168}
327 169
328void ConfigureInput::keyPressEvent(QKeyEvent* event) { 170void ConfigureInput::loadConfiguration() {
329 if (!input_setter || !event) 171 std::stable_partition(
330 return; 172 Settings::values.players.begin(),
173 Settings::values.players.begin() +
174 Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
175 [](const auto& player) { return player.connected; });
176
177 for (std::size_t i = 0; i < players_controller.size(); ++i) {
178 const auto connected = Settings::values.players[i].connected;
179 players_controller[i]->setCurrentIndex(
180 connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
181 }
182
183 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
184 ui->handheld_connected->setChecked(
185 Settings::values
186 .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
187 .connected);
188 ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
189 ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
190 ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
191 ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
192
193 updateUIEnabled();
194}
331 195
332 if (event->key() != Qt::Key_Escape) { 196void ConfigureInput::restoreDefaults() {
333 if (want_keyboard_keys) { 197 players_controller[0]->setCurrentIndex(2);
334 setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, 198
335 false); 199 for (std::size_t i = 1; i < players_controller.size(); ++i) {
336 } else { 200 players_controller[i]->setCurrentIndex(0);
337 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
338 return;
339 }
340 } 201 }
341 setPollingResult({}, true); 202
203 ui->use_docked_mode->setCheckState(Qt::Unchecked);
204 ui->handheld_connected->setCheckState(Qt::Unchecked);
205 ui->mouse_enabled->setCheckState(Qt::Unchecked);
206 ui->keyboard_enabled->setCheckState(Qt::Unchecked);
207 ui->debug_enabled->setCheckState(Qt::Unchecked);
208 ui->touchscreen_enabled->setCheckState(Qt::Checked);
209 updateUIEnabled();
342} 210}
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 32c7183f9..29a8a03f8 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -18,6 +18,7 @@
18#include "core/settings.h" 18#include "core/settings.h"
19#include "input_common/main.h" 19#include "input_common/main.h"
20#include "ui_configure_input.h" 20#include "ui_configure_input.h"
21#include "yuzu/configuration/config.h"
21 22
22class QPushButton; 23class QPushButton;
23class QString; 24class QString;
@@ -37,57 +38,20 @@ public:
37 void applyConfiguration(); 38 void applyConfiguration();
38 39
39private: 40private:
40 std::unique_ptr<Ui::ConfigureInput> ui; 41 void updateUIEnabled();
41
42 std::unique_ptr<QTimer> timeout_timer;
43 std::unique_ptr<QTimer> poll_timer;
44
45 /// This will be the the setting function when an input is awaiting configuration.
46 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
47
48 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
49 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
50
51 static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
52
53 /// Each button input is represented by a QPushButton.
54 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
55 42
56 /// A group of five QPushButtons represent one analog input. The buttons each represent up, 43 template <typename Dialog, typename... Args>
57 /// down, left, right, and modifier, respectively. 44 void CallConfigureDialog(Args&&... args);
58 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
59 analog_map_buttons;
60 45
61 /// Analog inputs are also represented each with a single button, used to configure with an 46 void OnDockedModeChanged(bool last_state, bool new_state);
62 /// actual analog stick
63 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
64
65 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
66
67 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
68
69 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
70 /// keyboard events are ignored.
71 bool want_keyboard_keys = false;
72 47
73 /// Load configuration settings. 48 /// Load configuration settings.
74 void loadConfiguration(); 49 void loadConfiguration();
75 /// Restore all buttons to their default values. 50 /// Restore all buttons to their default values.
76 void restoreDefaults(); 51 void restoreDefaults();
77 /// Clear all input configuration
78 void ClearAll();
79 52
80 /// Update UI to reflect current configuration. 53 std::unique_ptr<Ui::ConfigureInput> ui;
81 void updateButtonLabels();
82
83 /// Called when the button was pressed.
84 void handleClick(QPushButton* button,
85 std::function<void(const Common::ParamPackage&)> new_input_setter,
86 InputCommon::Polling::DeviceType type);
87
88 /// Finish polling and configure input using the input_setter
89 void setPollingResult(const Common::ParamPackage& params, bool abort);
90 54
91 /// Handle key press events. 55 std::array<QComboBox*, 8> players_controller;
92 void keyPressEvent(QKeyEvent* event) override; 56 std::array<QPushButton*, 8> players_configure;
93}; 57};
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index 8a019a693..dae8277bc 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>343</width> 9 <width>473</width>
10 <height>677</height> 10 <height>685</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -15,740 +15,470 @@
15 </property> 15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5"> 16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <item> 17 <item>
18 <layout class="QGridLayout" name="buttons"> 18 <layout class="QVBoxLayout" name="verticalLayout">
19 <item row="3" column="1"> 19 <item>
20 <widget class="QGroupBox" name="misc"> 20 <widget class="QGroupBox" name="gridGroupBox">
21 <property name="title"> 21 <property name="title">
22 <string>Misc.</string> 22 <string>Players</string>
23 </property>
24 <property name="flat">
25 <bool>false</bool>
26 </property>
27 <property name="checkable">
28 <bool>false</bool>
29 </property> 23 </property>
30 <layout class="QGridLayout" name="gridLayout_6"> 24 <layout class="QGridLayout" name="gridLayout">
31 <item row="0" column="0"> 25 <item row="1" column="2">
32 <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout"> 26 <widget class="QComboBox" name="player1_combobox">
33 <item> 27 <property name="minimumSize">
34 <widget class="QLabel" name="labelMinus"> 28 <size>
35 <property name="text"> 29 <width>110</width>
36 <string>Minus:</string> 30 <height>0</height>
37 </property> 31 </size>
38 </widget> 32 </property>
39 </item> 33 </widget>
40 <item>
41 <widget class="QPushButton" name="buttonMinus">
42 <property name="text">
43 <string/>
44 </property>
45 </widget>
46 </item>
47 </layout>
48 </item> 34 </item>
49 <item row="0" column="1"> 35 <item row="1" column="3">
50 <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout"> 36 <widget class="QPushButton" name="player1_configure">
51 <item> 37 <property name="text">
52 <widget class="QLabel" name="labelPlus"> 38 <string>Configure</string>
53 <property name="text"> 39 </property>
54 <string>Plus:</string> 40 </widget>
55 </property>
56 </widget>
57 </item>
58 <item>
59 <widget class="QPushButton" name="buttonPlus">
60 <property name="text">
61 <string/>
62 </property>
63 </widget>
64 </item>
65 </layout>
66 </item> 41 </item>
67 <item row="1" column="0"> 42 <item row="0" column="2">
68 <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout"> 43 <widget class="QLabel" name="label">
69 <item> 44 <property name="text">
70 <widget class="QLabel" name="labelHome"> 45 <string>Controller Type</string>
71 <property name="text"> 46 </property>
72 <string>Home:</string> 47 <property name="alignment">
73 </property> 48 <set>Qt::AlignCenter</set>
74 </widget> 49 </property>
75 </item> 50 </widget>
76 <item>
77 <widget class="QPushButton" name="buttonHome">
78 <property name="text">
79 <string/>
80 </property>
81 </widget>
82 </item>
83 </layout>
84 </item> 51 </item>
85 <item row="1" column="1"> 52 <item row="2" column="2">
86 <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout"> 53 <widget class="QComboBox" name="player2_combobox">
87 <item> 54 <property name="minimumSize">
88 <widget class="QLabel" name="labelScrCap"> 55 <size>
89 <property name="text"> 56 <width>110</width>
90 <string>Screen 57 <height>0</height>
91Capture:</string> 58 </size>
92 </property> 59 </property>
93 </widget> 60 </widget>
94 </item>
95 <item>
96 <widget class="QPushButton" name="buttonScreenshot">
97 <property name="text">
98 <string/>
99 </property>
100 </widget>
101 </item>
102 </layout>
103 </item> 61 </item>
104 <item row="2" column="1"> 62 <item row="3" column="2">
105 <spacer name="verticalSpacer"> 63 <widget class="QComboBox" name="player3_combobox">
64 <property name="minimumSize">
65 <size>
66 <width>110</width>
67 <height>0</height>
68 </size>
69 </property>
70 </widget>
71 </item>
72 <item row="4" column="2">
73 <widget class="QComboBox" name="player4_combobox">
74 <property name="minimumSize">
75 <size>
76 <width>110</width>
77 <height>0</height>
78 </size>
79 </property>
80 </widget>
81 </item>
82 <item row="5" column="2">
83 <widget class="QComboBox" name="player5_combobox">
84 <property name="minimumSize">
85 <size>
86 <width>110</width>
87 <height>0</height>
88 </size>
89 </property>
90 </widget>
91 </item>
92 <item row="6" column="2">
93 <widget class="QComboBox" name="player6_combobox">
94 <property name="minimumSize">
95 <size>
96 <width>110</width>
97 <height>0</height>
98 </size>
99 </property>
100 </widget>
101 </item>
102 <item row="7" column="2">
103 <widget class="QComboBox" name="player7_combobox">
104 <property name="minimumSize">
105 <size>
106 <width>110</width>
107 <height>0</height>
108 </size>
109 </property>
110 </widget>
111 </item>
112 <item row="8" column="2">
113 <widget class="QComboBox" name="player8_combobox">
114 <property name="minimumSize">
115 <size>
116 <width>110</width>
117 <height>0</height>
118 </size>
119 </property>
120 </widget>
121 </item>
122 <item row="2" column="3">
123 <widget class="QPushButton" name="player2_configure">
124 <property name="text">
125 <string>Configure</string>
126 </property>
127 </widget>
128 </item>
129 <item row="3" column="3">
130 <widget class="QPushButton" name="player3_configure">
131 <property name="text">
132 <string>Configure</string>
133 </property>
134 </widget>
135 </item>
136 <item row="4" column="3">
137 <widget class="QPushButton" name="player4_configure">
138 <property name="text">
139 <string>Configure</string>
140 </property>
141 </widget>
142 </item>
143 <item row="5" column="3">
144 <widget class="QPushButton" name="player5_configure">
145 <property name="text">
146 <string>Configure</string>
147 </property>
148 </widget>
149 </item>
150 <item row="6" column="3">
151 <widget class="QPushButton" name="player6_configure">
152 <property name="text">
153 <string>Configure</string>
154 </property>
155 </widget>
156 </item>
157 <item row="7" column="3">
158 <widget class="QPushButton" name="player7_configure">
159 <property name="text">
160 <string>Configure</string>
161 </property>
162 </widget>
163 </item>
164 <item row="8" column="3">
165 <widget class="QPushButton" name="player8_configure">
166 <property name="text">
167 <string>Configure</string>
168 </property>
169 </widget>
170 </item>
171 <item row="0" column="0">
172 <spacer name="horizontalSpacer">
106 <property name="orientation"> 173 <property name="orientation">
107 <enum>Qt::Vertical</enum> 174 <enum>Qt::Horizontal</enum>
108 </property> 175 </property>
109 <property name="sizeHint" stdset="0"> 176 <property name="sizeHint" stdset="0">
110 <size> 177 <size>
111 <width>20</width> 178 <width>40</width>
112 <height>40</height> 179 <height>20</height>
113 </size> 180 </size>
114 </property> 181 </property>
115 </spacer> 182 </spacer>
116 </item> 183 </item>
117 </layout> 184 <item row="0" column="4">
118 </widget> 185 <spacer name="horizontalSpacer_2">
119 </item> 186 <property name="orientation">
120 <item row="0" column="0"> 187 <enum>Qt::Horizontal</enum>
121 <widget class="QGroupBox" name="faceButtons"> 188 </property>
122 <property name="title"> 189 <property name="sizeHint" stdset="0">
123 <string>Face Buttons</string> 190 <size>
124 </property> 191 <width>40</width>
125 <property name="flat"> 192 <height>20</height>
126 <bool>false</bool> 193 </size>
127 </property> 194 </property>
128 <property name="checkable"> 195 </spacer>
129 <bool>false</bool>
130 </property>
131 <layout class="QGridLayout" name="gridLayout">
132 <item row="0" column="0">
133 <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
134 <item>
135 <widget class="QLabel" name="labelA">
136 <property name="text">
137 <string>A:</string>
138 </property>
139 </widget>
140 </item>
141 <item>
142 <widget class="QPushButton" name="buttonA">
143 <property name="text">
144 <string/>
145 </property>
146 </widget>
147 </item>
148 </layout>
149 </item> 196 </item>
150 <item row="0" column="1"> 197 <item row="1" column="1">
151 <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout"> 198 <widget class="QLabel" name="label_3">
152 <item> 199 <property name="minimumSize">
153 <widget class="QLabel" name="labelB"> 200 <size>
154 <property name="text"> 201 <width>55</width>
155 <string>B:</string> 202 <height>0</height>
156 </property> 203 </size>
157 </widget> 204 </property>
158 </item> 205 <property name="text">
159 <item> 206 <string>Player 1</string>
160 <widget class="QPushButton" name="buttonB"> 207 </property>
161 <property name="text"> 208 </widget>
162 <string/>
163 </property>
164 </widget>
165 </item>
166 </layout>
167 </item> 209 </item>
168 <item row="1" column="0"> 210 <item row="2" column="1">
169 <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout"> 211 <widget class="QLabel" name="label_4">
170 <item> 212 <property name="text">
171 <widget class="QLabel" name="labelX"> 213 <string>Player 2</string>
172 <property name="text"> 214 </property>
173 <string>X:</string> 215 </widget>
174 </property>
175 </widget>
176 </item>
177 <item>
178 <widget class="QPushButton" name="buttonX">
179 <property name="text">
180 <string/>
181 </property>
182 </widget>
183 </item>
184 </layout>
185 </item> 216 </item>
186 <item row="1" column="1"> 217 <item row="3" column="1">
187 <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout"> 218 <widget class="QLabel" name="label_5">
188 <item> 219 <property name="text">
189 <widget class="QLabel" name="labelY"> 220 <string>Player 3</string>
190 <property name="text"> 221 </property>
191 <string>Y:</string> 222 </widget>
192 </property>
193 </widget>
194 </item>
195 <item>
196 <widget class="QPushButton" name="buttonY">
197 <property name="text">
198 <string/>
199 </property>
200 </widget>
201 </item>
202 </layout>
203 </item> 223 </item>
204 </layout> 224 <item row="4" column="1">
205 </widget> 225 <widget class="QLabel" name="label_6">
206 </item> 226 <property name="text">
207 <item row="0" column="1"> 227 <string>Player 4</string>
208 <widget class="QGroupBox" name="Dpad"> 228 </property>
209 <property name="title"> 229 </widget>
210 <string>Directional Pad</string>
211 </property>
212 <property name="flat">
213 <bool>false</bool>
214 </property>
215 <property name="checkable">
216 <bool>false</bool>
217 </property>
218 <layout class="QGridLayout" name="gridLayout_2">
219 <item row="1" column="0">
220 <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
221 <item>
222 <widget class="QLabel" name="labelDpadUp">
223 <property name="text">
224 <string>Up:</string>
225 </property>
226 </widget>
227 </item>
228 <item>
229 <widget class="QPushButton" name="buttonDpadUp">
230 <property name="text">
231 <string/>
232 </property>
233 </widget>
234 </item>
235 </layout>
236 </item> 230 </item>
237 <item row="1" column="1"> 231 <item row="5" column="1">
238 <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout"> 232 <widget class="QLabel" name="label_7">
239 <item> 233 <property name="text">
240 <widget class="QLabel" name="labelDpadDown"> 234 <string>Player 5</string>
241 <property name="text"> 235 </property>
242 <string>Down:</string> 236 </widget>
243 </property>
244 </widget>
245 </item>
246 <item>
247 <widget class="QPushButton" name="buttonDpadDown">
248 <property name="text">
249 <string/>
250 </property>
251 </widget>
252 </item>
253 </layout>
254 </item> 237 </item>
255 <item row="0" column="0"> 238 <item row="6" column="1">
256 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout"> 239 <widget class="QLabel" name="label_8">
257 <item> 240 <property name="text">
258 <widget class="QLabel" name="labelDpadLeft"> 241 <string>Player 6</string>
259 <property name="text"> 242 </property>
260 <string>Left:</string> 243 </widget>
261 </property>
262 </widget>
263 </item>
264 <item>
265 <widget class="QPushButton" name="buttonDpadLeft">
266 <property name="text">
267 <string/>
268 </property>
269 </widget>
270 </item>
271 </layout>
272 </item> 244 </item>
273 <item row="0" column="1"> 245 <item row="7" column="1">
274 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout"> 246 <widget class="QLabel" name="label_9">
275 <item> 247 <property name="text">
276 <widget class="QLabel" name="labelDpadRight"> 248 <string>Player 7</string>
277 <property name="text"> 249 </property>
278 <string>Right:</string> 250 </widget>
279 </property> 251 </item>
280 </widget> 252 <item row="8" column="1">
281 </item> 253 <widget class="QLabel" name="label_10">
282 <item> 254 <property name="text">
283 <widget class="QPushButton" name="buttonDpadRight"> 255 <string>Player 8</string>
284 <property name="text"> 256 </property>
285 <string/> 257 </widget>
286 </property>
287 </widget>
288 </item>
289 </layout>
290 </item> 258 </item>
291 </layout> 259 </layout>
292 </widget> 260 </widget>
293 </item> 261 </item>
294 <item row="3" column="0"> 262 <item>
295 <widget class="QGroupBox" name="shoulderButtons"> 263 <widget class="QGroupBox" name="gridGroupBox">
296 <property name="title"> 264 <property name="title">
297 <string>Shoulder Buttons</string> 265 <string>Handheld</string>
298 </property>
299 <property name="flat">
300 <bool>false</bool>
301 </property> 266 </property>
302 <property name="checkable"> 267 <layout class="QGridLayout" name="gridLayout_2">
303 <bool>false</bool> 268 <item row="1" column="2">
304 </property> 269 <spacer name="horizontalSpacer_5">
305 <layout class="QGridLayout" name="gridLayout_3"> 270 <property name="orientation">
306 <item row="0" column="0"> 271 <enum>Qt::Horizontal</enum>
307 <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout"> 272 </property>
308 <item> 273 <property name="sizeType">
309 <widget class="QLabel" name="labelL"> 274 <enum>QSizePolicy::Fixed</enum>
310 <property name="text"> 275 </property>
311 <string>L:</string> 276 <property name="sizeHint" stdset="0">
312 </property> 277 <size>
313 </widget> 278 <width>72</width>
314 </item> 279 <height>20</height>
315 <item> 280 </size>
316 <widget class="QPushButton" name="buttonL"> 281 </property>
317 <property name="text"> 282 </spacer>
318 <string/>
319 </property>
320 </widget>
321 </item>
322 </layout>
323 </item> 283 </item>
324 <item row="0" column="1"> 284 <item row="1" column="4">
325 <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> 285 <spacer name="horizontalSpacer_4">
326 <item> 286 <property name="orientation">
327 <widget class="QLabel" name="labelR"> 287 <enum>Qt::Horizontal</enum>
328 <property name="text"> 288 </property>
329 <string>R:</string> 289 <property name="sizeHint" stdset="0">
330 </property> 290 <size>
331 </widget> 291 <width>40</width>
332 </item> 292 <height>20</height>
333 <item> 293 </size>
334 <widget class="QPushButton" name="buttonR"> 294 </property>
335 <property name="text"> 295 </spacer>
336 <string/> 296 </item>
337 </property> 297 <item row="1" column="3">
338 </widget> 298 <widget class="QPushButton" name="handheld_configure">
339 </item> 299 <property name="text">
340 </layout> 300 <string>Configure</string>
301 </property>
302 </widget>
341 </item> 303 </item>
342 <item row="1" column="0"> 304 <item row="1" column="0">
343 <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout"> 305 <spacer name="horizontalSpacer_3">
344 <item> 306 <property name="orientation">
345 <widget class="QLabel" name="labelZL"> 307 <enum>Qt::Horizontal</enum>
346 <property name="text"> 308 </property>
347 <string>ZL:</string> 309 <property name="sizeHint" stdset="0">
348 </property> 310 <size>
349 </widget> 311 <width>40</width>
350 </item> 312 <height>20</height>
351 <item> 313 </size>
352 <widget class="QPushButton" name="buttonZL"> 314 </property>
353 <property name="text"> 315 </spacer>
354 <string/>
355 </property>
356 </widget>
357 </item>
358 </layout>
359 </item> 316 </item>
360 <item row="1" column="1"> 317 <item row="1" column="1">
361 <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout"> 318 <widget class="QCheckBox" name="handheld_connected">
362 <item> 319 <property name="text">
363 <widget class="QLabel" name="labelZR"> 320 <string>Joycons Docked</string>
364 <property name="text"> 321 </property>
365 <string>ZR:</string> 322 </widget>
366 </property>
367 </widget>
368 </item>
369 <item>
370 <widget class="QPushButton" name="buttonZR">
371 <property name="text">
372 <string/>
373 </property>
374 </widget>
375 </item>
376 </layout>
377 </item>
378 <item row="2" column="0">
379 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
380 <item>
381 <widget class="QLabel" name="labelSL">
382 <property name="text">
383 <string>SL:</string>
384 </property>
385 </widget>
386 </item>
387 <item>
388 <widget class="QPushButton" name="buttonSL">
389 <property name="text">
390 <string/>
391 </property>
392 </widget>
393 </item>
394 </layout>
395 </item> 323 </item>
396 <item row="2" column="1"> 324 <item row="0" column="1">
397 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> 325 <widget class="QCheckBox" name="use_docked_mode">
398 <item> 326 <property name="text">
399 <widget class="QLabel" name="labelSR"> 327 <string>Use Docked Mode</string>
400 <property name="text"> 328 </property>
401 <string>SR:</string> 329 </widget>
402 </property>
403 </widget>
404 </item>
405 <item>
406 <widget class="QPushButton" name="buttonSR">
407 <property name="text">
408 <string/>
409 </property>
410 </widget>
411 </item>
412 </layout>
413 </item> 330 </item>
414 </layout> 331 </layout>
415 </widget> 332 </widget>
416 </item> 333 </item>
417 <item row="1" column="1"> 334 <item>
418 <widget class="QGroupBox" name="RStick"> 335 <widget class="QGroupBox" name="gridGroupBox">
419 <property name="title"> 336 <property name="title">
420 <string>Right Stick</string> 337 <string>Other</string>
421 </property>
422 <property name="alignment">
423 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
424 </property>
425 <property name="flat">
426 <bool>false</bool>
427 </property> 338 </property>
428 <property name="checkable"> 339 <layout class="QGridLayout" name="gridLayout_3">
429 <bool>false</bool>
430 </property>
431 <layout class="QGridLayout" name="gridLayout_5">
432 <item row="1" column="1"> 340 <item row="1" column="1">
433 <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout"> 341 <widget class="QCheckBox" name="keyboard_enabled">
434 <item> 342 <property name="minimumSize">
435 <widget class="QLabel" name="labelRStickDown"> 343 <size>
436 <property name="text"> 344 <width>0</width>
437 <string>Down:</string> 345 <height>23</height>
438 </property> 346 </size>
439 </widget> 347 </property>
440 </item>
441 <item>
442 <widget class="QPushButton" name="buttonRStickDown">
443 <property name="text">
444 <string/>
445 </property>
446 </widget>
447 </item>
448 </layout>
449 </item>
450 <item row="0" column="1">
451 <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
452 <item>
453 <widget class="QLabel" name="labelRStickRight">
454 <property name="text">
455 <string>Right:</string>
456 </property>
457 </widget>
458 </item>
459 <item>
460 <widget class="QPushButton" name="buttonRStickRight">
461 <property name="text">
462 <string/>
463 </property>
464 </widget>
465 </item>
466 </layout>
467 </item>
468 <item row="3" column="0" colspan="2">
469 <widget class="QPushButton" name="buttonRStickAnalog">
470 <property name="text"> 348 <property name="text">
471 <string>Set Analog Stick</string> 349 <string>Keyboard</string>
472 </property> 350 </property>
473 </widget> 351 </widget>
474 </item> 352 </item>
475 <item row="0" column="0">
476 <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
477 <item>
478 <widget class="QLabel" name="labelRStickLeft">
479 <property name="text">
480 <string>Left:</string>
481 </property>
482 </widget>
483 </item>
484 <item>
485 <widget class="QPushButton" name="buttonRStickLeft">
486 <property name="text">
487 <string/>
488 </property>
489 </widget>
490 </item>
491 </layout>
492 </item>
493 <item row="1" column="0">
494 <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
495 <item>
496 <widget class="QLabel" name="labelRStickUp">
497 <property name="text">
498 <string>Up:</string>
499 </property>
500 </widget>
501 </item>
502 <item>
503 <widget class="QPushButton" name="buttonRStickUp">
504 <property name="text">
505 <string/>
506 </property>
507 </widget>
508 </item>
509 </layout>
510 </item>
511 <item row="2" column="0">
512 <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
513 <item>
514 <widget class="QLabel" name="labelRStickPressed">
515 <property name="text">
516 <string>Pressed:</string>
517 </property>
518 </widget>
519 </item>
520 <item>
521 <widget class="QPushButton" name="buttonRStick">
522 <property name="text">
523 <string/>
524 </property>
525 </widget>
526 </item>
527 </layout>
528 </item>
529 <item row="2" column="1"> 353 <item row="2" column="1">
530 <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout"> 354 <widget class="QCheckBox" name="debug_enabled">
531 <item> 355 <property name="text">
532 <widget class="QLabel" name="labelRStickMod"> 356 <string>Debug Controller</string>
533 <property name="text"> 357 </property>
534 <string>Modifier:</string> 358 </widget>
535 </property>
536 </widget>
537 </item>
538 <item>
539 <widget class="QPushButton" name="buttonRStickMod">
540 <property name="text">
541 <string/>
542 </property>
543 </widget>
544 </item>
545 </layout>
546 </item> 359 </item>
547 </layout> 360 <item row="3" column="1">
548 </widget> 361 <widget class="QCheckBox" name="touchscreen_enabled">
549 </item>
550 <item row="1" column="0">
551 <widget class="QGroupBox" name="LStick">
552 <property name="title">
553 <string>Left Stick</string>
554 </property>
555 <property name="flat">
556 <bool>false</bool>
557 </property>
558 <property name="checkable">
559 <bool>false</bool>
560 </property>
561 <layout class="QGridLayout" name="gridLayout_4">
562 <item row="1" column="1">
563 <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
564 <item>
565 <widget class="QLabel" name="labelLStickDown">
566 <property name="text">
567 <string>Down:</string>
568 </property>
569 </widget>
570 </item>
571 <item>
572 <widget class="QPushButton" name="buttonLStickDown">
573 <property name="text">
574 <string/>
575 </property>
576 </widget>
577 </item>
578 </layout>
579 </item>
580 <item row="4" column="0" colspan="2">
581 <widget class="QPushButton" name="buttonLStickAnalog">
582 <property name="text"> 362 <property name="text">
583 <string>Set Analog Stick</string> 363 <string>Touchscreen</string>
584 </property> 364 </property>
585 </widget> 365 </widget>
586 </item> 366 </item>
587 <item row="0" column="1"> 367 <item row="0" column="1">
588 <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout"> 368 <widget class="QCheckBox" name="mouse_enabled">
589 <item> 369 <property name="minimumSize">
590 <widget class="QLabel" name="labelLStickRight"> 370 <size>
591 <property name="text"> 371 <width>0</width>
592 <string>Right:</string> 372 <height>23</height>
593 </property> 373 </size>
594 </widget> 374 </property>
595 </item> 375 <property name="text">
596 <item> 376 <string>Mouse</string>
597 <widget class="QPushButton" name="buttonLStickRight"> 377 </property>
598 <property name="text"> 378 </widget>
599 <string/> 379 </item>
600 </property> 380 <item row="0" column="4">
601 </widget> 381 <spacer name="horizontalSpacer_7">
602 </item> 382 <property name="orientation">
603 </layout> 383 <enum>Qt::Horizontal</enum>
384 </property>
385 <property name="sizeHint" stdset="0">
386 <size>
387 <width>40</width>
388 <height>20</height>
389 </size>
390 </property>
391 </spacer>
392 </item>
393 <item row="0" column="2">
394 <spacer name="horizontalSpacer_8">
395 <property name="orientation">
396 <enum>Qt::Horizontal</enum>
397 </property>
398 <property name="sizeType">
399 <enum>QSizePolicy::Fixed</enum>
400 </property>
401 <property name="sizeHint" stdset="0">
402 <size>
403 <width>76</width>
404 <height>20</height>
405 </size>
406 </property>
407 </spacer>
604 </item> 408 </item>
605 <item row="0" column="0"> 409 <item row="0" column="0">
606 <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout"> 410 <spacer name="horizontalSpacer_6">
607 <item> 411 <property name="orientation">
608 <widget class="QLabel" name="labelLStickLeft"> 412 <enum>Qt::Horizontal</enum>
609 <property name="text"> 413 </property>
610 <string>Left:</string> 414 <property name="sizeHint" stdset="0">
611 </property> 415 <size>
612 </widget> 416 <width>40</width>
613 </item> 417 <height>20</height>
614 <item> 418 </size>
615 <widget class="QPushButton" name="buttonLStickLeft"> 419 </property>
616 <property name="text"> 420 </spacer>
617 <string/>
618 </property>
619 </widget>
620 </item>
621 </layout>
622 </item> 421 </item>
623 <item row="1" column="0"> 422 <item row="3" column="3">
624 <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout"> 423 <widget class="QPushButton" name="touchscreen_advanced">
625 <item> 424 <property name="text">
626 <widget class="QLabel" name="labelLStickUp"> 425 <string>Advanced</string>
627 <property name="text"> 426 </property>
628 <string>Up:</string> 427 </widget>
629 </property>
630 </widget>
631 </item>
632 <item>
633 <widget class="QPushButton" name="buttonLStickUp">
634 <property name="text">
635 <string/>
636 </property>
637 </widget>
638 </item>
639 </layout>
640 </item>
641 <item row="3" column="0">
642 <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
643 <item>
644 <widget class="QLabel" name="labelLStickMod">
645 <property name="text">
646 <string>Modifier:</string>
647 </property>
648 </widget>
649 </item>
650 <item>
651 <widget class="QPushButton" name="buttonLStickMod">
652 <property name="text">
653 <string/>
654 </property>
655 </widget>
656 </item>
657 </layout>
658 </item> 428 </item>
659 <item row="3" column="1"> 429 <item row="2" column="3">
660 <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0"> 430 <widget class="QPushButton" name="debug_configure">
661 <item> 431 <property name="text">
662 <widget class="QLabel" name="labelLStickPressed"> 432 <string>Configure</string>
663 <property name="text"> 433 </property>
664 <string>Pressed:</string> 434 </widget>
665 </property> 435 </item>
666 </widget> 436 <item row="0" column="3">
667 </item> 437 <widget class="QPushButton" name="mouse_advanced">
668 <item> 438 <property name="text">
669 <widget class="QPushButton" name="buttonLStick"> 439 <string>Advanced</string>
670 <property name="text"> 440 </property>
671 <string/> 441 </widget>
672 </property>
673 </widget>
674 </item>
675 </layout>
676 </item> 442 </item>
677 </layout> 443 </layout>
678 </widget> 444 </widget>
679 </item> 445 </item>
680 </layout>
681 </item>
682 <item>
683 <layout class="QHBoxLayout" name="horizontalLayout">
684 <item> 446 <item>
685 <spacer name="horizontalSpacer"> 447 <spacer name="verticalSpacer">
686 <property name="orientation"> 448 <property name="orientation">
687 <enum>Qt::Horizontal</enum> 449 <enum>Qt::Vertical</enum>
688 </property> 450 </property>
689 <property name="sizeHint" stdset="0"> 451 <property name="sizeHint" stdset="0">
690 <size> 452 <size>
691 <width>40</width> 453 <width>20</width>
692 <height>20</height> 454 <height>40</height>
693 </size> 455 </size>
694 </property> 456 </property>
695 </spacer> 457 </spacer>
696 </item> 458 </item>
697 <item> 459 <item>
698 <widget class="QPushButton" name="buttonClearAll"> 460 <layout class="QHBoxLayout" name="horizontalLayout">
699 <property name="sizePolicy"> 461 <item>
700 <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> 462 <widget class="QPushButton" name="restore_defaults_button">
701 <horstretch>0</horstretch> 463 <property name="text">
702 <verstretch>0</verstretch> 464 <string>Restore Defaults</string>
703 </sizepolicy> 465 </property>
704 </property> 466 </widget>
705 <property name="sizeIncrement"> 467 </item>
706 <size> 468 <item>
707 <width>0</width> 469 <spacer name="horizontalSpacer_9">
708 <height>0</height> 470 <property name="orientation">
709 </size> 471 <enum>Qt::Horizontal</enum>
710 </property> 472 </property>
711 <property name="baseSize"> 473 <property name="sizeHint" stdset="0">
712 <size> 474 <size>
713 <width>0</width> 475 <width>40</width>
714 <height>0</height> 476 <height>20</height>
715 </size> 477 </size>
716 </property> 478 </property>
717 <property name="layoutDirection"> 479 </spacer>
718 <enum>Qt::LeftToRight</enum> 480 </item>
719 </property> 481 </layout>
720 <property name="text">
721 <string>Clear All</string>
722 </property>
723 </widget>
724 </item>
725 <item>
726 <widget class="QPushButton" name="buttonRestoreDefaults">
727 <property name="sizePolicy">
728 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
729 <horstretch>0</horstretch>
730 <verstretch>0</verstretch>
731 </sizepolicy>
732 </property>
733 <property name="sizeIncrement">
734 <size>
735 <width>0</width>
736 <height>0</height>
737 </size>
738 </property>
739 <property name="baseSize">
740 <size>
741 <width>0</width>
742 <height>0</height>
743 </size>
744 </property>
745 <property name="layoutDirection">
746 <enum>Qt::LeftToRight</enum>
747 </property>
748 <property name="text">
749 <string>Restore Defaults</string>
750 </property>
751 </widget>
752 </item> 482 </item>
753 </layout> 483 </layout>
754 </item> 484 </item>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
new file mode 100644
index 000000000..ba6e09368
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -0,0 +1,508 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7#include <utility>
8#include <QColorDialog>
9#include <QMenu>
10#include <QMessageBox>
11#include <QTimer>
12#include "common/assert.h"
13#include "common/param_package.h"
14#include "input_common/main.h"
15#include "ui_configure_input_player.h"
16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configure_input_player.h"
18
19const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
20 ConfigureInputPlayer::analog_sub_buttons{{
21 "up",
22 "down",
23 "left",
24 "right",
25 "modifier",
26 }};
27
28static void MoveGridElement(QGridLayout* grid, int row_old, int column_old, int row_new,
29 int column_new) {
30 const auto item = grid->itemAtPosition(row_old, column_old);
31 // grid->removeItem(item);
32 grid->addItem(item, row_new, column_new);
33}
34
35static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) {
36 const int index1 = grid->indexOf(item);
37 const int index2 = grid->indexOf(onTopOf);
38 int row, column, rowSpan, columnSpan;
39 grid->getItemPosition(index2, &row, &column, &rowSpan, &columnSpan);
40 grid->takeAt(index1);
41 grid->addWidget(item, row, column, rowSpan, columnSpan);
42}
43
44static QString GetKeyName(int key_code) {
45 switch (key_code) {
46 case Qt::Key_Shift:
47 return QObject::tr("Shift");
48 case Qt::Key_Control:
49 return QObject::tr("Ctrl");
50 case Qt::Key_Alt:
51 return QObject::tr("Alt");
52 case Qt::Key_Meta:
53 return "";
54 default:
55 return QKeySequence(key_code).toString();
56 }
57}
58
59static void SetAnalogButton(const Common::ParamPackage& input_param,
60 Common::ParamPackage& analog_param, const std::string& button_name) {
61 if (analog_param.Get("engine", "") != "analog_from_button") {
62 analog_param = {
63 {"engine", "analog_from_button"},
64 {"modifier_scale", "0.5"},
65 };
66 }
67 analog_param.Set(button_name, input_param.Serialize());
68}
69
70static QString ButtonToText(const Common::ParamPackage& param) {
71 if (!param.Has("engine")) {
72 return QObject::tr("[not set]");
73 } else if (param.Get("engine", "") == "keyboard") {
74 return GetKeyName(param.Get("code", 0));
75 } else if (param.Get("engine", "") == "sdl") {
76 if (param.Has("hat")) {
77 return QString(QObject::tr("Hat %1 %2"))
78 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
79 }
80 if (param.Has("axis")) {
81 return QString(QObject::tr("Axis %1%2"))
82 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
83 }
84 if (param.Has("button")) {
85 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
86 }
87 return QString();
88 } else {
89 return QObject::tr("[unknown]");
90 }
91};
92
93static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
94 if (!param.Has("engine")) {
95 return QObject::tr("[not set]");
96 } else if (param.Get("engine", "") == "analog_from_button") {
97 return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
98 } else if (param.Get("engine", "") == "sdl") {
99 if (dir == "modifier") {
100 return QString(QObject::tr("[unused]"));
101 }
102
103 if (dir == "left" || dir == "right") {
104 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_x", "").c_str());
105 } else if (dir == "up" || dir == "down") {
106 return QString(QObject::tr("Axis %1")).arg(param.Get("axis_y", "").c_str());
107 }
108 return QString();
109 } else {
110 return QObject::tr("[unknown]");
111 }
112};
113
114ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug)
115 : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()),
116 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
117 player_index(player_index), debug(debug) {
118
119 ui->setupUi(this);
120 setFocusPolicy(Qt::ClickFocus);
121
122 button_map = {
123 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
124 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
125 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
126 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
127 ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown,
128 ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
129 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
130 };
131
132 analog_map_buttons = {{
133 {
134 ui->buttonLStickUp,
135 ui->buttonLStickDown,
136 ui->buttonLStickLeft,
137 ui->buttonLStickRight,
138 ui->buttonLStickMod,
139 },
140 {
141 ui->buttonRStickUp,
142 ui->buttonRStickDown,
143 ui->buttonRStickLeft,
144 ui->buttonRStickRight,
145 ui->buttonRStickMod,
146 },
147 }};
148
149 debug_hidden = {
150 ui->buttonSL, ui->labelSL,
151 ui->buttonSR, ui->labelSR,
152 ui->buttonLStick, ui->labelLStickPressed,
153 ui->buttonRStick, ui->labelRStickPressed,
154 ui->buttonHome, ui->labelHome,
155 ui->buttonScreenshot, ui->labelScreenshot,
156 };
157
158 auto layout = Settings::values.players[player_index].type;
159 if (debug)
160 layout = Settings::ControllerType::DualJoycon;
161
162 switch (layout) {
163 case Settings::ControllerType::ProController:
164 case Settings::ControllerType::DualJoycon:
165 layout_hidden = {
166 ui->buttonSL,
167 ui->labelSL,
168 ui->buttonSR,
169 ui->labelSR,
170 };
171 break;
172 case Settings::ControllerType::LeftJoycon:
173 layout_hidden = {
174 ui->right_body_button,
175 ui->right_buttons_button,
176 ui->right_body_label,
177 ui->right_buttons_label,
178 ui->buttonR,
179 ui->labelR,
180 ui->buttonZR,
181 ui->labelZR,
182 ui->labelHome,
183 ui->buttonHome,
184 ui->buttonPlus,
185 ui->labelPlus,
186 ui->RStick,
187 ui->faceButtons,
188 };
189 break;
190 case Settings::ControllerType::RightJoycon:
191 layout_hidden = {
192 ui->left_body_button, ui->left_buttons_button,
193 ui->left_body_label, ui->left_buttons_label,
194 ui->buttonL, ui->labelL,
195 ui->buttonZL, ui->labelZL,
196 ui->labelScreenshot, ui->buttonScreenshot,
197 ui->buttonMinus, ui->labelMinus,
198 ui->LStick, ui->Dpad,
199 };
200 break;
201 }
202
203 if (debug || layout == Settings::ControllerType::ProController) {
204 ui->controller_color->hide();
205 } else {
206 if (layout == Settings::ControllerType::LeftJoycon ||
207 layout == Settings::ControllerType::RightJoycon) {
208 ui->horizontalSpacer_4->setGeometry({0, 0, 0, 0});
209
210 LayerGridElements(ui->buttons, ui->shoulderButtons, ui->Dpad);
211 LayerGridElements(ui->buttons, ui->misc, ui->RStick);
212 LayerGridElements(ui->buttons, ui->Dpad, ui->faceButtons);
213 LayerGridElements(ui->buttons, ui->RStick, ui->LStick);
214 }
215 }
216
217 for (auto* widget : layout_hidden)
218 widget->setVisible(false);
219
220 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
221
222 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
223 if (!button_map[button_id])
224 continue;
225 button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
226 connect(button_map[button_id], &QPushButton::released, [=]() {
227 handleClick(
228 button_map[button_id],
229 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
230 InputCommon::Polling::DeviceType::Button);
231 });
232 connect(button_map[button_id], &QPushButton::customContextMenuRequested,
233 [=](const QPoint& menu_location) {
234 QMenu context_menu;
235 context_menu.addAction(tr("Clear"), [&] {
236 buttons_param[button_id].Clear();
237 button_map[button_id]->setText(tr("[not set]"));
238 });
239 context_menu.addAction(tr("Restore Default"), [&] {
240 buttons_param[button_id] = Common::ParamPackage{
241 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
242 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
243 });
244 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
245 });
246 }
247
248 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
249 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
250 if (!analog_map_buttons[analog_id][sub_button_id])
251 continue;
252 analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
253 Qt::CustomContextMenu);
254 connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() {
255 handleClick(analog_map_buttons[analog_id][sub_button_id],
256 [=](const Common::ParamPackage& params) {
257 SetAnalogButton(params, analogs_param[analog_id],
258 analog_sub_buttons[sub_button_id]);
259 },
260 InputCommon::Polling::DeviceType::Button);
261 });
262 connect(analog_map_buttons[analog_id][sub_button_id],
263 &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
264 QMenu context_menu;
265 context_menu.addAction(tr("Clear"), [&] {
266 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
267 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
268 });
269 context_menu.addAction(tr("Restore Default"), [&] {
270 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
271 Config::default_analogs[analog_id][sub_button_id])};
272 SetAnalogButton(params, analogs_param[analog_id],
273 analog_sub_buttons[sub_button_id]);
274 analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
275 analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
276 });
277 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
278 menu_location));
279 });
280 }
281 connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
282 QMessageBox::information(this, tr("Information"),
283 tr("After pressing OK, first move your joystick horizontally, "
284 "and then vertically."));
285 handleClick(
286 analog_map_stick[analog_id],
287 [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; },
288 InputCommon::Polling::DeviceType::Analog);
289 });
290 }
291
292 connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
293 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
294
295 timeout_timer->setSingleShot(true);
296 connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
297
298 connect(poll_timer.get(), &QTimer::timeout, [this]() {
299 Common::ParamPackage params;
300 for (auto& poller : device_pollers) {
301 params = poller->GetNextInput();
302 if (params.Has("engine")) {
303 setPollingResult(params, false);
304 return;
305 }
306 }
307 });
308
309 controller_color_buttons = {
310 ui->left_body_button,
311 ui->left_buttons_button,
312 ui->right_body_button,
313 ui->right_buttons_button,
314 };
315
316 for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) {
317 connect(controller_color_buttons[i], &QPushButton::clicked, this,
318 std::bind(&ConfigureInputPlayer::OnControllerButtonClick, this, i));
319 }
320
321 this->loadConfiguration();
322 this->resize(0, 0);
323
324 // TODO(wwylele): enable this when we actually emulate it
325 ui->buttonHome->setEnabled(false);
326}
327
328ConfigureInputPlayer::~ConfigureInputPlayer() = default;
329
330void ConfigureInputPlayer::applyConfiguration() {
331 auto& buttons =
332 debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons;
333 auto& analogs =
334 debug ? Settings::values.debug_pad_analogs : Settings::values.players[player_index].analogs;
335
336 std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
337 [](const Common::ParamPackage& param) { return param.Serialize(); });
338 std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
339 [](const Common::ParamPackage& param) { return param.Serialize(); });
340
341 if (debug)
342 return;
343
344 std::array<u32, 4> colors{};
345 std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(),
346 [](QColor color) { return color.rgb(); });
347
348 Settings::values.players[player_index].body_color_left = colors[0];
349 Settings::values.players[player_index].button_color_left = colors[1];
350 Settings::values.players[player_index].body_color_right = colors[2];
351 Settings::values.players[player_index].button_color_right = colors[3];
352}
353
354void ConfigureInputPlayer::OnControllerButtonClick(int i) {
355 const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]);
356 if (!new_bg_color.isValid())
357 return;
358 controller_colors[i] = new_bg_color;
359 controller_color_buttons[i]->setStyleSheet(
360 QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
361}
362
363void ConfigureInputPlayer::loadConfiguration() {
364 if (debug) {
365 std::transform(Settings::values.debug_pad_buttons.begin(),
366 Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
367 [](const std::string& str) { return Common::ParamPackage(str); });
368 std::transform(Settings::values.debug_pad_analogs.begin(),
369 Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
370 [](const std::string& str) { return Common::ParamPackage(str); });
371 } else {
372 std::transform(Settings::values.players[player_index].buttons.begin(),
373 Settings::values.players[player_index].buttons.end(), buttons_param.begin(),
374 [](const std::string& str) { return Common::ParamPackage(str); });
375 std::transform(Settings::values.players[player_index].analogs.begin(),
376 Settings::values.players[player_index].analogs.end(), analogs_param.begin(),
377 [](const std::string& str) { return Common::ParamPackage(str); });
378 }
379
380 updateButtonLabels();
381
382 if (debug)
383 return;
384
385 std::array<u32, 4> colors = {
386 Settings::values.players[player_index].body_color_left,
387 Settings::values.players[player_index].button_color_left,
388 Settings::values.players[player_index].body_color_right,
389 Settings::values.players[player_index].button_color_right,
390 };
391
392 std::transform(colors.begin(), colors.end(), controller_colors.begin(),
393 [](u32 rgb) { return QColor::fromRgb(rgb); });
394
395 for (std::size_t i = 0; i < colors.size(); ++i) {
396 controller_color_buttons[i]->setStyleSheet(
397 QString("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
398 }
399}
400
401void ConfigureInputPlayer::restoreDefaults() {
402 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
403 buttons_param[button_id] = Common::ParamPackage{
404 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
405 }
406
407 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
408 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
409 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
410 Config::default_analogs[analog_id][sub_button_id])};
411 SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
412 }
413 }
414 updateButtonLabels();
415}
416
417void ConfigureInputPlayer::ClearAll() {
418 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
419 if (button_map[button_id] && button_map[button_id]->isEnabled())
420 buttons_param[button_id].Clear();
421 }
422 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
423 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
424 if (analog_map_buttons[analog_id][sub_button_id] &&
425 analog_map_buttons[analog_id][sub_button_id]->isEnabled())
426 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
427 }
428 }
429
430 updateButtonLabels();
431}
432
433void ConfigureInputPlayer::updateButtonLabels() {
434 for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
435 button_map[button]->setText(ButtonToText(buttons_param[button]));
436 }
437
438 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
439 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
440 if (analog_map_buttons[analog_id][sub_button_id]) {
441 analog_map_buttons[analog_id][sub_button_id]->setText(
442 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
443 }
444 }
445 analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
446 }
447}
448
449void ConfigureInputPlayer::handleClick(
450 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
451 InputCommon::Polling::DeviceType type) {
452 button->setText(tr("[press key]"));
453 button->setFocus();
454
455 const auto iter = std::find(button_map.begin(), button_map.end(), button);
456 ASSERT(iter != button_map.end());
457 const auto index = std::distance(button_map.begin(), iter);
458 ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
459
460 input_setter = new_input_setter;
461
462 device_pollers = InputCommon::Polling::GetPollers(type);
463
464 // Keyboard keys can only be used as button devices
465 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
466
467 for (auto& poller : device_pollers) {
468 poller->Start();
469 }
470
471 grabKeyboard();
472 grabMouse();
473 timeout_timer->start(5000); // Cancel after 5 seconds
474 poll_timer->start(200); // Check for new inputs every 200ms
475}
476
477void ConfigureInputPlayer::setPollingResult(const Common::ParamPackage& params, bool abort) {
478 releaseKeyboard();
479 releaseMouse();
480 timeout_timer->stop();
481 poll_timer->stop();
482 for (auto& poller : device_pollers) {
483 poller->Stop();
484 }
485
486 if (!abort) {
487 (*input_setter)(params);
488 }
489
490 updateButtonLabels();
491 input_setter = std::nullopt;
492}
493
494void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
495 if (!input_setter || !event)
496 return;
497
498 if (event->key() != Qt::Key_Escape) {
499 if (want_keyboard_keys) {
500 setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
501 false);
502 } else {
503 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
504 return;
505 }
506 }
507 setPollingResult({}, true);
508}
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
new file mode 100644
index 000000000..b0e5550c5
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -0,0 +1,103 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <functional>
9#include <memory>
10#include <optional>
11#include <string>
12#include <unordered_map>
13#include <QDialog>
14#include <QKeyEvent>
15#include "common/param_package.h"
16#include "core/settings.h"
17#include "input_common/main.h"
18#include "ui_configure_input.h"
19
20class QPushButton;
21class QString;
22class QTimer;
23
24namespace Ui {
25class ConfigureInputPlayer;
26}
27
28class ConfigureInputPlayer : public QDialog {
29 Q_OBJECT
30
31public:
32 explicit ConfigureInputPlayer(QWidget* parent, u8 player_index, bool debug = false);
33 ~ConfigureInputPlayer() override;
34
35 /// Save all button configurations to settings file
36 void applyConfiguration();
37
38private:
39 std::unique_ptr<Ui::ConfigureInputPlayer> ui;
40
41 u8 player_index;
42 bool debug;
43
44 std::unique_ptr<QTimer> timeout_timer;
45 std::unique_ptr<QTimer> poll_timer;
46
47 /// This will be the the setting function when an input is awaiting configuration.
48 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
49
50 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
51 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
52
53 static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
54
55 /// Each button input is represented by a QPushButton.
56 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
57
58 std::vector<QWidget*> debug_hidden;
59 std::vector<QWidget*> layout_hidden;
60
61 /// A group of five QPushButtons represent one analog input. The buttons each represent up,
62 /// down, left, right, and modifier, respectively.
63 std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
64 analog_map_buttons;
65
66 /// Analog inputs are also represented each with a single button, used to configure with an
67 /// actual analog stick
68 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
69
70 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
71
72 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
73
74 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
75 /// keyboard events are ignored.
76 bool want_keyboard_keys = false;
77
78 std::array<QPushButton*, 4> controller_color_buttons;
79 std::array<QColor, 4> controller_colors;
80
81 void OnControllerButtonClick(int i);
82
83 /// Load configuration settings.
84 void loadConfiguration();
85 /// Restore all buttons to their default values.
86 void restoreDefaults();
87 /// Clear all input configuration
88 void ClearAll();
89
90 /// Update UI to reflect current configuration.
91 void updateButtonLabels();
92
93 /// Called when the button was pressed.
94 void handleClick(QPushButton* button,
95 std::function<void(const Common::ParamPackage&)> new_input_setter,
96 InputCommon::Polling::DeviceType type);
97
98 /// Finish polling and configure input using the input_setter
99 void setPollingResult(const Common::ParamPackage& params, bool abort);
100
101 /// Handle key press events.
102 void keyPressEvent(QKeyEvent* event) override;
103};
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
new file mode 100644
index 000000000..42db020be
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -0,0 +1,1164 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureInputPlayer</class>
4 <widget class="QDialog" name="ConfigureInputPlayer">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>408</width>
10 <height>731</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Input</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout_5">
17 <item>
18 <layout class="QGridLayout" name="buttons">
19 <item row="1" column="1">
20 <widget class="QGroupBox" name="RStick">
21 <property name="title">
22 <string>Right Stick</string>
23 </property>
24 <property name="alignment">
25 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
26 </property>
27 <property name="flat">
28 <bool>false</bool>
29 </property>
30 <property name="checkable">
31 <bool>false</bool>
32 </property>
33 <layout class="QGridLayout" name="gridLayout_5">
34 <item row="1" column="1">
35 <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout">
36 <item>
37 <layout class="QHBoxLayout" name="buttonRStickDownHorizontalLayout">
38 <item>
39 <widget class="QLabel" name="labelRStickDown">
40 <property name="text">
41 <string>Down:</string>
42 </property>
43 </widget>
44 </item>
45 </layout>
46 </item>
47 <item>
48 <widget class="QPushButton" name="buttonRStickDown">
49 <property name="text">
50 <string/>
51 </property>
52 </widget>
53 </item>
54 </layout>
55 </item>
56 <item row="0" column="1">
57 <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
58 <item>
59 <layout class="QHBoxLayout" name="buttonRStickRightHorizontalLayout">
60 <item>
61 <widget class="QLabel" name="labelRStickRight">
62 <property name="text">
63 <string>Right:</string>
64 </property>
65 </widget>
66 </item>
67 </layout>
68 </item>
69 <item>
70 <widget class="QPushButton" name="buttonRStickRight">
71 <property name="text">
72 <string/>
73 </property>
74 </widget>
75 </item>
76 </layout>
77 </item>
78 <item row="3" column="0" colspan="2">
79 <widget class="QPushButton" name="buttonRStickAnalog">
80 <property name="text">
81 <string>Set Analog Stick</string>
82 </property>
83 </widget>
84 </item>
85 <item row="0" column="0">
86 <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
87 <item>
88 <layout class="QHBoxLayout" name="buttonRStickLeftHorizontalLayout">
89 <item>
90 <widget class="QLabel" name="labelRStickLeft">
91 <property name="text">
92 <string>Left:</string>
93 </property>
94 </widget>
95 </item>
96 </layout>
97 </item>
98 <item>
99 <widget class="QPushButton" name="buttonRStickLeft">
100 <property name="text">
101 <string/>
102 </property>
103 </widget>
104 </item>
105 </layout>
106 </item>
107 <item row="1" column="0">
108 <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
109 <item>
110 <layout class="QHBoxLayout" name="buttonRStickUpHorizontalLayout">
111 <item>
112 <widget class="QLabel" name="labelRStickUp">
113 <property name="text">
114 <string>Up:</string>
115 </property>
116 </widget>
117 </item>
118 </layout>
119 </item>
120 <item>
121 <widget class="QPushButton" name="buttonRStickUp">
122 <property name="text">
123 <string/>
124 </property>
125 </widget>
126 </item>
127 </layout>
128 </item>
129 <item row="2" column="0">
130 <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
131 <item>
132 <layout class="QHBoxLayout" name="buttonRStickPressedHorizontalLayout">
133 <item>
134 <widget class="QLabel" name="labelRStickPressed">
135 <property name="text">
136 <string>Pressed:</string>
137 </property>
138 </widget>
139 </item>
140 </layout>
141 </item>
142 <item>
143 <widget class="QPushButton" name="buttonRStick">
144 <property name="text">
145 <string/>
146 </property>
147 </widget>
148 </item>
149 </layout>
150 </item>
151 <item row="2" column="1">
152 <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout">
153 <item>
154 <layout class="QHBoxLayout" name="buttonRStickModHorizontalLayout">
155 <item>
156 <widget class="QLabel" name="labelRStickMod">
157 <property name="text">
158 <string>Modifier:</string>
159 </property>
160 </widget>
161 </item>
162 </layout>
163 </item>
164 <item>
165 <widget class="QPushButton" name="buttonRStickMod">
166 <property name="text">
167 <string/>
168 </property>
169 </widget>
170 </item>
171 </layout>
172 </item>
173 </layout>
174 </widget>
175 </item>
176 <item row="0" column="1">
177 <widget class="QGroupBox" name="Dpad">
178 <property name="title">
179 <string>Directional Pad</string>
180 </property>
181 <property name="flat">
182 <bool>false</bool>
183 </property>
184 <property name="checkable">
185 <bool>false</bool>
186 </property>
187 <layout class="QGridLayout" name="gridLayout_2">
188 <item row="1" column="0">
189 <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
190 <item>
191 <layout class="QHBoxLayout" name="buttonDpadUpHorizontalLayout">
192 <item>
193 <widget class="QLabel" name="labelDpadUp">
194 <property name="text">
195 <string>Up:</string>
196 </property>
197 </widget>
198 </item>
199 </layout>
200 </item>
201 <item>
202 <widget class="QPushButton" name="buttonDpadUp">
203 <property name="text">
204 <string/>
205 </property>
206 </widget>
207 </item>
208 </layout>
209 </item>
210 <item row="1" column="1">
211 <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
212 <item>
213 <layout class="QHBoxLayout" name="buttonDpadDownHorizontalLayout">
214 <item>
215 <widget class="QLabel" name="labelDpadDown">
216 <property name="text">
217 <string>Down:</string>
218 </property>
219 </widget>
220 </item>
221 </layout>
222 </item>
223 <item>
224 <widget class="QPushButton" name="buttonDpadDown">
225 <property name="text">
226 <string/>
227 </property>
228 </widget>
229 </item>
230 </layout>
231 </item>
232 <item row="0" column="0">
233 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
234 <item>
235 <layout class="QHBoxLayout" name="buttonDpadLeftHorizontalLayout">
236 <item>
237 <widget class="QLabel" name="labelDpadLeft">
238 <property name="minimumSize">
239 <size>
240 <width>80</width>
241 <height>0</height>
242 </size>
243 </property>
244 <property name="text">
245 <string>Left:</string>
246 </property>
247 </widget>
248 </item>
249 </layout>
250 </item>
251 <item>
252 <widget class="QPushButton" name="buttonDpadLeft">
253 <property name="text">
254 <string/>
255 </property>
256 </widget>
257 </item>
258 </layout>
259 </item>
260 <item row="0" column="1">
261 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
262 <item>
263 <layout class="QHBoxLayout" name="buttonDpadRightHorizontalLayout">
264 <item>
265 <widget class="QLabel" name="labelDpadRight">
266 <property name="minimumSize">
267 <size>
268 <width>80</width>
269 <height>0</height>
270 </size>
271 </property>
272 <property name="text">
273 <string>Right:</string>
274 </property>
275 </widget>
276 </item>
277 </layout>
278 </item>
279 <item>
280 <widget class="QPushButton" name="buttonDpadRight">
281 <property name="text">
282 <string/>
283 </property>
284 </widget>
285 </item>
286 </layout>
287 </item>
288 </layout>
289 </widget>
290 </item>
291 <item row="0" column="0">
292 <widget class="QGroupBox" name="faceButtons">
293 <property name="title">
294 <string>Face Buttons</string>
295 </property>
296 <property name="flat">
297 <bool>false</bool>
298 </property>
299 <property name="checkable">
300 <bool>false</bool>
301 </property>
302 <layout class="QGridLayout" name="gridLayout">
303 <item row="0" column="0">
304 <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
305 <item>
306 <layout class="QHBoxLayout" name="buttonFaceButtonsAHorizontalLayout">
307 <item>
308 <widget class="QLabel" name="labelA">
309 <property name="minimumSize">
310 <size>
311 <width>80</width>
312 <height>0</height>
313 </size>
314 </property>
315 <property name="text">
316 <string>A:</string>
317 </property>
318 </widget>
319 </item>
320 </layout>
321 </item>
322 <item>
323 <widget class="QPushButton" name="buttonA">
324 <property name="text">
325 <string/>
326 </property>
327 </widget>
328 </item>
329 </layout>
330 </item>
331 <item row="0" column="1">
332 <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
333 <item>
334 <layout class="QHBoxLayout" name="buttonFaceButtonsBHorizontalLayout">
335 <item>
336 <widget class="QLabel" name="labelB">
337 <property name="minimumSize">
338 <size>
339 <width>80</width>
340 <height>0</height>
341 </size>
342 </property>
343 <property name="text">
344 <string>B:</string>
345 </property>
346 </widget>
347 </item>
348 </layout>
349 </item>
350 <item>
351 <widget class="QPushButton" name="buttonB">
352 <property name="text">
353 <string/>
354 </property>
355 </widget>
356 </item>
357 </layout>
358 </item>
359 <item row="1" column="0">
360 <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
361 <item>
362 <layout class="QHBoxLayout" name="buttonFaceButtonsXHorizontalLayout">
363 <item>
364 <widget class="QLabel" name="labelX">
365 <property name="text">
366 <string>X:</string>
367 </property>
368 </widget>
369 </item>
370 </layout>
371 </item>
372 <item>
373 <widget class="QPushButton" name="buttonX">
374 <property name="text">
375 <string/>
376 </property>
377 </widget>
378 </item>
379 </layout>
380 </item>
381 <item row="1" column="1">
382 <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
383 <item>
384 <layout class="QHBoxLayout" name="buttonFaceButtonsYHorizontalLayout">
385 <item>
386 <widget class="QLabel" name="labelY">
387 <property name="text">
388 <string>Y:</string>
389 </property>
390 </widget>
391 </item>
392 </layout>
393 </item>
394 <item>
395 <widget class="QPushButton" name="buttonY">
396 <property name="text">
397 <string/>
398 </property>
399 </widget>
400 </item>
401 </layout>
402 </item>
403 </layout>
404 </widget>
405 </item>
406 <item row="5" column="0" colspan="2">
407 <widget class="QGroupBox" name="controller_color">
408 <property name="title">
409 <string>Controller Color</string>
410 </property>
411 <layout class="QGridLayout" name="gridLayout_10" columnstretch="0,0,0,0,0,0,0">
412 <item row="0" column="0">
413 <spacer name="horizontalSpacer_2">
414 <property name="orientation">
415 <enum>Qt::Horizontal</enum>
416 </property>
417 <property name="sizeHint" stdset="0">
418 <size>
419 <width>40</width>
420 <height>20</height>
421 </size>
422 </property>
423 </spacer>
424 </item>
425 <item row="0" column="1">
426 <widget class="QLabel" name="left_body_label">
427 <property name="text">
428 <string>Left Body</string>
429 </property>
430 </widget>
431 </item>
432 <item row="0" column="6">
433 <spacer name="horizontalSpacer_3">
434 <property name="orientation">
435 <enum>Qt::Horizontal</enum>
436 </property>
437 <property name="sizeHint" stdset="0">
438 <size>
439 <width>40</width>
440 <height>20</height>
441 </size>
442 </property>
443 </spacer>
444 </item>
445 <item row="1" column="1">
446 <widget class="QLabel" name="left_buttons_label">
447 <property name="minimumSize">
448 <size>
449 <width>90</width>
450 <height>0</height>
451 </size>
452 </property>
453 <property name="text">
454 <string>Left Buttons</string>
455 </property>
456 </widget>
457 </item>
458 <item row="1" column="5">
459 <widget class="QPushButton" name="right_buttons_button">
460 <property name="sizePolicy">
461 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
462 <horstretch>0</horstretch>
463 <verstretch>0</verstretch>
464 </sizepolicy>
465 </property>
466 <property name="minimumSize">
467 <size>
468 <width>32</width>
469 <height>0</height>
470 </size>
471 </property>
472 <property name="maximumSize">
473 <size>
474 <width>40</width>
475 <height>16777215</height>
476 </size>
477 </property>
478 <property name="text">
479 <string/>
480 </property>
481 </widget>
482 </item>
483 <item row="0" column="4">
484 <widget class="QLabel" name="right_body_label">
485 <property name="text">
486 <string>Right Body</string>
487 </property>
488 </widget>
489 </item>
490 <item row="1" column="4">
491 <widget class="QLabel" name="right_buttons_label">
492 <property name="minimumSize">
493 <size>
494 <width>90</width>
495 <height>0</height>
496 </size>
497 </property>
498 <property name="text">
499 <string>Right Buttons</string>
500 </property>
501 </widget>
502 </item>
503 <item row="1" column="2">
504 <widget class="QPushButton" name="left_buttons_button">
505 <property name="sizePolicy">
506 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
507 <horstretch>0</horstretch>
508 <verstretch>0</verstretch>
509 </sizepolicy>
510 </property>
511 <property name="minimumSize">
512 <size>
513 <width>32</width>
514 <height>0</height>
515 </size>
516 </property>
517 <property name="maximumSize">
518 <size>
519 <width>40</width>
520 <height>16777215</height>
521 </size>
522 </property>
523 <property name="text">
524 <string/>
525 </property>
526 </widget>
527 </item>
528 <item row="0" column="2">
529 <widget class="QPushButton" name="left_body_button">
530 <property name="sizePolicy">
531 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
532 <horstretch>0</horstretch>
533 <verstretch>0</verstretch>
534 </sizepolicy>
535 </property>
536 <property name="minimumSize">
537 <size>
538 <width>32</width>
539 <height>0</height>
540 </size>
541 </property>
542 <property name="maximumSize">
543 <size>
544 <width>40</width>
545 <height>16777215</height>
546 </size>
547 </property>
548 <property name="text">
549 <string/>
550 </property>
551 </widget>
552 </item>
553 <item row="0" column="5">
554 <widget class="QPushButton" name="right_body_button">
555 <property name="sizePolicy">
556 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
557 <horstretch>0</horstretch>
558 <verstretch>0</verstretch>
559 </sizepolicy>
560 </property>
561 <property name="minimumSize">
562 <size>
563 <width>32</width>
564 <height>0</height>
565 </size>
566 </property>
567 <property name="maximumSize">
568 <size>
569 <width>40</width>
570 <height>16777215</height>
571 </size>
572 </property>
573 <property name="text">
574 <string/>
575 </property>
576 </widget>
577 </item>
578 <item row="0" column="3">
579 <spacer name="horizontalSpacer_4">
580 <property name="orientation">
581 <enum>Qt::Horizontal</enum>
582 </property>
583 <property name="sizeType">
584 <enum>QSizePolicy::Fixed</enum>
585 </property>
586 <property name="sizeHint" stdset="0">
587 <size>
588 <width>20</width>
589 <height>20</height>
590 </size>
591 </property>
592 </spacer>
593 </item>
594 </layout>
595 </widget>
596 </item>
597 <item row="1" column="0">
598 <widget class="QGroupBox" name="LStick">
599 <property name="title">
600 <string>Left Stick</string>
601 </property>
602 <property name="flat">
603 <bool>false</bool>
604 </property>
605 <property name="checkable">
606 <bool>false</bool>
607 </property>
608 <layout class="QGridLayout" name="gridLayout_4">
609 <item row="1" column="1">
610 <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout">
611 <item>
612 <layout class="QHBoxLayout" name="buttonLStickUpHorizontalLayout">
613 <item>
614 <widget class="QLabel" name="labelLStickUp">
615 <property name="text">
616 <string>Up:</string>
617 </property>
618 </widget>
619 </item>
620 </layout>
621 </item>
622 <item>
623 <widget class="QPushButton" name="buttonLStickUp">
624 <property name="text">
625 <string/>
626 </property>
627 </widget>
628 </item>
629 </layout>
630 </item>
631 <item row="0" column="2">
632 <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
633 <item>
634 <layout class="QHBoxLayout" name="buttonLStickRightHorizontalLayout">
635 <item>
636 <widget class="QLabel" name="labelLStickRight">
637 <property name="text">
638 <string>Right:</string>
639 </property>
640 </widget>
641 </item>
642 </layout>
643 </item>
644 <item>
645 <widget class="QPushButton" name="buttonLStickRight">
646 <property name="text">
647 <string/>
648 </property>
649 </widget>
650 </item>
651 </layout>
652 </item>
653 <item row="4" column="1" colspan="2">
654 <widget class="QPushButton" name="buttonLStickAnalog">
655 <property name="text">
656 <string>Set Analog Stick</string>
657 </property>
658 </widget>
659 </item>
660 <item row="0" column="1">
661 <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
662 <item>
663 <layout class="QHBoxLayout" name="buttonLStickLeftHorizontalLayout_2">
664 <item>
665 <widget class="QLabel" name="labelLStickLeft">
666 <property name="text">
667 <string>Left:</string>
668 </property>
669 </widget>
670 </item>
671 </layout>
672 </item>
673 <item>
674 <widget class="QPushButton" name="buttonLStickLeft">
675 <property name="text">
676 <string/>
677 </property>
678 </widget>
679 </item>
680 </layout>
681 </item>
682 <item row="1" column="2">
683 <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
684 <item>
685 <layout class="QHBoxLayout" name="buttonLStickDownHorizontalLayout">
686 <item>
687 <widget class="QLabel" name="labelLStickDown">
688 <property name="text">
689 <string>Down:</string>
690 </property>
691 </widget>
692 </item>
693 </layout>
694 </item>
695 <item>
696 <widget class="QPushButton" name="buttonLStickDown">
697 <property name="text">
698 <string/>
699 </property>
700 </widget>
701 </item>
702 </layout>
703 </item>
704 <item row="3" column="2">
705 <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
706 <item>
707 <layout class="QHBoxLayout" name="buttonLStickModHorizontalLayout">
708 <item>
709 <widget class="QLabel" name="labelLStickMod">
710 <property name="text">
711 <string>Modifier:</string>
712 </property>
713 </widget>
714 </item>
715 </layout>
716 </item>
717 <item>
718 <widget class="QPushButton" name="buttonLStickMod">
719 <property name="text">
720 <string/>
721 </property>
722 </widget>
723 </item>
724 </layout>
725 </item>
726 <item row="3" column="1">
727 <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0">
728 <item>
729 <layout class="QHBoxLayout" name="buttonLStickPressedHorizontalLayout">
730 <item>
731 <widget class="QLabel" name="labelLStickPressed">
732 <property name="text">
733 <string>Pressed:</string>
734 </property>
735 </widget>
736 </item>
737 </layout>
738 </item>
739 <item>
740 <widget class="QPushButton" name="buttonLStick">
741 <property name="text">
742 <string/>
743 </property>
744 </widget>
745 </item>
746 </layout>
747 </item>
748 </layout>
749 </widget>
750 </item>
751 <item row="3" column="0">
752 <widget class="QGroupBox" name="shoulderButtons">
753 <property name="title">
754 <string>Shoulder Buttons</string>
755 </property>
756 <property name="flat">
757 <bool>false</bool>
758 </property>
759 <property name="checkable">
760 <bool>false</bool>
761 </property>
762 <layout class="QGridLayout" name="gridLayout_3">
763 <item row="3" column="0">
764 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
765 <item>
766 <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">
767 <item>
768 <widget class="QLabel" name="labelSL">
769 <property name="text">
770 <string>SL:</string>
771 </property>
772 </widget>
773 </item>
774 </layout>
775 </item>
776 <item>
777 <widget class="QPushButton" name="buttonSL">
778 <property name="text">
779 <string/>
780 </property>
781 </widget>
782 </item>
783 </layout>
784 </item>
785 <item row="2" column="1">
786 <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
787 <item>
788 <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
789 <item>
790 <widget class="QLabel" name="labelZR">
791 <property name="text">
792 <string>ZR:</string>
793 </property>
794 </widget>
795 </item>
796 </layout>
797 </item>
798 <item>
799 <widget class="QPushButton" name="buttonZR">
800 <property name="text">
801 <string/>
802 </property>
803 </widget>
804 </item>
805 </layout>
806 </item>
807 <item row="3" column="1">
808 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
809 <item>
810 <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">
811 <item>
812 <widget class="QLabel" name="labelSR">
813 <property name="text">
814 <string>SR:</string>
815 </property>
816 </widget>
817 </item>
818 </layout>
819 </item>
820 <item>
821 <widget class="QPushButton" name="buttonSR">
822 <property name="text">
823 <string/>
824 </property>
825 </widget>
826 </item>
827 </layout>
828 </item>
829 <item row="0" column="1">
830 <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
831 <item>
832 <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
833 <item>
834 <widget class="QLabel" name="labelZL">
835 <property name="text">
836 <string>ZL:</string>
837 </property>
838 </widget>
839 </item>
840 </layout>
841 </item>
842 <item>
843 <widget class="QPushButton" name="buttonZL">
844 <property name="text">
845 <string/>
846 </property>
847 </widget>
848 </item>
849 </layout>
850 </item>
851 <item row="0" column="0">
852 <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
853 <item>
854 <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout">
855 <item>
856 <widget class="QLabel" name="labelL">
857 <property name="text">
858 <string>L:</string>
859 </property>
860 </widget>
861 </item>
862 </layout>
863 </item>
864 <item>
865 <widget class="QPushButton" name="buttonL">
866 <property name="text">
867 <string/>
868 </property>
869 </widget>
870 </item>
871 </layout>
872 </item>
873 <item row="2" column="0">
874 <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
875 <item>
876 <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout">
877 <item>
878 <widget class="QLabel" name="labelR">
879 <property name="text">
880 <string>R:</string>
881 </property>
882 </widget>
883 </item>
884 </layout>
885 </item>
886 <item>
887 <widget class="QPushButton" name="buttonR">
888 <property name="text">
889 <string/>
890 </property>
891 </widget>
892 </item>
893 </layout>
894 </item>
895 </layout>
896 </widget>
897 </item>
898 <item row="3" column="1">
899 <widget class="QGroupBox" name="misc">
900 <property name="title">
901 <string>Misc.</string>
902 </property>
903 <property name="flat">
904 <bool>false</bool>
905 </property>
906 <property name="checkable">
907 <bool>false</bool>
908 </property>
909 <layout class="QGridLayout" name="gridLayout_6">
910 <item row="1" column="0">
911 <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout">
912 <item>
913 <layout class="QHBoxLayout" name="buttonMiscMinusHorizontalLayout">
914 <item>
915 <widget class="QLabel" name="labelMinus">
916 <property name="text">
917 <string>Minus:</string>
918 </property>
919 </widget>
920 </item>
921 </layout>
922 </item>
923 <item>
924 <widget class="QPushButton" name="buttonMinus">
925 <property name="text">
926 <string/>
927 </property>
928 </widget>
929 </item>
930 </layout>
931 </item>
932 <item row="3" column="1">
933 <spacer name="verticalSpacer_2">
934 <property name="orientation">
935 <enum>Qt::Vertical</enum>
936 </property>
937 <property name="sizeHint" stdset="0">
938 <size>
939 <width>20</width>
940 <height>40</height>
941 </size>
942 </property>
943 </spacer>
944 </item>
945 <item row="0" column="0">
946 <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
947 <item>
948 <layout class="QHBoxLayout" name="buttonMiscPlusHorizontalLayout">
949 <item>
950 <widget class="QLabel" name="labelPlus">
951 <property name="text">
952 <string>Plus:</string>
953 </property>
954 </widget>
955 </item>
956 </layout>
957 </item>
958 <item>
959 <widget class="QPushButton" name="buttonPlus">
960 <property name="text">
961 <string/>
962 </property>
963 </widget>
964 </item>
965 </layout>
966 </item>
967 <item row="0" column="1">
968 <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
969 <item>
970 <layout class="QHBoxLayout" name="buttonMiscHomeHorizontalLayout">
971 <item>
972 <widget class="QLabel" name="labelHome">
973 <property name="text">
974 <string>Home:</string>
975 </property>
976 </widget>
977 </item>
978 </layout>
979 </item>
980 <item>
981 <widget class="QPushButton" name="buttonHome">
982 <property name="text">
983 <string/>
984 </property>
985 </widget>
986 </item>
987 </layout>
988 </item>
989 <item row="1" column="1">
990 <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
991 <item>
992 <layout class="QHBoxLayout" name="buttonMiscScrCapHorizontalLayout">
993 <item>
994 <widget class="QLabel" name="labelScreenshot">
995 <property name="text">
996 <string>Screen Capture:</string>
997 </property>
998 <property name="wordWrap">
999 <bool>false</bool>
1000 </property>
1001 </widget>
1002 </item>
1003 </layout>
1004 </item>
1005 <item>
1006 <widget class="QPushButton" name="buttonScreenshot">
1007 <property name="sizePolicy">
1008 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1009 <horstretch>0</horstretch>
1010 <verstretch>0</verstretch>
1011 </sizepolicy>
1012 </property>
1013 <property name="maximumSize">
1014 <size>
1015 <width>80</width>
1016 <height>16777215</height>
1017 </size>
1018 </property>
1019 <property name="text">
1020 <string/>
1021 </property>
1022 </widget>
1023 </item>
1024 </layout>
1025 </item>
1026 </layout>
1027 </widget>
1028 </item>
1029 </layout>
1030 </item>
1031 <item>
1032 <spacer name="verticalSpacer">
1033 <property name="orientation">
1034 <enum>Qt::Vertical</enum>
1035 </property>
1036 <property name="sizeHint" stdset="0">
1037 <size>
1038 <width>20</width>
1039 <height>40</height>
1040 </size>
1041 </property>
1042 </spacer>
1043 </item>
1044 <item>
1045 <layout class="QHBoxLayout" name="horizontalLayout"/>
1046 </item>
1047 <item>
1048 <layout class="QHBoxLayout" name="horizontalLayout_2">
1049 <item>
1050 <widget class="QPushButton" name="buttonClearAll">
1051 <property name="sizePolicy">
1052 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1053 <horstretch>0</horstretch>
1054 <verstretch>0</verstretch>
1055 </sizepolicy>
1056 </property>
1057 <property name="sizeIncrement">
1058 <size>
1059 <width>0</width>
1060 <height>0</height>
1061 </size>
1062 </property>
1063 <property name="baseSize">
1064 <size>
1065 <width>0</width>
1066 <height>0</height>
1067 </size>
1068 </property>
1069 <property name="layoutDirection">
1070 <enum>Qt::LeftToRight</enum>
1071 </property>
1072 <property name="text">
1073 <string>Clear All</string>
1074 </property>
1075 </widget>
1076 </item>
1077 <item>
1078 <widget class="QPushButton" name="buttonRestoreDefaults">
1079 <property name="sizePolicy">
1080 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
1081 <horstretch>0</horstretch>
1082 <verstretch>0</verstretch>
1083 </sizepolicy>
1084 </property>
1085 <property name="sizeIncrement">
1086 <size>
1087 <width>0</width>
1088 <height>0</height>
1089 </size>
1090 </property>
1091 <property name="baseSize">
1092 <size>
1093 <width>0</width>
1094 <height>0</height>
1095 </size>
1096 </property>
1097 <property name="layoutDirection">
1098 <enum>Qt::LeftToRight</enum>
1099 </property>
1100 <property name="text">
1101 <string>Restore Defaults</string>
1102 </property>
1103 </widget>
1104 </item>
1105 <item>
1106 <spacer name="horizontalSpacer">
1107 <property name="orientation">
1108 <enum>Qt::Horizontal</enum>
1109 </property>
1110 <property name="sizeHint" stdset="0">
1111 <size>
1112 <width>40</width>
1113 <height>20</height>
1114 </size>
1115 </property>
1116 </spacer>
1117 </item>
1118 <item>
1119 <widget class="QDialogButtonBox" name="buttonBox">
1120 <property name="standardButtons">
1121 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
1122 </property>
1123 </widget>
1124 </item>
1125 </layout>
1126 </item>
1127 </layout>
1128 </widget>
1129 <resources/>
1130 <connections>
1131 <connection>
1132 <sender>buttonBox</sender>
1133 <signal>accepted()</signal>
1134 <receiver>ConfigureInputPlayer</receiver>
1135 <slot>accept()</slot>
1136 <hints>
1137 <hint type="sourcelabel">
1138 <x>371</x>
1139 <y>730</y>
1140 </hint>
1141 <hint type="destinationlabel">
1142 <x>229</x>
1143 <y>375</y>
1144 </hint>
1145 </hints>
1146 </connection>
1147 <connection>
1148 <sender>buttonBox</sender>
1149 <signal>rejected()</signal>
1150 <receiver>ConfigureInputPlayer</receiver>
1151 <slot>reject()</slot>
1152 <hints>
1153 <hint type="sourcelabel">
1154 <x>371</x>
1155 <y>730</y>
1156 </hint>
1157 <hint type="destinationlabel">
1158 <x>229</x>
1159 <y>375</y>
1160 </hint>
1161 </hints>
1162 </connection>
1163 </connections>
1164</ui>
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
new file mode 100644
index 000000000..dab58fbaa
--- /dev/null
+++ b/src/yuzu/configuration/configure_mouse_advanced.cpp
@@ -0,0 +1,213 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <memory>
7#include <utility>
8#include <QKeyEvent>
9#include <QMenu>
10#include <QMessageBox>
11#include <QTimer>
12#include "common/assert.h"
13#include "common/param_package.h"
14#include "input_common/main.h"
15#include "ui_configure_mouse_advanced.h"
16#include "yuzu/configuration/config.h"
17#include "yuzu/configuration/configure_mouse_advanced.h"
18
19static QString GetKeyName(int key_code) {
20 switch (key_code) {
21 case Qt::Key_Shift:
22 return QObject::tr("Shift");
23 case Qt::Key_Control:
24 return QObject::tr("Ctrl");
25 case Qt::Key_Alt:
26 return QObject::tr("Alt");
27 case Qt::Key_Meta:
28 return "";
29 default:
30 return QKeySequence(key_code).toString();
31 }
32}
33
34static QString ButtonToText(const Common::ParamPackage& param) {
35 if (!param.Has("engine")) {
36 return QObject::tr("[not set]");
37 } else if (param.Get("engine", "") == "keyboard") {
38 return GetKeyName(param.Get("code", 0));
39 } else if (param.Get("engine", "") == "sdl") {
40 if (param.Has("hat")) {
41 return QString(QObject::tr("Hat %1 %2"))
42 .arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str());
43 }
44 if (param.Has("axis")) {
45 return QString(QObject::tr("Axis %1%2"))
46 .arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str());
47 }
48 if (param.Has("button")) {
49 return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str());
50 }
51 return QString();
52 } else {
53 return QObject::tr("[unknown]");
54 }
55}
56
57ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
58 : QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()),
59 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
60 ui->setupUi(this);
61 setFocusPolicy(Qt::ClickFocus);
62
63 button_map = {
64 ui->left_button, ui->right_button, ui->middle_button, ui->forward_button, ui->back_button,
65 };
66
67 for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
68 if (!button_map[button_id])
69 continue;
70 button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
71 connect(button_map[button_id], &QPushButton::released, [=]() {
72 handleClick(
73 button_map[button_id],
74 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
75 InputCommon::Polling::DeviceType::Button);
76 });
77 connect(button_map[button_id], &QPushButton::customContextMenuRequested,
78 [=](const QPoint& menu_location) {
79 QMenu context_menu;
80 context_menu.addAction(tr("Clear"), [&] {
81 buttons_param[button_id].Clear();
82 button_map[button_id]->setText(tr("[not set]"));
83 });
84 context_menu.addAction(tr("Restore Default"), [&] {
85 buttons_param[button_id] =
86 Common::ParamPackage{InputCommon::GenerateKeyboardParam(
87 Config::default_mouse_buttons[button_id])};
88 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
89 });
90 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
91 });
92 }
93
94 connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
95 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
96
97 timeout_timer->setSingleShot(true);
98 connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); });
99
100 connect(poll_timer.get(), &QTimer::timeout, [this]() {
101 Common::ParamPackage params;
102 for (auto& poller : device_pollers) {
103 params = poller->GetNextInput();
104 if (params.Has("engine")) {
105 setPollingResult(params, false);
106 return;
107 }
108 }
109 });
110
111 loadConfiguration();
112 resize(0, 0);
113}
114
115ConfigureMouseAdvanced::~ConfigureMouseAdvanced() = default;
116
117void ConfigureMouseAdvanced::applyConfiguration() {
118 std::transform(buttons_param.begin(), buttons_param.end(),
119 Settings::values.mouse_buttons.begin(),
120 [](const Common::ParamPackage& param) { return param.Serialize(); });
121}
122
123void ConfigureMouseAdvanced::loadConfiguration() {
124 std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
125 buttons_param.begin(),
126 [](const std::string& str) { return Common::ParamPackage(str); });
127 updateButtonLabels();
128}
129
130void ConfigureMouseAdvanced::restoreDefaults() {
131 for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) {
132 buttons_param[button_id] = Common::ParamPackage{
133 InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])};
134 }
135
136 updateButtonLabels();
137}
138
139void ConfigureMouseAdvanced::ClearAll() {
140 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
141 if (button_map[i] && button_map[i]->isEnabled())
142 buttons_param[i].Clear();
143 }
144
145 updateButtonLabels();
146}
147
148void ConfigureMouseAdvanced::updateButtonLabels() {
149 for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) {
150 button_map[button]->setText(ButtonToText(buttons_param[button]));
151 }
152}
153
154void ConfigureMouseAdvanced::handleClick(
155 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
156 InputCommon::Polling::DeviceType type) {
157 button->setText(tr("[press key]"));
158 button->setFocus();
159
160 const auto iter = std::find(button_map.begin(), button_map.end(), button);
161 ASSERT(iter != button_map.end());
162 const auto index = std::distance(button_map.begin(), iter);
163 ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
164
165 input_setter = new_input_setter;
166
167 device_pollers = InputCommon::Polling::GetPollers(type);
168
169 // Keyboard keys can only be used as button devices
170 want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
171
172 for (auto& poller : device_pollers) {
173 poller->Start();
174 }
175
176 grabKeyboard();
177 grabMouse();
178 timeout_timer->start(5000); // Cancel after 5 seconds
179 poll_timer->start(200); // Check for new inputs every 200ms
180}
181
182void ConfigureMouseAdvanced::setPollingResult(const Common::ParamPackage& params, bool abort) {
183 releaseKeyboard();
184 releaseMouse();
185 timeout_timer->stop();
186 poll_timer->stop();
187 for (auto& poller : device_pollers) {
188 poller->Stop();
189 }
190
191 if (!abort) {
192 (*input_setter)(params);
193 }
194
195 updateButtonLabels();
196 input_setter = std::nullopt;
197}
198
199void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
200 if (!input_setter || !event)
201 return;
202
203 if (event->key() != Qt::Key_Escape) {
204 if (want_keyboard_keys) {
205 setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
206 false);
207 } else {
208 // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
209 return;
210 }
211 }
212 setPollingResult({}, true);
213}
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h
new file mode 100644
index 000000000..218df2bda
--- /dev/null
+++ b/src/yuzu/configuration/configure_mouse_advanced.h
@@ -0,0 +1,68 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <optional>
9#include <QDialog>
10#include <QWidget>
11#include "core/settings.h"
12
13class QCheckBox;
14class QPushButton;
15class QTimer;
16
17namespace Ui {
18class ConfigureMouseAdvanced;
19}
20
21class ConfigureMouseAdvanced : public QDialog {
22 Q_OBJECT
23
24public:
25 explicit ConfigureMouseAdvanced(QWidget* parent);
26 ~ConfigureMouseAdvanced() override;
27
28 void applyConfiguration();
29
30private:
31 std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
32
33 /// This will be the the setting function when an input is awaiting configuration.
34 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
35
36 std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map;
37 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param;
38
39 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
40
41 std::unique_ptr<QTimer> timeout_timer;
42 std::unique_ptr<QTimer> poll_timer;
43
44 /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
45 /// keyboard events are ignored.
46 bool want_keyboard_keys = false;
47
48 /// Load configuration settings.
49 void loadConfiguration();
50 /// Restore all buttons to their default values.
51 void restoreDefaults();
52 /// Clear all input configuration
53 void ClearAll();
54
55 /// Update UI to reflect current configuration.
56 void updateButtonLabels();
57
58 /// Called when the button was pressed.
59 void handleClick(QPushButton* button,
60 std::function<void(const Common::ParamPackage&)> new_input_setter,
61 InputCommon::Polling::DeviceType type);
62
63 /// Finish polling and configure input using the input_setter
64 void setPollingResult(const Common::ParamPackage& params, bool abort);
65
66 /// Handle key press events.
67 void keyPressEvent(QKeyEvent* event) override;
68};
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui
new file mode 100644
index 000000000..08245ecf0
--- /dev/null
+++ b/src/yuzu/configuration/configure_mouse_advanced.ui
@@ -0,0 +1,261 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureMouseAdvanced</class>
4 <widget class="QDialog" name="ConfigureMouseAdvanced">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>250</width>
10 <height>261</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Mouse</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QGroupBox" name="gridGroupBox">
19 <property name="title">
20 <string>Mouse Buttons</string>
21 </property>
22 <layout class="QGridLayout" name="gridLayout">
23 <item row="0" column="4">
24 <spacer name="horizontalSpacer_2">
25 <property name="orientation">
26 <enum>Qt::Horizontal</enum>
27 </property>
28 <property name="sizeType">
29 <enum>QSizePolicy::Fixed</enum>
30 </property>
31 <property name="sizeHint" stdset="0">
32 <size>
33 <width>20</width>
34 <height>20</height>
35 </size>
36 </property>
37 </spacer>
38 </item>
39 <item row="0" column="3">
40 <layout class="QVBoxLayout" name="verticalLayout_4">
41 <item>
42 <layout class="QHBoxLayout" name="horizontalLayout_3">
43 <item>
44 <widget class="QLabel" name="label_3">
45 <property name="text">
46 <string>Right:</string>
47 </property>
48 </widget>
49 </item>
50 </layout>
51 </item>
52 <item>
53 <widget class="QPushButton" name="right_button">
54 <property name="minimumSize">
55 <size>
56 <width>75</width>
57 <height>0</height>
58 </size>
59 </property>
60 <property name="text">
61 <string/>
62 </property>
63 </widget>
64 </item>
65 </layout>
66 </item>
67 <item row="0" column="0">
68 <spacer name="horizontalSpacer">
69 <property name="orientation">
70 <enum>Qt::Horizontal</enum>
71 </property>
72 <property name="sizeType">
73 <enum>QSizePolicy::Fixed</enum>
74 </property>
75 <property name="sizeHint" stdset="0">
76 <size>
77 <width>20</width>
78 <height>20</height>
79 </size>
80 </property>
81 </spacer>
82 </item>
83 <item row="2" column="1">
84 <layout class="QVBoxLayout" name="verticalLayout_3">
85 <item>
86 <layout class="QHBoxLayout" name="horizontalLayout_2">
87 <item>
88 <widget class="QLabel" name="label_2">
89 <property name="text">
90 <string>Middle:</string>
91 </property>
92 </widget>
93 </item>
94 </layout>
95 </item>
96 <item>
97 <widget class="QPushButton" name="middle_button">
98 <property name="text">
99 <string/>
100 </property>
101 </widget>
102 </item>
103 </layout>
104 </item>
105 <item row="3" column="1">
106 <layout class="QVBoxLayout" name="verticalLayout_5">
107 <item>
108 <layout class="QHBoxLayout" name="horizontalLayout_4">
109 <item>
110 <widget class="QLabel" name="label_4">
111 <property name="minimumSize">
112 <size>
113 <width>54</width>
114 <height>0</height>
115 </size>
116 </property>
117 <property name="text">
118 <string>Back:</string>
119 </property>
120 </widget>
121 </item>
122 </layout>
123 </item>
124 <item>
125 <widget class="QPushButton" name="back_button">
126 <property name="text">
127 <string/>
128 </property>
129 </widget>
130 </item>
131 </layout>
132 </item>
133 <item row="0" column="1">
134 <layout class="QVBoxLayout" name="verticalLayout_2">
135 <item>
136 <layout class="QHBoxLayout" name="horizontalLayout">
137 <item>
138 <widget class="QLabel" name="label">
139 <property name="text">
140 <string>Left:</string>
141 </property>
142 </widget>
143 </item>
144 </layout>
145 </item>
146 <item>
147 <widget class="QPushButton" name="left_button">
148 <property name="minimumSize">
149 <size>
150 <width>75</width>
151 <height>0</height>
152 </size>
153 </property>
154 <property name="text">
155 <string/>
156 </property>
157 </widget>
158 </item>
159 </layout>
160 </item>
161 <item row="3" column="3">
162 <layout class="QVBoxLayout" name="verticalLayout_6">
163 <item>
164 <layout class="QHBoxLayout" name="horizontalLayout_5">
165 <item>
166 <widget class="QLabel" name="label_5">
167 <property name="text">
168 <string>Forward:</string>
169 </property>
170 </widget>
171 </item>
172 </layout>
173 </item>
174 <item>
175 <widget class="QPushButton" name="forward_button">
176 <property name="text">
177 <string/>
178 </property>
179 </widget>
180 </item>
181 </layout>
182 </item>
183 </layout>
184 </widget>
185 </item>
186 <item>
187 <layout class="QHBoxLayout" name="horizontalLayout_6">
188 <item>
189 <widget class="QPushButton" name="buttonClearAll">
190 <property name="text">
191 <string>Clear All</string>
192 </property>
193 </widget>
194 </item>
195 <item>
196 <widget class="QPushButton" name="buttonRestoreDefaults">
197 <property name="text">
198 <string>Restore Defaults</string>
199 </property>
200 </widget>
201 </item>
202 <item>
203 <spacer name="horizontalSpacer_3">
204 <property name="orientation">
205 <enum>Qt::Horizontal</enum>
206 </property>
207 <property name="sizeHint" stdset="0">
208 <size>
209 <width>40</width>
210 <height>20</height>
211 </size>
212 </property>
213 </spacer>
214 </item>
215 </layout>
216 </item>
217 <item>
218 <widget class="QDialogButtonBox" name="buttonBox">
219 <property name="standardButtons">
220 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
221 </property>
222 </widget>
223 </item>
224 </layout>
225 </widget>
226 <resources/>
227 <connections>
228 <connection>
229 <sender>buttonBox</sender>
230 <signal>accepted()</signal>
231 <receiver>ConfigureMouseAdvanced</receiver>
232 <slot>accept()</slot>
233 <hints>
234 <hint type="sourcelabel">
235 <x>124</x>
236 <y>266</y>
237 </hint>
238 <hint type="destinationlabel">
239 <x>124</x>
240 <y>143</y>
241 </hint>
242 </hints>
243 </connection>
244 <connection>
245 <sender>buttonBox</sender>
246 <signal>rejected()</signal>
247 <receiver>ConfigureMouseAdvanced</receiver>
248 <slot>reject()</slot>
249 <hints>
250 <hint type="sourcelabel">
251 <x>124</x>
252 <y>266</y>
253 </hint>
254 <hint type="destinationlabel">
255 <x>124</x>
256 <y>143</y>
257 </hint>
258 </hints>
259 </connection>
260 </connections>
261</ui>
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.cpp b/src/yuzu/configuration/configure_touchscreen_advanced.cpp
new file mode 100644
index 000000000..9c1561e9d
--- /dev/null
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.cpp
@@ -0,0 +1,42 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include "ui_configure_touchscreen_advanced.h"
7#include "yuzu/configuration/config.h"
8#include "yuzu/configuration/configure_touchscreen_advanced.h"
9
10ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent)
11 : QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchscreenAdvanced>()) {
12 ui->setupUi(this);
13
14 connect(ui->restore_defaults_button, &QPushButton::pressed, this,
15 &ConfigureTouchscreenAdvanced::restoreDefaults);
16
17 loadConfiguration();
18 resize(0, 0);
19}
20
21ConfigureTouchscreenAdvanced::~ConfigureTouchscreenAdvanced() = default;
22
23void ConfigureTouchscreenAdvanced::applyConfiguration() {
24 Settings::values.touchscreen.finger = ui->finger_box->value();
25 Settings::values.touchscreen.diameter_x = ui->diameter_x_box->value();
26 Settings::values.touchscreen.diameter_y = ui->diameter_y_box->value();
27 Settings::values.touchscreen.rotation_angle = ui->angle_box->value();
28}
29
30void ConfigureTouchscreenAdvanced::loadConfiguration() {
31 ui->finger_box->setValue(Settings::values.touchscreen.finger);
32 ui->diameter_x_box->setValue(Settings::values.touchscreen.diameter_x);
33 ui->diameter_y_box->setValue(Settings::values.touchscreen.diameter_y);
34 ui->angle_box->setValue(Settings::values.touchscreen.rotation_angle);
35}
36
37void ConfigureTouchscreenAdvanced::restoreDefaults() {
38 ui->finger_box->setValue(0);
39 ui->diameter_x_box->setValue(15);
40 ui->diameter_y_box->setValue(15);
41 ui->angle_box->setValue(0);
42}
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.h b/src/yuzu/configuration/configure_touchscreen_advanced.h
new file mode 100644
index 000000000..41cd255fb
--- /dev/null
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.h
@@ -0,0 +1,32 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QDialog>
9#include <QWidget>
10#include "yuzu/configuration/config.h"
11
12namespace Ui {
13class ConfigureTouchscreenAdvanced;
14}
15
16class ConfigureTouchscreenAdvanced : public QDialog {
17 Q_OBJECT
18
19public:
20 explicit ConfigureTouchscreenAdvanced(QWidget* parent);
21 ~ConfigureTouchscreenAdvanced() override;
22
23 void applyConfiguration();
24
25private:
26 /// Load configuration settings.
27 void loadConfiguration();
28 /// Restore all buttons to their default values.
29 void restoreDefaults();
30
31 std::unique_ptr<Ui::ConfigureTouchscreenAdvanced> ui;
32};
diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.ui b/src/yuzu/configuration/configure_touchscreen_advanced.ui
new file mode 100644
index 000000000..1171c2dd1
--- /dev/null
+++ b/src/yuzu/configuration/configure_touchscreen_advanced.ui
@@ -0,0 +1,199 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureTouchscreenAdvanced</class>
4 <widget class="QDialog" name="ConfigureTouchscreenAdvanced">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>298</width>
10 <height>339</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Touchscreen</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QLabel" name="label_2">
19 <property name="minimumSize">
20 <size>
21 <width>280</width>
22 <height>0</height>
23 </size>
24 </property>
25 <property name="text">
26 <string>Warning: The settings in this page affect the inner workings of yuzu's emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</string>
27 </property>
28 <property name="wordWrap">
29 <bool>true</bool>
30 </property>
31 </widget>
32 </item>
33 <item>
34 <spacer name="verticalSpacer_2">
35 <property name="orientation">
36 <enum>Qt::Vertical</enum>
37 </property>
38 <property name="sizeType">
39 <enum>QSizePolicy::Fixed</enum>
40 </property>
41 <property name="sizeHint" stdset="0">
42 <size>
43 <width>20</width>
44 <height>20</height>
45 </size>
46 </property>
47 </spacer>
48 </item>
49 <item>
50 <widget class="QGroupBox" name="gridGroupBox">
51 <property name="title">
52 <string>Touch Parameters</string>
53 </property>
54 <layout class="QGridLayout" name="gridLayout">
55 <item row="0" column="0">
56 <spacer name="horizontalSpacer">
57 <property name="orientation">
58 <enum>Qt::Horizontal</enum>
59 </property>
60 <property name="sizeHint" stdset="0">
61 <size>
62 <width>40</width>
63 <height>20</height>
64 </size>
65 </property>
66 </spacer>
67 </item>
68 <item row="2" column="1">
69 <widget class="QLabel" name="label_4">
70 <property name="text">
71 <string>Touch Diameter Y</string>
72 </property>
73 </widget>
74 </item>
75 <item row="0" column="1">
76 <widget class="QLabel" name="label">
77 <property name="text">
78 <string>Finger</string>
79 </property>
80 </widget>
81 </item>
82 <item row="0" column="3">
83 <spacer name="horizontalSpacer_2">
84 <property name="orientation">
85 <enum>Qt::Horizontal</enum>
86 </property>
87 <property name="sizeHint" stdset="0">
88 <size>
89 <width>40</width>
90 <height>20</height>
91 </size>
92 </property>
93 </spacer>
94 </item>
95 <item row="1" column="1">
96 <widget class="QLabel" name="label_3">
97 <property name="text">
98 <string>Touch Diameter X</string>
99 </property>
100 </widget>
101 </item>
102 <item row="0" column="2">
103 <widget class="QSpinBox" name="finger_box">
104 <property name="minimumSize">
105 <size>
106 <width>80</width>
107 <height>0</height>
108 </size>
109 </property>
110 </widget>
111 </item>
112 <item row="3" column="1">
113 <widget class="QLabel" name="label_5">
114 <property name="text">
115 <string>Rotational Angle</string>
116 </property>
117 </widget>
118 </item>
119 <item row="1" column="2">
120 <widget class="QSpinBox" name="diameter_x_box"/>
121 </item>
122 <item row="2" column="2">
123 <widget class="QSpinBox" name="diameter_y_box"/>
124 </item>
125 <item row="3" column="2">
126 <widget class="QSpinBox" name="angle_box"/>
127 </item>
128 </layout>
129 </widget>
130 </item>
131 <item>
132 <spacer name="verticalSpacer">
133 <property name="orientation">
134 <enum>Qt::Vertical</enum>
135 </property>
136 <property name="sizeHint" stdset="0">
137 <size>
138 <width>20</width>
139 <height>40</height>
140 </size>
141 </property>
142 </spacer>
143 </item>
144 <item>
145 <layout class="QHBoxLayout" name="horizontalLayout">
146 <item>
147 <widget class="QPushButton" name="restore_defaults_button">
148 <property name="text">
149 <string>Restore Defaults</string>
150 </property>
151 </widget>
152 </item>
153 <item>
154 <widget class="QDialogButtonBox" name="buttonBox">
155 <property name="standardButtons">
156 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
157 </property>
158 </widget>
159 </item>
160 </layout>
161 </item>
162 </layout>
163 </widget>
164 <resources/>
165 <connections>
166 <connection>
167 <sender>buttonBox</sender>
168 <signal>accepted()</signal>
169 <receiver>ConfigureTouchscreenAdvanced</receiver>
170 <slot>accept()</slot>
171 <hints>
172 <hint type="sourcelabel">
173 <x>140</x>
174 <y>318</y>
175 </hint>
176 <hint type="destinationlabel">
177 <x>140</x>
178 <y>169</y>
179 </hint>
180 </hints>
181 </connection>
182 <connection>
183 <sender>buttonBox</sender>
184 <signal>rejected()</signal>
185 <receiver>ConfigureTouchscreenAdvanced</receiver>
186 <slot>reject()</slot>
187 <hints>
188 <hint type="sourcelabel">
189 <x>140</x>
190 <y>318</y>
191 </hint>
192 <hint type="destinationlabel">
193 <x>140</x>
194 <y>169</y>
195 </hint>
196 </hints>
197 </connection>
198 </connections>
199</ui>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 96f1ef636..c66353a65 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -65,54 +65,246 @@ static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs>
65 }, 65 },
66}}; 66}};
67 67
68static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> default_mouse_buttons = {
69 SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_APOSTROPHE,
70 SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS,
71};
72
73static const std::array<int, 0x8A> keyboard_keys = {
74 0,
75 0,
76 0,
77 0,
78 SDL_SCANCODE_A,
79 SDL_SCANCODE_B,
80 SDL_SCANCODE_C,
81 SDL_SCANCODE_D,
82 SDL_SCANCODE_E,
83 SDL_SCANCODE_F,
84 SDL_SCANCODE_G,
85 SDL_SCANCODE_H,
86 SDL_SCANCODE_I,
87 SDL_SCANCODE_J,
88 SDL_SCANCODE_K,
89 SDL_SCANCODE_L,
90 SDL_SCANCODE_M,
91 SDL_SCANCODE_N,
92 SDL_SCANCODE_O,
93 SDL_SCANCODE_P,
94 SDL_SCANCODE_Q,
95 SDL_SCANCODE_R,
96 SDL_SCANCODE_S,
97 SDL_SCANCODE_T,
98 SDL_SCANCODE_U,
99 SDL_SCANCODE_V,
100 SDL_SCANCODE_W,
101 SDL_SCANCODE_X,
102 SDL_SCANCODE_Y,
103 SDL_SCANCODE_Z,
104 SDL_SCANCODE_1,
105 SDL_SCANCODE_2,
106 SDL_SCANCODE_3,
107 SDL_SCANCODE_4,
108 SDL_SCANCODE_5,
109 SDL_SCANCODE_6,
110 SDL_SCANCODE_7,
111 SDL_SCANCODE_8,
112 SDL_SCANCODE_9,
113 SDL_SCANCODE_0,
114 SDL_SCANCODE_RETURN,
115 SDL_SCANCODE_ESCAPE,
116 SDL_SCANCODE_BACKSPACE,
117 SDL_SCANCODE_TAB,
118 SDL_SCANCODE_SPACE,
119 SDL_SCANCODE_MINUS,
120 SDL_SCANCODE_EQUALS,
121 SDL_SCANCODE_LEFTBRACKET,
122 SDL_SCANCODE_RIGHTBRACKET,
123 SDL_SCANCODE_BACKSLASH,
124 0,
125 SDL_SCANCODE_SEMICOLON,
126 SDL_SCANCODE_APOSTROPHE,
127 SDL_SCANCODE_GRAVE,
128 SDL_SCANCODE_COMMA,
129 SDL_SCANCODE_PERIOD,
130 SDL_SCANCODE_SLASH,
131 SDL_SCANCODE_CAPSLOCK,
132
133 SDL_SCANCODE_F1,
134 SDL_SCANCODE_F2,
135 SDL_SCANCODE_F3,
136 SDL_SCANCODE_F4,
137 SDL_SCANCODE_F5,
138 SDL_SCANCODE_F6,
139 SDL_SCANCODE_F7,
140 SDL_SCANCODE_F8,
141 SDL_SCANCODE_F9,
142 SDL_SCANCODE_F10,
143 SDL_SCANCODE_F11,
144 SDL_SCANCODE_F12,
145
146 0,
147 SDL_SCANCODE_SCROLLLOCK,
148 SDL_SCANCODE_PAUSE,
149 SDL_SCANCODE_INSERT,
150 SDL_SCANCODE_HOME,
151 SDL_SCANCODE_PAGEUP,
152 SDL_SCANCODE_DELETE,
153 SDL_SCANCODE_END,
154 SDL_SCANCODE_PAGEDOWN,
155 SDL_SCANCODE_RIGHT,
156 SDL_SCANCODE_LEFT,
157 SDL_SCANCODE_DOWN,
158 SDL_SCANCODE_UP,
159
160 SDL_SCANCODE_NUMLOCKCLEAR,
161 SDL_SCANCODE_KP_DIVIDE,
162 SDL_SCANCODE_KP_MULTIPLY,
163 SDL_SCANCODE_KP_MINUS,
164 SDL_SCANCODE_KP_PLUS,
165 SDL_SCANCODE_KP_ENTER,
166 SDL_SCANCODE_KP_1,
167 SDL_SCANCODE_KP_2,
168 SDL_SCANCODE_KP_3,
169 SDL_SCANCODE_KP_4,
170 SDL_SCANCODE_KP_5,
171 SDL_SCANCODE_KP_6,
172 SDL_SCANCODE_KP_7,
173 SDL_SCANCODE_KP_8,
174 SDL_SCANCODE_KP_9,
175 SDL_SCANCODE_KP_0,
176 SDL_SCANCODE_KP_PERIOD,
177
178 0,
179 0,
180 SDL_SCANCODE_POWER,
181 SDL_SCANCODE_KP_EQUALS,
182
183 SDL_SCANCODE_F13,
184 SDL_SCANCODE_F14,
185 SDL_SCANCODE_F15,
186 SDL_SCANCODE_F16,
187 SDL_SCANCODE_F17,
188 SDL_SCANCODE_F18,
189 SDL_SCANCODE_F19,
190 SDL_SCANCODE_F20,
191 SDL_SCANCODE_F21,
192 SDL_SCANCODE_F22,
193 SDL_SCANCODE_F23,
194 SDL_SCANCODE_F24,
195
196 0,
197 SDL_SCANCODE_HELP,
198 SDL_SCANCODE_MENU,
199 0,
200 0,
201 0,
202 0,
203 0,
204 0,
205 0,
206 0,
207 0,
208 0,
209 0,
210 0,
211 SDL_SCANCODE_KP_COMMA,
212 SDL_SCANCODE_KP_LEFTPAREN,
213 SDL_SCANCODE_KP_RIGHTPAREN,
214 0,
215 0,
216 0,
217 0,
218};
219
220static const std::array<int, 8> keyboard_mods{
221 SDL_SCANCODE_LCTRL, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_LALT, SDL_SCANCODE_LGUI,
222 SDL_SCANCODE_RCTRL, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI,
223};
224
68void Config::ReadValues() { 225void Config::ReadValues() {
69 // Controls 226 // Controls
227 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
228 const auto group = fmt::format("ControlsP{}", p);
229 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
230 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
231 Settings::values.players[p].buttons[i] =
232 sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
233 if (Settings::values.players[p].buttons[i].empty())
234 Settings::values.players[p].buttons[i] = default_param;
235 }
236
237 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
238 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
239 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
240 default_analogs[i][3], default_analogs[i][4], 0.5f);
241 Settings::values.players[p].analogs[i] =
242 sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
243 if (Settings::values.players[p].analogs[i].empty())
244 Settings::values.players[p].analogs[i] = default_param;
245 }
246 }
247
248 Settings::values.mouse_enabled =
249 sdl2_config->GetBoolean("ControlsGeneral", "mouse_enabled", false);
250 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
251 std::string default_param = InputCommon::GenerateKeyboardParam(default_mouse_buttons[i]);
252 Settings::values.mouse_buttons[i] = sdl2_config->Get(
253 "ControlsGeneral", std::string("mouse_") + Settings::NativeMouseButton::mapping[i],
254 default_param);
255 if (Settings::values.mouse_buttons[i].empty())
256 Settings::values.mouse_buttons[i] = default_param;
257 }
258
259 Settings::values.motion_device = sdl2_config->Get(
260 "ControlsGeneral", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
261
262 Settings::values.keyboard_enabled =
263 sdl2_config->GetBoolean("ControlsGeneral", "keyboard_enabled", false);
264
265 Settings::values.debug_pad_enabled =
266 sdl2_config->GetBoolean("ControlsGeneral", "debug_pad_enabled", false);
70 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 267 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
71 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 268 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
72 Settings::values.buttons[i] = 269 Settings::values.debug_pad_buttons[i] = sdl2_config->Get(
73 sdl2_config->Get("Controls", Settings::NativeButton::mapping[i], default_param); 270 "ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i],
74 if (Settings::values.buttons[i].empty()) 271 default_param);
75 Settings::values.buttons[i] = default_param; 272 if (Settings::values.debug_pad_buttons[i].empty())
273 Settings::values.debug_pad_buttons[i] = default_param;
76 } 274 }
77 275
78 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 276 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
79 std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 277 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
80 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 278 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
81 default_analogs[i][3], default_analogs[i][4], 0.5f); 279 default_analogs[i][3], default_analogs[i][4], 0.5f);
82 Settings::values.analogs[i] = 280 Settings::values.debug_pad_analogs[i] = sdl2_config->Get(
83 sdl2_config->Get("Controls", Settings::NativeAnalog::mapping[i], default_param); 281 "ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i],
84 if (Settings::values.analogs[i].empty()) 282 default_param);
85 Settings::values.analogs[i] = default_param; 283 if (Settings::values.debug_pad_analogs[i].empty())
284 Settings::values.debug_pad_analogs[i] = default_param;
86 } 285 }
87 286
88 Settings::values.motion_device = sdl2_config->Get( 287 Settings::values.touchscreen.enabled =
89 "Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01"); 288 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
90 Settings::values.touch_device = 289 Settings::values.touchscreen.device =
91 sdl2_config->Get("Controls", "touch_device", "engine:emu_window"); 290 sdl2_config->Get("ControlsGeneral", "touch_device", "engine:emu_window");
92 291 Settings::values.touchscreen.finger =
93 // Core 292 sdl2_config->GetInteger("ControlsGeneral", "touch_finger", 0);
94 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); 293 Settings::values.touchscreen.rotation_angle =
95 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); 294 sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0);
96 295 Settings::values.touchscreen.diameter_x =
97 // Renderer 296 sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
98 Settings::values.resolution_factor = 297 Settings::values.touchscreen.diameter_y =
99 (float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0); 298 sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
100 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
101 Settings::values.frame_limit =
102 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
103 Settings::values.use_accurate_gpu_emulation =
104 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
105
106 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
107 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
108 Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
109 299
110 // Audio 300 std::transform(keyboard_keys.begin(), keyboard_keys.end(),
111 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); 301 Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam);
112 Settings::values.enable_audio_stretching = 302 std::transform(keyboard_mods.begin(), keyboard_mods.end(),
113 sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); 303 Settings::values.keyboard_keys.begin() +
114 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); 304 Settings::NativeKeyboard::LeftControlKey,
115 Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1); 305 InputCommon::GenerateKeyboardParam);
306 std::transform(keyboard_mods.begin(), keyboard_mods.end(),
307 Settings::values.keyboard_mods.begin(), InputCommon::GenerateKeyboardParam);
116 308
117 // Data Storage 309 // Data Storage
118 Settings::values.use_virtual_sd = 310 Settings::values.use_virtual_sd =
@@ -139,6 +331,30 @@ void Config::ReadValues() {
139 Settings::values.rng_seed = std::nullopt; 331 Settings::values.rng_seed = std::nullopt;
140 } 332 }
141 333
334 // Core
335 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
336 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
337
338 // Renderer
339 Settings::values.resolution_factor =
340 (float)sdl2_config->GetReal("Renderer", "resolution_factor", 1.0);
341 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
342 Settings::values.frame_limit =
343 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
344 Settings::values.use_accurate_gpu_emulation =
345 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
346
347 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
348 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
349 Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
350
351 // Audio
352 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
353 Settings::values.enable_audio_stretching =
354 sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
355 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
356 Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);
357
142 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1); 358 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
143 359
144 // Miscellaneous 360 // Miscellaneous