2019-02-21 11:46:56 +00:00
|
|
|
#include "../input/gamepad_device.h"
|
2019-02-22 18:23:03 +00:00
|
|
|
#include "oslib/oslib.h"
|
2019-02-21 11:46:56 +00:00
|
|
|
#include "evdev.h"
|
|
|
|
|
|
|
|
class EvdevGamepadDevice : public GamepadDevice
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
EvdevGamepadDevice(int maple_port, const char *devnode, int fd, const char *mapping_file = NULL)
|
|
|
|
: GamepadDevice(maple_port, "evdev"), _fd(fd), _rumble_effect_id(-1), _devnode(devnode)
|
|
|
|
{
|
|
|
|
fcntl(fd, F_SETFL, O_NONBLOCK);
|
2019-03-29 16:19:18 +00:00
|
|
|
char buf[256] = "Unknown";
|
|
|
|
if (ioctl(fd, EVIOCGNAME(sizeof(buf) - 1), buf) < 0)
|
2019-02-21 11:46:56 +00:00
|
|
|
perror("evdev: ioctl(EVIOCGNAME)");
|
|
|
|
else
|
2019-06-30 19:06:46 +00:00
|
|
|
INFO_LOG(INPUT, "evdev: Opened device '%s'", buf);
|
2019-03-29 16:19:18 +00:00
|
|
|
_name = buf;
|
|
|
|
buf[0] = 0;
|
|
|
|
if (ioctl(fd, EVIOCGUNIQ(sizeof(buf) - 1), buf) == 0)
|
|
|
|
_unique_id = buf;
|
|
|
|
if (_unique_id.empty())
|
|
|
|
_unique_id = devnode;
|
|
|
|
|
2019-02-21 11:46:56 +00:00
|
|
|
if (!find_mapping(mapping_file))
|
|
|
|
{
|
|
|
|
#if defined(TARGET_PANDORA)
|
|
|
|
mapping_file = "controller_pandora.cfg";
|
|
|
|
#elif defined(TARGET_GCW0)
|
|
|
|
mapping_file = "controller_gcwz.cfg";
|
|
|
|
#else
|
2019-03-29 16:19:18 +00:00
|
|
|
if (_name == "Microsoft X-Box 360 pad"
|
|
|
|
|| _name == "Xbox 360 Wireless Receiver"
|
|
|
|
|| _name == "Xbox 360 Wireless Receiver (XBOX)")
|
2019-02-21 11:46:56 +00:00
|
|
|
{
|
|
|
|
mapping_file = "controller_xpad.cfg";
|
|
|
|
}
|
2019-03-29 16:19:18 +00:00
|
|
|
else if (_name.find("Xbox Gamepad (userspace driver)") != std::string::npos)
|
2019-02-21 11:46:56 +00:00
|
|
|
{
|
|
|
|
mapping_file = "controller_xboxdrv.cfg";
|
|
|
|
}
|
2019-03-29 16:19:18 +00:00
|
|
|
else if (_name.find("keyboard") != std::string::npos
|
|
|
|
|| _name.find("Keyboard") != std::string::npos)
|
2019-02-21 11:46:56 +00:00
|
|
|
{
|
|
|
|
mapping_file = "keyboard.cfg";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mapping_file = "controller_generic.cfg";
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (find_mapping(mapping_file))
|
|
|
|
{
|
2019-06-30 19:06:46 +00:00
|
|
|
INFO_LOG(INPUT, "using default mapping '%s'", input_mapper->name.c_str());
|
2020-03-12 15:09:05 +00:00
|
|
|
input_mapper = std::make_shared<InputMapping>(*input_mapper);
|
2019-02-21 11:46:56 +00:00
|
|
|
}
|
|
|
|
else
|
2020-03-12 15:09:05 +00:00
|
|
|
input_mapper = std::make_shared<IdentityInputMapping>();
|
2019-02-21 11:46:56 +00:00
|
|
|
input_mapper->name = _name + " mapping";
|
|
|
|
save_mapping();
|
|
|
|
}
|
|
|
|
else
|
2019-06-30 19:06:46 +00:00
|
|
|
INFO_LOG(INPUT, "using custom mapping '%s'", input_mapper->name.c_str());
|
2019-02-21 11:46:56 +00:00
|
|
|
}
|
|
|
|
virtual ~EvdevGamepadDevice() override
|
|
|
|
{
|
2019-06-30 19:06:46 +00:00
|
|
|
INFO_LOG(INPUT, "evdev: Device '%s' on port %d disconnected", _name.c_str(), maple_port());
|
2019-02-21 11:46:56 +00:00
|
|
|
close(_fd);
|
|
|
|
}
|
|
|
|
|
2019-02-22 18:23:03 +00:00
|
|
|
virtual void rumble(float power, float inclination, u32 duration_ms) override
|
2019-02-21 11:46:56 +00:00
|
|
|
{
|
2019-02-22 18:23:03 +00:00
|
|
|
vib_inclination = inclination * power;
|
|
|
|
vib_stop_time = os_GetSeconds() + duration_ms / 1000.0;
|
2019-02-21 11:46:56 +00:00
|
|
|
|
2019-02-22 18:23:03 +00:00
|
|
|
do_rumble(power, duration_ms);
|
|
|
|
}
|
|
|
|
virtual void update_rumble() override
|
|
|
|
{
|
|
|
|
if (vib_inclination > 0)
|
|
|
|
{
|
|
|
|
int rem_time = (vib_stop_time - os_GetSeconds()) * 1000;
|
|
|
|
if (rem_time <= 0)
|
|
|
|
vib_inclination = 0;
|
|
|
|
else
|
|
|
|
do_rumble(vib_inclination * rem_time, rem_time);
|
2019-02-21 11:46:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 13:49:27 +00:00
|
|
|
static std::shared_ptr<EvdevGamepadDevice> GetControllerForPort(int port)
|
2019-02-21 11:46:56 +00:00
|
|
|
{
|
2019-06-08 11:04:35 +00:00
|
|
|
for (auto& pair : evdev_gamepads)
|
2019-02-21 11:46:56 +00:00
|
|
|
if (pair.second->maple_port() == port)
|
|
|
|
return pair.second;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-02-21 13:49:27 +00:00
|
|
|
static std::shared_ptr<EvdevGamepadDevice> GetControllerForDevnode(const char *devnode)
|
2019-02-21 11:46:56 +00:00
|
|
|
{
|
|
|
|
auto it = evdev_gamepads.find(devnode);
|
|
|
|
if (it == evdev_gamepads.end())
|
|
|
|
return NULL;
|
|
|
|
return it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PollDevices()
|
|
|
|
{
|
2019-06-08 11:04:35 +00:00
|
|
|
for (auto& pair : evdev_gamepads)
|
2019-02-21 11:46:56 +00:00
|
|
|
pair.second->read_input();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CloseDevices()
|
|
|
|
{
|
|
|
|
while (!evdev_gamepads.empty())
|
2019-02-21 13:49:27 +00:00
|
|
|
RemoveDevice(evdev_gamepads.begin()->second);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AddDevice(std::shared_ptr<EvdevGamepadDevice> gamepad)
|
|
|
|
{
|
|
|
|
evdev_gamepads[gamepad->_devnode] = gamepad;
|
|
|
|
GamepadDevice::Register(gamepad);
|
|
|
|
}
|
|
|
|
static void RemoveDevice(std::shared_ptr<EvdevGamepadDevice> gamepad)
|
|
|
|
{
|
|
|
|
evdev_gamepads.erase(gamepad->_devnode);
|
|
|
|
GamepadDevice::Unregister(gamepad);
|
2019-02-21 11:46:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual void load_axis_min_max(u32 axis) override
|
|
|
|
{
|
|
|
|
struct input_absinfo abs;
|
|
|
|
if (ioctl(_fd, EVIOCGABS(axis), &abs))
|
|
|
|
{
|
|
|
|
perror("evdev: ioctl(EVIOCGABS)");
|
|
|
|
axis_ranges[axis] = 255;
|
|
|
|
axis_min_values[axis] = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
axis_min_values[axis] = abs.minimum;
|
|
|
|
axis_ranges[axis] = abs.maximum - abs.minimum;
|
2019-06-30 19:06:46 +00:00
|
|
|
DEBUG_LOG(INPUT, "evdev: range of axis %d is from %d to %d", axis, axis_min_values[axis], axis_min_values[axis] + axis_ranges[axis]);
|
2019-02-21 11:46:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void read_input()
|
|
|
|
{
|
2019-02-22 18:23:03 +00:00
|
|
|
update_rumble();
|
2019-02-21 11:46:56 +00:00
|
|
|
input_event ie;
|
|
|
|
|
|
|
|
while (read(_fd, &ie, sizeof(ie)) == sizeof(ie))
|
|
|
|
{
|
|
|
|
switch (ie.type)
|
|
|
|
{
|
|
|
|
case EV_KEY:
|
|
|
|
gamepad_btn_input(ie.code, ie.value != 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EV_ABS:
|
|
|
|
gamepad_axis_input(ie.code, ie.value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2019-02-22 18:23:03 +00:00
|
|
|
void do_rumble(float power, u32 duration_ms)
|
|
|
|
{
|
|
|
|
// Remove previous effect
|
|
|
|
if (_rumble_effect_id != -1)
|
|
|
|
ioctl(_fd, EVIOCRMFF, _rumble_effect_id);
|
|
|
|
|
|
|
|
// Upload new effect
|
|
|
|
struct ff_effect effect;
|
|
|
|
effect.type = FF_RUMBLE;
|
|
|
|
effect.id = -1; // Let the driver assign one
|
|
|
|
effect.direction = 0;
|
|
|
|
effect.replay.length = (u16)duration_ms;
|
|
|
|
effect.replay.delay = 0;
|
|
|
|
effect.u.rumble.strong_magnitude = (s16)(power * 32767);
|
|
|
|
effect.u.rumble.weak_magnitude = (s16)(power * 32767);
|
|
|
|
if (ioctl(_fd, EVIOCSFF, &effect) == -1)
|
|
|
|
{
|
|
|
|
perror("evdev: Force feedback error");
|
|
|
|
_rumble_effect_id = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_rumble_effect_id = effect.id;
|
|
|
|
|
|
|
|
// Let's play the effect
|
|
|
|
input_event play;
|
|
|
|
play.type = EV_FF;
|
|
|
|
play.code = effect.id;
|
|
|
|
play.value = 1;
|
|
|
|
if (write(_fd, (const void*) &play, sizeof(play)) == -1)
|
|
|
|
{
|
|
|
|
perror("evdev: Force feedback error");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-02-21 11:46:56 +00:00
|
|
|
|
|
|
|
int _fd;
|
|
|
|
std::string _devnode;
|
2019-02-22 18:23:03 +00:00
|
|
|
int _rumble_effect_id = -1;
|
|
|
|
float vib_inclination = 0;
|
|
|
|
double vib_stop_time = 0;
|
2019-02-21 13:49:27 +00:00
|
|
|
static std::map<std::string, std::shared_ptr<EvdevGamepadDevice>> evdev_gamepads;
|
2019-02-21 11:46:56 +00:00
|
|
|
};
|
|
|
|
|
2019-02-21 13:49:27 +00:00
|
|
|
std::map<std::string, std::shared_ptr<EvdevGamepadDevice>> EvdevGamepadDevice::evdev_gamepads;
|