Warning, file /plasma-bigscreen/plasma-remotecontrollers/src/wiimote/wiimote.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  *   SPDX-FileCopyrightText: 2022 Bart Ribbers <bribbers@disroot.org>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "wiimote.h"
0008 #include "../controllermanager.h"
0009 
0010 #include <QLoggingCategory>
0011 #include <QDebug>
0012 #include <KLocalizedString>
0013 
0014 #include <xwiimote.h>
0015 #include <linux/input-event-codes.h>
0016 #include <unistd.h>
0017 
0018 Wiimote::Wiimote(struct xwii_iface *iface, const QString &sysPath)
0019 {
0020     m_iface = iface;
0021     m_uniqueIdentifier = sysPath;
0022     m_name = i18nc("What Nintendo Wii remote controllers are called", "Wii Remote");
0023     m_deviceType = DeviceWiimote;
0024 
0025     int ret;
0026     char *devtype;
0027     do {
0028         ret = xwii_iface_get_devtype(iface, &devtype);
0029         if (ret) {
0030             qCritical() << "wiimote: ERROR: Failed to read devtype of new Wiimote device, error:" << ret;
0031             return;
0032         }
0033     } while (strcmp(devtype, "pending") == 0);
0034 
0035     if (strcmp(devtype, "unknown") == 0)
0036         m_devType = WIIMOTE_DEVTYPE_UNKNOWN;
0037     else if (strcmp(devtype, "generic") == 0)
0038         m_devType = WIIMOTE_DEVTYPE_GENERIC;
0039     else if (strcmp(devtype, "gen10") == 0)
0040         m_devType = WIIMOTE_DEVTYPE_GEN10;
0041     else if (strcmp(devtype, "gen15") == 0)
0042         m_devType = WIIMOTE_DEVTYPE_GEN15;
0043     else if (strcmp(devtype, "gen20") == 0)
0044         m_devType = WIIMOTE_DEVTYPE_GEN20;
0045     else if (strcmp(devtype, "balanceboard") == 0)
0046         m_devType = WIIMOTE_DEVTYPE_BALANCEBOARD;
0047     else if (strcmp(devtype, "procontroller") == 0)
0048         m_devType = WIIMOTE_DEVTYPE_PROCONTROLLER;
0049 
0050     free(devtype);
0051 
0052     if (m_devType == WIIMOTE_DEVTYPE_UNKNOWN) {
0053         return;
0054     }
0055 
0056     QObject::connect(this, &Wiimote::keyPress,
0057                      &ControllerManager::instance(), &ControllerManager::emitKey);
0058 
0059     ret = xwii_iface_open(m_iface, xwii_iface_available(m_iface) | XWII_IFACE_WRITABLE);
0060 
0061     if (ret) {
0062         qCritical() << "wiimote: Error: Cannot open interface " << ret;
0063     }
0064 
0065     // We want to watch for hotplug events and adapt our device accordingly
0066     ret = xwii_iface_watch(m_iface, true);
0067 
0068     if (ret) {
0069         qCritical() << "wiimote: Error: Cannot initialize hotplug watch descriptor";
0070     }
0071 
0072     memset(m_fds, 0, sizeof(m_fds));
0073     m_fds[0].fd = 0;
0074     m_fds[0].events = POLLIN;
0075     m_fds[1].fd = xwii_iface_get_fd(m_iface);
0076     m_fds[1].events = POLLIN;
0077     m_fdsNum = 2;
0078     
0079     getExtensionType();
0080 
0081     // Let the user know the device is being used by rumbling
0082     xwii_iface_rumble(m_iface, true);
0083     usleep(200 * 1000); // Only rumble for half a second
0084     xwii_iface_rumble(m_iface, false);
0085 }
0086 
0087 WiimoteDevtypes Wiimote::getDevType()
0088 {
0089     return m_devType;
0090 }
0091 
0092 
0093 void Wiimote::watchEvents()
0094 {
0095     struct xwii_event event;
0096     int ret;
0097 
0098     ret = poll(m_fds, m_fdsNum, -1);
0099     if (ret < 0) {
0100         qDebug() << "wiimote: Error: Cannot poll fds: " << ret;
0101         return;
0102     }
0103 
0104     ret = xwii_iface_dispatch(m_iface, &event, sizeof(event));
0105     if (ret && ret != -EAGAIN) {
0106         qCritical() << "wiimote: Error: Read failed with err: " << ret;
0107         return;
0108     }
0109 
0110     switch (event.type) {
0111         case XWII_EVENT_GONE:
0112             // TODO: we don't always get this event
0113             // https://github.com/dvdhrm/xwiimote/issues/99
0114             emit deviceDisconnected(m_index);
0115             break;
0116         case XWII_EVENT_WATCH:
0117             handleWatch();
0118             break;
0119         case XWII_EVENT_KEY:
0120             handleKeypress(&event);
0121             break;
0122         case XWII_EVENT_NUNCHUK_KEY:
0123         case XWII_EVENT_NUNCHUK_MOVE:
0124             handleNunchuk(&event);
0125             break;
0126         case XWII_EVENT_ACCEL:
0127         case XWII_EVENT_IR:
0128         case XWII_EVENT_BALANCE_BOARD:
0129         case XWII_EVENT_MOTION_PLUS:
0130         case XWII_EVENT_CLASSIC_CONTROLLER_KEY:
0131         case XWII_EVENT_CLASSIC_CONTROLLER_MOVE:
0132         case XWII_EVENT_PRO_CONTROLLER_KEY:
0133         case XWII_EVENT_PRO_CONTROLLER_MOVE:
0134         case XWII_EVENT_NUM:
0135         case XWII_EVENT_GUITAR_KEY:
0136         case XWII_EVENT_GUITAR_MOVE:
0137         case XWII_EVENT_DRUMS_KEY:
0138         case XWII_EVENT_DRUMS_MOVE:
0139             break;
0140     }
0141 }
0142 
0143 void Wiimote::handleWatch()
0144 {
0145     int ret;
0146 
0147     // After a hotplug event occurred xwii_iface_open will fail if called too shortly after it happened
0148     // Because of this, just keep calling it till it succeeds
0149     // https://github.com/dvdhrm/xwiimote/issues/97
0150     do {
0151         ret = xwii_iface_open(m_iface, xwii_iface_available(m_iface) | XWII_IFACE_WRITABLE);
0152     } while (ret);
0153 
0154     getExtensionType();
0155 }
0156 
0157 void Wiimote::handleKeypress(struct xwii_event *event)
0158 {
0159     static QHash<int, int> keyCodeTranslation = {
0160         { XWII_KEY_A, KEY_ENTER},
0161         { XWII_KEY_B, KEY_BACK},
0162         { XWII_KEY_UP, KEY_UP},
0163         { XWII_KEY_DOWN, KEY_DOWN},
0164         { XWII_KEY_LEFT, KEY_LEFT},
0165         { XWII_KEY_RIGHT, KEY_RIGHT},
0166         { XWII_KEY_ONE, KEY_1},
0167         { XWII_KEY_TWO, KEY_2},
0168         { XWII_KEY_PLUS, KEY_VOLUMEUP},
0169         { XWII_KEY_MINUS, KEY_VOLUMEDOWN},
0170         { XWII_KEY_HOME, KEY_HOME},
0171     };
0172 
0173     bool pressed = event->v.key.state;
0174     int nativeKeyCode = keyCodeTranslation.value(event->v.key.code, -1);
0175 
0176     if (nativeKeyCode < 0) {
0177         qDebug() << "wiimote: DEBUG: Received a keypress we do not handle!";
0178         return;
0179     }
0180 
0181     emit keyPress(nativeKeyCode, pressed);
0182 }
0183 
0184 void Wiimote::handleNunchuk(struct xwii_event *event)
0185 {
0186     double val;
0187 
0188     if (event->type == XWII_EVENT_NUNCHUK_MOVE) {
0189         int time_since_previous_event =
0190         event->time.tv_sec - m_previousNunchukAxisTime;
0191         
0192         if (time_since_previous_event > 0) {
0193             // pow(val, 1/4) for smoother interpolation around the origin
0194             val = event->v.abs[0].x * 12;
0195             if (val > 1000) {
0196                 emit keyPress(KEY_RIGHT, true);
0197                 emit keyPress(KEY_RIGHT, false);
0198                 m_previousNunchukAxisTime = event->time.tv_sec;
0199             } else if (val < -1000) {
0200                 emit keyPress(KEY_LEFT, true);
0201                 emit keyPress(KEY_LEFT, false);
0202                 m_previousNunchukAxisTime = event->time.tv_sec;
0203             }
0204             
0205             val = event->v.abs[0].y * 12;
0206             if (val > 1000) {
0207                 emit keyPress(KEY_UP, true);
0208                 emit keyPress(KEY_UP, false);
0209                 m_previousNunchukAxisTime = event->time.tv_sec;
0210             } else if (val < -1000) {
0211                 emit keyPress(KEY_DOWN, true);
0212                 emit keyPress(KEY_DOWN, false);
0213                 m_previousNunchukAxisTime = event->time.tv_sec;
0214             }
0215         }
0216     }
0217 }
0218 
0219 void Wiimote::getExtensionType()
0220 {
0221     char* extensionName;
0222     int ret = xwii_iface_get_extension(m_iface, &extensionName);
0223 
0224     if (ret)
0225         qCritical() << "wiimote: ERROR: Failed to read extension type!";
0226 
0227     if (strcmp(extensionName, "none") == 0)
0228         m_extensionType = WIIMOTE_EXTENSION_NONE;
0229     else if (strcmp(extensionName, "unknown") == 0)
0230         m_extensionType = WIIMOTE_EXTENSION_UNKNOWN;
0231     else if (strcmp(extensionName, "nunchuk") == 0)
0232         m_extensionType = WIIMOTE_EXTENSION_NUNCHUK;
0233     else if (strcmp(extensionName, "classic") == 0)
0234         m_extensionType = WIIMOTE_EXTENSION_CLASSIC;
0235     else if (strcmp(extensionName, "balanceboard") == 0)
0236         m_extensionType = WIIMOTE_EXTENSION_CLASSIC;
0237     else if (strcmp(extensionName, "procontroller") == 0)
0238         m_extensionType = WIIMOTE_EXTENSION_PROCONTROLLER;
0239 }
0240 
0241 Wiimote::~Wiimote()
0242 {
0243     xwii_iface_unref(m_iface);
0244 }