#include "../input/gamepad_device.h" #include "oslib/oslib.h" #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); char buf[256] = "Unknown"; if (ioctl(fd, EVIOCGNAME(sizeof(buf) - 1), buf) < 0) perror("evdev: ioctl(EVIOCGNAME)"); else INFO_LOG(INPUT, "evdev: Opened device '%s'", buf); _name = buf; buf[0] = 0; if (ioctl(fd, EVIOCGUNIQ(sizeof(buf) - 1), buf) == 0) _unique_id = buf; if (_unique_id.empty()) _unique_id = devnode; if (!find_mapping(mapping_file)) { #if defined(TARGET_PANDORA) mapping_file = "controller_pandora.cfg"; #elif defined(TARGET_GCW0) mapping_file = "controller_gcwz.cfg"; #else if (_name == "Microsoft X-Box 360 pad" || _name == "Xbox 360 Wireless Receiver" || _name == "Xbox 360 Wireless Receiver (XBOX)") { mapping_file = "controller_xpad.cfg"; } else if (_name.find("Xbox Gamepad (userspace driver)") != std::string::npos) { mapping_file = "controller_xboxdrv.cfg"; } else if (_name.find("keyboard") != std::string::npos || _name.find("Keyboard") != std::string::npos) { mapping_file = "keyboard.cfg"; } else { mapping_file = "controller_generic.cfg"; } #endif if (find_mapping(mapping_file)) { INFO_LOG(INPUT, "using default mapping '%s'", input_mapper->name.c_str()); input_mapper = new InputMapping(*input_mapper); } else input_mapper = new IdentityInputMapping(); input_mapper->name = _name + " mapping"; save_mapping(); } else INFO_LOG(INPUT, "using custom mapping '%s'", input_mapper->name.c_str()); } virtual ~EvdevGamepadDevice() override { INFO_LOG(INPUT, "evdev: Device '%s' on port %d disconnected", _name.c_str(), maple_port()); close(_fd); } virtual void rumble(float power, float inclination, u32 duration_ms) override { vib_inclination = inclination * power; vib_stop_time = os_GetSeconds() + duration_ms / 1000.0; 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); } } static std::shared_ptr GetControllerForPort(int port) { for (auto& pair : evdev_gamepads) if (pair.second->maple_port() == port) return pair.second; return NULL; } static std::shared_ptr GetControllerForDevnode(const char *devnode) { auto it = evdev_gamepads.find(devnode); if (it == evdev_gamepads.end()) return NULL; return it->second; } static void PollDevices() { for (auto& pair : evdev_gamepads) pair.second->read_input(); } static void CloseDevices() { while (!evdev_gamepads.empty()) RemoveDevice(evdev_gamepads.begin()->second); } static void AddDevice(std::shared_ptr gamepad) { evdev_gamepads[gamepad->_devnode] = gamepad; GamepadDevice::Register(gamepad); } static void RemoveDevice(std::shared_ptr gamepad) { evdev_gamepads.erase(gamepad->_devnode); GamepadDevice::Unregister(gamepad); } 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; 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]); } private: void read_input() { update_rumble(); 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; } } } 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"); } } } int _fd; std::string _devnode; int _rumble_effect_id = -1; float vib_inclination = 0; double vib_stop_time = 0; static std::map> evdev_gamepads; }; std::map> EvdevGamepadDevice::evdev_gamepads;