Mac 上按时段调暗屏幕,以及那个会把屏幕一路调成全黑的陷阱

按时段调暗屏幕听起来是两行代码的事:看表、设亮度。陷阱在「设」这个字上。如果你要设的值是从当前已有的值推算出来的,那么每复查一次屏幕就更暗一点——到早上它就全黑了。

这个功能,从用户角度看

一天被分成几个时间段——工作时段、傍晚时段、夜间时段——每一段各自带白点与亮度。没有全局开关;改 21:00 的行为不会影响 14:00。

时间以距本地午夜的分钟数存储。这带来一个漂亮的结果:结束时间早于开始时间就自动跨越午夜,所以「夜间 20:00 到 08:00」完全不需要特例——它和别的时段是同一套算术。

陷阱一——会自己越乘越小的「倍数」

下面这种写法看起来完全合理:

let current = display.brightness
let dimmed  = current * 0.6      // 错的
apply(dimmed)

把它挂在 60 秒一次的心跳上,面板就会被反复乘以 0.6:0.6、0.36、0.22、0.13……屏幕会自己走向全黑,而用户的说法是「这个应用一夜之间把我屏幕弄坏了」。

解法是把值做成绝对量——按满量程的比例,而不是实时值的倍数:

let target = 0.6               // 满量程的比例
apply(target)

现在应用两次和应用一次的结果相同。用一个词说:这个调度器是幂等的。反复求值不等于空操作的调度器,不是幂等的——而它会漂移。

陷阱二——你拖滑杆时,心跳不跳了

第二个陷阱更安静。挂在默认 run loop 模式上的定时器,在用户拖动滑杆时不会触发——因为拖动会把 run loop 按在事件跟踪模式里。于是那个本该在 21:00 落下的复查,悄无声息地没落下。

把心跳注册在 .common 模式下,它就仍然会落下——而那恰恰是用户最容易注意到的时刻。

陷阱三——会自我重启的淡入淡出

值变成绝对量之后,紧接着的问题是:调度器对同一个时段做了两次决定会怎样?如果每次决定都重启淡入淡出,屏幕就会每分钟可见地脉冲一次。正确答案是:已经生效的时段是空操作,所以心跳不会重启一段已经在往正确目标走的过渡。用户明确要求时,走另一条「立即应用」的路径。

而且必须可逆

关掉日程时,把面板交还给开启日程之前的状态——不是交还给某个默认值。用户原本设的是 80%,就拿回 80%,而不是 100%。

两个更小、但值得点名的决定

诚实的边界

相关

全部指南 · 外接显示器亮度怎么调 · 色彩发灰 · DDC/CI 无响应