mouseSync后续功能完善心得 

背景

上篇博客中,我介绍了mouseSync这款软件的开发初衷、使用场景和开发的过程,感谢一些朋友试用该软件并给我留言,提出了一些新的需求和issue。同时也感谢开发者头条的推荐至头版的精选板块,让那篇开发笔记被2万开发者阅读过了,收获了github的star若干、公众号的关注若干、赞赏约50元。真的非常感谢大家的关注、star鼓励和金钱赞赏。

我整理了一下大家的需求,大体上可归纳为3类。

  1. 没有logo
  2. 在外部设备(鼠标/Trackpad真正属于的那台Mac)上容易引起误操作
  3. 一个关键的功能,左键拖拽功能,没有实现

在这篇博客中,我就来写一下对上面提到的三个需求的解决方案。完成上述三个需求后的软件在网盘可下载到,最新代码依然开源在github上。

需求

没有logo

其实这件事挺尴尬的,我不是设计师,因此平时logo的主要来源是flaticon,这次没忍心打扰正在熟睡的非著名设计师Joseph,因此还是在flaticon上找的icon,进行了简单的颜色变换与组合。

icon考虑到了mouseSync具有鼠标和蓝牙的特性,因此还是比较写实的。考虑到蓝牙客户端有外部设备和数据中心(两台Mac)之分,因此两个icon虽然很相似,但仍有细微的不同。不同点在于,数据中心的icon多了一个类似信号指示的标志,表示它是接收蓝牙信号的一端。

由于本人设计方面实在业余,还请大家轻喷。有空再找非著名设计师Joseph实现一枚漂亮的logo。

icons

在外部设备端容易引起误操作

之前的demo视频中,为了让两台电脑看起来一致,我录的是同时打开文件,同时在图标上右击,在现实生活中,在同一时刻同时操作两台Mac的需求很少不存在。通常情况下,用户还是想对某一台Mac进行操作的。mouseSync的定位应该是:帮助用户用一个鼠标操作两台电脑,且给用户决定什么时候操作哪台,省去两个鼠标之间换来换去的麻烦。

之前的项目中,我对鼠标事件添加的均是全局监控事件,因此只要mouseSync生命周期没有结束,它的操作将实时被发送到另一台mac上。我想过两个解决方案:

  1. 跟其他共享键盘的软件一样,使用全局快捷键对蓝牙通知服务进行开启/关闭
  2. 将全局监控事件变成局部监控事件,蓝牙通知服务仅在应用视图为Keywindow时有效

为什么选择Plan B

关于方案1,我觉得作为一款软件,快捷键应当由用户自己决定,虽然cocoapod上已经有非常好的项目KeyHolder进行支持,但是考虑到两个因素还是决定使用Plan B。

先看看Plan B,Plan B是这样的,首先让当前的window全屏,但是里面没有内容,因此无论在当前试图下如何操作,都不会有可能造成误操作。回到Plan A,快捷键的录入放到哪里呢?如果放到当前视图,将会带来一定的误操作风险;如果放到MenuBar,那么用户将不得不退出全屏模式,到菜单栏进行设置,这样的解决方案破坏了用户体验,综合考虑,最终还是选了Plan B。

实现过程中遇到的问题

让当前窗口一打开就全屏的方法是在ViewDidLoad方法体中进行控制的:

override func viewDidLoad() {
    super.viewDidLoad()
    self.view.window?.zoom(self)
}

但是我很快就发现了问题:尽管已经将应用全屏了,但是用户依然会有一定几率对dock栏和顶部菜单栏进行误操作。用户如果在操作另一台电脑的时候,还得注意这台电脑上是不是误操作了,这不符合我们软件开发的初衷。考虑到有些用户将dock放在底部,有些用户将dock放在左边,因此,我加了这样的限制,当用户将鼠标移动到距离屏幕边缘50px以内的时候,程序帮助他将鼠标移动至屏幕中央。参照下图,灰色地带就可以作为防止鼠标误操作的缓冲区。

防止对dock和菜单栏误操作解决方法示意图

阅读过上一篇博客的朋友一定注意到了,我们的程序是记录上一个鼠标点的位置,并将上一个鼠标点的位置与当前位置相减,将位置变动作为特征值进行发送。因此,在鼠标被程序移动到屏幕中央的时候,需要将上一个位置更新为屏幕中央的坐标。

//屏幕宽高的获取方式
    let SCREEN_WIDTH = NSScreen.main()!.frame.width
    let SCREEN_HEIGHT = NSScreen.main()!.frame.height
}

左键拖拽功能

左键拖一个文件或窗口到一个新位置这个功能还是经常被使用到的,我使用的是Trackpad,通常是使用三支拖移。想要监听外围设备的这个动作并不难,但是想让计算机模拟这个操作相对比较困难。就像双击不是单纯的模拟单击事件两次一样,这次我又踩到坑了,drag and move事件并不是简单的mousedown-> mousemove -> mouseup这么容易的事情。

我在Stack Overflow上找到了正确的方法:有一个叫kCGEventLeftMouseDragged的事件,专门来处理这件事。

void mouse_left_drag_to(float x, float y) {
    CGEventRef left_drag_event = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDragged, CGPointMake(x, y), 0);

    CGEventPost(kCGHIDEventTap, left_drag_event);
    CFRelease(left_drag_event);
}

和Scroll滚轮事件一样,这里我们采用的是Quartz模拟鼠标事件,因此依然需要C与swift进行混编。模拟总共可分为3步:第一步是创建一个Quartz鼠标事件;第二步是讲这个时间放到事件流中,并指定位置;第三步是release这个事件。

模拟这个事件的时候,随着鼠标拖拽后的不停移动,会不停产生event,而且event中x和y的偏移量始终是相对于第一次鼠标drag按下时的坐标。因此,我们需要记录两件事,第一件事是第一次drag时候的坐标,第二件事是当前是不是开启了新一轮的drag还是仍然在上一轮的drag中。核心代码如下:

    if dragFirst == true{
        dragFirst = false
    mouseLocBeforeDrag = NSEvent.mouseLocation()                 mouseLocBeforeDrag.y = NSHeight(NSScreen.screens()![0].frame) - mouseLocBeforeDrag.y;
    
    mouse_left_drag_to(Float(mouseLocBeforeDrag.x)+Float(dx!),Float(mouseLocBeforeDrag.y)-Float(dy!)
  }else{
    mouse_left_drag_to(Float(mouseLocBeforeDrag.x)+Float(dx!),Float(mouseLocBeforeDrag.y)-Float(dy!))
}

上面的代码有两处需要注意:

  1. dragFirst初始值为true,在mouseUp之后重新置为true,表示一轮的drag已经完成了;
  2. y的坐标是-,x的坐标是+,这是由OS X的坐标系决定的。

最后,我用两台电脑同时在画板上写了一个牛B。mouseSync的开发工作算是告一段落了,希望对大家有所帮助!

drag事件demo

Reference

  1. https://stackoverflow.com/questions/1817628/clicking-the-mouse-down-to-drag-objects-on-mac
  2. https://www.flaticon.com

结束

试用软件之后如果您对软件有任何的意见与建议,欢迎留言。如果您喜欢这款软件,也欢迎您将它推荐给您的朋友们。让我们一起将mouseSync变得更好!

我会在订阅号里不定期分享我个人的macOS/ios开发心得和开发笔记,也会在里面发表对于苹果产品/框架/趋势的拙见,希望爱好科技产品或者苹果生态圈的开发者关注。相信本公众号一定能给您带来收获和启发。

【欢迎扫码关注微信公众号】

扫码关注微信公众号 骨灰级果粉 获得最新文章更新

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,407评论 25 707
  • 泉州生活很丰富【17359710213 (可加微信)】 泉州哈里别墅开启聚会新潮流 娱乐 住宿 餐饮 集一身 一条...
    散匠阅读 325评论 0 0
  • 4月16日,小雨。 早晨,从小丫头的摇晃中醒来,她兴奋的告诉我,下雨了。 拉开窗帘,微雨淅沥,鸟儿欢鸣,花落无语,...
    菱然阅读 397评论 0 0
  • 从未想过常常开导身边人情感的我谈恋爱是什么样子,多愁善感易喜易怒,一遇到关于他的事情完全变成傻瓜,别人只要说...
    你在看么阅读 504评论 2 3