1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
|
#pragma once
#include "StarInputEvent.hpp"
#include "StarJson.hpp"
#include "StarListener.hpp"
#include "StarHash.hpp"
namespace Star {
STAR_CLASS(Input);
STAR_EXCEPTION(InputException, StarException);
typedef Variant<Key, MouseButton, ControllerButton> InputVariant;
template <>
struct hash<InputVariant> {
size_t operator()(InputVariant const& v) const;
};
class Input {
public:
static Json inputEventToJson(InputEvent const& event);
struct KeyBind {
Key key = Key::Zero;
KeyMod mods = KeyMod::NoMod;
uint8_t priority = 0;
inline bool operator<(KeyBind const& rhs) const {
return priority < rhs.priority;
}
inline bool operator>(KeyBind const& rhs) const {
return priority > rhs.priority;
}
};
struct MouseBind {
MouseButton button = MouseButton::Left;
KeyMod mods = KeyMod::NoMod;
uint8_t priority = 0;
};
struct ControllerBind {
unsigned int controller = 0;
ControllerButton button = ControllerButton::Invalid;
};
typedef MVariant<KeyBind, MouseBind, ControllerBind> Bind;
static Bind bindFromJson(Json const& json);
static Json bindToJson(Bind const& bind);
struct BindCategory;
struct BindEntry {
// The internal ID of this entry.
String id;
// The user-facing name of this entry.
String name;
// The category this entry belongs to.
BindCategory const* category;
// Associated string tags that become active when this bind is pressed.
StringList tags;
// The default binds.
List<Bind> defaultBinds;
// The user-configured binds.
List<Bind> customBinds;
BindEntry(String entryId, Json const& config, BindCategory const& parentCategory);
void updated();
};
struct BindRef {
KeyMod mods;
uint8_t priority = 0;
BindEntry* entry = nullptr; // Invalidated on reload, careful!
BindRef(BindEntry& bindEntry, KeyBind& keyBind);
BindRef(BindEntry& bindEntry, MouseBind& mouseBind);
BindRef(BindEntry& bindEntry);
};
struct BindCategory {
String id;
String name;
Json config;
StableHashMap<String, BindEntry> entries;
BindCategory(String categoryId, Json const& categoryConfig);
};
struct InputState {
unsigned presses = 0;
unsigned releases = 0;
bool pressed = false;
bool held = false;
bool released = false;
// Calls the passed functions for each press and release.
template <typename PressFunction, typename ReleaseFunction>
void forEach(PressFunction&& pressed, ReleaseFunction&& released) {
for (size_t i = 0; i != releases; ++i) {
pressed();
released();
}
}
inline void reset() {
presses = releases = 0;
pressed = released = false;
}
inline void press() { pressed = ++presses; held = true; }
inline void release() { released = ++releases; held = false; }
};
struct KeyInputState : InputState {
KeyMod mods = KeyMod::NoMod;
};
struct MouseInputState : InputState {
List<Vec2I> pressPositions;
List<Vec2I> releasePositions;
};
typedef InputState ControllerInputState;
// Get pointer to the singleton Input instance, if it exists. Otherwise,
// returns nullptr.
static Input* singletonPtr();
// Gets reference to Input singleton, throws InputException if root
// is not initialized.
static Input& singleton();
Input();
~Input();
Input(Input const&) = delete;
Input& operator=(Input const&) = delete;
List<std::pair<InputEvent, bool>> const& inputEventsThisFrame() const;
// Clears input state. Should be done at the very start or end of the client loop.
void reset(bool clear = false);
void update();
// Handles an input event.
bool handleInput(InputEvent const& input, bool gameProcessed);
void rebuildMappings();
// Loads input categories and their binds from Assets.
void reload();
void setTextInputActive(bool active);
Maybe<unsigned> bindDown(String const& categoryId, String const& bindId);
bool bindHeld(String const& categoryId, String const& bindId);
Maybe<unsigned> bindUp (String const& categoryId, String const& bindId);
Maybe<unsigned> keyDown(Key key, Maybe<KeyMod> keyMod);
bool keyHeld(Key key);
Maybe<unsigned> keyUp (Key key);
Maybe<List<Vec2I>> mouseDown(MouseButton button);
bool mouseHeld(MouseButton button);
Maybe<List<Vec2I>> mouseUp (MouseButton button);
Vec2I mousePosition() const;
void resetBinds(String const& categoryId, String const& bindId);
void setBinds(String const& categoryId, String const& bindId, Json const& binds);
Json getDefaultBinds(String const& categoryId, String const& bindId);
Json getBinds(String const& categoryId, String const& bindId);
unsigned getTag(String const& tagName) const;
class ClipboardUnlock {
public:
ClipboardUnlock(Input& input);
ClipboardUnlock(ClipboardUnlock const&) = delete;
ClipboardUnlock(ClipboardUnlock&&);
~ClipboardUnlock();
private:
Input* m_input;
};
ClipboardUnlock unlockClipboard();
bool clipboardAllowed() const;
private:
List<BindEntry*> filterBindEntries(List<BindRef> const& binds, KeyMod mods) const;
BindEntry* bindEntryPtr(String const& categoryId, String const& bindId);
BindEntry& bindEntry(String const& categoryId, String const& bindId);
InputState* bindStatePtr(String const& categoryId, String const& bindId);
InputState& addBindState(BindEntry const* bindEntry);
static Input* s_singleton;
// Regenerated on reload.
StableHashMap<String, BindCategory> m_bindCategories;
// Contains raw pointers to bind entries in categories, so also regenerated on reload.
HashMap<InputVariant, List<BindRef>> m_bindMappings;
ListenerPtr m_rootReloadListener;
// Per-frame input event storage for Lua.
List<std::pair<InputEvent, bool>> m_inputEvents;
// Per-frame input state maps.
//Input states
HashMap<Key, KeyInputState> m_keyStates;
HashMap<MouseButton, MouseInputState> m_mouseStates;
HashMap<ControllerButton, ControllerInputState> m_controllerStates;
//Bind states
HashMap<BindEntry const*, InputState> m_bindStates;
StringMap<unsigned> m_activeTags;
KeyMod m_pressedMods;
bool m_textInputActive;
Vec2I m_mousePosition;
unsigned m_clipboardAllowed = 0;
};
}
|