Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read a file device in Linux using Qt?

Tags:

c++

linux

qt

I'm working on a small Qt5-based GUI, which shall display a data stream from a Linux file device. For that I choose a joystick input. With cat /dev/input/js0 it is possible to see the incoming stream on the terminal.

Using C you can read this device file using a loop with a blocking read or handle the device signals. But I don't get this with Qt.

What is the typical approach to interact with device files using Qt?


Based on the answer of @rodrigo here a new implementation:

joystick.h

#ifndef JOYSTICK_H
#define JOYSTICK_H

#include <QObject>
#include <QFile>
#include <QSocketNotifier>

class Joystick
      : public QObject
{
    Q_OBJECT

    QString fileName = "/dev/input/js0";
    QFile *file;
    QSocketNotifier *notifier;

public:
    explicit Joystick(QObject *parent = nullptr);
    ~Joystick();

signals:

public slots:
    void handle_readNotification(int socket);
};

#endif // JOYSTICK_H

joystick.cpp

#include "joystick.h"

Joystick::Joystick(QObject *parent)
   : QObject(parent)
{
    file = new QFile();
    file->setFileName(fileName);
    if( !file->exists() ){
        qWarning("file does not exist");
        return;
    }

    if( !file->open(QFile::ReadOnly) ){
        qWarning("can not open file");
        return;
    }

    notifier = new QSocketNotifier( file->handle(),
                                    QSocketNotifier::Read,
                                    this);

    connect( notifier,
             &QSocketNotifier::activated,
             this,
             &Joystick::handle_readNotification );

    if( !notifier->isEnabled() ){
        qInfo("enable notifier");
        notifier->setEnabled(true);
    }
    qInfo("Joystick init ready");
}

void
Joystick::handle_readNotification(int /*socket*/)
{
    static quint64 cnt=0;
    qInfo("cnt: %d",cnt++);

    if( !(file->isOpen()) ){
        qWarning("file closed");
        return;
    }

    char buf[16]; /* tested with different sizes */
    if( file->read(buf,sizeof(buf)) ){
        qInfo("read: %s",buf);
    }
//  QByteArray ba = file->readAll();
//  qInfo("Data: %s", ba.data());
}

Then I run this, the last out put is cnt: 0. It seems, that the read or readAll call now blocks. If I comment out the read calls the counter runs very fast. here a similiar post That is wrong here?


final solution

Thanks to rodrigo!

joystick.h

#ifndef JOYSTICK_H
#define JOYSTICK_H

#include <QObject>
#include <QFile>
#include <QSocketNotifier>

class Joystick
      : public QObject
{
    Q_OBJECT

    QString fileName = "/dev/input/js0";
    QSocketNotifier *notifier;
    int fd;

public:
    explicit Joystick(QObject *parent = nullptr);
    ~Joystick();

signals:
    void buttonPressed(quint8 number, qint16 value);
    void axisMoved(quint8 number, qint16 value);


public slots:
    void handle_readNotification(int socket);
};

#endif // JOYSTICK_H

joystick.cpp

#include "joystick.h"

#include <fcntl.h>
#include <unistd.h>
#include <linux/joystick.h>

Joystick::Joystick(QObject *parent)
   : QObject(parent)
{
    auto file = new QFile();
    file->setFileName(fileName);
    if( !file->exists() ){
        qWarning("file does not exist");
        return;
    }

    fd = open(fileName.toUtf8().data(), O_RDONLY|O_NONBLOCK);
    if( fd==-1 ){
        qWarning("can not open file");
        return;
    }

    notifier = new QSocketNotifier( fd,
                                    QSocketNotifier::Read,
                                    this);

    connect( notifier,
             &QSocketNotifier::activated,
             this,
             &Joystick::handle_readNotification );
}

Joystick::~Joystick()
{
    if( fd>=0 ){
        close(fd);
    }
}

void
Joystick::handle_readNotification(int /*socket*/)
{
    struct js_event buf;
    while( read(fd,&buf,sizeof(buf))>0 ){
        switch (buf.type) {
        case JS_EVENT_BUTTON:
            emit buttonPressed(buf.number, buf.value);
            break;
        case JS_EVENT_AXIS:
            emit axisMoved(buf.number, buf.value);
            break;
        }
    }
}
like image 252
Alex44 Avatar asked Feb 11 '18 17:02

Alex44


2 Answers

This problem is usually solved with a polling source for your toolkit. In case of Qt, this is the QSocketNotifier. Despite its name (an historical accident?), it can be used to poll on any file descriptor, not just sockets.

So you just open the device, with open() to get the file descriptor, and then create a QSocketNotifier on it, with type QSocketNotifier::Read. When there are events to be read you will get the activate() signal on it.

like image 62
rodrigo Avatar answered Oct 01 '22 06:10

rodrigo


Alternative solution, if you still want to use QFile instead of low-level read/write functions:

auto file = new QFile(fileName);

...

// IMPORTANT: You must set the Unbuffered flag below
if(!file->open(QFile::ReadOnly | QFile::Unbuffered))
{
    // handle error
}

auto fd = file.handle();

auto flags = fcntl(fd, F_GETFL, 0);
if(flags == -1)
{
    // handle error
}

flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
if(flags == -1)
{
    // handle error
}

auto notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);

...

struct js_event buf;
while(file->read(&buf, sizeof(buf)) > 0)
{
    // process data
}
like image 23
Innocent Bystander Avatar answered Oct 01 '22 07:10

Innocent Bystander