File indexing completed on 2024-05-12 17:07:10

0001 /*
0002     This file is part of the KDE Control Center Module for Joysticks
0003 
0004     SPDX-FileCopyrightText: 2003 Martin Koller <kollix@aon.at>
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "joydevice.h"
0009 
0010 #include <KLocalizedString>
0011 #include <QDebug>
0012 
0013 #include <errno.h>
0014 #include <fcntl.h>
0015 #include <math.h>
0016 #include <sys/ioctl.h>
0017 #include <sys/select.h>
0018 #include <sys/stat.h>
0019 #include <sys/time.h>
0020 #include <unistd.h>
0021 
0022 JoyDevice::JoyDevice(const QString &devicefile)
0023     : devName(devicefile)
0024     , joyFd(-1)
0025     , buttons(0)
0026     , axes(0)
0027     , amin(nullptr)
0028     , amax(nullptr)
0029     , corr(nullptr)
0030     , origCorr(nullptr)
0031 {
0032 }
0033 
0034 QString JoyDevice::errText(ErrorCode code) const
0035 {
0036     switch (code) {
0037     case SUCCESS:
0038         return QString();
0039 
0040     case OPEN_FAILED: {
0041         return i18n("The given device %1 could not be opened: %2", devName, strerror(errno));
0042     }
0043 
0044     case NO_JOYSTICK: {
0045         return i18n("The given device %1 is not a joystick.", devName);
0046     }
0047 
0048     case ERR_GET_VERSION: {
0049         return i18n("Could not get kernel driver version for joystick device %1: %2", devName, strerror(errno));
0050     }
0051 
0052     case WRONG_VERSION: {
0053         int version = 0;
0054         int fd = ::open(devName.toLatin1(), O_RDONLY);
0055         if (fd != -1) {
0056             ::ioctl(fd, JSIOCGVERSION, &version);
0057             ::close(fd);
0058         }
0059 
0060         KLocalizedString loc = ki18n("The current running kernel driver version (%1.%2.%3) is not the one this module was compiled for (%4.%5.%6).");
0061         loc = loc.subs(version >> 16);
0062         loc = loc.subs((version >> 8) & 0xFF);
0063         loc = loc.subs(version & 0xFF);
0064         loc = loc.subs(JS_VERSION >> 16);
0065         loc = loc.subs((JS_VERSION >> 8) & 0xFF);
0066         loc = loc.subs(JS_VERSION & 0xFF);
0067         return loc.toString();
0068     }
0069 
0070     case ERR_GET_BUTTONS: {
0071         return i18n("Could not get number of buttons for joystick device %1: %2", devName, strerror(errno));
0072     }
0073 
0074     case ERR_GET_AXES: {
0075         return i18n("Could not get number of axes for joystick device %1: %2", devName, strerror(errno));
0076     }
0077 
0078     case ERR_GET_CORR: {
0079         return i18n("Could not get calibration values for joystick device %1: %2", devName, strerror(errno));
0080     }
0081 
0082     case ERR_RESTORE_CORR: {
0083         return i18n("Could not restore calibration values for joystick device %1: %2", devName, strerror(errno));
0084     }
0085 
0086     case ERR_INIT_CAL: {
0087         return i18n("Could not initialize calibration values for joystick device %1: %2", devName, strerror(errno));
0088     }
0089 
0090     case ERR_APPLY_CAL: {
0091         return i18n("Could not apply calibration values for joystick device %1: %2", devName, strerror(errno));
0092     }
0093 
0094     default:
0095         return i18n("internal error - code %1 unknown", int(code));
0096     }
0097 }
0098 
0099 JoyDevice::ErrorCode JoyDevice::open()
0100 {
0101     if (joyFd != -1)
0102         return JoyDevice::SUCCESS; // already open
0103 
0104     int fd = ::open(devName.toLatin1(), O_RDONLY);
0105 
0106     if (fd == -1)
0107         return JoyDevice::OPEN_FAILED;
0108 
0109     // we could open the devicefile, now check if a joystick is attached
0110     char name[128];
0111 
0112     if (::ioctl(fd, JSIOCGNAME(sizeof(name)), &name) == -1) {
0113         ::close(fd);
0114         return JoyDevice::NO_JOYSTICK;
0115     }
0116 
0117     // check the kernel driver version
0118     int version;
0119     if (::ioctl(fd, JSIOCGVERSION, &version) == -1) {
0120         ::close(fd);
0121         return JoyDevice::ERR_GET_VERSION;
0122     }
0123 
0124     if (version != JS_VERSION) {
0125         ::close(fd);
0126         return JoyDevice::WRONG_VERSION;
0127     }
0128 
0129     char bt = 0, ax = 0;
0130     if (::ioctl(fd, JSIOCGBUTTONS, &bt) == -1) {
0131         ::close(fd);
0132         return JoyDevice::ERR_GET_BUTTONS;
0133     }
0134 
0135     if (::ioctl(fd, JSIOCGAXES, &ax) == -1) {
0136         ::close(fd);
0137         return JoyDevice::ERR_GET_AXES;
0138     }
0139 
0140     struct js_corr *oldCorr = new struct js_corr[ax];
0141 
0142     if (::ioctl(fd, JSIOCGCORR, oldCorr) == -1) {
0143         ::close(fd);
0144         delete[] oldCorr;
0145         return JoyDevice::ERR_GET_CORR;
0146     }
0147 
0148     if (bt < 0) {
0149         ::close(fd);
0150         delete[] oldCorr;
0151         return JoyDevice::ERR_GET_BUTTONS;
0152     }
0153 
0154     descr = name;
0155     joyFd = fd;
0156     axes = ax;
0157     buttons = bt;
0158     origCorr = oldCorr;
0159     corr = new struct js_corr[axes];
0160 
0161     amin = new int[axes];
0162     amax = new int[axes];
0163 
0164     int i;
0165 
0166     for (i = 0; i < axes; i++)
0167         resetMinMax(i);
0168 
0169     return JoyDevice::SUCCESS;
0170 }
0171 
0172 void JoyDevice::close()
0173 {
0174     if (joyFd == -1)
0175         return;
0176 
0177     ::close(joyFd);
0178 
0179     joyFd = -1;
0180     descr = QString();
0181 
0182     delete[] amin;
0183     delete[] amax;
0184     amin = nullptr;
0185     amax = nullptr;
0186 
0187     delete[] corr;
0188     corr = nullptr;
0189     delete[] origCorr;
0190     origCorr = nullptr;
0191 }
0192 
0193 int JoyDevice::axisMin(int axis) const
0194 {
0195     if ((axis < 0) || (axis >= axes))
0196         return 0;
0197 
0198     return amin[axis];
0199 }
0200 
0201 int JoyDevice::axisMax(int axis) const
0202 {
0203     if ((axis < 0) || (axis >= axes))
0204         return 0;
0205 
0206     return amax[axis];
0207 }
0208 
0209 JoyDevice::ErrorCode JoyDevice::initCalibration()
0210 {
0211     if (joyFd == -1)
0212         return JoyDevice::ERR_INIT_CAL;
0213 
0214     int i;
0215 
0216     // Reset all current correction values
0217     for (i = 0; i < axes; i++) {
0218         corr[i].type = JS_CORR_NONE;
0219         corr[i].prec = 0;
0220     }
0221 
0222     if (::ioctl(joyFd, JSIOCSCORR, corr) == -1)
0223         return JoyDevice::ERR_INIT_CAL;
0224 
0225     for (i = 0; i < axes; i++)
0226         corr[i].type = JS_CORR_BROKEN;
0227 
0228     return JoyDevice::SUCCESS;
0229 }
0230 
0231 JoyDevice::ErrorCode JoyDevice::applyCalibration()
0232 {
0233     if (joyFd == -1)
0234         return JoyDevice::ERR_APPLY_CAL;
0235 
0236     if (::ioctl(joyFd, JSIOCSCORR, corr) == -1)
0237         return JoyDevice::ERR_APPLY_CAL;
0238 
0239     return JoyDevice::SUCCESS;
0240 }
0241 
0242 void JoyDevice::resetMinMax(int axis, int value)
0243 {
0244     amin[axis] = value;
0245     amax[axis] = value;
0246 }
0247 
0248 void JoyDevice::calcPrecision()
0249 {
0250     if (!corr)
0251         return;
0252 
0253     int i;
0254 
0255     for (i = 0; i < axes; i++) {
0256         corr[i].prec = amax[i] - amin[i];
0257         qDebug() << "Precision for axis: " << i << ": " << corr[i].prec;
0258     }
0259 }
0260 
0261 JoyDevice::ErrorCode JoyDevice::restoreCorr()
0262 {
0263     if (joyFd == -1)
0264         return JoyDevice::SUCCESS;
0265 
0266     if (::ioctl(joyFd, JSIOCSCORR, origCorr) == -1)
0267         return JoyDevice::ERR_RESTORE_CORR;
0268     else
0269         return JoyDevice::SUCCESS;
0270 }
0271 
0272 JoyDevice::~JoyDevice()
0273 {
0274     close();
0275 }
0276 
0277 bool JoyDevice::getEvent(JoyDevice::EventType &type, int &number, int &value, bool wait)
0278 {
0279     number = value = 0;
0280 
0281     int ret;
0282 
0283     fd_set readSet;
0284 
0285     FD_ZERO(&readSet);
0286     FD_SET(joyFd, &readSet);
0287 
0288     struct timeval timeout;
0289     timeout.tv_sec = 0;
0290     timeout.tv_usec = wait ? 10000 : 0;
0291 
0292     ret = ::select(joyFd + 1, &readSet, nullptr, nullptr, &timeout);
0293 
0294     if (ret == 1) // got an event from the joystick
0295     {
0296         struct js_event e;
0297 
0298         if (::read(joyFd, &e, sizeof(struct js_event)) == sizeof(struct js_event)) {
0299             if (e.type & JS_EVENT_BUTTON) {
0300                 type = JoyDevice::BUTTON;
0301                 value = e.value;
0302                 number = e.number;
0303 
0304                 return true;
0305             }
0306 
0307             if (e.type & JS_EVENT_AXIS) {
0308                 type = JoyDevice::AXIS;
0309                 value = e.value;
0310                 number = e.number;
0311 
0312                 // store min, max values
0313                 if (e.value < amin[number])
0314                     amin[number] = e.value;
0315                 if (e.value > amax[number])
0316                     amax[number] = e.value;
0317 
0318                 return true;
0319             }
0320         }
0321     }
0322 
0323     return false; // no event
0324 }
0325 
0326 void JoyDevice::calcCorrection(int axis, int *min, int *center, int *max)
0327 {
0328     const int MIN = 0;
0329     const int MAX = 1;
0330 
0331     double a, b, c, d;
0332 
0333     a = center[MIN]; // inputs.cmin[1];
0334     b = center[MAX]; // inputs.cmax[1];
0335     c = 32767.0 / (center[MIN] - min[MAX]); // (inputs.cmin[1] - inputs.cmax[0]);
0336     d = 32767.0 / (max[MIN] - center[MAX]); // (inputs.cmin[2] - inputs.cmax[1]);
0337 
0338     corr[axis].coef[0] = (int)rint(a);
0339     corr[axis].coef[1] = (int)rint(b);
0340     corr[axis].coef[2] = (int)rint(c * 16384.0);
0341     corr[axis].coef[3] = (int)rint(d * 16384.0);
0342 
0343     qDebug() << "min min: " << min[0] << " max: " << min[1];
0344     qDebug() << "max min: " << max[0] << " max: " << max[1];
0345     qDebug() << "Correction values for axis: " << axis << ": " << corr[axis].coef[0] << ", " << corr[axis].coef[1] << ", " << corr[axis].coef[2] << ", "
0346              << corr[axis].coef[3] << Qt::endl;
0347 }