/* * The ManaPlus Client * Copyright (C) 2012-2020 The ManaPlus Developers * Copyright (C) 2020-2023 The ManaVerse Developers * * This file is part of The ManaPlus Client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "input/inputmanager.h" #include "configuration.h" #include "game.h" #include "settings.h" #include "being/localplayer.h" #include "being/playerinfo.h" #include "input/inputactionmap.h" #include "input/inputactionsortfunctor.h" #include "input/joystick.h" #include "input/keyboardconfig.h" #ifdef USE_SDL2 #include "input/touch/multitouchmanager.h" #endif // USE_SDL2 #include "input/touch/touchmanager.h" #include "gui/gui.h" #include "gui/sdlinput.h" #include "gui/widgets/selldialog.h" #include "gui/widgets/textfield.h" #include "gui/widgets/tabs/setup_input.h" #include "gui/windows/buydialog.h" #include "gui/windows/chatwindow.h" #include "gui/windows/inventorywindow.h" #include "gui/windows/npcdialog.h" #include "gui/windows/setupwindow.h" #include "gui/windows/textdialog.h" #include "gui/windows/tradewindow.h" #include "utils/checkutils.h" #include "utils/foreach.h" #include "utils/gettext.h" #include "utils/stdmove.h" #include "utils/timer.h" #include "gui/focushandler.h" #include #include "debug.h" InputManager inputManager; class QuitDialog; extern QuitDialog *quitDialog; namespace { InputActionSortFunctor inputActionDataSorter; } // namespace InputManager::InputManager() : mSetupInput(nullptr), mNewKeyIndex(InputAction::NO_VALUE), mMask(1), mNameMap(), mChatMap(), mKey() { } void InputManager::init() restrict2 { #ifdef __SWITCH__ SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "1"); #endif for (size_t i = 0; i < CAST_SIZE(InputAction::TOTAL); i ++) { InputFunction &kf = mKey[i]; mKeyStr[i].clear(); for (size_t f = 0; f < inputFunctionSize; f ++) { InputItem &ki = kf.values[f]; ki.type = InputType::UNKNOWN; ki.value = -1; } } mNewKeyIndex = InputAction::NO_VALUE; resetKeys(); retrieve(); update(); } void InputManager::update() { keyboard.update(); if (joystick != nullptr) joystick->update(); } void InputManager::retrieve() restrict2 { for (int i = 0; i < CAST_S32(InputAction::TOTAL); i ++) { const std::string &restrict cmd = inputActionData[i].chatCommand; if (!cmd.empty()) { StringVect tokens; splitToStringVector(tokens, cmd, '|'); FOR_EACH (StringVectCIter, it, tokens) mChatMap[*it] = i; } #ifdef USE_SDL2 const std::string cf = std::string("sdl2") + inputActionData[i].configField; #else // USE_SDL2 const std::string cf = inputActionData[i].configField; #endif // USE_SDL2 InputFunction &restrict kf = mKey[i]; if (!cf.empty()) { mNameMap[cf] = static_cast(i); const std::string keyStr = config.getValue(cf, ""); const size_t keyStrSize = keyStr.size(); if (keyStr.empty()) { updateKeyString(kf, i); continue; } StringVect keys; splitToStringVector(keys, keyStr, ','); unsigned int i2 = 0; for (StringVectCIter it = keys.begin(), it_end = keys.end(); it != it_end && i2 < inputFunctionSize; ++ it) { std::string keyStr2 = *it; if (keyStrSize < 2) continue; InputTypeT type = InputType::KEYBOARD; if ((keyStr2[0] < '0' || keyStr2[0] > '9') && keyStr2[0] != '-') { switch (keyStr2[0]) { case 'm': type = InputType::MOUSE; break; case 'j': type = InputType::JOYSTICK; break; default: break; } keyStr2 = keyStr2.substr(1); } const int key = atoi(keyStr2.c_str()); if (key >= -255 && key < SDLK_LAST) { kf.values[i2] = InputItem(type, key); i2 ++; } } for (; i2 < inputFunctionSize; i2 ++) kf.values[i2] = InputItem(); } updateKeyString(kf, i); } } void InputManager::store() const restrict2 { for (int i = 0; i < CAST_S32(InputAction::TOTAL); i ++) { #ifdef USE_SDL2 const std::string cf = std::string("sdl2") + inputActionData[i].configField; #else // USE_SDL2 const std::string cf = inputActionData[i].configField; #endif // USE_SDL2 if (!cf.empty()) { std::string keyStr; const InputFunction &restrict kf = mKey[i]; for (size_t i2 = 0; i2 < inputFunctionSize; i2 ++) { const InputItem &restrict key = kf.values[i2]; if (key.type != InputType::UNKNOWN) { std::string tmp("k"); switch (key.type) { case InputType::MOUSE: tmp = "m"; break; case InputType::JOYSTICK: tmp = "j"; break; case InputType::KEYBOARD: case InputType::UNKNOWN: default: break; } if (key.value != -1) { if (keyStr.empty()) { keyStr.append(tmp).append(toString(key.value)); } else { keyStr.append(strprintf(",%s%d", tmp.c_str(), key.value)); } } } } if (keyStr.empty()) keyStr = "-1"; config.setValue(cf, keyStr); } } } void InputManager::resetKey(const InputActionT i) restrict2 { InputFunction &restrict key = mKey[CAST_SIZE(i)]; for (size_t i2 = 1; i2 < inputFunctionSize; i2 ++) { InputItem &restrict ki2 = key.values[i2]; ki2.type = InputType::UNKNOWN; ki2.value = -1; } const InputActionData &restrict kd = inputActionData[CAST_SIZE(i)]; InputItem &restrict val0 = key.values[0]; val0.type = kd.defaultType1; InputItem &restrict val1 = key.values[1]; val1.type = kd.defaultType2; #ifdef USE_SDL2 if (kd.defaultType1 == InputType::KEYBOARD) { val0.value = SDL_GetScancodeFromKey(kd.defaultValue1); if (val0.value == SDL_SCANCODE_UNKNOWN) val0.value = -1; } else val0.value = kd.defaultValue1; if (kd.defaultType2 == InputType::KEYBOARD) { val1.value = SDL_GetScancodeFromKey(kd.defaultValue2); if (val1.value == SDL_SCANCODE_UNKNOWN) val1.value = -1; } else val1.value = kd.defaultValue2; #else // USE_SDL2 val0.value = kd.defaultValue1; val1.value = kd.defaultValue2; #endif // USE_SDL2 updateKeyString(key, CAST_SIZE(i)); } void InputManager::resetKeys() restrict2 { for (int i = 0; i < CAST_S32(InputAction::TOTAL); i++) resetKey(static_cast(i)); } void InputManager::makeDefault(const InputActionT i) restrict2 { if (i > InputAction::NO_VALUE && i < InputAction::TOTAL) { resetKey(i); update(); } } bool InputManager::hasConflicts(InputActionT &restrict key1, InputActionT &restrict key2) const restrict2 { /** * No need to parse the square matrix: only check one triangle * that's enough to detect conflicts */ for (int i = 0; i < CAST_S32(InputAction::TOTAL); i++) { const InputActionData &restrict kdi = inputActionData[i]; if (*kdi.configField == 0) continue; const InputFunction &restrict ki = mKey[i]; for (size_t i2 = 0; i2 < inputFunctionSize; i2 ++) { const InputItem &restrict vali2 = ki.values[i2]; if (vali2.value == -1) continue; size_t j; for (j = i, j++; j < CAST_S32(InputAction::TOTAL); j++) { if ((kdi.grp & inputActionData[j].grp) == 0 || (*kdi.configField == 0)) { continue; } for (size_t j2 = 0; j2 < inputFunctionSize; j2 ++) { const InputItem &restrict valj2 = mKey[j].values[j2]; // Allow for item shortcut and emote keys to overlap // as well as emote and ignore keys, but no other keys if (valj2.type != InputType::UNKNOWN && vali2.value == valj2.value && vali2.type == valj2.type) { key1 = static_cast(i); key2 = static_cast(j); return true; } } } } } return false; } void InputManager::callbackNewKey() restrict2 { #ifndef DYECMD mSetupInput->newKeyCallback(mNewKeyIndex); #endif // DYECMD } bool InputManager::isActionActive(const InputActionT index) const restrict2 { if (!isActionActive0(index)) return false; const InputActionData &restrict key = inputActionData[CAST_SIZE(index)]; // logger->log("isActionActive mask=%d, condition=%d, index=%d", // mMask, key.condition, index); if ((key.condition & mMask) != key.condition) return false; return true; } bool InputManager::isActionActive0(const InputActionT index) { if (keyboard.isActionActive(index)) return true; if ((joystick != nullptr) && joystick->isActionActive(index)) return true; return touchManager.isActionActive(index); } InputFunction &InputManager::getKey(InputActionT index) restrict2 { if (CAST_S32(index) < 0 || index >= InputAction::TOTAL) index = InputAction::MOVE_UP; return mKey[CAST_SIZE(index)]; } std::string InputManager::getKeyStringLong(const InputActionT index) const restrict2 { std::string keyStr; const InputFunction &restrict ki = mKey[CAST_SIZE(index)]; for (size_t i = 0; i < inputFunctionSize; i ++) { const InputItem &restrict key = ki.values[i]; std::string str; if (key.type == InputType::KEYBOARD) { if (key.value >= 0) { str = KeyboardConfig::getKeyName(key.value); } else if (key.value < -1) { // TRANSLATORS: long key name. must be short. str = strprintf(_("key_%d"), -key.value); } } else if (key.type == InputType::JOYSTICK) { if (key.value < Joystick::MAX_BUTTONS) { // TRANSLATORS: joystick button name. must be short. str = strprintf(_("JButton%d"), key.value + 1); } else if (key.value == Joystick::KEY_UP) { // TRANSLATORS: joystick up button. must be short. str = _("JUp"); } else if (key.value == Joystick::KEY_DOWN) { // TRANSLATORS: joystick down button. must be short. str = _("JDown"); } else if (key.value == Joystick::KEY_LEFT) { // TRANSLATORS: joystick left button. must be short. str = _("JLeft"); } else if (key.value == Joystick::KEY_RIGHT) { // TRANSLATORS: joystick right button. must be short. str = _("JRight"); } else if (key.value >= Joystick::KEY_NEGATIVE_AXIS_FIRST && key.value < Joystick::KEY_POSITIVE_AXIS_FIRST) { // TRANSLATORS: joystick negative axis. must be short. str = strprintf(_("JAxis%dMinus"), key.value - Joystick::KEY_NEGATIVE_AXIS_FIRST); } else if (key.value >= Joystick::KEY_POSITIVE_AXIS_FIRST && key.value < Joystick::KEY_END) { // TRANSLATORS: joystick positive axis. must be short. str = strprintf(_("JAxis%dPlus"), key.value - Joystick::KEY_POSITIVE_AXIS_FIRST); } } if (!str.empty()) { if (keyStr.empty()) keyStr = str; else keyStr.append(", ").append(str); } } if (keyStr.empty()) { // TRANSLATORS: unknown long key type return _("unknown key"); } return keyStr; } void InputManager::updateKeyString(const InputFunction &ki, const size_t actionIdx) restrict2 { std::string keyStr; for (size_t i = 0; i < inputFunctionSize; i ++) { const InputItem &restrict key = ki.values[i]; std::string str; if (key.type == InputType::KEYBOARD) { if (key.value >= 0) { str = KeyboardConfig::getKeyShortString( KeyboardConfig::getKeyName(key.value)); } else if (key.value < -1) { // TRANSLATORS: short key name. must be very short. str = strprintf(_("key_%d"), -key.value); } } else if (key.type == InputType::JOYSTICK) { if (key.value < Joystick::MAX_BUTTONS) { // TRANSLATORS: joystick button name. muse be very short str = strprintf(_("JB%d"), key.value + 1); } else if (key.value == Joystick::KEY_UP) { // TRANSLATORS: joystick up button. muse be very short str = _("JUp"); } else if (key.value == Joystick::KEY_DOWN) { // TRANSLATORS: joystick down button. muse be very short str = _("JDn"); } else if (key.value == Joystick::KEY_LEFT) { // TRANSLATORS: joystick left button. muse be very short str = _("JLt"); } else if (key.value == Joystick::KEY_RIGHT) { // TRANSLATORS: joystick right button. muse be very short str = _("JRt"); } else if (key.value >= Joystick::KEY_NEGATIVE_AXIS_FIRST && key.value < Joystick::KEY_POSITIVE_AXIS_FIRST) { // TRANSLATORS: joystick negative axis. must be very short. str = strprintf(_("JA%d-"), key.value - Joystick::KEY_NEGATIVE_AXIS_FIRST); } else if (key.value >= Joystick::KEY_POSITIVE_AXIS_FIRST && key.value < Joystick::KEY_END) { // TRANSLATORS: joystick positive axis. must be very short. str = strprintf(_("JA%d+"), key.value - Joystick::KEY_POSITIVE_AXIS_FIRST); } } if (!str.empty()) { if (keyStr.empty()) keyStr = str; else keyStr.append(", ").append(str); } } if (keyStr.empty()) { // TRANSLATORS: unknown short key type. must be short mKeyStr[actionIdx] = _("u key"); } else { mKeyStr[actionIdx] = STD_MOVE(keyStr); } } std::string InputManager::getKeyValueString(const InputActionT index) const restrict2 { return mKeyStr[CAST_SIZE(index)]; } std::string InputManager::getKeyValueByName(const std::string &restrict keyName) restrict2 { const StringInpActionMapCIter it = mNameMap.find(keyName); if (it == mNameMap.end()) return std::string(); return getKeyValueString((*it).second); } std::string InputManager::getKeyValueByNameLong(const std::string &restrict keyName) restrict2 { const StringInpActionMapCIter it = mNameMap.find(keyName); if (it == mNameMap.end()) return std::string(); return getKeyStringLong((*it).second); } void InputManager::addActionKey(const InputActionT action, const InputTypeT type, const int val) restrict2 { if (CAST_S32(action) < 0 || action >= InputAction::TOTAL) return; int idx = -1; InputFunction &restrict key = mKey[CAST_SIZE(action)]; for (size_t i = 0; i < inputFunctionSize; i ++) { const InputItem &restrict val2 = key.values[i]; if (val2.type == InputType::UNKNOWN || (val2.type == type && val2.value == val)) { idx = CAST_S32(i); break; } } if (idx == -1) { for (size_t i = 1; i < inputFunctionSize; i ++) { InputItem &restrict val1 = key.values[i - 1]; InputItem &restrict val2 = key.values[i]; val1.type = val2.type; val1.value = val2.value; } idx = inputFunctionSize - 1; } key.values[idx] = InputItem(type, val); updateKeyString(key, CAST_SIZE(action)); } void InputManager::setNewKey(const SDL_Event &event, const InputTypeT type) restrict2 { int val = -1; if (type == InputType::KEYBOARD) val = KeyboardConfig::getKeyValueFromEvent(event); else if (type == InputType::JOYSTICK && (joystick != nullptr)) val = joystick->getButtonFromEvent(event); if (val != -1) { addActionKey(mNewKeyIndex, type, val); update(); } } void InputManager::unassignKey() restrict2 { InputFunction &restrict key = mKey[CAST_SIZE(mNewKeyIndex)]; for (size_t i = 0; i < inputFunctionSize; i ++) { InputItem &restrict val = key.values[i]; val.type = InputType::UNKNOWN; val.value = -1; } updateKeyString(key, CAST_SIZE(mNewKeyIndex)); update(); } #ifndef DYECMD bool InputManager::handleAssignKey(const SDL_Event &restrict event, const InputTypeT type) restrict2 { if ((setupWindow != nullptr) && setupWindow->isWindowVisible() && getNewKeyIndex() > InputAction::NO_VALUE) { setNewKey(event, type); callbackNewKey(); setNewKeyIndex(InputAction::NO_VALUE); return true; } return false; } #else // DYECMD bool InputManager::handleAssignKey(const SDL_Event &restrict event A_UNUSED, const InputTypeT type A_UNUSED) restrict2 { return false; } #endif // DYECMD bool InputManager::handleEvent(const SDL_Event &restrict event) restrict2 { BLOCK_START("InputManager::handleEvent") switch (event.type) { case SDL_KEYDOWN: { #ifdef USE_SDL2 if (keyboard.ignoreKey(event)) { BLOCK_END("InputManager::handleEvent") return true; } #endif // USE_SDL2 keyboard.refreshActiveKeys(); updateConditionMask(true); if (handleAssignKey(event, InputType::KEYBOARD)) { BLOCK_END("InputManager::handleEvent") return true; } keyboard.handleActivateKey(event); // send straight to gui for certain windows #ifndef DYECMD if ((quitDialog != nullptr) || TextDialog::isActive()) { if (guiInput != nullptr) guiInput->pushInput(event); if (gui != nullptr) gui->handleInput(); BLOCK_END("InputManager::handleEvent") return true; } #endif // DYECMD break; } case SDL_KEYUP: { #ifdef USE_SDL2 if (keyboard.ignoreKey(event)) { BLOCK_END("InputManager::handleEvent") return true; } #endif // USE_SDL2 keyboard.refreshActiveKeys(); updateConditionMask(false); keyboard.handleDeActicateKey(event); break; } case SDL_JOYBUTTONDOWN: case SDL_JOYHATMOTION: case SDL_JOYAXISMOTION: { updateConditionMask(true); // joystick.handleActicateButton(event); if (joystick != nullptr && joystick->isActionEvent(event) && handleAssignKey(event, InputType::JOYSTICK)) { BLOCK_END("InputManager::handleEvent") return true; } break; } case SDL_JOYBUTTONUP: { updateConditionMask(false); // joystick.handleDeActicateButton(event); break; } #ifdef USE_SDL2 case SDL_FINGERDOWN: multiTouchManager.handleFingerDown(event); break; case SDL_FINGERUP: multiTouchManager.handleFingerUp(event); break; #else // USE_SDL2 #ifdef ANDROID case SDL_ACCELEROMETER: { break; } #endif // ANDROID #endif // USE_SDL2 default: break; } if (guiInput != nullptr) guiInput->pushInput(event); if (gui != nullptr) { const bool res = gui->handleInput(); const bool joystickActionEvent = joystick != nullptr && joystick->isActionEvent(event); if (res && (event.type == SDL_KEYDOWN || joystickActionEvent)) { BLOCK_END("InputManager::handleEvent") return true; } } switch (event.type) { case SDL_KEYDOWN: if (isActionActive(InputAction::IGNORE_INPUT_1) || isActionActive(InputAction::IGNORE_INPUT_2)) { BLOCK_END("InputManager::handleEvent") return true; } if (triggerAction(keyboard.getActionVector(event))) { BLOCK_END("InputManager::handleEvent") return true; } break; // disabled temporary // case SDL_KEYUP: // if (triggerAction(keyboard.getActionVector(event))) // { // BLOCK_END("InputManager::handleEvent") // return true; // } // break; case SDL_JOYBUTTONDOWN: case SDL_JOYHATMOTION: case SDL_JOYAXISMOTION: if ((joystick != nullptr) && joystick->validate()) { if (triggerAction(joystick->getActionVector(event))) { BLOCK_END("InputManager::handleEvent") return true; } } break; #ifdef ANDROID #ifndef USE_SDL2 case SDL_ACCELEROMETER: { break; } #endif // USE_SDL2 #endif // ANDROID default: break; } BLOCK_END("InputManager::handleEvent") return false; } void InputManager::handleRepeat() { const int time = tick_time; keyboard.handleRepeat(time); if (joystick != nullptr) joystick->handleRepeat(time); } void InputManager::updateConditionMask(const bool pressed A_UNUSED) restrict2 { mMask = 1; if (keyboard.isEnabled()) mMask |= InputCondition::ENABLED; #ifndef DYECMD if (((chatWindow == nullptr) || !chatWindow->isInputFocused()) && !NpcDialog::isAnyInputFocused() && !InventoryWindow::isAnyInputFocused() && ((tradeWindow == nullptr) || !tradeWindow->isInpupFocused())) { if (gui != nullptr) { FocusHandler *restrict const focus = gui->getFocusHandler(); if (focus != nullptr) { if (dynamic_cast(focus->getFocused()) == nullptr) mMask |= InputCondition::NOINPUT; } else { mMask |= InputCondition::NOINPUT; } } else { mMask |= InputCondition::NOINPUT; } } if (!BuyDialog::isActive() && !SellDialog::isActive()) mMask |= InputCondition::NOBUYSELL; if (!PlayerInfo::isVending()) mMask |= InputCondition::NOVENDING; if (!PlayerInfo::isInRoom()) mMask |= InputCondition::NOROOM; const NpcDialog *restrict const dialog = NpcDialog::getActive(); if ((dialog == nullptr) || !dialog->isTextInputFocused()) mMask |= InputCondition::NONPCINPUT; if ((dialog == nullptr) || (dialog->isCloseState() != 0)) { mMask |= InputCondition::NONPCDIALOG; if (!InventoryWindow::isStorageActive()) mMask |= InputCondition::NOTALKING; } if ((setupWindow == nullptr) || !setupWindow->isWindowVisible()) mMask |= InputCondition::NOSETUP; if ((Game::instance() != nullptr) && Game::instance()->getValidSpeed()) mMask |= InputCondition::VALIDSPEED; if (Game::instance() != nullptr) mMask |= InputCondition::INGAME; if (localPlayer != nullptr) { if (localPlayer->getFollow().empty()) mMask |= InputCondition::NOFOLLOW; if (!localPlayer->isTrickDead()) mMask |= InputCondition::NOBLOCK; if (localPlayer->isAlive()) mMask |= InputCondition::ALIVE; } else { mMask |= InputCondition::NOFOLLOW; mMask |= InputCondition::NOBLOCK; } #endif // DYECMD if (!settings.awayMode) mMask |= InputCondition::NOAWAY; if (gui != nullptr && gui->getFocusHandler()->getModalFocused() == nullptr) mMask |= InputCondition::NOMODAL; if (!settings.disableGameModifiers) mMask |= InputCondition::EMODS; if (!isActionActive0(InputAction::STOP_ATTACK) && !isActionActive0(InputAction::UNTARGET)) { mMask |= InputCondition::NOTARGET; } // enable it temporary mMask |= InputCondition::KEY_DOWN; // if (pressed == true) // mMask |= InputCondition::KEY_DOWN; // else // mMask |= InputCondition::KEY_UP; } bool InputManager::checkKey(const InputActionData *restrict const key) const restrict2 { // logger->log("checkKey mask=%d, condition=%d", mMask, key->condition); if ((key == nullptr) || (key->condition & mMask) != key->condition) return false; return key->modKeyIndex == InputAction::NO_VALUE || isActionActive0(key->modKeyIndex); } bool InputManager::invokeKey(const InputActionData *restrict const key, const InputActionT keyNum) restrict2 { // no validation to keyNum because it validated in caller if (checkKey(key)) { InputEvent evt(keyNum, mMask); ActionFuncPtr func = *(inputActionData[ CAST_SIZE(keyNum)].action); if ((func != nullptr) && func(evt)) return true; } return false; } void InputManager::executeAction(const InputActionT keyNum) restrict2 { if (keyNum < InputAction::MOVE_UP || keyNum >= InputAction::TOTAL) return; ActionFuncPtr func = *(inputActionData[CAST_SIZE( keyNum)].action); if (func != nullptr) { InputEvent evt(keyNum, mMask); func(evt); } } bool InputManager::executeChatCommand(const std::string &restrict cmd, const std::string &restrict args, ChatTab *restrict const tab) restrict2 { const StringIntMapCIter it = mChatMap.find(cmd); if (it != mChatMap.end()) { ActionFuncPtr func = *(inputActionData[(*it).second].action); if (func != nullptr) { InputEvent evt(args, tab, mMask); func(evt); return true; } } else { reportAlways("Unknown chat command: /%s %s", cmd.c_str(), args.c_str()) } return false; } bool InputManager::executeRemoteChatCommand(const std::string &restrict cmd, const std::string &restrict args, ChatTab *restrict const tab) restrict2 { const StringIntMapCIter it = mChatMap.find(cmd); if (it != mChatMap.end()) { const InputActionData &restrict data = inputActionData[(*it).second]; if (data.isProtected == Protected_true) return false; ActionFuncPtr func = *(data.action); if (func != nullptr) { InputEvent evt(args, tab, mMask); func(evt); return true; } } return false; } bool InputManager::executeChatCommand(const InputActionT keyNum, const std::string &restrict args, ChatTab *restrict const tab) restrict2 { if (CAST_S32(keyNum) < 0 || keyNum >= InputAction::TOTAL) return false; ActionFuncPtr func = *(inputActionData[CAST_SIZE( keyNum)].action); if (func != nullptr) { InputEvent evt(args, tab, mMask); func(evt); return true; } return false; } void InputManager::updateKeyActionMap(KeyToActionMap &restrict actionMap, KeyToIdMap &restrict idMap, KeyTimeMap &restrict keyTimeMap, const InputTypeT type) const restrict2 { actionMap.clear(); keyTimeMap.clear(); for (size_t i = 0; i < CAST_SIZE(InputAction::TOTAL); i ++) { const InputFunction &restrict key = mKey[i]; const InputActionData &restrict kd = inputActionData[i]; if (kd.action != nullptr) { for (size_t i2 = 0; i2 < inputFunctionSize; i2 ++) { const InputItem &restrict ki = key.values[i2]; if (ki.type == type && ki.value != -1) { actionMap[ki.value].push_back( static_cast(i)); } } } if (kd.configField != nullptr && (kd.grp & Input::GRP_GUICHAN) != 0) { for (size_t i2 = 0; i2 < inputFunctionSize; i2 ++) { const InputItem &restrict ki = key.values[i2]; if (ki.type == type && ki.value != -1) idMap[ki.value] = static_cast(i); } } if (kd.configField != nullptr && (kd.grp & Input::GRP_REPEAT) != 0) { for (size_t i2 = 0; i2 < inputFunctionSize; i2 ++) { const InputItem &restrict ki = key.values[i2]; if (ki.type == type && ki.value != -1) keyTimeMap[ki.value] = 0; } } } inputActionDataSorter.keys = &inputActionData[0]; FOR_EACH (KeyToActionMapIter, it, actionMap) { KeysVector *const keys = &it->second; if (keys->size() > 1) std::sort(keys->begin(), keys->end(), inputActionDataSorter); } } bool InputManager::triggerAction(const KeysVector *restrict const ptrs) restrict2 { if (ptrs == nullptr) return false; // logger->log("ptrs: %d", (int)ptrs.size()); FOR_EACHP (KeysVectorCIter, it, ptrs) { const InputActionT keyNum = *it; if (CAST_S32(keyNum) < 0 || keyNum >= InputAction::TOTAL) continue; if (invokeKey(&inputActionData[CAST_SIZE(keyNum)], keyNum)) return true; } return false; } InputActionT InputManager::getKeyIndex(const int value, const int grp, const InputTypeT type) const restrict2 { for (size_t i = 0; i < CAST_SIZE(InputAction::TOTAL); i++) { const InputFunction &restrict key = mKey[i]; const InputActionData &restrict kd = inputActionData[i]; for (size_t i2 = 0; i2 < inputFunctionSize; i2 ++) { const InputItem &restrict vali2 = key.values[i2]; if (value == vali2.value && (grp & kd.grp) != 0 && vali2.type == type) { return static_cast(i); } } } return InputAction::NO_VALUE; } InputActionT InputManager::getActionByKey(const SDL_Event &restrict event) const restrict2 { if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) { const InputActionT idx = keyboard.getActionId(event); if (CAST_S32(idx) >= 0 && checkKey(&inputActionData[CAST_SIZE(idx)])) { return idx; } } if (joystick != nullptr && joystick->isActionEvent(event)) { const InputActionT idx = joystick->getActionId(event); if (CAST_S32(idx) >= 0 && checkKey(&inputActionData[CAST_SIZE(idx)])) { return idx; } } return InputAction::NO_VALUE; } InputActionT InputManager::getActionByConfigField(const std::string &field) { for (int i = 0; i < CAST_S32(InputAction::TOTAL); i ++) { const std::string cf = inputActionData[i].configField; if (field == cf) return static_cast(i); } return InputAction::NO_VALUE; } void InputManager::addChatCommands(std::list &restrict arr) { const int sz = CAST_S32(InputAction::TOTAL); for (int i = 0; i < sz; i++) { const InputActionData &restrict ad = inputActionData[i]; std::string cmd = ad.chatCommand; if (!cmd.empty()) { StringVect tokens; splitToStringVector(tokens, cmd, '|'); FOR_EACH (StringVectCIter, it, tokens) { cmd = std::string("/").append(*it); if (ad.useArgs == UseArgs_true) cmd.append(" "); arr.push_back(cmd); } } } }