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 | |
10 | namespace 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 | |