WPF 触摸底层 PenImc 是如何工作的
在 wpf 里面有其他软件完全比不上的超快速的触摸,这个触摸是通过 penimc 获取的。现在 wpf 开源了,本文就带大家来阅读触摸底层的代码,阅读本文需要一点 c# 和 c++ 基础
现在 wpf 开源,所有源代码都可以在官方代码找到,本文只是让大家能够更快的了解整个触摸的代码和更快的了解代码,和知道对应的功能在哪个代码
在wpf的触摸的 penthreadworker 调用 threadproc 的方法,就通过 ms.win32.penimc.unsafenativemethods.getpenevent 方法获取触摸。本文仅讨论在 penthreadworker 下层的内容,在此上层的内容,请看wpf 触摸到事件
那么在 penimc 里面做了什么?
在 penimc 原理里面,其实就是通过共享内存和 com 的方式通过 realtimestylus 的方式快速获取触摸消息
先通过 wisptis_sm_section_name 和 wisptis_sm_mutex_name 分别拿到共享内存和进程锁这样可以通过锁通知共享内存收到消息,然后通过读取内存的信息返回到上层
整个初始化的代码放在 pimccontext.cpp 里
在 hresult cpimccontext::initnamedcommunications(__in ccomptr<itabletcontextp> pctxp)
的方法里面,初始 szsectionname 字符串作为命名管道连接方法
tchar szsectionname[max_path + 1]; stringcchprintf( szsectionname, lengthofarray(szsectionname), wisptis_sm_section_name, dwpid, dwfilemappingid);
而 wisptis_sm_section_name
的定义如下
#define wisptis_sm_more_data_event_name _t("wisptis-1-%d-%u") #define wisptis_sm_mutex_name _t("wisptis-2-%d-%u") #define wisptis_sm_section_name _t("wisptis-3-%d-%u") #define wisptis_sm_thread_event_name _t("wisptis-4-%u")
此时通过打开内存的方式
m_hfilemappingsharedmemory = openfilemapping(file_map_read | file_map_write, false, szsectionname);
可以获取内存信息
m_psharedmemoryheader = (sharedmemory_header*)mapviewoffile( m_hfilemappingsharedmemory, // handle file_map_read | file_map_write, // desired access 0, // offset in file, high 0, // offset in file, low sizeof(sharedmemory_header)); // number of bytes to map m_pbsharedmemoryrawdata = (byte*)mapviewoffile( m_hfilemappingsharedmemory, // handle file_map_read, // desired access 0, // offset in file, high 0, // offset in file, low m_psharedmemoryheader->cbtotal);// number of bytes to map
关于打开的代码请看
itabletcontextp::usenamedsharedmemorycommunications method - win32 apps
此时就可以通过 m_pbsharedmemoryrawdata
获取内存信息
这就是初始化的代码
在 wpf 调用 getpenevent 方法,将会进入 pimccontext.cpp 的 getpenevent 方法
在这个方法里面先通过 msgwaitformultipleobjectsex 等待 wisp 服务的收集,在收集完成之后会释放锁,进入 getpeneventcore 方法
在 getpeneventcore 使用很长的判断逻辑,其中主要是判断当前是获取数据才会进入到 wpf 的收集到触摸点
switch (dwwait) { case wait_timeout: m_fsinglefiretimeout = false; // (only fire the timeout once before more data shows up) *pevt = 1; // timeout event *pcursorid = 0; *pcpackets = 0; *pcbpacket = 0; *ppackets = null; break; case wait_object_0 + 0: // update // 忽略代码 case wait_object_0 + 1: // more data // 这里就是等待共享内存 dword dwwaitaccess = waitforsingleobject(m_hmutexsharedmemory, infinite); }
通过上面代码可以看到在 m_hmutexsharedmemory
的信息,可以在 m_psharedmemoryheader
读取
switch (m_psharedmemoryheader->dwevent) { case wm_tablet_packet: case wm_tablet_cursordown: case wm_tablet_cursorup: *pevt = m_psharedmemoryheader->dwevent; *pcursorid = m_psharedmemoryheader->cid; *pcpackets = m_psharedmemoryheader->cpackets; *pcbpacket = m_psharedmemoryheader->cbpackets / m_psharedmemoryheader->cpackets; chr(ensurepackets(m_psharedmemoryheader->cbpackets)); copymemory(m_pbpackets, m_pbsharedmemorypackets, m_psharedmemoryheader->cbpackets); *ppackets = (int_ptr)m_pbpackets; #ifdef delivery_profiling for (int ipacket = 0; ipacket < *pcpackets; ipacket++) { int ioffset = ipacket * (*pcbpacket) / sizeof(long); switch (m_psharedmemoryheader->dwevent) { case wm_tablet_packet: profilepackets(/*fdown*/false, /*fup*/false, ((long*)m_pbsharedmemorypackets)[ioffset + 0], ((long*)m_pbsharedmemorypackets)[ioffset + 1]); break; case wm_tablet_cursordown: profilepackets(/*fdown*/true, /*fup*/false, ((long*)m_pbsharedmemorypackets)[ioffset + 0], ((long*)m_pbsharedmemorypackets)[ioffset + 1]); break; case wm_tablet_cursorup: profilepackets(/*fdown*/false, /*fup*/true, ((long*)m_pbsharedmemorypackets)[ioffset + 0], ((long*)m_pbsharedmemorypackets)[ioffset + 1]); break; } } #endif break; case wm_tablet_cursorinrange: case wm_tablet_cursoroutofrange: *pevt = m_psharedmemoryheader->dwevent; *pcursorid = m_psharedmemoryheader->cid; *pcpackets = 0; *pcbpacket = 0; *ppackets = null; break; case wm_tablet_systemevent: *pevt = m_psharedmemoryheader->dwevent; *pcursorid = m_psharedmemoryheader->cid; *pcpackets = 0; *pcbpacket = 0; *ppackets = null; m_sysevt = m_psharedmemoryheader->sysevt; m_sysevtdata = m_psharedmemoryheader->sysevtdata; break; default: *pevt = 0; *pcursorid = 0; *pcpackets = 0; *pcbpacket = 0; *ppackets = null; break; }
定义的代码放在 pentypes.h 文件
#define wm_tablet_defbase 0x02c0 #define wm_tablet_contextcreate (wm_tablet_defbase + 0) #define wm_tablet_contextdestroy (wm_tablet_defbase + 1) #define wm_tablet_cursornew (wm_tablet_defbase + 2) #define wm_tablet_cursorinrange (wm_tablet_defbase + 3) #define wm_tablet_cursoroutofrange (wm_tablet_defbase + 4) #define wm_tablet_cursordown (wm_tablet_defbase + 5) #define wm_tablet_cursorup (wm_tablet_defbase + 6) #define wm_tablet_packet (wm_tablet_defbase + 7) #define wm_tablet_added (wm_tablet_defbase + 8) #define wm_tablet_deleted (wm_tablet_defbase + 9) #define wm_tablet_systemevent (wm_tablet_defbase + 10) #define wm_tablet_max (wm_tablet_defbase + wm_tablet_maxoffset)
这里的 wm_tablet_cursorinrange 是 (wm_tablet_defbase + 3) 也就是 707 对应在 wpf 定义的 peneventpeninrange 的值
const int peneventpeninrange = 707; const int peneventpenoutofrange = 708; const int peneventpendown = 709; const int peneventpenup = 710; const int peneventpackets = 711; const int peneventsystem = 714;
也就是上面的代码就是整个触摸的核心代码
更多代码请看
irealtimestylus::getpacketdescriptiondata (rtscom.h) - win32 apps