1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Input/BsVirtualInput.h"
4#include "Input/BsInput.h"
5#include "Math/BsMath.h"
6#include "Utility/BsTime.h"
7
8using namespace std::placeholders;
9
10namespace bs
11{
12 VirtualInput::VirtualInput()
13 {
14 mInputConfiguration = createConfiguration();
15
16 Input::instance().onButtonDown.connect(std::bind(&VirtualInput::buttonDown, this, _1));
17 Input::instance().onButtonUp.connect(std::bind(&VirtualInput::buttonUp, this, _1));
18 }
19
20 SPtr<InputConfiguration> VirtualInput::createConfiguration()
21 {
22 return bs_shared_ptr_new<InputConfiguration>();
23 }
24
25 void VirtualInput::setConfiguration(const SPtr<InputConfiguration>& input)
26 {
27 mInputConfiguration = input;
28
29 // Note: Technically this is slightly wrong as it will
30 // "forget" any buttons currently held down, but shouldn't matter much in practice.
31 for (auto& deviceData : mDevices)
32 deviceData.cachedStates.clear();
33 }
34
35 bool VirtualInput::isButtonDown(const VirtualButton& button, UINT32 deviceIdx) const
36 {
37 if (deviceIdx >= (UINT32)mDevices.size())
38 return false;
39
40 const Map<UINT32, ButtonData>& cachedStates = mDevices[deviceIdx].cachedStates;
41 auto iterFind = cachedStates.find(button.buttonIdentifier);
42
43 if (iterFind != cachedStates.end())
44 return iterFind->second.state == ButtonState::ToggledOn;
45
46 return false;
47 }
48
49 bool VirtualInput::isButtonUp(const VirtualButton& button, UINT32 deviceIdx) const
50 {
51 if (deviceIdx >= (UINT32)mDevices.size())
52 return false;
53
54 const Map<UINT32, ButtonData>& cachedStates = mDevices[deviceIdx].cachedStates;
55 auto iterFind = cachedStates.find(button.buttonIdentifier);
56
57 if (iterFind != cachedStates.end())
58 return iterFind->second.state == ButtonState::ToggledOff;
59
60 return false;
61 }
62
63 bool VirtualInput::isButtonHeld(const VirtualButton& button, UINT32 deviceIdx) const
64 {
65 if (deviceIdx >= (UINT32)mDevices.size())
66 return false;
67
68 const Map<UINT32, ButtonData>& cachedStates = mDevices[deviceIdx].cachedStates;
69 auto iterFind = cachedStates.find(button.buttonIdentifier);
70
71 if (iterFind != cachedStates.end())
72 return iterFind->second.state == ButtonState::On || iterFind->second.state == ButtonState::ToggledOn;
73
74 return false;
75 }
76
77 float VirtualInput::getAxisValue(const VirtualAxis& axis, UINT32 deviceIdx) const
78 {
79 VIRTUAL_AXIS_DESC axisDesc;
80 if (mInputConfiguration->_getAxis(axis, axisDesc))
81 {
82 float axisValue = gInput().getAxisValue((UINT32)axisDesc.type, deviceIdx);
83
84 bool isMouseAxis = (UINT32)axisDesc.type <= (UINT32)InputAxis::MouseZ;
85 bool isNormalized = axisDesc.normalize || !isMouseAxis;
86
87 if (isNormalized && axisDesc.deadZone > 0.0f)
88 {
89 // Scale to [-1, 1] range after removing the dead zone
90 if (axisValue > 0)
91 axisValue = std::max(0.f, axisValue - axisDesc.deadZone) / (1.0f - axisDesc.deadZone);
92 else
93 axisValue = -std::max(0.f, -axisValue - axisDesc.deadZone) / (1.0f - axisDesc.deadZone);
94 }
95
96 if(axisDesc.normalize)
97 {
98 if(isMouseAxis)
99 {
100 // Currently normalizing using value of 1, which isn't doing anything, but keep the code in case that
101 // changes
102 axisValue /= 1.0f;
103 }
104
105 axisValue = Math::clamp(axisValue * axisDesc.sensitivity, -1.0f, 1.0f);
106 }
107 else
108 axisValue *= axisDesc.sensitivity;
109
110 if (axisDesc.invert)
111 axisValue = -axisValue;
112
113 return axisValue;
114 }
115
116 return 0.0f;
117 }
118
119 void VirtualInput::_update()
120 {
121 UINT64 frameIdx = gTime().getFrameIdx();
122 for (auto& deviceData : mDevices)
123 {
124 for (auto& state : deviceData.cachedStates)
125 {
126 // We need to stay in toggled state for one frame.
127 if (state.second.updateFrameIdx == frameIdx)
128 continue;
129
130 if (state.second.state == ButtonState::ToggledOff)
131 state.second.state = ButtonState::Off;
132 else if (state.second.state == ButtonState::ToggledOn)
133 state.second.state = ButtonState::On;
134 }
135 }
136
137 bool hasEvents = true;
138 UINT64 repeatInternal = mInputConfiguration->getRepeatInterval();
139 UINT64 currentTime = gTime().getTimeMs();
140
141 // Trigger all events
142 while(hasEvents)
143 {
144 while(!mEvents.empty())
145 {
146 VirtualButtonEvent& event = mEvents.front();
147
148 if(event.state == ButtonState::On)
149 {
150 if(!onButtonDown.empty())
151 onButtonDown(event.button, event.deviceIdx);
152 }
153 else if(event.state == ButtonState::Off)
154 {
155 if(!onButtonUp.empty())
156 onButtonUp(event.button, event.deviceIdx);
157 }
158
159 mEvents.pop();
160 }
161
162 // Queue up any repeatable events
163 hasEvents = false;
164
165 for (auto& deviceData : mDevices)
166 {
167 for (auto& state : deviceData.cachedStates)
168 {
169 if (state.second.state != ButtonState::On)
170 continue;
171
172 if (!state.second.allowRepeat)
173 continue;
174
175 UINT64 diff = currentTime - state.second.timestamp;
176 if (diff >= repeatInternal)
177 {
178 state.second.timestamp += repeatInternal;
179
180 VirtualButtonEvent event;
181 event.button = state.second.button;
182 event.state = ButtonState::On;
183 event.deviceIdx = 0;
184
185 mEvents.push(event);
186 hasEvents = true;
187 }
188 }
189
190 break; // Only repeat the first device. Repeat only makes sense for keyboard which there is only one of.
191 }
192 }
193
194 // Send button held events
195 UINT32 deviceIdx = 0;
196 for (auto& deviceData : mDevices)
197 {
198 for (auto& btnIdentifier : deviceData.heldButtons)
199 {
200 Map<UINT32, ButtonData>& cachedStates = deviceData.cachedStates;
201 ButtonData& data = cachedStates[btnIdentifier];
202
203 onButtonHeld(data.button, deviceIdx);
204 }
205
206 deviceIdx++;
207 }
208
209 }
210
211 void VirtualInput::buttonDown(const ButtonEvent& event)
212 {
213 if(event.buttonCode == BC_LSHIFT || event.buttonCode == BC_RSHIFT)
214 mActiveModifiers |= (UINT32)ButtonModifier::Shift;
215 else if(event.buttonCode == BC_LCONTROL || event.buttonCode == BC_RCONTROL)
216 mActiveModifiers |= (UINT32)ButtonModifier::Ctrl;
217 else if(event.buttonCode == BC_LMENU || event.buttonCode == BC_RMENU)
218 mActiveModifiers |= (UINT32)ButtonModifier::Alt;
219
220 tempButtons.clear();
221 tempBtnDescs.clear();
222
223 if (mInputConfiguration->_getButtons(event.buttonCode, mActiveModifiers, tempButtons, tempBtnDescs))
224 {
225 while (event.deviceIdx >= (UINT32)mDevices.size())
226 mDevices.push_back(DeviceData());
227
228 Map<UINT32, ButtonData>& cachedStates = mDevices[event.deviceIdx].cachedStates;
229 DynArray<UINT32>& heldButtons = mDevices[event.deviceIdx].heldButtons;
230
231 UINT32 numButtons = (UINT32)tempButtons.size();
232 for (UINT32 i = 0; i < numButtons; i++)
233 {
234 const VirtualButton& btn = tempButtons[i];
235 const VIRTUAL_BUTTON_DESC& btnDesc = tempBtnDescs[i];
236
237 ButtonData& data = cachedStates[btn.buttonIdentifier];
238
239 data.button = btn;
240 data.state = ButtonState::ToggledOn;
241 data.timestamp = event.timestamp;
242 data.updateFrameIdx = gTime().getFrameIdx();
243 data.allowRepeat = btnDesc.repeatable;
244
245 VirtualButtonEvent virtualEvent;
246 virtualEvent.button = btn;
247 virtualEvent.state = ButtonState::On;
248 virtualEvent.deviceIdx = event.deviceIdx;
249
250 mEvents.push(virtualEvent);
251 heldButtons.add(btn.buttonIdentifier);
252 }
253 }
254 }
255
256 void VirtualInput::buttonUp(const ButtonEvent& event)
257 {
258 if(event.buttonCode == BC_LSHIFT || event.buttonCode == BC_RSHIFT)
259 mActiveModifiers &= ~(UINT32)ButtonModifier::Shift;
260 else if(event.buttonCode == BC_LCONTROL || event.buttonCode == BC_RCONTROL)
261 mActiveModifiers &= ~(UINT32)ButtonModifier::Ctrl;
262 else if(event.buttonCode == BC_LMENU || event.buttonCode == BC_RMENU)
263 mActiveModifiers &= ~(UINT32)ButtonModifier::Alt;
264
265 tempButtons.clear();
266 tempBtnDescs.clear();
267
268 if (mInputConfiguration->_getButtons(event.buttonCode, mActiveModifiers, tempButtons, tempBtnDescs))
269 {
270 while (event.deviceIdx >= (UINT32)mDevices.size())
271 mDevices.push_back(DeviceData());
272
273 Map<UINT32, ButtonData>& cachedStates = mDevices[event.deviceIdx].cachedStates;
274 DynArray<UINT32>& heldButtons = mDevices[event.deviceIdx].heldButtons;
275
276 UINT32 numButtons = (UINT32)tempButtons.size();
277 for (UINT32 i = 0; i < numButtons; i++)
278 {
279 const VirtualButton& btn = tempButtons[i];
280 const VIRTUAL_BUTTON_DESC& btnDesc = tempBtnDescs[i];
281
282 ButtonData& data = cachedStates[btn.buttonIdentifier];
283
284 data.button = btn;
285 data.state = ButtonState::ToggledOff;
286 data.timestamp = event.timestamp;
287 data.updateFrameIdx = gTime().getFrameIdx();
288 data.allowRepeat = btnDesc.repeatable;
289
290 VirtualButtonEvent virtualEvent;
291 virtualEvent.button = btn;
292 virtualEvent.state = ButtonState::Off;
293 virtualEvent.deviceIdx = event.deviceIdx;
294
295 mEvents.push(virtualEvent);
296
297 auto iterFind = std::find(heldButtons.begin(), heldButtons.end(), btn.buttonIdentifier);
298 if(iterFind != heldButtons.end())
299 heldButtons.swapAndErase(iterFind);
300 }
301 }
302 }
303
304 VirtualInput& gVirtualInput()
305 {
306 return VirtualInput::instance();
307 }
308}