1 // Copyright 2017 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <limits>
10 #include <memory>
11 #include <string>
12
13 #include "Common/Common.h"
14 #include "Common/MathUtil.h"
15
16 #include "InputCommon/ControlReference/ControlReference.h"
17 #include "InputCommon/ControllerEmu/Control/Control.h"
18 #include "InputCommon/ControllerEmu/Control/Input.h"
19 #include "InputCommon/ControllerEmu/ControllerEmu.h"
20 #include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
21
22 namespace ControllerEmu
23 {
Cursor(std::string name_,std::string ui_name_)24 Cursor::Cursor(std::string name_, std::string ui_name_)
25 : ReshapableInput(std::move(name_), std::move(ui_name_), GroupType::Cursor),
26 m_last_update(Clock::now())
27 {
28 for (auto& named_direction : named_directions)
29 AddInput(Translate, named_direction);
30
31 AddInput(Translate, _trans("Hide"));
32 AddInput(Translate, _trans("Recenter"));
33
34 AddInput(Translate, _trans("Relative Input Hold"));
35
36 // Default values chosen to reach screen edges in most games including the Wii Menu.
37
38 AddSetting(&m_vertical_offset_setting,
39 // i18n: Refers to a positional offset applied to an emulated wiimote.
40 {_trans("Vertical Offset"),
41 // i18n: The symbol/abbreviation for centimeters.
42 _trans("cm")},
43 10, -100, 100);
44
45 AddSetting(&m_yaw_setting,
46 // i18n: Refers to an amount of rotational movement about the "yaw" axis.
47 {_trans("Total Yaw"),
48 // i18n: The symbol/abbreviation for degrees (unit of angular measure).
49 _trans("°"),
50 // i18n: Refers to emulated wii remote movements.
51 _trans("Total rotation about the yaw axis.")},
52 25, 0, 360);
53
54 AddSetting(&m_pitch_setting,
55 // i18n: Refers to an amount of rotational movement about the "pitch" axis.
56 {_trans("Total Pitch"),
57 // i18n: The symbol/abbreviation for degrees (unit of angular measure).
58 _trans("°"),
59 // i18n: Refers to emulated wii remote movements.
60 _trans("Total rotation about the pitch axis.")},
61 20, 0, 360);
62
63 AddSetting(&m_relative_setting, {_trans("Relative Input")}, false);
64 AddSetting(&m_autohide_setting, {_trans("Auto-Hide")}, false);
65 }
66
GetReshapableState(bool adjusted)67 Cursor::ReshapeData Cursor::GetReshapableState(bool adjusted)
68 {
69 const ControlState y = controls[0]->GetState() - controls[1]->GetState();
70 const ControlState x = controls[3]->GetState() - controls[2]->GetState();
71
72 // Return raw values. (used in UI)
73 if (!adjusted)
74 return {x, y};
75
76 return Reshape(x, y, 0.0);
77 }
78
GetGateRadiusAtAngle(double ang) const79 ControlState Cursor::GetGateRadiusAtAngle(double ang) const
80 {
81 return SquareStickGate(1.0).GetRadiusAtAngle(ang);
82 }
83
GetState(const bool adjusted)84 Cursor::StateData Cursor::GetState(const bool adjusted)
85 {
86 if (!adjusted)
87 {
88 const auto raw_input = GetReshapableState(false);
89
90 return {raw_input.x, raw_input.y};
91 }
92
93 const auto input = GetReshapableState(true);
94
95 // TODO: Using system time is ugly.
96 // Kill this after state is moved into wiimote rather than this class.
97 const auto now = Clock::now();
98 const auto ms_since_update =
99 std::chrono::duration_cast<std::chrono::milliseconds>(now - m_last_update).count();
100 m_last_update = now;
101
102 const double max_step = STEP_PER_SEC / 1000.0 * ms_since_update;
103
104 // Relative input:
105 if (m_relative_setting.GetValue() ^ (controls[6]->GetState<bool>()))
106 {
107 // Recenter:
108 if (controls[5]->GetState<bool>())
109 {
110 m_state.x = 0.0;
111 m_state.y = 0.0;
112 }
113 else
114 {
115 m_state.x = std::clamp(m_state.x + input.x * max_step, -1.0, 1.0);
116 m_state.y = std::clamp(m_state.y + input.y * max_step, -1.0, 1.0);
117 }
118 }
119 // Absolute input:
120 else
121 {
122 m_state.x = input.x;
123 m_state.y = input.y;
124 }
125
126 StateData result = m_state;
127
128 const bool autohide = m_autohide_setting.GetValue();
129
130 // Auto-hide timer:
131 // TODO: should Z movement reset this?
132 if (!autohide || std::abs(m_prev_result.x - result.x) > AUTO_HIDE_DEADZONE ||
133 std::abs(m_prev_result.y - result.y) > AUTO_HIDE_DEADZONE)
134 {
135 m_auto_hide_timer = AUTO_HIDE_MS;
136 }
137 else if (m_auto_hide_timer)
138 {
139 m_auto_hide_timer -= std::min<int>(ms_since_update, m_auto_hide_timer);
140 }
141
142 m_prev_result = result;
143
144 // If auto-hide time is up or hide button is held:
145 if (!m_auto_hide_timer || controls[4]->GetState<bool>())
146 {
147 result.x = std::numeric_limits<ControlState>::quiet_NaN();
148 result.y = 0;
149 }
150
151 return result;
152 }
153
GetTotalYaw() const154 ControlState Cursor::GetTotalYaw() const
155 {
156 return m_yaw_setting.GetValue() * MathUtil::TAU / 360;
157 }
158
GetTotalPitch() const159 ControlState Cursor::GetTotalPitch() const
160 {
161 return m_pitch_setting.GetValue() * MathUtil::TAU / 360;
162 }
163
GetVerticalOffset() const164 ControlState Cursor::GetVerticalOffset() const
165 {
166 return m_vertical_offset_setting.GetValue() / 100;
167 }
168
IsVisible() const169 bool Cursor::StateData::IsVisible() const
170 {
171 return !std::isnan(x);
172 }
173
174 } // namespace ControllerEmu
175