pcsx2/plugins/LilyPad/Linux/JoyEvdev.cpp

209 lines
5.8 KiB
C++

/* LilyPad - Pad plugin for PS2 Emulator
* Copyright (C) 2002-2015 PCSX2 Dev Team/ChickenLiver
*
* PCSX2 is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Found- ation, either version 3 of the License, or (at your option)
* any later version.
*
* PCSX2 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 PCSX2. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "InputManager.h"
#include "Linux/JoyEvdev.h"
#include "Linux/bitmaskros.h"
JoyEvdev::JoyEvdev(int fd, bool ds3, const wchar_t *id) : Device(LNX_JOY, OTHER, id, id), m_fd(fd) {
// XXX LNX_JOY => DS3 or ???
m_abs.clear();
m_btn.clear();
m_rel.clear();
int last = 0;
uint8_t abs_bitmap[nUcharsForNBits(ABS_CNT)] = {0};
uint8_t btn_bitmap[nUcharsForNBits(KEY_CNT)] = {0};
uint8_t rel_bitmap[nUcharsForNBits(REL_CNT)] = {0};
// Add buttons
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(btn_bitmap)), btn_bitmap) >= 0) {
for (int bit = BTN_MISC; bit < KEY_CNT; bit++) {
if (testBit(bit, btn_bitmap)) {
AddPhysicalControl(PSHBTN, last, 0);
m_btn.push_back(bit);
last++;
}
}
}
// Add Absolute axis
if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmap)), abs_bitmap) >= 0) {
for (int bit = 0; bit < ABS_CNT; bit++) {
ControlType type = ABSAXIS; // FIXME DS3
if (testBit(bit, abs_bitmap)) {
input_absinfo info;
if (ioctl(m_fd, EVIOCGABS(bit), &info) < 0) {
fprintf(stderr, "Invalid IOCTL EVIOCGID\n");
continue;
}
AddPhysicalControl(ABSAXIS, last, 0);
last++;
if (std::abs(info.value - 127) < 2) {
fprintf(stderr, "HALF Axis info %d=>%d, current %d, flat %d, resolution %d\n", info.minimum, info.maximum, info.value, info.flat, info.resolution);
// Half axis must be split into 2 parts...
AddPhysicalControl(ABSAXIS, last, 0);
last++;
m_abs.push_back(abs_info(bit, info.minimum, info.value, type));
m_abs.push_back(abs_info(bit, info.value, info.maximum, type));
} else {
fprintf(stderr, "FULL Axis info %d=>%d, current %d, flat %d, resolution %d\n", info.minimum, info.maximum, info.value, info.flat, info.resolution);
m_abs.push_back(abs_info(bit, info.minimum, info.maximum, type));
}
}
}
}
// Add relative axis
if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmap)), rel_bitmap) >= 0) {
for (int bit = 0; bit < REL_CNT; bit++) {
if (testBit(bit, rel_bitmap)) {
AddPhysicalControl(RELAXIS, last, last);
m_rel.push_back(bit);
last++;
fprintf(stderr, "Add relative nb %d\n", bit);
}
}
}
fprintf(stderr, "New device created. Found axe:%d, buttons:%d, m_rel:%d\n\n", m_abs.size(), m_btn.size(), m_rel.size());
}
JoyEvdev::~JoyEvdev() {
close(m_fd);
}
int JoyEvdev::Activate(InitInfo* args) {
AllocState();
uint16_t size = m_abs.size()+m_rel.size()+m_btn.size();
memset(physicalControlState, 0, sizeof(int)*size);
active = 1;
return 1;
}
int JoyEvdev::Update() {
struct input_event events[32];
int len;
int status = 0;
//fprintf(stderr, "Update was called\n");
// Do a big read to reduce kernel validation
while ((len = read(m_fd, events, (sizeof events))) > 0) {
int evt_nb = len / sizeof(input_event);
//fprintf(stderr, "Poll %d events available\n", evt_nb);
for (int i = 0; i < evt_nb; i++) {
switch(events[i].type) {
case EV_ABS:
{
for (size_t idx = 0; idx < m_abs.size(); idx++) {
if (m_abs[idx].code == events[i].code) {
// XXX strict or not ?
if ((events[i].value >= m_abs[idx].min) && (events[i].value <= m_abs[idx].max)) {
// XXX FIX shitty api
int scale = m_abs[idx].scale(events[i].value);
fprintf(stderr, "axis value %d scaled to %d\n", events[i].value, scale);
physicalControlState[idx + m_btn.size()] = scale;
status = 1;
}
}
}
}
break;
case EV_KEY:
{
for (size_t idx = 0; idx < m_btn.size(); idx++) {
if (m_btn[idx] == events[i].code) {
fprintf(stderr, "Event KEY:%d detected with value %d\n", events[i].code, events[i].value);
physicalControlState[idx] = FULLY_DOWN * events[i].value;
status = 1;
break;
}
}
}
break;
case EV_REL:
// XXX
break;
default:
break;
}
}
}
return status;
}
static std::wstring CorrectJoySupport(int fd) {
struct input_id id;
if (ioctl(fd, EVIOCGID, &id) < 0) {
fprintf(stderr, "Invalid IOCTL EVIOCGID\n");
return L"";
}
char dev_name[128];
if (ioctl(fd, EVIOCGNAME(128), dev_name) < 0) {
fprintf(stderr, "Invalid IOCTL EVIOCGNAME\n");
return L"";
}
fprintf(stderr, "Found input device => bustype:%x, vendor:%x, product:%x, version:%x\n", id.bustype, id.vendor, id.product, id.version);
fprintf(stderr, "\tName:%s\n", dev_name);
std::string s(dev_name);
return std::wstring(s.begin(), s.end());
}
void EnumJoystickEvdev() {
// Technically it must be done with udev but another lib for
// avoid a loop is too much for me (even if udev is mandatory
// so maybe later)
int found_devices = 0;
std::string input_root("/dev/input/event");
for (int i = 0; i < 32; i++) {
std::string dev = input_root + std::to_string(i);
int fd = open(dev.c_str(), O_RDWR | O_NONBLOCK);
if (fd < 0) {
continue;
}
std::wstring id = CorrectJoySupport(fd);
if (id.size() != 0) {
bool ds3 = id.find(L"PLAYSTATION(R)3") != std::string::npos;
if (ds3) {
fprintf(stderr, "DS3 device detected !!!\n");
}
dm->AddDevice(new JoyEvdev(fd, ds3, id.c_str()));
} else if (fd >= 0)
close(fd);
}
}