apps/runner/src/eu/runner/input.cc
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "eu/runner/input.h" | ||
| 2 | |||
| 3 | #include <SDL_keycode.h> | ||
| 4 | |||
| 5 | #include "eu/io/file.h" | ||
| 6 | #include "eu/kdl/kdl.h" | ||
| 7 | |||
| 8 | namespace eu::runner | ||
| 9 | { | ||
| 10 | |||
| 11 | ✗ | std::string lower(const std::string& str) | |
| 12 | { | ||
| 13 | ✗ | std::string result; | |
| 14 | ✗ | result.reserve(str.size()); | |
| 15 | ✗ | for (const char c : str) | |
| 16 | { | ||
| 17 | ✗ | result.push_back(static_cast<char>(std::tolower(c))); | |
| 18 | } | ||
| 19 | ✗ | return result; | |
| 20 | ✗ | } | |
| 21 | |||
| 22 | ✗ | std::optional<KeyboardKey> key_from_name(const std::string& name) | |
| 23 | { | ||
| 24 | static const std::unordered_map<std::string, KeyboardKey> key_map = { | ||
| 25 | #define K(name){lower(#name), SDLK_##name} | ||
| 26 | ✗ | K(RETURN), K(ESCAPE), K(BACKSPACE), K(TAB), K(SPACE), K(EXCLAIM), K(DBLAPOSTROPHE), | |
| 27 | ✗ | K(HASH), K(PERCENT), K(DOLLAR), K(AMPERSAND), K(APOSTROPHE), K(LEFTPAREN), | |
| 28 | ✗ | K(RIGHTPAREN), K(ASTERISK), K(PLUS), K(COMMA), K(MINUS), K(PERIOD), K(SLASH), | |
| 29 | ✗ | K(0), K(1), K(2), K(3), K(4), K(5), K(6), K(7), K(8), K(9), | |
| 30 | ✗ | K(COLON), K(SEMICOLON), K(LESS), K(EQUALS), K(GREATER), K(QUESTION), K(AT), | |
| 31 | ✗ | K(LEFTBRACKET), K(BACKSLASH), K(RIGHTBRACKET), K(CARET), K(UNDERSCORE), K(GRAVE), | |
| 32 | ✗ | K(A), K(B), K(C), K(D), K(E), K(F), K(G), K(H), K(I), K(J), | |
| 33 | ✗ | K(K), K(L), K(M), K(N), K(O), K(P), K(Q), K(R), K(S), K(T), | |
| 34 | ✗ | K(U), K(V), K(W), K(X), K(Y), K(Z), | |
| 35 | ✗ | K(CAPSLOCK), K(F1), K(F2), K(F3), K(F4), K(F5), K(F6), K(F7), K(F8), K(F9), K(F10), K(F11), K(F12), | |
| 36 | ✗ | K(PRINTSCREEN), K(SCROLLLOCK), K(PAUSE), K(INSERT), K(HOME), K(PAGEUP), K(DELETE), | |
| 37 | ✗ | K(END), K(PAGEDOWN), K(RIGHT), K(LEFT), K(DOWN), K(UP), K(NUMLOCKCLEAR), | |
| 38 | ✗ | K(KP_DIVIDE), K(KP_MULTIPLY), K(KP_MINUS), K(KP_PLUS), K(KP_ENTER), | |
| 39 | ✗ | K(KP_1), K(KP_2), K(KP_3), K(KP_4), K(KP_5), K(KP_6), K(KP_7), K(KP_8), K(KP_9), K(KP_0), | |
| 40 | ✗ | K(KP_PERIOD), K(APPLICATION), K(POWER), K(KP_EQUALS), | |
| 41 | ✗ | K(F13), K(F14), K(F15), K(F16), K(F17), K(F18), K(F19), K(F20), K(F21), K(F22), K(F23), K(F24), | |
| 42 | ✗ | K(EXECUTE), K(HELP), K(MENU), K(SELECT), K(STOP), K(AGAIN), K(UNDO), | |
| 43 | ✗ | K(CUT), K(COPY), K(PASTE), K(FIND), K(MUTE), K(VOLUMEUP), K(VOLUMEDOWN), | |
| 44 | ✗ | K(KP_COMMA), K(KP_EQUALSAS400), K(ALTERASE), K(SYSREQ), K(CANCEL), K(CLEAR), | |
| 45 | ✗ | K(PRIOR), K(RETURN2), K(SEPARATOR), K(OUT), K(OPER), K(CLEARAGAIN), K(CRSEL), | |
| 46 | ✗ | K(EXSEL), K(KP_00), K(KP_000), K(THOUSANDSSEPARATOR), K(DECIMALSEPARATOR), | |
| 47 | ✗ | K(CURRENCYUNIT), K(CURRENCYSUBUNIT), K(KP_LEFTPAREN), K(KP_RIGHTPAREN), K(KP_LEFTBRACE), | |
| 48 | ✗ | K(KP_RIGHTBRACE), K(KP_TAB), K(KP_BACKSPACE), K(KP_A), K(KP_B), K(KP_C), K(KP_D), | |
| 49 | ✗ | K(KP_E), K(KP_F), K(KP_XOR), K(KP_POWER), K(KP_PERCENT), K(KP_LESS), K(KP_GREATER), | |
| 50 | ✗ | K(KP_AMPERSAND), K(KP_DBLAMPERSAND), K(KP_VERTICALBAR), K(KP_DBLVERTICALBAR), K(KP_COLON), | |
| 51 | ✗ | K(KP_HASH), K(KP_SPACE), K(KP_AT), K(KP_EXCLAM), K(KP_MEMSTORE), K(KP_MEMRECALL), | |
| 52 | ✗ | K(KP_MEMCLEAR), K(KP_MEMADD), K(KP_MEMSUBTRACT), K(KP_MEMMULTIPLY), K(KP_MEMDIVIDE), | |
| 53 | ✗ | K(KP_PLUSMINUS), K(KP_CLEAR), K(KP_CLEARENTRY), K(KP_BINARY), K(KP_OCTAL), K(KP_DECIMAL), | |
| 54 | ✗ | K(KP_HEXADECIMAL), K(LCTRL), K(LSHIFT), K(LALT), K(LGUI), K(RCTRL), K(RSHIFT), K(RALT), | |
| 55 | ✗ | K(RGUI), K(MODE), K(MEDIA_NEXT_TRACK), K(MEDIA_PREVIOUS_TRACK), K(MEDIA_STOP), K(MEDIA_PLAY), K(MUTE), | |
| 56 | ✗ | K(MEDIA_SELECT), K(AC_SEARCH), K(AC_HOME), K(AC_BACK), | |
| 57 | ✗ | K(AC_FORWARD), K(AC_STOP), K(AC_REFRESH), K(AC_BOOKMARKS), | |
| 58 | ✗ | K(MEDIA_EJECT), K(SLEEP), K(MEDIA_REWIND), K(MEDIA_FAST_FORWARD), | |
| 59 | ✗ | K(SOFTLEFT), K(SOFTRIGHT), K(CALL), K(ENDCALL), | |
| 60 | #undef K | ||
| 61 | ✗ | }; | |
| 62 | |||
| 63 | ✗ | const auto it = key_map.find(lower(name)); | |
| 64 | ✗ | if (it != key_map.end()) | |
| 65 | { | ||
| 66 | ✗ | return it->second; | |
| 67 | } | ||
| 68 | |||
| 69 | ✗ | LOG_WARN("Unknown key name: '{}'", name); | |
| 70 | ✗ | return std::nullopt; | |
| 71 | ✗ | } | |
| 72 | |||
| 73 | ✗ | bool Input::load_from_file(const std::string& path) | |
| 74 | { | ||
| 75 | ✗ | const auto doc = kdl::parse(eu::io::string_from_file(path)); | |
| 76 | |||
| 77 | ✗ | bool ok = true; | |
| 78 | |||
| 79 | ✗ | for (const auto& node : doc) | |
| 80 | { | ||
| 81 | ✗ | if (node.name() == "action") | |
| 82 | { | ||
| 83 | ✗ | const auto name = node.args()[0].as_string(); | |
| 84 | |||
| 85 | ✗ | const auto existing = index_from_action.find(name); | |
| 86 | ✗ | if (existing != index_from_action.end()) | |
| 87 | { | ||
| 88 | ✗ | LOG_WARN("Duplicate action found: '{}'", name); | |
| 89 | ✗ | ok = false; | |
| 90 | ✗ | continue; | |
| 91 | } | ||
| 92 | ✗ | const auto new_index = actions.size(); | |
| 93 | ✗ | actions.emplace_back(Action{ | |
| 94 | .name = name, | ||
| 95 | .is_down = false, | ||
| 96 | .last_down = false | ||
| 97 | }); | ||
| 98 | ✗ | index_from_action[name] = new_index; | |
| 99 | ✗ | } | |
| 100 | ✗ | else if (node.name() == "key") | |
| 101 | { | ||
| 102 | ✗ | const auto action_name = node.args()[0].as_string(); | |
| 103 | ✗ | const auto found_action = index_from_action.find(action_name); | |
| 104 | ✗ | if (found_action == index_from_action.end()) | |
| 105 | { | ||
| 106 | ✗ | LOG_WARN("Invalid action name: {}", action_name); | |
| 107 | ✗ | ok = false; | |
| 108 | ✗ | continue; | |
| 109 | } | ||
| 110 | |||
| 111 | ✗ | const auto key_name = node.args()[1].as_string(); | |
| 112 | ✗ | const auto key = key_from_name(key_name); | |
| 113 | ✗ | if (!key) | |
| 114 | { | ||
| 115 | ✗ | LOG_WARN("Invalid key name: {}", key_name); | |
| 116 | ✗ | ok = false; | |
| 117 | ✗ | continue; | |
| 118 | } | ||
| 119 | |||
| 120 | const auto bind = KeyBind{ | ||
| 121 | ✗ | .sdl_keyboard_key = *key, | |
| 122 | ✗ | .bind_action = found_action->second | |
| 123 | ✗ | }; | |
| 124 | ✗ | bind_from_key[*key] = bind; | |
| 125 | ✗ | } | |
| 126 | else | ||
| 127 | { | ||
| 128 | ✗ | LOG_WARN("Unknown node in input file: '{}'", node.name()); | |
| 129 | ✗ | ok = false; | |
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | ✗ | return ok && !bind_from_key.empty(); | |
| 134 | ✗ | } | |
| 135 | |||
| 136 | |||
| 137 | ✗ | void Input::update() | |
| 138 | { | ||
| 139 | ✗ | for (auto& action : actions) | |
| 140 | { | ||
| 141 | ✗ | action.last_down = action.is_down; | |
| 142 | } | ||
| 143 | ✗ | } | |
| 144 | |||
| 145 | |||
| 146 | ✗ | void Input::on_key(int key, bool state) | |
| 147 | { | ||
| 148 | ✗ | const auto found = bind_from_key.find(key); | |
| 149 | ✗ | if (found == bind_from_key.end()) | |
| 150 | { | ||
| 151 | ✗ | return; | |
| 152 | } | ||
| 153 | |||
| 154 | ✗ | const KeyBind& bind = found->second; | |
| 155 | ✗ | auto& action = actions[bind.bind_action]; | |
| 156 | ✗ | action.is_down = state; | |
| 157 | } | ||
| 158 | |||
| 159 | |||
| 160 | ✗ | InputFrame Input::get_current_frame() const | |
| 161 | { | ||
| 162 | ✗ | InputFrame ret; | |
| 163 | ✗ | for (const auto& a: actions) | |
| 164 | { | ||
| 165 | ✗ | ret.binds.emplace_back(ScriptBind{ | |
| 166 | ✗ | .name = a.name, | |
| 167 | ✗ | .current = a.is_down ? 1.0f : 0.0f, | |
| 168 | ✗ | .last = a.last_down ? 1.0f : 0.0f | |
| 169 | }); | ||
| 170 | } | ||
| 171 | ✗ | return ret; | |
| 172 | ✗ | } | |
| 173 | |||
| 174 | |||
| 175 | |||
| 176 | } | ||
| 177 |