Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions MonitorControl.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
AAD7DD342CAFF3D90062822F /* Settings in Frameworks */ = {isa = PBXBuildFile; productRef = AAD7DD332CAFF3D90062822F /* Settings */; };
AADB625A26BC196900DFFAA5 /* DisplayServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AADB625926BC196900DFFAA5 /* DisplayServices.framework */; };
CB0D15010000000000000001 /* KeyboardShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0D15010000000000000002 /* KeyboardShortcuts.swift */; };
CB0D15010000000000000011 /* BrightnessSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0D15010000000000000012 /* BrightnessSync.swift */; };
F01B0699228221B7008E64DB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F01B0680228221B6008E64DB /* Localizable.strings */; };
F01B069F228221B7008E64DB /* SliderHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F01B068F228221B7008E64DB /* SliderHandler.swift */; };
F03A8DF21FFBAA6F0034DC27 /* OtherDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03A8DF11FFBAA6F0034DC27 /* OtherDisplay.swift */; };
Expand Down Expand Up @@ -167,6 +168,7 @@
B7FA437F2AC5857A00A94C01 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InternetAccessPolicy.strings; sourceTree = "<group>"; };
B7FA43802AC5857A00A94C01 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
CB0D15010000000000000002 /* KeyboardShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardShortcuts.swift; sourceTree = "<group>"; };
CB0D15010000000000000012 /* BrightnessSync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrightnessSync.swift; sourceTree = "<group>"; };
F01B0682228221B6008E64DB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
F01B0685228221B6008E64DB /* Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = "<group>"; };
F01B0686228221B6008E64DB /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -312,6 +314,7 @@
AA99521826FE49A300612E07 /* MenuHandler.swift */,
F01B068F228221B7008E64DB /* SliderHandler.swift */,
6C85EFD922C941B000227EA1 /* DisplayManager.swift */,
CB0D15010000000000000012 /* BrightnessSync.swift */,
AA25F6D626E68C160087F3A2 /* MediaKeyTapManager.swift */,
CB0D15010000000000000002 /* KeyboardShortcuts.swift */,
AA44E70627038F7F00E06865 /* KeyboardShortcutsManager.swift */,
Expand Down Expand Up @@ -630,6 +633,7 @@
FE4E0896249D584C003A50BB /* OSDUtils.swift in Sources */,
6CBFE27A23DB266000D1BC41 /* Display.swift in Sources */,
CB0D15010000000000000001 /* KeyboardShortcuts.swift in Sources */,
CB0D15010000000000000011 /* BrightnessSync.swift in Sources */,
AA44E70727038F7F00E06865 /* KeyboardShortcutsManager.swift in Sources */,
F03A8DF21FFBAA6F0034DC27 /* OtherDisplay.swift in Sources */,
AA78BDBD2709FE7B00CA8DF7 /* UpdaterDelegate.swift in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions MonitorControl/Model/Display.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class Display: Equatable {

var sliderHandler: [Command: SliderHandler] = [:]
var brightnessSyncSourceValue: Float = 1
// Where brightness sync would have put this display if it had no ends - see BrightnessSync.
var brightnessSyncAnchor: Float?
var isVirtual: Bool = false
var isDummy: Bool = false

Expand Down
11 changes: 8 additions & 3 deletions MonitorControl/Support/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -264,11 +264,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
refreshedSomething = true
if prefs.bool(forKey: PrefKey.enableBrightnessSync.rawValue) {
for targetDisplay in DisplayManager.shared.displays where targetDisplay != display {
let current = targetDisplay.getBrightness()
let step = BrightnessSync.step(current: current, anchor: targetDisplay.brightnessSyncAnchor, delta: delta)
targetDisplay.brightnessSyncAnchor = step.anchor
guard step.value != current else {
continue // The display is at the end of its range, there is nothing to push yet.
}
os_log("Updating delta from display %{public}@ to display %{public}@", type: .info, String(display.identifier), String(targetDisplay.identifier))
let newValue = max(0, min(1, targetDisplay.getBrightness() + delta))
_ = targetDisplay.setBrightness(newValue)
_ = targetDisplay.setBrightness(step.value)
if let slider = targetDisplay.sliderHandler[.brightness] {
slider.setValue(newValue, displayID: targetDisplay.identifier)
slider.setValue(step.value, displayID: targetDisplay.identifier)
}
}
}
Expand Down
52 changes: 52 additions & 0 deletions MonitorControl/Support/BrightnessSync.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright © MonitorControl. @JoniVR, @theOneyouseek, @waydabber and others

import Foundation

/// Keeps the other displays in step with a display that changed on its own (ambient
/// light, Touch Bar, System Settings or another app) while keeping the difference
/// the user set up between them.
///
/// Adding the reported delta straight to a display and clamping the result to 0...1
/// throws away whatever part of the change did not fit. A display that already sits
/// near the top of its range therefore loses ground on every ambient light swing up
/// but gets the whole change back on the way down, so the displays creep together
/// until every slider is aligned.
///
/// The anchor below is where the display would be if it had no ends, and it is what
/// the change is applied to. The part that did not fit is given back on the way down
/// instead of being lost, which is what keeps the offset intact.
enum BrightnessSync {
/// A brightness difference smaller than this is neither settable by the user nor
/// visible, so the display still counts as being where the last sync left it.
static let tolerance: Float = 0.01

/// How far outside of 0...1 an anchor is allowed to travel. The offset between two
/// displays can never be wider than the whole range, so this only keeps the anchor
/// in bounds if several displays report changes of their own at the same time.
static let headroom: Float = 1

struct Step {
/// Where the display should be driven to now.
let value: Float
/// The anchor to hand back the next time this display is synced.
let anchor: Float
}

/// `current` is where the display is now, `anchor` is what the previous step
/// returned for it (nil if it has not been synced yet) and `delta` is the change
/// the display that moved on its own reported.
static func step(current: Float, anchor: Float?, delta: Float) -> Step {
var anchor = anchor ?? current
if abs(self.clamp(anchor) - current) > self.tolerance {
// The display is not where the last sync left it, so the user moved this one
// on purpose: that is the relationship to keep from here on.
anchor = current
}
anchor = min(max(anchor + delta, -self.headroom), 1 + self.headroom)
return Step(value: self.clamp(anchor), anchor: anchor)
}

static func clamp(_ value: Float) -> Float {
min(max(value, 0), 1)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ class MainPrefsViewController: NSViewController, SettingsPane {
}

@IBAction func enableBrightnessSync(_ sender: NSButton) {
// While sync was off the user was free to move the displays apart, so drop the
// anchors and let the next change pick the offsets up from where they are now.
for display in DisplayManager.shared.displays {
display.brightnessSyncAnchor = nil
}
switch sender.state {
case .on:
prefs.set(true, forKey: PrefKey.enableBrightnessSync.rawValue)
Expand Down