USB: Remove joydev support

This commit is contained in:
jackun 2021-04-06 16:59:35 +03:00
parent d0ada6b40a
commit 2925077169
6 changed files with 2 additions and 766 deletions

View File

@ -403,8 +403,6 @@ set(pcsx2USBSources
USB/shared/ringbuffer.cpp
USB/icon_buzz_24.cpp
USB/usb-msd/usb-msd-gtk.cpp
USB/usb-pad/joydev/joydev.cpp
USB/usb-pad/joydev/joydev-gtk.cpp
USB/usb-pad/evdev/shared-gtk.cpp
USB/usb-pad/evdev/evdev-ff.cpp
USB/usb-pad/evdev/evdev.cpp
@ -468,7 +466,6 @@ set(pcsx2USBHeaders
USB/linux/util.h
USB/gtk.h
USB/icon_buzz_24.h
USB/usb-pad/joydev/joydev.h
USB/usb-pad/evdev/shared.h
USB/usb-pad/evdev/evdev.h
USB/usb-pad/evdev/evdev-ff.h

View File

@ -15,11 +15,9 @@
#include "padproxy.h"
#include "evdev/evdev.h"
#include "joydev/joydev.h"
void usb_pad::RegisterPad::Register()
{
auto& inst = RegisterPad::instance();
inst.Add("evdev", new PadProxy<evdev::EvDevPad>());
inst.Add("joydev", new PadProxy<joydev::JoyDevPad>());
}

View File

@ -540,7 +540,7 @@ namespace usb_pad
cfg.use_hidraw_ff_pt = false;
bool is_evdev = (strncmp(apiname, "evdev", 5) == 0);
if (is_evdev) //TODO idk about joydev
if (is_evdev)
{
LoadSetting(dev_type, port, apiname, N_HIDRAW_FF_PT, cfg.use_hidraw_ff_pt);
}
@ -716,7 +716,7 @@ namespace usb_pad
std::stringstream str;
str << it.name;
if (!strcmp(apiname, "evdev") && !it.id.empty())
if (!it.id.empty())
str << " [" << it.id << "]";
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(rs_cb), str.str().c_str());

View File

@ -1,178 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* 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 "joydev.h"
#include <chrono>
#include <thread>
#include <stdio.h>
#include <sstream>
#include "USB/gtk.h"
namespace usb_pad
{
namespace joydev
{
using sys_clock = std::chrono::system_clock;
using ms = std::chrono::milliseconds;
#define JOYTYPE "joytype"
#define CFG "cfg"
static bool GetEventName(const char* dev_type, int map, int event, bool is_button, const char** name)
{
static char buf[256] = {0};
if (is_button)
{
snprintf(buf, sizeof(buf), "Button %d", event);
}
else
{
// assuming that PS2 axes are always mapped to PC axes
snprintf(buf, sizeof(buf), "Axis %d", event);
}
*name = buf;
return true;
}
static bool PollInput(const std::vector<std::pair<std::string, usb_pad::evdev::ConfigMapping>>& fds, std::string& dev_name, bool isaxis, int& value, bool& inverted, int& initial)
{
int event_fd = -1;
ssize_t len;
struct js_event event;
fd_set fdset;
int maxfd = -1;
FD_ZERO(&fdset);
for (const auto& js : fds)
{
FD_SET(js.second.fd, &fdset);
if (maxfd < js.second.fd)
maxfd = js.second.fd;
}
inverted = false;
// empty event queues
for (const auto& js : fds)
while ((len = read(js.second.fd, &event, sizeof(event))) > 0)
;
struct axis_value
{
int16_t value;
bool initial;
};
axis_value axisVal[ABS_MAX + 1] = {0};
struct timeval timeout
{
};
timeout.tv_sec = 5;
int result = select(maxfd + 1, &fdset, NULL, NULL, &timeout);
if (!result)
return false;
if (result == -1)
{
return false;
}
for (const auto& js : fds)
{
if (FD_ISSET(js.second.fd, &fdset))
{
event_fd = js.second.fd;
dev_name = js.first;
break;
}
}
if (event_fd == -1)
return false;
auto last = sys_clock::now();
//Non-blocking read sets len to -1 and errno to EAGAIN if no new data
while (true)
{
auto dur = std::chrono::duration_cast<ms>(sys_clock::now() - last).count();
if (dur > 5000)
goto error;
if ((len = read(event_fd, &event, sizeof(event))) > -1 && (len == sizeof(event)))
{
if (isaxis && event.type == JS_EVENT_AXIS)
{
auto& val = axisVal[event.number];
if (!val.initial)
{
val.value = event.value;
val.initial = true;
}
else
{
int diff = event.value - val.value;
if (std::abs(diff) > 2047)
{
value = event.number;
inverted = (diff < 0);
initial = val.value;
break;
}
}
}
else if (!isaxis && event.type == JS_EVENT_BUTTON)
{
if (event.value)
{
value = event.number;
break;
}
}
}
else if (errno != EAGAIN)
{
goto error;
}
else
{
while (gtk_events_pending())
gtk_main_iteration_do(FALSE);
std::this_thread::sleep_for(ms(1));
}
}
return true;
error:
return false;
}
int JoyDevPad::Configure(int port, const char* dev_type, void* data)
{
if (!strcmp(dev_type, BuzzDevice::TypeName()))
return RESULT_CANCELED;
evdev::ApiCallbacks apicbs{GetEventName, EnumerateDevices, PollInput};
int ret = evdev::GtkPadConfigure(port, dev_type, "Joydev Settings", joydev::APINAME, GTK_WINDOW(data), apicbs);
return ret;
}
} // namespace joydev
} // namespace usb_pad

View File

@ -1,497 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* 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 "joydev.h"
#include "USB/linux/util.h"
#include "Utilities/Console.h"
#include <cassert>
#include <sstream>
namespace usb_pad
{
namespace joydev
{
using namespace evdev;
#define NORM(x, n) (((uint32_t)(32768 + x) * n) / 0xFFFF)
#define NORM2(x, n) (((uint32_t)(32768 + x) * n) / 0x7FFF)
void EnumerateDevices(device_list& list)
{
int fd;
int res;
char buf[256];
std::stringstream str;
struct dirent* dp;
DIR* dirp = opendir("/dev/input/");
if (!dirp)
{
Console.Warning("Error opening /dev/input/");
return;
}
while ((dp = readdir(dirp)))
{
if (strncmp(dp->d_name, "js", 2) == 0)
{
str.clear();
str.str("");
str << "/dev/input/" << dp->d_name;
const std::string& path = str.str();
fd = open(path.c_str(), O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
Console.Warning("Joydev: Unable to open device: %s", path.c_str());
continue;
}
res = ioctl(fd, JSIOCGNAME(sizeof(buf)), buf);
if (res < 0)
Console.Warning("JSIOCGNAME");
else
{
list.push_back({buf, buf, path});
}
close(fd);
}
}
closedir(dirp);
}
int JoyDevPad::TokenIn(uint8_t* buf, int buflen)
{
ssize_t len;
struct js_event events[32];
fd_set fds;
int maxfd;
int range = range_max(mType);
FD_ZERO(&fds);
maxfd = -1;
for (auto& device : mDevices)
{
FD_SET(device.cfg.fd, &fds);
if (maxfd < device.cfg.fd)
maxfd = device.cfg.fd;
}
struct timeval timeout;
timeout.tv_usec = timeout.tv_sec = 0; // 0 - return from select immediately
int result = select(maxfd + 1, &fds, NULL, NULL, &timeout);
if (result <= 0)
{
return USB_RET_NAK; // If no new data, NAK it
}
for (auto& device : mDevices)
{
if (!FD_ISSET(device.cfg.fd, &fds))
{
continue;
}
//Non-blocking read sets len to -1 and errno to EAGAIN if no new data
while ((len = read(device.cfg.fd, &events, sizeof(events))) > -1)
{
len /= sizeof(events[0]);
for (int i = 0; i < len; i++)
{
js_event& event = events[i];
if ((event.type & ~JS_EVENT_INIT) == JS_EVENT_AXIS)
{
switch (device.axis_map[event.number])
{
case 0x80 | JOY_STEERING:
case ABS_X:
mWheelData.steering = device.cfg.inverted[0] ? range - NORM(event.value, range) : NORM(event.value, range);
break;
case ABS_Y:
mWheelData.clutch = NORM(event.value, 0xFF);
break;
//case ABS_RX: mWheelData.axis_rx = NORM(event.value, 0xFF); break;
case ABS_RY:
treat_me_like_ABS_RY:
mWheelData.throttle = 0xFF;
mWheelData.brake = 0xFF;
if (event.value < 0)
mWheelData.throttle = NORM2(event.value, 0xFF);
else
mWheelData.brake = NORM2(-event.value, 0xFF);
break;
case 0x80 | JOY_THROTTLE:
case ABS_Z:
if (device.is_gamepad)
mWheelData.brake = 0xFF - NORM(event.value, 0xFF);
else
mWheelData.throttle = device.cfg.inverted[1] ? NORM(event.value, 0xFF) : 0xFF - NORM(event.value, 0xFF);
break;
case 0x80 | JOY_BRAKE:
case ABS_RZ:
if (device.is_gamepad)
mWheelData.throttle = 0xFF - NORM(event.value, 0xFF);
else if (device.is_dualanalog)
goto treat_me_like_ABS_RY;
else
mWheelData.brake = device.cfg.inverted[2] ? NORM(event.value, 0xFF) : 0xFF - NORM(event.value, 0xFF);
break;
//FIXME hatswitch mapping maybe
case ABS_HAT0X:
case ABS_HAT1X:
case ABS_HAT2X:
case ABS_HAT3X:
if (event.value < 0) //left usually
mWheelData.hat_horz = PAD_HAT_W;
else if (event.value > 0) //right
mWheelData.hat_horz = PAD_HAT_E;
else
mWheelData.hat_horz = PAD_HAT_COUNT;
break;
case ABS_HAT0Y:
case ABS_HAT1Y:
case ABS_HAT2Y:
case ABS_HAT3Y:
if (event.value < 0) //up usually
mWheelData.hat_vert = PAD_HAT_N;
else if (event.value > 0) //down
mWheelData.hat_vert = PAD_HAT_S;
else
mWheelData.hat_vert = PAD_HAT_COUNT;
break;
default:
break;
}
}
else if ((event.type & ~JS_EVENT_INIT) == JS_EVENT_BUTTON)
{
PS2Buttons button = PAD_BUTTON_COUNT;
if (device.btn_map[event.number] >= (0x8000 | JOY_CROSS) &&
device.btn_map[event.number] <= (0x8000 | JOY_L3))
{
button = (PS2Buttons)(device.btn_map[event.number] & ~0x8000);
}
else if (device.btn_map[event.number] >= BTN_TRIGGER &&
device.btn_map[event.number] < BTN_BASE5)
{
button = (PS2Buttons)(device.btn_map[event.number] - BTN_TRIGGER);
}
else
{
// Map to xbox360ish controller
switch (device.btn_map[event.number])
{
// Digital hatswitch
case 0x8000 | JOY_LEFT:
mWheelData.hat_horz = PAD_HAT_W;
break;
case 0x8000 | JOY_RIGHT:
mWheelData.hat_horz = PAD_HAT_E;
break;
case 0x8000 | JOY_UP:
mWheelData.hat_vert = PAD_HAT_N;
break;
case 0x8000 | JOY_DOWN:
mWheelData.hat_vert = PAD_HAT_S;
break;
case BTN_WEST:
button = PAD_SQUARE;
break;
case BTN_NORTH:
button = PAD_TRIANGLE;
break;
case BTN_EAST:
button = PAD_CIRCLE;
break;
case BTN_SOUTH:
button = PAD_CROSS;
break;
case BTN_SELECT:
button = PAD_SELECT;
break;
case BTN_START:
button = PAD_START;
break;
case BTN_TR:
button = PAD_R1;
break;
case BTN_TL:
button = PAD_L1;
break;
case BTN_THUMBR:
button = PAD_R2;
break;
case BTN_THUMBL:
button = PAD_L2;
break;
default:
break;
}
}
//if (button != PAD_BUTTON_COUNT)
{
if (event.value)
mWheelData.buttons |= 1 << convert_wt_btn(mType, button); //on
else
mWheelData.buttons &= ~(1 << convert_wt_btn(mType, button)); //off
}
}
}
if (len <= 0)
{
break;
}
}
}
switch (mWheelData.hat_vert)
{
case PAD_HAT_N:
switch (mWheelData.hat_horz)
{
case PAD_HAT_W:
mWheelData.hatswitch = PAD_HAT_NW;
break;
case PAD_HAT_E:
mWheelData.hatswitch = PAD_HAT_NE;
break;
default:
mWheelData.hatswitch = PAD_HAT_N;
break;
}
break;
case PAD_HAT_S:
switch (mWheelData.hat_horz)
{
case PAD_HAT_W:
mWheelData.hatswitch = PAD_HAT_SW;
break;
case PAD_HAT_E:
mWheelData.hatswitch = PAD_HAT_SE;
break;
default:
mWheelData.hatswitch = PAD_HAT_S;
break;
}
break;
default:
mWheelData.hatswitch = mWheelData.hat_horz;
break;
}
pad_copy_data(mType, buf, mWheelData);
return buflen;
}
int JoyDevPad::TokenOut(const uint8_t* data, int len)
{
const ff_data* ffdata = (const ff_data*)data;
bool hires = (mType == WT_DRIVING_FORCE_PRO);
ParseFFData(ffdata, hires);
return len;
}
int JoyDevPad::Open()
{
device_list device_list;
bool has_steering;
int count;
int32_t b_gain, gain, b_ac, ac;
memset(&mWheelData, 0, sizeof(wheel_data_t));
// Setting to unpressed
mWheelData.steering = 0x3FF >> 1;
mWheelData.clutch = 0xFF;
mWheelData.throttle = 0xFF;
mWheelData.brake = 0xFF;
mWheelData.hatswitch = 0x8;
mWheelData.hat_horz = 0x8;
mWheelData.hat_vert = 0x8;
mHandleFF = -1;
std::string joypath;
/*if (!LoadSetting(mDevType, mPort, APINAME, N_JOYSTICK, joypath))
{
return 1;
}*/
EnumerateDevices(device_list);
if (!LoadSetting(mDevType, mPort, APINAME, N_GAIN_ENABLED, b_gain))
b_gain = 1;
if (!LoadSetting(mDevType, mPort, APINAME, N_GAIN, gain))
gain = 100;
if (!LoadSetting(mDevType, mPort, APINAME, N_AUTOCENTER_MANAGED, b_ac))
b_ac = 1;
if (!LoadSetting(mDevType, mPort, APINAME, N_AUTOCENTER, ac))
ac = 100;
for (const auto& it : device_list)
{
has_steering = false;
mDevices.push_back({});
struct device_data& device = mDevices.back();
device.name = it.name;
if ((device.cfg.fd = open(it.path.c_str(), O_RDWR | O_NONBLOCK)) < 0)
{
continue;
}
//int flags = fcntl(device.fd, F_GETFL, 0);
//fcntl(device.fd, F_SETFL, flags | O_NONBLOCK);
unsigned int version;
if (ioctl(device.cfg.fd, JSIOCGVERSION, &version) < 0)
{
SysMessage("%s: Get version failed: %s\n", APINAME, strerror(errno));
continue;
}
if (version < 0x010000)
{
SysMessage("%s: Driver version 0x%X is too old\n", APINAME, version);
continue;
}
LoadMappings(mDevType, mPort, device.name, 3, 16, device.cfg);
// Axis Mapping
if (ioctl(device.cfg.fd, JSIOCGAXMAP, device.axis_map) < 0)
{
SysMessage("%s: Axis mapping failed: %s\n", APINAME, strerror(errno));
continue;
}
else
{
if (ioctl(device.cfg.fd, JSIOCGAXES, &(count)) >= 0)
{
for (int i = 0; i < count; ++i)
{
for (int k = 0; k < count; k++)
{
for (int i = JOY_STEERING; i < JOY_MAPS_COUNT; i++)
{
if (k == device.cfg.controls[i])
{
device.axis_map[k] = 0x80 | i;
if (i == JOY_STEERING)
has_steering = true;
}
}
}
}
}
}
// Button Mapping
if (ioctl(device.cfg.fd, JSIOCGBTNMAP, device.btn_map) < 0)
{
SysMessage("%s: Button mapping failed: %s\n", APINAME, strerror(errno));
continue;
}
else
{
if (ioctl(device.cfg.fd, JSIOCGBUTTONS, &(count)) >= 0)
{
for (int i = 0; i < count; ++i)
{
if (device.btn_map[i] == BTN_GAMEPAD)
device.is_gamepad = true;
}
if (!device.is_gamepad) //TODO Don't remap if gamepad?
for (int k = 0; k < count; k++)
{
for (int i = 0; i < JOY_STEERING; i++)
{
if (k == device.cfg.controls[i])
device.btn_map[k] = 0x8000 | i;
}
}
}
}
std::stringstream event;
int index = 0;
const char* tmp = it.path.c_str();
while (*tmp && !isdigit(*tmp))
tmp++;
sscanf(tmp, "%d", &index);
//TODO kernel limit is 32?
for (int j = 0; j <= 99; j++)
{
event.clear();
event.str(std::string());
/* Try to discover the corresponding event number */
event << "/sys/class/input/js" << index << "/device/event" << j;
if (dir_exists(event.str()))
{
event.clear();
event.str(std::string());
event << "/dev/input/event" << j;
break;
}
}
if (!mFFdev && has_steering)
{
if ((mHandleFF = open(event.str().c_str(), /*O_WRONLY*/ O_RDWR)) < 0)
{
}
else
mFFdev = new evdev::EvdevFF(mHandleFF, b_gain, gain, b_ac, ac);
}
}
return 0;
}
int JoyDevPad::Close()
{
delete mFFdev;
mFFdev = nullptr;
if (mHandleFF != -1)
close(mHandleFF);
mHandleFF = -1;
for (auto& it : mDevices)
{
close(it.cfg.fd);
it.cfg.fd = -1;
}
return 0;
}
} // namespace joydev
} // namespace usb_pad

View File

@ -1,84 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2020 PCSX2 Dev Team
*
* 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/>.
*/
#pragma once
#include "USB/linux/util.h"
#include "USB/usb-pad/evdev/evdev-ff.h"
#include "USB/usb-pad/evdev/shared.h"
#include "Utilities/Console.h"
namespace usb_pad
{
namespace joydev
{
void EnumerateDevices(device_list& list);
static constexpr const char* APINAME = "joydev";
class JoyDevPad : public Pad
{
public:
JoyDevPad(int port, const char* dev_type)
: Pad(port, dev_type)
{
}
~JoyDevPad() { Close(); }
int Open();
int Close();
int TokenIn(uint8_t* buf, int len);
int TokenOut(const uint8_t* data, int len);
int Reset() { return 0; }
static const TCHAR* Name()
{
return "Joydev";
}
static int Configure(int port, const char* dev_type, void* data);
protected:
int mHandleFF = -1;
struct wheel_data_t mWheelData
{
};
std::vector<evdev::device_data> mDevices;
};
template <size_t _Size>
bool GetJoystickName(const std::string& path, char (&name)[_Size])
{
int fd = 0;
if ((fd = open(path.c_str(), O_RDONLY)) < 0)
{
Console.Warning("Cannot open %s\n", path.c_str());
}
else
{
if (ioctl(fd, JSIOCGNAME(_Size), name) < -1)
{
Console.Warning("Cannot get controller's name\n");
close(fd);
return false;
}
close(fd);
return true;
}
return false;
}
} // namespace joydev
} // namespace usb_pad