diff --git a/plugins/LilyPad/CMakeLists.txt b/plugins/LilyPad/CMakeLists.txt index d34f7994e3..799ada8207 100644 --- a/plugins/LilyPad/CMakeLists.txt +++ b/plugins/LilyPad/CMakeLists.txt @@ -32,6 +32,7 @@ set(lilypadSources LilyPad.cpp Linux/Config.cpp Linux/ConfigHelper.cpp + Linux/JoyEvdev.cpp Linux/KeyboardMouse.cpp Linux/KeyboardQueue.cpp ) diff --git a/plugins/LilyPad/DeviceEnumerator.cpp b/plugins/LilyPad/DeviceEnumerator.cpp index c06088c3d8..a848095d3f 100644 --- a/plugins/LilyPad/DeviceEnumerator.cpp +++ b/plugins/LilyPad/DeviceEnumerator.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); diff --git a/plugins/LilyPad/InputManager.h b/plugins/LilyPad/InputManager.h index 62e3111da9..f78ddf99ab 100644 --- a/plugins/LilyPad/InputManager.h +++ b/plugins/LilyPad/InputManager.h @@ -132,6 +132,7 @@ enum DeviceAPI { IGNORE_KEYBOARD = 7, // XXX LNX_KEYBOARD = 16, + LNX_JOY = 17, }; enum DeviceType { diff --git a/plugins/LilyPad/Linux/Config.cpp b/plugins/LilyPad/Linux/Config.cpp index f45a3468d3..eaf9a5e4f2 100644 --- a/plugins/LilyPad/Linux/Config.cpp +++ b/plugins/LilyPad/Linux/Config.cpp @@ -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 } } diff --git a/plugins/LilyPad/Linux/JoyEvdev.cpp b/plugins/LilyPad/Linux/JoyEvdev.cpp new file mode 100644 index 0000000000..1d83a57252 --- /dev/null +++ b/plugins/LilyPad/Linux/JoyEvdev.cpp @@ -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 . + */ + +#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); + } + +} diff --git a/plugins/LilyPad/Linux/JoyEvdev.h b/plugins/LilyPad/Linux/JoyEvdev.h new file mode 100644 index 0000000000..b084aa2441 --- /dev/null +++ b/plugins/LilyPad/Linux/JoyEvdev.h @@ -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 . + */ + +#include "Global.h" +#include "InputManager.h" + +#include +#include +#include +#include + +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 m_abs; + std::vector m_btn; + std::vector m_rel; + + public: + JoyEvdev(int fd, bool ds3, const wchar_t *id); + ~JoyEvdev(); + int Activate(InitInfo* args); + int Update(); +}; + +void EnumJoystickEvdev(); diff --git a/plugins/LilyPad/Linux/bitmaskros.h b/plugins/LilyPad/Linux/bitmaskros.h new file mode 100644 index 0000000000..2543022255 --- /dev/null +++ b/plugins/LilyPad/Linux/bitmaskros.h @@ -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)) & 1)