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)