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 ifdef
ing 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
Deploying Qt Applications in Linux Properly
How to Get a List of Installed True Type Fonts on Linux Using C or C++
Is a Moved-From Vector Always Empty
Problems with Std::Stoi, Not Working on Mingw Gcc 4.7.2
Get the Compiler Options from a Compiled Executable
How to Include Header Files in Gcc Search Path
How to Overload Operators for Built-In Types Like Int or Float
What's the Best Way to Do Fixed-Point Math
Emulate "Double" Using 2 "Float"S
How to Create a Game Loop with Xlib
About the Binary Compatibility of Linux
How True Is "Want Speed? Pass by Value"
Does Vector::Erase() on a Vector of Object Pointers Destroy the Object Itself
Start Windows Service from Application Without Admin Right(C++)
Why Use Functors Over Functions
C++ Warning: Deprecated Conversion from String Constant to 'Char*' [-Wwrite-Strings]