1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // Keyboard input mapping to engine functions
17 
18 #ifndef INC_C4KeyboardInput
19 #define INC_C4KeyboardInput
20 
21 // key context classifications
22 enum C4KeyScope
23 {
24 	KEYSCOPE_None       = 0,
25 	KEYSCOPE_Control    = 1,  // player control (e.g. NUM1 to move left on keypad control)
26 	KEYSCOPE_Gui        = 2,  // keys used to manipulate GUI elements (e.g. Tab to cycle through controls)
27 	KEYSCOPE_Fullscreen = 4,  // generic fullscreen-only keys (e.g. F9 for screenshot)
28 	KEYSCOPE_Console    = 8,  // generic console-mode only keys (e.g. Space to switch edit cursor)
29 	KEYSCOPE_Generic    = 16, // generic keys available in fullscreen and console mode outside GUI (e.g. F1 for music on/off)
30 	KEYSCOPE_FullSMenu  = 32, // fullscreen menu control. If fullscreen menu is active, this disables viewport controls (e.g. Return to close player join menu)
31 	KEYSCOPE_FilmView   = 64, // ownerless viewport scrolling in film mode, player switching, etc. (e.g. Enter to switch to next player)
32 	KEYSCOPE_FreeView   = 128, // ownerless viewport scrolling, player switching, etc. (e.g. arrow left to scroll left in view)
33 	KEYSCOPE_FullSView  = 256 // player fullscreen viewport
34 };
35 
36 // what can happen to keys
37 enum C4KeyEventType
38 {
39 	KEYEV_None    =  0, // no event
40 	KEYEV_Down    =  1, // in response to WM_KEYDOWN or joypad button pressed
41 	KEYEV_Up      =  2, // in response to WM_KEYUP or joypad button released
42 	KEYEV_Pressed =  3, // in response to WM_KEYPRESSED
43 	KEYEV_Moved   =  4, // when moving a gamepad stick
44 };
45 
46 // keyboard code
47 typedef unsigned long C4KeyCode;
48 
49 // Gamepad codes (KEY_CONTROLLER_*): Masked as 0x420000; bit 8-15 used for gamepad index
50 const C4KeyCode KEY_CONTROLLER_Mask = 0x420000;
51 
52 // Mouse codes (KEY_MOUSE_*): Masked as 0x430000; bit 8-15 used for mouse index
53 const C4KeyCode KEY_MOUSE_Mask = 0x430000;
54 
55 const C4KeyCode
56 	KEY_Default                  = 0,  // no key
57 	KEY_Any                      = ~0, // used for default key processing
58 	KEY_Undefined                = (~0)^1, // used to indicate an unknown key
59 	KEY_CONTROLLER_ButtonMin           = 0x10, // first button
60 	KEY_CONTROLLER_ButtonA             = 0x10,
61 	KEY_CONTROLLER_ButtonB             = 0x11,
62 	KEY_CONTROLLER_ButtonX             = 0x12,
63 	KEY_CONTROLLER_ButtonY             = 0x13,
64 	KEY_CONTROLLER_ButtonBack          = 0x14,
65 	KEY_CONTROLLER_ButtonGuide         = 0x15,
66 	KEY_CONTROLLER_ButtonStart         = 0x16,
67 	KEY_CONTROLLER_ButtonLeftStick     = 0x17,
68 	KEY_CONTROLLER_ButtonRightStick    = 0x18,
69 	KEY_CONTROLLER_ButtonLeftShoulder  = 0x19,
70 	KEY_CONTROLLER_ButtonRightShoulder = 0x1a,
71 	KEY_CONTROLLER_ButtonDpadUp        = 0x1b,
72 	KEY_CONTROLLER_ButtonDpadDown      = 0x1c,
73 	KEY_CONTROLLER_ButtonDpadLeft      = 0x1d,
74 	KEY_CONTROLLER_ButtonDpadRight     = 0x1e,
75 	KEY_CONTROLLER_ButtonMax           = 0x1e, // last button
76 	KEY_CONTROLLER_AnyButton           = 0xff, // any of the buttons above
77 	KEY_CONTROLLER_AxisMin             = 0x30, // first axis
78 	KEY_CONTROLLER_AxisLeftXLeft       = 0x30,
79 	KEY_CONTROLLER_AxisLeftXRight      = 0x31,
80 	KEY_CONTROLLER_AxisLeftYUp         = 0x32,
81 	KEY_CONTROLLER_AxisLeftYDown       = 0x33,
82 	KEY_CONTROLLER_AxisRightXLeft      = 0x34,
83 	KEY_CONTROLLER_AxisRightXRight     = 0x35,
84 	KEY_CONTROLLER_AxisRightYUp        = 0x36,
85 	KEY_CONTROLLER_AxisRightYDown      = 0x37,
86 	KEY_CONTROLLER_AxisTriggerLeft     = 0x39, // triggers are only positive
87 	KEY_CONTROLLER_AxisTriggerRight    = 0x3b,
88 	KEY_CONTROLLER_AxisMax             = 0x3b, // last axis
89 	KEY_MOUSE_Move               = 1,    // mouse control: mouse movement
90 	KEY_MOUSE_Button1            = 0x10, // key index of mouse buttons + button index for more buttons
91 	KEY_MOUSE_ButtonLeft         = KEY_MOUSE_Button1 + 0,
92 	KEY_MOUSE_ButtonRight        = KEY_MOUSE_Button1 + 1,
93 	KEY_MOUSE_ButtonMiddle       = KEY_MOUSE_Button1 + 2,
94 	KEY_MOUSE_ButtonX1           = KEY_MOUSE_Button1 + 3,
95 	KEY_MOUSE_ButtonX2           = KEY_MOUSE_Button1 + 4,
96 	KEY_MOUSE_ButtonMax          = KEY_MOUSE_Button1 + 0x1f, // max number of supported mouse buttons
97 	KEY_MOUSE_Button1Double      = 0x30, // double clicks have special events because double click speed is issued by OS
98 	KEY_MOUSE_ButtonLeftDouble   = KEY_MOUSE_Button1Double + 0,
99 	KEY_MOUSE_ButtonRightDouble  = KEY_MOUSE_Button1Double + 1,
100 	KEY_MOUSE_ButtonMiddleDouble = KEY_MOUSE_Button1Double + 2,
101 	KEY_MOUSE_ButtonX1Double     = KEY_MOUSE_Button1Double + 3,
102 	KEY_MOUSE_ButtonX2Double     = KEY_MOUSE_Button1Double + 4,
103 	KEY_MOUSE_ButtonMaxDouble    = KEY_MOUSE_Button1Double + 0x1f, // max number of supported mouse buttons
104 	KEY_MOUSE_Wheel1Up           = 0x40,    // mouse control: wheel up
105 	KEY_MOUSE_Wheel1Down         = 0x41;    // mouse control: wheel down
106 
KEY_CONTROLLER_Button(uint8_t idx)107 inline uint8_t KEY_CONTROLLER_Button(uint8_t idx) { return KEY_CONTROLLER_ButtonMin+idx; }
KEY_CONTROLLER_Axis(uint8_t idx,bool fMax)108 inline uint8_t KEY_CONTROLLER_Axis(uint8_t idx, bool fMax) { return KEY_CONTROLLER_AxisMin+2*idx+fMax; }
109 
KEY_Gamepad(uint8_t idButton)110 inline C4KeyCode KEY_Gamepad(uint8_t idButton) // convert gamepad key to Clonk-gamepad-keycode
111 {
112 	// mask key as 0x004200bb, where 00 used to be the gamepad ID and bb is button ID.
113 	return KEY_CONTROLLER_Mask + idButton;
114 }
115 
Key_IsGamepad(C4KeyCode key)116 inline bool Key_IsGamepad(C4KeyCode key)
117 {
118 	return (0xff0000 & key) == KEY_CONTROLLER_Mask;
119 }
120 
Key_GetGamepad(C4KeyCode key)121 inline uint8_t Key_GetGamepad(C4KeyCode key)
122 {
123 	return ((uint32_t)key >> 8) & 0xff;
124 }
125 
Key_GetGamepadEvent(C4KeyCode key)126 inline uint8_t Key_GetGamepadEvent(C4KeyCode key)
127 {
128 	return ((uint32_t)key) & 0xff;
129 }
130 
Key_IsGamepadButton(C4KeyCode key)131 inline bool Key_IsGamepadButton(C4KeyCode key)
132 {
133 	// whether this is a unique button event (AnyButton not included)
134 	return Key_IsGamepad(key) && Inside<uint8_t>(Key_GetGamepadEvent(key), KEY_CONTROLLER_ButtonMin, KEY_CONTROLLER_ButtonMax);
135 }
136 
Key_IsGamepadAxis(C4KeyCode key)137 inline bool Key_IsGamepadAxis(C4KeyCode key)
138 {
139 	// whether this is a unique button event (AnyButton not included)
140 	return Key_IsGamepad(key) && Inside<uint8_t>(Key_GetGamepadEvent(key), KEY_CONTROLLER_AxisMin, KEY_CONTROLLER_AxisMax);
141 }
142 
Key_GetGamepadButtonIndex(C4KeyCode key)143 inline uint8_t Key_GetGamepadButtonIndex(C4KeyCode key)
144 {
145 	// get zero-based button index
146 	return Key_GetGamepadEvent(key) - KEY_CONTROLLER_ButtonMin;
147 }
148 
Key_GetGamepadAxisIndex(C4KeyCode key)149 inline uint8_t Key_GetGamepadAxisIndex(C4KeyCode key)
150 {
151 	// get zero-based axis index
152 	return (Key_GetGamepadEvent(key) - KEY_CONTROLLER_AxisMin) / 2;
153 }
154 
Key_IsGamepadAxisHigh(C4KeyCode key)155 inline bool Key_IsGamepadAxisHigh(C4KeyCode key)
156 {
157 	return !!(key & 1);
158 }
159 
KEY_Mouse(uint8_t mouse_id,uint8_t mouseevent)160 inline C4KeyCode KEY_Mouse(uint8_t mouse_id, uint8_t mouseevent)
161 {
162 	// mask key as 0x0043ggbb, where mm is mouse ID and bb is mouse event ID.
163 	return KEY_MOUSE_Mask + (mouse_id<<8) + mouseevent;
164 }
165 
Key_IsMouse(C4KeyCode key)166 inline bool Key_IsMouse(C4KeyCode key)
167 {
168 	return (0xff0000 & key) == KEY_MOUSE_Mask;
169 }
170 
Key_GetMouse(C4KeyCode key)171 inline uint8_t Key_GetMouse(C4KeyCode key)
172 {
173 	return ((uint32_t)key >> 8) & 0xff;
174 }
175 
Key_GetMouseEvent(C4KeyCode key)176 inline uint8_t Key_GetMouseEvent(C4KeyCode key)
177 {
178 	return ((uint32_t)key) & uint8_t(0xff);
179 }
180 
181 bool KEY_IsModifier(C4KeyCode k);
182 
183 #ifdef _WIN32
184 #define TOUPPERIFX11(key) (key)
185 #else
186 #define TOUPPERIFX11(key) toupper(key)
187 #endif
188 
189 enum C4KeyShiftState
190 {
191 	KEYS_None    =  0,
192 	KEYS_First   =  1,
193 	KEYS_Alt     =  1,
194 	KEYS_Control =  2,
195 	KEYS_Shift   =  4,
196 	KEYS_Max     = KEYS_Shift,
197 	KEYS_Undefined = 0xffff
198 };
199 
200 // extended key information containing shift state
201 struct C4KeyCodeEx
202 {
203 	C4KeyCode Key; // the key
204 	DWORD dwShift; // the status of Alt, Shift, Control
205 
206 	int32_t deviceId;
207 
208 	// if set, the keycode was generated by a key that has been held down
209 	// this flag is ignored in comparison operations
210 	bool fRepeated;
211 
212 	// helpers
213 	static C4KeyShiftState String2KeyShift(const StdStrBuf &sName);
214 	static C4KeyCode String2KeyCode(const StdStrBuf &sName);
215 	static StdStrBuf KeyCode2String(C4KeyCode wCode, bool fHumanReadable, bool fShort);
216 	StdStrBuf ToString(bool fHumanReadable, bool fShort) const;
217 	static StdStrBuf KeyShift2String(C4KeyShiftState eShift);
218 
219 	// comparison operator for map access
220 	inline bool operator <(const C4KeyCodeEx &v2) const
221 	{
222 		return Key < v2.Key || (Key == v2.Key && dwShift < v2.dwShift);
223 	}
224 
225 	inline bool operator ==(const C4KeyCodeEx &v2) const
226 	{
227 		return Key == v2.Key && dwShift == v2.dwShift;
228 	}
229 
230 	void CompileFunc(StdCompiler *pComp, StdStrBuf *pOutBuf=nullptr);
231 
232 	C4KeyCodeEx(C4KeyCode Key = KEY_Default, DWORD Shift = KEYS_None, bool fIsRepeated = false, int32_t deviceId = -1);
233 	static C4KeyCodeEx FromC4MC(int8_t mouse_id, int32_t button, DWORD param, bool * is_down = nullptr);
234 
IsRepeatedC4KeyCodeEx235 	bool IsRepeated() const { return fRepeated; }
236 
237 	void FixShiftKeys(); // reduce stuff like Ctrl+RightCtrl to simply RightCtrl
238 private:
239 	static C4KeyCode GetKeyByScanCode(const char *scan_code);
240 };
241 
242 // extra data associated with a key event
243 struct C4KeyEventData
244 {
245 	enum { KeyPos_None = 0x7fffff }; // value used to denote invalid/none key positions
246 	int32_t iStrength{0}; // pressure between 0 and 100 (100 for nomal keypress)
247 	int32_t game_x{KeyPos_None},game_y{KeyPos_None}, vp_x{KeyPos_None},vp_y{KeyPos_None};       // position for mouse event, landscape+viewport coordinates
248 	C4KeyEventData() = default;
C4KeyEventDataC4KeyEventData249 	C4KeyEventData(int32_t iStrength, int32_t game_x, int32_t game_y, int32_t vp_x, int32_t vp_y) : iStrength(iStrength), game_x(game_x), game_y(game_y),vp_x(vp_x),vp_y(vp_y) {}
250 	void CompileFunc(StdCompiler *pComp);
251 	bool operator ==(const struct C4KeyEventData &cmp) const;
252 };
253 
254 // Helper functions for high-level GUI control mappings.
255 namespace ControllerKeys {
Any(T & keys)256 template<class T> void Any(T &keys)    { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_AnyButton))); }
Cancel(T & keys)257 template<class T> void Cancel(T &keys) { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_ButtonB))); }
Ok(T & keys)258 template<class T> void Ok(T &keys)     { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_ButtonA))); }
Left(T & keys)259 template<class T> void Left(T &keys)   { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_AxisLeftXLeft)));
260                                          keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_ButtonDpadLeft))); }
Right(T & keys)261 template<class T> void Right(T &keys)  { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_AxisLeftXRight)));
262                                          keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_ButtonDpadRight))); }
Up(T & keys)263 template<class T> void Up(T &keys)     { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_AxisLeftYUp)));
264                                          keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_ButtonDpadUp))); }
Down(T & keys)265 template<class T> void Down(T &keys)   { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_AxisLeftYDown)));
266                                          keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_ButtonDpadDown))); }
267 }
268 
269 // callback interface
270 class C4KeyboardCallbackInterface
271 {
272 private:
273 	int iRef{0};
274 public:
275 	class C4CustomKey *pOriginalKey{nullptr};
276 
277 public:
278 	virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv) = 0; // return true if processed
279 
280 	friend class C4KeyboardMapping;
281 
282 	// reference counter
Ref()283 	inline void Ref() { ++iRef; }
Deref()284 	inline void Deref() { if (!--iRef) delete this; }
285 
286 	C4KeyboardCallbackInterface() = default;
287 	virtual ~C4KeyboardCallbackInterface() = default;
288 
IsOriginalKey(const class C4CustomKey * pCheckKey)289 	bool IsOriginalKey(const class C4CustomKey *pCheckKey) const { return pCheckKey == pOriginalKey; }
290 };
291 
292 // callback interface
293 template <class TargetClass> class C4KeyCB : public C4KeyboardCallbackInterface
294 {
295 public:
296 	typedef bool(TargetClass::*CallbackFunc)();
297 
298 protected:
299 	TargetClass &rTarget;
300 	CallbackFunc pFuncDown, pFuncUp, pFuncPressed, pFuncMoved;
301 
302 protected:
OnKeyEvent(const C4KeyCodeEx & key,C4KeyEventType eEv)303 	bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv) override
304 	{
305 		if (!CheckCondition()) return false;
306 		switch (eEv)
307 		{
308 		case KEYEV_Down: return pFuncDown ? (rTarget.*pFuncDown)() : false;
309 		case KEYEV_Up: return pFuncUp ? (rTarget.*pFuncUp)() : false;
310 		case KEYEV_Pressed: return pFuncPressed ? (rTarget.*pFuncPressed)() : false;
311 		case KEYEV_Moved: return pFuncMoved ? (rTarget.*pFuncMoved)() : false;
312 		default: return false;
313 		}
314 	}
315 
CheckCondition()316 	virtual bool CheckCondition() { return true; }
317 
318 public:
319 	C4KeyCB(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr, CallbackFunc pFuncMoved=nullptr)
rTarget(rTarget)320 			: rTarget(rTarget), pFuncDown(pFuncDown), pFuncUp(pFuncUp), pFuncPressed(pFuncPressed), pFuncMoved(pFuncMoved) {}
321 };
322 
323 // callback interface that passes the pressed key as a parameter
324 template <class TargetClass> class C4KeyCBPassKey : public C4KeyboardCallbackInterface
325 {
326 public:
327 	typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key);
328 
329 protected:
330 	TargetClass &rTarget;
331 	CallbackFunc pFuncDown, pFuncUp, pFuncPressed, pFuncMoved;
332 
333 protected:
OnKeyEvent(const C4KeyCodeEx & key,C4KeyEventType eEv)334 	bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv) override
335 	{
336 		if (!CheckCondition()) return false;
337 		switch (eEv)
338 		{
339 		case KEYEV_Down: return pFuncDown ? (rTarget.*pFuncDown)(key) : false;
340 		case KEYEV_Up: return pFuncUp ? (rTarget.*pFuncUp)(key) : false;
341 		case KEYEV_Pressed: return pFuncPressed ? (rTarget.*pFuncPressed)(key) : false;
342 		case KEYEV_Moved: return pFuncMoved ? (rTarget.*pFuncMoved)(key) : false;
343 		default: return false;
344 		}
345 	}
346 
CheckCondition()347 	virtual bool CheckCondition() { return true; }
348 
349 public:
350 	C4KeyCBPassKey(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr, CallbackFunc pFuncMoved=nullptr)
rTarget(rTarget)351 			: rTarget(rTarget), pFuncDown(pFuncDown), pFuncUp(pFuncUp), pFuncPressed(pFuncPressed), pFuncMoved(pFuncMoved) {}
352 };
353 
354 // parameterized callback interface
355 template <class TargetClass, class ParameterType> class C4KeyCBEx : public C4KeyboardCallbackInterface
356 {
357 public:
358 	typedef bool(TargetClass::*CallbackFunc)(ParameterType par);
359 
360 protected:
361 	TargetClass &rTarget;
362 	CallbackFunc pFuncDown, pFuncUp, pFuncPressed, pFuncMoved;
363 	ParameterType par;
364 
365 protected:
OnKeyEvent(const C4KeyCodeEx & key,C4KeyEventType eEv)366 	bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv) override
367 	{
368 		if (!CheckCondition()) return false;
369 		switch (eEv)
370 		{
371 		case KEYEV_Down: return pFuncDown ? (rTarget.*pFuncDown)(par) : false;
372 		case KEYEV_Up: return pFuncUp ? (rTarget.*pFuncUp)(par) : false;
373 		case KEYEV_Pressed: return pFuncPressed ? (rTarget.*pFuncPressed)(par) : false;
374 		case KEYEV_Moved: return pFuncMoved ? (rTarget.*pFuncMoved)(par) : false;
375 		default: return false;
376 		}
377 	}
378 
CheckCondition()379 	virtual bool CheckCondition() { return true; }
380 
381 public:
382 	C4KeyCBEx(TargetClass &rTarget, const ParameterType &par, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr, CallbackFunc pFuncMoved=nullptr)
rTarget(rTarget)383 			: rTarget(rTarget), pFuncDown(pFuncDown), pFuncUp(pFuncUp), pFuncPressed(pFuncPressed), pFuncMoved(pFuncMoved), par(par) {}
384 };
385 
386 template <class TargetClass, class ParameterType> class C4KeyCBExPassKey : public C4KeyboardCallbackInterface
387 {
388 public:
389 	typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key, const ParameterType &par);
390 
391 protected:
392 	TargetClass &rTarget;
393 	CallbackFunc pFuncDown, pFuncUp, pFuncPressed, pFuncMoved;
394 	ParameterType par;
395 
396 protected:
OnKeyEvent(const C4KeyCodeEx & key,C4KeyEventType eEv)397 	bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv) override
398 	{
399 		if (!CheckCondition()) return false;
400 		switch (eEv)
401 		{
402 		case KEYEV_Down: return pFuncDown ? (rTarget.*pFuncDown)(key, par) : false;
403 		case KEYEV_Up: return pFuncUp ? (rTarget.*pFuncUp)(key, par) : false;
404 		case KEYEV_Pressed: return pFuncPressed ? (rTarget.*pFuncPressed)(key, par) : false;
405 		case KEYEV_Moved: return pFuncMoved ? (rTarget.*pFuncMoved)(key, par) : false;
406 		default: return false;
407 		}
408 	}
409 
CheckCondition()410 	virtual bool CheckCondition() { return true; }
411 
412 public:
413 	C4KeyCBExPassKey(TargetClass &rTarget, const ParameterType &par, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr, CallbackFunc pFuncMoved=nullptr)
rTarget(rTarget)414 			: rTarget(rTarget), pFuncDown(pFuncDown), pFuncUp(pFuncUp), pFuncPressed(pFuncPressed), pFuncMoved(pFuncMoved), par(par) {}
415 };
416 
417 // one mapped keyboard entry
418 class C4CustomKey
419 {
420 public:
421 	typedef std::vector<C4KeyCodeEx> CodeList;
422 private:
423 	CodeList Codes, DefaultCodes; // keyboard scancodes of OS plus shift state
424 	C4KeyScope Scope;              // scope in which key is processed
425 	StdStrBuf Name;                // custom key name; used for association in config files
426 	typedef std::vector<C4KeyboardCallbackInterface *> CBVec;
427 	unsigned int uiPriority;       // key priority: If multiple keys of same code are defined, high prio overwrites low prio keys
428 	bool is_down;                  // down-callbacks have been executed but up-callbacks have not (not compiled)
429 
430 public:
431 	CBVec vecCallbacks; // a list of all callbacks assigned to that key
432 
433 	enum Priority
434 	{
435 		PRIO_None = 0u,
436 		PRIO_Base = 1u,
437 		PRIO_Dlg  = 2u,
438 		PRIO_Ctrl = 3u, // controls have higher priority than dialogs in GUI
439 		PRIO_CtrlOverride = 4u, // dialog handlings of keys that overwrite regular control handlings
440 		PRIO_FocusCtrl = 5u,  // controls override special dialog handling keys (e.g., RenameEdit)
441 		PRIO_Context = 6u, // context menus above controls
442 		PRIO_PlrControl = 7u, // player controls overwrite any other controls
443 		PRIO_MoreThanMax  = 100u // must be larger than otherwise largest used priority
444 	};
445 
446 protected:
447 	int iRef;
448 	C4CustomKey(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key
449 	C4CustomKey(CodeList rDefCodes, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key with multiple possible keys assigned
450 	friend class C4Game;
451 
452 public:
453 	C4CustomKey(const C4CustomKey &rCpy, bool fCopyCallbacks);
454 	virtual ~C4CustomKey(); // dtor
455 
Ref()456 	inline void Ref() { ++iRef; }
Deref()457 	inline void Deref() { if (!--iRef) delete this; }
458 
GetCodes()459 	const CodeList &GetCodes() const { return Codes.size() ? Codes : DefaultCodes; } // return assigned codes; default if no custom has been assigned
GetName()460 	const StdStrBuf &GetName() const { return Name; }
GetScope()461 	C4KeyScope GetScope() const { return Scope; }
GetPriority()462 	unsigned int GetPriority() const { return uiPriority; }
463 	bool IsCodeMatched(const C4KeyCodeEx &key) const;
464 
465 	void Update(const C4CustomKey *pByKey); // merge given key into this
466 	bool Execute(C4KeyEventType eEv, C4KeyCodeEx key);
467 
IsDown()468 	bool IsDown() const { return is_down; }
469 
470 	void KillCallbacks(const C4CustomKey *pOfKey); // remove any callbacks that were created by given key
471 
472 	void CompileFunc(StdCompiler *pComp);
473 };
474 
475 // a key that auto-registers itself into main game keyboard input class and does dereg when deleted
476 class C4KeyBinding : protected C4CustomKey
477 {
478 	// Stuffing these into an std::vector ends badly, so I've marked them non-copyable.
479 	C4KeyBinding(const C4KeyBinding&) = delete;
480 	C4KeyBinding& operator=(const C4KeyBinding&) = delete;
481 public:
482 	C4KeyBinding(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key
483 	C4KeyBinding(const CodeList &rDefCodes, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority = PRIO_Base); // ctor for default key
484 	~C4KeyBinding() override;
485 };
486 
487 // main keyboard mapping class
488 class C4KeyboardInput
489 {
490 private:
491 	// comparison fn for map
492 	struct szLess
493 	{
operatorszLess494 		bool operator()(const char *p, const char *q) const { return p && q && (strcmp(p,q)<0); }
495 	};
496 
497 	typedef std::multimap<C4KeyCode, C4CustomKey *> KeyCodeMap;
498 	typedef std::map<const char *, C4CustomKey *, szLess> KeyNameMap;
499 	// mapping of all keys by code and name
500 	KeyCodeMap KeysByCode;
501 	KeyNameMap KeysByName;
502 	C4KeyEventData LastKeyExtraData;
503 
504 public:
505 	static bool IsValid; // global var to fix any deinitialization orders of key map and static keys
506 
C4KeyboardInput()507 	C4KeyboardInput() { IsValid = true; }
~C4KeyboardInput()508 	~C4KeyboardInput() { Clear(); IsValid = false; }
509 
510 	void Clear(); // empty keyboard maps
511 
512 private:
513 	// assign keycodes changed for a key: Update codemap
514 	void UpdateKeyCodes(C4CustomKey *pKey, const C4CustomKey::CodeList &rOldCodes, const C4CustomKey::CodeList &rNewCodes);
515 
516 public:
517 	void RegisterKey(C4CustomKey *pRegKey); // register key into code and name maps, or update specific key
518 	void UnregisterKey(const StdStrBuf &rsName); // remove key from all maps
519 	void UnregisterKeyBinding(C4CustomKey *pKey); // just remove callbacks from a key
520 
521 	bool DoInput(const C4KeyCodeEx &InKey, C4KeyEventType InEvent, DWORD InScope, int32_t iStrength);
522 
523 	void CompileFunc(StdCompiler *pComp);
524 	bool LoadCustomConfig(); // load keyboard customization file
525 
526 	C4CustomKey *GetKeyByName(const char *szKeyName);
527 	StdStrBuf GetKeyCodeNameByKeyName(const char *szKeyName, bool fShort = false, int32_t iIndex = 0);
GetLastKeyExtraData()528 	const C4KeyEventData &GetLastKeyExtraData() const { return LastKeyExtraData; }
SetLastKeyExtraData(const C4KeyEventData & data)529 	void SetLastKeyExtraData(const C4KeyEventData &data) { LastKeyExtraData=data; }
530 };
531 
532 // keyboardinput-initializer-helper
533 C4KeyboardInput &C4KeyboardInput_Init();
534 
535 #endif // INC_C4KeyboardInput
536 
537