在 Linux 系统中,如果硬件设备的驱动程序被正确安装,那么在 /dev 路 径下会有相应的设备文件,它们是对应硬件设备的驱动程序接口,应用程序可以打开这些设备文件,从中读取的数据就对应着硬件设备传回的信息。当鼠标被点击 时,会产生中断并进入中断处理程序,在中断处理程序中,鼠标的动作会被翻译成相应的数据存在一个数据缓冲区中。用户打开设备文件后,就是从这个这个缓冲区 读取数据的。注意,这时的数据是最原始的数据。
当 Qt 应用程序作为 GuiServer 运行时, QApplication 会创建一个 QWSServer* 类的指针 qwsServer ,它是一个全局的指针,在整个 Qt 应用程序的生命周期都存在,而且只有一个。 qwsServer 在创建过程中会调用 QWSServer::startup(), 这个函数会调用 QWSServer::openMouse() 和 QWSServer::openKeyboard() 来建立与鼠标和键盘硬件设备的连接。在 Qt 中,触摸屏作为一种特别的鼠标,具有和鼠标同等的处理方法。
1. openMouse()
它会从环境变量 QWS_MOUSE_PROT 得到鼠标的类型和设备,它的格式是 protocol:device , protocol 包括以下的几种 :MouseMan,IntelliMouse,Microsoft 等, device 就是鼠标 ( 或者触摸屏 ) 的设备文件 , 一般是 /dev/mouse, 还可能是 /dev/ps2(ps 类型的鼠标 ) ,而对于触摸屏 , 则会是 /dev/Tpanel 。最后它会创建一个 QWSMouseHandler 。
2. QWSMouseHandler* h = newMouseHandler(ms);
newMouseHandler 从环境变量字符串中分析出设备文件路径和协议名,然后调用 QMouseDriverFactory::create() 创建 QWSMouseHandler 对象。
注 : 有的 qt 版本是直接在 newMouseHandler() 中创建 QWSMouseHandler 对象
3. QMouseDriverFactory::create(mouseProto, mouseDev)
这个鼠标驱动工厂创建一个指定的 QWSMouseHandler 对象
4. QWSMouseHandler 对象的创建
它会调用自己的数据子对象 QWSMouseHandlerPrivate 的 openDevices() 打开鼠标设备文件并进行测试,最后调用 QWSMouseHandlerPrivate 的 notify() 将文件描述符 ( 使用 open() 打开后返回的标志符 ) 和处理函数连接起来。
5. notify(fd)
为设备 fd 创建一个 QSocketNotifier* 指针 mouseNotifier ,并将其 activated(int) 信号和 QWSMouseHandlerPrivate::readMouseData(int) 绑定在一起。
这样当设备 fd 发生中断时, readMouseData(int) 会去读取设备缓冲区的内容。 这中间有一个桥梁。当应用程序进入主事件循环时,会在循环中不断调用 select() 来检查文件描述符的变化,检测到变化时会发出 QEvent::SockAt 事件 ( 通过 QApplication::sendEvent() 发送 ) 给对应的 QSocketNotifier , QSocketNotifier 的 event() 函数中会对数据进行处理并发出 activated(int) 信号。
6. QWSMouseHandlerPrivate::readMouseData(int)
它先读取数据,得到按下点的坐标,然后调用 sendFiltered( pp, Qt::LeftButton ) 将数据通过信号 mouseChanged() 发送出去。
如果要判断是否长按,先初始化一个计数器 counter 用来表示鼠标按下的时间,然后在 if ( pressure > 0 ){..} 中启动一个定时器, counter++ ,它表示鼠标还在按下。在 if ( pressure < 0 ){…} 中停止这个定时器, counter=0 。它表示鼠标已经离开触摸屏。
在定时器的 slot 函数中进行判断,如果 counter>0 表示鼠标被按下了一段时间,这时就可以发送 mouseChanged() 信号了,普通状态下发送的信号形如 mouseChanged(mousePos, Qt::LeftButton, 0); 长按信号就形如 emit mouseChanged(mousePos, Qt::MouseLongPress | Qt::LeftButton, 0);
可我们还没定义 Qt::MouseLongPress 呢? 在哪里定义呢?在 qnamespace.h 中
Qt 里原来定义了一个 Qt::ButtonState 的枚举类型,描述了鼠标和组合键的状态,现在我们对它进行了扩展,让它还可以描述键盘的状态:
enum ButtonState
{
NoButton = 0x0000,
LeftButton = 0x0001,
RightButton = 0x0002,
MidButton = 0x0004,
MouseButtonMask = 0x0007,
ShiftButton = 0x0008,
ControlButton = 0x0010,
AltButton = 0x0020,
KeyButtonMask = 0x0038,
MouseLongPress = 0x0100,
ButtonLongPress = 0x0200,
ReplaceKey = 0x0400,
IMSpecKey = 0x8000,
Keypad = 0x4000
};
mouseChanged() 这个信号是绑定在 QWSServer* qwsServer 上的, slot 为 setMouse(const QPoint &, int, int 它会调用 QWSServer 的方法 QWSServer::sendMouseEvent(const QPoint & p, int state, int wheel) 中进行处理的。
7. QWSServer::sendMouseEvent(const QPoint & p, int state, int wheel)
它会创建一个 QWSMouseEvent event ,并对其进行赋值
event.simpleData.state = state | qws_keyModifiers;
最后调用 serverClient->sendEvent(&event); 将事件发送给客户端。
8. QWSClient::sendEvent(QWSEvent* e)
当应用程序为多进程时,会将事件写入到 socket 中,并调用 csocket->flush(). 如果为单进程,则将事件写入到全局队列中 qt_client_enqueue(e). 客户端收到事件后,会将 QWSMouseEvent 转化为 QWSEvent ,转化过程是在 QApplication::qwsEventFilter ( QWSEvent* e ) 中进行。
9. QApplication::qwsEventFilter ( QWSEvent* e)
所有重 QWSServer 传回的 QWSEvent 事件都将在这里进行处理,它先将 QWSEvent 转化为 QEvent ,然后使用 QApplication:: nofity() 派发相应的接收窗口。???
当窗口接收到 QEvent 之后,将其转化为 QMouseEvent ,其成员函数 state() 返回的状态就包括我们事先写入的 MouseLongPress 属性了,简单的使用方式如下:
if(e->state() & Qt::MouseLongPress) {…}
到这里,我们可以看出,移植触摸屏的操作主要分为两步,一是将硬件设备安装到 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 中。 S3c2410 的触摸屏驱动是 iPAQ 兼容的驱动 , 编译的时候需要定义 2 个宏 :
QWS_MOUSE_IPAQ,QWS_MOUSE_IPAQ_RAW.
同时设定: QWS_MOUSE_PROTO=Tpanel:/dev/h3600_tsraw