flycast/core/input/virtual_gamepad.h

118 lines
3.1 KiB
C++

/*
Copyright 2024 flyinghead
This file is part of Flycast.
Flycast 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.
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "gamepad_device.h"
#include "ui/vgamepad.h"
class VirtualGamepad : public GamepadDevice
{
public:
VirtualGamepad(const char *api_name, int maple_port = 0)
: GamepadDevice(maple_port, api_name, false)
{
_name = "Virtual Gamepad";
_unique_id = "virtual_gamepad_uid";
input_mapper = std::make_shared<IdentityInputMapping>();
// hasAnalogStick = true; // TODO has an analog stick but input mapping isn't persisted
halfAxes.insert(DC_AXIS_LT);
halfAxes.insert(DC_AXIS_RT);
}
bool is_virtual_gamepad() override {
return true;
};
// normalized coordinates [-1, 1]
void joystickInput(float x, float y)
{
vgamepad::setAnalogStick(x, y);
int joyx = std::round(x * 32767.f);
int joyy = std::round(y * 32767.f);
if (joyx >= 0)
gamepad_axis_input(DC_AXIS_RIGHT, joyx);
else
gamepad_axis_input(DC_AXIS_LEFT, -joyx);
if (joyy >= 0)
gamepad_axis_input(DC_AXIS_DOWN, joyy);
else
gamepad_axis_input(DC_AXIS_UP, -joyy);
}
void releaseAll()
{
for (int i = 0; i < 32; i++)
if (buttonState & (1 << i))
gamepad_btn_input(1 << i, false);
buttonState = 0;
joystickInput(0, 0);
gamepad_axis_input(DC_AXIS_LT, 0);
gamepad_axis_input(DC_AXIS_RT, 0);
if (previousFastForward)
gamepad_btn_input(EMU_BTN_FFORWARD, false);
previousFastForward = false;
}
virtual bool handleButtonInput(u32& state, u32 key, bool pressed) {
// can be overridden in derived classes to handle specific key combos
// (iOS up+down or left+right)
return false;
}
void buttonInput(vgamepad::ControlId controlId, bool pressed)
{
u32 kcode = vgamepad::controlToDcKey(controlId);
if (kcode == 0)
return;
if (handleButtonInput(buttonState, kcode, pressed))
return;
if (kcode == DC_AXIS_LT) {
gamepad_axis_input(DC_AXIS_LT, pressed ? 0x7fff : 0);
}
else if (kcode == DC_AXIS_RT) {
gamepad_axis_input(DC_AXIS_RT, pressed ? 0x7fff : 0);
}
else if (kcode == EMU_BTN_SRVMODE) {
if (pressed)
vgamepad::toggleServiceMode();
}
else
{
if (pressed)
buttonState |= kcode;
else
buttonState &= ~kcode;
if ((kcode & (DC_DPAD_LEFT | DC_DPAD_RIGHT)) != 0
&& (kcode & (DC_DPAD_UP | DC_DPAD_DOWN)) != 0)
{
// diagonals
gamepad_btn_input(kcode & (DC_DPAD_LEFT | DC_DPAD_RIGHT), pressed);
gamepad_btn_input(kcode & (DC_DPAD_UP | DC_DPAD_DOWN), pressed);
}
else {
gamepad_btn_input(kcode, pressed);
}
}
}
private:
u32 buttonState = 0;
bool previousFastForward = false;
};