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#pragma once
4
5#include "BsCorePrerequisites.h"
6#include "Utility/BsModule.h"
7#include "Platform/BsPlatform.h"
8#include "Input/BsInputFwd.h"
9
10namespace bs
11{
12 class Mouse;
13 class Keyboard;
14 class Gamepad;
15 struct InputPrivateData;
16
17 /** @addtogroup Input
18 * @{
19 */
20
21 /**
22 * Primary module used for dealing with input. Allows you to receieve and query raw or OS input for
23 * mouse/keyboard/gamepad.
24 */
25 class BS_CORE_EXPORT Input : public Module<Input>
26 {
27 /** Possible button states. */
28 enum class ButtonState
29 {
30 Off, /**< Button is not being pressed. */
31 On, /**< Button is being pressed. */
32 ToggledOn, /**< Button has been pressed this frame. */
33 ToggledOff, /**< Button has been released this frame. */
34 ToggledOnOff, /**< Button has been pressed and released this frame. */
35 };
36
37 /** Contains axis and device data per device. */
38 struct DeviceData
39 {
40 DeviceData();
41
42 Vector<float> axes;
43 ButtonState keyStates[BC_Count];
44 };
45
46 /** Different types of possible input event callbacks. */
47 enum class EventType
48 {
49 ButtonUp, ButtonDown, PointerMoved, PointerUp, PointerDown, PointerDoubleClick, TextInput, Command
50 };
51
52 /** Stores information about a queued input event that is to be triggered later. */
53 struct QueuedEvent
54 {
55 QueuedEvent(EventType type, UINT32 idx)
56 :type(type), idx(idx)
57 { }
58
59 EventType type;
60 UINT32 idx;
61 };
62
63 public:
64 Input();
65 ~Input();
66
67 /**
68 * Returns value of the specified input axis. Normally in range [-1.0, 1.0] but can be outside the range for
69 * devices with unbound axes (for example mouse).
70 *
71 * @param[in] type Type of axis to query. Usually a type from InputAxis but can be a custom value.
72 * @param[in] deviceIdx Index of the device in case more than one is hooked up (0 - primary).
73 */
74 float getAxisValue(UINT32 type, UINT32 deviceIdx = 0) const;
75
76 /**
77 * Query if the provided button is currently being held (this frame or previous frames).
78 *
79 * @param[in] keyCode Code of the button to query.
80 * @param[in] deviceIdx Device to query the button on (0 - primary).
81 */
82 bool isButtonHeld(ButtonCode keyCode, UINT32 deviceIdx = 0) const;
83
84 /**
85 * Query if the provided button is currently being released (only true for one frame).
86 *
87 * @param[in] keyCode Code of the button to query.
88 * @param[in] deviceIdx Device to query the button on (0 - primary).
89 */
90 bool isButtonUp(ButtonCode keyCode, UINT32 deviceIdx = 0) const;
91
92 /**
93 * Query if the provided button is currently being pressed (only true for one frame).
94 *
95 * @param[in] keyCode Code of the button to query.
96 * @param[in] deviceIdx Device to query the button on (0 - primary).
97 */
98 bool isButtonDown(ButtonCode keyCode, UINT32 deviceIdx = 0) const;
99
100 /** Returns position of the pointer (for example mouse cursor) relative to the screen. */
101 Vector2I getPointerPosition() const;
102
103 /** Returns difference between pointer position between current and last frame. */
104 Vector2I getPointerDelta() const { return mPointerDelta; }
105
106 /**
107 * Query if the provided pointer button is currently being held (this frame or previous frames).
108 *
109 * @param[in] pointerButton Code of the button to query.
110 */
111 bool isPointerButtonHeld(PointerEventButton pointerButton) const;
112
113 /**
114 * Query if the provided pointer button is currently being released (only true for one frame).
115 *
116 * @param[in] pointerButton Code of the button to query.
117 */
118 bool isPointerButtonUp(PointerEventButton pointerButton) const;
119
120 /**
121 * Query if the provided pointer button is currently being pressed (only true for one frame).
122 *
123 * @param[in] pointerButton Code of the button to query.
124 */
125 bool isPointerButtonDown(PointerEventButton pointerButton) const;
126
127 /** Query has the left pointer button has been double-clicked this frame. */
128 bool isPointerDoubleClicked() const;
129
130 /** Enables or disables mouse smoothing. Smoothing makes the changes to mouse axes more gradual. */
131 void setMouseSmoothing(bool enabled);
132
133 /** Returns the number of detected devices of the specified type. */
134 UINT32 getDeviceCount(InputDevice device) const;
135
136 /** Returns the name of a specific input device. Returns empty string if the device doesn't exist. */
137 String getDeviceName(InputDevice type, UINT32 idx);
138
139 /** Triggered whenever a button is first pressed. */
140 Event<void(const ButtonEvent&)> onButtonDown;
141
142 /** Triggered whenever a button is first released. */
143 Event<void(const ButtonEvent&)> onButtonUp;
144
145 /** Triggered whenever user inputs a text character. */
146 Event<void(const TextInputEvent&)> onCharInput;
147
148 /** Triggers when some pointing device (mouse cursor, touch) moves. */
149 Event<void(const PointerEvent&)> onPointerMoved;
150
151 /** Triggers when some pointing device (mouse cursor, touch) button is pressed. */
152 Event<void(const PointerEvent&)> onPointerPressed;
153
154 /** Triggers when some pointing device (mouse cursor, touch) button is released. */
155 Event<void(const PointerEvent&)> onPointerReleased;
156
157 /** Triggers when some pointing device (mouse cursor, touch) button is double clicked. */
158 Event<void(const PointerEvent&)> onPointerDoubleClick;
159
160 // TODO Low priority: Remove this, I can emulate it using virtual input
161 /** Triggers on special input commands. */
162 Event<void(InputCommandType)> onInputCommand;
163
164 public: // ***** INTERNAL ******
165 /** @name Internal
166 * @{
167 */
168
169 /**
170 * Called every frame. Detects button state changes and prepares callback events to trigger via a call to
171 * _triggerCallbacks().
172 */
173 void _update();
174
175 /** Triggers any queued input event callbacks. */
176 void _triggerCallbacks();
177
178 /** Returns internal, platform specific privata data. */
179 InputPrivateData* _getPrivateData() const { return mPlatformData; }
180
181 /** Returns a handle to the window that is currently receiving input. */
182 UINT64 _getWindowHandle() const { return mWindowHandle; }
183
184 /** Called by Mouse when mouse movement is detected. */
185 void _notifyMouseMoved(INT32 relX, INT32 relY, INT32 relZ);
186
187 /** Called by any of the raw input devices when analog axis movement is detected. */
188 void _notifyAxisMoved(UINT32 gamepadIdx, UINT32 axisIdx, INT32 value);
189
190 /** Called by any of the raw input devices when a button is pressed. */
191 void _notifyButtonPressed(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp);
192
193 /** Called by any of the raw input devices when a button is released. */
194 void _notifyButtonReleased(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp);
195
196 /** @} */
197
198 private:
199 /** Performs platform specific raw input system initialization. */
200 void initRawInput();
201
202 /** Performs platform specific raw input system cleanup. */
203 void cleanUpRawInput();
204
205 /**
206 * Smooths the input mouse axis value. Smoothing makes the changes to the axis more gradual depending on previous
207 * values.
208 *
209 * @param[in] value Value to smooth.
210 * @param[in] idx Index of the mouse axis to smooth, 0 - horizontal, 1 - vertical.
211 * @return Smoothed value.
212 */
213 float smoothMouse(float value, UINT32 idx);
214
215 /** Triggered by input handler when a button is pressed. */
216 void buttonDown(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp);
217
218 /** Triggered by input handler when a button is released. */
219 void buttonUp(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp);
220
221 /** Triggered by input handler when a mouse/joystick axis is moved. */
222 void axisMoved(UINT32 deviceIdx, float value, UINT32 axis);
223
224 /**
225 * Called from the message loop to notify user has entered a character.
226 *
227 * @see onCharInput
228 */
229 void charInput(UINT32 character);
230
231 /**
232 * Called from the message loop to notify user has moved the cursor.
233 *
234 * @see onCursorMoved
235 */
236 void cursorMoved(const Vector2I& cursorPos, const OSPointerButtonStates& btnStates);
237
238 /**
239 * Called from the message loop to notify user has pressed a mouse button.
240 *
241 * @see onCursorPressed
242 */
243 void cursorPressed(const Vector2I& cursorPos, OSMouseButton button, const OSPointerButtonStates& btnStates);
244
245 /**
246 * Called from the message loop to notify user has released a mouse button.
247 *
248 * @see onCursorReleased
249 */
250 void cursorReleased(const Vector2I& cursorPos, OSMouseButton button, const OSPointerButtonStates& btnStates);
251
252 /**
253 * Called from the message loop to notify user has double-clicked a mouse button.
254 *
255 * @see onDoubleClick
256 */
257 void cursorDoubleClick(const Vector2I& cursorPos, const OSPointerButtonStates& btnStates);
258
259 /**
260 * Called from the message loop to notify user has entered an input command.
261 *
262 * @see onInputCommand
263 */
264 void inputCommandEntered(InputCommandType commandType);
265
266 /**
267 * Called from the message loop to notify user has scrolled the mouse wheel.
268 *
269 * @see onMouseWheelScrolled
270 */
271 void mouseWheelScrolled(float scrollPos);
272
273 /** Called when window in focus changes, as reported by the OS. */
274 void inputWindowChanged(RenderWindow& win);
275
276 /**
277 * Called when the current window loses input focus. This might be followed by inputWindowChanged() if the focus
278 * just switched to another of this application's windows.
279 */
280 void inputFocusLost();
281
282 private:
283 Mutex mMutex;
284
285 Vector<DeviceData> mDevices;
286 Vector2I mLastPointerPosition;
287 Vector2I mPointerDelta;
288 ButtonState mPointerButtonStates[3];
289 bool mPointerDoubleClicked = false;
290 bool mLastPositionSet = false;
291
292 // Thread safe
293 Vector2I mPointerPosition;
294 float mMouseScroll = 0.0f;
295 OSPointerButtonStates mPointerState;
296
297 Vector<QueuedEvent> mQueuedEvents[2];
298
299 Vector<TextInputEvent> mTextInputEvents[2];
300 Vector<InputCommandType> mCommandEvents[2];
301 Vector<PointerEvent> mPointerDoubleClickEvents[2];
302 Vector<PointerEvent> mPointerReleasedEvents[2];
303 Vector<PointerEvent> mPointerPressedEvents[2];
304
305 Vector<ButtonEvent> mButtonDownEvents[2];
306 Vector<ButtonEvent> mButtonUpEvents[2];
307
308 // OS input events
309 HEvent mCharInputConn;
310 HEvent mCursorMovedConn;
311 HEvent mCursorPressedConn;
312 HEvent mCursorReleasedConn;
313 HEvent mCursorDoubleClickConn;
314 HEvent mInputCommandConn;
315 HEvent mMouseWheelScrolledConn;
316
317 // Raw input
318 bool mMouseSmoothingEnabled = false;
319 UINT64 mWindowHandle;
320
321 Mouse* mMouse = nullptr;
322 Keyboard* mKeyboard = nullptr;
323 Vector<Gamepad*> mGamepads;
324
325 float mTotalMouseSamplingTime[2];
326 UINT32 mTotalMouseNumSamples[2];
327 float mMouseZeroTime[2];
328 INT32 mMouseSampleAccumulator[2];
329 float mMouseSmoothedAxis[2];
330 UINT64 mLastMouseUpdateFrame;
331
332 UINT64 mTimestampClockOffset;
333
334 InputPrivateData* mPlatformData;
335
336 /************************************************************************/
337 /* STATICS */
338 /************************************************************************/
339 static const int HISTORY_BUFFER_SIZE; // Size of buffer used for input smoothing
340 static const float WEIGHT_MODIFIER;
341 };
342
343 /** Provides global access to Input. */
344 BS_CORE_EXPORT Input& gInput();
345
346 /** @} */
347}
348