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