☰
Lunar 5.6.0 版本深度解析:XDR 亮度增强、BlackOut 子系统重写与授权模式变更
2026/10/15 3:45:39 网站建设 项目流程
  • 桌面应用

【免费下载链接】Lunar

Intelligent adaptive brightness for your external monitors

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

Lunar 5.6.0 是该版本周期中一次以"稳定性与体验打磨"为主线的发布:它重写了负责内置屏熄屏(BlackOut)的核心子系统,增强了 XDR(HDR)亮度下的对比度与切换流畅度,并为 QuickActions 菜单栏引入了 App Info 区块、Advanced Settings 标签页和 Menu Density(菜单密度)三项 UI 能力;同时,官方取消了"购买后仅享 1 年更新"的限制,将 Lunar 5 的授权模型切换为"按大版本计费"。本文以 ReleaseNotes/5.6.0.md 为核心骨架,结合仓库源码逐项还原每个功能点在实现层面的真实逻辑,帮助读者理解这些改动背后的工程原理。

一、授权模式变更:取消 1 年更新限制,转向大版本授权

发布说明在开头单独用一节(Licensing model)阐述了商业化模型的调整,这是 5.6.0 中唯一非技术性的重大变更:

  • 取消 1 年更新限制:所有用户将享受Lunar 5 大版本范围内的无限量免费更新(unlimited free updates);
  • 按大版本计费:只有当发布新的大版本(例如 Lunar 6)时才需要购买新授权;
  • 老授权可作抵扣券:如果未来真的发布 Lunar 6,旧版购买者可以将之前的授权作为优惠券(coupon)获得可观折扣。

对开发者和用户而言,这意味着 Lunar 5 生命周期内的所有迭代(5.6.x、5.7、5.8、5.9 直至 5.x 结尾)都不再额外收费。在仓库中,授权与版本相关的 UI 入口被集中放置,例如 Lunar/SwiftUIViews/QuickActionsMenuView.swift 中的appInfo视图同时挂载了LicenseView、VersionView(updater:)与MenuDensityView,与下文第五节的 App Info 区块直接对应。

二、XDR 亮度增强:对比度、无闪烁切换与待机保持

5.6.0 对 XDR 的支持做了五项具体增强,发布说明原文如下:

  1. 增强对比度(Enhance contrast):让"深色背景 + 亮色文字"的场景在阳光直射下更可用;
  2. 更无缝的 XDR 切换:仅用键盘把亮度调到超过 100%就应该平滑地进入 XDR,且不再有大量闪烁;
  3. 屏幕从待机恢复后保持 XDR 开启状态;
  4. 修复 HDR 内容过曝:只要不启用 Gamma 就生效。发布说明明确指出这仍然是macOS 的系统级 bug,Apple 尚未修复,Lunar 只是做了绕过(work around);
  5. 在非 Apple 显示器上暂时禁用 XDR:等待"强制单色(forced-monochrome)"相关 bug 被修复后再启用。

从源码结构看,这些行为落在 Apple 私有控制通道 Lunar/Control/AppleNativeControl.swift 与调度层 Lunar/Utils/DisplayController.swift 中。以updateNits()为例,当显示器处于 XDR 增强态(display.enhanced)且存在xdrBrightness时,软件亮度值(0~1)会被线性映射到硬件的 nits 区间:

if display.enhanced, let maxHardwareNits = display.maxHardwareNits, display.xdrBrightness > 0 { nits = display.xdrBrightness.d.map(from: (0, 1), to: (maxNits, maxHardwareNits)) }

紧接着,只要系统支持 XDR 对比度(DC.supportsXDRContrast)且距离上次重置超过 3 秒,就会按当前 nits(钳制在 600~1600 之间)计算并写入对比度,xdrContrastFactor来自用户可调的缓存默认值:

if DC.supportsXDRContrast, timeSince(lastXDRContrastResetTime) > 3, display.softwareBrightness >= 1 { let xdrContrast = display.computeXDRContrast( xdrBrightness: nits.f.capped(between: 600, and: 1600), xdrContrastFactor: CachedDefaults[.xdrContrastFactor] + 0.3, minBrightness: 600, maxBrightness: 1600, gamma: 2.2) if xdrContrast != DC.xdrContrast { DC.setXDRContrast(xdrContrast) } }

这里的 600 nits 下限、1600 nits 上限与 gamma 2.2 参数,正是发布说明中"增强对比度使深色背景场景在阳光可用"这一条在实现上的落点:进入 XDR 亮度区间后,Lunar 并非只抬亮暗部,而是同步调整对比度曲线。相关的用户侧提示视图可参见 Lunar/Views/XDRTipView.swift 与 Lunar/SwiftUIViews/HDRSettingsView.swift。

需要强调的是发布说明中的事实边界:HDR 过曝问题被明确归因为 macOS bug,Lunar 的"修复"是绕过而非根治;"非 Apple 显示器禁用 XDR"也是临时策略,以"forced-monochrome bugs 被修复"为前提,因此这一行为可能随后续版本变化。

三、BlackOut 子系统重写:对齐系统设置所用的私有框架

5.6.0 对 BlackOut(内置屏熄屏,用于把 MacBook 当作外接显示底座)子系统做了一次重写,发布说明列出三点:

  • 改为使用System Preferences(系统设置)同款私有框架来操作面板背光;
  • 镜像(mirroring)行为更稳定;
  • 与 BetterDummy 的最新版本(v1.1.10-beta)做了紧密集成——BetterDummy 是配合虚拟显示使用,使内置屏熄屏后系统不降级分辨率的第三方驱动。

在实现层,BlackOut 的状态机位于 Lunar/Utils/DisplayController.swift,每个显示器持有blackOutEnabled/blackOutEnabledWithoutMirroring两个状态标志,显示插拔与镜像变化时通过blackOut(display:state:mirroringAllowed:)统一调度。例如内置屏被镜像时主动关闭熄屏、显示列表变化时校验状态一致性:

log.debug("\(d): blackOutEnabled=\(d.blackOutEnabled) blackOutEnabledWithoutMirroring=\(d.blackOutEnabledWithoutMirroring)") guard d.blackOutEnabled, !d.blackOutEnabledWithoutMirroring, let panel = Self.panel(with: d.id), !panel.isMirrored else { ... }

镜像场景下熄屏会被暂时关闭并保留自适应模式,解除镜像时再按blackOutEnabledWithoutMirroring决定是否恢复:

let preservedAdaptiveMode = d.blackOutEnabledWithoutMirroring && CachedDefaults[.overrideAdaptiveMode] ? adaptiveModeKey : nil blackOut(display: d.id, state: .off, mirroringAllowed: !d.blackOutEnabledWithoutMirroring)

这一分支逻辑正是发布说明"More stable mirroring(更稳定的镜像)"的源码印证:重写后的子系统把"镜像中是否允许熄屏"显式建模,而不是让镜像副作用直接污染熄屏状态。对外控制入口分布在 Lunar/AppDelegate.swift(blackOutPowerOff菜单动作)、Lunar/Data/Hotkeys.swift(BlackOut 系列全局快捷键,含blackOutNoMirroringHotkeyHandler、blackOutOthersHotkeyHandler)以及 SwiftUI 侧的 Lunar/SwiftUIViews/BlackoutPopoverView.swift。

四、Auto BlackOut OSD:等待系统稳定 + Esc 取消

Auto BlackOut 是连接非内置外接显示器时自动熄屏内置屏的能力。5.6.0 为它新增了 OSD(屏幕叠加)提示,并引入两个行为改进:

  • 允许系统先完成屏幕配置稳定(settle),再执行熄屏,避免在显示器协商过程中误触发;
  • 用户可按esc键取消Auto BlackOut。

取消路径在 Lunar/AppDelegate.swift 的按键处理中可以直接看到:

case kVK_Escape where DC.autoBlackoutPending: DC.cancelAutoBlackout()

autoBlackoutPending是一个带日志的原子状态位,而"等待系统稳定后重试"由 Lunar/Utils/DisplayController.swift 中的retryAutoBlackoutLater()实现——它同时校验四个前提:熄屏尚未生效、该显示器未被判定为可能已断开、当前不在校准流程中:

func retryAutoBlackoutLater() { if autoBlackoutPending, let d = builtinDisplay, !d.blackOutEnabled, DisplayController.possiblyDisconnectedDisplays[d.id] == nil, !calibrating { log.info("Retrying Auto Black Out later") d.showAutoBlackOutOSD() autoBlackoutPublisher.send(true) } }

OSD 窗口的窗口控制器按显示器的autoBlackoutOsd类型缓存,见 Lunar/Data/Display.swift:

get { Self.getWindowController(id, type: "autoBlackoutOsd") }

AppDelegate在多个系统事件回调中(如显示配置变化)调用retryAutoBlackoutLater(),这正对应发布说明中"Allow the system to settle screen configuration"的语义:不是立刻熄屏,而是监听稳定信号后再经 Publisher 驱动执行。

五、App Info 区块与 Advanced Settings 标签页

发布说明中的 App Info section 一节列出四项内容:License status(授权状态)、版本号与 Check for updates 按钮、Auto update 设置、Menu density 选择器;Advanced settings tab 一节则展示了 QuickActions 弹出面板新增的设置标签页。

App Info 区块的完整实现位于 Lunar/SwiftUIViews/QuickActionsMenuView.swift 的appInfo计算属性中,结构与发布说明逐条对应:

var appInfo: some View { VStack(alignment: .leading, spacing: 6) { HStack { Toggle("Launch at login", isOn: $startAtLogin) ... } LicenseView() // License status VersionView(updater: appDelegate!.updater) // 版本号 + Check for updates MenuDensityView() // Menu density selector } ... }

其中"Check for updates"最终调用 Sparkle 系更新器:菜单栏路径走 Lunar/AppDelegate.swift 的checkForUpdates(_:)→updater.checkForUpdates(),而 QuickActions 脚部在检测到新版本时直接以版本号按钮触发同一方法。Auto update 开关则绑定@Default(.checkForUpdate)并同样驱动updater.checkForUpdates(),实现见 Lunar/Views/ProViews.swift。

Advanced Settings 标签页的容器视图是 Lunar/SwiftUIViews/AdvancedSettingsView.swift,它挂在 QuickActions 的选项菜单中;标签枚举OptionsTab(layout / advanced / hdr)定义在 Lunar/Controllers/QuickActionsViewController.swift:

enum OptionsTab: String, Defaults.Serializable { case layout case advanced case hdr }

六、Menu Density:clean / comfortable / dense 三档菜单密度

Menu Density 允许用户调整 QuickActions 菜单栏的行距密度,三档取值定义在 Lunar/Controllers/QuickActionsViewController.swift:

enum MenuDensity: String, Codable, Defaults.Serializable { case clean case comfortable case dense }

其默认值在 Lunar/Data/DataStore.swift 中声明为.comfortable,并被纳入跨进程缓存与 Shortcut 扩展同步的键集合:

static let menuDensity = Key<MenuDensity>("menuDensity", default: .comfortable)

用户侧交互由 Lunar/Views/ProViews.swift 中的MenuDensityView提供,三个按钮分别写入.clean/.comfortable/.dense,并通过onChange触发菜单重排。菜单宽度常量(如MENU_WIDTH = 320、MENU_CLEAN_WIDTH = 300、MENU_HORIZONTAL_PADDING = 24)同样集中定义在 QuickActionsViewController.swift 中,密度档位影响的就是这些行距与内边距的组合。

七、Improvements:镜像分辨率保持、CPU 占用与 DDC 端口分配

发布说明的 Improvements 小节包含四条,逐条对应如下:

  1. 镜像前保存上一次的分辨率,并在镜像结束后重新应用。发布说明注明这是在规避一个系统 bug:关闭镜像时系统会应用非原生分辨率。
  2. 进一步降低 CPU 占用:在菜单不可见时减少 UI 结构更新——对应 QuickActions 菜单大量使用DispatchWorkItem延迟任务(如windowShowTask、headerShowHideTask、footerShowHideTask)做按需刷新与取消的架构。
  3. QuickActions 菜单触底时可滚动:菜单最大高度由EnvState.menuMaxHeight(屏幕可见高度减 50pt)约束,超出部分滚动显示。
  4. 对无法通过 EDID 匹配的显示器,按顺序(sequentially)分配剩余的 DDC 端口。

第 4 条的匹配基础在 Lunar/DDC/DDC.swift:DDC 子系统维护一张displayUUIDByEDID映射(EDID 原始 256 字节数据 → 显示器 UUID),并通过getEdid(displayID:)、extractName(from:)、extractSerialNumber(from:)等函数解析 EDID 块;当多个显示器因 EDID 重复或无法读取而无法唯一匹配时,5.6.0 引入了顺序分配剩余端口的回退策略,保证每个可寻址显示器都能获得一个确定的 DDC 端口,而不是随机命中他人端口。

八、Fixes:递归调用与内存/线程优化

Fixes 小节两条都指向资源占用:

  • 降低 CPU 占用:修复分辨率下拉框(resolutions dropdown)上的一个递归调用——该下拉框用于切换显示器分辨率,递归调用会在每次枚举时重复触发自身;
  • 降低内存占用与创建的线程数量。

这两项属于纯性能修复,发布说明未提供额外细节;从仓库结构看,分辨率与显示配置相关逻辑集中在 Lunar/Data/Display.swift 与 Lunar/Utils/DisplayController.swift,修复的具体调用点未在公开源码中单独标记,此处不做进一步推测。

九、适用前提与边界说明

  • 本文所述行为以当前仓库快照为准,对应发布说明 ReleaseNotes/5.6.0.md;仓库已演进到后续版本(6.x 系列,见 CHANGELOG.md 与 ReleaseNotes/ 目录),部分 5.6.0 时期的实现细节(如 Auto XDR 相关状态机)已在此后继续重构;
  • XDR 相关增强仅适用于支持 XDR 的内置显示器(如带 XDR 屏的 MacBook Pro),发布说明明确非 Apple 显示器在 forced-monochrome bug 修复前 XDR 被禁用;
  • HDR 过曝问题的根治依赖 Apple 修复 macOS 系统 bug,Lunar 侧仅做绕过;
  • BlackOut 与虚拟显示驱动的紧密集成以 BetterDummy v1.1.10-beta 为版本基线,更换虚拟显示驱动方案时行为可能不同;
  • 授权模式变更(大版本计费、旧授权可作折扣券)属于产品策略,本文仅转录发布说明原文,不构成承诺条款的引用。
  • 桌面应用

【免费下载链接】Lunar

Intelligent adaptive brightness for your external monitors

项目地址:https://gitcode.com/gh_mirrors/lu/Lunar
点击查看免费下载
上一篇:Jekyll 日期与时区处理:深入解析 front matter `date` 字段的解析流程与 `timezone` 配置
下一篇:并查集(Union-Find)算法全解:从连通性判断到带权模板与 LeetCode 实战

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

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

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

立即咨询