6

我希望能够从我的程序中为 Mac 上的光标大小设置系统偏好设置(如辅助功能偏好设置中所示),然后在程序退出后将其设置回来。

有没有办法从应用程序中设置光标大小(特别是)或系统偏好?

4

1 回答 1

13

首先,如果您只是想在光标指向您的窗口/视图/小部件时获得更大的光标,那么您的说法是错误的。正确阅读光标管理器简介

其次,即使您认为您确实想在程序运行时设置系统范围的光标,在继续之前请仔细考虑一下。即使您的应用程序在后台或隐藏,光标也会保持较大。如果您完全朝着透明生命周期的想法(用户通常不应该注意到或关心您的应用程序不可见和您的应用程序退出之间的区别)采取任何行动,这将更加令人困惑。如果两个应用程序尝试这样做,会发生什么?等等。(不用说,Apple 会拒绝 App Store 中任何这样做的应用程序。)

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

但如果这真的是你想做的……</p>


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

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

defaults write com.apple.universalaccess mouseDriverCursorSize 4.0

ObjC 有点乏味,但是像这样(未经测试):

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]

那么,如果你想设置偏好,然后强制系统注意到变化呢?显然 System Preferences 应用程序正在做某事,您可以随时跟踪它,看看它到底做了什么。

通常,它所做的是调用一些未记录或公开的私有函数。并且在不同的操作系统版本之间可能会有所不同。无论如何,它所做的可能不是最好的事情。但通过快速测试:

看起来调用CGSShowCursor工作,只要它被隐藏时取消隐藏光标是可以接受的。连续调用CGSGetGlobalCursorData两次似乎也有效,尽管我不知道为什么会这样。

当然,这些是没有记录或公开的 CGSPrivate 函数,但至少其他人已经对它们进行了逆向工程,所以你不必这样做。您所要做的就是从一些开源项目中借用代码(iTerm2具有更完整的标头集之一),并在 Apple 发布每个新的次要 OS 后进行测试,并调试 25 年不起作用的黑魔法% 的用户,即使它适用于其他 75%(无法访问这 25% 正在使用的机器,通常甚至无法从他们那里得到体面的问题或答案)。

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

  • 启动系统偏好设置并导航到辅助功能、显示。
  • 启动 Xcode 4.4 或更高版本,进入“Xcode”菜单,选择“Open Developer Tool”,然后选择“Instruments”。
  • 在 Instruments 中,选择“Mac OS X | All”部分,然后选择“System Trace”。
  • 在“目标”下拉菜单中,附加到“系统偏好设置”进程。
  • 单击“记录”按钮,然后等待几秒钟以停止沙滩球。
  • 拖动光标大小滑块。
  • 单击“停止”按钮,等待更长时间以完成分析。
  • 阅读 Instruments 上的扩展文档,了解如何找到你想要的。

但是,请记住,系统偏好设置可能不会调用特殊的系统调用来完成它需要的操作;例如,它可能是向 Window Server 任务发送特定的 mach 消息。幸运的是,你可以从任何看起来可能的事情中倒退。通过这样做,我发现它似乎在调用UACursorSetScaleUniversalAccessCore,它调用UAPreferencesSetValue/System/Library/PrivateFrameworks/UniversalAccess.framework/Versions/A/Libraries/libUAPreferences.dylib一个似乎执行 aCFPreferencesSetValue并发送 a的函数CFNotificationCenterPostNotification。也许只是那个通知很重要?您可以通过在 Xcode/gdb/lldb 中的相关函数上放置断点并查看参数是什么来测试它。或者您可以弄清楚如何称呼UAPreferencesSetValue自己(我的第一个猜测是参数与 相同CFPreferencesSetValue)。

作为快速检查:它发送的通知是“UniversalAccessDomainMouseSettingsDidChangeNotification”,带有一个类似于默认分布式通知中心nil objectuserInfo字典@{@"mouseDriverCursorSize": @1.8327533, @"pid": @12345},并且在更改首选项后自己做同样的事情NSUserDefaults没有效果。此外,UAPreferencesSetValue显然需要与 不同的参数CFPreferencesSetValue,因为如果您传递明显的值,您会在 内部发生崩溃CFNotificationCenterPostNotification,因此您可能需要在 System Preferences 中对调用进行断点以查看它发送的内容。

如果你能从这个开始舒服地前进,那就太好了。如果没有,那么在您考虑尝试使这项工作之前,您需要学习很多东西。


另一种解决方法是编写脚本。如果您可以使系统偏好设置应用程序执行鼠标所做的相同操作,那么您就设置好了,对吗?

只要启用了 UI 脚本(请参阅您已经在“系统偏好设置”中查看的同一窗格中的“启用辅助设备的访问权限”复选框,或者如果您具有 root 访问权限,请参阅谷歌如何以编程方式打开和关闭它),那就是乏味,但很容易,通过系统事件。

事实上,虽然系统偏好设置没有提供足够的细节来实际更改任何内容,但它确实提供了足够的信息让我们导航到右侧窗格,从而节省了大量的 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

从 ObjC 运行它NSAppleScript——或者,如果你愿意,可以将它翻译成ScriptingBridge, Appscript,或者其他你可以在本地运行的东西——你就完成了。

于 2013-01-25T01:35:51.520 回答