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 #include "C4Include.h"
19 #include "gui/C4KeyboardInput.h"
20 
21 #include "gui/C4MouseControl.h"
22 #include "c4group/C4Components.h"
23 #include "platform/C4Window.h"
24 
25 #include <unordered_map>
26 
27 #ifdef HAVE_SDL
28 #include <SDL.h>
29 #endif
30 
31 #ifdef USE_SDL_MAINLOOP
32 // Required for KeycodeToString translation table.
33 #include "platform/C4App.h"
34 #endif
35 
36 /* ----------------- Key maps ------------------ */
37 
38 struct C4KeyShiftMapEntry
39 {
40 	C4KeyShiftState eShift;
41 	const char *szName;
42 };
43 
44 const C4KeyShiftMapEntry KeyShiftMap [] =
45 {
46 	{ KEYS_Alt,     "Alt" },
47 	{ KEYS_Control, "Ctrl" },
48 	{ KEYS_Shift,   "Shift" },
49 	{ KEYS_Undefined, nullptr }
50 };
51 
String2KeyShift(const StdStrBuf & sName)52 C4KeyShiftState C4KeyCodeEx::String2KeyShift(const StdStrBuf &sName)
53 {
54 	// query map
55 	const C4KeyShiftMapEntry *pCheck = KeyShiftMap;
56 	while (pCheck->szName)
57 			if (SEqualNoCase(sName.getData(), pCheck->szName)) break; else ++pCheck;
58 	return pCheck->eShift;
59 }
60 
KeyShift2String(C4KeyShiftState eShift)61 StdStrBuf C4KeyCodeEx::KeyShift2String(C4KeyShiftState eShift)
62 {
63 	// query map
64 	const C4KeyShiftMapEntry *pCheck = KeyShiftMap;
65 	while (pCheck->szName)
66 			if (eShift == pCheck->eShift) break; else ++pCheck;
67 	return StdStrBuf(pCheck->szName);
68 }
69 
70 struct C4KeyCodeMapEntry
71 {
72 	C4KeyCode wCode;
73 	const char *szName;
74 	const char *szShortName;
75 };
76 
77 #if defined(USE_COCOA)
78 #include "platform/CocoaKeycodeMap.h"
79 #else
80 const C4KeyCodeMapEntry KeyCodeMap[] = {
81 	{K_ESCAPE,        "Escape",       "Esc"},
82 	{K_1,             "1",            nullptr},
83 	{K_2,             "2",            nullptr},
84 	{K_3,             "3",            nullptr},
85 	{K_4,             "4",            nullptr},
86 	{K_5,             "5",            nullptr},
87 	{K_6,             "6",            nullptr},
88 	{K_7,             "7",            nullptr},
89 	{K_8,             "8",            nullptr},
90 	{K_9,             "9",            nullptr},
91 	{K_0,             "0",            nullptr},
92 	{K_MINUS,         "Minus",        "-"},
93 	{K_EQUAL,         "Equal",        "="},
94 	{K_BACK,          "BackSpace",    nullptr},
95 	{K_TAB,           "Tab",          nullptr},
96 	{K_Q,             "Q",            nullptr},
97 	{K_W,             "W",            nullptr},
98 	{K_E,             "E",            nullptr},
99 	{K_R,             "R",            nullptr},
100 	{K_T,             "T",            nullptr},
101 	{K_Y,             "Y",            nullptr},
102 	{K_U,             "U",            nullptr},
103 	{K_I,             "I",            nullptr},
104 	{K_O,             "O",            nullptr},
105 	{K_P,             "P",            nullptr},
106 	{K_LEFT_BRACKET,  "LeftBracket",  "["},
107 	{K_RIGHT_BRACKET, "RightBracket", "]"},
108 	{K_RETURN,        "Return",       "Ret"},
109 	{K_CONTROL_L,     "LeftControl",  "LCtrl"},
110 	{K_A,             "A",            nullptr},
111 	{K_S,             "S",            nullptr},
112 	{K_D,             "D",            nullptr},
113 	{K_F,             "F",            nullptr},
114 	{K_G,             "G",            nullptr},
115 	{K_H,             "H",            nullptr},
116 	{K_J,             "J",            nullptr},
117 	{K_K,             "K",            nullptr},
118 	{K_L,             "L",            nullptr},
119 	{K_SEMICOLON,     "Semicolon",    ";"},
120 	{K_APOSTROPHE,    "Apostrophe",   "'"},
121 	{K_GRAVE_ACCENT,  "GraveAccent",  "`"},
122 	{K_SHIFT_L,       "LeftShift",    "LShift"},
123 	{K_BACKSLASH,     "Backslash",    R"(\)"},
124 	{K_Z,             "Z",            nullptr},
125 	{K_X,             "X",            nullptr},
126 	{K_C,             "C",            nullptr},
127 	{K_V,             "V",            nullptr},
128 	{K_B,             "B",            nullptr},
129 	{K_N,             "N",            nullptr},
130 	{K_M,             "M",            nullptr},
131 	{K_COMMA,         "Comma",        ","},
132 	{K_PERIOD,        "Period",       "."},
133 	{K_SLASH,         "Slash",        "/"},
134 	{K_SHIFT_R,       "RightShift",   "RShift"},
135 	{K_MULTIPLY,      "Multiply",     "N*"},
136 	{K_ALT_L,         "LeftAlt",      "LAlt"},
137 	{K_SPACE,         "Space",        "Sp"},
138 	{K_CAPS,          "Capslock",     nullptr},
139 	{K_F1,            "F1",           nullptr},
140 	{K_F2,            "F2",           nullptr},
141 	{K_F3,            "F3",           nullptr},
142 	{K_F4,            "F4",           nullptr},
143 	{K_F5,            "F5",           nullptr},
144 	{K_F6,            "F6",           nullptr},
145 	{K_F7,            "F7",           nullptr},
146 	{K_F8,            "F8",           nullptr},
147 	{K_F9,            "F9",           nullptr},
148 	{K_F10,           "F10",          nullptr},
149 	{K_NUM,           "NumLock",      "NLock"},
150 	{K_SCROLL,        "ScrollLock",   "SLock"},
151 	{K_NUM7,          "Num7",         "N7"},
152 	{K_NUM8,          "Num8",         "N8"},
153 	{K_NUM9,          "Num9",         "N9"},
154 	{K_SUBTRACT,      "Subtract",     "N-"},
155 	{K_NUM4,          "Num4",         "N4"},
156 	{K_NUM5,          "Num5",         "N5"},
157 	{K_NUM6,          "Num6",         "N6"},
158 	{K_ADD,           "Add",          "N+"},
159 	{K_NUM1,          "Num1",         "N1"},
160 	{K_NUM2,          "Num2",         "N2"},
161 	{K_NUM3,          "Num3",         "N3"},
162 	{K_NUM0,          "Num0",         "N0"},
163 	{K_DECIMAL,       "Decimal",      "N,"},
164 	{K_86,            "|<>",          nullptr},
165 	{K_F11,           "F11",          nullptr},
166 	{K_F12,           "F12",          nullptr},
167 	{K_NUM_RETURN,    "NumReturn",    "NRet"},
168 	{K_CONTROL_R,     "RightControl", "RCtrl"},
169 	{K_DIVIDE,        "Divide",       "N/"},
170 	{K_ALT_R,         "RightAlt",     "RAlt"},
171 	{K_HOME,          "Home",         nullptr},
172 	{K_UP,            "Up",           nullptr},
173 	{K_PAGEUP,        "PageUp",       nullptr},
174 	{K_LEFT,          "Left",         nullptr},
175 	{K_RIGHT,         "Right",        nullptr},
176 	{K_END,           "End",          nullptr},
177 	{K_DOWN,          "Down",         nullptr},
178 	{K_PAGEDOWN,      "PageDown",     nullptr},
179 	{K_INSERT,        "Insert",       "Ins"},
180 	{K_DELETE,        "Delete",       "Del"},
181 	{K_PAUSE,         "Pause",        nullptr},
182 	{K_WIN_L,         "LeftWin",      "LWin"},
183 	{K_WIN_R,         "RightWin",     "RWin"},
184 	{K_MENU,          "Menu",         nullptr},
185 	{K_PRINT,         "Print",        nullptr},
186 	{0x00,            nullptr,           nullptr}
187 };
188 #endif
189 
C4KeyCodeEx(C4KeyCode key,DWORD Shift,bool fIsRepeated,int32_t deviceId)190 C4KeyCodeEx::C4KeyCodeEx(C4KeyCode key, DWORD Shift, bool fIsRepeated, int32_t deviceId)
191 : Key(key), dwShift(Shift), fRepeated(fIsRepeated), deviceId(deviceId)
192 {
193 }
194 
FromC4MC(int8_t mouse_id,int32_t iButton,DWORD dwKeyParam,bool * is_down)195 C4KeyCodeEx C4KeyCodeEx::FromC4MC(int8_t mouse_id, int32_t iButton, DWORD dwKeyParam, bool *is_down)
196 {
197 	bool dummy;
198 	if (!is_down)
199 		is_down = &dummy;
200 	*is_down = true;
201 	C4KeyCode mouseevent_code;
202 	int wheel_dir = 0;
203 	if (iButton == C4MC_Button_Wheel) wheel_dir = (short)(dwKeyParam >> 16);
204 	switch (iButton)
205 	{
206 	case C4MC_Button_None: mouseevent_code = KEY_MOUSE_Move; break;
207 	case C4MC_Button_LeftDown: mouseevent_code = KEY_MOUSE_ButtonLeft; break;
208 	case C4MC_Button_LeftUp: mouseevent_code = KEY_MOUSE_ButtonLeft; *is_down = false; break;
209 	case C4MC_Button_LeftDouble: mouseevent_code = KEY_MOUSE_ButtonLeftDouble; break;
210 	case C4MC_Button_RightDown: mouseevent_code = KEY_MOUSE_ButtonRight; break;
211 	case C4MC_Button_RightDouble: mouseevent_code = KEY_MOUSE_ButtonRightDouble; break;
212 	case C4MC_Button_RightUp: mouseevent_code = KEY_MOUSE_ButtonRight; *is_down = false; break;
213 	case C4MC_Button_MiddleDown: mouseevent_code = KEY_MOUSE_ButtonMiddle; break;
214 	case C4MC_Button_MiddleUp: mouseevent_code = KEY_MOUSE_ButtonMiddle; *is_down = false; break;
215 	case C4MC_Button_MiddleDouble: mouseevent_code = KEY_MOUSE_ButtonMiddleDouble; break;
216 	case C4MC_Button_X1Down: mouseevent_code = KEY_MOUSE_ButtonX1; break;
217 	case C4MC_Button_X1Up: mouseevent_code = KEY_MOUSE_ButtonX1; *is_down = false; break;
218 	case C4MC_Button_X1Double: mouseevent_code = KEY_MOUSE_ButtonX1Double; break;
219 	case C4MC_Button_X2Down: mouseevent_code = KEY_MOUSE_ButtonX2; break;
220 	case C4MC_Button_X2Up: mouseevent_code = KEY_MOUSE_ButtonX2; *is_down = false; break;
221 	case C4MC_Button_X2Double: mouseevent_code = KEY_MOUSE_ButtonX2Double; break;
222 	case C4MC_Button_Wheel:
223 		if (!wheel_dir) assert("Attempted to record mouse wheel movement without a direction");
224 		mouseevent_code = (wheel_dir > 0) ? KEY_MOUSE_Wheel1Up : KEY_MOUSE_Wheel1Down; break;
225 	}
226 	C4KeyCodeEx key{KEY_Mouse(mouse_id, mouseevent_code), KEYS_None};
227 	if (dwKeyParam & MK_CONTROL) key.dwShift |= KEYS_Control;
228 	if (dwKeyParam & MK_SHIFT) key.dwShift |= KEYS_Shift;
229 	if (dwKeyParam & MK_ALT) key.dwShift |= KEYS_Alt;
230 	return key;
231 }
232 
FixShiftKeys()233 void C4KeyCodeEx::FixShiftKeys()
234 {
235 	// reduce stuff like Ctrl+RightCtrl to simply RightCtrl
236 	if ((dwShift & KEYS_Alt) && (Key == K_ALT_L || Key == K_ALT_R)) dwShift &= ~KEYS_Alt;
237 	if ((dwShift & KEYS_Control) && (Key == K_CONTROL_L || Key == K_CONTROL_R)) dwShift &= ~KEYS_Control;
238 	if ((dwShift & KEYS_Shift) && (Key == K_SHIFT_L || Key == K_SHIFT_R)) dwShift &= ~KEYS_Shift;
239 }
240 
GetKeyByScanCode(const char * scan_code)241 C4KeyCode C4KeyCodeEx::GetKeyByScanCode(const char *scan_code)
242 {
243 	// scan code is in hex format
244 	unsigned int scan_code_int;
245 	if (sscanf(scan_code, "$%x", &scan_code_int) != 1) return KEY_Undefined;
246 	return scan_code_int;
247 }
248 
249 static const std::unordered_map<std::string, C4KeyCode> controllercodes =
250 {
251 		{ "ButtonA",               KEY_CONTROLLER_ButtonA             },
252 		{ "ButtonB",               KEY_CONTROLLER_ButtonB             },
253 		{ "ButtonX",               KEY_CONTROLLER_ButtonX             },
254 		{ "ButtonY",               KEY_CONTROLLER_ButtonY             },
255 		{ "ButtonBack",            KEY_CONTROLLER_ButtonBack          },
256 		{ "ButtonGuide",           KEY_CONTROLLER_ButtonGuide         },
257 		{ "ButtonStart",           KEY_CONTROLLER_ButtonStart         },
258 		{ "ButtonLeftStick",       KEY_CONTROLLER_ButtonLeftStick     },
259 		{ "ButtonRightStick",      KEY_CONTROLLER_ButtonRightStick    },
260 		{ "ButtonLeftShoulder",    KEY_CONTROLLER_ButtonLeftShoulder  },
261 		{ "ButtonRightShoulder",   KEY_CONTROLLER_ButtonRightShoulder },
262 		{ "ButtonDpadUp",          KEY_CONTROLLER_ButtonDpadUp        },
263 		{ "ButtonDpadDown",        KEY_CONTROLLER_ButtonDpadDown      },
264 		{ "ButtonDpadLeft",        KEY_CONTROLLER_ButtonDpadLeft      },
265 		{ "ButtonDpadRight",       KEY_CONTROLLER_ButtonDpadRight     },
266 		{ "AnyButton",             KEY_CONTROLLER_AnyButton           },
267 		{ "LeftStickLeft",         KEY_CONTROLLER_AxisLeftXLeft       },
268 		{ "LeftStickRight",        KEY_CONTROLLER_AxisLeftXRight      },
269 		{ "LeftStickUp",           KEY_CONTROLLER_AxisLeftYUp         },
270 		{ "LeftStickDown",         KEY_CONTROLLER_AxisLeftYDown       },
271 		{ "RightStickLeft",        KEY_CONTROLLER_AxisRightXLeft      },
272 		{ "RightStickRight",       KEY_CONTROLLER_AxisRightXRight     },
273 		{ "RightStickUp",          KEY_CONTROLLER_AxisRightYUp        },
274 		{ "RightStickDown",        KEY_CONTROLLER_AxisRightYDown      },
275 		{ "LeftTrigger",           KEY_CONTROLLER_AxisTriggerLeft     },
276 		{ "RightTrigger",          KEY_CONTROLLER_AxisTriggerRight    },
277 };
278 
String2KeyCode(const StdStrBuf & sName)279 C4KeyCode C4KeyCodeEx::String2KeyCode(const StdStrBuf &sName)
280 {
281 	// direct key code, e.g. "$e" (Backspace)?
282 	if (sName.getLength() > 1)
283 	{
284 		unsigned int dwRVal;
285 		if (sscanf(sName.getData(), R"(\x%x)", &dwRVal) == 1) return dwRVal;
286 		// scan code
287 		if (*sName.getData() == '$') return GetKeyByScanCode(sName.getData());
288 		// direct gamepad code
289 		std::regex controller_re(R"/(^Controller(\w+)$)/");
290 		std::cmatch matches;
291 		if (std::regex_match(sName.getData(), matches, controller_re))
292 		{
293 			auto keycode_it = controllercodes.find(matches[1].str());
294 			if (keycode_it != controllercodes.end())
295 				return KEY_Gamepad(keycode_it->second);
296 			else
297 				return KEY_Undefined;
298 
299 		}
300 		bool is_mouse_key;
301 #ifdef _WIN32
302 		is_mouse_key = !strnicmp(sName.getData(), "Mouse", 5);
303 #else
304 		is_mouse_key = !strncasecmp(sName.getData(), "Mouse", 5);
305 #endif
306 		if (is_mouse_key)
307 		{
308 			// skip Mouse/GameMouse
309 			const char *key_str = sName.getData()+5;
310 			int mouse_id;
311 			if (sscanf(key_str, "%d",  &mouse_id) == 1)
312 			{
313 				// skip number
314 				while (isdigit(*key_str)) ++key_str;
315 				// check for known mouse events (e.g. Mouse0Move or GameMouse0Wheel)
316 				if (!stricmp(key_str, "Move")) return KEY_Mouse(mouse_id, KEY_MOUSE_Move);
317 				if (!stricmp(key_str, "Wheel1Up")) return KEY_Mouse(mouse_id, KEY_MOUSE_Wheel1Up);
318 				if (!stricmp(key_str, "Wheel1Down")) return KEY_Mouse(mouse_id, KEY_MOUSE_Wheel1Down);
319 				// check for known mouse button events
320 				if (SEqualNoCase(key_str, "Button", 6)) // e.g. Mouse0ButtonLeft or GameMouse0ButtonRightDouble (This line is left here to not break anything, the buttons are now named Mouse0Left)
321 					key_str += 6;
322 				uint8_t mouseevent_id = 0;
323 				if (SEqualNoCase(key_str, "Left",4)) { mouseevent_id=KEY_MOUSE_ButtonLeft; key_str += 4; }
324 				else if (SEqualNoCase(key_str, "Right",5)) { mouseevent_id=KEY_MOUSE_ButtonRight; key_str += 5; }
325 				else if (SEqualNoCase(key_str, "Middle",6)) { mouseevent_id=KEY_MOUSE_ButtonMiddle; key_str += 6; }
326 				else if (SEqualNoCase(key_str, "X1",2)) { mouseevent_id=KEY_MOUSE_ButtonX1; key_str += 2; }
327 				else if (SEqualNoCase(key_str, "X2",2)) { mouseevent_id=KEY_MOUSE_ButtonX2; key_str += 2; }
328 				else if (isdigit(*key_str))
329 				{
330 					// indexed mouse button (e.g. Mouse0Button4 or Mouse0Button4Double)
331 					int button_index;
332 					if (sscanf(key_str, "%d",  &button_index) == 1)
333 					{
334 						mouseevent_id=static_cast<uint8_t>(KEY_MOUSE_Button1+button_index);
335 						while (isdigit(*key_str)) ++key_str;
336 					}
337 				}
338 				if (mouseevent_id)
339 				{
340 					// valid event if finished or followed by "Double"
341 					if (!*key_str) return KEY_Mouse(mouse_id, mouseevent_id);
342 					if (!stricmp(key_str, "Double")) return KEY_Mouse(mouse_id, mouseevent_id+(KEY_MOUSE_Button1Double-KEY_MOUSE_Button1));
343 					// invalid mouse key...
344 				}
345 			}
346 		}
347 
348 	}
349 	// query map
350 	const C4KeyCodeMapEntry *pCheck = KeyCodeMap;
351 	while (pCheck->szName) {
352 		if (SEqualNoCase(sName.getData(), pCheck->szName)) {
353 			return(pCheck->wCode);
354 		}
355 		++pCheck;
356 	}
357 #if defined(USE_SDL_MAINLOOP)
358 	SDL_Scancode s = SDL_GetScancodeFromName(sName.getData());
359 	if (s != SDL_SCANCODE_UNKNOWN) return s;
360 #endif
361 	return KEY_Undefined;
362 }
363 
KeyCode2String(C4KeyCode wCode,bool fHumanReadable,bool fShort)364 StdStrBuf C4KeyCodeEx::KeyCode2String(C4KeyCode wCode, bool fHumanReadable, bool fShort)
365 {
366 	// Gamepad keys
367 	if (Key_IsGamepad(wCode))
368 	{
369 		if (fHumanReadable)
370 		{
371 			switch (Key_GetGamepadEvent(wCode))
372 			{
373 			case KEY_CONTROLLER_ButtonA             : return StdStrBuf("{{@Ico:A}}");
374 			case KEY_CONTROLLER_ButtonB             : return StdStrBuf("{{@Ico:B}}");
375 			case KEY_CONTROLLER_ButtonX             : return StdStrBuf("{{@Ico:X}}");
376 			case KEY_CONTROLLER_ButtonY             : return StdStrBuf("{{@Ico:Y}}");
377 			case KEY_CONTROLLER_ButtonBack          : return StdStrBuf("{{@Ico:Back}}");
378 			case KEY_CONTROLLER_ButtonGuide         : return StdStrBuf("Guide");
379 			case KEY_CONTROLLER_ButtonStart         : return StdStrBuf("{{@Ico:Start}}");
380 			case KEY_CONTROLLER_ButtonLeftStick     : return StdStrBuf("{{@Ico:LeftStick}}");
381 			case KEY_CONTROLLER_ButtonRightStick    : return StdStrBuf("{{@Ico:RightStick}}");
382 			case KEY_CONTROLLER_ButtonLeftShoulder  : return StdStrBuf("{{@Ico:LeftShoulder}}");
383 			case KEY_CONTROLLER_ButtonRightShoulder : return StdStrBuf("{{@Ico:RightShoulder}}");
384 			case KEY_CONTROLLER_ButtonDpadUp        : return StdStrBuf("{{@Ico:DpadUp}}");
385 			case KEY_CONTROLLER_ButtonDpadDown      : return StdStrBuf("{{@Ico:DpadDown}}");
386 			case KEY_CONTROLLER_ButtonDpadLeft      : return StdStrBuf("{{@Ico:DpadLeft}}");
387 			case KEY_CONTROLLER_ButtonDpadRight     : return StdStrBuf("{{@Ico:DpadRight}}");
388 			case KEY_CONTROLLER_AnyButton           : return StdStrBuf("Any Button");
389 			case KEY_CONTROLLER_AxisLeftXLeft       : return StdStrBuf("{{@Ico:LeftStick}} Left");
390 			case KEY_CONTROLLER_AxisLeftXRight      : return StdStrBuf("{{@Ico:LeftStick}} Right");
391 			case KEY_CONTROLLER_AxisLeftYUp         : return StdStrBuf("{{@Ico:LeftStick}} Up");
392 			case KEY_CONTROLLER_AxisLeftYDown       : return StdStrBuf("{{@Ico:LeftStick}} Down");
393 			case KEY_CONTROLLER_AxisRightXLeft      : return StdStrBuf("{{@Ico:RightStick}} Left");
394 			case KEY_CONTROLLER_AxisRightXRight     : return StdStrBuf("{{@Ico:RightStick}} Right");
395 			case KEY_CONTROLLER_AxisRightYUp        : return StdStrBuf("{{@Ico:RightStick}} Up");
396 			case KEY_CONTROLLER_AxisRightYDown      : return StdStrBuf("{{@Ico:RightStick}} Down");
397 			case KEY_CONTROLLER_AxisTriggerLeft     : return StdStrBuf("{{@Ico:LeftTrigger}}");
398 			case KEY_CONTROLLER_AxisTriggerRight    : return StdStrBuf("{{@Ico:RightTrigger}}");
399 			}
400 		}
401 		else
402 		{
403 			// A linear search in our small map is probably fast enough.
404 			auto it = std::find_if(controllercodes.begin(), controllercodes.end(), [wCode](const auto &p)
405 			{
406 				return p.second == Key_GetGamepadEvent(wCode);
407 			});
408 			if (it != controllercodes.end())
409 				return FormatString("Controller%s", it->first.c_str());
410 		}
411 		return StdStrBuf("Unknown");
412 	}
413 
414 	// Mouse keys
415 	if (Key_IsMouse(wCode))
416 	{
417 		int mouse_id = Key_GetMouse(wCode);
418 		int mouse_event = Key_GetMouseEvent(wCode);
419 		const char *mouse_str = "Mouse";
420 		switch (mouse_event)
421 		{
422 		case KEY_MOUSE_Move:              return FormatString("%s%dMove", mouse_str, mouse_id);
423 		case KEY_MOUSE_Wheel1Up:          return FormatString("%s%dWheel1Up", mouse_str, mouse_id);
424 		case KEY_MOUSE_Wheel1Down:        return FormatString("%s%dWheel1Down", mouse_str, mouse_id);
425 		case KEY_MOUSE_ButtonLeft:        return FormatString("%s%dLeft", mouse_str, mouse_id);
426 		case KEY_MOUSE_ButtonRight:       return FormatString("%s%dRight", mouse_str, mouse_id);
427 		case KEY_MOUSE_ButtonMiddle:      return FormatString("%s%dMiddle", mouse_str, mouse_id);
428 		case KEY_MOUSE_ButtonX1:          return FormatString("%s%dX1", mouse_str, mouse_id);
429 		case KEY_MOUSE_ButtonX2:          return FormatString("%s%dX2", mouse_str, mouse_id);
430 		case KEY_MOUSE_ButtonLeftDouble:  return FormatString("%s%dLeftDouble", mouse_str, mouse_id);
431 		case KEY_MOUSE_ButtonRightDouble: return FormatString("%s%dRightDouble", mouse_str, mouse_id);
432 		case KEY_MOUSE_ButtonMiddleDouble:return FormatString("%s%dMiddleDouble", mouse_str, mouse_id);
433 		case KEY_MOUSE_ButtonX1Double:    return FormatString("%s%dX1Double", mouse_str, mouse_id);
434 		case KEY_MOUSE_ButtonX2Double:    return FormatString("%s%dX2Double", mouse_str, mouse_id);
435 		default:
436 			// extended mouse button
437 		{
438 			uint8_t btn = Key_GetMouseEvent(wCode);
439 			if (btn >= KEY_MOUSE_Button1Double)
440 				return FormatString("%s%dButton%dDouble", mouse_str, mouse_id, int(btn-KEY_MOUSE_Button1Double));
441 			else
442 				return FormatString("%s%dButton%d", mouse_str, mouse_id, int(btn-KEY_MOUSE_Button1));
443 		}
444 		}
445 	}
446 
447 	// it's a keyboard key
448 	if (!fHumanReadable) {
449 		// for config files and such: dump scancode
450 		return FormatString("$%x", static_cast<unsigned int>(wCode));
451 	}
452 #if defined(USE_WIN32_WINDOWS)
453 
454 	// Query map
455 	const C4KeyCodeMapEntry *pCheck = KeyCodeMap;
456 	while (pCheck->szName)
457 		if (wCode == pCheck->wCode) return StdStrBuf((pCheck->szShortName && fShort) ? pCheck->szShortName : pCheck->szName); else ++pCheck;
458 
459 //  TODO: Works?
460 //  StdStrBuf Name; Name.SetLength(1000);
461 //  int res = GetKeyNameText(wCode, Name.getMData(), Name.getSize());
462 //  if(!res)
463 //    // not found: Compose as direct code
464 //    return FormatString("\\x%x", (DWORD) wCode);
465 //  // Set size
466 //  Name.SetLength(res);
467 //  return Name;
468 
469 	wchar_t buf[100];
470 	int len = GetKeyNameText(wCode<<16, buf, 100);
471 	if (len > 0) {
472 		// buf is nullterminated name
473 		return StdStrBuf(buf);
474 	}
475 #elif defined (USE_COCOA)
476 	// query map
477 	const C4KeyCodeMapEntry *pCheck = KeyCodeMap;
478 	while (pCheck->szName)
479 			if (wCode == pCheck->wCode) return StdStrBuf((pCheck->szShortName && fShort) ? pCheck->szShortName : pCheck->szName); else ++pCheck;
480 	// not found: Compose as direct code
481 	return FormatString("\\x%x", static_cast<unsigned int>(wCode));
482 #elif defined(USE_SDL_MAINLOOP)
483 	StdStrBuf buf;
484 	auto name = KeycodeToString(wCode);
485 	if (name) buf.Copy(name);
486 	if (!buf.getLength()) buf.Format("\\x%lx", wCode);
487 	return buf;
488 #endif
489 	return FormatString("$%x", static_cast<unsigned int>(wCode));
490 }
491 
ToString(bool fHumanReadable,bool fShort) const492 StdStrBuf C4KeyCodeEx::ToString(bool fHumanReadable, bool fShort) const
493 {
494 	static StdStrBuf sResult;
495 	sResult.Clear();
496 	// Add shift
497 	for (DWORD dwShiftCheck = KEYS_First; dwShiftCheck <= KEYS_Max; dwShiftCheck <<= 1)
498 		if (dwShiftCheck & dwShift)
499 		{
500 			sResult.Append(KeyShift2String((C4KeyShiftState) dwShiftCheck));
501 			sResult.AppendChar('+');
502 		}
503 	// Add key
504 	if (sResult.getLength())
505 	{
506 		sResult.Append(KeyCode2String(Key, fHumanReadable, fShort));
507 		return sResult;
508 	}
509 	else
510 	{
511 		return KeyCode2String(Key, fHumanReadable, fShort);
512 	}
513 }
514 
515 
516 
517 /* ----------------- C4KeyCodeEx ------------------ */
518 
CompileFunc(StdCompiler * pComp,StdStrBuf * pOutBuf)519 void C4KeyCodeEx::CompileFunc(StdCompiler *pComp, StdStrBuf *pOutBuf)
520 {
521 	if (pComp->isDeserializer())
522 	{
523 		// reading from file
524 		StdStrBuf sCode;
525 		bool is_scan_code;
526 		// read shifts
527 		DWORD dwSetShift = 0;
528 		for (;;)
529 		{
530 			is_scan_code = pComp->Separator(StdCompiler::SEP_DOLLAR);
531 			if (!is_scan_code) pComp->NoSeparator();
532 			pComp->Value(mkParAdapt(sCode, StdCompiler::RCT_Idtf));
533 			if (is_scan_code) // scan codes start with $. Reassamble the two tokens that were split by StdCompiler
534 			{
535 				sCode.Take(FormatString("$%s", sCode.getData()));
536 				break;
537 			}
538 			if (!pComp->Separator(StdCompiler::SEP_PLUS)) break; // no more separator: Parse this as keyboard code
539 			// try to convert to shift state
540 			C4KeyShiftState eAddState = String2KeyShift(sCode);
541 			if (eAddState == KEYS_Undefined)
542 				pComp->excCorrupt("undefined key shift state: %s", sCode.getData());
543 			dwSetShift |= eAddState;
544 		}
545 		// any code given? Otherwise, keep default
546 		if (sCode.getLength())
547 		{
548 			// last section: convert to key code
549 			C4KeyCode eCode = String2KeyCode(sCode);
550 			if (eCode == KEY_Undefined)
551 			{
552 				if (pOutBuf)
553 				{
554 					// unknown key, but an output buffer for unknown keys was provided. No failure; caller might resolve key.
555 					eCode = KEY_Default;
556 				}
557 				else
558 				{
559 					pComp->excCorrupt("undefined key code: %s", sCode.getData());
560 				}
561 			}
562 			dwShift = dwSetShift;
563 			Key = eCode;
564 			if (pOutBuf) {
565 				// FIXME: This function is used both, to deserialize things like CON_Right and Shift+$12
566 				// For CON_…, eCode and dwShift will be zero, and sCode will contain the key name.
567 				// For Shift+… sCode will only contain the last token. What is correct here?
568 				// Reading C4PlayerControlAssignment::KeyComboItem::CompileFunc suggests that setting not value for parsed combinations may be correct.
569 				if (eCode == 0)
570 					pOutBuf->Take(std::move(sCode));
571 				else
572 					pOutBuf->Copy(ToString(false, false));
573 			}
574 		}
575 	}
576 	else
577 	{
578 		// write shift states
579 		for (DWORD dwShiftCheck = KEYS_First; dwShiftCheck <= KEYS_Max; dwShiftCheck <<= 1)
580 			if (dwShiftCheck & dwShift)
581 			{
582 				pComp->Value(mkDecompileAdapt(KeyShift2String((C4KeyShiftState) dwShiftCheck)));
583 				pComp->Separator(StdCompiler::SEP_PLUS);
584 			}
585 		// write key
586 		pComp->Value(mkDecompileAdapt(KeyCode2String(Key, false, false)));
587 	}
588 }
589 
CompileFunc(StdCompiler * pComp)590 void C4KeyEventData::CompileFunc(StdCompiler *pComp)
591 {
592 	pComp->Value(iStrength);
593 	pComp->Separator();
594 	pComp->Value(game_x);
595 	pComp->Separator();
596 	pComp->Value(game_y);
597 	pComp->Separator();
598 	pComp->Value(vp_x);
599 	pComp->Separator();
600 	pComp->Value(vp_y);
601 }
602 
operator ==(const struct C4KeyEventData & cmp) const603 bool C4KeyEventData::operator ==(const struct C4KeyEventData &cmp) const
604 {
605 	return iStrength == cmp.iStrength
606 	       && game_x == cmp.game_x && game_y == cmp.game_y
607 	       && vp_x == cmp.vp_x && vp_y == cmp.vp_y;
608 
609 }
610 
KEY_IsModifier(C4KeyCode k)611 bool KEY_IsModifier(C4KeyCode k) {
612 	return k == K_CONTROL_L || k == K_SHIFT_L || k == K_ALT_L ||
613 	       k == K_CONTROL_R || k == K_SHIFT_R || k == K_ALT_R;
614 }
615 
616 
617 /* ----------------- C4CustomKey------------------ */
618 
C4CustomKey(const C4KeyCodeEx & DefCode,const char * szName,C4KeyScope Scope,C4KeyboardCallbackInterface * pCallback,unsigned int uiPriority)619 C4CustomKey::C4CustomKey(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority)
620 		: Scope(Scope), Name(), uiPriority(uiPriority), iRef(0), is_down(false)
621 {
622 	// generate code
623 	if (DefCode.Key != KEY_Default) DefaultCodes.push_back(DefCode);
624 	// ctor for default key
625 	Name.Copy(szName);
626 	if (pCallback)
627 	{
628 		pCallback->Ref();
629 		vecCallbacks.push_back(pCallback);
630 		pCallback->pOriginalKey = this;
631 	}
632 }
633 
C4CustomKey(CodeList rDefCodes,const char * szName,C4KeyScope Scope,C4KeyboardCallbackInterface * pCallback,unsigned int uiPriority)634 C4CustomKey::C4CustomKey(CodeList rDefCodes, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority)
635 		: DefaultCodes(std::move(rDefCodes)), Scope(Scope), Name(), uiPriority(uiPriority), iRef(0), is_down(false)
636 {
637 	// ctor for default key
638 	Name.Copy(szName);
639 	if (pCallback)
640 	{
641 		pCallback->Ref();
642 		vecCallbacks.push_back(pCallback);
643 		pCallback->pOriginalKey = this;
644 	}
645 }
646 
C4CustomKey(const C4CustomKey & rCpy,bool fCopyCallbacks)647 C4CustomKey::C4CustomKey(const C4CustomKey &rCpy, bool fCopyCallbacks)
648 		: Codes(rCpy.Codes), DefaultCodes(rCpy.DefaultCodes), Scope(rCpy.Scope), Name(), uiPriority(rCpy.uiPriority), iRef(0), is_down(false)
649 {
650 	Name.Copy(rCpy.GetName());
651 	if (fCopyCallbacks)
652 	{
653 		for (auto callback : rCpy.vecCallbacks)
654 		{
655 			callback->Ref();
656 			vecCallbacks.push_back(callback);
657 		}
658 	}
659 }
660 
~C4CustomKey()661 C4CustomKey::~C4CustomKey()
662 {
663 	// free callback handles
664 	for (CBVec::const_iterator i = vecCallbacks.begin(); i != vecCallbacks.end(); ++i)
665 		(*i)->Deref();
666 }
667 
IsCodeMatched(const C4KeyCodeEx & key) const668 bool C4CustomKey::IsCodeMatched(const C4KeyCodeEx &key) const
669 {
670 	const CodeList &codes = GetCodes();
671 	for (const auto &code : codes)
672 		if (code == key)
673 			return true;
674 	return false;
675 }
676 
Update(const C4CustomKey * pByKey)677 void C4CustomKey::Update(const C4CustomKey *pByKey)
678 {
679 	assert(pByKey);
680 	assert(Name == pByKey->Name);
681 	// transfer any assigned data, except name which should be equal anyway
682 	if (pByKey->DefaultCodes.size()) DefaultCodes = pByKey->DefaultCodes;
683 	if (pByKey->Codes.size()) Codes = pByKey->Codes;
684 	if (pByKey->Scope != KEYSCOPE_None) Scope = pByKey->Scope;
685 	if (pByKey->uiPriority != PRIO_None) uiPriority = pByKey->uiPriority;
686 	for (auto callback : pByKey->vecCallbacks)
687 	{
688 		callback->Ref();
689 		vecCallbacks.push_back(callback);
690 	}
691 }
692 
C4KeyboardCallbackInterfaceHasOriginalKey(C4KeyboardCallbackInterface * pIntfc,const C4CustomKey * pCheckKey)693 bool C4KeyboardCallbackInterfaceHasOriginalKey(C4KeyboardCallbackInterface *pIntfc, const C4CustomKey *pCheckKey)
694 {
695 	return pIntfc->IsOriginalKey(pCheckKey);
696 }
697 
KillCallbacks(const C4CustomKey * pOfKey)698 void C4CustomKey::KillCallbacks(const C4CustomKey *pOfKey)
699 {
700 	// remove all instances from list
701 	CBVec::iterator i;
702 	while ((i = std::find_if(vecCallbacks.begin(), vecCallbacks.end(), std::bind2nd(std::ptr_fun(&C4KeyboardCallbackInterfaceHasOriginalKey), pOfKey))) != vecCallbacks.end())
703 	{
704 		C4KeyboardCallbackInterface *pItfc = *i;
705 		vecCallbacks.erase(i);
706 		pItfc->Deref();
707 	}
708 }
709 
CompileFunc(StdCompiler * pComp)710 void C4CustomKey::CompileFunc(StdCompiler *pComp)
711 {
712 	pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(Codes), Name.getData(), DefaultCodes));
713 }
714 
Execute(C4KeyEventType eEv,C4KeyCodeEx key)715 bool C4CustomKey::Execute(C4KeyEventType eEv, C4KeyCodeEx key)
716 {
717 	// remember down-state
718 	is_down = (eEv == KEYEV_Down);
719 	// execute all callbacks
720 	for (auto & callback : vecCallbacks)
721 		if (callback->OnKeyEvent(key, eEv))
722 			return true;
723 	// no event processed it
724 	return false;
725 }
726 
727 
728 
729 /* ----------------- C4KeyBinding ------------------ */
730 
C4KeyBinding(const C4KeyCodeEx & DefCode,const char * szName,C4KeyScope Scope,C4KeyboardCallbackInterface * pCallback,unsigned int uiPriority)731 C4KeyBinding::C4KeyBinding(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority)
732 		: C4CustomKey(DefCode, szName, Scope, pCallback, uiPriority)
733 {
734 	// self holds a ref
735 	Ref();
736 	// register into keyboard input class
737 	C4KeyboardInput_Init().RegisterKey(this);
738 }
739 
C4KeyBinding(const CodeList & rDefCodes,const char * szName,C4KeyScope Scope,C4KeyboardCallbackInterface * pCallback,unsigned int uiPriority)740 C4KeyBinding::C4KeyBinding(const CodeList &rDefCodes, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority)
741 		: C4CustomKey(rDefCodes, szName, Scope, pCallback, uiPriority)
742 {
743 	// self holds a ref
744 	Ref();
745 	// register into keyboard input class
746 	C4KeyboardInput_Init().RegisterKey(this);
747 }
748 
~C4KeyBinding()749 C4KeyBinding::~C4KeyBinding()
750 {
751 	// deregister from keyboard input class, if that class still exists
752 	if (C4KeyboardInput::IsValid)
753 		Game.KeyboardInput.UnregisterKeyBinding(this);
754 	// shouldn't be refed now
755 	assert(iRef==1);
756 	iRef = 0;
757 }
758 
759 
760 /* ----------------- C4KeyboardInput ------------------ */
761 
762 bool C4KeyboardInput::IsValid = false;
763 
Clear()764 void C4KeyboardInput::Clear()
765 {
766 	LastKeyExtraData = C4KeyEventData();
767 	// release all keys - name map is guarantueed to contain them all
768 	for (KeyNameMap::const_iterator i = KeysByName.begin(); i != KeysByName.end(); ++i)
769 		i->second->Deref();
770 	// clear maps
771 	KeysByCode.clear();
772 	KeysByName.clear();
773 }
774 
UpdateKeyCodes(C4CustomKey * pKey,const C4CustomKey::CodeList & rOldCodes,const C4CustomKey::CodeList & rNewCodes)775 void C4KeyboardInput::UpdateKeyCodes(C4CustomKey *pKey, const C4CustomKey::CodeList &rOldCodes, const C4CustomKey::CodeList &rNewCodes)
776 {
777 	// new key codes must be the new current key codes
778 	assert(pKey->GetCodes() == rNewCodes);
779 	// kill from old list
780 	C4CustomKey::CodeList::const_iterator iCode;
781 	for (iCode = rOldCodes.begin(); iCode != rOldCodes.end(); ++iCode)
782 	{
783 		// no need to kill if code stayed
784 		if (std::find(rNewCodes.begin(), rNewCodes.end(), *iCode) != rNewCodes.end()) continue;
785 		std::pair<KeyCodeMap::iterator, KeyCodeMap::iterator> KeyRange = KeysByCode.equal_range((*iCode).Key);
786 		for (KeyCodeMap::iterator i = KeyRange.first; i != KeyRange.second; ++i)
787 			if (i->second == pKey)
788 			{
789 				KeysByCode.erase(i);
790 				break;
791 			}
792 	}
793 	// readd new codes
794 	for (iCode = rNewCodes.begin(); iCode != rNewCodes.end(); ++iCode)
795 	{
796 		// no double-add if it was in old list already
797 		if (std::find(rOldCodes.begin(), rOldCodes.end(), *iCode) != rOldCodes.end()) continue;
798 		KeysByCode.insert(std::make_pair((*iCode).Key, pKey));
799 	}
800 }
801 
RegisterKey(C4CustomKey * pRegKey)802 void C4KeyboardInput::RegisterKey(C4CustomKey *pRegKey)
803 {
804 	assert(pRegKey); if (!pRegKey) return;
805 	// key will be added: ref it
806 	pRegKey->Ref();
807 	// search key of same name first
808 	C4CustomKey *pDupKey = KeysByName[pRegKey->GetName().getData()];
809 	if (pDupKey)
810 	{
811 		// key of this name exists: Merge them (old codes copied cuz they'll be overwritten)
812 		C4CustomKey::CodeList OldCodes = pDupKey->GetCodes();
813 		const C4CustomKey::CodeList &rNewCodes = pRegKey->GetCodes();
814 		pDupKey->Update(pRegKey);
815 		// update access map if key changed
816 		if (!(OldCodes == rNewCodes)) UpdateKeyCodes(pDupKey, OldCodes, rNewCodes);
817 		// key to be registered no longer used
818 		pRegKey->Deref();
819 	}
820 	else
821 	{
822 		// new unique key: Insert into map
823 		KeysByName[pRegKey->GetName().getData()] = pRegKey;
824 		for (C4CustomKey::CodeList::const_iterator i = pRegKey->GetCodes().begin(); i != pRegKey->GetCodes().end(); ++i)
825 		{
826 			KeysByCode.insert(std::make_pair((*i).Key, pRegKey));
827 		}
828 	}
829 }
830 
UnregisterKey(const StdStrBuf & rsName)831 void C4KeyboardInput::UnregisterKey(const StdStrBuf &rsName)
832 {
833 	// kill from name map
834 	KeyNameMap::iterator in = KeysByName.find(rsName.getData());
835 	if (in == KeysByName.end()) return;
836 	C4CustomKey *pKey = in->second;
837 	KeysByName.erase(in);
838 	// kill all key bindings from key map
839 	for (C4CustomKey::CodeList::const_iterator iCode = pKey->GetCodes().begin(); iCode != pKey->GetCodes().end(); ++iCode)
840 	{
841 		std::pair<KeyCodeMap::iterator, KeyCodeMap::iterator> KeyRange = KeysByCode.equal_range((*iCode).Key);
842 		for (KeyCodeMap::iterator i = KeyRange.first; i != KeyRange.second; ++i)
843 			if (i->second == pKey)
844 			{
845 				KeysByCode.erase(i);
846 				break;
847 			}
848 	}
849 	// release reference to key
850 	pKey->Deref();
851 }
852 
UnregisterKeyBinding(C4CustomKey * pUnregKey)853 void C4KeyboardInput::UnregisterKeyBinding(C4CustomKey *pUnregKey)
854 {
855 	// find key in name map
856 	KeyNameMap::iterator in = KeysByName.find(pUnregKey->GetName().getData());
857 	if (in == KeysByName.end()) return;
858 	C4CustomKey *pKey = in->second;
859 	// is this key in the map?
860 	if (pKey != pUnregKey)
861 	{
862 		// Other key is in the list: Just remove the callbacks
863 		pKey->KillCallbacks(pUnregKey);
864 		return;
865 	}
866 	// this key is in the list: Replace by a duplicate...
867 	C4CustomKey *pNewKey = new C4CustomKey(*pUnregKey, true);
868 	// ...without the own callbacks
869 	pNewKey->KillCallbacks(pUnregKey);
870 	// and replace current key by duplicate
871 	UnregisterKey(pUnregKey->GetName());
872 	RegisterKey(pNewKey);
873 }
874 
DoInput(const C4KeyCodeEx & InKey,C4KeyEventType InEvent,DWORD InScope,int32_t iStrength)875 bool C4KeyboardInput::DoInput(const C4KeyCodeEx &InKey, C4KeyEventType InEvent, DWORD InScope, int32_t iStrength)
876 {
877 	// store last-key-info
878 	LastKeyExtraData.iStrength = (iStrength >= 0) ? iStrength : ((InEvent != KEYEV_Up) * 100);
879 	LastKeyExtraData.game_x = LastKeyExtraData.game_y = LastKeyExtraData.vp_x = LastKeyExtraData.vp_y = C4KeyEventData::KeyPos_None;
880 	// check all key events generated by this key: First the keycode itself, then any more generic key events like KEY_Any
881 	const int32_t iKeyRangeMax = 5;
882 	int32_t iKeyRangeCnt=0, j;
883 	C4KeyCode FallbackKeys[iKeyRangeMax];
884 	FallbackKeys[iKeyRangeCnt++] = InKey.Key;
885 	if (Key_IsGamepadButton(InKey.Key))
886 	{
887 		// "any gamepad button"-event
888 		FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(KEY_CONTROLLER_AnyButton);
889 	}
890 	else if (Key_IsGamepadAxis(InKey.Key))
891 	{
892 		// TODO: do we need "any axis" events?
893 	}
894 	if (InKey.Key != KEY_Any) FallbackKeys[iKeyRangeCnt++] = KEY_Any;
895 	// now get key ranges for fallback chain
896 	std::pair<KeyCodeMap::iterator, KeyCodeMap::iterator> KeyRanges[iKeyRangeMax];
897 	assert(iKeyRangeCnt <= iKeyRangeMax);
898 	for (int32_t i = 0; i<iKeyRangeCnt; ++i)
899 	{
900 		KeyRanges[i] = KeysByCode.equal_range(FallbackKeys[i]);
901 	}
902 	// check all assigned keys
903 	// exec from highest to lowest priority
904 	unsigned int uiLastPrio = C4CustomKey::PRIO_MoreThanMax;
905 	for (;;)
906 	{
907 		KeyCodeMap::const_iterator i;
908 		// get priority to exec
909 		unsigned int uiExecPrio = C4CustomKey::PRIO_None, uiCurr;
910 		for (j = 0; j < iKeyRangeCnt; ++j)
911 			for (i = KeyRanges[j].first; i != KeyRanges[j].second; ++i)
912 			{
913 				uiCurr = i->second->GetPriority();
914 				if (uiCurr > uiExecPrio && uiCurr < uiLastPrio) uiExecPrio = uiCurr;
915 			}
916 		// nothing with correct priority set left?
917 		if (uiExecPrio == C4CustomKey::PRIO_None) break;
918 		// exec all of this priority
919 		for (j = 0; j < iKeyRangeCnt; ++j)
920 			for (i = KeyRanges[j].first; i != KeyRanges[j].second; ++i)
921 			{
922 				C4CustomKey *pKey = i->second;
923 				assert(pKey);
924 				// check priority
925 				if (pKey->GetPriority() == uiExecPrio)
926 					// check scope and modifier
927 					// (not on release of a key that has been down, because a key release might happen with a different modifier or in different scope than its pressing!)
928 					if ((InEvent == KEYEV_Up && pKey->IsDown())
929 						|| ((pKey->GetScope() & InScope) && pKey->IsCodeMatched(C4KeyCodeEx(FallbackKeys[j], C4KeyShiftState(InKey.dwShift)))))
930 						// exec it
931 						if (pKey->Execute(InEvent, InKey))
932 								return true;
933 			}
934 		// nothing found in this priority: exec next
935 		uiLastPrio = uiExecPrio;
936 	}
937 	// no key matched or all returned false in Execute: Not processed
938 	return false;
939 }
940 
CompileFunc(StdCompiler * pComp)941 void C4KeyboardInput::CompileFunc(StdCompiler *pComp)
942 {
943 	// compile all keys that are already defined
944 	// no definition of new keys with current compiler...
945 	pComp->Name("Keys");
946 	try
947 	{
948 		for (KeyNameMap::const_iterator i = KeysByName.begin(); i != KeysByName.end(); ++i)
949 		{
950 			// naming done in C4CustomKey, because default is determined by key only
951 			C4CustomKey::CodeList OldCodes = i->second->GetCodes();
952 			pComp->Value(*i->second);
953 			// resort in secondary map if key changed
954 			if (pComp->isDeserializer())
955 			{
956 				const C4CustomKey::CodeList &rNewCodes = i->second->GetCodes();
957 				if (!(OldCodes == rNewCodes)) UpdateKeyCodes(i->second, OldCodes, rNewCodes);
958 			}
959 		}
960 	}
961 	catch (StdCompiler::Exception *pEx)
962 	{
963 		pComp->NameEnd(true);
964 		throw pEx;
965 	}
966 	pComp->NameEnd();
967 }
968 
LoadCustomConfig()969 bool C4KeyboardInput::LoadCustomConfig()
970 {
971 	// load from INI file (2do: load from registry)
972 	C4Group GrpExtra;
973 	if (!GrpExtra.Open(C4CFN_Extra)) return false;
974 	StdBuf sFileContents;
975 	if (!GrpExtra.LoadEntry(C4CFN_KeyConfig, &sFileContents)) return false;
976 	StdStrBuf sFileContentsString((const char *) sFileContents.getData());
977 	if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, sFileContentsString, "Custom keys from" C4CFN_Extra DirSep C4CFN_KeyConfig))
978 		return false;
979 	LogF(LoadResStr("IDS_PRC_LOADEDKEYCONF"), C4CFN_Extra DirSep C4CFN_KeyConfig);
980 	return true;
981 }
982 
GetKeyByName(const char * szKeyName)983 C4CustomKey *C4KeyboardInput::GetKeyByName(const char *szKeyName)
984 {
985 	KeyNameMap::const_iterator i = KeysByName.find(szKeyName);
986 	if (i == KeysByName.end()) return nullptr; else return (*i).second;
987 }
988 
GetKeyCodeNameByKeyName(const char * szKeyName,bool fShort,int32_t iIndex)989 StdStrBuf C4KeyboardInput::GetKeyCodeNameByKeyName(const char *szKeyName, bool fShort, int32_t iIndex)
990 {
991 	C4CustomKey *pKey = GetKeyByName(szKeyName);
992 	if (pKey)
993 	{
994 		const C4CustomKey::CodeList &codes = pKey->GetCodes();
995 		if ((size_t)iIndex < codes.size())
996 		{
997 			C4KeyCodeEx code = codes[iIndex];
998 			return code.ToString(true, fShort);
999 		}
1000 	}
1001 	// Error
1002 	return StdStrBuf();
1003 }
1004 
C4KeyboardInput_Init()1005 C4KeyboardInput &C4KeyboardInput_Init()
1006 {
1007 	static C4KeyboardInput keyinp;
1008 	return keyinp;
1009 }
1010