☰
Lunar 6.7.11 修复说明:Sync Mode 在 MacBook Air 与 iMac 上改用 nits 同步
2026/10/16 6:27:37 网站建设 项目流程
  • 桌面应用

【免费下载链接】Lunar

Intelligent adaptive brightness for your external monitors

项目地址:https://gitcode.com/gh_mirrors/lu/Lunar
点击查看免费下载

Lunar 6.7.11 是一个针对Sync Mode(同步模式)的定向修复版本。此次更新修复了 Apple Silicon MacBook Air 与 iMac 上,Sync Mode 未按 nits(亮度单位)进行同步的问题。本文将结合该版本 Release Notes 与仓库源码,说明问题成因、修复机制、相关配置与排查方法。

背景:什么是 Sync Mode?

Lunar 内置多种自适应亮度模式,Sync Mode 是其中之一,其核心思路是:跟随一台"源显示器"(Source Display)的亮度,同步调整其他外接显示器的亮度。例如,当你在 MacBook 内置屏幕上调高亮度时,外接显示器会随之同步变亮。

在源码中,Sync Mode 通过AdaptiveModeKey.sync标识,其单例SyncMode.shared负责同步逻辑(参见 Lunar/Modes/AdaptiveMode.swift 中的模式枚举与SyncMode.shared引用)。

Sync Mode 的源显示器选择逻辑位于 Lunar/Utils/DisplayController.swift:

let externalSource = displays.filter { !$0.isBuiltin && $0.isSource && !$0.blackout } if let source = externalSource.first(where: { $0.hasAmbientLightAdaptiveBrightness }) ?? externalSource.first(where: { $0.isNative }) ?? externalSource.first { return source } if let d = displays.first(where: \.isBuiltin), d.isSource, !d.blackout { return d }

即优先选择支持环境光自适应亮度(hasAmbientLightAdaptiveBrightness)的外接显示器,其次选择原生(native)显示器,最后可回退到内置屏幕。

问题描述:MacBook Air 与 iMac 未使用 nits 同步

本版本修复的核心 bug 是:

Fix Sync Mode not using nits on MacBook Air and iMac (Apple Silicon only)

在 Apple Silicon 的 MacBook Air 与 iMac 上,Sync Mode 此前未按 nits(尼特)进行同步。

为什么要按 nits 同步?

nits(cd/m²)是显示器亮度的绝对物理单位。macOS 中,内置屏幕的亮度调节范围通常为 0~500 nits(部分支持 XDR 的机型可达 1600 nits),而不同外接显示器的亮度范围各异。若仅按 0~100 的百分比直接映射,不同显示器的亮度曲线不一致,同步效果会失真。

Lunar 为此实现了 SDR 亮度与 nits 的双向换算函数,位于 Lunar/Data/Display.swift:

func sdrBrightnessToNits(_ brightness: Double, maxNits: Double) -> Double { guard brightness > 0 else { return 0 } if brightness <= 0.5 { return brightness * 2 * 140 } let t = (brightness - 0.5) / 0.5 return 140 * pow(maxNits / 140, t) } func nitsToSdrBrightness(_ nits: Double, maxNits: Double) -> Double { guard nits > 0 else { return 0 } if nits <= 140 { return nits / 280 } return 0.5 + 0.5 * Foundation.log(nits / 140) / Foundation.log(maxNits / 140) }

该换算基于 macOS 的 SDR 亮度曲线:0~0.5 为线性段(0~140 nits),0.5~1.0 为对数段(140 nits 至 maxNits)。这正是 Sync Mode 按 nits 同步的数学基础。

修复机制:isUsingNits() 与 nits 读取

nits 读取链路

在 Apple Silicon 上,Lunar 通过 AppleNativeControl.swift 读取并推算 nits。核心逻辑在updateNits()(第 L206-L247 行):

if display.enhanced, let maxHardwareNits = display.maxHardwareNits, display.xdrBrightness > 0 { nits = display.xdrBrightness.d.map(from: (0, 1), to: (maxNits, maxHardwareNits)) } else { var brightness: Float = 0.0 DisplayServicesGetLinearBrightness(display.id, &brightness) nits = maxNits * brightness.d }

即通过DisplayServicesGetLinearBrightness获取线性亮度,再乘以显示器的最大 nits(maxNits)得到当前 nits。若显示器处于增强(XDR)模式,则基于xdrBrightness在 SDR 上限与硬件上限之间映射。

isUsingNits() 的判定作用

SyncMode.isUsingNits()用于判定当前是否按 nits 同步。代码库中该判定用于以下关键位置:

  • Lunar/SwiftUIViews/DisplayRowView.swift:nitsText()在SyncMode.isUsingNits()成立时,显示显示器当前 nits 数值(display.nits)。
  • Lunar/SwiftUIViews/NitsTextField.swift:当syncMode && SyncMode.isUsingNits()时,显示可编辑的 min/max nits 输入框。
  • Lunar/Data/Display.swift:在isUserAdjusting()中,当处于 Sync Mode 且isUsingNits()成立时,通过nitsBrightnessMappingSaver/nitsContrastMappingSaver判断用户是否正在调整。

本次修复正是让 MacBook Air 与 iMac 上的 Sync Mode 正确进入 nits 同步路径,从而在 UI 中显示 nits、并基于 nits 做插值映射。

相关配置:syncNits 与 min/max nits

syncNits 开关

syncNits是 Sync Mode 是否按 nits 同步的默认开关,定义于 Lunar/Data/DataStore.swift:

.syncNits,

其缓存键与发布者分别位于 DataStore.swift 第 802 行 与 第 935 行:

cacheKey(.syncNits) let syncNitsPublisher = pub(.syncNits)

UI 层通过@Default(.syncNits)读取该设置(见 Lunar/SwiftUIViews/UsefulInfo.swift)。

minNits / maxNits 的约束

nits 同步依赖显示器的 min/max nits 数值,其约束逻辑位于 Lunar/Data/Display.swift:

  • minNits:必须满足minNits < maxNits && minNits >= 0,否则被钳制为 0 或maxNits - 1。
  • maxNits:必须满足maxNits > minNits && maxNits > 0 && maxNits <= 3000,超过 3000 会被钳制回旧值。

用户可通过 NitsTextField.swift 中的输入框自定义 min/max nits,修改后通过display.minNits = nits/display.maxNits = nits持久化,并触发nitsEditPublisher通知重算映射。

源码中的插值映射

Sync Mode 按 nits 同步时,会将源显示器的 nits 映射到目标显示器的亮度百分比。相关映射定义于 Lunar/Data/Display.swift:

func recomputeNitsMapping() { nitsToPercentageMapping = Self.LUX_TO_NITS.sorted(by: { $0.key < $1.key }).map { k, v in AutoLearnMapping(source: k, target: nitsToPercentage(v, minNits: minNits, maxNits: userMaxNits ?? maxNits) * 100) } }

其中nitsToPercentage结合用户自定义的userMinNits/userMaxNits(若未设置则回退到minNits/maxNits),将 nits 转换为 0~100 的亮度百分比。

相关限制与边界条件

仅 Apple Silicon

本次修复标注为 "Apple Silicon only"。从源码看,nits 相关的读取与显示逻辑大量使用#if arch(arm64)条件编译:

  • AppleNativeControl.swift 的updateNits()整体包裹于#if arch(arm64)。
  • DisplayRowView.swift 的nitsText()同样仅在 arm64 下启用。
  • Display.swift 中isUsingNits()分支也带#if arch(arm64)。

因此,Intel Mac 上 Sync Mode 仍按百分比(0~100)同步,nits 同步路径仅在 Apple Silicon 生效。

禁用亮度观察器时的回退

若disableBrightnessObservers为真(设置见 DataStore.swift),Lunar 无法通过系统回调监听亮度变化,此时 Display.swift 的startBrightnessContrastRefreshers()会改用 2 秒间隔的Repeater轮询读取亮度,Sync Mode 的同步仍可进行,但响应存在延迟。

合盖(Clamshell)模式的处理

在 Clamshell 模式下,Sync Mode 会自动切回 Manual Mode,相关逻辑位于 DisplayController.swift:

func disableAdaptiveInClamshellMode() { if adaptiveModeKey == .sync { disabledAdaptiveInClamshellMode = true disable() } }

即检测到合盖时禁用 Sync Mode,开盖后重新启用。

如何验证与排查

  1. 确认机型与模式:仅 Apple Silicon Mac(MacBook Air / iMac)在 Sync Mode 下生效。可查看 Lunar/Data/Display.swift 的isActiveSyncSource判断当前显示器是否为活动源。
  2. 查看 nits 显示:在 Sync Mode 下,若修复生效,显示行中会显示当前 nits 数值(见 DisplayRowView.swift)。
  3. 调整 min/max nits:通过 NitsTextField.swift 的弹窗自定义显示器的 min/max nits 值,优化映射精度。
  4. 检查设置:确认syncNits(DataStore.swift)为开启状态,且未误开disableBrightnessObservers(DataStore.swift)。

小结

Lunar 6.7.11 通过让 Sync Mode 在 MacBook Air 与 iMac(Apple Silicon)上正确走 nits 同步路径,统一了内置屏与外接屏的亮度感知。nits 同步以 sdrBrightnessToNits 的 SDR 亮度曲线换算为基础,配合syncNits开关、minNits/maxNits配置与 AutoLearnMapping 插值映射,实现了更贴近人眼感知的跨屏亮度同步。

  • 桌面应用

【免费下载链接】Lunar

Intelligent adaptive brightness for your external monitors

项目地址:https://gitcode.com/gh_mirrors/lu/Lunar
点击查看免费下载

相关推荐

上一篇:GhostESP设备端OTA全解:固件校验、回滚保护与SD卡更新背后的4重防护
下一篇:DGX Spark集群踩坑一次解决:spark-vllm-docker故障排查与常见问题完整清单(OOM、卡死、加载慢)

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询