macOS下截图程序的开发

Aug 31 2017

讲一讲macOS下截图程序是如何开发的,目前网络上也没有什么好的资料

准备阶段

说道编程,现在的趋势一定是不要造轮子(当然优秀的轮子可以造),所以第一件事是去Github查查看是否有人写过好的,结果发现大部分都是工具类型的库,并没有完整的带UI的库。

第二个想法是看看别人是怎么写的。这个年头,一个码农没多几门手艺是活不下去的。作为一个前端开发工程师,我业余的时间几乎都在写ObjC、C++、Java等,反编译和基础的网络攻防更是掌握了一点点,于是我就对业界标杆QQ和价值观钉钉动手了 =。=

QQ的方案是双进程,钉钉的方案是点击截图的时候启动一个独立的进程。我开心坏了,这不是等着我拿来直接用么?而且价值观场还把截图封装成了一个npm包,简直不要不要的。不过,很快这个方案就被PM否定了:怕引起版权问题。说的很对,毕竟那是人家内网npm服务器上的包,即使源码摆在你面前你也不能用,因为人家有价值观!

画风变得有点沉重,我决定自己开发截图。考虑到项目时间上的问题,我准备简化UI的搭建,使用macOS自带的WebKit和前端技术来实现UI,这便是坑的开始,当然这个坑并不大。

截图必备的几个Features

键盘事件响应,鼠标事件响应,肯定都是必要的。考虑到目前macOS基本都开启了隔离Spaces的功能,我准备在每个NSScreen上开启一个浮动的窗口,拦截鼠标和键盘事件。

But,macOS的安全机制是很健全的,如果你使用继承NSPanel的方式实现,你的窗口就不能获取焦点。这里交代一下,macOS下一个窗口完全获取我们认知上那种焦点,需要满足三个条件的:你的NSRunningApplication是active的,你的NSWindow是mainWindow,你的程序是keyWindow。所以,最后的最后,我的FloatPanel继承的是NSWindow实现的。

鼠标事件的监听,是我觉的最大的坑。我目前并没有找到什么好的方案。在多个屏幕的前提下,每个屏幕是独立的NSWindow,我需要在鼠标移动进响应frame的时候使齐成为mainWindow和keyWindow,不然鼠标click的第一下默认行为是获取焦点而不是开始截取区域。

目前我的实现是通过NSTimer实现的2333…谁有好的建议告诉我…

截取的操作是通过Core Graphics相关接口实现的。这里有个trick,就是如果你隐藏掉鼠标然后立刻截取,会因为屏幕缓冲的原因截取到鼠标。但是当你等到下一个小渲染阶段,就不会截取到,而且Retina屏的缓冲会比普分屏多(可能是一个,我观察到的是一个) =。=

键盘事件,可以使用Carbon监听,不过有点大材小用了。另外,使用的是Carbon用来监听快捷键的接口,但是修饰键为0,就可以只监听一个键,比如早期我的实现是监听了Esc键来退出。。。现在换成了addLocalMonitorForEventsMatchingMask,因为我的程序继承NSWindow后能够获取焦点了 =。=

截取和创建窗口使用多线程异步,当然,在macOS下,用GCD爽的多 =。=

因为做成尽可能快,所以采用了QQ的模式。但是我的主进程是Node.js,所以采用了最简单的UNIX信号量,SIGALRM,简单暴力。

那些坑们

  • 截图后需要让Node.js进程获取焦点,后来想起我用户UNIX信号量,所以直接获取对应PID所属的NSRunningApplication,让其active了
  • 最最最大的坑,Display的数量和NSScreen的数量不一样!!不一样!!!所以我采用NSScreen过滤,然后获取其包含的Display列表,取第一个来截图,反而更快乐 =。=

说了那么多,代码呢?

不给 -。-

macOS, 截图, 编程