From 115ea1372c4bbbb6ace4af582992779d607c9828 Mon Sep 17 00:00:00 2001 From: ad Date: Fri, 13 Apr 2018 16:25:25 +0300 Subject: [PATCH] + volume and brightness sliders --- MTMR.xcodeproj/project.pbxproj | 8 ++ MTMR/BrightnessViewController.swift | 45 ++++++++++ MTMR/ItemsParsing.swift | 9 ++ MTMR/TouchBarController.swift | 8 ++ MTMR/VolumeViewController.swift | 122 ++++++++++++++++++++++++++++ MTMR/defaultPreset.json | 2 + 6 files changed, 194 insertions(+) create mode 100644 MTMR/BrightnessViewController.swift create mode 100644 MTMR/VolumeViewController.swift diff --git a/MTMR.xcodeproj/project.pbxproj b/MTMR.xcodeproj/project.pbxproj index 3324c2b..fd8dea0 100644 --- a/MTMR.xcodeproj/project.pbxproj +++ b/MTMR.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */; }; 36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; }; 36C2ECE0207CB1B0003CDA33 /* defaultPreset.json in Resources */ = {isa = PBXBuildFile; fileRef = 36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */; }; + 6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */; }; + 6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */; }; B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D621205E03F5006E6B86 /* TouchBarController.swift */; }; B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */; }; B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B059D62C205F11E8006E6B86 /* DFRFoundation.framework */; }; @@ -53,6 +55,8 @@ 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsParsing.swift; sourceTree = ""; }; 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfigTests.swift; sourceTree = ""; }; 36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = defaultPreset.json; sourceTree = ""; }; + 6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrightnessViewController.swift; sourceTree = ""; }; + 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VolumeViewController.swift; sourceTree = ""; }; B059D621205E03F5006E6B86 /* TouchBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarController.swift; sourceTree = ""; }; B059D623205E04F3006E6B86 /* CustomButtonTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButtonTouchBarItem.swift; sourceTree = ""; }; B059D629205E13E5006E6B86 /* TouchBarPrivateApi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarPrivateApi.h; sourceTree = ""; }; @@ -151,6 +155,8 @@ B082B25C205C7D8000BC04DC /* MTMR.entitlements */, B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */, 36C2ECDF207CB1B0003CDA33 /* defaultPreset.json */, + 6027D1B72080E52A004FFDC7 /* BrightnessViewController.swift */, + 6027D1B82080E52A004FFDC7 /* VolumeViewController.swift */, ); path = MTMR; sourceTree = ""; @@ -313,10 +319,12 @@ B0F8771A207AC1EA00D6E430 /* TouchBarSupport.m in Sources */, B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */, B059D624205E04F3006E6B86 /* CustomButtonTouchBarItem.swift in Sources */, + 6027D1BA2080E52A004FFDC7 /* VolumeViewController.swift in Sources */, B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */, 36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */, B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */, 36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */, + 6027D1B92080E52A004FFDC7 /* BrightnessViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MTMR/BrightnessViewController.swift b/MTMR/BrightnessViewController.swift new file mode 100644 index 0000000..043ba8a --- /dev/null +++ b/MTMR/BrightnessViewController.swift @@ -0,0 +1,45 @@ +import Cocoa +import AppKit +import AVFoundation +import CoreAudio + +class BrightnessViewController: NSCustomTouchBarItem { + private(set) var sliderItem: NSSlider! + + init(identifier: NSTouchBarItem.Identifier, image: NSImage? = nil) { + super.init(identifier: identifier) + let brightness:Double = Double(getBrightness()) + sliderItem = NSSlider(value: brightness*100.0, minValue: 0.0, maxValue: 100.0, target: self, action:#selector(BrightnessViewController.sliderValueChanged(_:))) + + if (image != nil) { + sliderItem.cell = CustomSliderCell(knob: image!) + } + + self.view = sliderItem + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc func sliderValueChanged(_ sender: Any) { + if let sliderItem = sender as? NSSlider { + setBrightness(level: Float32(sliderItem.intValue)/100.0) + } + } + + private func getBrightness() -> Float32 { + var level: Float32 = 0.5 + let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect")) + + IODisplayGetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, &level) + return level + } + + private func setBrightness(level: Float) { + let service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODisplayConnect")) + + IODisplaySetFloatParameter(service, 0, kIODisplayBrightnessKey as CFString, level) + IOObjectRelease(service) + } +} diff --git a/MTMR/ItemsParsing.swift b/MTMR/ItemsParsing.swift index bb34b72..a32d6de 100644 --- a/MTMR/ItemsParsing.swift +++ b/MTMR/ItemsParsing.swift @@ -1,4 +1,5 @@ import Foundation +import AppKit extension Data { @@ -94,6 +95,8 @@ enum ItemType: Decodable { case appleScriptTitledButton(source: Source, refreshInterval: Double) case timeButton(formatTemplate: String) case flexSpace() + case volume() + case brightness() private enum CodingKeys: String, CodingKey { case type @@ -110,6 +113,8 @@ enum ItemType: Decodable { case appleScriptTitledButton case timeButton case flexSpace + case volume + case brightness } init(from decoder: Decoder) throws { @@ -137,6 +142,10 @@ enum ItemType: Decodable { self = .timeButton(formatTemplate: template) case .flexSpace: self = .flexSpace() + case .volume: + self = .volume() + case .brightness: + self = .brightness() } } } diff --git a/MTMR/TouchBarController.swift b/MTMR/TouchBarController.swift index a8927d5..6e42b9e 100644 --- a/MTMR/TouchBarController.swift +++ b/MTMR/TouchBarController.swift @@ -27,6 +27,10 @@ extension ItemType { return "com.toxblh.mtmr.timeButton." case .flexSpace(): return "NSTouchBarItem.Identifier.flexibleSpace" + case .volume(): + return "com.toxblh.mtmr.volume" + case .brightness(): + return "com.toxblh.mtmr.brightness" } } @@ -114,6 +118,10 @@ class TouchBarController: NSObject, NSTouchBarDelegate { barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template) case .flexSpace: barItem = nil + case .volume: + barItem = VolumeViewController(identifier: identifier) + case .brightness: + barItem = BrightnessViewController(identifier: identifier) } for parameter in item.additionalParameters { if case .width(let value) = parameter, let widthBarItem = barItem as? CanSetWidth { diff --git a/MTMR/VolumeViewController.swift b/MTMR/VolumeViewController.swift new file mode 100644 index 0000000..73517f1 --- /dev/null +++ b/MTMR/VolumeViewController.swift @@ -0,0 +1,122 @@ +import Cocoa +import AppKit +import AVFoundation +import CoreAudio + +class VolumeViewController: NSCustomTouchBarItem { + private(set) var sliderItem: NSSlider! + + init(identifier: NSTouchBarItem.Identifier, image: NSImage? = nil) { + super.init(identifier: identifier) + let volume:Double = Double(getInputGain()) + sliderItem = NSSlider(value: volume*100, minValue: 0.0, maxValue: 100.0, target: self, action:#selector(VolumeViewController.sliderValueChanged(_:))) + + if (image != nil) { + sliderItem.cell = CustomSliderCell(knob: image!) + } + + self.view = sliderItem + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc func sliderValueChanged(_ sender: Any) { + if let sliderItem = sender as? NSSlider { + setInputGain(Float32(sliderItem.intValue)/100.0) + } + } + + private var defaultDeviceID: AudioObjectID { + var deviceID: AudioObjectID = AudioObjectID(0) + var size: UInt32 = UInt32(MemoryLayout.size) + var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress() + address.mSelector = AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice) + address.mScope = AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal) + address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster) + AudioObjectGetPropertyData(AudioObjectID(kAudioObjectSystemObject), &address, 0, nil, &size, &deviceID) + return deviceID + } + + private func getInputGain() -> Float32 { + var volume: Float32 = 0.5 + var size: UInt32 = UInt32(MemoryLayout.size(ofValue: volume)) + var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress() + address.mSelector = AudioObjectPropertySelector(kAudioHardwareServiceDeviceProperty_VirtualMasterVolume) + address.mScope = AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput) + address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster) + AudioObjectGetPropertyData(defaultDeviceID, &address, 0, nil, &size, &volume) + return volume + } + + private func setInputGain(_ volume: Float32) -> OSStatus { + var inputVolume: Float32 = volume + let size: UInt32 = UInt32(MemoryLayout.size(ofValue: inputVolume)) + var address: AudioObjectPropertyAddress = AudioObjectPropertyAddress() + address.mSelector = AudioObjectPropertySelector(kAudioHardwareServiceDeviceProperty_VirtualMasterVolume) + address.mScope = AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput) + address.mElement = AudioObjectPropertyElement(kAudioObjectPropertyElementMaster) + return AudioObjectSetPropertyData(defaultDeviceID, &address, 0, nil, size, &inputVolume) + } +} + +class CustomSliderCell: NSSliderCell { + var knobImage:NSImage! + private var _currentKnobRect:NSRect! + private var _barRect:NSRect! + + required init(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override init() { + super.init() + } + + init(knob:NSImage) { + knobImage = knob; + super.init() + } + + override func drawKnob(_ knobRect: NSRect) { + + if (knobImage == nil) { + super.drawKnob(knobRect) + return; + } + + _currentKnobRect = knobRect; + drawBar(inside: _barRect, flipped: true) + self.controlView?.lockFocus() + + knobImage.size.width = knobRect.size.width + knobImage.size.height = knobRect.size.height + + let newOriginX:CGFloat = knobRect.origin.x * + (_barRect.size.width - (knobImage.size.width - knobRect.size.width)) / _barRect.size.width; + + knobImage.draw(at: NSPoint(x: newOriginX, y: knobRect.origin.y), from: NSRect(x: 0, y: 0, width: knobImage.size.width, height: knobImage.size.height), operation: NSCompositingOperation.sourceOver, fraction: 1) + + self.controlView?.unlockFocus() + + } + + override func drawBar(inside aRect: NSRect, flipped: Bool) { + _barRect = aRect + + var rect = aRect + rect.size.height = CGFloat(3) + let barRadius = CGFloat(2.5) + let value = CGFloat((self.doubleValue - self.minValue) / (self.maxValue - self.minValue)) + let finalWidth = CGFloat(value * (self.controlView!.frame.size.width - 8)) + var leftRect = rect + leftRect.size.width = finalWidth + let bg = NSBezierPath(roundedRect: rect, xRadius: barRadius, yRadius: barRadius) + NSColor.lightGray.setFill() + bg.fill() + let active = NSBezierPath(roundedRect: leftRect, xRadius: barRadius, yRadius: barRadius) + NSColor.darkGray.setFill() + active.fill() + } +} diff --git a/MTMR/defaultPreset.json b/MTMR/defaultPreset.json index 31cdebd..9322ee9 100644 --- a/MTMR/defaultPreset.json +++ b/MTMR/defaultPreset.json @@ -1,6 +1,8 @@ [ { "type": "escape" }, { "type": "exitTouchbar" }, + { "type": "volume", "width": 80 }, + { "type": "brightness", "width": 80 }, { "type": "brightnessDown" }, { "type": "staticButton",