Hands-On Mobile and Embedded Development with Qt 5 学习笔记 - libevdev,evdev

2024-04-13 15:18

本文主要是介绍Hands-On Mobile and Embedded Development with Qt 5 学习笔记 - libevdev,evdev,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在 Linux 操作系统上,有多种输入系统可以与 Qt 一起使用。

Qt 内置支持以下类型的触摸屏界面:

evdev:事件设备接口
libinput:处理输入设备的库
tslib:打字稿运行时库
我们将从学习Linux evdev系统开始,直接读取设备文件。

evdev

Qt 内置支持 Linux 和嵌入式 Linux 的 evdev 标准事件处理系统。 如果没有配置或检测到其他系统,这就是默认情况下您将获得的结果。 它处理键盘、鼠标和触摸。 然后您可以像往常一样使用 Qt 处理键盘、触摸和鼠标事件。

您可以分配启动参数,例如设备文件路径和屏幕默认旋转,如下所示:

QT_QPA_EVDEV_TOUCHSCREEN_PARAMETERS=/dev/input/input2:rotate=90

其他可用的参数是 invertx 和 inverty。 当然,这些输入事件不需要在Qt上回复,直接在Qt下面的栈中访问即可。 我称它们为原始事件,但它们实际上只是读取特殊的 Linux 内核设备文件。

让我们来看看在使用 Qt 时自己处理这些 evdev 输入事件。 这是低级系统文件访问,因此您可能需要 root 或管理员权限才能运行以这种方式使用它的应用程序。

Linux 上的输入事件通过内核的 dev 节点访问,通常位于 /dev/input 中,但它们可以位于 /dev 目录树下的任何位置,具体取决于驱动程序。 QFile 不应用于实际读取这些特殊设备节点文件。

读取输入节点的主要包含文件如下:

#include <linux/input.h>

您需要扫描设备文件以检测触摸屏生成的文件。 在 Linux 中,这些设备节点是动态命名的,因此您需要使用其他方法来识别正确的文件,而不仅仅是文件名。 因此,您必须打开该文件并要求它告诉您它的名称。

我们可以使用 QDir 及其过滤器至少过滤掉一些我们知道不是我们正在寻找的文件:

   QDir inputDir = QDir("/dev/input");QStringList filters;filters << "event*";QStringList eventFiles = inputDir.entryList(filters,
QDir::System);int fd = -1;char name[256];for (QString file : eventFiles) {file.prepend(inputDir.absolutePath());fd = ::open(file.toLocal8Bit().constData(), O_RDONLY|O_NONBLOCK);if (fd >= 0) {ioctl(fd, EVIOCGNAME(sizeof(name)), name);::close(fd);}
}

确保包含 O_NONBLOCK 参数来打开。

此时,我们有一个不同输入设备的名称列表。 您可能只需要猜测要使用的名称,然后进行字符串比较即可找到正确的设备。 有时,驱动程序会有正确的 id 信息,可以像这样使用 EVIOCGID 获取:

unsigned short id[4];
ioctl(fd, EVIOCGID, &id);

有时,您可以使用 EVIOCGBIT 检测某些功能。 这将告诉我们硬件驱动程序支持哪些按钮或键。 当您触摸它时,触摸屏驱动程序会输出 0x14a (BTN_TOUCH) 的键码,因此我们可以使用它来检测哪个输入事件将成为我们的触摸屏:

bool MainWindow::isTouchDevice(int fd)
{unsigned short id[4];long bitsKey[LONG_FIELD_SIZE(KEY_CNT)];memset(bitsKey, 0, sizeof(bitsKey));ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitsKey)), bitsKey);if (testBit(BTN_TOUCH, bitsKey)) {return true;}return false;
}

我们现在可以相当确定我们有正确的设备文件。 现在,我们可以设置一个 QSocketNotifier 对象来在该文件被激活时通知我们,然后我们可以读取它以获取触摸的 X 和 Y 值。 我们使用 QSocketNotifier 类是因为我们不能使用 QFile,因为它没有任何信号来告诉我们 Linux 设备文件何时更改,因此这使它变得更容易:

int MainWindow::doScan(int fd)
{QSocketNotifier *notifier= new QSocketNotifier(fd, QSocketNotifier::Read,this);auto c = connect(notifier,  &QSocketNotifier::activated,[=]( int /*socket*/ ) {struct input_event ev;unsigned int size;size = read(fd, &ev, sizeof(struct input_event));if (size < sizeof(struct input_event)) {qWarning("expected %u bytes, got %u\n", sizeof(structinput_event), size);perror("\nerror reading");return EXIT_FAILURE;}if (ev.type == EV_KEY && ev.code == BTN_TOUCH)qWarning("Touchscreen value: %i\n", ev.value);if (ev.type == EV_ABS && ev.code == ABS_MT_POSITION_X)qWarning("X value: %i\n", ev.value);if (ev.type == EV_ABS && ev.code == ABS_MT_POSITION_Y)qWarning("Y value: %i\n", ev.value);return 0;});return true;
}

我们还使用标准的 read() 函数而不是 QFile 来读取它。

BTN_TOUCH 事件值告诉我们何时按下或释放触摸屏。

ABS_MT_POSITION_X 值将是触摸屏的 X 位置,而 ABS_MT_POSITION_Y 值将是 Y 位置。

有一个库可以用来做同样的事情,这可能会更容易一些。

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;bool isTouchDevice(int fd);void scanInputDevices();int doScan(int fd);int fd;
};#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"#include <QDir>#include <QFile>
#include <QSocketNotifier>
#include <QTextStream>#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <linux/input.h>#include <QDebug>#define BITSLONG (sizeof(long) * 8)
#define LONG_FIELD_SIZE(bits) ((bits / BITSLONG) + 1)
#define BTN_TOUCH 0x14a
#define ABS_MT_POSITION_X 0x35
#define ABS_MT_POSITION_Y 0x36static inline bool testBit(long bit, const long *array)
{return (array[bit / BITSLONG] >> bit % BITSLONG) & 1;
}MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow),fd(-1)
{ui->setupUi(this);scanInputDevices();
}MainWindow::~MainWindow()
{::close(fd);delete ui;
}int MainWindow::doScan(int fd)
{QSocketNotifier *notifier= new QSocketNotifier(fd, QSocketNotifier::Read, this);auto c = connect(notifier,  &QSocketNotifier::activated,[=]( int /*socket*/ ) {struct input_event ev;unsigned int size;size = read(fd, &ev, sizeof(struct input_event));if (size < sizeof(struct input_event)) {qWarning("expected %u bytes, got %u\n", sizeof(struct input_event), size);perror("\nerror reading");return EXIT_FAILURE;}if (ev.type == EV_KEY && ev.code == BTN_TOUCH)qWarning("Touchscreen value: %i\n", ev.value);if (ev.type == EV_ABS && ev.code == ABS_MT_POSITION_X)qWarning("X value: %i\n",  ev.value);if (ev.type == EV_ABS && ev.code == ABS_MT_POSITION_Y)qWarning("Y value: %i\n",  ev.value);return 0;});return true;
}void MainWindow::scanInputDevices(){QDir inputDir = QDir("/dev/input");QStringList filters;filters << "event*";QStringList eventFiles = inputDir.entryList(filters, QDir::System);char name[256];for (QString file : eventFiles) {file.prepend("/dev/input/");fd = ::open(file.toLocal8Bit().constData(), O_RDONLY);if (fd >= 0) {if (isTouchDevice(fd)) {doScan(fd);break;}}}}bool MainWindow::isTouchDevice(int fd){unsigned short id[4];long bitsKey[LONG_FIELD_SIZE(KEY_CNT)];memset(bitsKey, 0, sizeof(bitsKey));ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitsKey)), bitsKey);if (testBit(BTN_TOUCH, bitsKey)) {return true;}return false;}

libevdev

当您使用库 libevdev 时,您将不必访问诸如 QSocketNotifier 之类的低级文件系统函数并自己读取文件。

要使用 libevdev,我们首先在项目 .pro 文件中添加 LIBS 条目。

LIBS += -levdev

这允许 qmake 设置正确的链接器参数。 包含标头如下

#include <libevdev-1.0/libevdev/libevdev.h>

我们可以借用前面代码中的初始代码来扫描设备文件的目录,但是 isTouchDevice 函数得到了更清晰的代码:

bool MainWindow::isTouchDevice(int fd)
{int rc = 1;rc = libevdev_new_from_fd(fd, &dev);if (rc < 0) {qWarning("Failed to init libevdev (%s)\n", strerror(-rc));return false;}if (libevdev_has_event_code(dev, EV_KEY, BTN_TOUCH)) {qWarning("Device: %s\n", libevdev_get_name(dev));return true;}libevdev_free(dev);return false;
}

libevdev 有很好的 libevdev_has_event_code 函数,可以用来轻松检测设备是否有特定的事件代码。 这正是我们识别触摸屏所需要的! 注意 libevdev_free 函数,它将释放我们不需要的正在使用的内存。

doScan 函数失去对 read 的调用,但代之以对 libevdev_next_event 的调用。 它还可以通过调用 libevdev_event_code_get_name 输出带有事件代码实际名称的好消息:

int MainWindow::doScan(int fd)
{QSocketNotifier *notifier= new QSocketNotifier(fd, QSocketNotifier::Read,
this);auto c = connect(notifier,  &QSocketNotifier::activated,[=]( int /*socket*/ ) {int rc = -1;do {            struct input_event ev;rc = libevdev_next_event(dev,
LIBEVDEV_READ_FLAG_NORMAL, &ev);if (rc == LIBEVDEV_READ_STATUS_SYNC) {while (rc == LIBEVDEV_READ_STATUS_SYNC) {rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev);}} else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) {if ((ev.type == EV_KEY && ev.code == BTN_TOUCH) ||(ev.type == EV_ABS && ev.code ==
ABS_MT_POSITION_X) ||(ev.type == EV_ABS && ev.code ==
ABS_MT_POSITION_Y)) {qWarning("%s value: %i\n",
libevdev_event_code_get_name(ev.type, ev.code), ev.value);}}} while (rc == 1 || rc == 0 || rc == -EAGAIN);return 0;});return 0;
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <libevdev-1.0/libevdev/libevdev.h>
namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;void scanInputDevices();bool isTouchDevice(int fd);int doScan(int fd);int fd;struct libevdev *dev = NULL;
//private slots:
//    void activated(int);
};#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"#include <QDir>#include <QFile>
#include <QSocketNotifier>#include <fcntl.h>
#include <unistd.h>
#include <QDebug>//#define BTN_TOUCH 0x14aMainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow),fd(-1)
{ui->setupUi(this);scanInputDevices();
}MainWindow::~MainWindow()
{libevdev_free(dev);delete ui;
}void MainWindow::scanInputDevices()
{QDir inputDir = QDir("/dev/input");QStringList filters;filters << "event*";QStringList eventFiles = inputDir.entryList(filters, QDir::System);char name[256];for (QString file : eventFiles) {file.prepend("/dev/input/");fd = ::open(file.toLocal8Bit().constData(), O_RDONLY|O_NONBLOCK);if (fd >= 0) {if (isTouchDevice(fd)) {doScan(fd);break;} else {::close(fd);}}}
}bool MainWindow::isTouchDevice(int fd)
{int rc = 1;rc = libevdev_new_from_fd(fd, &dev);if (rc < 0) {qWarning("Failed to init libevdev (%s)\n", strerror(-rc));return false;}if (libevdev_has_event_code(dev, EV_KEY, BTN_TOUCH)) {qWarning("Device: %s\n", libevdev_get_name(dev));return true;}libevdev_free(dev);return false;
}int MainWindow::doScan(int fd)
{QSocketNotifier *notifier= new QSocketNotifier(fd, QSocketNotifier::Read, this);auto c = connect(notifier,  &QSocketNotifier::activated,[=]( int /*socket*/ ) {int rc = -1;do {struct input_event ev;rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);if (rc == LIBEVDEV_READ_STATUS_SYNC) {while (rc == LIBEVDEV_READ_STATUS_SYNC) {rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev);}} else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) {if ((ev.type == EV_KEY && ev.code == BTN_TOUCH) ||(ev.type == EV_ABS && ev.code == ABS_MT_POSITION_X) ||(ev.type == EV_ABS && ev.code == ABS_MT_POSITION_Y)) {qWarning("%s value: %i\n", libevdev_event_code_get_name(ev.type, ev.code), ev.value);}}} while (rc == 1 || rc == 0 || rc == -EAGAIN);return 0;});return 0;
}

这篇关于Hands-On Mobile and Embedded Development with Qt 5 学习笔记 - libevdev,evdev的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/900489

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个