mirror of
https://github.com/MonitorControl/MonitorControl.git
synced 2026-05-16 14:15:55 -06:00
DRY and make sliders snap to multiples of 25
This commit is contained in:
parent
8edd2471fe
commit
0f66aad47e
1 changed files with 137 additions and 198 deletions
|
|
@ -15,7 +15,42 @@ struct Display {
|
|||
var serial: String
|
||||
}
|
||||
|
||||
var app : AppDelegate! = nil
|
||||
var app: AppDelegate! = nil
|
||||
let prefs = UserDefaults.standard
|
||||
|
||||
func ddcctl(monitor: CGDirectDisplayID, command: Int32, value: Int) {
|
||||
var wrcmd = DDCWriteCommand(control_id: UInt8(command), new_value: UInt8(value))
|
||||
DDCWrite(monitor, &wrcmd)
|
||||
print(value)
|
||||
}
|
||||
|
||||
class SliderHandler : NSObject {
|
||||
var display : Display
|
||||
var command : Int32 = 0
|
||||
|
||||
public init(display: Display, command: Int32) {
|
||||
self.display = display
|
||||
self.command = command
|
||||
}
|
||||
|
||||
func valueChanged(slider: NSSlider) {
|
||||
let snapInterval = 25
|
||||
let snapThreshold = 3
|
||||
|
||||
var value = slider.integerValue
|
||||
|
||||
let closest = (value + snapInterval / 2) / snapInterval * snapInterval
|
||||
if abs(closest - value) <= snapThreshold {
|
||||
value = closest
|
||||
slider.integerValue = value
|
||||
}
|
||||
|
||||
ddcctl(monitor: display.id, command: command, value: value)
|
||||
|
||||
prefs.setValue(value, forKey: "\(command)-\(display.serial)")
|
||||
prefs.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
|
@ -23,54 +58,15 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
@IBOutlet weak var statusMenu: NSMenu!
|
||||
@IBOutlet weak var window: NSWindow!
|
||||
|
||||
let prefs = UserDefaults.standard
|
||||
|
||||
let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength)
|
||||
|
||||
let keycode = UInt16(0x07)
|
||||
|
||||
var monitorItems : [NSMenuItem] = []
|
||||
var displays : [Display] = []
|
||||
var monitorItems: [NSMenuItem] = []
|
||||
var displays: [Display] = []
|
||||
var sliderHandlers: [SliderHandler] = []
|
||||
|
||||
@IBAction func quitClicked(_ sender: AnyObject) {
|
||||
NSApplication.shared().terminate(self)
|
||||
}
|
||||
|
||||
func setBrightness( slider: NSSlider ){
|
||||
let command = "-b"
|
||||
let value = slider.integerValue
|
||||
let i = slider.tag
|
||||
let d = displays[i]
|
||||
|
||||
ddcctl(monitor: d.id, command: command, value: value)
|
||||
|
||||
prefs.setValue(value, forKey: "\(command)-\(d.serial)")
|
||||
prefs.synchronize()
|
||||
}
|
||||
|
||||
func setVolume(slider: NSSlider ){
|
||||
let command = "-v"
|
||||
let value = slider.integerValue
|
||||
let i = slider.tag
|
||||
let d = displays[i]
|
||||
|
||||
ddcctl(monitor: d.id, command: command, value: value)
|
||||
|
||||
prefs.setValue(value, forKey: "\(command)-\(d.serial)")
|
||||
prefs.synchronize()
|
||||
}
|
||||
|
||||
func setContrast(slider: NSSlider ){
|
||||
let command = "-c"
|
||||
let value = slider.integerValue
|
||||
let i = slider.tag
|
||||
let d = displays[i]
|
||||
|
||||
ddcctl(monitor: d.id, command: command, value: value)
|
||||
|
||||
prefs.setValue(value, forKey: "\(command)-\(d.serial)")
|
||||
prefs.synchronize()
|
||||
}
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
app = self
|
||||
|
|
@ -84,16 +80,62 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
updateDisplays()
|
||||
}
|
||||
|
||||
func makeLabel(text: String, frame: NSRect) -> NSTextField {
|
||||
let label = NSTextField(frame: frame)
|
||||
label.stringValue = text
|
||||
label.isBordered = false
|
||||
label.isBezeled = false
|
||||
label.isEditable = false
|
||||
label.drawsBackground = false
|
||||
return label
|
||||
}
|
||||
|
||||
func addSliderItem(menu: NSMenu, defaultDisplay: Bool, display: Display, command: Int32, title: String, shortcut: String) -> NSSlider {
|
||||
let item = NSMenuItem()
|
||||
|
||||
let view = NSView(frame: NSRect(x: 0, y: 5, width: 250, height: 40))
|
||||
|
||||
let label = makeLabel(text: title, frame: NSRect(x: 20, y: 19, width: 130, height: 20))
|
||||
|
||||
let labelKeyCode = makeLabel(text: shortcut, frame: NSRect(x: 120, y: 19, width: 100, height: 20))
|
||||
labelKeyCode.isHidden = !defaultDisplay
|
||||
labelKeyCode.alignment = NSTextAlignment.right
|
||||
|
||||
let handler = SliderHandler(display: display, command: command)
|
||||
sliderHandlers.append(handler)
|
||||
|
||||
let slider = NSSlider(frame: NSRect(x: 20, y: 0, width: 200, height: 19))
|
||||
slider.target = handler
|
||||
slider.minValue = 0
|
||||
slider.maxValue = 100
|
||||
slider.integerValue = prefs.integer(forKey: "\(command)-\(display.serial)")
|
||||
slider.action = #selector(SliderHandler.valueChanged)
|
||||
|
||||
view.addSubview(label)
|
||||
view.addSubview(labelKeyCode)
|
||||
view.addSubview(slider)
|
||||
|
||||
item.view = view
|
||||
|
||||
menu.addItem(item)
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
return slider
|
||||
}
|
||||
|
||||
func updateDisplays() {
|
||||
for m in monitorItems {
|
||||
statusMenu.removeItem(m)
|
||||
}
|
||||
monitorItems = []
|
||||
displays = []
|
||||
sliderHandlers = []
|
||||
|
||||
sleep(1)
|
||||
|
||||
var firstDisplay : Display? = nil
|
||||
var firstDisplay: Display? = nil
|
||||
var firstBrightnessSlider: NSSlider! = nil
|
||||
var firstVolumeSlider: NSSlider! = nil
|
||||
|
||||
for s in NSScreen.screens()! {
|
||||
let id = s.deviceDescription["NSScreenNumber"] as! CGDirectDisplayID
|
||||
|
|
@ -109,131 +151,43 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
let name = getDisplayName(edid)
|
||||
let serial = getDisplaySerial(edid)
|
||||
|
||||
let defaultDisplay = firstDisplay == nil
|
||||
|
||||
let d = Display(id: id, name: name, serial: serial)
|
||||
displays.append(d)
|
||||
|
||||
let i = displays.count - 1
|
||||
|
||||
let monitorMenuItem = NSMenuItem()
|
||||
let monitorSubMenu = NSMenu()
|
||||
|
||||
let brightnessItem = NSMenuItem()
|
||||
let contrastItem = NSMenuItem()
|
||||
let volumeItem = NSMenuItem()
|
||||
let brightnessSlider = addSliderItem(menu: monitorSubMenu, defaultDisplay: defaultDisplay, display: d, command: BRIGHTNESS, title: "Brightness", shortcut: "⇧⌘- / ⇧⌘+")
|
||||
let _ = addSliderItem(menu: monitorSubMenu, defaultDisplay: defaultDisplay, display: d, command: CONTRAST, title: "Contrast", shortcut: "")
|
||||
let volumeSlider = addSliderItem(menu: monitorSubMenu, defaultDisplay: defaultDisplay, display: d, command: AUDIO_SPEAKER_VOLUME, title: "Volume", shortcut: "⌥⌘- / ⌥⌘+")
|
||||
|
||||
|
||||
let defaultMonitorItem = NSMenuItem()
|
||||
|
||||
let brightnessSlider = NSSlider(frame: NSRect(x: 20, y: 0, width: 200, height: 19))
|
||||
|
||||
brightnessSlider.target = self
|
||||
brightnessSlider.minValue = 0
|
||||
brightnessSlider.maxValue = 100
|
||||
brightnessSlider.integerValue = prefs.integer(forKey: "-b-\(serial)")
|
||||
brightnessSlider.action = #selector(AppDelegate.setBrightness)
|
||||
brightnessSlider.tag = i
|
||||
|
||||
let contrastSlider = NSSlider(frame: NSRect(x: 20, y: 0, width: 200, height: 19))
|
||||
|
||||
contrastSlider.target = self
|
||||
contrastSlider.minValue = 0
|
||||
contrastSlider.maxValue = 100
|
||||
contrastSlider.integerValue = prefs.integer(forKey: "-c-\(serial)")
|
||||
contrastSlider.action = #selector(AppDelegate.setContrast)
|
||||
contrastSlider.tag = i
|
||||
|
||||
let volumeSlider = NSSlider(frame: NSRect(x: 20, y: 3, width: 200, height: 19))
|
||||
|
||||
volumeSlider.target = self
|
||||
volumeSlider.minValue = 0
|
||||
volumeSlider.maxValue = 100
|
||||
volumeSlider.integerValue = prefs.integer(forKey: "-v-\(serial)")
|
||||
volumeSlider.action = #selector(AppDelegate.setVolume)
|
||||
volumeSlider.tag = i
|
||||
|
||||
let brightnesSliderView = NSView(frame: NSRect(x: 0, y: 5, width: 250, height: 40))
|
||||
let contrastSliderView = NSView(frame: NSRect(x: 0, y: 5, width: 250, height: 40))
|
||||
let volumeSliderView = NSView(frame: NSRect(x: 0, y: 5, width: 250, height: 40))
|
||||
let defaultMonitorView = NSView(frame: NSRect(x: 0, y: 5, width: 250, height: 25))
|
||||
|
||||
let brightnessLabel = NSTextField(frame: NSRect(x: 20, y: 16, width: 130, height: 20))
|
||||
brightnessLabel.stringValue = "Brightness"
|
||||
brightnessLabel.isBordered = false
|
||||
brightnessLabel.isBezeled = false
|
||||
brightnessLabel.isEditable = false
|
||||
brightnessLabel.drawsBackground = false
|
||||
|
||||
let brightnessLabelKeyCode = NSTextField(frame: NSRect(x: 120, y: 16, width: 100, height: 20))
|
||||
brightnessLabelKeyCode.stringValue = "⇧⌘- / ⇧⌘+"
|
||||
brightnessLabelKeyCode.isBordered = false
|
||||
brightnessLabelKeyCode.isBezeled = false
|
||||
brightnessLabelKeyCode.isEditable = false
|
||||
brightnessLabelKeyCode.drawsBackground = false
|
||||
brightnessLabelKeyCode.isHidden = firstDisplay != nil
|
||||
brightnessLabelKeyCode.alignment = NSTextAlignment.right
|
||||
|
||||
let constrastLabel = NSTextField(frame: NSRect(x: 20, y: 16, width: 130, height: 20))
|
||||
constrastLabel.stringValue = "Contrast"
|
||||
constrastLabel.isBordered = false
|
||||
constrastLabel.isBezeled = false
|
||||
constrastLabel.isEditable = false
|
||||
constrastLabel.drawsBackground = false
|
||||
|
||||
let volumeLabel = NSTextField(frame: NSRect(x: 20, y: 19, width: 130, height: 20))
|
||||
volumeLabel.stringValue = "Volume"
|
||||
volumeLabel.isBordered = false
|
||||
volumeLabel.isBezeled = false
|
||||
volumeLabel.isEditable = false
|
||||
volumeLabel.drawsBackground = false
|
||||
|
||||
let volumeLabelKeyCode = NSTextField(frame: NSRect(x: 120, y: 19, width: 100, height: 20))
|
||||
volumeLabelKeyCode.stringValue = "⌥⌘- / ⌥⌘+"
|
||||
volumeLabelKeyCode.isBordered = false
|
||||
volumeLabelKeyCode.isBezeled = false
|
||||
volumeLabelKeyCode.isEditable = false
|
||||
volumeLabelKeyCode.drawsBackground = false
|
||||
volumeLabelKeyCode.isHidden = firstDisplay != nil
|
||||
volumeLabelKeyCode.alignment = NSTextAlignment.right
|
||||
|
||||
brightnesSliderView.addSubview(brightnessLabel)
|
||||
brightnesSliderView.addSubview(brightnessLabelKeyCode)
|
||||
brightnesSliderView.addSubview(brightnessSlider)
|
||||
|
||||
contrastSliderView.addSubview(constrastLabel)
|
||||
contrastSliderView.addSubview(contrastSlider)
|
||||
|
||||
volumeSliderView.addSubview(volumeLabel)
|
||||
volumeSliderView.addSubview(volumeLabelKeyCode)
|
||||
volumeSliderView.addSubview(volumeSlider)
|
||||
|
||||
brightnessItem.view = brightnesSliderView
|
||||
contrastItem.view = contrastSliderView
|
||||
volumeItem.view = volumeSliderView
|
||||
|
||||
let defaultMonitorSelectButtom = NSButton(frame: NSRect(x: 25, y: 0, width: 200, height: 25))
|
||||
defaultMonitorSelectButtom.title = firstDisplay == nil ? "Default" : "Set as default"
|
||||
defaultMonitorSelectButtom.title = defaultDisplay ? "Default" : "Set as default"
|
||||
defaultMonitorSelectButtom.bezelStyle = NSRoundRectBezelStyle
|
||||
defaultMonitorSelectButtom.isEnabled = firstDisplay != nil
|
||||
defaultMonitorSelectButtom.tag = i
|
||||
defaultMonitorSelectButtom.isEnabled = !defaultDisplay
|
||||
|
||||
defaultMonitorView.addSubview(defaultMonitorSelectButtom)
|
||||
|
||||
defaultMonitorItem.view = defaultMonitorView
|
||||
|
||||
monitorSubMenu.addItem(brightnessItem)
|
||||
monitorSubMenu.addItem(NSMenuItem.separator())
|
||||
monitorSubMenu.addItem(contrastItem)
|
||||
monitorSubMenu.addItem(NSMenuItem.separator())
|
||||
monitorSubMenu.addItem(volumeItem)
|
||||
monitorSubMenu.addItem(NSMenuItem.separator())
|
||||
monitorSubMenu.addItem(defaultMonitorItem)
|
||||
|
||||
monitorMenuItem.title = "\(name)"
|
||||
monitorMenuItem.submenu = monitorSubMenu
|
||||
|
||||
monitorItems.append(monitorMenuItem)
|
||||
statusMenu.insertItem(monitorMenuItem, at: i)
|
||||
statusMenu.insertItem(monitorMenuItem, at: displays.count - 1)
|
||||
|
||||
if firstDisplay == nil {
|
||||
firstDisplay = d
|
||||
firstBrightnessSlider = brightnessSlider
|
||||
firstVolumeSlider = volumeSlider
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,45 +199,51 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
|
||||
NSEvent.addGlobalMonitorForEvents(
|
||||
matching: NSEventMask.keyDown, handler: {(event: NSEvent) in
|
||||
if (event.keyCode == 27 &&
|
||||
(event.modifierFlags.contains(NSEventModifierFlags.control)) &&
|
||||
(event.modifierFlags.contains(NSEventModifierFlags.command))) {
|
||||
let value = abs(self.prefs.integer(forKey: "-v-\(d.serial)") - 1)
|
||||
let modifiers = NSEventModifierFlags.init(rawValue: NSEventModifierFlags.command.rawValue |
|
||||
NSEventModifierFlags.control.rawValue |
|
||||
NSEventModifierFlags.option.rawValue |
|
||||
NSEventModifierFlags.shift.rawValue)
|
||||
var flags = event.modifierFlags.intersection(modifiers)
|
||||
|
||||
self.prefs.setValue(value, forKey: "-v-\(d.serial)")
|
||||
|
||||
self.ddcctl(monitor: d.id, command: "-v", value: value)
|
||||
|
||||
} else if (event.keyCode == 24 &&
|
||||
(event.modifierFlags.contains(NSEventModifierFlags.control)) &&
|
||||
(event.modifierFlags.contains(NSEventModifierFlags.command))) {
|
||||
let value = abs(self.prefs.integer(forKey: "-v-\(d.serial)") + 1)
|
||||
|
||||
self.prefs.setValue(value, forKey: "-v-\(d.serial)")
|
||||
|
||||
self.ddcctl(monitor: d.id, command: "-v", value: value)
|
||||
} else if (event.keyCode == 27 &&
|
||||
(event.modifierFlags.contains(NSEventModifierFlags.option)) &&
|
||||
(event.modifierFlags.contains(NSEventModifierFlags.command))) {
|
||||
let value = abs(self.prefs.integer(forKey: "-b-\(d.serial)") - 1)
|
||||
|
||||
self.prefs.setValue(value, forKey: "-b-\(d.serial))")
|
||||
|
||||
self.ddcctl(monitor: d.id, command: "-b", value: value)
|
||||
} else if (event.keyCode == 24 &&
|
||||
(event.modifierFlags.contains(NSEventModifierFlags.option)) &&
|
||||
(event.modifierFlags.contains(NSEventModifierFlags.command))) {
|
||||
let value = abs(self.prefs.integer(forKey: "-b-\(d.serial)") + 1)
|
||||
|
||||
self.prefs.setValue(value, forKey: "-b-\(d.serial)")
|
||||
|
||||
self.ddcctl(monitor: d.id, command: "-b", value: value)
|
||||
if !flags.contains(NSEventModifierFlags.command) {
|
||||
return
|
||||
}
|
||||
flags.subtract(NSEventModifierFlags.command)
|
||||
|
||||
var rel = 0
|
||||
if event.keyCode == 27 {
|
||||
rel = -5
|
||||
} else if event.keyCode == 24 {
|
||||
rel = +5
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
var command = Int32()
|
||||
var slider: NSSlider! = nil
|
||||
if flags == NSEventModifierFlags.option {
|
||||
command = AUDIO_SPEAKER_VOLUME
|
||||
slider = firstVolumeSlider
|
||||
} else if flags == NSEventModifierFlags.shift {
|
||||
command = BRIGHTNESS
|
||||
slider = firstBrightnessSlider
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
let k = "\(command)-\(d.serial)"
|
||||
let value = max(0, min(100, prefs.integer(forKey: k) + rel))
|
||||
|
||||
prefs.setValue(value, forKey: k)
|
||||
prefs.synchronize()
|
||||
slider.intValue = Int32(value)
|
||||
|
||||
ddcctl(monitor: d.id, command: command, value: value)
|
||||
})
|
||||
}
|
||||
|
||||
func acquirePrivileges() {
|
||||
let options : NSDictionary = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true]
|
||||
let options: NSDictionary = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true]
|
||||
let accessibilityEnabled = AXIsProcessTrustedWithOptions(options)
|
||||
|
||||
if !accessibilityEnabled {
|
||||
|
|
@ -293,27 +253,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||
return
|
||||
}
|
||||
|
||||
func ddcctl(monitor: CGDirectDisplayID, command: String, value: Int) {
|
||||
var cmd : Int32! = nil
|
||||
switch command {
|
||||
case "-b":
|
||||
cmd = BRIGHTNESS
|
||||
break
|
||||
case "-v":
|
||||
cmd = AUDIO_SPEAKER_VOLUME
|
||||
break
|
||||
case "-c":
|
||||
cmd = CONTRAST
|
||||
break
|
||||
default:
|
||||
precondition(false, "Unknown command: \(command)")
|
||||
}
|
||||
|
||||
var wrcmd = DDCWriteCommand(control_id: UInt8(cmd), new_value: UInt8(value))
|
||||
DDCWrite(monitor, &wrcmd)
|
||||
print(value)
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
// Insert code here to tear down your application
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue