如何在Mac上通过编程方式更改光标大小

6

我希望能够在我的程序中设置光标大小的系统偏好设置(如辅助功能偏好设置中所见),然后在程序退出时将其恢复。

是否有一种方法可以从应用程序中设置光标大小(特别是)或系统偏好设置?


你实际上想要更改全局首选项,而不仅仅是在指针指向你的窗口时使用更大的光标吗? - abarnert
在这里发布了我的答案 https://dev59.com/0JXfa4cB1Zd3GeqPgIb2#40004327 - Marek H
1个回答

13

首先,如果你只是想在光标指向窗口/视图/小部件时获得更大的光标,那么你正在错误的方式下进行。阅读介绍光标管理器以了解正确的方法。

其次,即使你认为你确实想在程序运行时设置系统范围内的光标,请在继续之前仔细考虑一下。即使你的应用程序在后台或隐藏时,光标仍将保持较大。如果您已经采取了任何透明生命周期思想(用户通常不应注意到或关心您的应用程序不可见和您的应用程序已退出之间的区别),这将变得更加混乱。如果两个应用程序尝试执行此操作,会发生什么?等等。(不用说,苹果公司将拒绝App Store中执行此操作的任何应用程序。)

第三,设置系统偏好设置实际上并没有做任何事情,直到系统读取该偏好设置的新时间。而且无法保证何时会发生这种情况。因此,除非您的应用程序满足于更改可能在用户注销并重新登录(然后在退出后更改回来)之前可能无效的首选项,否则它并不是很有用。

但是,如果这确实是您要做的事情...


设置系统偏好设置非常容易。大多数由“系统偏好设置”修改的值都在默认存储中。辅助功能面板中的大多数值都在com.apple.universalaccess域中。光标大小的特定键是mouseDriverCursorSize

因此,要从bash更改光标的最大大小:

defaults write com.apple.universalaccess mouseDriverCursorSize 4.0

从 Objective-C 来说,可能需要稍微费点功夫,但是像这样(未经测试):

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *olddict = [defaults persistentDomainForName:@"com.apple.universalaccess"];
NSMutableDictionary *newdict = [olddict mutableCopy];
[newdict setObject:@4.0 forKey:@"mouseDriverCursorSize"];
[defaults setPersistentDomain:newdict forName:@"com.apple.universalaccess"];
[defaults synchronize]

那么,假设你想设置首选项,然后强制系统注意到更改呢?显然,“系统偏好设置”应用程序正在执行某些操作,并且您可以始终跟踪它以确切了解其所做的操作。

往往情况下,它会调用一些未记录或公开的私有函数。并且在不同的操作系统版本之间可能会有所不同。而且,即使这样做,结果也可能不是最好的。但从快速测试来看:

只要接受如果光标被隐藏则取消隐藏,调用CGSShowCursor似乎有效。连续两次调用CGSGetGlobalCursorData也似乎有效,尽管我不知道为什么。

当然,这些都是未记录或公开的CGSPrivate函数,但至少其他人已经对它们进行了逆向工程,因此您无需自己去完成。您需要做的就是从一些开源项目中借用代码(iTerm2拥有较完整的头文件集之一),并在苹果每次发布新的次要操作系统版本后进行测试,并调试黑魔法,即使它可用于75%的用户,却无法为那剩余的25%的用户解决问题(通常无法获得这25%的机器,甚至无法从它们获取合适的问题或答案)。

如果您想跟踪“系统偏好设置”,而且在OS X中没有追踪进程的经验,则最简单的方法是通过GUI工具“Instruments”:

  • 启动“系统偏好设置”,并导航到辅助功能、显示。
  • 启动Xcode 4.4或更高版本,在“Xcode”菜单中选择“打开开发者工具”,然后选择“Instruments”。
  • 在Instruments中,选择“Mac OS X | All”部分,然后选择“System Trace”。
  • 在“目标”下拉列表中,连接到“系统偏好设置”进程。
  • 点击“记录”按钮,并等待几秒钟让它停止旋转。
  • 拖动光标大小滑块。
  • 点击“停止”按钮,并等待更长的时间以完成分析。
  • 阅读Instruments的扩展文档,找出如何找到您想要的内容。

请注意,系统偏好设置可能不是调用特殊的系统调用来完成所需操作;例如,它可能会向窗口服务器任务发送特定的 mach 消息。幸运的是,您可以从任何看起来合理的地方开始逆向寻找。通过这样做,我发现它似乎在 UniversalAccessCore 中调用了 UACursorSetScale,该函数调用 /System/Library/PrivateFrameworks/UniversalAccess.framework/Versions/A/Libraries/libUAPreferences.dylib 中的 UAPreferencesSetValue 函数,而后者似乎会执行 CFPreferencesSetValue 并发送 CFNotificationCenterPostNotification。也许只有通知很重要?您可以在 Xcode/gdb/lldb 中对相关函数设置断点,并查看参数是什么来测试它。或者您可以弄清楚如何自己调用 UAPreferencesSetValue(我的第一个猜测是参数与 CFPreferencesSetValue 相同)。

简单检查一下:它发送的通知是“UniversalAccessDomainMouseSettingsDidChangeNotification”,带有 nilobject 和像 @{@"mouseDriverCursorSize": @1.8327533, @"pid": @12345} 这样的 userInfo 字典到默认分布式通知中心,如果在更改 NSUserDefaults 偏好后自己执行相同的操作,不会产生任何效果。此外,UAPreferencesSetValue 显然需要与 CFPreferencesSetValue 不同的参数,因为如果传递明显的值,您将在 CFNotificationCenterPostNotification 中崩溃,因此您可能需要在系统偏好设置中断点调用以查看它发送的内容。

如果您对继续进行感到舒适,请继续前进。如果不是,您需要学习很多东西,然后才能考虑如何使其正常工作。


另一种方法是通过脚本编写。如果您可以让“系统偏好设置”应用程序执行与鼠标执行相同的操作,则完成了,对吗?

只要启用了 UI 脚本(请参见您已经在“系统偏好设置”中查看的同一面板中的“启用辅助功能设备访问”复选框,或者如果您具有根访问权限,则搜索如何在编程方式下打开和关闭它),那就可以通过 System Events 进行冗长但容易操作的UI脚本。

事实上,尽管“系统偏好设置”不会公开足够的细节来实际更改任何内容,但它确实公开了足够的内容来使我们导航到正确的面板,这节省了很多UI脚本步骤。因此,这是要做的 AppleScript:

tell application "System Preferences"
    reveal anchor "Seeing_Display" of pane id "com.apple.preference.universalaccess"
end tell
tell application "System Events"
    set theSlider to slider "Cursor Size:" of group 1 of window 1 of application process "System Preferences"
    set stash to value of theSlider
    set value of theSlider to 4.0
    stash
end tell

使用NSAppleScript在ObjC中运行它,或者,如果您愿意,将其翻译为ScriptingBridgeAppscript或其他能够本地运行的内容即可完成。


太棒了!感谢你所付出的所有努力。我正在开发一个数字签名应用程序,要求用户从外部设置光标大小。这非常有帮助。 - SimonClark
好的,我已经设置了偏好,无论是从命令行还是从Obj-C,当我读取它时,它确实发生了变化,但是无论是CGSShowCursor还是CGSGetGlobalCursorData都没有导致它更新。这个AppleScript在ScriptEditor中可以工作,但在我的应用程序中却不能,并且没有返回任何错误。一个简单的脚本可以正常工作。有什么指针可以追踪系统偏好调用了什么。我无法弄清楚如何做到这一点。 - SimonClark
@SimonClark:我在答案中添加了一些初步的跟踪信息。但是,如果你不知道自己在做什么,除非你的目标是学习如何跟踪Mac应用程序(在这种情况下,你应该搜索一些指南,而不是在这里问“下一步该怎么做?”),否则这可能不是你想要继续的方式。我认为你最好弄清楚为什么脚本在你的应用程序中不起作用。你是否使用沙盒?你能否构建一个什么都不做,只运行那个脚本的微不足道的应用程序? - abarnert
在Catalina上,我也无法让那个脚本工作。它找不到“光标大小:”滑块而失败。我尝试了几种名称的滑块变体,但都没有成功。 - John Breedlove

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