/* ** input-retro.cpp ** ** This file is part of mkxp. ** ** Copyright (C) 2013 - 2021 Amaryllis Kulla ** ** mkxp 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 ** (at your option) any later version. ** ** mkxp 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 mkxp. If not, see . */ #include "input.h" #include "core.h" #include "sharedstate.h" #include "graphics.h" #define JOYPAD_BUTTON_MAX 16 #define REPEAT_NONE 255 #define REPEAT_START (rgssVer >= 2 ? 0.375 : 0.400) #define REPEAT_DELAY 0.1 static std::unordered_map codeToJoypadId = { {Input::Down, RETRO_DEVICE_ID_JOYPAD_DOWN}, {Input::Left, RETRO_DEVICE_ID_JOYPAD_LEFT}, {Input::Right, RETRO_DEVICE_ID_JOYPAD_RIGHT}, {Input::Up, RETRO_DEVICE_ID_JOYPAD_UP}, {Input::A, RETRO_DEVICE_ID_JOYPAD_X}, {Input::B, RETRO_DEVICE_ID_JOYPAD_B}, {Input::C, RETRO_DEVICE_ID_JOYPAD_A}, {Input::X, RETRO_DEVICE_ID_JOYPAD_Y}, {Input::Y, RETRO_DEVICE_ID_JOYPAD_L3}, {Input::Z, RETRO_DEVICE_ID_JOYPAD_R3}, {Input::L, RETRO_DEVICE_ID_JOYPAD_L}, {Input::R, RETRO_DEVICE_ID_JOYPAD_R}, {Input::Shift, RETRO_DEVICE_ID_JOYPAD_L2}, {Input::Ctrl, RETRO_DEVICE_ID_JOYPAD_R2}, {Input::Alt, RETRO_DEVICE_ID_JOYPAD_SELECT}, }; static const uint8_t otherDirs[4][3] = { { RETRO_DEVICE_ID_JOYPAD_LEFT, RETRO_DEVICE_ID_JOYPAD_RIGHT, RETRO_DEVICE_ID_JOYPAD_DOWN }, /* Up */ { RETRO_DEVICE_ID_JOYPAD_LEFT, RETRO_DEVICE_ID_JOYPAD_RIGHT, RETRO_DEVICE_ID_JOYPAD_UP }, /* Down */ { RETRO_DEVICE_ID_JOYPAD_DOWN, RETRO_DEVICE_ID_JOYPAD_UP, RETRO_DEVICE_ID_JOYPAD_RIGHT }, /* Left */ { RETRO_DEVICE_ID_JOYPAD_DOWN, RETRO_DEVICE_ID_JOYPAD_UP, RETRO_DEVICE_ID_JOYPAD_LEFT }, /* Right */ }; struct InputPrivate { uint32_t repeatCount; uint16_t currJoypadState; uint16_t prevJoypadState; uint8_t repeat; uint8_t currDir4; uint8_t prevDir4; uint8_t dir8; bool joypadMaskSupported; InputPrivate() : repeatCount(0), currJoypadState(0), prevJoypadState(0), repeat(-1), currDir4(0), prevDir4(0), dir8(0), joypadMaskSupported(mkxp_retro::environment(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL)) {} void updateJoypad() { prevJoypadState = currJoypadState; if (joypadMaskSupported) { currJoypadState = (uint16_t)mkxp_retro::input_state(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); } else { currJoypadState = 0; for (uint8_t i = 0; i < JOYPAD_BUTTON_MAX; ++i) { if (mkxp_retro::input_state(0, RETRO_DEVICE_JOYPAD, 0, i)) { currJoypadState |= (1 << i); } } } if (repeat == REPEAT_NONE) { if (currJoypadState != 0) { for (uint8_t i = 0; i < JOYPAD_BUTTON_MAX; ++i) { if (currJoypadState & (1 << i)) { repeat = i; repeatCount = 0; break; } } } } else if (currJoypadState & (1 << repeat)) { __builtin_add_overflow(repeatCount, 1, &repeatCount); } else { repeat = REPEAT_NONE; repeatCount = 0; } } void updateDir4() { /* Check for dead keys */ if (((currJoypadState & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN)) && (currJoypadState & (1 << RETRO_DEVICE_ID_JOYPAD_UP))) || ((currJoypadState & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT)) && (currJoypadState & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT)))) { currDir4 = 0; return; } if (prevDir4 != 0) { /* Check if prev still pressed */ if (currJoypadState & (1 << prevDir4)) { for (size_t i = 0; i < 3; ++i) { uint8_t other = otherDirs[prevDir4 - RETRO_DEVICE_ID_JOYPAD_UP][i]; if (!(currJoypadState & (1 << other))) continue; currDir4 = other; return; } } } for (size_t i = 0; i < 4; ++i) { if (!(currJoypadState & (1 << (RETRO_DEVICE_ID_JOYPAD_UP + i)))) continue; currDir4 = prevDir4 = RETRO_DEVICE_ID_JOYPAD_UP + i; return; } currDir4 = prevDir4 = 0; } void updateDir8() { static const int combos[4][4] = { { 8, 0, 7, 9 }, { 0, 2, 1, 3 }, { 7, 1, 4, 0 }, { 9, 3, 0, 6 }, }; dir8 = 0; for (size_t i = 0; i < 4; ++i) { if (!(currJoypadState & (1 << (RETRO_DEVICE_ID_JOYPAD_UP + i)))) continue; for (int j = 0; j < 3; ++j) { uint8_t other = otherDirs[i][j]; if (!(currJoypadState & (1 << other))) continue; dir8 = combos[i][other - RETRO_DEVICE_ID_JOYPAD_UP]; return; } dir8 = RETRO_DEVICE_ID_JOYPAD_UP + i; return; } } bool getCurrJoypadState(int button) { auto it = codeToJoypadId.find(button); return it != codeToJoypadId.end() && (currJoypadState & (1 << it->second)); } bool getPrevJoypadState(int button) { auto it = codeToJoypadId.find(button); return it != codeToJoypadId.end() && (prevJoypadState & (1 << it->second)); } bool isRepeat(int button) { auto it = codeToJoypadId.find(button); return it != codeToJoypadId.end() && repeat == it->second; } }; Input::Input() { p = new InputPrivate(); } double Input::getDelta() { return 0.0; // TODO } void Input::update() { p->updateJoypad(); p->updateDir4(); p->updateDir8(); } bool Input::isPressed(int button) { return p->getCurrJoypadState(button); } bool Input::isTriggered(int button) { return p->getCurrJoypadState(button) && !p->getPrevJoypadState(button); } bool Input::isReleased(int button) { return p->getPrevJoypadState(button) && !p->getCurrJoypadState(button); } bool Input::isRepeated(int button) { int frame_rate = shState->graphics().getFrameRate(); return p->isRepeat(button) && (p->repeatCount == 0 || (p->repeatCount >= (size_t)std::ceil(REPEAT_START * frame_rate) && (p->repeatCount + 1) % (size_t)std::ceil(REPEAT_DELAY * frame_rate) == 0)); } unsigned int Input::count(int button) { return p->isRepeat(button) ? p->repeatCount : 0; } double Input::repeatTime(int button) { return p->isRepeat(button) ? (double)p->repeatCount / shState->graphics().getFrameRate() : 0; } bool Input::isPressedEx(int button, bool isVKey) { return false; // TODO } bool Input::isTriggeredEx(int button, bool isVKey) { return false; // TODO } bool Input::isReleasedEx(int button, bool isVKey) { return false; // TODO } bool Input::isRepeatedEx(int button, bool isVKey) { return false; // TODO } unsigned int Input::repeatcount(int button, bool isVKey) { return 0; // TODO } double Input::repeatTimeEx(int button, bool isVKey) { return 0.0; // TODO } bool Input::controllerIsPressedEx(int button) { return false; // TODO } bool Input::controllerIsTriggeredEx(int button) { return false; // TODO } bool Input::controllerIsReleasedEx(int button) { return false; // TODO } bool Input::controllerIsRepeatedEx(int button) { return false; // TODO } double Input::controllerRepeatTimeEx(int button) { return 0.0; // TODO } int Input::dir4Value() { switch (p->currDir4) { case RETRO_DEVICE_ID_JOYPAD_DOWN: return Input::Down; case RETRO_DEVICE_ID_JOYPAD_LEFT: return Input::Left; case RETRO_DEVICE_ID_JOYPAD_RIGHT: return Input::Right; case RETRO_DEVICE_ID_JOYPAD_UP: return Input::Up; default: return Input::None; } } int Input::dir8Value() { return p->dir8; } Input::~Input() { delete p; }