mirror of https://github.com/PCSX2/pcsx2.git
lilypad: add joy support based on linux evdev
I wanted to play with linux interface :) It is far from perfect but joysticks are detected. The biggest issue is the correct generation of the config file!
This commit is contained in:
parent
3a2e8f38df
commit
642371996a
|
@ -32,6 +32,7 @@ set(lilypadSources
|
|||
LilyPad.cpp
|
||||
Linux/Config.cpp
|
||||
Linux/ConfigHelper.cpp
|
||||
Linux/JoyEvdev.cpp
|
||||
Linux/KeyboardMouse.cpp
|
||||
Linux/KeyboardQueue.cpp
|
||||
)
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#ifdef __linux__
|
||||
#include "Linux/KeyboardMouse.h"
|
||||
#include "Linux/JoyEvdev.h"
|
||||
#endif
|
||||
|
||||
void EnumDevices(int hideDXXinput) {
|
||||
|
@ -46,6 +47,7 @@ void EnumDevices(int hideDXXinput) {
|
|||
EnumDirectInputDevices(hideDXXinput);
|
||||
#else
|
||||
EnumLnx();
|
||||
EnumJoystickEvdev();
|
||||
#endif
|
||||
|
||||
dm->CopyBindings(oldDm->numDevices, oldDm->devices);
|
||||
|
|
|
@ -132,6 +132,7 @@ enum DeviceAPI {
|
|||
IGNORE_KEYBOARD = 7,
|
||||
// XXX
|
||||
LNX_KEYBOARD = 16,
|
||||
LNX_JOY = 17,
|
||||
};
|
||||
|
||||
enum DeviceType {
|
||||
|
|
|
@ -480,6 +480,8 @@ void RefreshEnabledDevices(int updateDeviceList) {
|
|||
dev->displayName = newName;
|
||||
}
|
||||
|
||||
dm->EnableDevice(i);
|
||||
#if 0 // windows magic?
|
||||
if ((dev->type == KEYBOARD && dev->api == IGNORE_KEYBOARD) ||
|
||||
(dev->type == KEYBOARD && dev->api == config.keyboardApi) ||
|
||||
(dev->type == MOUSE && dev->api == config.mouseApi) ||
|
||||
|
@ -488,7 +490,6 @@ void RefreshEnabledDevices(int updateDeviceList) {
|
|||
(dev->api == DS3 && config.gameApis.dualShock3) ||
|
||||
(dev->api == XINPUT && config.gameApis.xInput)))) {
|
||||
dm->EnableDevice(i);
|
||||
#if 0 // windows magic?
|
||||
if (config.gameApis.dualShock3 && dev->api == DI && dev->displayName &&
|
||||
!wcsicmp(dev->displayName, L"DX PLAYSTATION(R)3 Controller")) {
|
||||
dm->DisableDevice(i);
|
||||
|
@ -496,11 +497,11 @@ void RefreshEnabledDevices(int updateDeviceList) {
|
|||
else {
|
||||
dm->EnableDevice(i);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
dm->DisableDevice(i);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
/* 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/* 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 <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
struct abs_info {
|
||||
uint16_t code;
|
||||
int32_t min;
|
||||
int32_t max;
|
||||
|
||||
int32_t factor;
|
||||
int32_t translation;
|
||||
|
||||
abs_info(int32_t _code, int32_t _min, int32_t _max, ControlType type) : code(_code), min(_min), max(_max) {
|
||||
translation = 0;
|
||||
// Note: ABSAXIS ranges from -64K to 64K
|
||||
// Note: PSHBTN ranges from 0 to 64K
|
||||
if ((min == 0) && (max == 255)) {
|
||||
if (type == ABSAXIS) {
|
||||
translation = 128;
|
||||
factor = FULLY_DOWN/128;
|
||||
} else {
|
||||
factor = FULLY_DOWN/256;
|
||||
}
|
||||
} else if ((min == -1) && (max == 1)) {
|
||||
factor = FULLY_DOWN;
|
||||
} else if ((min == 0) && (std::abs(max - 127) < 2)) {
|
||||
translation = 64;
|
||||
factor = -FULLY_DOWN/64;
|
||||
} else if ((max == 255) && (std::abs(min - 127) < 2)) {
|
||||
translation = 64+128;
|
||||
factor = FULLY_DOWN/64;
|
||||
} else {
|
||||
fprintf(stderr, "Scale not supported\n");
|
||||
factor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int scale(int32_t value) {
|
||||
return (value - translation) * factor;
|
||||
}
|
||||
};
|
||||
|
||||
class JoyEvdev : public Device {
|
||||
int m_fd;
|
||||
std::vector<abs_info> m_abs;
|
||||
std::vector<uint16_t> m_btn;
|
||||
std::vector<uint16_t> m_rel;
|
||||
|
||||
public:
|
||||
JoyEvdev(int fd, bool ds3, const wchar_t *id);
|
||||
~JoyEvdev();
|
||||
int Activate(InitInfo* args);
|
||||
int Update();
|
||||
};
|
||||
|
||||
void EnumJoystickEvdev();
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* bitmaskros.h
|
||||
*
|
||||
* Helper macros for large bit masks management
|
||||
*
|
||||
* Copyright (C) 2008 Jean-Philippe Meuret
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* Number of bits for 1 unsigned char */
|
||||
#define nBitsPerUchar (sizeof(unsigned char) * 8)
|
||||
|
||||
/* Number of unsigned chars to contain a given number of bits */
|
||||
#define nUcharsForNBits(nBits) ((((nBits)-1)/nBitsPerUchar)+1)
|
||||
|
||||
/* Index=Offset of given bit in 1 unsigned char */
|
||||
#define bitOffsetInUchar(bit) ((bit)%nBitsPerUchar)
|
||||
|
||||
/* Index=Offset of the unsigned char associated to the bit
|
||||
at the given index=offset */
|
||||
#define ucharIndexForBit(bit) ((bit)/nBitsPerUchar)
|
||||
|
||||
/* Value of an unsigned char with bit set at given index=offset */
|
||||
#define ucharValueForBit(bit) (((unsigned char)(1))<<bitOffsetInUchar(bit))
|
||||
|
||||
/* Test the bit with given index=offset in an unsigned char array */
|
||||
#define testBit(bit, array) ((array[ucharIndexForBit(bit)] >> bitOffsetInUchar(bit)) & 1)
|
Loading…
Reference in New Issue