Using Qsocketnotifier to Select on a Char Device

Using QSocketNotifier to select on a char device.

Your QSocketNotifer gets destroyed as soon as that if block ends. It doesn't stand a chance of reporting anything.

You must keep that socket notifier alive as long as you want that file to be monitored. The simplest way of doing that is probably keeping a QSocketNotifer* member in one of your classes.

Reading from character device with Qt

I can't test with your specific device, however, I'm using the keyboard as a read only device.

The program attempts to connect to keyboard and read all keys pressed inside and outside the window. It's a broad solution you'll have to adapt to meet your demands.

Note that I'm opening the file with O_RDONLY | O_NONBLOCK which means open in read only mode and no wait for the event be triggered(some notifier needed to know when data is ready!) respectively.

You'll need super user privilege to run this example!

#include <QtCore>
#include <fcntl.h>
#include <linux/input.h>

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

const char *device_name = "/dev/input/by-path/platform-i8042-serio-0-event-kbd";

int descriptor = open(device_name, O_RDONLY | O_NONBLOCK);

if (descriptor < 0)
{
qDebug() << "Error" << strerror(errno);
return a.exec();
}

QFile device;

if (!device.open(descriptor, QFile::ReadOnly))
{
qDebug() << "Error" << qPrintable(device.errorString());
return a.exec();
}

QSocketNotifier notifier(device.handle(), QSocketNotifier::Read);

QObject::connect(¬ifier, &QSocketNotifier::activated, ¬ifier, [&](int socket){
Q_UNUSED(socket)

struct input_event ev;

QByteArray data = device.readAll();

qDebug() << "Event caught:"
<< "\n\nDATA SIZE" << data.size()
<< "\nSTRUCT COUNT" << data.size() / int(sizeof(input_event))
<< "\nSTRUCT SIZE" << sizeof(input_event);

qDebug() << ""; //New line

while (data.size() >= int(sizeof(input_event)))
{
memcpy(&ev, data.data(), sizeof(input_event));

data.remove(0, int(sizeof(input_event)));

qDebug() << "TYPE" << ev.type << "CODE" << ev.code << "VALUE" << ev.value << "TIME" << ev.time.tv_sec;
}

qDebug() << ""; //New line
});

return a.exec();
}

How to read a file device in Linux using Qt?

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.

QSocketNotifier opened on a FIFO keeps firing even if I read everything

Ultimately, this is just a variation over the problem described here, as Qt under the hood uses select/epoll machinery to implement QSocketNotifier. Opening the FIFO as O_RDWR fixes the problem.

using QTextStream to read stdin in a non-blocking fashion

Line buffering.

Default is flushing after a "\n". If you write 5 lines to your process, your slot gets called 5 times. If you want to avoid that, you have to call setbuf(stdin, _IOFBF). But even then it is not guaranteed you can read arbitrarily large amounts of data in one chunk.

Edit: It would probably better to use QTextStream::atEnd() instead of select, since QTextStream has its own internal buffers.

Using bluetooth with qt in linux

Instead of your own select, you should use QSocketNotifier class and give your own file handles for Qt event loop.

You can also use this overload of QFile::open to turn your socket into a QIODevice instance.

Third choice is to put your own select loop into a different thread, so it does not block the Qt main event loop. But that is going to bring quite a lot of extra complexity, so I'd do that only as a last resort.

When to use environment variable or command line parameter?

In most of the scripts I write, I allow both with the command line parameters taking preference.

This is to allow 'lazy' users who want to set'n'forget the parameters to do so.

It also allows over-riding of those parameters in special cases by the command line.

For those that don't want to take the chance that their parameters might be set up incorrectly, they can just use parameters.

Sometimes I'll even have more levels in the hierarchy, in order of precedence:

  • Value set while program running.
  • Command-line parameter.
  • Environment variable.
  • Local config file.
  • Global config file.
  • Default.

That way, for each variable, you just work your way up that list, setting it to the relevant value, if it's there.

FIFO pipe is always readable in select()

You opened that FIFO as read only (O_RDONLY), whenever there is no writer to the FIFO, the read end will receive an EOF.

Select system call will return on EOF and for every EOF you handle there will be a new EOF. This is the reason for the observed behavior.

To avoid this open that FIFO for both reading and writing (O_RDWR). This ensures that you have at least one writer on the FIFO thus there wont be an EOF and as a result select won't return unless someone writes to that FIFO.

How to avoid specific #ifdef in multi - platform qt code?

You can create separate EventListener.cpp files for windows and unix and put these files into subdirectories like (win, linux). To the makefile or to the projectfile you can add one implementation file based on the current platform. The compiler will compile just one file for the current platform.

With this method you can avoid ifdefing totally.

If the definitions are different you can use pImpl idiom to separate the implementation details of a class: https://cpppatterns.com/patterns/pimpl.html



Related Topics



Leave a reply



Submit