File indexing completed on 2024-04-28 16:42:44

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 "plasmarc-evdev-debug.h"
0009 #include "../controllermanager.h"
0010 #include <Solid/Block>
0011 #include <Solid/GenericInterface>
0012 #include <Solid/Device>
0013 #include <Solid/DeviceNotifier>
0014 
0015 #include <QDirIterator>
0016 #include <QFile>
0017 #include <QSocketNotifier>
0018 
0019 #include <linux/input-event-codes.h>
0020 #include <fcntl.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     int fd = open(QFile::encodeName(inputDevice->device()), O_RDONLY|O_NONBLOCK);
0051     if (fd < 0) {
0052         qCDebug(PLASMARC_EVDEV) << "Failed to open" << inputDevice->device() << strerror(errno);
0053         libevdev_free(dev);
0054         return false;
0055     }
0056 
0057     int rc = libevdev_new_from_fd(fd, &dev);
0058     if (rc < 0) {
0059         qCWarning(PLASMARC_EVDEV) << "Failed to init libevdev" << inputDevice->device() << strerror(-rc) << fd;
0060         close(fd);
0061         libevdev_free(dev);
0062         return false;
0063     }
0064     if (!libevdev_has_event_type(dev, EV_ABS) ||
0065         !libevdev_has_event_code(dev, EV_KEY, BTN_MODE))
0066     {
0067         qCDebug(PLASMARC_EVDEV) << "This device does not look like a remote controller:" << libevdev_get_name(dev);
0068         close(fd);
0069         libevdev_free(dev);
0070         return false;
0071     }
0072 
0073     qCInfo(PLASMARC_EVDEV) << "Added evdev device:" << device.udi();
0074     auto evdevDevice = new EvdevDevice(device.udi(), dev, this);
0075     ControllerManager::instance().newDevice(evdevDevice);
0076 
0077     auto notifier = new QSocketNotifier(fd, QSocketNotifier::Read, evdevDevice);
0078     connect(notifier, &QSocketNotifier::activated, evdevDevice, &EvdevDevice::readNow);
0079 
0080     return true;
0081 }
0082 
0083 EvdevDevice::EvdevDevice(const QString& path, libevdev *device, EvdevController *controller)
0084     : Device(DeviceGamepad, QString::fromUtf8(libevdev_get_name(device)), path)
0085     , m_controller(controller)
0086     , m_device(device)
0087     , m_udi(path)
0088     , m_buttons ({
0089         { BTN_MODE, { KEY_LEFTMETA } },
0090         { BTN_START, { KEY_GAMES } },
0091         { BTN_SOUTH, { KEY_ENTER } },
0092         { BTN_EAST, { KEY_CANCEL } },
0093         { BTN_WEST, { KEY_MENU } },
0094         { BTN_TL, { KEY_LEFTSHIFT, KEY_TAB } },
0095         { BTN_TR, { KEY_TAB } },
0096         { BTN_TL2, { KEY_BACK } },
0097         { BTN_TR2, { KEY_FORWARD } },
0098         { BTN_DPAD_UP, { KEY_UP } },
0099         { BTN_DPAD_DOWN, { KEY_DOWN } },
0100         { BTN_DPAD_LEFT, { KEY_LEFT } },
0101         { BTN_DPAD_RIGHT, { KEY_RIGHT } }
0102     })
0103 {
0104     auto notifier = Solid::DeviceNotifier::instance();
0105     connect(notifier, &Solid::DeviceNotifier::deviceRemoved, this, &EvdevDevice::deviceRemoved);
0106 
0107     QSet<int> keys;
0108     for (auto keyCombination : m_buttons) {
0109         keys += QSet<int>(keyCombination.cbegin(), keyCombination.cend());
0110     }
0111     setUsedKeys(keys);
0112 }
0113 
0114 EvdevDevice::~EvdevDevice()
0115 {
0116     libevdev_free(m_device);
0117 }
0118 
0119 void EvdevDevice::deviceRemoved(const QString& udi)
0120 {
0121     if (m_udi == udi) {
0122         deleteLater();
0123     }
0124 }
0125 
0126 void EvdevDevice::setKey(int key, bool pressed)
0127 {
0128     if (pressed == m_pressedKeys.contains(key)) {
0129         return;
0130     }
0131 
0132     if (pressed) {
0133         m_pressedKeys.insert(key);
0134     } else {
0135         m_pressedKeys.remove(key);
0136     }
0137     m_controller->m_dbusInterface.emitKeyPress(key);
0138     ControllerManager::instance().emitKey(key, pressed);
0139 }
0140 
0141 void EvdevDevice::readNow()
0142 {
0143     const int fd = libevdev_get_fd(m_device);
0144     struct input_event ev;
0145     int ret = read(fd, &ev, sizeof(ev));
0146     if (ret == 0) {
0147         qDebug() << "nothing to read";
0148     } else if (ret < 0) {
0149         qWarning() << "Error while reading" << strerror(errno);
0150         if(errno == 19) {
0151             ControllerManager::instance().deviceRemoved(this);
0152             deleteLater();
0153         }
0154     } else {
0155         processEvent(ev);
0156     }
0157 
0158     uint bytes;
0159     ret = ::ioctl(fd, FIONREAD, &bytes);
0160     if (ret == 0 && bytes >= sizeof(ev))
0161         readNow();
0162 }
0163 
0164 void EvdevDevice::processEvent(struct input_event& ev)
0165 {
0166     if (ev.type == EV_KEY) {
0167         const auto nativeKeyCodes = m_buttons.value(ev.code);
0168 
0169         if (!nativeKeyCodes.isEmpty()) {
0170             for (auto code : nativeKeyCodes) {
0171                 m_controller->m_dbusInterface.emitKeyPress(ev.code);
0172                 ControllerManager::instance().emitKey(code, ev.value);
0173             }
0174             return;
0175         }
0176         qCDebug(PLASMARC_EVDEV) << "Ignoring Key:" << libevdev_event_type_get_name(ev.type) << libevdev_event_code_get_name(ev.type, ev.code) << ev.value;
0177     } else if (ev.type == EV_ABS) {
0178         switch (ev.code) {
0179             case ABS_HAT0Y:
0180                 setKey(KEY_DOWN, ev.value > 0);
0181                 setKey(KEY_UP, ev.value < 0);
0182                 return;
0183             case ABS_HAT0X:
0184                 setKey(KEY_RIGHT, ev.value > 0);
0185                 setKey(KEY_LEFT, ev.value < 0);
0186                 return;
0187         }
0188     }
0189 }