1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details 2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt 3 4 #ifndef INPUT_H 5 #define INPUT_H 6 7 #include "InputBindings.h" 8 #include "utils.h" 9 10 #include <algorithm> 11 #include <array> 12 13 class IniConfig; 14 15 // Macro to simplify registering input bindings in the codebase 16 // TODO: evaluate if registering key bindings via lua / json file works better 17 #define REGISTER_INPUT_BINDING(name) \ 18 namespace name##Input \ 19 { \ 20 void Register(Input::Manager *input); \ 21 bool name##Registered = Input::AddBindingRegistrar(&Register); \ 22 } \ 23 void name##Input::Register(Input::Manager *input) 24 25 namespace Input { 26 class Manager; 27 28 // The Page->Group->Binding system serves as a thin veneer for the UI to make 29 // sane reasonings about how to structure the Options dialog. 30 struct BindingGroup { 31 enum EntryType : uint8_t { 32 ENTRY_ACTION, 33 ENTRY_AXIS 34 }; 35 36 std::map<std::string, EntryType> bindings; 37 }; 38 39 struct BindingPage { GetBindingGroupBindingPage40 BindingGroup *GetBindingGroup(std::string id) { return &groups[id]; } 41 42 std::map<std::string, BindingGroup> groups; 43 }; 44 45 struct InputFrame { 46 using Axis = InputBindings::Axis; 47 using Action = InputBindings::Action; 48 49 InputFrame(Input::Manager *man, bool modal = false) : managerInputFrame50 manager(man), 51 modal(modal) 52 {} 53 54 std::vector<Action *> actions; 55 std::vector<Axis *> axes; 56 57 // Must set this to a valid Input::Manager instance before using AddAction / AddAxis 58 Manager *manager = nullptr; 59 bool active = false; 60 bool modal = false; 61 62 // Call this at startup to register all the bindings associated with the frame. RegisterBindingsInputFrame63 virtual void RegisterBindings(){}; 64 65 // Called when the frame is added to the stack. 66 sigc::signal<void, InputFrame *> onFrameAdded; 67 68 // Called when the frame is removed from the stack. 69 sigc::signal<void, InputFrame *> onFrameRemoved; 70 71 Action *AddAction(std::string id); 72 Axis *AddAxis(std::string id); 73 }; 74 75 struct JoystickInfo { 76 struct Axis { 77 float value = 0.0; 78 float deadzone = 0.0; 79 float curve = 1.0; 80 bool zeroToOne = false; 81 }; 82 83 SDL_Joystick *joystick; 84 SDL_JoystickGUID guid; 85 std::string name; 86 87 std::vector<bool> buttons; 88 std::vector<int> hats; 89 90 std::vector<Axis> axes; 91 }; 92 93 void InitJoysticks(IniConfig *config); 94 std::map<SDL_JoystickID, JoystickInfo> &GetJoysticks(); 95 96 // User display name for the joystick from the API/OS. 97 std::string JoystickName(int joystick); 98 // fetch the GUID for the named joystick 99 SDL_JoystickGUID JoystickGUID(int joystick); 100 std::string JoystickGUIDString(int joystick); 101 102 // reverse map a JoystickGUID to the actual internal ID. 103 int JoystickFromGUIDString(const std::string &guid); 104 int JoystickFromGUIDString(const char *guid); 105 int JoystickFromGUID(SDL_JoystickGUID guid); 106 107 // We use SDL's joystick IDs because they're stable enough for the job. JoystickFromID(SDL_JoystickID id)108 inline int JoystickFromID(SDL_JoystickID id) { return id; } 109 110 // An adapter to decouple input frame creation from input binding registration. 111 // The functions registered via AddBindingRegistrar should be thread-safe and 112 // should not depend on anything but the manager object being passed in. 113 // The registrars are guaranteed to be called after static initialization has finished. 114 std::vector<sigc::slot<void, Input::Manager *>> &GetBindingRegistration(); 115 bool AddBindingRegistrar(sigc::slot<void, Input::Manager *> &&fn); 116 } // namespace Input 117 118 class Input::Manager { 119 public: 120 Manager(IniConfig *config, SDL_Window *window); 121 void InitGame(); 122 123 // Call this at the start of a frame, before passing SDL events in 124 void NewFrame(); 125 126 // Call once per SDL event, handles updating all internal state 127 void HandleSDLEvent(SDL_Event &ev); 128 129 // Call immediately after processing events, dispatches events to Action / Axis bindings. 130 void DispatchEvents(); 131 132 // When enable is false, this prevents the input system from writing to the config file. EnableConfigSaving(bool enable)133 void EnableConfigSaving(bool enable) { m_enableConfigSaving = enable; } 134 GetBindingPage(std::string id)135 BindingPage *GetBindingPage(std::string id) { return &bindingPages[id]; } GetBindingPages()136 std::map<std::string, BindingPage> GetBindingPages() { return bindingPages; } 137 138 // Pushes an InputFrame onto the input stack. 139 bool AddInputFrame(InputFrame *frame); 140 141 // Get a read-only list of input frames. GetInputFrames()142 const std::vector<InputFrame *> &GetInputFrames() { return m_inputFrames; } 143 144 // Check if a specific input frame is currently on the stack. HasInputFrame(InputFrame * frame)145 bool HasInputFrame(InputFrame *frame) 146 { 147 return std::count(m_inputFrames.begin(), m_inputFrames.end(), frame) > 0; 148 } 149 150 // Remove an arbitrary input frame from the input stack. 151 void RemoveInputFrame(InputFrame *frame); 152 153 // Inform the input system that a binding or frame was changed this frame. MarkBindingsDirty()154 void MarkBindingsDirty() { m_frameListChanged = true; } 155 156 // Creates a new action binding, copying the provided binding. 157 // The returned binding pointer points to the actual binding. 158 InputBindings::Action *AddActionBinding(std::string id, BindingGroup *group, InputBindings::Action &&binding); 159 InputBindings::Action *GetActionBinding(std::string id); 160 161 // Creates a new axis binding, copying the provided binding. 162 // The returned binding pointer points to the actual binding. 163 InputBindings::Axis *AddAxisBinding(std::string id, BindingGroup *group, InputBindings::Axis &&binding); 164 InputBindings::Axis *GetAxisBinding(std::string id); 165 166 // Call EnableBindings() to temporarily disable handling input bindings while 167 // you're recording a new input binding or are in a modal window. EnableBindings(bool enabled)168 void EnableBindings(bool enabled) { m_enableBindings = enabled; } 169 KeyState(SDL_Keycode k)170 bool KeyState(SDL_Keycode k) { return IsKeyDown(k); } 171 172 // returns true if key K is currently pressed IsKeyDown(SDL_Keycode k)173 bool IsKeyDown(SDL_Keycode k) { return keyState[k] & 0x3; } 174 175 // returns true if key K was pressed this frame IsKeyPressed(SDL_Keycode k)176 bool IsKeyPressed(SDL_Keycode k) { return keyState[k] == 1; } 177 178 // returns true if key K was released this frame IsKeyReleased(SDL_Keycode k)179 bool IsKeyReleased(SDL_Keycode k) { return keyState[k] == 4; } 180 KeyModState()181 int KeyModState() { return keyModState; } 182 183 int JoystickButtonState(int joystick, int button); 184 int JoystickHatState(int joystick, int hat); 185 float JoystickAxisState(int joystick, int axis); 186 IsJoystickEnabled()187 bool IsJoystickEnabled() { return joystickEnabled; } 188 void SetJoystickEnabled(bool state); 189 IsMouseYInvert()190 bool IsMouseYInvert() { return mouseYInvert; } 191 void SetMouseYInvert(bool state); 192 MouseButtonState(int button)193 int MouseButtonState(int button) { return mouseButton[button]; } SetMouseButtonState(int button,bool state)194 void SetMouseButtonState(int button, bool state) { mouseButton[button] = state; } 195 GetMouseMotion(int motion[2])196 void GetMouseMotion(int motion[2]) 197 { 198 std::copy_n(mouseMotion.data(), mouseMotion.size(), motion); 199 } 200 GetMouseWheel()201 int GetMouseWheel() { return mouseWheel; } 202 203 // Capturing the mouse hides the cursor, puts the mouse into relative mode, 204 // and passes all mouse inputs to the input system, regardless of whether 205 // ImGui is using them or not. IsCapturingMouse()206 bool IsCapturingMouse() const { return m_capturingMouse; } 207 208 // Set whether the application would like to capture the mouse. 209 // To avoid contention between different classes, please only call this when the state 210 // has actually changed. 211 void SetCapturingMouse(bool enabled); 212 213 sigc::signal<void, SDL_Keysym *> onKeyPress; 214 sigc::signal<void, SDL_Keysym *> onKeyRelease; 215 sigc::signal<void, int, int, int> onMouseButtonUp; 216 sigc::signal<void, int, int, int> onMouseButtonDown; 217 sigc::signal<void, bool> onMouseWheel; 218 219 private: 220 void RebuildInputFrames(); 221 bool GetModifierState(InputBindings::KeyChord *key); 222 bool GetBindingState(InputBindings::KeyBinding &key); 223 float GetAxisState(InputBindings::JoyAxis &axis); 224 225 SDL_Window *m_window; 226 IniConfig *m_config; 227 bool m_enableConfigSaving; 228 229 std::map<SDL_Keycode, uint8_t> keyState; 230 int keyModState; 231 std::array<char, 6> mouseButton; 232 std::array<int, 2> mouseMotion; 233 int mouseWheel; 234 bool m_capturingMouse; 235 236 bool joystickEnabled; 237 bool mouseYInvert; 238 239 std::map<std::string, BindingPage> bindingPages; 240 std::map<std::string, InputBindings::Action> actionBindings; 241 std::map<std::string, InputBindings::Axis> axisBindings; 242 bool m_enableBindings; 243 244 std::vector<InputFrame *> m_inputFrames; 245 bool m_frameListChanged; 246 247 std::vector<InputBindings::Action *> m_activeActions; 248 std::vector<InputBindings::Axis *> m_activeAxes; 249 250 std::map<InputBindings::KeyBinding, bool> m_modifiers; 251 std::vector<InputBindings::KeyChord *> m_chords; 252 }; 253 254 #endif 255