1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
|
// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <fcntl.h>
#include <libudev.h>
#include <map>
#include <unistd.h>
#include "Common/Assert.h"
#include "Common/Logging/Log.h"
#include "InputCommon/ControllerInterface/evdev/evdev.h"
namespace ciface
{
namespace evdev
{
static std::string GetName(const std::string& devnode)
{
int fd = open(devnode.c_str(), O_RDWR|O_NONBLOCK);
libevdev* dev = nullptr;
int ret = libevdev_new_from_fd(fd, &dev);
if (ret != 0)
{
close(fd);
return std::string();
}
std::string res = libevdev_get_name(dev);
libevdev_free(dev);
close(fd);
return res;
}
void Init(std::vector<Core::Device*> &controllerDevices)
{
// this is used to number the joysticks
// multiple joysticks with the same name shall get unique ids starting at 0
std::map<std::string, int> name_counts;
int num_controllers = 0;
// We use Udev to find any devices. In the future this will allow for hotplugging.
// But for now it is essentially iterating over /dev/input/event0 to event31. However if the
// naming scheme is ever updated in the future, this *should* be forwards compatable.
struct udev* udev = udev_new();
_assert_msg_(PAD, udev != 0, "Couldn't initilize libudev.");
// List all input devices
udev_enumerate* enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate, "input");
udev_enumerate_scan_devices(enumerate);
udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate);
// Iterate over all input devices
udev_list_entry* dev_list_entry;
udev_list_entry_foreach(dev_list_entry, devices)
{
const char* path = udev_list_entry_get_name(dev_list_entry);
udev_device* dev = udev_device_new_from_syspath(udev, path);
const char* devnode = udev_device_get_devnode(dev);
// We only care about devices which we have read/write access to.
if (devnode && access(devnode, W_OK) == 0)
{
// Unfortunately udev gives us no way to filter out the non event device interfaces.
// So we open it and see if it works with evdev ioctls or not.
std::string name = GetName(devnode);
evdevDevice* input = new evdevDevice(devnode, name_counts[name]++);
if (input->IsInteresting())
{
controllerDevices.push_back(input);
num_controllers++;
}
else
{
// Either it wasn't a evdev device, or it didn't have at least 8 buttons or two axis.
delete input;
}
}
udev_device_unref(dev);
}
udev_enumerate_unref(enumerate);
udev_unref(udev);
}
evdevDevice::evdevDevice(const std::string &devnode, int id) : m_devfile(devnode), m_id(id)
{
// The device file will be read on one of the main threads, so we open in non-blocking mode.
m_fd = open(devnode.c_str(), O_RDWR|O_NONBLOCK);
int ret = libevdev_new_from_fd(m_fd, &m_dev);
if (ret != 0)
{
// This useally fails because the device node isn't an evdev device, such as /dev/input/js0
m_initialized = false;
close(m_fd);
return;
}
m_name = libevdev_get_name(m_dev);
// Controller buttons (and keyboard keys)
int num_buttons = 0;
for (int key = 0; key < KEY_MAX; key++)
if (libevdev_has_event_code(m_dev, EV_KEY, key))
AddInput(new Button(num_buttons++, key, m_dev));
// Absolute axis (thumbsticks)
int num_axis = 0;
for (int axis = 0; axis < 0x100; axis++)
if (libevdev_has_event_code(m_dev, EV_ABS, axis))
{
AddAnalogInputs(new Axis(num_axis, axis, false, m_dev),
new Axis(num_axis, axis, true, m_dev));
num_axis++;
}
// Force feedback
if (libevdev_has_event_code(m_dev, EV_FF, FF_PERIODIC))
{
for (auto type : {FF_SINE, FF_SQUARE, FF_TRIANGLE, FF_SAW_UP, FF_SAW_DOWN})
if (libevdev_has_event_code(m_dev, EV_FF, type))
AddOutput(new ForceFeedback(type, m_dev));
}
if (libevdev_has_event_code(m_dev, EV_FF, FF_RUMBLE))
{
AddOutput(new ForceFeedback(FF_RUMBLE, m_dev));
}
// TODO: Add leds as output devices
m_initialized = true;
m_interesting = num_axis >= 2 || num_buttons >= 8;
}
evdevDevice::~evdevDevice()
{
if (m_initialized)
{
libevdev_free(m_dev);
close(m_fd);
}
}
void evdevDevice::UpdateInput()
{
// Run through all evdev events
// libevdev will keep track of the actual controller state internally which can be queried
// later with libevdev_fetch_event_value()
input_event ev;
int rc = LIBEVDEV_READ_STATUS_SUCCESS;
do
{
if (rc == LIBEVDEV_READ_STATUS_SYNC)
rc = libevdev_next_event(m_dev, LIBEVDEV_READ_FLAG_SYNC, &ev);
else
rc = libevdev_next_event(m_dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
} while (rc >= 0);
}
std::string evdevDevice::Button::GetName() const
{
// Buttons below 0x100 are mostly keyboard keys, and the names make sense
if (m_code < 0x100)
{
const char* name = libevdev_event_code_get_name(EV_KEY, m_code);
if (name)
return std::string(name);
}
// But controllers use codes above 0x100, and the standard label often doesn't match.
// We are better off with Button 0 and so on.
return "Button " + std::to_string(m_index);
}
ControlState evdevDevice::Button::GetState() const
{
int value = 0;
libevdev_fetch_event_value(m_dev, EV_KEY, m_code, &value);
return value;
}
evdevDevice::Axis::Axis(u8 index, u16 code, bool upper, libevdev* dev) :
m_code(code), m_index(index), m_upper(upper), m_dev(dev)
{
m_min = libevdev_get_abs_minimum(m_dev, m_code);
m_range = libevdev_get_abs_maximum(m_dev, m_code) + abs(m_min);
}
std::string evdevDevice::Axis::GetName() const
{
return "Axis " + std::to_string(m_index) + (m_upper ? "+" : "-");
}
ControlState evdevDevice::Axis::GetState() const
{
int value = 0;
libevdev_fetch_event_value(m_dev, EV_ABS, m_code, &value);
// Value from 0.0 to 1.0
ControlState fvalue = double(value - m_min) / double(m_range);
// Split into two axis, each covering half the range from 0.0 to 1.0
if (m_upper)
return std::max(0.0, fvalue - 0.5) * 2.0;
else
return (0.5 - std::min(0.5, fvalue)) * 2.0;
}
std::string evdevDevice::ForceFeedback::GetName() const
{
// We have some default names.
switch (m_type)
{
case FF_SINE:
return "Sine";
case FF_TRIANGLE:
return "Triangle";
case FF_SQUARE:
return "Square";
case FF_RUMBLE:
return "LeftRight";
default:
{
const char* name = libevdev_event_code_get_name(EV_FF, m_type);
if (name)
return std::string(name);
return "Unknown";
}
}
}
void evdevDevice::ForceFeedback::SetState(ControlState state)
{
// libevdev doesn't have nice helpers for forcefeedback
// we will use the file descriptors directly.
if (m_id != -1) // delete the previous effect (which also stops it)
{
ioctl(m_fd, EVIOCRMFF, m_id);
m_id = -1;
}
if (state > 0) // Upload and start an effect.
{
ff_effect effect;
effect.id = -1;
effect.direction = 0; // down
effect.replay.length = 500; // 500ms
effect.replay.delay = 0;
effect.trigger.button = 0; // don't trigger on button press
effect.trigger.interval = 0;
// This is the the interface that XInput uses, with 2 motors of differing sizes/frequencies that
// are controlled seperatally
if (m_type == FF_RUMBLE)
{
effect.type = FF_RUMBLE;
// max ranges tuned to 'feel' similar in magnitude to triangle/sine on xbox360 controller
effect.u.rumble.strong_magnitude = u16(state * 0x4000);
effect.u.rumble.weak_magnitude = u16(state * 0xFFFF);
}
else // FF_PERIODIC, a more generic interface.
{
effect.type = FF_PERIODIC;
effect.u.periodic.waveform = m_type;
effect.u.periodic.phase = 0x7fff; // 180 degrees
effect.u.periodic.offset = 0;
effect.u.periodic.period = 10;
effect.u.periodic.magnitude = s16(state * 0x7FFF);
effect.u.periodic.envelope.attack_length = 0; // no attack
effect.u.periodic.envelope.attack_level = 0;
effect.u.periodic.envelope.fade_length = 0;
effect.u.periodic.envelope.fade_level = 0;
}
ioctl(m_fd, EVIOCSFF, &effect);
m_id = effect.id;
input_event play;
play.type = EV_FF;
play.code = m_id;
play.value = 1;
write(m_fd, (const void*) &play, sizeof(play));
}
}
evdevDevice::ForceFeedback::~ForceFeedback()
{
// delete the uploaded effect, so we don't leak it.
if (m_id != -1)
{
ioctl(m_fd, EVIOCRMFF, m_id);
}
}
}
}
|