欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

WPF 触摸底层 PenImc 是如何工作的

程序员文章站 2022-04-14 16:20:51
在 WPF 里面有其他软件完全比不上的超快速的触摸,这个触摸是通过 PenImc 获取的。现在 WPF 开源了,本文就带大家来阅读触摸底层的代码,阅读本文需要一点 C# 和 C++ 基础 ......

在 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