OpenClonk
C4KeyboardInput.cpp
Go to the documentation of this file.
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 
39 {
41  const char *szName;
42 };
43 
45 {
46  { KEYS_Alt, "Alt" },
47  { KEYS_Control, "Ctrl" },
48  { KEYS_Shift, "Shift" },
49  { KEYS_Undefined, nullptr }
50 };
51 
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 
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 
71 {
73  const char *szName;
74  const char *szShortName;
75 };
76 
77 #if defined(USE_COCOA)
79 #else
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 
190 C4KeyCodeEx::C4KeyCodeEx(C4KeyCode key, DWORD Shift, bool fIsRepeated, int32_t deviceId)
191 : Key(key), dwShift(Shift), fRepeated(fIsRepeated), deviceId(deviceId)
192 {
193 }
194 
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 
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 
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 
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 
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 
492 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 
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)));
584  }
585  // write key
586  pComp->Value(mkDecompileAdapt(KeyCode2String(Key, false, false)));
587  }
588 }
589 
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 
603 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 
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 
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 
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 
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 
662 {
663  // free callback handles
664  for (CBVec::const_iterator i = vecCallbacks.begin(); i != vecCallbacks.end(); ++i)
665  (*i)->Deref();
666 }
667 
669 {
670  const CodeList &codes = GetCodes();
671  for (const auto &code : codes)
672  if (code == key)
673  return true;
674  return false;
675 }
676 
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 
694 {
695  // remove all instances from list
696  CBVec::iterator i;
697  while ((i = std::find_if(vecCallbacks.begin(), vecCallbacks.end(), [pOfKey](CBVec::value_type pIntfc) { return pIntfc->IsOriginalKey(pOfKey); })) != vecCallbacks.end())
698  {
699  C4KeyboardCallbackInterface *pItfc = *i;
700  vecCallbacks.erase(i);
701  pItfc->Deref();
702  }
703 }
704 
706 {
707  pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(Codes), Name.getData(), DefaultCodes));
708 }
709 
711 {
712  // remember down-state
713  is_down = (eEv == KEYEV_Down);
714  // execute all callbacks
715  for (auto & callback : vecCallbacks)
716  if (callback->OnKeyEvent(key, eEv))
717  return true;
718  // no event processed it
719  return false;
720 }
721 
722 
723 
724 /* ----------------- C4KeyBinding ------------------ */
725 
726 C4KeyBinding::C4KeyBinding(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority)
727  : C4CustomKey(DefCode, szName, Scope, pCallback, uiPriority)
728 {
729  // self holds a ref
730  Ref();
731  // register into keyboard input class
733 }
734 
735 C4KeyBinding::C4KeyBinding(const CodeList &rDefCodes, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority)
736  : C4CustomKey(rDefCodes, szName, Scope, pCallback, uiPriority)
737 {
738  // self holds a ref
739  Ref();
740  // register into keyboard input class
742 }
743 
745 {
746  // deregister from keyboard input class, if that class still exists
749  // shouldn't be refed now
750  assert(iRef==1);
751  iRef = 0;
752 }
753 
754 
755 /* ----------------- C4KeyboardInput ------------------ */
756 
757 bool C4KeyboardInput::IsValid = false;
758 
760 {
761  LastKeyExtraData = C4KeyEventData();
762  // release all keys - name map is guarantueed to contain them all
763  for (KeyNameMap::const_iterator i = KeysByName.begin(); i != KeysByName.end(); ++i)
764  i->second->Deref();
765  // clear maps
766  KeysByCode.clear();
767  KeysByName.clear();
768 }
769 
770 void C4KeyboardInput::UpdateKeyCodes(C4CustomKey *pKey, const C4CustomKey::CodeList &rOldCodes, const C4CustomKey::CodeList &rNewCodes)
771 {
772  // new key codes must be the new current key codes
773  assert(pKey->GetCodes() == rNewCodes);
774  // kill from old list
775  C4CustomKey::CodeList::const_iterator iCode;
776  for (iCode = rOldCodes.begin(); iCode != rOldCodes.end(); ++iCode)
777  {
778  // no need to kill if code stayed
779  if (std::find(rNewCodes.begin(), rNewCodes.end(), *iCode) != rNewCodes.end()) continue;
780  std::pair<KeyCodeMap::iterator, KeyCodeMap::iterator> KeyRange = KeysByCode.equal_range((*iCode).Key);
781  for (KeyCodeMap::iterator i = KeyRange.first; i != KeyRange.second; ++i)
782  if (i->second == pKey)
783  {
784  KeysByCode.erase(i);
785  break;
786  }
787  }
788  // readd new codes
789  for (iCode = rNewCodes.begin(); iCode != rNewCodes.end(); ++iCode)
790  {
791  // no double-add if it was in old list already
792  if (std::find(rOldCodes.begin(), rOldCodes.end(), *iCode) != rOldCodes.end()) continue;
793  KeysByCode.insert(std::make_pair((*iCode).Key, pKey));
794  }
795 }
796 
798 {
799  assert(pRegKey); if (!pRegKey) return;
800  // key will be added: ref it
801  pRegKey->Ref();
802  // search key of same name first
803  C4CustomKey *pDupKey = KeysByName[pRegKey->GetName().getData()];
804  if (pDupKey)
805  {
806  // key of this name exists: Merge them (old codes copied cuz they'll be overwritten)
807  C4CustomKey::CodeList OldCodes = pDupKey->GetCodes();
808  const C4CustomKey::CodeList &rNewCodes = pRegKey->GetCodes();
809  pDupKey->Update(pRegKey);
810  // update access map if key changed
811  if (!(OldCodes == rNewCodes)) UpdateKeyCodes(pDupKey, OldCodes, rNewCodes);
812  // key to be registered no longer used
813  pRegKey->Deref();
814  }
815  else
816  {
817  // new unique key: Insert into map
818  KeysByName[pRegKey->GetName().getData()] = pRegKey;
819  for (C4CustomKey::CodeList::const_iterator i = pRegKey->GetCodes().begin(); i != pRegKey->GetCodes().end(); ++i)
820  {
821  KeysByCode.insert(std::make_pair((*i).Key, pRegKey));
822  }
823  }
824 }
825 
827 {
828  // kill from name map
829  KeyNameMap::iterator in = KeysByName.find(rsName.getData());
830  if (in == KeysByName.end()) return;
831  C4CustomKey *pKey = in->second;
832  KeysByName.erase(in);
833  // kill all key bindings from key map
834  for (C4CustomKey::CodeList::const_iterator iCode = pKey->GetCodes().begin(); iCode != pKey->GetCodes().end(); ++iCode)
835  {
836  std::pair<KeyCodeMap::iterator, KeyCodeMap::iterator> KeyRange = KeysByCode.equal_range((*iCode).Key);
837  for (KeyCodeMap::iterator i = KeyRange.first; i != KeyRange.second; ++i)
838  if (i->second == pKey)
839  {
840  KeysByCode.erase(i);
841  break;
842  }
843  }
844  // release reference to key
845  pKey->Deref();
846 }
847 
849 {
850  // find key in name map
851  KeyNameMap::iterator in = KeysByName.find(pUnregKey->GetName().getData());
852  if (in == KeysByName.end()) return;
853  C4CustomKey *pKey = in->second;
854  // is this key in the map?
855  if (pKey != pUnregKey)
856  {
857  // Other key is in the list: Just remove the callbacks
858  pKey->KillCallbacks(pUnregKey);
859  return;
860  }
861  // this key is in the list: Replace by a duplicate...
862  C4CustomKey *pNewKey = new C4CustomKey(*pUnregKey, true);
863  // ...without the own callbacks
864  pNewKey->KillCallbacks(pUnregKey);
865  // and replace current key by duplicate
866  UnregisterKey(pUnregKey->GetName());
867  RegisterKey(pNewKey);
868 }
869 
870 bool C4KeyboardInput::DoInput(const C4KeyCodeEx &InKey, C4KeyEventType InEvent, DWORD InScope, int32_t iStrength)
871 {
872  // store last-key-info
873  LastKeyExtraData.iStrength = (iStrength >= 0) ? iStrength : ((InEvent != KEYEV_Up) * 100);
874  LastKeyExtraData.game_x = LastKeyExtraData.game_y = LastKeyExtraData.vp_x = LastKeyExtraData.vp_y = C4KeyEventData::KeyPos_None;
875  // check all key events generated by this key: First the keycode itself, then any more generic key events like KEY_Any
876  const int32_t iKeyRangeMax = 5;
877  int32_t iKeyRangeCnt=0, j;
878  C4KeyCode FallbackKeys[iKeyRangeMax];
879  FallbackKeys[iKeyRangeCnt++] = InKey.Key;
880  if (Key_IsGamepadButton(InKey.Key))
881  {
882  // "any gamepad button"-event
883  FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(KEY_CONTROLLER_AnyButton);
884  }
885  else if (Key_IsGamepadAxis(InKey.Key))
886  {
887  // TODO: do we need "any axis" events?
888  }
889  if (InKey.Key != KEY_Any) FallbackKeys[iKeyRangeCnt++] = KEY_Any;
890  // now get key ranges for fallback chain
891  std::pair<KeyCodeMap::iterator, KeyCodeMap::iterator> KeyRanges[iKeyRangeMax];
892  assert(iKeyRangeCnt <= iKeyRangeMax);
893  for (int32_t i = 0; i<iKeyRangeCnt; ++i)
894  {
895  KeyRanges[i] = KeysByCode.equal_range(FallbackKeys[i]);
896  }
897  // check all assigned keys
898  // exec from highest to lowest priority
899  unsigned int uiLastPrio = C4CustomKey::PRIO_MoreThanMax;
900  for (;;)
901  {
902  KeyCodeMap::const_iterator i;
903  // get priority to exec
904  unsigned int uiExecPrio = C4CustomKey::PRIO_None, uiCurr;
905  for (j = 0; j < iKeyRangeCnt; ++j)
906  for (i = KeyRanges[j].first; i != KeyRanges[j].second; ++i)
907  {
908  uiCurr = i->second->GetPriority();
909  if (uiCurr > uiExecPrio && uiCurr < uiLastPrio) uiExecPrio = uiCurr;
910  }
911  // nothing with correct priority set left?
912  if (uiExecPrio == C4CustomKey::PRIO_None) break;
913  // exec all of this priority
914  for (j = 0; j < iKeyRangeCnt; ++j)
915  for (i = KeyRanges[j].first; i != KeyRanges[j].second; ++i)
916  {
917  C4CustomKey *pKey = i->second;
918  assert(pKey);
919  // check priority
920  if (pKey->GetPriority() == uiExecPrio)
921  // check scope and modifier
922  // (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!)
923  if ((InEvent == KEYEV_Up && pKey->IsDown())
924  || ((pKey->GetScope() & InScope) && pKey->IsCodeMatched(C4KeyCodeEx(FallbackKeys[j], C4KeyShiftState(InKey.dwShift)))))
925  // exec it
926  if (pKey->Execute(InEvent, InKey))
927  return true;
928  }
929  // nothing found in this priority: exec next
930  uiLastPrio = uiExecPrio;
931  }
932  // no key matched or all returned false in Execute: Not processed
933  return false;
934 }
935 
937 {
938  // compile all keys that are already defined
939  // no definition of new keys with current compiler...
940  pComp->Name("Keys");
941  try
942  {
943  for (KeyNameMap::const_iterator i = KeysByName.begin(); i != KeysByName.end(); ++i)
944  {
945  // naming done in C4CustomKey, because default is determined by key only
946  C4CustomKey::CodeList OldCodes = i->second->GetCodes();
947  pComp->Value(*i->second);
948  // resort in secondary map if key changed
949  if (pComp->isDeserializer())
950  {
951  const C4CustomKey::CodeList &rNewCodes = i->second->GetCodes();
952  if (!(OldCodes == rNewCodes)) UpdateKeyCodes(i->second, OldCodes, rNewCodes);
953  }
954  }
955  }
956  catch (StdCompiler::Exception *pEx)
957  {
958  pComp->NameEnd(true);
959  throw pEx;
960  }
961  pComp->NameEnd();
962 }
963 
965 {
966  // load from INI file (2do: load from registry)
967  C4Group GrpExtra;
968  if (!GrpExtra.Open(C4CFN_Extra)) return false;
969  StdBuf sFileContents;
970  if (!GrpExtra.LoadEntry(C4CFN_KeyConfig, &sFileContents)) return false;
971  StdStrBuf sFileContentsString((const char *) sFileContents.getData());
972  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, sFileContentsString, "Custom keys from" C4CFN_Extra DirSep C4CFN_KeyConfig))
973  return false;
974  LogF(LoadResStr("IDS_PRC_LOADEDKEYCONF"), C4CFN_Extra DirSep C4CFN_KeyConfig);
975  return true;
976 }
977 
979 {
980  KeyNameMap::const_iterator i = KeysByName.find(szKeyName);
981  if (i == KeysByName.end()) return nullptr; else return (*i).second;
982 }
983 
984 StdStrBuf C4KeyboardInput::GetKeyCodeNameByKeyName(const char *szKeyName, bool fShort, int32_t iIndex)
985 {
986  C4CustomKey *pKey = GetKeyByName(szKeyName);
987  if (pKey)
988  {
989  const C4CustomKey::CodeList &codes = pKey->GetCodes();
990  if ((size_t)iIndex < codes.size())
991  {
992  C4KeyCodeEx code = codes[iIndex];
993  return code.ToString(true, fShort);
994  }
995  }
996  // Error
997  return StdStrBuf();
998 }
999 
1001 {
1002  static C4KeyboardInput keyinp;
1003  return keyinp;
1004 }
const char * KeycodeToString(C4KeyCode code)
Definition: C4AppSDL.cpp:248
#define C4CFN_Extra
Definition: C4Components.h:31
#define C4CFN_KeyConfig
Definition: C4Components.h:139
#define s
C4Game Game
Definition: C4Globals.cpp:52
const char * szShortName
C4KeyboardInput & C4KeyboardInput_Init()
const C4KeyShiftMapEntry KeyShiftMap[]
const C4KeyCodeMapEntry KeyCodeMap[]
C4KeyShiftState eShift
bool KEY_IsModifier(C4KeyCode k)
C4KeyCode KEY_Gamepad(uint8_t idButton)
const C4KeyCode KEY_MOUSE_Wheel1Down
uint8_t Key_GetGamepadEvent(C4KeyCode key)
const C4KeyCode KEY_CONTROLLER_ButtonRightStick
const C4KeyCode KEY_CONTROLLER_ButtonB
const C4KeyCode KEY_Any
const C4KeyCode KEY_MOUSE_ButtonX2Double
const C4KeyCode KEY_CONTROLLER_ButtonRightShoulder
const C4KeyCode KEY_Default
const C4KeyCode KEY_CONTROLLER_ButtonX
const C4KeyCode KEY_CONTROLLER_ButtonDpadUp
const C4KeyCode KEY_CONTROLLER_AxisLeftXLeft
const C4KeyCode KEY_CONTROLLER_ButtonDpadLeft
const C4KeyCode KEY_MOUSE_ButtonRightDouble
const C4KeyCode KEY_MOUSE_ButtonX1
C4KeyScope
@ KEYSCOPE_None
const C4KeyCode KEY_MOUSE_ButtonX2
C4KeyShiftState
@ KEYS_First
@ KEYS_Shift
@ KEYS_Control
@ KEYS_Max
@ KEYS_None
@ KEYS_Alt
@ KEYS_Undefined
const C4KeyCode KEY_CONTROLLER_ButtonLeftStick
const C4KeyCode KEY_CONTROLLER_ButtonStart
bool Key_IsGamepadAxis(C4KeyCode key)
const C4KeyCode KEY_MOUSE_Button1
const C4KeyCode KEY_CONTROLLER_AxisRightYDown
const C4KeyCode KEY_Undefined
const C4KeyCode KEY_CONTROLLER_ButtonA
const C4KeyCode KEY_CONTROLLER_AxisLeftYDown
const C4KeyCode KEY_CONTROLLER_ButtonY
const C4KeyCode KEY_MOUSE_ButtonMiddleDouble
uint8_t Key_GetMouse(C4KeyCode key)
const C4KeyCode KEY_CONTROLLER_ButtonDpadRight
bool Key_IsGamepad(C4KeyCode key)
const C4KeyCode KEY_CONTROLLER_AxisLeftYUp
C4KeyEventType
@ KEYEV_Up
@ KEYEV_Down
const C4KeyCode KEY_MOUSE_Move
bool Key_IsMouse(C4KeyCode key)
const C4KeyCode KEY_MOUSE_ButtonLeft
const C4KeyCode KEY_MOUSE_ButtonRight
const C4KeyCode KEY_MOUSE_ButtonX1Double
const C4KeyCode KEY_CONTROLLER_AxisRightXRight
const C4KeyCode KEY_CONTROLLER_AxisLeftXRight
const C4KeyCode KEY_CONTROLLER_AxisRightXLeft
C4KeyCode KEY_Mouse(uint8_t mouse_id, uint8_t mouseevent)
uint8_t Key_GetMouseEvent(C4KeyCode key)
const C4KeyCode KEY_CONTROLLER_ButtonGuide
const C4KeyCode KEY_CONTROLLER_ButtonBack
const C4KeyCode KEY_CONTROLLER_ButtonLeftShoulder
const C4KeyCode KEY_MOUSE_Button1Double
const C4KeyCode KEY_CONTROLLER_AxisTriggerLeft
const C4KeyCode KEY_CONTROLLER_AxisRightYUp
const C4KeyCode KEY_MOUSE_ButtonLeftDouble
const C4KeyCode KEY_CONTROLLER_AxisTriggerRight
const C4KeyCode KEY_CONTROLLER_AnyButton
const C4KeyCode KEY_MOUSE_Wheel1Up
const C4KeyCode KEY_MOUSE_ButtonMiddle
bool Key_IsGamepadButton(C4KeyCode key)
unsigned long C4KeyCode
const C4KeyCode KEY_CONTROLLER_ButtonDpadDown
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:262
const int32_t C4MC_Button_X1Double
const int32_t C4MC_Button_X2Up
const int32_t C4MC_Button_None
const int32_t C4MC_Button_RightDown
const int32_t C4MC_Button_MiddleUp
const int32_t C4MC_Button_RightUp
const int32_t C4MC_Button_MiddleDouble
const int32_t C4MC_Button_X1Up
const int32_t C4MC_Button_MiddleDown
const int32_t C4MC_Button_RightDouble
const int32_t C4MC_Button_LeftUp
const int32_t C4MC_Button_X1Down
const int32_t C4MC_Button_LeftDown
const int32_t C4MC_Button_LeftDouble
const int32_t C4MC_Button_X2Double
const int32_t C4MC_Button_X2Down
const int32_t C4MC_Button_Wheel
int stricmp(const char *s1, const char *s2)
#define DirSep
uint32_t DWORD
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:213
StdSTLContainerAdapt< C > mkSTLContainerAdapt(C &rTarget, StdCompiler::Sep eSep=StdCompiler::SEP_SEP)
Definition: StdAdaptors.h:713
StdDecompileAdapt< T > mkDecompileAdapt(const T &rValue)
Definition: StdAdaptors.h:153
StdParameterAdapt< T, P > mkParAdapt(T &&rObj, P &&rPar)
Definition: StdAdaptors.h:490
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
const CodeList & GetCodes() const
virtual ~C4CustomKey()
bool IsDown() const
std::vector< C4KeyCodeEx > CodeList
C4CustomKey(const C4KeyCodeEx &DefCode, const char *szName, C4KeyScope Scope, C4KeyboardCallbackInterface *pCallback, unsigned int uiPriority=PRIO_Base)
unsigned int GetPriority() const
const StdStrBuf & GetName() const
void CompileFunc(StdCompiler *pComp)
bool Execute(C4KeyEventType eEv, C4KeyCodeEx key)
void Update(const C4CustomKey *pByKey)
bool IsCodeMatched(const C4KeyCodeEx &key) const
void KillCallbacks(const C4CustomKey *pOfKey)
C4KeyScope GetScope() const
C4KeyboardInput & KeyboardInput
Definition: C4Game.h:96
bool LoadEntry(const char *entry_name, char **buffer, size_t *size_info=nullptr, int zeros_to_append=0)
Definition: C4Group.cpp:2375
bool Open(const char *group_name, bool do_create=false)
Definition: C4Group.cpp:660
~C4KeyBinding() override
class C4CustomKey * pOriginalKey
void UnregisterKeyBinding(C4CustomKey *pKey)
void UnregisterKey(const StdStrBuf &rsName)
C4CustomKey * GetKeyByName(const char *szKeyName)
static bool IsValid
bool DoInput(const C4KeyCodeEx &InKey, C4KeyEventType InEvent, DWORD InScope, int32_t iStrength)
void RegisterKey(C4CustomKey *pRegKey)
StdStrBuf GetKeyCodeNameByKeyName(const char *szKeyName, bool fShort=false, int32_t iIndex=0)
void CompileFunc(StdCompiler *pComp)
Definition: StdBuf.h:30
const void * getData() const
Definition: StdBuf.h:99
virtual bool Separator(Sep eSep=SEP_SEP)
Definition: StdCompiler.h:119
void excCorrupt(const char *szMessage,...)
Definition: StdCompiler.h:249
virtual void NoSeparator()
Definition: StdCompiler.h:120
void Value(const T &rStruct)
Definition: StdCompiler.h:161
virtual void NameEnd(bool fBreak=false)
Definition: StdCompiler.h:78
virtual bool isDeserializer()
Definition: StdCompiler.h:53
virtual bool Name(const char *szName)
Definition: StdCompiler.h:77
const char * getData() const
Definition: StdBuf.h:442
void AppendChar(char cChar)
Definition: StdBuf.h:588
void Copy()
Definition: StdBuf.h:467
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519
void Clear()
Definition: StdBuf.h:466
size_t getLength() const
Definition: StdBuf.h:445
void Take(char *pnData)
Definition: StdBuf.h:457
static C4KeyCode String2KeyCode(const StdStrBuf &sName)
static C4KeyCodeEx FromC4MC(int8_t mouse_id, int32_t button, DWORD param, bool *is_down=nullptr)
static StdStrBuf KeyCode2String(C4KeyCode wCode, bool fHumanReadable, bool fShort)
StdStrBuf ToString(bool fHumanReadable, bool fShort) const
C4KeyCode Key
C4KeyCodeEx(C4KeyCode Key=KEY_Default, DWORD Shift=KEYS_None, bool fIsRepeated=false, int32_t deviceId=-1)
static C4KeyShiftState String2KeyShift(const StdStrBuf &sName)
static StdStrBuf KeyShift2String(C4KeyShiftState eShift)
void CompileFunc(StdCompiler *pComp, StdStrBuf *pOutBuf=nullptr)
void CompileFunc(StdCompiler *pComp)
bool operator==(const struct C4KeyEventData &cmp) const