Qt消息传递机制分析及输入设备驱动开发

Qt消息传递机制分析及输入设备驱动开发

2023年6月26日发(作者:)

Qt消息传递机制分析及输入设备驱动开发OS:linuxArch:MipsInput:remoter、touch screenQt source:qt-embedded-linux-opensource-src-4.5.0难点分析:1、如《Virt FrameBuffer开发小结》所描述般,输入设备遥控器、触摸屏等设备驱动不是按照Linux的内核驱动规范来写的,而Qt消息的获取过程则满足Linux的文件读写操作标准,是正统的open、close、read、write。解决方法参照virt framebuffer的做法:建立一个虚拟的输入设备virtinput,当底层设备发生中断时,先读取码值,然后write(virtInputFd,&input_event,sizeof(input_event))将码值填送到虚拟设备中;Qt则read(virtInputFd,&input_event,sizeof(input_event))将码值读出来,进行分析处理。大体思路就这样,稍后分析一下Qt关于消息机制,事实其机制是非常典型的,在我们原来平台上也是类似的。2、Qt4是基于LPGL协议的,不能随便修改Qt source中源文件。一旦修改了其中的库文件,就要公开自己的商业非开源代码。为了避免这种情况,我们使用这样的技巧:在Qt中我们open的依然是"标准设备"(例如usb键盘),但实际上open的是我们的遥控器、触摸屏等输入设备,所以需要在我们的遥控器驱动中,将码值一一转为usb键盘的标准键码值,用switch语句,效率还是很高的。源码编译配置相关:./configure-arch mipsel-fast-qt-kbd-usb-no-kbd-tty大体选项就这些,其他一些优化裁剪的自己看着办,记住如果启用usb键盘,tty键盘是要disable的。一开始我在这里吃了亏,搞了很久,usb键盘都没反应,最后还是看keyboard工厂那部分代码才反应过来。运行时的环境变量设置:设置Qt动态库路径、fonts路径等等,具体的我也记不了那么多,网上很多这方面的资料。如果如难点2那样,用我们的虚拟输入设备来替代usb键盘,则还需要export QWS_KEYBOARD='usb:/dev/virtput'字符串中usb是指要启用的键盘类型,/dev/virtput是虚拟输入设备的节点名称,这样Qt上层以为read的是usb键盘消息,但真正的数据是从虚拟输入设备virtinput读取的,而virtinput的数据又是由遥控器送过来的。听起来非常啰嗦且效率低下,但很多时候要兼顾风险、市场效率、软件协议的,技术只是一小部分。出色的工程师或许很偏激,但我明显不是。以下代码是QWSKeyboardHandler*QKbdDriverFactory:create(const QString&key,const

QString&device)摘下来的:

#ifndef QT_NO_QWS_KBD_TTY

if(driver==QLatin1String("tty")||y())

return new QWSTtyKeyboardHandler(device);

#endif

#ifndef QT_NO_QWS_KBD_USB if(driver==QLatin1String("usb"))

return new QWSUsbKeyboardHandler(device);

//从中可以看出:如果没有disable tty设备的话,那么tty kbd会先于usb kbd初始化,return new QWSTtyKeyboardHandler(device)会导致直接返回。很明显,这部分代码架构用到了工厂模式,封装性非常强,值得体会学习。

我们再来看qkbdusb_中的代码:

QWSUsbKbPrivate:QWSUsbKbPrivate(QWSPC101KeyboardHandler*h,const

QString&device):handler(h)

{

#ifdef QT_QWS_ZYLONITE shift=FALSE;

#endif fd=:open(y()?"/dev/input/event1":l8Bit(),O_RDONLY,0);

if(fd=0){

QSocketNotifier*notifier;

notifier=new QSocketNotifier(fd,QSocketNotifier:Read,this); connect(notifier,SIGNAL(activated(int)),this,

SLOT(readKeyboardData()));

}

}

//当前对象继承了QWSPC101KeyboardHandler。

*语句fd=:open(y()?"/dev/input/event1":l8Bit(),O_RDONLY,0)的y()是用来判断环境变量QWS_KEYBOARD有没有被export,如果没有,则使用是默认的设备节点/dev/input/event1。

*语句connect(notifier,SIGNAL(activated(int)),this,SLOT(readKeyboardData()))要理解的话,最好先理解一下qt的信号/槽机制原理,这里就不详述了。简单的来说:对象notifier的信号activated(int)和当前对象this的槽readKeyboardData()联系起来,这样当对象notifier的信号发射后,当前对象this的readKeyboardData()就会立即被执行。

*readKeyboardData()无非是read(fd,&event,sizeof(input_event)),将键值从设备的buffer中提取出来,转换成qt中的命令字,然后handler-processKeyEvent进行处理。

*至于qt如何维护readKeyboardData操作的,我们可以追寻QSocketNotifier并结合connect来分析。

QSocketNotifier:QSocketNotifier(int socket,Type

type,QObject*parent)

:QObject(parent)

{ if(socket 0)

qWarning("QSocketNotifier:Invalid socket specified");

sockfd=socket;

sntype=type;

snenabled=true;

Q_D(QObject);

if(!d-threadData-eventDispatcher){

qWarning("QSocketNotifier:Can only be used with threads started

with QThread");

}else{

d-threadData-eventDispatcher-registerSocketNotifier(this);

}

//从这里往registerSocketNotifier追寻如下,我们知道,这是类似于UNIX上高级IO方法select/poll,不断轮询直到文件句柄的标志位发生变化(这些变化应该对应于输入设备驱动的poll函数的mask),从而emit

activated(sockfd)

}

void QEventDispatcherGlib:registerSocketNotifier(QSocketNotifier*notifier)

{

GPollFDWithQSocketNotifier*p=new GPollFDWithQSocketNotifier; =sockfd;

switch(type){

case QSocketNotifier:Read:

=G_IO_IN|G_IO_HUP|G_IO_ERR;

break;

case QSocketNotifier:Write:

=G_IO_OUT|G_IO_ERR;

break;

case QSocketNotifier:Exception:

=G_IO_PRI|G_IO_ERR;

break;

}

p-socketNotifier=notifier;

(p);

g_source_add_poll(&d-socketNotifierSource-source,&p-pollfd);

}

bool QSocketNotifier:event(QEvent*e)

{

//Emits the activated()signal when aQEvent:SockAct is //received.

if(e-type()==QEvent:ThreadChange){

if(snenabled){

QMetaObject:invokeMethod(this,"setEnabled",Qt:QueuedConnection,

Q_ARG(bool,snenabled));

setEnabled(false);

}

}

QObject:event(e);//will activate filters if(e-type()==QEvent:SockAct){

emit activated(sockfd);//注意这里,发射信号,会导致相绑定的槽会立即被执行

return true;

}

return false;

}

//虽然我们不能从代码级上明白QSocketNotifier如何触发event,从而发射activated(sockfd)信号的,但是大体还是能了解其流程。

结合文档前面的一些注释:

Once you have opened adevice using alow-level(usually platform-specific)API,you can create asocket notifier to monitor the file socket notifier is enabled by default, emits the

activated()signal whenever asocket event corresponding to its type

t the activated()signal to the slot you want to be

called when an event corresponding to your socket notifier's type

occurs.

如果要透彻理解QSocketNotifier如何触发event的,可以看下socketNotifierSourceDispatch、registerSocketNotifier的实现以及它们的关联。

我们可以总结出:当设备产生中断时,系统会进入中断处理例程,将码值读取出来,但是并不立即通知应用程序产生新的消息了,而是将码值送入某个buffer中;而应用程序总是运行一个后台线程,不断轮询直到文件句柄标志位发生变化(通过select/poll实现),然后应用程序就知道有新的信息到达了,接着读取该buffer,如果从中提取到消息,则进行分派处理。

MSN空间完美搬家到新浪博客!

发布者:admin,转转请注明出处:http://www.yc00.com/news/1687756294a39978.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信