在OS X中以编程方式模拟鼠标输入

72

在OS X中,从程序中模拟鼠标的操作是否可能?具体来说,我正在尝试使用两个网络摄像头模拟触摸屏。所以,假设我可以获得X、Y位置,我可以将信息发送到操作系统作为鼠标移动或单击吗?

编辑-或者,如果在另一个操作系统中特别容易,我愿意考虑。


根据您的使用情况,有一个名为“UI Recorder”的工具,可以记录您对程序用户界面所做的任何操作,并在需要时进行回放。 - zneak
谢谢,但我真正想做的是向操作系统发送任意鼠标输入。 - Rob Lourens
3
我之前做过这件事,通过运行VNC服务器并编写一个小的VNC客户端来向服务器发送鼠标事件。有一些开源的VNC服务器可以实现这一点,如果必要的话可以查找其中一个的源代码。 - user132014
好主意,Tom。我会考虑的。 - Rob Lourens
3
你们真是救命恩人。我通过将PowerMac连接到6个投影仪显示器上,排列成一个2x3的网格,组成了一个巨大的桌面展示墙。这个方法很有效,但有一个问题:操作系统不允许我通过编程方式调整窗口大小高过一行。我可以用鼠标调整窗口大小,但不能使用AppleScript。使用这段代码,我首先可以使用AppleScript创建和定位一个窗口,然后使用一个命令行拖放程序来拉伸窗口到所需大小。需要注意的是,没有特殊的拖动事件,只要按照正确的顺序调度这四个事件即可。 - user1203016
4个回答

102

可以,你可以使用Quartz事件服务来模拟输入事件。

假设是C语言,我写了这个快速示例:

#include <ApplicationServices/ApplicationServices.h>
#include <unistd.h>

int main() {
    // Move to 200x200
    CGEventRef move1 = CGEventCreateMouseEvent(
        NULL, kCGEventMouseMoved,
        CGPointMake(200, 200),
        kCGMouseButtonLeft // ignored
    );
    // Move to 250x250
    CGEventRef move2 = CGEventCreateMouseEvent(
        NULL, kCGEventMouseMoved,
        CGPointMake(250, 250),
        kCGMouseButtonLeft // ignored
    );
    // Left button down at 250x250
    CGEventRef click1_down = CGEventCreateMouseEvent(
        NULL, kCGEventLeftMouseDown,
        CGPointMake(250, 250),
        kCGMouseButtonLeft
    );
    // Left button up at 250x250
    CGEventRef click1_up = CGEventCreateMouseEvent(
        NULL, kCGEventLeftMouseUp,
        CGPointMake(250, 250),
        kCGMouseButtonLeft
    );
    // Now, execute these events with an interval to make them noticeable
    CGEventPost(kCGHIDEventTap, move1);
    sleep(1);
    CGEventPost(kCGHIDEventTap, move2);
    sleep(1);
    CGEventPost(kCGHIDEventTap, click1_down);
    CGEventPost(kCGHIDEventTap, click1_up);
    // Release the events
    CFRelease(click1_up);
    CFRelease(click1_down);
    CFRelease(move2);
    CFRelease(move1);
        return 0;
}

假设使用GCC编译,请使用以下命令:

gcc -o program program.c -Wall -framework ApplicationServices

享受神奇的体验。


@Rob:不用谢。希望我的回答没有来得太晚。 - jweyrich
4
答案很好,但现在所引用的文档说明:“这些函数仍然得到支持,但不建议在新开发中使用”,他们忘记提到什么是推荐的方法,请问有人知道吗? - Alex
4
根据文档,these functions 严格指的是在文件 CGRemoteOperation.h 中声明的旧版事件相关函数,但这里没有使用到。 - jweyrich
1
CGEventCreateMouseEvent方法似乎在最近的OSX 10.10和10.11 SKD中不存在。从QT IDE编译时,我收到错误消息:“没有匹配的函数调用'CGEventCreateMouseEvent' CGEventRef event = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake(x, y), 0 /ignored/); - Yiannis Mpourkelis
1
@YiannisMpourkelis:该方法自OS X 10.4以来就存在(请参见我在10.10.5上的输出)。如果您使用Homebrew安装Qt,则问题似乎出现在Qt5公式中(https://github.com/Homebrew/homebrew/issues/42014)。 - jweyrich

14

Swift鼠标移动和点击示例:

func mouseMoveAndClick(onPoint point: CGPoint) { 
    guard let moveEvent = CGEvent(mouseEventSource: nil, mouseType: .mouseMoved, mouseCursorPosition: point, mouseButton: .left) else {
        return
    }
    guard let downEvent = CGEvent(mouseEventSource: nil, mouseType: .leftMouseDown, mouseCursorPosition: point, mouseButton: .left) else {
        return
    }
    guard let upEvent = CGEvent(mouseEventSource: nil, mouseType: .leftMouseUp, mouseCursorPosition: point, mouseButton: .left) else {
        return
    }
    moveEvent.post(tap: CGEventTapLocation.cghidEventTap)
    downEvent.post(tap: CGEventTapLocation.cghidEventTap)
    upEvent.post(tap: CGEventTapLocation.cghidEventTap)
}

11

如果您不想编译软件,而是寻找一个基于shell的工具,Cliclick 可能是解决方案。


谢谢您的推荐,@ghoti。我已经寻找了几周这样的工具 :) - Bogdan Solga

4

这是一个基于jweyrick答案的工作中的C程序:

// Compile instructions:
//
// gcc -o click click.c -Wall -framework ApplicationServices

#include <ApplicationServices/ApplicationServices.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
  int x = 0, y = 0, n = 1;
  float duration = 0.1;

  if (argc < 3) {
    printf("USAGE: click X Y [N] [DURATION]\n");
    exit(1);
  }

  x = atoi(argv[1]);
  y = atoi(argv[2]);

  if (argc >= 4) {
    n = atoi(argv[3]);
  }

  if (argc >= 5) {
    duration = atof(argv[4]);
  }

  CGEventRef click_down = CGEventCreateMouseEvent(
    NULL, kCGEventLeftMouseDown,
    CGPointMake(x, y),
    kCGMouseButtonLeft
  );

  CGEventRef click_up = CGEventCreateMouseEvent(
    NULL, kCGEventLeftMouseUp,
    CGPointMake(x, y),
    kCGMouseButtonLeft
  );

  // Now, execute these events with an interval to make them noticeable
  for (int i = 0; i < n; i++) {
    CGEventPost(kCGHIDEventTap, click_down);
    sleep(duration);
    CGEventPost(kCGHIDEventTap, click_up);
    sleep(duration);
  }

  // Release the events
  CFRelease(click_down);
  CFRelease(click_up);

  return 0;
}

托管于https://gist.github.com/Dorian/5ae010cd70f02adf2107


3
我会使用usleep(duration*1000000)。否则,你只能睡整数秒! - nneonneo
2
因为没有超时并且导致我的Mac崩溃,所以被踩了。 - Artem Bernatskyi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接