多点触控 总结
一、 研发目的
使用win7自带API或者PQ-LABS所提供的开发包,在win7系统下实现多点触控与OSG结合的漫游方式。
二、 确定选型
? 网络资料对比
大量查阅网络相关触摸屏资料,总结出现有触摸屏类型。按外形的分类可分为触摸一体机、触摸屏显示器、外挂式触摸屏;按照原理和材质可分为电阻式、电容式、表面声波式、红外式、光学式。
并对以上不同类型的触摸屏从外观及原理上进行了理论的比对:
? 具体厂家业务员信息提供
联系了北京现有知名厂家的业务人员,进一步了解了市场上触摸屏的实际信息,发现触摸屏最大的区别不是在外观,而是在材质及原理。
触摸一体机、触摸屏显示器、外挂式触摸屏的显示部分其实都是一个显示屏加一个触摸屏组合而成只是加工的方式不一样:外挂式是直接将触摸屏用双面胶粘贴在显示屏表面;触摸显示器是在外挂式的基础上人工加上一个外框,使其拥有一个完整的外观;而触摸一体机又是在触摸屏显示器的基础上将底座和主机集
合于一体。
经实际了解发现目前厂家提供的24寸以上的触摸屏只有红外和光学两种。
? 实地考察
对触摸屏有了一定了解之后开始实际的考察工作,主要考察了以下公司的触摸屏:吉元平,英策长远,金雀,翔远时代,安信思拓。
安信思拓生产的是只有光学屏无红外屏,其中21.5寸两点光学屏处于批量生产阶段,有大量存货。
翔远时代和吉平元主要生产两点红外屏,其中翔远时代的触摸屏存在不能实时变化触摸点数的技术问题,吉平元所作的42寸屏是这几家中无论外观还是性能都是最好的,但需要整体购买(不能由我方提供显示器),且只限于42寸可达到高性能。而且此屏幕为两点屏。
金雀和英策长远都采用美国进口的PQ-LABS屏,PQ-LABS总部在硅谷,中国研发基地在上海,是世界先进的触摸屏研发生产商,质量有保证。英策长远目前最小可以做32寸屏,我们现场试用了这种屏,体验效果很好。目前只有金雀一家公司可以做32寸以下6点屏。
经过以上各方面的限定,最终确定选用红外6点屏,24寸小屏采用金雀提供的屏幕,而32寸及以上大屏则可在英策长远和金雀中做选择。
三、 研发使用触摸屏
24寸外挂式触摸屏+现有24寸液晶显示器
四、 研发前期准备
? 大量查阅网络可用资料,寻找一切可利用资源
主要参考QP-LABS官网,win7官网,以及各种触控相关网页 ? 观看了大量网络多点触控相关视频,使用了google earth,bing
maps 3d等现有可操作的触控软件。
结合实时视景现有漫游操作初步定下漫游的手势操作方式。 ? 公司内部开展交流讨论,后组内讨论
修正了漫游手势操作方式
? 研究了win7官网提供的例子程序以及PQ-LABS自带的程序范例,
联系了PQ-LABS中国总部技术人员。 发现用PQ-LABS实现手势操作相对简单,有现成函数可调用,但考虑到如使用PQ-LABS开发包开发就必须使用PQ-LABS厂家提供的触摸屏,通用性受到限制,故只能使用win7自带的API进行程序的研发。
? 学习了OSG漫游相关知识。
学习了相机,矩阵,漫游器等相关知识。
五、 确定研发方案确定
由于通用性的限制,只能选择win7自带的API进行开发,其中win7提供了两种触控消息处理方法:WM_GISTRUE和WM_TOUCH。
前者是win7自带的默认触控处理方式,用于手势操作的处理,win7提供了5种操作手势:平移,旋转,缩放,两触点同时点击,一触点按下后另一触点点击,此种处理方式是针对于已有手势,不能得到具体执行手势的触摸点数
后者需要先将窗口注册为触摸窗口,然后可以接收触摸消息,可以跟踪到具体某个触摸点,但没有手势的操作,需要自己定义何种操作对应何种手势。
对比两种触控处理方法,后者需要的工作量太大,精准度也很难确定,前者出点数不能获取外,手势操作基本满足现有漫游需要。
故决定选取前者进行研发。
六、 研发阶段
研发采用OSG+QT框架,首先遇到的问题是如何让屏幕接收到触摸消息,为此尝试了以下几种方法:
? 子类化
首先需要得到触控窗口的句柄:
osgViewer::GraphicsWindowWin32* pGraphicsWindow = dynamic_cast< osgViewer::GraphicsWindowWin32* >(_viewQOSG->getCamera()->getGraphicsContext());
HWND hwnd = pGraphicsWindow->getHWND();
通过SetWindowLong来重新设置窗口的过程函数:
WNDPROC g_OldEdit;
g_OldEdit=(WNDPROC)SetWindowLong(hwnd,GWL_WNDPROC,(LONG)NewProc);
LRESULT CALLBACK NewProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN: {
int j = 1; }
break;
case WM_LBUTTONDBLCLK: {
int j = 1; }
break; case WM_TOUCH: {
int i = 0; }
break; }
return CallWindowProc (g_OldEdit, hwnd, message, wParam, lParam); }
此时出现只可以截获到鼠标消息并不能接收到触摸消息。猜测是OSG窗口截获过滤掉了触摸消息,于是准备采用hook来截获窗口消息。
? HOOK截获窗口消息
查看网络相关资料,尝试使用钩子来截获窗口消息,发现网上引用的代码hook是从MFC中的CObject类继承过来的,而win32程序无此类库,并且HOOK内部关键代码也是通过SetWindowLong转换现有窗口过程实现窗口消息截获,故放弃使用此方法。
? 通过引用C#现有触控工程来实现消息的接收
在网络上搜索到win7多点触控的C#工程代码,研究其实现方式,发现可以用于现有窗口的触控程序接收,但发现C++无法引用C#工程,反而C#引用C++相对简单,于是分析例子程序中的工程设置,发现需要做以下更改才能正确引用C#项目: ? 添加公共运行时支持:
工程?右键?Properties?General?Common Language Runtime Support?Common Language Runtime Support (/clr) ? 添加C#工程引用
工程 ?右键?References?Framework and References?Add New References?Browse?XX.dll ? 消息handle的建立
_gestureHandler=Windows7::Multitouch::Win32Helper::Factory::CreateHandler
_gestureHandler->PanBegin+=gcnew
System::EventHandler
_gestureHandler->Pan += gcnew
System::EventHandler
_gestureHandler->PanEnd += gcnew
System::EventHandler
_gestureHandler->RotateBegin += gcnew
System::EventHandler ^>(OnRotateHandler); _gestureHandler->Rotate += gcnew System::EventHandler _gestureHandler->RotateEnd += gcnew System::EventHandler _gestureHandler->ZoomBegin += gcnew System::EventHandler _gestureHandler->Zoom += gcnew System::EventHandler _gestureHandler->ZoomEnd+=gcnew System::EventHandler 通过以上代码可以在OnPanHandler函数中处理平移手势,在OnRotateHandler函数中处理旋转手势,在OnZoomHandler函数中处理缩放手势。 使用以上方法解决了消息接收问题,接下来进入了消息处理阶段的代码编写。由于手势操作不区分点数只辨认手势所以在实现单点拖动和多点拖动实现不同功能时又遇到问题。现在必须解决获取触摸点数问题,这时考虑到一下三种解决方案: ? 从C#工程中的窗口处理过程中获取触摸点数 C#工程提供了类似于win7的两种消息处理方法GestureHandler和TouchHandler,前者核心函数为: WindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam) 第一个参数为窗口句柄,第二个为手势类型,第三个没有使用,第四个用来获取手势信息,此时需要提到此处理方法用到的结构体 public struct GESTUREINFO { public uint cbSize; // size, in bytes, of this structure (including variable length Args field) public uint dwFlags; // see GF_* flags public uint dwID; // gesture ID, see GID_* defines public IntPtr hwndTarget; // handle to window targeted by this gesture public POINTS ptsLocation; // current location of this gesture public uint dwInstanceID; // internally used public uint dwSequenceID; // internally used