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"