Lunar 5.9.5 Alpha 1 解析:让 Shortcuts 显示器布局在主屏非内建屏时正常工作,并新增“Set Screen as Main“快捷指令
发布时间:2026/10/16 6:49:16 锦皓数字建站

桌面应用【免费下载链接】LunarIntelligent adaptive brightness for your external monitors项目地址https://gitcode.com/gh_mirrors/lu/Lunar点击查看免费下载导读本文基于 Lunar 开源仓库的发布说明 ReleaseNotes/5.9.5a1.md聚焦 5.9.5 Alpha 1 引入的两项关键改进一是修复了当主显示器不是 Mac 内建屏built-in display时Shortcuts 中的显示器布局arrangement/layout快捷指令失效的问题二是新增了Set screen as main设为屏幕为主屏快捷指令。读完本文你将理解 Lunar 如何通过 macOS App Intents 与 CoreGraphics 显示配置 API 实现多屏布局自动化以及如何在合盖外接屏场景下正确使用这些快捷指令。背景为什么布局快捷指令此前会失效在 macOS 的 Shortcuts 生态中Lunar 通过 LunarShortcuts/LunarShortcuts.swift 提供了一整套基于 AppIntents 的显示控制动作。其中Arrangement布局类别的动作负责把多块屏幕摆放成指定几何关系例如两块屏幕垂直/水平排列、三块屏幕上二下一等。在这些布局动作的实现里一个隐含前提是主显示器main display的坐标原点位于 (0, 0)。此前 Lunar 的布局算法默认以内建屏即主屏为前提来计算其他屏幕的偏移量但当用户外接显示器、合上 MacBook 盖子clamshell mode后主屏变成外接屏原有坐标假设失效布局动作就会把屏幕摆放到错误的位置甚至无法生效。5.9.5 Alpha 1 的这条改进正是针对该场景的修复。新增快捷指令Set Screen as Main动作定义与使用方式在 5.9.5 Alpha 1 中新增的Set Screen as Main快捷指令源码实现在 LunarShortcuts/LunarShortcuts.swift 的MakeMonitorMainIntent约 L2997-L3040动作标题Set Screen as Main所属分类Arrangement参数Screen目标屏幕使用ScreenQuery(single: true, noDefault: true)提供候选即要求用户明确选择一块屏幕不提供默认值Skip action if screen is missing当所选屏幕不存在时跳过该动作避免快捷指令报错中断。使用场景非常直接在多屏环境中通过该动作把任意一块外接屏设为主屏Main Display替代系统显示器设置里的拖拽操作从而让菜单栏、桌面图标与焦点窗口出现在目标屏幕上。源码级实现原理MakeMonitorMainIntent.perform()的核心逻辑如下guard let mainDisplay DC.mainDisplay, let display screen.dynamicDisplay, mainDisplay.id ! display.id else { return .result() } Display.configure { config in if let mainDisplay DC.mainDisplay, let mainDisplayBounds mainDisplay.nsScreen?.bounds, mainDisplayBounds.origin .zero, let displayBounds display.nsScreen?.bounds { CGConfigureDisplayOrigin(config, mainDisplay.id, -mainDisplayBounds.width.intround.i32, -displayBounds.origin.y.intround.i32) } return CGConfigureDisplayOrigin(config, display.id.u32, 0, 0) .success }关键点解读幂等保护若目标屏幕已经是DC.mainDisplay直接返回成功不会做无谓的重新配置。Display.configure事务化提交这是 Lunar 封装好的 CoreGraphics 配置入口定义在 Lunar/Data/Display.swift约 L4483-L4505先CGBeginDisplayConfiguration开启配置会话执行闭包内的CGConfigureDisplayOrigin布局修改闭包返回true则CGCompleteDisplayConfiguration(..., .permanently)永久生效否则CGCancelDisplayConfiguration回滚。这种方式保证主屏切换 旧主屏移位是一整套原子操作避免出现半个屏幕布局变更的状态。坐标搬移技巧当新主屏已位于原点时先把旧主屏内建屏从 (0,0) 平移到左侧x 取负再把目标屏坐标原点设为 (0,0)。由于 macOS 约定原点所在屏幕即主屏这一步实质上是完成了主屏的切换。主屏判定来源DC.mainDisplay定义在 Lunar/Utils/DisplayController.swift约 L984-L986var mainDisplay: Display? { activeDisplayList.first(where: { CGDisplayIsMain($0.id) 1 }) }即通过系统 APICGDisplayIsMain实时判定当前主屏与用户看到的显示器排列窗口中菜单栏所在屏一致。布局快捷指令完整阵容与修复要点可用的布局动作根据源码5.9.5 Alpha 1 时代的布局Arrangement动作包括快捷指令动作源码类型适用场景Set Screen as MainMakeMonitorMainIntent任意屏幕设为主屏Arrange 2 Screens VerticallyVerticalMonitorLayoutIntent两块屏上下排列MacBook 1 块外接屏Arrange 2 Screens HorizontallyHorizontalMonitorLayoutIntent两块屏左右排列2 块外接屏Arrange 3 Screens in a 2-above-1 configurationTwoAboveOneMonitorLayoutIntent三块屏上二下一Arrange 4 Screens in a 3-above-1 configurationThreeAboveOneMonitorLayoutIntent四块屏上三下一Arrange 3 Screens HorizontallyHorizontalMonitorThreeLayoutIntent三块屏一字横排Fix monitor arrangementFixMonitorArrangementIntent修复 3 块以上外接屏被系统打乱顺序的问题修复思路不再假设内建屏是主屏本次修复的关键改动是让所有布局动作在计算屏幕坐标时动态检测哪块屏是主屏而不是默认内建屏即主屏。以两块屏垂直排列为例VerticalMonitorLayoutIntent.perform()let topScreenBounds CGDisplayBounds(display1.id) let bottomScreenBounds CGDisplayBounds(display2.id) if CGDisplayIsMain(display2.id) ! 0 { // 底部屏是主屏顶部屏摆放到底部屏正上方水平居中 let topScreenX bottomScreenBounds.midX - topScreenBounds.width / 2 let topScreenY bottomScreenBounds.minY - topScreenBounds.height CGConfigureDisplayOrigin(config, display1.id, Int32(topScreenX.rounded()), Int32(topScreenY.rounded())) } else { // 顶部屏是主屏底部屏摆放到顶部屏正下方水平居中 let bottomScreenX topScreenBounds.midX - bottomScreenBounds.width / 2 let bottomScreenY topScreenBounds.maxY CGConfigureDisplayOrigin(config, display2.id, Int32(bottomScreenX.rounded()), Int32(bottomScreenY.rounded())) }可以看到算法先通过CGDisplayIsMain判断哪块屏占据主屏位然后固定主屏不动、移动另一块屏从而在主屏是外接屏的合盖场景下也能得到正确的物理位置。2 屏横排、2-above-1、3-above-1、3 屏横排等动作在 LunarShortcuts/LunarShortcuts.swift约 L3095-L3380中均采用同样的分支策略分别针对主屏在左、在右、在下等不同情况计算偏移。屏幕筛选参数布局动作的参数统一使用ScreenQuery(single: true, noDefault: true)且都要求所选屏幕互不相同例如垂直布局中display1 ! display23-above-1 布局中校验四个屏幕 ID 的去重集合大小为 4否则抛出Two/Three/Four different screens are needed to perform arrangement.的错误提示。ScreenQuery本身是完整的EntityPropertyQuery实现约 L204-L508支持按名称、序列号、是否外接、是否内建、亮度、对比度等属性做谓词过滤与排序并内置DisplayFilter动态筛选如main、cursor、builtin、sidecar等定义见 Lunar/Data/CLI.swift 的DisplayFilter枚举约 L111-L120。这意味着用户既可以在快捷指令里选具体某块屏也可以选择当前主屏光标所在屏这类动态目标。配套能力Fix monitor arrangement与本次改进同属 Arrangement 分类的还有Fix monitor arrangement动作FixMonitorArrangementIntent约 L3383-L3458它解决的是3 块及以上外接屏的物理顺序被系统反复打乱的问题。工作流程为在每块外接屏上通过display.showArrangementOSD(id:number:)显示一个编号OSD用户按从左到右的顺序在键盘上依次输入各屏显示的编号Lunar 依据输入顺序重新排列屏幕按Esc取消按Enter部分应用已选顺序。它有两个参数Only for monitors with same UUID默认开启在 Apple Silicon 上通过DDC.DCP(displayID:)读取各屏 EDID UUID仅对出现重复 UUID 的屏幕进行重排避免误动正常布局Wait for operation to finish默认关闭开启后动作会等待重排完成后才返回方便串联后续动作。该动作同样依赖本次改进后的主屏无关坐标计算可在外接屏为主屏的环境下使用。小结Lunar 5.9.5 Alpha 1 的两个改动相互配合构成了完整的多屏布局自动化能力Set screen as Main让你可以用快捷指令显式指定主屏解决合盖/外接场景下的主屏归属问题全部Arrangement 布局动作改为动态检测主屏、以主屏为锚点计算偏移使主屏不是内建屏时布局依然精确生效。如果想进一步研究实现细节建议阅读以下源码快捷指令实体与布局动作LunarShortcuts/LunarShortcuts.swift显示配置事务封装Display.configureLunar/Data/Display.swift主屏/外接屏判定逻辑mainDisplay与externalActiveDisplaysLunar/Utils/DisplayController.swift动态屏幕筛选过滤器DisplayFilterLunar/Data/CLI.swift对应正式版发布说明ReleaseNotes/5.9.5.md赞分享桌面应用【免费下载链接】LunarIntelligent adaptive brightness for your external monitors项目地址https://gitcode.com/gh_mirrors/lu/Lunar点击查看免费下载相关推荐Lunar 5.6.7a1 技术解析亮度键新增「主显示器 / 非主显示器」目标与非 DDC 亮度锁定修复Lunar 5.6.7a1 技术解析亮度键新增「主显示器 / 非主显示器」目标与非 DDC 亮度锁定修复 本篇基于仓库中 ReleaseNotes/5.6.7桌面应用Lunar 6.4.4 高级 Gamma 快捷指令用 Shortcuts 为任意显示器打造自定义色彩配置Lunar 6.4.4 高级 Gamma 快捷指令用 Shortcuts 为任意显示器打造自定义色彩配置 本文围绕 Lunar 6.4.4 引入的 Advan桌面应用Qtile 屏幕配置完全指南Screen、Bar、Widget 与多显示器布局实战Qtile 屏幕配置完全指南Screen、Bar、Widget 与多显示器布局实战 导读 本文围绕 Qtile 配置体系中与物理显示器直接相关的 screen桌面应用操作系统上一篇InterviewGuide 力扣刷题笔记434. 字符串中的单词数 —— C 双解法与 stringstream 流式分割剖析下一篇Untrunc终极指南三步修复损坏MP4视频的免费开源神器创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。