1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #pragma once
24 
25 #include "../Container/HashSet.h"
26 #include "../Core/Mutex.h"
27 #include "../Core/Object.h"
28 #include "../Container/List.h"
29 #include "../Input/InputEvents.h"
30 #include "../UI/Cursor.h"
31 
32 namespace Urho3D
33 {
34 
35 /// %Input Mouse Modes.
36 enum MouseMode
37 {
38     MM_ABSOLUTE = 0,
39     MM_RELATIVE,
40     MM_WRAP,
41     MM_FREE,
42     MM_INVALID
43 };
44 
45 class Deserializer;
46 class Graphics;
47 class Serializer;
48 class UIElement;
49 class XMLFile;
50 
51 const IntVector2 MOUSE_POSITION_OFFSCREEN = IntVector2(M_MIN_INT, M_MIN_INT);
52 
53 /// %Input state for a finger touch.
54 struct TouchState
55 {
56     /// Return last touched UI element, used by scripting integration.
57     UIElement* GetTouchedElement();
58 
59     /// Touch (finger) ID.
60     int touchID_;
61     /// Position in screen coordinates.
62     IntVector2 position_;
63     /// Last position in screen coordinates.
64     IntVector2 lastPosition_;
65     /// Movement since last frame.
66     IntVector2 delta_;
67     /// Finger pressure.
68     float pressure_;
69     /// Last touched UI element from screen joystick.
70     WeakPtr<UIElement> touchedElement_;
71 };
72 
73 /// %Input state for a joystick.
74 struct JoystickState
75 {
76     /// Construct with defaults.
JoystickStateJoystickState77     JoystickState() :
78         joystick_(0), controller_(0), screenJoystick_(0)
79     {
80     }
81 
82     /// Initialize the number of buttons, axes and hats and set them to neutral state.
83     void Initialize(unsigned numButtons, unsigned numAxes, unsigned numHats);
84     /// Reset button, axis and hat states to neutral.
85     void Reset();
86 
87     /// Return whether is a game controller. Game controllers will use standardized axis and button mappings.
IsControllerJoystickState88     bool IsController() const { return controller_ != 0; }
89 
90     /// Return number of buttons.
GetNumButtonsJoystickState91     unsigned GetNumButtons() const { return buttons_.Size(); }
92 
93     /// Return number of axes.
GetNumAxesJoystickState94     unsigned GetNumAxes() const { return axes_.Size(); }
95 
96     /// Return number of hats.
GetNumHatsJoystickState97     unsigned GetNumHats() const { return hats_.Size(); }
98 
99     /// Check if a button is held down.
GetButtonDownJoystickState100     bool GetButtonDown(unsigned index) const { return index < buttons_.Size() ? buttons_[index] : false; }
101 
102     /// Check if a button has been pressed on this frame.
GetButtonPressJoystickState103     bool GetButtonPress(unsigned index) const { return index < buttonPress_.Size() ? buttonPress_[index] : false; }
104 
105     /// Return axis position.
GetAxisPositionJoystickState106     float GetAxisPosition(unsigned index) const { return index < axes_.Size() ? axes_[index] : 0.0f; }
107 
108     /// Return hat position.
GetHatPositionJoystickState109     int GetHatPosition(unsigned index) const { return index < hats_.Size() ? hats_[index] : HAT_CENTER; }
110 
111     /// SDL joystick.
112     SDL_Joystick* joystick_;
113     /// SDL joystick instance ID.
114     SDL_JoystickID joystickID_;
115     /// SDL game controller.
116     SDL_GameController* controller_;
117     /// UI element containing the screen joystick.
118     UIElement* screenJoystick_;
119     /// Joystick name.
120     String name_;
121     /// Button up/down state.
122     PODVector<bool> buttons_;
123     /// Button pressed on this frame.
124     PODVector<bool> buttonPress_;
125     /// Axis position from -1 to 1.
126     PODVector<float> axes_;
127     /// POV hat bits.
128     PODVector<int> hats_;
129 };
130 
131 #ifdef __EMSCRIPTEN__
132 class EmscriptenInput;
133 #endif
134 
135 /// %Input subsystem. Converts operating system window messages to input state and events.
136 class URHO3D_API Input : public Object
137 {
138     URHO3D_OBJECT(Input, Object);
139 
140 #ifdef __EMSCRIPTEN__
141     friend class EmscriptenInput;
142 #endif
143 
144 public:
145     /// Construct.
146     Input(Context* context);
147     /// Destruct.
148     virtual ~Input();
149 
150     /// Poll for window messages. Called by HandleBeginFrame().
151     void Update();
152     /// Set whether ALT-ENTER fullscreen toggle is enabled.
153     void SetToggleFullscreen(bool enable);
154     /// Set whether the operating system mouse cursor is visible. When not visible (default), is kept centered to prevent leaving the window. Mouse visibility event can be suppressed-- this also recalls any unsuppressed SetMouseVisible which can be returned by ResetMouseVisible().
155     void SetMouseVisible(bool enable, bool suppressEvent = false);
156     /// Reset last mouse visibility that was not suppressed in SetMouseVisible.
157     void ResetMouseVisible();
158     /// Set whether the mouse is currently being grabbed by an operation.
159     void SetMouseGrabbed(bool grab, bool suppressEvent = false);
160     /// Reset the mouse grabbed to the last unsuppressed SetMouseGrabbed call
161     void ResetMouseGrabbed();
162     /// Set the mouse mode.
163     /** Set the mouse mode behaviour.
164      *  MM_ABSOLUTE is the default behaviour, allowing the toggling of operating system cursor visibility and allowing the cursor to escape the window when visible.
165      *  When the operating system cursor is invisible in absolute mouse mode, the mouse is confined to the window.
166      *  If the operating system and UI cursors are both invisible, interaction with the Urho UI will be limited (eg: drag move / drag end events will not trigger).
167      *  SetMouseMode(MM_ABSOLUTE) will call SetMouseGrabbed(false).
168      *
169      *  MM_RELATIVE sets the operating system cursor to invisible and confines the cursor to the window.
170      *  The operating system cursor cannot be set to be visible in this mode via SetMouseVisible(), however changes are tracked and will be restored when another mouse mode is set.
171      *  When the virtual cursor is also invisible, UI interaction will still function as normal (eg: drag events will trigger).
172      *  SetMouseMode(MM_RELATIVE) will call SetMouseGrabbed(true).
173      *
174      *  MM_WRAP grabs the mouse from the operating system and confines the operating system cursor to the window, wrapping the cursor when it is near the edges.
175      *  SetMouseMode(MM_WRAP) will call SetMouseGrabbed(true).
176      *
177      *  MM_FREE does not grab/confine the mouse cursor even when it is hidden. This can be used for cases where the cursor should render using the operating system
178      *  outside the window, and perform custom rendering (with SetMouseVisible(false)) inside.
179     */
180     void SetMouseMode(MouseMode mode, bool suppressEvent = false);
181     /// Reset the last mouse mode that wasn't suppressed in SetMouseMode
182     void ResetMouseMode();
183     /// Add screen joystick.
184     /** Return the joystick instance ID when successful or negative on error.
185      *  If layout file is not given, use the default screen joystick layout.
186      *  If style file is not given, use the default style file from root UI element.
187      *
188      *  This method should only be called in main thread.
189      */
190     SDL_JoystickID AddScreenJoystick(XMLFile* layoutFile = 0, XMLFile* styleFile = 0);
191     /// Remove screen joystick by instance ID.
192     /** Return true if successful.
193      *
194      *  This method should only be called in main thread.
195      */
196     bool RemoveScreenJoystick(SDL_JoystickID id);
197     /// Set whether the virtual joystick is visible.
198     void SetScreenJoystickVisible(SDL_JoystickID id, bool enable);
199     /// Show or hide on-screen keyboard on platforms that support it. When shown, keypresses from it are delivered as key events.
200     void SetScreenKeyboardVisible(bool enable);
201     /// Set touch emulation by mouse. Only available on desktop platforms. When enabled, actual mouse events are no longer sent and the mouse cursor is forced visible.
202     void SetTouchEmulation(bool enable);
203     /// Begin recording a touch gesture. Return true if successful. The E_GESTURERECORDED event (which contains the ID for the new gesture) will be sent when recording finishes.
204     bool RecordGesture();
205     /// Save all in-memory touch gestures. Return true if successful.
206     bool SaveGestures(Serializer& dest);
207     /// Save a specific in-memory touch gesture to a file. Return true if successful.
208     bool SaveGesture(Serializer& dest, unsigned gestureID);
209     /// Load touch gestures from a file. Return number of loaded gestures, or 0 on failure.
210     unsigned LoadGestures(Deserializer& source);
211     /// Remove an in-memory gesture by ID. Return true if was found.
212     bool RemoveGesture(unsigned gestureID);
213     /// Remove all in-memory gestures.
214     void RemoveAllGestures();
215     /// Set the mouse cursor position. Uses the backbuffer (Graphics width/height) coordinates.
216     void SetMousePosition(const IntVector2& position);
217     /// Center the mouse position.
218     void CenterMousePosition();
219 
220     /// Return keycode from key name.
221     int GetKeyFromName(const String& name) const;
222     /// Return keycode from scancode.
223     int GetKeyFromScancode(int scancode) const;
224     /// Return name of key from keycode.
225     String GetKeyName(int key) const;
226     /// Return scancode from keycode.
227     int GetScancodeFromKey(int key) const;
228     /// Return scancode from key name.
229     int GetScancodeFromName(const String& name) const;
230     /// Return name of key from scancode.
231     String GetScancodeName(int scancode) const;
232     /// Check if a key is held down.
233     bool GetKeyDown(int key) const;
234     /// Check if a key has been pressed on this frame.
235     bool GetKeyPress(int key) const;
236     /// Check if a key is held down by scancode.
237     bool GetScancodeDown(int scancode) const;
238     /// Check if a key has been pressed on this frame by scancode.
239     bool GetScancodePress(int scancode) const;
240     /// Check if a mouse button is held down.
241     bool GetMouseButtonDown(int button) const;
242     /// Check if a mouse button has been pressed on this frame.
243     bool GetMouseButtonPress(int button) const;
244     /// Check if a qualifier key is held down.
245     bool GetQualifierDown(int qualifier) const;
246     /// Check if a qualifier key has been pressed on this frame.
247     bool GetQualifierPress(int qualifier) const;
248     /// Return the currently held down qualifiers.
249     int GetQualifiers() const;
250     /// Return mouse position within window. Should only be used with a visible mouse cursor. Uses the backbuffer (Graphics width/height) coordinates.
251     IntVector2 GetMousePosition() const;
252     /// Return mouse movement since last frame.
253     IntVector2 GetMouseMove() const;
254     /// Return horizontal mouse movement since last frame.
255     int GetMouseMoveX() const;
256     /// Return vertical mouse movement since last frame.
257     int GetMouseMoveY() const;
258     /// Return mouse wheel movement since last frame.
GetMouseMoveWheel()259     int GetMouseMoveWheel() const { return mouseMoveWheel_; }
260     /// Return input coordinate scaling. Should return non-unity on High DPI display.
GetInputScale()261     Vector2 GetInputScale() const { return inputScale_; }
262 
263     /// Return number of active finger touches.
GetNumTouches()264     unsigned GetNumTouches() const { return touches_.Size(); }
265     /// Return active finger touch by index.
266     TouchState* GetTouch(unsigned index) const;
267 
268     /// Return number of connected joysticks.
GetNumJoysticks()269     unsigned GetNumJoysticks() const { return joysticks_.Size(); }
270     /// Return joystick state by ID, or null if does not exist.
271     JoystickState* GetJoystick(SDL_JoystickID id);
272     /// Return joystick state by index, or null if does not exist. 0 = first connected joystick.
273     JoystickState* GetJoystickByIndex(unsigned index);
274     /// Return joystick state by name, or null if does not exist.
275     JoystickState* GetJoystickByName(const String& name);
276 
277     /// Return whether fullscreen toggle is enabled.
GetToggleFullscreen()278     bool GetToggleFullscreen() const { return toggleFullscreen_; }
279 
280     /// Return whether a virtual joystick is visible.
281     bool IsScreenJoystickVisible(SDL_JoystickID id) const;
282     /// Return whether on-screen keyboard is supported.
283     bool GetScreenKeyboardSupport() const;
284     /// Return whether on-screen keyboard is being shown.
285     bool IsScreenKeyboardVisible() const;
286 
287     /// Return whether touch emulation is enabled.
GetTouchEmulation()288     bool GetTouchEmulation() const { return touchEmulation_; }
289 
290     /// Return whether the operating system mouse cursor is visible.
IsMouseVisible()291     bool IsMouseVisible() const { return mouseVisible_; }
292     /// Return whether the mouse is currently being grabbed by an operation.
IsMouseGrabbed()293     bool IsMouseGrabbed() const { return mouseGrabbed_; }
294     /// Return whether the mouse is locked to the window
295     bool IsMouseLocked() const;
296 
297     /// Return the mouse mode.
GetMouseMode()298     MouseMode GetMouseMode() const { return mouseMode_; }
299 
300     /// Return whether application window has input focus.
HasFocus()301     bool HasFocus() { return inputFocus_; }
302 
303     /// Return whether application window is minimized.
304     bool IsMinimized() const;
305 
306 private:
307     /// Initialize when screen mode initially set.
308     void Initialize();
309     /// Open a joystick and return its ID. Return -1 if no joystick.
310     SDL_JoystickID OpenJoystick(unsigned index);
311     /// Setup internal joystick structures.
312     void ResetJoysticks();
313     /// Prepare input state for application gaining input focus.
314     void GainFocus();
315     /// Prepare input state for application losing input focus.
316     void LoseFocus();
317     /// Clear input state.
318     void ResetState();
319     /// Clear touch states and send touch end events.
320     void ResetTouches();
321     /// Reset input accumulation.
322     void ResetInputAccumulation();
323     /// Get the index of a touch based on the touch ID.
324     unsigned GetTouchIndexFromID(int touchID);
325     /// Used internally to return and remove the next available touch index.
326     unsigned PopTouchIndex();
327     /// Push a touch index back into the list of available when finished with it.
328     void PushTouchIndex(int touchID);
329     /// Send an input focus or window minimization change event.
330     void SendInputFocusEvent();
331     /// Handle a mouse button change.
332     void SetMouseButton(int button, bool newState);
333     /// Handle a key change.
334     void SetKey(int key, int scancode, bool newState);
335     /// Handle mouse wheel change.
336     void SetMouseWheel(int delta);
337     /// Suppress next mouse movement.
338     void SuppressNextMouseMove();
339     /// Unsuppress mouse movement.
340     void UnsuppressMouseMove();
341     /// Handle screen mode event.
342     void HandleScreenMode(StringHash eventType, VariantMap& eventData);
343     /// Handle frame start event.
344     void HandleBeginFrame(StringHash eventType, VariantMap& eventData);
345     /// Handle touch events from the controls of screen joystick(s).
346     void HandleScreenJoystickTouch(StringHash eventType, VariantMap& eventData);
347     /// Handle SDL event.
348     void HandleSDLEvent(void* sdlEvent);
349 
350 #ifndef __EMSCRIPTEN__
351     /// Set SDL mouse mode relative.
352     void SetMouseModeRelative(SDL_bool enable);
353     /// Set SDL mouse mode absolute.
354     void SetMouseModeAbsolute(SDL_bool enable);
355 #else
356     /// Set whether the operating system mouse cursor is visible (Emscripten platform only).
357     void SetMouseVisibleEmscripten(bool enable, bool suppressEvent = false);
358     /// Set mouse mode final resolution (Emscripten platform only).
359     void SetMouseModeEmscriptenFinal(MouseMode mode, bool suppressEvent = false);
360     /// SetMouseMode  (Emscripten platform only).
361     void SetMouseModeEmscripten(MouseMode mode, bool suppressEvent);
362     /// Handle frame end event.
363     void HandleEndFrame(StringHash eventType, VariantMap& eventData);
364 #endif
365 
366     /// Graphics subsystem.
367     WeakPtr<Graphics> graphics_;
368     /// Key down state.
369     HashSet<int> keyDown_;
370     /// Key pressed state.
371     HashSet<int> keyPress_;
372     /// Key down state by scancode.
373     HashSet<int> scancodeDown_;
374     /// Key pressed state by scancode.
375     HashSet<int> scancodePress_;
376     /// Active finger touches.
377     HashMap<int, TouchState> touches_;
378     /// List that maps between event touch IDs and normalised touch IDs
379     List<int> availableTouchIDs_;
380     /// Mapping of touch indices
381     HashMap<int, int> touchIDMap_;
382     /// String for text input.
383     String textInput_;
384     /// Opened joysticks.
385     HashMap<SDL_JoystickID, JoystickState> joysticks_;
386     /// Mouse buttons' down state.
387     unsigned mouseButtonDown_;
388     /// Mouse buttons' pressed state.
389     unsigned mouseButtonPress_;
390     /// Last mouse position for calculating movement.
391     IntVector2 lastMousePosition_;
392     /// Last mouse position before being set to not visible.
393     IntVector2 lastVisibleMousePosition_;
394     /// Mouse movement since last frame.
395     IntVector2 mouseMove_;
396     /// Mouse wheel movement since last frame.
397     int mouseMoveWheel_;
398     /// Input coordinate scaling. Non-unity when window and backbuffer have different sizes (e.g. Retina display.)
399     Vector2 inputScale_;
400     /// SDL window ID.
401     unsigned windowID_;
402     /// Fullscreen toggle flag.
403     bool toggleFullscreen_;
404     /// Operating system mouse cursor visible flag.
405     bool mouseVisible_;
406     /// The last operating system mouse cursor visible flag set by end use call to SetMouseVisible.
407     bool lastMouseVisible_;
408     /// Flag to indicate the mouse is being grabbed by an operation. Subsystems like UI that uses mouse should temporarily ignore the mouse hover or click events.
409     bool mouseGrabbed_;
410     /// The last mouse grabbed set by SetMouseGrabbed.
411     bool lastMouseGrabbed_;
412     /// Determines the mode of mouse behaviour.
413     MouseMode mouseMode_;
414     /// The last mouse mode set by SetMouseMode.
415     MouseMode lastMouseMode_;
416 #ifndef __EMSCRIPTEN__
417     /// Flag to determine whether SDL mouse relative was used.
418     bool sdlMouseRelative_;
419 #endif
420     /// Touch emulation mode flag.
421     bool touchEmulation_;
422     /// Input focus flag.
423     bool inputFocus_;
424     /// Minimized flag.
425     bool minimized_;
426     /// Gained focus on this frame flag.
427     bool focusedThisFrame_;
428     /// Next mouse move suppress flag.
429     bool suppressNextMouseMove_;
430     /// Whether mouse move is accumulated in backbuffer scale or not (when using events directly).
431     bool mouseMoveScaled_;
432     /// Initialized flag.
433     bool initialized_;
434 
435 #ifdef __EMSCRIPTEN__
436     /// Emscripten Input glue instance.
437     UniquePtr<EmscriptenInput> emscriptenInput_;
438     /// Flag used to detect mouse jump when exiting pointer-lock.
439     bool emscriptenExitingPointerLock_;
440     /// Flag used to detect mouse jump on initial mouse click when entering pointer-lock.
441     bool emscriptenEnteredPointerLock_;
442     /// Flag indicating current pointer-lock status.
443     bool emscriptenPointerLock_;
444 #endif
445 };
446 
447 }
448