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