桌面应用【免费下载链接】LunarIntelligent adaptive brightness for your external monitors项目地址https://gitcode.com/gh_mirrors/lu/Lunar点击查看免费下载本文围绕 LunarmacOS 外部显示器智能亮度调节工具2.8.1 版本的三个稳定性修复展开从源码层面剖析显示器唯一标识UUID的生成与回退链路、显示器名称display name的探测优先级以及新接入的显示器数据如何覆盖本地缓存中的旧记录。读完本文你将理解 Lunar 是如何持久化并识别一台显示器的以及遇到显示器未被正确识别/重复出现/名称显示异常问题时可以从哪些代码路径入手排查。背景为什么 Lunar 需要稳定的显示器标识Lunar 的核心功能是持久保存每台显示器的亮度、对比度、Gamma 曲线、输入源等设置数据保存在Defaults键值存储中见 DataStore.swift 中的static let displays Key[Display]?(displays, default: nil)。要做到换接口、重启、拔插后依然认得出同一台显示器Lunar 必须为每台显示器计算一个稳定且唯一的标识符——Display.serial内部实为 UUID。这一标识贯穿整个系统Display初始化时以self.serial (serial ?? Display.uuid(id: id))生成持久标识见 Display.swift同步模式、传感器模式等跨显示器映射以DisplayUUID为键例如syncBrightnessMapping: [DisplayUUID: [AutoLearnMapping]]Display.swiftCLI 的display-uuid子命令可直接为指定显示器 ID 输出 UUID见 CLI.swift。2.8.1 的三条修复正是围绕这条标识—名称—缓存链路中的漏洞展开的。修复一解决另一种情况下 UUID 无法分配的问题生成 UUID 的三级回退链Display.uuid(id:)是核心入口其实现位于 Display.swift采用逐级回退策略static func uuid(id: CGDirectDisplayID) - String { #if DEBUG // 测试显示器返回固定的测试序列号 ... #endif // 第一级系统级 API优先使用 CoreGraphics 分配 UUID if let uuid CGDisplayCreateUUIDFromDisplayID(id) { let uuidValue uuid.takeRetainedValue() let uuidString CFUUIDCreateString(kCFAllocatorDefault, uuidValue) as String return uuidString } // 第二级回退到 EDID 十六进制串 if let edid Display.edid(id: id) { return edid } // 第三级退化为显示器 ID 字符串 return String(describing: id) }第一级CGDisplayCreateUUIDFromDisplayID依赖 macOS 系统框架对应头文件声明见 Lunar-Bridging-Header.h第二级Display.edid(id:)通过DDC.getEdidData(displayID:)读取显示器 EDID 数据并转为十六进制字符串Display.swift。2.8.1 之前某些环境下CGDisplayCreateUUIDFromDisplayID会返回空串CFUUIDCreateString结果为空字符串此时函数直接返回空字符串导致显示器serial为空、无法持久化。修复后的逻辑改为当 UUID 字符串为空时不再直接返回而是继续走 EDID 回退分支。CLI 侧对应的 fallback 行为命令行工具lunar display-uuid display-id提供了可选的--fallback参数与上述回退链对应struct DisplayUuid: ParsableCommand { static let configuration CommandConfiguration( abstract: Generates UUID for a display ID. ) Flag(name: .shortAndLong, help: Fall back to EDID if UUID is not possible to generate) var fallback false Argument(help: Display ID) var id: CGDirectDisplayID func run() throws { if let uuid CGDisplayCreateUUIDFromDisplayID(id) { let uuidValue uuid.takeRetainedValue() let uuidString CFUUIDCreateString(kCFAllocatorDefault, uuidValue) as String if !uuidString.isEmpty { cliPrint(uuidString) return cliExit(0) } } if fallback { if let edid Display.edid(id: id), let uuid UUID(namespace: .oid, name: edid) { cliPrint(uuid) return cliExit(0) } } throw LunarCommandError.noUUID(id) } }代码见 CLI.swift。注意 CLI 的--fallback与 GUI 路径略有不同它用 EDID 内容生成一个基于 OID 命名空间的标准 UUIDUUID(namespace: .oid, name: edid)而不是直接输出 EDID 十六进制串若系统 API 与 EDID 均不可用则抛出LunarCommandError.noUUID(CGDirectDisplayID)错误定义与描述见 CLI.swift。EDID 派生 UUID 的匹配机制当缓存中已存在由 EDID 派生的历史标识时Lunar 需要判断新接入的显示器是否与历史记录匹配。Display提供了possibleEDIDUUIDs()与matchesEDIDUUID(_:)两个方法Display.swiftpossibleEDIDUUIDs()从显示器信息字典中读取kDisplayProductID、kDisplayVendorID、制造年份/周次、物理尺寸等字段按 EDID 规范拼出三档带正则通配符的 UUID 模式从严格到宽松逐步放宽序列号位matchesEDIDUUID(_:)用待匹配串与上述模式逐一比对命中的会记录日志Matched EDID UUID pattern ... with ...便于排查。这一机制保证即使系统 UUID 丢失Lunar 仍能通过 EDID 特征认出历史显示器从而避免设置丢失或设备重复注册。相关后续2.8.2 的 EDID 内存管理修复紧接 2.8.1 的 2.8.2 版本修复了 Fix memory management of EDID data见 CHANGELOG.md。这与 2.8.1 的 UUID 修复同属一条技术线EDID 读取走takeRetainedValue()等 CoreFoundation/IOKit 内存语义若对象所有权处理不当会造成泄漏或过早释放进而影响后续的 UUID 生成与匹配。升级时可一并关注这两个版本的修复组合。修复二修复显示器名称检测display name detection名称探测的优先级顺序Display.printableName(_:)负责生成显示器默认名称实现见 Display.swift按以下优先级依次探测特殊类型优先内置显示器返回Built-in随航Sidecar返回Sidecar分别通过DDC.isBuiltinDisplay(id:checkName:)、DDC.isSidecarDisplay(id:checkName:)判断系统屏幕名通过NSScreen.forDisplayID(id)?.localizedName获取 macOS 对该显示器的本地化名称EDID 产品名读取信息字典中的DisplayProductName字段一个按语言区分的字典依次取Locale.current.identifier、en_US、首个可用项兜底以上全部失败时返回Unknown。DEBUG 模式下还会为测试用显示器 ID 映射固定的厂商名如 LG Ultra HD、DELL U3419W、Pro Display XDR 等见 Display.swift。修复内容与初始化链路在 2.8.1 之前某些显示器尤其是缺乏本地化名称或元数据不完整的型号会被探测为Unknown或DisplayProductName字典的取值逻辑在语言缺失时出错。修复后的探测链在Display.init中被调用edidName Self.printableName(id)随后self.name (传入名称非空 ? 传入名称 : edidName)Display.swift。也就是说外部传入的用户自定义名称优先级最高其次才是探测得到的名称。若用户之后想恢复系统探测名可调用resetName()name Display.printableName(id)见 Display.swift这也是重置名称功能在源码层的落点。修复三允许新显示器数据覆盖缓存中的旧记录cached db rows缓存写入逻辑Lunar 对显示器列表使用CachedDefaults作为内存缓存真正落盘则在现在写入now: true时进行。核心写入函数DataStore.storeDisplay(display:now:)位于 DataStore.swiftstatic func storeDisplay(display: Display, now: Bool false) { guard !isGeneric(display.id) else { return } guard var displays CachedDefaults[.displays] else { CachedDefaults[.displays] [display] if now { Defaults[.displays] [display] } return } if let displayIndex displays.firstIndex(where: { $0.serial display.serial }) { displays[displayIndex] display } else { displays.append(display) } CachedDefaults[.displays] displays if now { Defaults[.displays] displays } }2.8.1 修复了什么修复前的逻辑存在一个隐蔽缺陷当新接入的显示器解析出的数据比缓存中的旧记录更新例如 EDID 信息更新、分辨率/名称变化、或者系统刚重新分配了 serial旧缓存行可能因为匹配策略过严而无法被替换导致同一台物理显示器以旧数据错误名称、过期亮度范围长期驻留缓存已拔除显示器的新数据无法刷新界面显示幽灵显示器条目。修复后的行为以serial为唯一匹配键displays.firstIndex(where: { $0.serial display.serial })命中则原地覆盖displays[displayIndex] display未命中则追加新记录。这样既允许新数据覆盖旧行又保证不会为同一台显示器重复建行。同时写入采用先更新CachedDefaults再按需落盘到Defaults的两段式提交兼顾界面响应速度与持久化一致性。缓存的全局重置若出现无法通过覆盖修复的脏数据Lunar 还提供DataStore.reset()作为兜底CachedDefaults.reset(Array(settings))其中 settings 为APP_SETTINGS减去NON_RESETTABLE_SETTINGS后的集合DataStore.swift。该函数在firstRunAfterLunar4Upgrade()与firstRunAfterDefaults5Upgrade()等跨版本升级路径中也会被调用DataStore.swift用于在破坏性升级后清理旧格式的显示器缓存。如何验证本版本修复升级到 2.8.1当前仓库中该版本的变更同时记录于 ReleaseNotes/2.8.1.md 与 CHANGELOG.md后可通过以下方式验证修复效果检查显示器标识运行lunar display-uuid display-id查看返回的 UUID 是否非空若系统 API 失败加上--fallback观察是否回退到 EDID 派生 UUID检查名称在 Lunar 的设置界面查看显示器名称是否已从Unknown恢复为真实型号若仍异常使用重置名称触发Display.resetName()观察缓存覆盖拔插显示器或更换连接口后确认同一显示器条目被原地刷新而非新增重复行且亮度/对比度等历史设置仍然保留说明serial匹配成功。需要说明的是以上验证命令与代码路径均以当前仓库实际内容为准2.8.1 距今已有多个大版本迭代仓库中已包含直至 6.x 的 ReleaseNotes新版本中相关机制可能已演进升级用户应以最新版行为为准。小结Lunar 2.8.1 的三个修复分别补齐了三条关键链路的薄弱点UUID 分配Display.uuid(id:)增加空字符串判定保证CGDisplayCreateUUIDFromDisplayID不可用时无缝回退到 EDID 与显示器 ID名称检测Display.printableName(_:)的多级探测顺序让更多型号的显示器能获得真实名称而非Unknown缓存覆盖DataStore.storeDisplay(display:now:)以serial为唯一键支持原地覆盖避免新旧数据打架导致显示异常。对普通用户而言这三项修复共同提升了多显示器场景下识别—命名—持久化的稳定性对开发者而言Display.swift、DataStore.swift 与 CLI.swift 中的对应实现也为理解 macOS 显示器标识体系CGDisplayUUID / EDID / NSScreen.localizedName提供了可读的参考样例。赞分享桌面应用【免费下载链接】LunarIntelligent adaptive brightness for your external monitors项目地址https://gitcode.com/gh_mirrors/lu/Lunar点击查看免费下载相关推荐如何把微信聊天记录导出成可以永久保存的文档WeChatMsg 免费上手完整指南如何把微信聊天记录导出成可以永久保存的文档WeChatMsg 免费上手完整指南 想找的那条消息你在聊天列表里翻半天也翻不到WeChatMsg 是一款免费的桌面应用VueUiQuickChart数据集名称显示问题解析VueUiQuickChart数据集名称显示问题解析 在vue data ui项目的最新版本中开发者发现了一个关于VueUiQuickChart组件显示数据集前端数据可视化UI组件图表库上一篇Lighthouse多版本性能差异对比13个版本深度分析指南下一篇5分钟搞定边缘AI部署pytorch-image-models量化转ONNX全流程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考