Mac指针事件模拟-远程桌面工具(二)

129 阅读2分钟

前言

上篇文章中,我们借助WebRTC的P2P能力做了一个远程桌面工具的基础拉流功能,但远程工具只看到画面还不够,我们还需要对画面进行交互,这篇文章我们看一下指针事件如何实现。

指针时间的采集

因为我们使用的是浏览器作为拉流端,那么我的画面在浏览器是一个video标签来作为远程电脑的画面承载容器。我们对这边video标签做的点击事件要作为采集的目标,这里选择的是pointerEvent事件作为采集,相关采集的代码也很简单,我直接贴在下方


            function initDcEvent(dataChannel) {
                const remoteVideo = document.getElementById('remoteVideo');

                remoteVideo.addEventListener('pointerdown', event => {
                    sendPointerEvent('down', event);
                });
                remoteVideo.addEventListener('pointerup', event => {
                    sendPointerEvent('up', event);
                });
                remoteVideo.addEventListener('pointermove', event => {
                    if (event.pointerType === 'mouse' && event.buttons === 0) return; // 鼠标滑过忽略
                    sendPointerEvent('move', event);
                });

                function sendPointerEvent(type, event) {
                    const rect = remoteVideo.getBoundingClientRect();
                   
                    const x = +((event.pageX - (rect.left + window.scrollX)) / widthRatio).toFixed(4);
                    const y = +((event.pageY - (rect.top + window.scrollY)) / heightRatio).toFixed(4);
                    const message = {
                        type: 'pointer',
                        eventType: type,
                        x: x,
                        y: y
                    };
                    if (!dataChannel || dataChannel.readyState !== 'open') return;
                    dataChannel.send(JSON.stringify(message));
                }
            }

这里的x, y值就是通过计算和比例系数widthRatio算出来的目标电脑的指针坐标,然后数据通过dataChannel通道发送给我们上文写的Mac客户端。

dataChannel通道是WebRTC内的P2P通道,这里不做额外解释,不熟悉的朋友建议查询一下文档

指针事件的模拟

我们的Mac客户端接收到浏览器通过DataChannel通道传输过来Json字符串,我们解析Json之后能得到相应的坐标,我们看下Mac怎么使用代码来模拟鼠标的注入,通过查询我找到了CGEvent的相关能力. 相关核心代码在这里


    private func handleCoordinateMessage(_ message: String) {
        guard let data = message.data(using: .utf8),
              let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
              let type = json["type"] as? String,
              type == "pointer",
              let eventType = json["eventType"] as? String,
              let x = json["x"] as? CGFloat,
              let y = json["y"] as? CGFloat else {
            return
        }
        
        let location = CGPoint(x: x, y: y)
        
        // 检查辅助功能权限
        if !AXIsProcessTrusted() {
            print("请前往系统设置 -> 隐私与安全性 -> 辅助功能中开启权限")
            return
        }
        
        
        switch eventType {
        case "move":
            let event = CGEvent(mouseEventSource: nil, mouseType: .mouseMoved, mouseCursorPosition: location, mouseButton: .left)
            event?.post(tap: .cghidEventTap)
        case "down":
            let event = CGEvent(mouseEventSource: nil, mouseType: .leftMouseDown, mouseCursorPosition: location, mouseButton: .left)
            event?.post(tap: .cghidEventTap)
        case "up":
            let event = CGEvent(mouseEventSource: nil, mouseType: .leftMouseUp, mouseCursorPosition: location, mouseButton: .left)
            event?.post(tap: .cghidEventTap)
        default:
            break
        }
    }

大致测试了,鼠标的点击能力和相关坐标对应位置都没问题,完!