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/BsInput.h" |
4 | #include "Input/BsMouse.h" |
5 | #include "Input/BsKeyboard.h" |
6 | #include "Input/BsGamepad.h" |
7 | #include "Utility/BsTime.h" |
8 | #include "Math/BsMath.h" |
9 | #include "Managers/BsRenderWindowManager.h" |
10 | #include "BsCoreApplication.h" |
11 | |
12 | using namespace std::placeholders; |
13 | |
14 | namespace bs |
15 | { |
16 | // Note: Input polling methods for button/axis could be re-written so their query immediate state |
17 | // instead of returning cached state from event callbacks. This /might/ result in even less input lag? |
18 | |
19 | const int Input::HISTORY_BUFFER_SIZE = 10; // Size of buffer used for input smoothing |
20 | const float Input::WEIGHT_MODIFIER = 0.5f; |
21 | |
22 | Input::DeviceData::DeviceData() |
23 | { |
24 | for (UINT32 i = 0; i < BC_Count; i++) |
25 | keyStates[i] = ButtonState::Off; |
26 | } |
27 | |
28 | Input::Input() |
29 | { |
30 | SPtr<RenderWindow> primaryWindow = gCoreApplication().getPrimaryWindow(); |
31 | primaryWindow->getCustomAttribute("WINDOW" , &mWindowHandle); |
32 | |
33 | // Subscribe to events |
34 | mCharInputConn = Platform::onCharInput.connect(std::bind(&Input::charInput, this, _1)); |
35 | mCursorMovedConn = Platform::onCursorMoved.connect(std::bind(&Input::cursorMoved, this, _1, _2)); |
36 | mCursorPressedConn = Platform::onCursorButtonPressed.connect(std::bind(&Input::cursorPressed, this, _1, _2, _3)); |
37 | mCursorReleasedConn = Platform::onCursorButtonReleased.connect(std::bind(&Input::cursorReleased, this, _1, _2, _3)); |
38 | mCursorDoubleClickConn = Platform::onCursorDoubleClick.connect(std::bind(&Input::cursorDoubleClick, this, _1, _2)); |
39 | mInputCommandConn = Platform::onInputCommand.connect(std::bind(&Input::inputCommandEntered, this, _1)); |
40 | |
41 | mMouseWheelScrolledConn = Platform::onMouseWheelScrolled.connect(std::bind(&Input::mouseWheelScrolled, this, _1)); |
42 | |
43 | RenderWindowManager::instance().onFocusGained.connect(std::bind(&Input::inputWindowChanged, this, _1)); |
44 | RenderWindowManager::instance().onFocusLost.connect(std::bind(&Input::inputFocusLost, this)); |
45 | |
46 | for (int i = 0; i < 3; i++) |
47 | mPointerButtonStates[i] = ButtonState::Off; |
48 | |
49 | // Mouse smoothing |
50 | mMouseSampleAccumulator[0] = 0; |
51 | mMouseSampleAccumulator[1] = 0; |
52 | mTotalMouseSamplingTime[0] = 1.0f / 125.0f; // Use 125Hz as initial pooling rate for mice |
53 | mTotalMouseSamplingTime[1] = 1.0f / 125.0f; |
54 | mTotalMouseNumSamples[0] = 1; |
55 | mTotalMouseNumSamples[1] = 1; |
56 | mMouseSmoothedAxis[0] = 0.0f; |
57 | mMouseSmoothedAxis[1] = 0.0f; |
58 | mMouseZeroTime[0] = 0.0f; |
59 | mMouseZeroTime[1] = 0.0f; |
60 | |
61 | // Raw input |
62 | initRawInput(); |
63 | } |
64 | |
65 | Input::~Input() |
66 | { |
67 | cleanUpRawInput(); |
68 | |
69 | mCharInputConn.disconnect(); |
70 | mCursorMovedConn.disconnect(); |
71 | mCursorPressedConn.disconnect(); |
72 | mCursorReleasedConn.disconnect(); |
73 | mCursorDoubleClickConn.disconnect(); |
74 | mInputCommandConn.disconnect(); |
75 | mMouseWheelScrolledConn.disconnect(); |
76 | } |
77 | |
78 | void Input::_update() |
79 | { |
80 | // Toggle states only remain active for a single frame before they are transitioned |
81 | // into permanent state |
82 | |
83 | for (auto& deviceData : mDevices) |
84 | { |
85 | for (UINT32 i = 0; i < BC_Count; i++) |
86 | { |
87 | if (deviceData.keyStates[i] == ButtonState::ToggledOff || deviceData.keyStates[i] == ButtonState::ToggledOnOff) |
88 | deviceData.keyStates[i] = ButtonState::Off; |
89 | else if (deviceData.keyStates[i] == ButtonState::ToggledOn) |
90 | deviceData.keyStates[i] = ButtonState::On; |
91 | } |
92 | |
93 | UINT32 numAxes = (UINT32)deviceData.axes.size(); |
94 | for (UINT32 i = 0; i < numAxes; i++) |
95 | deviceData.axes[i] = 0.0f; |
96 | } |
97 | |
98 | for (UINT32 i = 0; i < 3; i++) |
99 | { |
100 | if (mPointerButtonStates[i] == ButtonState::ToggledOff || mPointerButtonStates[i] == ButtonState::ToggledOnOff) |
101 | mPointerButtonStates[i] = ButtonState::Off; |
102 | else if (mPointerButtonStates[i] == ButtonState::ToggledOn) |
103 | mPointerButtonStates[i] = ButtonState::On; |
104 | } |
105 | |
106 | mPointerDelta = Vector2I::ZERO; // Reset delta in case we don't receive any mouse input this frame |
107 | mPointerDoubleClicked = false; |
108 | |
109 | // Capture raw input |
110 | if (mMouse != nullptr) |
111 | mMouse->capture(); |
112 | |
113 | if (mKeyboard != nullptr) |
114 | mKeyboard->capture(); |
115 | |
116 | for (auto& gamepad : mGamepads) |
117 | gamepad->capture(); |
118 | |
119 | float rawXValue = 0.0f; |
120 | float rawYValue = 0.0f; |
121 | |
122 | // Smooth mouse axes if needed |
123 | if (mMouseSmoothingEnabled) |
124 | { |
125 | rawXValue = smoothMouse((float)mMouseSampleAccumulator[0], 0); |
126 | rawYValue = smoothMouse((float)mMouseSampleAccumulator[1], 1); |
127 | } |
128 | else |
129 | { |
130 | rawXValue = (float)mMouseSampleAccumulator[0]; |
131 | rawYValue = (float)mMouseSampleAccumulator[1]; |
132 | } |
133 | |
134 | rawXValue *= 0.1f; |
135 | rawYValue *= 0.1f; |
136 | |
137 | mMouseSampleAccumulator[0] = 0; |
138 | mMouseSampleAccumulator[1] = 0; |
139 | |
140 | axisMoved(0, -rawXValue, (UINT32)InputAxis::MouseX); |
141 | axisMoved(0, -rawYValue, (UINT32)InputAxis::MouseY); |
142 | } |
143 | |
144 | void Input::_triggerCallbacks() |
145 | { |
146 | Vector2I pointerPos; |
147 | float mouseScroll; |
148 | OSPointerButtonStates pointerState; |
149 | |
150 | { |
151 | Lock lock(mMutex); |
152 | |
153 | std::swap(mQueuedEvents[0], mQueuedEvents[1]); |
154 | |
155 | std::swap(mButtonDownEvents[0], mButtonDownEvents[1]); |
156 | std::swap(mButtonUpEvents[0], mButtonUpEvents[1]); |
157 | |
158 | std::swap(mPointerPressedEvents[0], mPointerPressedEvents[1]); |
159 | std::swap(mPointerReleasedEvents[0], mPointerReleasedEvents[1]); |
160 | std::swap(mPointerDoubleClickEvents[0], mPointerDoubleClickEvents[1]); |
161 | |
162 | std::swap(mTextInputEvents[0], mTextInputEvents[1]); |
163 | std::swap(mCommandEvents[0], mCommandEvents[1]); |
164 | |
165 | pointerPos = mPointerPosition; |
166 | mouseScroll = mMouseScroll; |
167 | pointerState = mPointerState; |
168 | |
169 | mMouseScroll = 0.0f; |
170 | } |
171 | |
172 | if(pointerPos != mLastPointerPosition || mouseScroll != 0.0f) |
173 | { |
174 | PointerEvent event; |
175 | event.alt = false; |
176 | event.shift = pointerState.shift; |
177 | event.control = pointerState.ctrl; |
178 | event.buttonStates[0] = pointerState.mouseButtons[0]; |
179 | event.buttonStates[1] = pointerState.mouseButtons[1]; |
180 | event.buttonStates[2] = pointerState.mouseButtons[2]; |
181 | event.mouseWheelScrollAmount = mouseScroll; |
182 | |
183 | event.type = PointerEventType::CursorMoved; |
184 | event.screenPos = pointerPos; |
185 | |
186 | if (mLastPositionSet) |
187 | mPointerDelta = event.screenPos - mLastPointerPosition; |
188 | |
189 | event.delta = mPointerDelta; |
190 | |
191 | onPointerMoved(event); |
192 | |
193 | mLastPointerPosition = event.screenPos; |
194 | mLastPositionSet = true; |
195 | } |
196 | |
197 | for (auto& event : mQueuedEvents[1]) |
198 | { |
199 | switch (event.type) |
200 | { |
201 | case EventType::ButtonDown: |
202 | { |
203 | const ButtonEvent& eventData = mButtonDownEvents[1][event.idx]; |
204 | |
205 | mDevices[eventData.deviceIdx].keyStates[eventData.buttonCode & 0x0000FFFF] = ButtonState::ToggledOn; |
206 | onButtonDown(mButtonDownEvents[1][event.idx]); |
207 | } |
208 | break; |
209 | case EventType::ButtonUp: |
210 | { |
211 | const ButtonEvent& eventData = mButtonUpEvents[1][event.idx]; |
212 | |
213 | while (eventData.deviceIdx >= (UINT32)mDevices.size()) |
214 | mDevices.push_back(DeviceData()); |
215 | |
216 | if (mDevices[eventData.deviceIdx].keyStates[eventData.buttonCode & 0x0000FFFF] == ButtonState::ToggledOn) |
217 | mDevices[eventData.deviceIdx].keyStates[eventData.buttonCode & 0x0000FFFF] = ButtonState::ToggledOnOff; |
218 | else |
219 | mDevices[eventData.deviceIdx].keyStates[eventData.buttonCode & 0x0000FFFF] = ButtonState::ToggledOff; |
220 | |
221 | onButtonUp(mButtonUpEvents[1][event.idx]); |
222 | } |
223 | break; |
224 | case EventType::PointerDown: |
225 | { |
226 | const PointerEvent& eventData = mPointerPressedEvents[1][event.idx]; |
227 | mPointerButtonStates[(UINT32)eventData.button] = ButtonState::ToggledOn; |
228 | |
229 | onPointerPressed(eventData); |
230 | } |
231 | break; |
232 | case EventType::PointerUp: |
233 | { |
234 | const PointerEvent& eventData = mPointerReleasedEvents[1][event.idx]; |
235 | |
236 | if (mPointerButtonStates[(UINT32)eventData.button] == ButtonState::ToggledOn) |
237 | mPointerButtonStates[(UINT32)eventData.button] = ButtonState::ToggledOnOff; |
238 | else |
239 | mPointerButtonStates[(UINT32)eventData.button] = ButtonState::ToggledOff; |
240 | |
241 | onPointerReleased(eventData); |
242 | } |
243 | break; |
244 | case EventType::PointerDoubleClick: |
245 | mPointerDoubleClicked = true; |
246 | onPointerDoubleClick(mPointerDoubleClickEvents[1][event.idx]); |
247 | break; |
248 | case EventType::TextInput: |
249 | onCharInput(mTextInputEvents[1][event.idx]); |
250 | break; |
251 | case EventType::Command: |
252 | onInputCommand(mCommandEvents[1][event.idx]); |
253 | break; |
254 | default: |
255 | break; |
256 | } |
257 | } |
258 | |
259 | mQueuedEvents[1].clear(); |
260 | mButtonDownEvents[1].clear(); |
261 | mButtonUpEvents[1].clear(); |
262 | mPointerPressedEvents[1].clear(); |
263 | mPointerReleasedEvents[1].clear(); |
264 | mPointerDoubleClickEvents[1].clear(); |
265 | mTextInputEvents[1].clear(); |
266 | mCommandEvents[1].clear(); |
267 | } |
268 | |
269 | void Input::inputWindowChanged(RenderWindow& win) |
270 | { |
271 | UINT64 hWnd = 0; |
272 | win.getCustomAttribute("WINDOW" , &hWnd); |
273 | |
274 | if(mKeyboard != nullptr) |
275 | mKeyboard->changeCaptureContext(hWnd); |
276 | |
277 | if(mMouse != nullptr) |
278 | mMouse->changeCaptureContext(hWnd); |
279 | |
280 | for (auto& gamepad : mGamepads) |
281 | gamepad->changeCaptureContext(hWnd); |
282 | } |
283 | |
284 | void Input::inputFocusLost() |
285 | { |
286 | if(mKeyboard != nullptr) |
287 | mKeyboard->changeCaptureContext((UINT64)-1); |
288 | |
289 | if(mMouse != nullptr) |
290 | mMouse->changeCaptureContext((UINT64)-1); |
291 | |
292 | for (auto& gamepad : mGamepads) |
293 | gamepad->changeCaptureContext((UINT64)-1); |
294 | } |
295 | |
296 | void Input::_notifyMouseMoved(INT32 relX, INT32 relY, INT32 relZ) |
297 | { |
298 | mMouseSampleAccumulator[0] += relX; |
299 | mMouseSampleAccumulator[1] += relY; |
300 | |
301 | mTotalMouseNumSamples[0] += Math::roundToInt(Math::abs((float)relX)); |
302 | mTotalMouseNumSamples[1] += Math::roundToInt(Math::abs((float)relY)); |
303 | |
304 | // Update sample times used for determining sampling rate. But only if something was |
305 | // actually sampled, and only if this isn't the first non-zero sample. |
306 | if (mLastMouseUpdateFrame != gTime().getFrameIdx()) |
307 | { |
308 | if (relX != 0 && !Math::approxEquals(mMouseSmoothedAxis[0], 0.0f)) |
309 | mTotalMouseSamplingTime[0] += gTime().getFrameDelta(); |
310 | |
311 | if (relY != 0 && !Math::approxEquals(mMouseSmoothedAxis[1], 0.0f)) |
312 | mTotalMouseSamplingTime[1] += gTime().getFrameDelta(); |
313 | |
314 | mLastMouseUpdateFrame = gTime().getFrameIdx(); |
315 | } |
316 | |
317 | axisMoved(0, (float)relZ, (UINT32)InputAxis::MouseZ); |
318 | } |
319 | |
320 | void Input::_notifyAxisMoved(UINT32 gamepadIdx, UINT32 axisIdx, INT32 value) |
321 | { |
322 | // Move axis values into [-1.0f, 1.0f] range |
323 | float axisRange = Math::abs((float)Gamepad::MAX_AXIS) + Math::abs((float)Gamepad::MIN_AXIS); |
324 | |
325 | float axisValue = ((value + Math::abs((float)Gamepad::MIN_AXIS)) / axisRange) * 2.0f - 1.0f; |
326 | axisMoved(gamepadIdx, axisValue, axisIdx); |
327 | } |
328 | |
329 | void Input::_notifyButtonPressed(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp) |
330 | { |
331 | buttonDown(deviceIdx, code, timestamp - mTimestampClockOffset); |
332 | } |
333 | |
334 | void Input::_notifyButtonReleased(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp) |
335 | { |
336 | buttonUp(deviceIdx, code, timestamp - mTimestampClockOffset); |
337 | } |
338 | |
339 | void Input::buttonDown(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp) |
340 | { |
341 | Lock lock(mMutex); |
342 | |
343 | while (deviceIdx >= (UINT32)mDevices.size()) |
344 | mDevices.push_back(DeviceData()); |
345 | |
346 | ButtonEvent btnEvent; |
347 | btnEvent.buttonCode = code; |
348 | btnEvent.timestamp = timestamp; |
349 | btnEvent.deviceIdx = deviceIdx; |
350 | |
351 | mQueuedEvents[0].push_back(QueuedEvent(EventType::ButtonDown, (UINT32)mButtonDownEvents[0].size())); |
352 | mButtonDownEvents[0].push_back(btnEvent); |
353 | } |
354 | |
355 | void Input::buttonUp(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp) |
356 | { |
357 | Lock lock(mMutex); |
358 | |
359 | ButtonEvent btnEvent; |
360 | btnEvent.buttonCode = code; |
361 | btnEvent.timestamp = timestamp; |
362 | btnEvent.deviceIdx = deviceIdx; |
363 | |
364 | mQueuedEvents[0].push_back(QueuedEvent(EventType::ButtonUp, (UINT32)mButtonUpEvents[0].size())); |
365 | mButtonUpEvents[0].push_back(btnEvent); |
366 | } |
367 | |
368 | void Input::axisMoved(UINT32 deviceIdx, float value, UINT32 axis) |
369 | { |
370 | // Note: This method must only ever be called from the main thread, as we don't lock access to axis data |
371 | while (deviceIdx >= (UINT32)mDevices.size()) |
372 | mDevices.push_back(DeviceData()); |
373 | |
374 | Vector<float>& axes = mDevices[deviceIdx].axes; |
375 | while (axis >= (UINT32)axes.size()) |
376 | axes.push_back(0.0f); |
377 | |
378 | mDevices[deviceIdx].axes[axis] = value; |
379 | } |
380 | |
381 | void Input::cursorMoved(const Vector2I& cursorPos, const OSPointerButtonStates& btnStates) |
382 | { |
383 | Lock lock(mMutex); |
384 | |
385 | mPointerPosition = cursorPos; |
386 | mPointerState = btnStates; |
387 | } |
388 | |
389 | void Input::cursorPressed(const Vector2I& cursorPos, OSMouseButton button, const OSPointerButtonStates& btnStates) |
390 | { |
391 | Lock lock(mMutex); |
392 | |
393 | PointerEvent event; |
394 | event.alt = false; |
395 | event.shift = btnStates.shift; |
396 | event.control = btnStates.ctrl; |
397 | event.buttonStates[0] = btnStates.mouseButtons[0]; |
398 | event.buttonStates[1] = btnStates.mouseButtons[1]; |
399 | event.buttonStates[2] = btnStates.mouseButtons[2]; |
400 | |
401 | switch(button) |
402 | { |
403 | case OSMouseButton::Left: |
404 | event.button = PointerEventButton::Left; |
405 | break; |
406 | case OSMouseButton::Middle: |
407 | event.button = PointerEventButton::Middle; |
408 | break; |
409 | case OSMouseButton::Right: |
410 | event.button = PointerEventButton::Right; |
411 | break; |
412 | default: |
413 | break; |
414 | } |
415 | |
416 | event.screenPos = cursorPos; |
417 | event.type = PointerEventType::ButtonPressed; |
418 | |
419 | mQueuedEvents[0].push_back(QueuedEvent(EventType::PointerDown, (UINT32)mPointerPressedEvents[0].size())); |
420 | mPointerPressedEvents[0].push_back(event); |
421 | } |
422 | |
423 | void Input::cursorReleased(const Vector2I& cursorPos, OSMouseButton button, const OSPointerButtonStates& btnStates) |
424 | { |
425 | Lock lock(mMutex); |
426 | |
427 | PointerEvent event; |
428 | event.alt = false; |
429 | event.shift = btnStates.shift; |
430 | event.control = btnStates.ctrl; |
431 | event.buttonStates[0] = btnStates.mouseButtons[0]; |
432 | event.buttonStates[1] = btnStates.mouseButtons[1]; |
433 | event.buttonStates[2] = btnStates.mouseButtons[2]; |
434 | |
435 | switch(button) |
436 | { |
437 | case OSMouseButton::Left: |
438 | event.button = PointerEventButton::Left; |
439 | break; |
440 | case OSMouseButton::Middle: |
441 | event.button = PointerEventButton::Middle; |
442 | break; |
443 | case OSMouseButton::Right: |
444 | event.button = PointerEventButton::Right; |
445 | break; |
446 | default: |
447 | break; |
448 | } |
449 | |
450 | event.screenPos = cursorPos; |
451 | event.type = PointerEventType::ButtonReleased; |
452 | |
453 | mQueuedEvents[0].push_back(QueuedEvent(EventType::PointerUp, (UINT32)mPointerReleasedEvents[0].size())); |
454 | mPointerReleasedEvents[0].push_back(event); |
455 | } |
456 | |
457 | void Input::cursorDoubleClick(const Vector2I& cursorPos, const OSPointerButtonStates& btnStates) |
458 | { |
459 | Lock lock(mMutex); |
460 | |
461 | PointerEvent event; |
462 | event.alt = false; |
463 | event.shift = btnStates.shift; |
464 | event.control = btnStates.ctrl; |
465 | event.buttonStates[0] = btnStates.mouseButtons[0]; |
466 | event.buttonStates[1] = btnStates.mouseButtons[1]; |
467 | event.buttonStates[2] = btnStates.mouseButtons[2]; |
468 | event.button = PointerEventButton::Left; |
469 | event.screenPos = cursorPos; |
470 | event.type = PointerEventType::DoubleClick; |
471 | |
472 | mQueuedEvents[0].push_back(QueuedEvent(EventType::PointerDoubleClick, (UINT32)mPointerDoubleClickEvents[0].size())); |
473 | mPointerDoubleClickEvents[0].push_back(event); |
474 | } |
475 | |
476 | void Input::inputCommandEntered(InputCommandType commandType) |
477 | { |
478 | Lock lock(mMutex); |
479 | |
480 | mQueuedEvents[0].push_back(QueuedEvent(EventType::Command, (UINT32)mCommandEvents[0].size())); |
481 | mCommandEvents[0].push_back(commandType); |
482 | } |
483 | |
484 | void Input::mouseWheelScrolled(float scrollPos) |
485 | { |
486 | Lock lock(mMutex); |
487 | |
488 | mMouseScroll = scrollPos; |
489 | } |
490 | |
491 | void Input::charInput(UINT32 chr) |
492 | { |
493 | Lock lock(mMutex); |
494 | |
495 | TextInputEvent textInputEvent; |
496 | textInputEvent.textChar = chr; |
497 | |
498 | mQueuedEvents[0].push_back(QueuedEvent(EventType::TextInput, (UINT32)mTextInputEvents[0].size())); |
499 | mTextInputEvents[0].push_back(textInputEvent); |
500 | } |
501 | |
502 | float Input::getAxisValue(UINT32 type, UINT32 deviceIdx) const |
503 | { |
504 | if (deviceIdx >= (UINT32)mDevices.size()) |
505 | return 0.0f; |
506 | |
507 | const Vector<float>& axes = mDevices[deviceIdx].axes; |
508 | if (type >= (UINT32)axes.size()) |
509 | return 0.0f; |
510 | |
511 | return axes[type]; |
512 | } |
513 | |
514 | bool Input::isButtonHeld(ButtonCode button, UINT32 deviceIdx) const |
515 | { |
516 | if (deviceIdx >= (UINT32)mDevices.size()) |
517 | return false; |
518 | |
519 | return mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::On || |
520 | mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOn || |
521 | mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOnOff; |
522 | } |
523 | |
524 | bool Input::isButtonUp(ButtonCode button, UINT32 deviceIdx) const |
525 | { |
526 | if (deviceIdx >= (UINT32)mDevices.size()) |
527 | return false; |
528 | |
529 | return mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOff || |
530 | mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOnOff; |
531 | } |
532 | |
533 | bool Input::isButtonDown(ButtonCode button, UINT32 deviceIdx) const |
534 | { |
535 | if (deviceIdx >= (UINT32)mDevices.size()) |
536 | return false; |
537 | |
538 | return mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOn || |
539 | mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOnOff; |
540 | } |
541 | |
542 | bool Input::isPointerButtonHeld(PointerEventButton pointerButton) const |
543 | { |
544 | return mPointerButtonStates[(UINT32)pointerButton] == ButtonState::On || |
545 | mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOn || |
546 | mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOnOff; |
547 | } |
548 | |
549 | bool Input::isPointerButtonUp(PointerEventButton pointerButton) const |
550 | { |
551 | return mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOff || |
552 | mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOnOff; |
553 | } |
554 | |
555 | bool Input::isPointerButtonDown(PointerEventButton pointerButton) const |
556 | { |
557 | return mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOn || |
558 | mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOnOff; |
559 | } |
560 | |
561 | bool Input::isPointerDoubleClicked() const |
562 | { |
563 | return mPointerDoubleClicked; |
564 | } |
565 | |
566 | Vector2I Input::getPointerPosition() const |
567 | { |
568 | return mPointerPosition; |
569 | } |
570 | |
571 | String Input::getDeviceName(InputDevice type, UINT32 idx) |
572 | { |
573 | switch(type) |
574 | { |
575 | case InputDevice::Keyboard: |
576 | if (mKeyboard != nullptr && idx == 0) |
577 | return mKeyboard->getName(); |
578 | |
579 | return StringUtil::BLANK; |
580 | case InputDevice::Mouse: |
581 | if (mMouse != nullptr && idx == 0) |
582 | return mMouse->getName(); |
583 | |
584 | return StringUtil::BLANK; |
585 | case InputDevice::Gamepad: |
586 | if (idx < (UINT32)mGamepads.size()) |
587 | return mGamepads[idx]->getName(); |
588 | |
589 | return StringUtil::BLANK; |
590 | default: |
591 | return StringUtil::BLANK; |
592 | } |
593 | } |
594 | |
595 | void Input::setMouseSmoothing(bool enable) |
596 | { |
597 | mMouseSmoothingEnabled = enable; |
598 | } |
599 | |
600 | float Input::smoothMouse(float value, UINT32 idx) |
601 | { |
602 | UINT32 sampleCount = 1; |
603 | |
604 | float deltaTime = gTime().getFrameDelta(); |
605 | if (deltaTime < 0.25f) |
606 | { |
607 | float secondsPerSample = mTotalMouseSamplingTime[idx] / mTotalMouseNumSamples[idx]; |
608 | |
609 | if (value == 0.0f) |
610 | { |
611 | mMouseZeroTime[idx] += deltaTime; |
612 | if (mMouseZeroTime[idx] < secondsPerSample) |
613 | value = mMouseSmoothedAxis[idx] * deltaTime / secondsPerSample; |
614 | else |
615 | mMouseSmoothedAxis[idx] = 0; |
616 | } |
617 | else |
618 | { |
619 | mMouseZeroTime[idx] = 0; |
620 | if (mMouseSmoothedAxis[idx] != 0) |
621 | { |
622 | if (deltaTime < secondsPerSample * (sampleCount + 1)) |
623 | value = value * deltaTime / (secondsPerSample * sampleCount); |
624 | else |
625 | sampleCount = Math::roundToInt(deltaTime / secondsPerSample); |
626 | } |
627 | |
628 | mMouseSmoothedAxis[idx] = value / sampleCount; |
629 | } |
630 | } |
631 | else |
632 | { |
633 | mMouseSmoothedAxis[idx] = 0.0f; |
634 | mMouseZeroTime[idx] = 0.0f; |
635 | } |
636 | |
637 | return value; |
638 | } |
639 | |
640 | Input& gInput() |
641 | { |
642 | return Input::instance(); |
643 | } |
644 | } |
645 | |