Search code examples
c++qtlibusbopensuseinput-devices

How to check if keyboard is connected or or not and use it if it is connected during runtime?


So I have a QT project in which I want to check if a keyboard is connected. In case it's not connected I want to be able to still use it if it is connected during the program is running. I looked around and found a few libraries that might be suitable but I am not sure if they can do what I want. Libraries in question: libinput, libusb or Solid with KDE. My question is, will one of these libraries do what I want it to do or is it something completly different? If it is one of the above libraries than any examples would help a ton because I can't really get anything out of the documentation. I should probably also mention that I use a linux, or to be more exact openSUSE Leap 15.2


Solution

  • Okay, well turns out it wasn't as complicated as I thought and none of the libraries that I talked about are needed. Here is my solution, in case any one in the future is looking for something similar.

    scanner.h

    #ifndef SCANNER_H
    #define SCANNER_H
    
    #include <QObject>
    #include <QThread>
    #include <QTimer>
    #include <QDebug>
    #include <QString>
    #include <QFile>
    #include <QSocketNotifier>
    
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <linux/input.h>
    
    class Scanner : public QThread
    {
        Q_OBJECT
    
    public:
        static Scanner* getInstance(void);
        int saveInCorrectFormat(int code);
    
    protected:
        void run() override;
    
    signals:
        void ChipScanned(QString rfid);
    
    private slots:
        void handleNotification(int socket);
        void checkScannerData();
    
    private:
        Scanner(QObject *parent = 0);
        ~Scanner();
    
        void initScanner(void);
    
        static Scanner* sInstance;
        QString defaultPath = "/dev/input/event2";
        QString rfid;
        QTimer* sCheckScanner;
        QFile *sScannerFile;
        QSocketNotifier *sNotifier;
        int fd;
        bool notificationEnabled;
        struct input_event event;
        int RFID[10];
        int i = 0;
        int buffer = 0;
    };
    
    #endif // SCANNER_H
    

    scanner.cpp

    #include "scanner.h"
    
    Scanner* Scanner::sInstance = new Scanner();
    
    Scanner* Scanner::getInstance(void){
        return sInstance;
    }
    
    Scanner::Scanner(QObject *parent) : QThread(parent){
        moveToThread(this);
        start();
    }
    
    Scanner::~Scanner(){
    
    }
    
    void Scanner::run(){
        initScanner();
        QThread::exec();
    }
    
    /**
     * @brief Scanner::initScanner
     * initialize the timer to check up on the keyboard event file
     */
    void Scanner::initScanner(void){
        notificationEnabled = false;
        sScannerFile = new QFile(defaultPath);
    
        sCheckScanner = new QTimer(this);
        sCheckScanner->setInterval(100);
        sCheckScanner->setSingleShot(false);
        connect(sCheckScanner, SIGNAL(timeout()), this, SLOT(checkScannerData()));
        sCheckScanner->start();
    }
    
    /**
     * @brief Scanner::checkScannerData
     * check if the keyboard is connected or not
     * if it is connected, activate event handling
     */
    void Scanner::checkScannerData(){
        if(sScannerFile->exists()){
            if(notificationEnabled){
                return;
            }
    
            fd = open(defaultPath.toUtf8().data(), O_RDONLY | O_NONBLOCK);
            if(-1 != fd){
                sNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
                connect(sNotifier, SIGNAL(activated(int)), this, SLOT(handleNotification(int)));
                qDebug() << "Scanner connected";
                notificationEnabled = true;
            }
        }else{
            if(notificationEnabled){
                sNotifier->setEnabled(false);
                disconnect(sNotifier, SIGNAL(activated(int)), this, SLOT(handleNotification(int)));
                delete sNotifier;
                close(fd);
                qDebug() << "Scanner disconnect";
                notificationEnabled = false;
            }
        }
    }
    
    /**
     * @brief Scanner::handleNotification
     * check if the keyboard is still connected or if the event was the disconnect
     * if still connected than read the data and save it
     * @param socket
     */
    void Scanner::handleNotification(int socket){
        if(!sScannerFile->exists()){
            if(notificationEnabled){
                sNotifier->setEnabled(false);
                disconnect(sNotifier, SIGNAL(activated(int)), this, SLOT(handleNotification(int)));
                delete sNotifier;
                close(fd);
                qDebug() << "Scanner disconnect";
                notificationEnabled = false;
            }
            return;
        }
    
        if(read(fd, &event, sizeof(event)) == sizeof(event)){
            if(event.type != EV_SYN){
                if(event.value == 1 && event.code != 28){
                    RFID[i] = saveInCorrectFormat(event.code);
                    rfid.append(QString("%1").arg(saveInCorrectFormat(event.code)));
                    i++;
                }
            }
        }
    
        if(rfid.size() == 10){
            buffer++;
            if(buffer == 10){
                emit ChipScanned(rfid);
                qDebug() << rfid;
                i = 0;
                buffer = 0;
                rfid.clear();
            }
        }
    }
    
    /**
     * @brief Scanner::saveInCorrectFormat
     * correct the raw data in the it's right format
     * @param code
     * current data to convert
     * @return
     * converted data
     */
    int Scanner::saveInCorrectFormat(int code){
        switch(code){
        case 11:
            return 0;
        case 28:
            return -1;
        default:
            return code-1;
        }
    }
    

    A few extra infos: My device isn't really a keyboard but the input is handled as if it is a keyboard that's why I have to make a few tweaks with the raw data before it is in the expected format. Anyone else most likely won't need those tweaks, like int saveInCorrectFormat or the if condition after reading the data. I believe this code is rather universal which means changing the defaultPath and making a few tweaks while reading the raw data will probably make it possible to use it for other devices as well.