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