有人能告诉我如何将鼠标点击事件发送到mac osx中的隐藏(不显示在前台)窗口吗?我正在尝试使用pyobjc
或pyautogui
,这对于这种情况来说真的很新。任何关键字或想法将不胜感激。谢谢!
答案 0 :(得分:6)
编辑:根据更多调查结果,最后会有更新,但简短的回答似乎是在OSX中通常无法做到这一点。
免责声明:这不是真的答案。我即将发布大致相同的问题,但后来我发现了这个问题。我没有问同样的问题,而是认为我会对我已经找到/试过的内容做出一些评论。不过,我很擅长为SO做贡献,所以我没有足够的声誉来发表评论,所以我在这里以“回答”的方式发表评论。所以这不是一个真正的答案,但希望它可以帮助你或其他人更近一点。
排序答案:
AFAICT,在OSX中向一个应用程序发送鼠标事件的“正确”方法是使用核心图形CGEventPostToPSN
功能。
获取“PSN”(进程序列号)并非超级简单,并且不再使用人们过去常用的所有方法。有一个名为CGEventPostToPid
的替换函数,它使用标准的* nix进程ID来指定目标应用程序。
我已成功将键盘事件发布到具有此功能的后台应用程序,但不是鼠标事件。
例如,这会将字符发送到您通过PID指定的任何应用程序:
pid = 1234 # get/input a real pid from somewhere for the target app.
type_a_key_down_event = Quartz.CGEventCreateKeyboardEvent(objc.NULL, 0, True)
type_a_key_up_event = Quartz.CGEventCreateKeyboardEvent(objc.NULL, 0, False)
Quartz.CGEventPostToPid(pid, type_a_key_down_event)
Quartz.CGEventPostToPid(pid, type_a_key_up_event)
(键盘事件创建的文档:https://developer.apple.com/reference/coregraphics/1456564-cgeventcreatekeyboardevent?language=objc)
但是,这不会向目标应用发送点击:
pid = 1234 # get/input a real pid from somewhere for the target app.
point = Quartz.CGPoint()
point.x = 100 # get a target x from somewhere
point.y = 100 # likewise, your target y from somewhere
left_mouse_down_event = Quartz.CGEventCreateMouseEvent(objc.NULL, Quartz.kCGEventLeftMouseDown, point, Quartz.kCGMouseButtonLeft)
left_mouse_up_event = Quartz.CGEventCreateMouseEvent(objc.NULL, Quartz.kCGEventLeftMouseUp, point, Quartz.kCGMouseButtonLeft)
Quartz.CGEventPostToPid(pid, left_mouse_down_event)
Quartz.CGEventPostToPid(pid, left_mouse_up_event)
相关问题的一些SO答案建议您使用CGEventPost
进行鼠标事件:
Quartz.CGEventPost(Quartz.kCGHIDEventTap, left_mouse_down_event)
Quartz.CGEventPost(Quartz.kCGHIDEventTap, left_mouse_up_event)
这就是我认为pyautogui正在做的事情,因为它成功地在请求的坐标处单击了鼠标,但它通过移动并单击全局鼠标来实现。运行该代码会将鼠标移动到屏幕上的某个点,然后单击该点上的任何内容,然后将鼠标放在那里。这不是我想要的,我认为这不是你想要的。
我想要的是CGEventPostToPSN
或其更现代的对应CGEventPostToPid
中描述的行为:仅向目标应用程序发布的输入事件,无论它是否在前台,不会窃取聚焦或更改实际的鼠标位置。
我最接近的是调整Simulating mouse-down event on another window from CGWindowListCreate
中的一些代码建议使用NSEvent而不是CGEvent。我用Objective-C和pyobjc尝试了这个,但成效有限。我尝试的Objective-C版本对我没有任何帮助,所以我不会发布它。 pyobjc版本允许我从终端点击iTerm,反之亦然,但我无法让点击进入其他应用程序。我试过Chrome,Console和其他几个没有运气的人。
这是部分工作的pyobjc代码:
#! /usr/bin/env python
"""
CLI for sending mouse clicks to OSX apps.
Author: toejough
License: MIT
"""
# [ Imports ]
import Quartz
import fire
import AppKit
# [ API ]
def click(*, x, y, window_id, pid):
"""Click at x, y in the app with the pid."""
point = Quartz.CGPoint()
point.x = x
point.y = y
event_types = (
AppKit.NSEventTypeMouseMoved,
AppKit.NSEventTypeLeftMouseDown,
AppKit.NSEventTypeLeftMouseUp,
)
for this_event_type in event_types:
event = _create_ns_mouse_event(this_event_type, point=point, window_id=window_id)
Quartz.CGEventPostToPid(pid, event)
# [ Internal ]
def _create_ns_mouse_event(event_type, *, point, window_id=None):
"""Create a mouse event."""
create_ns_mouse_event = AppKit.NSEvent.mouseEventWithType_location_modifierFlags_timestamp_windowNumber_context_eventNumber_clickCount_pressure_
ns_event = create_ns_mouse_event(
event_type, # Event type
point, # Window-specific coordinate
0, # Flags
AppKit.NSProcessInfo.processInfo().systemUptime(), # time of the event since system boot
window_id, # window ID the event is targeted to
None, # display graphics context.
0, # event number
1, # the number of mouse clicks associated with the event.
0 # pressure applied to the input device, from 0.0 to 1.0
)
return ns_event.CGEvent()
# [ Script ]
if __name__ == "__main__":
fire.Fire(click)
我试着玩旗帜,活动#,时间等,没有运气。无论我如何改变这些,我能够实现的最好的方法是将iTerm的点击发送到终端或从终端发送到iTerm。不确定终端应用程序是什么让它们起作用而不是其他应用程序。
您可以从window_id
获取CGWindowListCopyWindowInfo
4/21/17 4:16:08.284 PM launchservicesd[80]: SecTaskLoadEntitlements failed error=22
4/21/17 4:16:08.288 PM launchservicesd[80]: SecTaskLoadEntitlements failed error=22
4/21/17 4:16:08.295 PM tccd[21292]: SecTaskLoadEntitlements failed error=22
以及https://developer.apple.com/reference/coregraphics/quartz_window_services?language=objc
如果某人对所有应用都有更好的答案,真实答案,请发布并告知我们。或者,如果您对点击不起作用的原因有解释/线索,请发布。
我收集的唯一其他可能有趣的证据是,无论何时运行上述脚本,我都会在我的系统日志中获得以下内容:
{{1}}
虽然可能是红鲱鱼 - 我在成功的情况下(向不同的终端应用发送点击)和失败案例(向非终端应用发送点击)都会收到这些错误。
希望这可以帮助您或其他读者更接近真正的解决方案 - 如果是这样,请回复!
<强>更新强> 我认为这些事件适用于终端应用程序,而不是一般的osx内置反焦点跟随鼠标范例的结果。您可以发送事件,但除非应用程序处于活动状态(或允许FFM),否则操作系统会阻止它。
允许FFM的应用程序(如iTerm和终端)可以接收上面的ns_event变体,这就是我可以让它们工作的原因。其他应用程序也将接收鼠标事件(不移动全局鼠标光标!),只要它们首先被聚焦。
您可以通过在上述程序或终端中插入睡眠来自行查看,并手动将目标应用程序置于前面,并查看鼠标事件是否通过。
AFAICT,使鼠标事件工作的唯一方法是绕过操作系统的FFM过滤器,我不知道有什么方法可以做到这一点。如果有人发现了,请告诉我!
有关FFM和OSX的更多信息here。