开发mouseSync的初衷
或许是不想让买显示器送主机的笑话成真,Apple将iMac 2015之后的版本关闭了纯显示器模式。Apple store里LG的显示器挺贵的,27寸5K版本售价超过了1万元,衬托出了iMac 27寸13000+的定价之良心。考虑到两者颜值的差别,在看脸的时代,iMac的销量还是很不错的。iMac的用户很可能也是MacBook的用户,因此让iMac和MacBook协同工作是很有意义的一件事。当然,也同样适用于两台MacBook之间的协同工作。
为了让两台Mac协同工作,我发现最痛苦的事情莫过于需要两套触摸板(鼠标)和键盘。在谷歌里搜了半天,发现了很多通过蓝牙将一台mac的键盘作为另一台mac键盘的软件,但是将一台mac的鼠标作为另一台mac的鼠标的软件却很少。比较知名的软件是Synergy,但是它的普通版售价19刀、pro版售价29刀,作为一个工程师的我不愿意掏这钱,于是mouseSync出现了。和以前一样,它是开源的,托管在github上,地址是:https://github.com/zhihaozhang/mouseSync
mouseSync开发步骤
开发任何东西,我首先问自己的事情便是:大象放进冰箱一共分几步?要想让一台mac的鼠标事件同步给另一台mac,首先需要将两台电脑连接起来,建立一条稳定的专用通道。然后每当宿主机发生相应时间的时候,发通知给另一台Mac,让它的鼠标也跟着移动。
为什么选用蓝牙
考虑到协同工作肯定两台电脑靠的很近,在蓝牙的接收范围内。再考虑到蓝牙4.0标准的耗电量很低,而且即使在没有wifi的情况下依然可以工作,因此我选用了蓝牙技术来建立专用通道。蓝牙框架中值得注意的概念大约有5个。我想举一个我生活中的例子来介绍下面的概念:我每天会用一个体重计量一下体重,然后体重计会通过蓝牙传到我的手机App里,这样我就可以知道我每天的体重以及近期体重的趋势。
- Central(数据中心) 用来展示信息的设备,对应上面例子中的手机。
- Peripheral(外围设置) 原始数据采集,对应上面例子中的体重计。
- Service(服务) 指外围设置提供的各种能力,对应上面例子中体重计称重的能力。
- Characteristic(特征值) 服务中的数据成为特征值,对应上面例子中的体重数据、体质数据等其他数据。
- UUID 服务或特征值的唯一标志。每个服务或特征值均有一个唯一标识。比如我的体重计不仅能称重,还能测体脂、水分等特征值,手机App端能区分体重数据和水分数据,就靠UUID。当然,UUID的值需要数据中心和外围设置通讯前就约定好。
蓝牙通讯的流程图清楚的解释了蓝牙通讯的整个过程,这个过程类似TCP/IP的三次握手,不过没有那么严格。简单的来说,这个过程就是外设将自己的特征值通过服务的形式打包发布,通过建立起来的蓝牙通道广播给数据中心。首先,数据中心扫描蓝牙信号,发现外设后,与外设建立起链接,请求服务携带的数据,一次最基本的读取数据交换就算是完成了。当然,数据中心也可以发指令给外设。对于经常变化的特征值,数据中心可以订阅它,每当它发生变化时,可以实时通知数据中心,以便数据中心做出相应变化。具体到Cocoa框架,蓝牙的相关类见下图。
Peripheral相关类
CBPeripheralManager是管理外设的,提供了发布服务,广播方法,CBPeripheralManagerDelegate是它的代理方法,考虑到了外设和数据中心之间数据交换是异步的,它定义了一组协议方法,根据不同的状态出发回调代理方法完成双方数据交换。
CBPeripheral类是建立蓝牙连接后,给CBCentral调用peripheral外设提供的对象。CBPeripheralDelegate是它的代理协议,提供辅助CBPeripheral完成服务发现、数据获取、订阅数据后的回调方法。
Central相关类
CBCentral是数据中心类,完成蓝牙设备发现,建立连接,请求服务数据功能。CBCentralDelegate是它的代理协议,辅助它central提供Peripheral发现,建立连接后的回调。
service相关类
CBMutableService是可变类型的服务对象类,UUID和特征值可以修改。与它对应的是CBService,属性不可修改的服务对象类。
characteristic相关类
CBMutableCharacteristic是可变类型的特征值对象类,UUID、properties属性可以修改。与它对应的是CBCharacteristic类,上述属性不能修改。
回到mouseSync,两台Mac中,负责采集触摸板事件的那台Mac是外围设置,另一台Mac是数据中心。建立起蓝牙物理通道之后,接下来要做的事是监听外围设置的触摸板事件,然后通知数据中心的触摸板跟着该事件进行响应。
在外围设备端监听鼠标事件
鼠标事件的监听分为全局监听和应用内监听,显然全局监听更符合我们的要求。对键盘鼠标事件的监听,主要调用的是NSEvnet里的addGlobalMonitorForEvents方法。鼠标事件有很多种,这里我选取了比较重要的几种,分别是移动(.mouseMoved)、单击(.leftMouseDown/up)、双击(特殊处理)、右击(.rightMouseDonw/up)、触摸板的上下左右滑动(.scrollWheel)和鼠标的上下滑动。
移动事件
移动比较好处理,这里我关心的是当前位置相比于上一个位置x和y方向的坐标变动情况,因此需要一个变量记录上一个位置,然后将x和y方向的变动通过蓝牙发送给数据中心,让数据中心的鼠标也进行相应的变动。值得注意的是,Mac的坐标(0,0)点在左下方,因此移动鼠标的时候需要进行相应的转换。
单击事件
单击事件分成两个步骤,第一个步骤是左键按下,第二个步骤是左键弹起来。只有两个事件都发生,我们才认为整个单击的过程完成了。单击事件并不需要传外围设置的坐标给数据中心,直接人数据中心在当前的鼠标位置执行单击事件即可。
双击事件
双击事件和单击事件很像,监听事件传入的event对象有一个clickCount属性,如果该属性的值大于等于2,那么就认为它是双击事件。后面的步骤跟单击事件一样。
右击事件
右击事件跟单击事件就更像了,只是将名称由left改成了right。
滑动事件
滑动事件在x和y方向的变化值分别对应的是event对象里的deltaX和deltaY,将这两个值传给数据中心,并有数据中心跟着这两个值进行相应的滑动即可。通常情况下,用户只会在一个方向上进行滑动(鼠标只能上下滑动),因此我将鼠标左右和上下的滑动分成了两个部分进行处理,传递的时候判断外围设置在哪个方向上更显著,让数据中心进行相应的滑动。
在数据中心端模拟鼠标事件
单击、右击事件的模拟
前面提到过了,单击和右击事件非常像,因此举同一个例子就可以说明问题。
|
|
思路是通过CGEvent创建一个事件,然后调用该事件的post方法告诉系统,执行该事件。
鼠标移动事件的模拟
移动事件的模拟跟上面思路一样,要注意的是OS X屏幕的坐标系统,x和y方向的变化是加还是减。
双击事件的模拟
开始以为双击事件的模拟无非就是模拟两次单击事件,让他们之间的时间间隔非常小。但经过尝试,该方式行不通。在网上搜了一些solution,最后居然是在我最喜欢的炉石传说的一个插件里发现的答案。 代码如下:
|
|
滑动事件的模拟
滑动事件的模拟,swift目前支持的不是很好,我在Stack Overflow上找到了一个比较tricky的方法。需要swift和C混编,借助C来完成任务。关于swift和C/OC的混编内容比较多,这里不展开讲了,感兴趣的可以研究我的开源代码。
|
|
在这里遇到了一个诡异的问题,上下滑动可以很好的工作,但是左右滑动死活不能和预期的一样,单步跟踪后发现传入函数的确实是float类型的正确值,但是创建upEvent的时候,该变量的值突变了。后面将float类型改成了int类型,又可以正常的工作了。如果知道为什么的同学可以留言告诉我。
demo
后记
开发mouseSync的过程中,让我更深刻的理解了Cocoa的蓝牙框架、键盘鼠标事件。纸上得来终觉浅,绝知此事要躬行。
另外,mouseSync的开发还没有完成,目前存在的问题是外围设备这边的鼠标容易误操作,因此我准备将外围设备的鼠标控制在一个window内,一旦移除这个window,就强行将光标移动回window中心。通过快捷键控制软件的开启和关闭。还有一个比较重要的功能正在开发中,那就是我非常喜爱的三指拖移功能,敬请期待!
你对mouseSync的功能还有哪些期待或建议呢?不妨留言告诉我吧!
开发和写本文过程中参考了以下资料,在这表示感谢:
【1】 macOS应用开发,赵剑 张帆 合著
【2】HSTracker的github开源代码 https://github.com/HearthSim/HSTracker/blob/master/HSTracker/Utility/Automation.swift
【3】Stack Overflow: https://stackoverflow.com/questions/42813264/cocoa-application-scroll-programmatically
【4】 ios蓝牙技术:http://www.jianshu.com/p/c1154179da8a
我会在微信公众号【骨灰级果粉】中不定期分享我个人的macOS/ios开发心得和开发笔记,也会在里面发表对于苹果产品/框架/趋势的拙见,希望爱好科技产品或者苹果生态圈的开发者关注。相信本公众号一定能给您带来收获和启发。
原文作者: Chih-Hao
原文链接: http://zhihaozhang.github.io/2017/09/23/让iMac与MacBook高效协同工作——mouseSync开发心得/
发表日期: September 23rd 2017, 7:49:34 pm
版权声明: 本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可
-
Next Post青芒 for Mac客户端开发笔记
-
Previous PostBuild reusable visualization using d3.js and AngularJS