QtEmbedded鼠标驱动流程分析

QtEmbedded是怎么从main函数到加载鼠标驱

1. 从QApplication开始,因为每个带GUI的main函数都会创建一个QApplication的实例

2. 构造函数中调用QApplicationPrivate::construct(…)

src/gui/kernel/qapplication.cpp
665 QApplication::QApplication(int &argc, char **argv)
666     : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient))
667 { Q_D(QApplication); d->construct(); }

3. construct(…)则会调用qt_init(…)函数

4. qt_init(…) 则会解析命令行参数,从而调用QWSServer::startup(flags);

src/gui/kernel/qapplication_qws.cpp
2279     if (type == QApplication::GuiServer) {
2280         qt_appType = QApplication::Type(type);
2281         qws_single_process = true;
2282         QWSServer::startup(flags);
2283         setenv(”QWS_DISPLAY”, qws_display_spec.constData(), 0);
2284     }

我们知道如果应用命令行加”-qws”选项,就会以server(GuiSever)方式运行。上面代码中的startup的参数flags也是通过对命令行参数做运算得到的.

5. QWSServer::startup(flags) 会创建QWSServer的一个实例

src/gui/embedded/qwindowsystem_qws.cpp
4054 void QWSServer::startup(int flags)
4055 {
4056     if (qwsServer)
4057         return;
4058     unlink(qws_qtePipeFilename().toLatin1().constData());
4059     (void)new QWSServer(flags);
4060 }

6QWSServer的构造函数则会调用QWSServerPrivate::initServer(…)

src/gui/embedded/qwindowsystem_qws.cpp
1303 QWSServer::QWSServer(int flags, QObject *parent) :
1304     QObject(*new QWSServerPrivate, parent)
1305 {
1306     Q_D(QWSServer);
1307     d->initServer(flags);
1308 }

7. initServer则会在这里调用openMouse()

src/gui/embedded/qwindowsystem_qws.cpp
1440     if (!(flags&QWSServer::DisableMouse)) {
1441         q->openMouse();
1442     }
1443 #ifndef QT_NO_QWS_KEYBOARD
1444     if (!(flags&QWSServer::DisableKeyboard)) {
1445         q->openKeyboard();
1446     }
1447 #endif

鼠标相关的东西从这里进入我们视野,1440行的判断来自于命令行参数,如果运行server程序的时候带了”-nomouse”选项,那么这里就1441行就不会被调用了。
我们还可以看到openKeyboard()也是在这个函数里被调用的,可见他们还真是哥俩。

8.a openMouse()函数则根据Linux中的环境变量QWS_MOUSE_PROTO来判断加载什么样的鼠标驱动(handler)

从上面代码我们可以得出3个结论
A) 控制使用什么鼠标的环境变量是QWS_MOUSE_PROTO
B) 如果QWS_MOUSE_PROTO没有设置,系统会加载默认的鼠标驱动
C) 可以加载多个鼠标hanlder,也就是可以接多个鼠标设备.在QWS_MOUSE_PROTO变量中以空格分开
newMouseHandler(…)是被调用去创建鼠标驱动的函数

 QWSMouseHandler *handler = d->newMouseHandler(mouse.at(i));
 

8.b newMouseHandler(QString spec)处理具体的某一个驱动规格
spec的形式可能是这样的”Vr41xx:press=500:/dev/misc/ts”,该函数会对spec进行分离出不同的部分,然后调用QMouseDriverFactory::create(mouseProto, mouseDev)去创建真正的驱动。

src/gui/embedded/qwindowsystem_qws.cpp

QWSMouseHandler* QWSServerPrivate::newMouseHandler(const QString& spec)
{

    QWSMouseHandler *handler = 0;
    handler = QMouseDriverFactory::create(mouseProto, mouseDev);
    if (screen != -1)
        handler->setScreen(qt_screen->subScreens().at(screen));

    return handler;
}

如果返回值 handler为0就有问题了:(

8.c QMouseDriverFactory::create 是真正创建QWSMouseHandler实例的具体地方,不同的鼠标类型处理都是QWSMouseHandler的子类,这些QWSMouseHandler子类有可能编译成库文件,也有可能以插件的形式存在,如果以插件形式存在那么加载方法是

src/gui/embedded/qmousedriverfactory_qws.cpp
144     if (QWSMouseHandlerFactoryInterface *factory = qobject_cast(loader()->instance(driver)))
145         return factory->create(driver, device);

你的鼠标驱动是否加载成功,这第八步的3个函数很关键,前面的七步基本上不会出问题,作为系统运行的一个了解就可以。

src/gui/embedded/qmousedriverfactory_qws.cpp 中
QMouseDriverFactory::create(…)函数加载一个QMouseHandler的地方,那么我今天就接着地方往下讲,我们以tslib为例来讲解

133 #ifndef QT_NO_QWS_MOUSE_TSLIB
134     if (driver == QLatin1String("tslib") || driver.isEmpty())
135         return new QWSTslibMouseHandler(key, device);
136 #endif

要使135行的QWSTslibMouseHandler能够正常被加载,需要注意两个条件
1. 就是133行的那个宏,如果你在编译Qt-Embedded的时候没有加“-qt-mouse-tslib”选项的话,那么该宏会被使能,那么134,135自然不会被编译咯。
当然,如果你编译Qt-Embedded的时候,使用“-plugin-mouse-tslib”,那么下面的代码

142 #if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL)
143 #ifndef QT_NO_LIBRARY
144     if (QWSMouseHandlerFactoryInterface *factory = qobject_cast(loader()->instance(driver)))
145         return factory->create(driver, device);
146 #endif
147 #endif

去插件目录下找到对应的代码
//插件与非插件都是用到QWSTslibMouseHandler.

QWSTslibMouseHandler 是 QWSMouseHandler和 QWSCalibratedMouseHandler 的子类

QWSCalibratedMouseHandler 的构造函数中会读取校准文件/etc/pointercal

//非插件 QWSTslibMouseHandler是QWSMouseHandler的子类

class QWSTslibMouseHandler : public QWSCalibratedMouseHandler

class Q_GUI_EXPORT QWSCalibratedMouseHandler : public QWSMouseHandler

//插件 (src/plugins/mousedrivers/tslib中)

QWSMouseHandler* TslibMouseDriver::create(const QString &driver,
 const QString &device)
{
    if (driver.toLower() != "tslib")
        return 0;
    return new QWSTslibMouseHandler(driver, device);
}



2. 134行的driver名字来自于QWS_MOUSE_PROTO环境变量中的冒号前那一部分.例如”tslib:/dev/ts”中tslib就是driver,/dev/ts就是device。 名字不能搞错:)

如果前面两部分都没有问题,可以进入实现代码看看了
src/gui/embedded/qmousetslib_qws.cpp

320 QWSTslibMouseHandler::QWSTslibMouseHandler(const QString &driver,
321                                            const QString &device)
322     : QWSCalibratedMouseHandler(driver, device)
323 {
324     d = new QWSTslibMouseHandlerPrivate(this, device);
325 }

看来默默无闻的做着后勤工作的还是QWSTslibMouseHandlerPrivate

136 QWSTslibMouseHandlerPrivate::QWSTslibMouseHandlerPrivate(QWSTslibMouseHandler *h,
137                                                          const QString &device)
138     : handler(h), dev(0), mouseNotifier(0), jitter_limit(3)
139 {
140     QStringList args = device.split(QLatin1Char(':'), QString::SkipEmptyParts);
//..............
148     devName = args.join(QString());
150     if (devName.isNull()) {
151         const char *str = getenv("TSLIB_TSDEVICE");
152         if (str)
153             devName = QString::fromLocal8Bit(str);
154     }
156     if (devName.isNull())
157         devName = QLatin1String("/dev/ts");
159     if (!open())
160         return;
165     mouseNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
166     connect(mouseNotifier, SIGNAL(activated(int)),this, SLOT(readMouseData()));
168 }

通过上面代码可以明白,如果在QWS_MOUSE_PROTO没写设备名,还可以通过环境TSLIB_TSDEVICE得到,如果TSLIB_TSDEVICE也没有设置,那么就会使用设备/dev/ts,你需要搞清你的驱动建立的设备名是什么。
165,166行是关键的两行,这表明Qt会通过QSocketNotifier监视你的鼠标设备上是否有新的数据传入,如果有则通过readMouseData()读取数据。

226 void QWSTslibMouseHandlerPrivate::readMouseData()
227 {
//............
285         if (calibrated) {
286             // tslib should do all the translation and filtering, so we send a
287             // "raw" mouse event
288             handler->QWSMouseHandler::mouseChanged(p, pressed);
289         } else {
290             handler->sendFiltered(p, pressed);
291         }
292     }
293 }

readMouseData()中大部分代码都是读取鼠标数据的,我们更关心把数据送到哪里去,那么我们看290行的sendFilterer(…)
src/gui/embedded/qmouse_qws.cpp

604 bool QWSCalibratedMouseHandler::sendFiltered(const QPoint &position, int button)
605 {
//.......
642
643         mouseChanged(transform(pos), button);
644         sent = true;
//.......
651 }
在我快没有耐心的时候终于看到了我想看到的代码
  1. 259 void QWSMouseHandler::mouseChanged(const QPoint &position, int state, int wheel)

  2. 260 {

  3. 261 mousePos = position + d_ptr->screen->offset();

  4. 262 QWSServer::sendMouseEvent(mousePos, state, wheel);

  5. 263 }

在这里把鼠标事件正式交给QWSServer. 
 

QWSServer::sendMouseEvent(const QPoint & p, int state, int wheel)

 它会创建一个QWSMouseEvent event,并对其进行赋值

        event.simpleData.state = state | qws_keyModifiers;

最后调用serverClient->sendEvent(&event);将事件发送给客户端。

 QWSClient::sendEvent(QWSEvent* e)

当应用程序为多进程时,会将事件写入到socket中,并调用csocket->flush().如果为单进程,则将事件写入到全局队列中qt_client_enqueue(e).客户端收到事件后,会将QWSMouseEvent转化为QWSEvent,转化过程是在QApplication::qwsProcessEvent(QWSEvent* e)中进行。

QApplication::qwsProcessEvent(QWSEvent* e)

所有重QWSServer传回的QWSEvent事件都将在这里进行处理,它先将QWSEvent转化为QEvent,然后使用QApplication::nofity()派发相应的接收窗口(这里面的内容没有深究)。

当窗口接收到QEvent之后,将其转化为QMouseEvent。

        到这里,我们可以看出,移植触摸屏的操作主要分为两步,一是将硬件设备安装到Linux系统中,在/dev下能看到相应的设备。二是基于Qt层面的处理,我们需要重新设置QWS_MOUSE_PROT,让它对应步骤一中的设备,然后就是修改动缓冲区读取数据的函数。

        从设备缓冲区读取数据的是QWSMouseHandlerPrivate::readMouseData(int),对不同的鼠标设备,readMouseData(int)的内容是不同的。为了方便用户移植不同类型的鼠标,Qt提供了一个基类QWSMouseHandler,不同类型的鼠标从它派生出不同的子类,最后重载它的一些方法(主要是readMouseData(int))即可。这些dd的定义都在qwsmouse_qws.cpp当中。

       此外,触摸屏和鼠标有一点的不同的地方,它需要进行调整。因为从设备得到的数据是物理屏的数据,比如s3c2410的触摸屏的ad转换是10位精度,也就是说物理数据从0~1023,在实际的情况中一般是100~1000之间的数据,而我们的液晶屏是640*480(或者是其他的,这和触摸屏的数据没有任何的关系),所以必须将物理数据转换为屏幕上点的数据.他们之间的转换公式,就必须通过定标的方式来确定。

所谓的定标,就是在屏幕上依次出现topleft,bottomleft,bottomright,topright和center一共5个点,用户必须依次在这5个点上点击(在触摸屏上点击,触摸屏就放在液晶屏的上方),这样我们得到了物理的点,也得到了对应的实际的点,因此就可以计算出相应的参数。

如果屏的质量稳定,我们可以将测得的数据放在这个文件当中,并取消掉定标的过程,这样就可以每次使用默认的设置,而不需要重新计算了,通常这些设置会保存在文件/etc/pointercal中。

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值