File indexing completed on 2024-05-12 05:27:54
0001 /* 0002 * SPDX-FileCopyrightText: 2022 Aleix Pol i Gonzalez <aleixpol@kde.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "evdevcontroller.h" 0008 #include "../controllermanager.h" 0009 #include "plasmarc-evdev-debug.h" 0010 #include <Solid/Block> 0011 #include <Solid/Device> 0012 #include <Solid/DeviceNotifier> 0013 #include <Solid/GenericInterface> 0014 0015 #include <QDirIterator> 0016 #include <QFile> 0017 #include <QSocketNotifier> 0018 0019 #include <fcntl.h> 0020 #include <linux/input-event-codes.h> 0021 0022 EvdevController::EvdevController() 0023 { 0024 auto notifier = Solid::DeviceNotifier::instance(); 0025 auto refresh = [this](const QString &udi) { 0026 Solid::Device device(udi); 0027 if (device.is<Solid::Block>()) { 0028 qCInfo(PLASMARC_EVDEV) << "Trying device on evdev:" << device.product() << device.as<Solid::Block>()->device(); 0029 addDevice(device); 0030 } 0031 }; 0032 connect(notifier, &Solid::DeviceNotifier::deviceAdded, this, refresh); 0033 0034 const auto devices = Solid::Device::listFromType(Solid::DeviceInterface::Block); 0035 for (const Solid::Device &device : devices) { 0036 addDevice(device); 0037 } 0038 } 0039 0040 bool EvdevController::addDevice(const Solid::Device &device) 0041 { 0042 auto inputDevice = device.as<Solid::Block>(); 0043 auto generic = device.as<Solid::GenericInterface>(); 0044 if (generic && !generic->property("ID_INPUT_JOYSTICK").toBool()) { 0045 return false; 0046 } 0047 0048 qDebug() << "trying" << device.udi() << device.displayName() << inputDevice->device(); 0049 struct libevdev *dev = NULL; 0050 QByteArray encoded(QFile::encodeName(inputDevice->device())); 0051 int fd = open(encoded.constData(), O_RDONLY | O_NONBLOCK); 0052 if (fd < 0) { 0053 qCDebug(PLASMARC_EVDEV) << "Failed to open" << inputDevice->device() << strerror(errno); 0054 libevdev_free(dev); 0055 return false; 0056 } 0057 0058 int rc = libevdev_new_from_fd(fd, &dev); 0059 if (rc < 0) { 0060 qCWarning(PLASMARC_EVDEV) << "Failed to init libevdev" << inputDevice->device() << strerror(-rc) << fd; 0061 close(fd); 0062 libevdev_free(dev); 0063 return false; 0064 } 0065 if (!libevdev_has_event_type(dev, EV_ABS) || !libevdev_has_event_code(dev, EV_KEY, BTN_MODE)) { 0066 qCDebug(PLASMARC_EVDEV) << "This device does not look like a remote controller:" << libevdev_get_name(dev); 0067 close(fd); 0068 libevdev_free(dev); 0069 return false; 0070 } 0071 0072 qCInfo(PLASMARC_EVDEV) << "Added evdev device:" << device.udi(); 0073 auto evdevDevice = new EvdevDevice(device.udi(), dev, this); 0074 ControllerManager::instance().newDevice(evdevDevice); 0075 0076 auto notifier = new QSocketNotifier(fd, QSocketNotifier::Read, evdevDevice); 0077 connect(notifier, &QSocketNotifier::activated, evdevDevice, &EvdevDevice::readNow); 0078 0079 return true; 0080 } 0081 0082 EvdevDevice::EvdevDevice(const QString &path, libevdev *device, EvdevController *controller) 0083 : Device(DeviceGamepad, QString::fromUtf8(libevdev_get_name(device)), path) 0084 , m_controller(controller) 0085 , m_device(device) 0086 , m_udi(path) 0087 , m_buttons({ 0088 {BTN_MODE, {KEY_LEFTMETA}}, 0089 {BTN_START, {KEY_GAMES}}, 0090 {BTN_SOUTH, {KEY_ENTER}}, 0091 {BTN_EAST, {KEY_CANCEL}}, 0092 {BTN_WEST, {KEY_MENU}}, 0093 {BTN_TL, {KEY_LEFTSHIFT, KEY_TAB}}, 0094 {BTN_TR, {KEY_TAB}}, 0095 {BTN_TL2, {KEY_BACK}}, 0096 {BTN_TR2, {KEY_FORWARD}}, 0097 {BTN_DPAD_UP, {KEY_UP}}, 0098 {BTN_DPAD_DOWN, {KEY_DOWN}}, 0099 {BTN_DPAD_LEFT, {KEY_LEFT}}, 0100 {BTN_DPAD_RIGHT, {KEY_RIGHT}}, 0101 }) 0102 { 0103 auto notifier = Solid::DeviceNotifier::instance(); 0104 connect(notifier, &Solid::DeviceNotifier::deviceRemoved, this, &EvdevDevice::deviceRemoved); 0105 0106 QSet<int> keys; 0107 for (auto keyCombination : m_buttons) { 0108 keys += QSet<int>(keyCombination.cbegin(), keyCombination.cend()); 0109 } 0110 setUsedKeys(keys); 0111 } 0112 0113 EvdevDevice::~EvdevDevice() 0114 { 0115 libevdev_free(m_device); 0116 } 0117 0118 void EvdevDevice::deviceRemoved(const QString &udi) 0119 { 0120 if (m_udi == udi) { 0121 deleteLater(); 0122 } 0123 } 0124 0125 void EvdevDevice::setKey(int key, bool pressed) 0126 { 0127 if (pressed == m_pressedKeys.contains(key)) { 0128 return; 0129 } 0130 0131 if (pressed) { 0132 m_pressedKeys.insert(key); 0133 } else { 0134 m_pressedKeys.remove(key); 0135 } 0136 m_controller->m_dbusInterface.emitKeyPress(key); 0137 ControllerManager::instance().emitKey(key, pressed); 0138 } 0139 0140 void EvdevDevice::readNow() 0141 { 0142 const int fd = libevdev_get_fd(m_device); 0143 struct input_event ev; 0144 int ret = read(fd, &ev, sizeof(ev)); 0145 if (ret == 0) { 0146 qDebug() << "nothing to read"; 0147 } else if (ret < 0) { 0148 qWarning() << "Error while reading" << strerror(errno); 0149 if (errno == 19) { 0150 ControllerManager::instance().deviceRemoved(this); 0151 deleteLater(); 0152 } 0153 } else { 0154 processEvent(ev); 0155 } 0156 0157 uint bytes; 0158 ret = ::ioctl(fd, FIONREAD, &bytes); 0159 if (ret == 0 && bytes >= sizeof(ev)) 0160 readNow(); 0161 } 0162 0163 void EvdevDevice::processEvent(struct input_event &ev) 0164 { 0165 if (ev.type == EV_KEY) { 0166 const auto nativeKeyCodes = m_buttons.value(ev.code); 0167 0168 if (!nativeKeyCodes.isEmpty()) { 0169 for (auto code : nativeKeyCodes) { 0170 m_controller->m_dbusInterface.emitKeyPress(ev.code); 0171 ControllerManager::instance().emitKey(code, ev.value); 0172 } 0173 return; 0174 } 0175 qCDebug(PLASMARC_EVDEV) << "Ignoring Key:" << libevdev_event_type_get_name(ev.type) << libevdev_event_code_get_name(ev.type, ev.code) << ev.value; 0176 } else if (ev.type == EV_ABS) { 0177 switch (ev.code) { 0178 case ABS_HAT0Y: 0179 setKey(KEY_DOWN, ev.value > 0); 0180 setKey(KEY_UP, ev.value < 0); 0181 return; 0182 case ABS_HAT0X: 0183 setKey(KEY_RIGHT, ev.value > 0); 0184 setKey(KEY_LEFT, ev.value < 0); 0185 return; 0186 } 0187 } 0188 } 0189 0190 #include "moc_evdevcontroller.cpp"