【问题标题】:How to programatically change the cursor size on a Mac如何以编程方式更改 Mac 上的光标大小
【发布时间】:2013-01-24 21:12:04
【问题描述】:

我希望能够在我的程序中为 Mac 上的光标大小设置系统首选项(如辅助功能首选项中所示),然后在程序退出后将其重新设置。

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

【问题讨论】:

标签: objective-c macos mouse-cursor


【解决方案1】:

首先,如果您只是想在光标指向您的窗口/视图/小部件时获得更大的光标,那么您的说法是错误的。阅读Introduction to Cursor Manager 了解正确方法。

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

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

但如果这真的是你想做的……


设置系统偏好非常容易。系统偏好设置修改的大部分值都在默认存储中。辅助功能窗格中的大多数值都在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 每次发布新的次要操作系统后进行测试,并调试不起作用的黑魔法为 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 消息。幸运的是,你可以从任何看起来可能的事情中倒退。通过这样做,我发现它似乎在 UniversalAccessCore 中调用UACursorSetScale,它在/System/Library/PrivateFrameworks/UniversalAccess.framework/Versions/A/Libraries/libUAPreferences.dylib 中调用UAPreferencesSetValue,该函数似乎执行CFPreferencesSetValue 并发送CFNotificationCenterPostNotification。也许只是那个通知很重要?您可以通过在 Xcode/gdb/lldb 中的相关函数上放置断点并查看参数是什么来测试它。或者您可以自己弄清楚如何调用UAPreferencesSetValue(我的第一个猜测是参数与CFPreferencesSetValue 相同)。

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

如果您愿意从这个开始继续前进,那就太好了。如果没有,那么在考虑尝试完成这项工作之前,您需要学习很多东西。


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

只要启用了 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

使用 NSAppleScript 从 ObjC 运行它——或者,如果你愿意,可以将其转换为 ScriptingBridgeAppscript 或其他你可以在本机运行的东西——然后你就完成了。

【讨论】:

  • 太棒了!感谢您为此付出的所有努力。我正在为数字标牌构建一个应用程序,该应用程序要求用户从外部源设置光标大小。这非常有帮助。
  • 好的,我已经从命令行和 Obj-C 设置了首选项,当我读回它时,它确实发生了变化,但是 CGSShowCursor 和 CGSGetGlobalCursorData 都没有导致它更新。 applescript 在 ScriptEditor 中工作,但不在我的应用程序中,并且不返回任何错误。一个简单的脚本工作正常。任何指针如何跟踪系统首选项正在调用的内容。我不知道该怎么做。
  • @SimonClark:我在答案中添加了一些初步的跟踪信息。但是,如果您不知道自己在做什么,这可能不是您想要继续的方式,除非您的目标是学习如何跟踪 Mac 应用程序(在这种情况下,您应该搜索一些指南,而不是问“什么我接下来要做什么?”在这里……)。我认为你最好弄清楚为什么脚本不能在你的应用程序中运行。你在使用沙盒吗?你能构建一个只运行该脚本的简单应用程序吗?
  • 在 Catalina 上,我也无法让该脚本正常工作。找不到“光标大小:”滑块失败。我尝试了几种滑块名称的变体,但均未成功。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-06
  • 2015-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多