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 }