Skip to content

Commit

Permalink
Fix non-standard volume/brightness scales not working properly (#245)
Browse files Browse the repository at this point in the history
  • Loading branch information
victorchabbert committed Sep 20, 2020
1 parent 9ff4a93 commit 5cacd25
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 30 deletions.
4 changes: 4 additions & 0 deletions MonitorControl.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
F0445D40200259C10025AE82 /* DisplayPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0445D3F200259C10025AE82 /* DisplayPrefsViewController.swift */; };
F06792EA200A73460066C438 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = F06792E9200A73460066C438 /* main.swift */; };
F06792F6200A745F0066C438 /* MonitorControlHelper.app in [Login] Copy Helper to start at Login */ = {isa = PBXBuildFile; fileRef = F06792E7200A73460066C438 /* MonitorControlHelper.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
FE4E0896249D584C003A50BB /* OSDUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE4E0895249D584C003A50BB /* OSDUtils.swift */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
Expand Down Expand Up @@ -163,6 +164,7 @@
F06792F0200A73470066C438 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
F06792F1200A73470066C438 /* MonitorControlHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MonitorControlHelper.entitlements; sourceTree = "<group>"; };
F0A987D61F77B290009B603D /* OSD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = OSD.framework; sourceTree = "<group>"; };
FE4E0895249D584C003A50BB /* OSDUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSDUtils.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -291,6 +293,7 @@
F01B0683228221B6008E64DB /* Utils.swift */,
6C2EA1CC228F644B00060E3F /* OnlyIntegerValueFormatter.swift */,
6C2EA1CE228F7DFB00060E3F /* PollingMode.swift */,
FE4E0895249D584C003A50BB /* OSDUtils.swift */,
);
path = Support;
sourceTree = "<group>";
Expand Down Expand Up @@ -531,6 +534,7 @@
F03FE4C0228DF62B001F59A4 /* FriendlyNameCellView.swift in Sources */,
6C2EA1CF228F7DFB00060E3F /* PollingMode.swift in Sources */,
6CBFE27C23DB27A200D1BC41 /* InternalDisplay.swift in Sources */,
FE4E0896249D584C003A50BB /* OSDUtils.swift in Sources */,
6CBFE27A23DB266000D1BC41 /* Display.swift in Sources */,
F03A8DF21FFBAA6F0034DC27 /* ExternalDisplay.swift in Sources */,
F0445D40200259C10025AE82 /* DisplayPrefsViewController.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion MonitorControl/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>647</string>
<string>719</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
Expand Down
30 changes: 22 additions & 8 deletions MonitorControl/Model/Display.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import DDC
import Foundation
import os.log

class Display {
internal let identifier: CGDirectDisplayID
Expand Down Expand Up @@ -42,7 +43,7 @@ class Display {
return self.prefs.string(forKey: "friendlyName-\(self.identifier)") ?? self.name
}

func showOsd(command: DDC.Command, value: Int, maxValue: Int = 100) {
func showOsd(command: DDC.Command, value: Int, maxValue: Int = 100, roundChiclet: Bool = false) {
guard let manager = OSDManager.sharedManager() as? OSDManager else {
return
}
Expand All @@ -59,12 +60,25 @@ class Display {
osdImage = 1
}

manager.showImage(osdImage,
onDisplayID: self.identifier,
priority: 0x1F4,
msecUntilFade: 1000,
filledChiclets: UInt32(value),
totalChiclets: UInt32(maxValue),
locked: false)
if roundChiclet {
let osdChiclet = OSDUtils.chiclet(fromValue: Float(value), maxValue: Float(maxValue))
let filledChiclets = round(osdChiclet)

manager.showImage(osdImage,
onDisplayID: self.identifier,
priority: 0x1F4,
msecUntilFade: 1000,
filledChiclets: UInt32(filledChiclets),
totalChiclets: UInt32(16),
locked: false)
} else {
manager.showImage(osdImage,
onDisplayID: self.identifier,
priority: 0x1F4,
msecUntilFade: 1000,
filledChiclets: UInt32(value),
totalChiclets: UInt32(maxValue),
locked: false)
}
}
}
45 changes: 25 additions & 20 deletions MonitorControl/Model/ExternalDisplay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class ExternalDisplay: Display {
}

private var audioPlayer: AVAudioPlayer?
private let osdChicletBoxes: Float = 16

override init(_ identifier: CGDirectDisplayID, name: String, vendorNumber: UInt32?, modelNumber: UInt32?) {
super.init(identifier, name: name, vendorNumber: vendorNumber, modelNumber: modelNumber)
Expand Down Expand Up @@ -90,7 +89,7 @@ class ExternalDisplay: Display {

if !fromVolumeSlider {
self.hideDisplayOsd()
self.showOsd(command: volumeOSDValue > 0 ? .audioSpeakerVolume : .audioMuteScreenBlank, value: volumeOSDValue)
self.showOsd(command: volumeOSDValue > 0 ? .audioSpeakerVolume : .audioMuteScreenBlank, value: volumeOSDValue, roundChiclet: true)

if volumeOSDValue > 0 {
self.playVolumeChangedSound()
Expand All @@ -106,7 +105,6 @@ class ExternalDisplay: Display {
var muteValue: Int?
let volumeOSDValue = self.calcNewValue(for: .audioSpeakerVolume, isUp: isUp, isSmallIncrement: isSmallIncrement)
let volumeDDCValue = UInt16(volumeOSDValue)

if self.isMuted(), volumeOSDValue > 0 {
muteValue = 2
} else if !self.isMuted(), volumeOSDValue == 0 {
Expand All @@ -132,7 +130,7 @@ class ExternalDisplay: Display {
}

self.hideDisplayOsd()
self.showOsd(command: .audioSpeakerVolume, value: volumeOSDValue)
self.showOsd(command: .audioSpeakerVolume, value: volumeOSDValue, roundChiclet: !isSmallIncrement)

if !isAlreadySet {
self.saveValue(volumeOSDValue, for: .audioSpeakerVolume)
Expand Down Expand Up @@ -163,7 +161,7 @@ class ExternalDisplay: Display {
}
}

self.showOsd(command: .brightness, value: osdValue)
self.showOsd(command: .brightness, value: osdValue, roundChiclet: !isSmallIncrement)

if !isAlreadySet {
if let slider = self.brightnessSliderHandler?.slider {
Expand Down Expand Up @@ -221,25 +219,32 @@ class ExternalDisplay: Display {
func calcNewValue(for command: DDC.Command, isUp: Bool, isSmallIncrement: Bool) -> Int {
let currentValue = self.getValue(for: command)
let nextValue: Int
let maxValue = Float(self.getMaxValue(for: command))

if isSmallIncrement {
nextValue = currentValue + (isUp ? 1 : -1)
} else {
let filledChicletBoxes = self.osdChicletBoxes * (Float(currentValue) / Float(self.getMaxValue(for: command)))

var nextFilledChicletBoxes: Float
var filledChicletBoxesRel: Float = isUp ? 1 : -1

// This is a workaround to ensure that if the user has set the value using a small step (that is, the current chiclet box isn't completely filled,
// the next regular up or down step will only fill or empty that chiclet, and not the next one as well - it only really works because the max value is 100
if (isUp && ceil(filledChicletBoxes) - filledChicletBoxes > 0.15) || (!isUp && filledChicletBoxes - floor(filledChicletBoxes) > 0.15) {
filledChicletBoxesRel = 0
let osdChicletFromValue = OSDUtils.chiclet(fromValue: Float(currentValue), maxValue: maxValue)

let distance = OSDUtils.getDistance(fromNearestChiclet: osdChicletFromValue)
// get the next rounded chiclet
var nextFilledChiclet = isUp ? ceil(osdChicletFromValue) : floor(osdChicletFromValue)

// Depending on the direction, if the chiclet is above or below a certain threshold, we go to the next whole chiclet
let distanceThreshold = Float(0.25) // 25% of the distance between the edges of an osd box
if distance == 0 {
nextFilledChiclet += isUp ? 1 : -1
} else if !isUp, distance < distanceThreshold {
nextFilledChiclet -= 1
} else if isUp, distance > (1 - distanceThreshold) {
nextFilledChiclet += 1
}

nextFilledChicletBoxes = isUp ? ceil(filledChicletBoxes + filledChicletBoxesRel) : floor(filledChicletBoxes + filledChicletBoxesRel)
nextValue = Int(Float(self.getMaxValue(for: command)) * (nextFilledChicletBoxes / self.osdChicletBoxes))
nextValue = Int(round(OSDUtils.value(fromChiclet: nextFilledChiclet, maxValue: maxValue)))

os_log("next: .value %{public}@/%{public}@, .osd %{public}@/%{public}@", type: .debug, String(nextValue), String(maxValue), String(nextFilledChiclet), String(OSDUtils.chicletCount))
}
return max(0, min(self.getMaxValue(for: command), Int(nextValue)))
return max(0, min(self.getMaxValue(for: command), nextValue))
}

func getValue(for command: DDC.Command) -> Int {
Expand Down Expand Up @@ -308,11 +313,11 @@ class ExternalDisplay: Display {
}

private func stepSize(for command: DDC.Command, isSmallIncrement: Bool) -> Int {
return isSmallIncrement ? 1 : Int(floor(Float(self.getMaxValue(for: command)) / self.osdChicletBoxes))
return isSmallIncrement ? 1 : Int(floor(Float(self.getMaxValue(for: command)) / OSDUtils.chicletCount))
}

override func showOsd(command: DDC.Command, value: Int, maxValue _: Int = 100) {
super.showOsd(command: command, value: value, maxValue: self.getMaxValue(for: command))
override func showOsd(command: DDC.Command, value: Int, maxValue _: Int = 100, roundChiclet: Bool = false) {
super.showOsd(command: command, value: value, maxValue: self.getMaxValue(for: command), roundChiclet: roundChiclet)
}

private func supportsMuteCommand() -> Bool {
Expand Down
25 changes: 25 additions & 0 deletions MonitorControl/Support/OSDUtils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// OSDUtils.swift
// MonitorControl
//
// Created by Victor Chabbert on 19/06/2020.
// Copyright 漏 2020 Guillaume Broder. All rights reserved.
//

import Cocoa

class OSDUtils: NSObject {
static let chicletCount: Float = 16

static func chiclet(fromValue value: Float, maxValue: Float) -> Float {
return (value * self.chicletCount) / maxValue
}

static func value(fromChiclet chiclet: Float, maxValue: Float) -> Float {
return (chiclet * maxValue) / self.chicletCount
}

static func getDistance(fromNearestChiclet chiclet: Float) -> Float {
return abs(chiclet.rounded(.towardZero) - chiclet)
}
}
2 changes: 1 addition & 1 deletion MonitorControlHelper/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>647</string>
<string>719</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSBackgroundOnly</key>
Expand Down

0 comments on commit 5cacd25

Please sign in to comment.