Skip to content

Commit 036a29d

Browse files
committed
🎉 1.0
- Code rework - Native OSD - Change default shortcuts for problems with apps - Codesign app Signed-off-by: Guillaume Broder <[email protected]>
1 parent 38137e8 commit 036a29d

File tree

33 files changed

+691
-160
lines changed

33 files changed

+691
-160
lines changed

.github/menulet.png

23.1 KB
Loading

.github/osd.png

2.31 KB
Loading

License.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright © 2017
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

MonitorControl.OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata

Lines changed: 0 additions & 7 deletions
This file was deleted.

MonitorControl.OSX.xcodeproj/xcuserdata/mkurian.xcuserdatad/xcschemes/xcschememanagement.plist

Lines changed: 0 additions & 22 deletions
This file was deleted.

MonitorControl.OSX/Bridging-Header.h

Lines changed: 0 additions & 2 deletions
This file was deleted.

MonitorControl.OSX.xcodeproj/project.pbxproj renamed to MonitorControl.xcodeproj/project.pbxproj

Lines changed: 161 additions & 25 deletions
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
//
22
// AppDelegate.swift
3-
// MonitorControl.OSX
3+
// MonitorControl
44
//
55
// Created by Mathew Kurian on 9/26/16.
6-
// Copyright © 2016 Mathew Kurian. All rights reserved.
6+
// Last edited by Guillaume Broder on 9/17/2017
7+
// MIT Licensed. 2017.
78
//
89

910
import Cocoa
@@ -18,47 +19,13 @@ struct Display {
1819
var app: AppDelegate! = nil
1920
let prefs = UserDefaults.standard
2021

21-
func ddcctl(monitor: CGDirectDisplayID, command: Int32, value: Int) {
22-
var wrcmd = DDCWriteCommand(control_id: UInt8(command), new_value: UInt8(value))
23-
DDCWrite(monitor, &wrcmd)
24-
print(value)
25-
}
26-
27-
class SliderHandler : NSObject {
28-
var display : Display
29-
var command : Int32 = 0
30-
31-
public init(display: Display, command: Int32) {
32-
self.display = display
33-
self.command = command
34-
}
35-
36-
func valueChanged(slider: NSSlider) {
37-
let snapInterval = 25
38-
let snapThreshold = 3
39-
40-
var value = slider.integerValue
41-
42-
let closest = (value + snapInterval / 2) / snapInterval * snapInterval
43-
if abs(closest - value) <= snapThreshold {
44-
value = closest
45-
slider.integerValue = value
46-
}
47-
48-
ddcctl(monitor: display.id, command: command, value: value)
49-
50-
prefs.setValue(value, forKey: "\(command)-\(display.serial)")
51-
prefs.synchronize()
52-
}
53-
}
54-
5522
@NSApplicationMain
5623
class AppDelegate: NSObject, NSApplicationDelegate {
5724

5825
@IBOutlet weak var statusMenu: NSMenu!
5926
@IBOutlet weak var window: NSWindow!
6027

61-
let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength)
28+
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
6229

6330
var monitorItems: [NSMenuItem] = []
6431
var displays: [Display] = []
@@ -68,14 +35,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
6835
var defaultBrightnessSlider: NSSlider! = nil
6936
var defaultVolumeSlider: NSSlider! = nil
7037

38+
let step = 100/16;
39+
7140
@IBAction func quitClicked(_ sender: AnyObject) {
72-
NSApplication.shared().terminate(self)
41+
NSApplication.shared.terminate(self)
7342
}
7443

7544
func applicationDidFinishLaunching(_ aNotification: Notification) {
7645
app = self
7746

78-
statusItem.title = ""
47+
statusItem.image = NSImage.init(named: NSImage.Name(rawValue: "status"))
7948
statusItem.menu = statusMenu
8049

8150
acquirePrivileges()
@@ -84,39 +53,63 @@ class AppDelegate: NSObject, NSApplicationDelegate {
8453
updateDisplays()
8554

8655
NSEvent.addGlobalMonitorForEvents(
87-
matching: NSEventMask.keyDown, handler: {(event: NSEvent) in
56+
matching: NSEvent.EventTypeMask.keyDown, handler: {(event: NSEvent) in
8857
if self.defaultDisplay == nil {
8958
return
9059
}
9160

92-
let modifiers = NSEventModifierFlags.init(rawValue: NSEventModifierFlags.command.rawValue |
93-
NSEventModifierFlags.control.rawValue |
94-
NSEventModifierFlags.option.rawValue |
95-
NSEventModifierFlags.shift.rawValue)
96-
var flags = event.modifierFlags.intersection(modifiers)
97-
98-
if !flags.contains(NSEventModifierFlags.command) {
99-
return
100-
}
101-
flags.subtract(NSEventModifierFlags.command)
102-
103-
var rel = 0
104-
if event.keyCode == 27 {
105-
rel = -5
106-
} else if event.keyCode == 24 {
107-
rel = +5
108-
} else {
61+
// Keyboard shortcut only for main screen
62+
let currentDisplayId = NSScreen.main?.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as! CGDirectDisplayID
63+
if (self.defaultDisplay.id != currentDisplayId) {
64+
return
65+
}
66+
67+
// Brightness -> Shift + Control + Alt + Command + (Up/Down)
68+
// Volume -> Shift + Control + Alt + Command + (Left/Right)
69+
// Mute -> Minus
70+
71+
// Capture keys
72+
let modifiers = NSEvent.ModifierFlags.init(rawValue: NSEvent.ModifierFlags.shift.rawValue | NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.control.rawValue | NSEvent.ModifierFlags.option.rawValue)
73+
let flags = event.modifierFlags.intersection(modifiers)
74+
75+
// Only do something if all modifiers are active
76+
if !flags.contains(NSEvent.ModifierFlags.shift) || !flags.contains(NSEvent.ModifierFlags.command) || !flags.contains(NSEvent.ModifierFlags.control) || !flags.contains(NSEvent.ModifierFlags.option) {
77+
return
78+
}
79+
80+
var brightnessRel = 0
81+
var volumeRel = 0
82+
var rel = 0
83+
84+
// Down key
85+
if event.keyCode == Utils.key.keyDownArrow.rawValue {
86+
brightnessRel = -self.step
87+
// Up key
88+
} else if event.keyCode == Utils.key.keyUpArrow.rawValue {
89+
brightnessRel = +self.step
90+
// Left key
91+
} else if event.keyCode == Utils.key.keyLeftArrow.rawValue {
92+
volumeRel = -self.step
93+
// Right key
94+
} else if event.keyCode == Utils.key.keyRightArrow.rawValue {
95+
volumeRel = +self.step
96+
// M key
97+
} else if event.keyCode == Utils.key.keyMute.rawValue {
98+
volumeRel = -100
99+
} else {
109100
return
110101
}
111102

112103
var command = Int32()
113104
var slider: NSSlider! = nil
114-
if flags == NSEventModifierFlags.option {
105+
if brightnessRel == 0 {
115106
command = AUDIO_SPEAKER_VOLUME
116107
slider = self.defaultVolumeSlider
117-
} else if flags == NSEventModifierFlags.shift {
108+
rel = volumeRel
109+
} else if volumeRel == 0 {
118110
command = BRIGHTNESS
119111
slider = self.defaultBrightnessSlider
112+
rel = brightnessRel
120113
} else {
121114
return
122115
}
@@ -128,30 +121,27 @@ class AppDelegate: NSObject, NSApplicationDelegate {
128121
prefs.synchronize()
129122
slider.intValue = Int32(value)
130123

131-
ddcctl(monitor: self.defaultDisplay.id, command: command, value: value)
124+
Utils.ddcctl(monitor: self.defaultDisplay.id, command: command, value: value)
125+
126+
// OSD
127+
let manager : OSDManager = OSDManager.sharedManager() as! OSDManager
128+
var osdImage : Int = 1 // Brightness Image
129+
if brightnessRel == 0 {
130+
osdImage = 3 // Speaker image
131+
if value == 0 {
132+
osdImage = 4 // Mute speaker
133+
}
134+
}
135+
manager.showImage(Int64(osdImage), onDisplayID: self.defaultDisplay.id, priority: 0x1f4, msecUntilFade: 2000, filledChiclets: UInt32(value/self.step), totalChiclets: UInt32(100/self.step), locked: false)
132136
})
133137
}
134138

135-
func makeLabel(text: String, frame: NSRect) -> NSTextField {
136-
let label = NSTextField(frame: frame)
137-
label.stringValue = text
138-
label.isBordered = false
139-
label.isBezeled = false
140-
label.isEditable = false
141-
label.drawsBackground = false
142-
return label
143-
}
144-
145-
func addSliderItem(menu: NSMenu, isDefaultDisplay: Bool, display: Display, command: Int32, title: String, shortcut: String) -> NSSlider {
139+
func addSliderItem(menu: NSMenu, isDefaultDisplay: Bool, display: Display, command: Int32, title: String) -> NSSlider {
146140
let item = NSMenuItem()
147141

148142
let view = NSView(frame: NSRect(x: 0, y: 5, width: 250, height: 40))
149143

150-
let label = makeLabel(text: title, frame: NSRect(x: 20, y: 19, width: 130, height: 20))
151-
152-
let labelKeyCode = makeLabel(text: shortcut, frame: NSRect(x: 120, y: 19, width: 100, height: 20))
153-
labelKeyCode.isHidden = !isDefaultDisplay
154-
labelKeyCode.alignment = NSTextAlignment.right
144+
let label = Utils.makeLabel(text: title, frame: NSRect(x: 20, y: 19, width: 130, height: 20))
155145

156146
let handler = SliderHandler(display: display, command: command)
157147
sliderHandlers.append(handler)
@@ -161,14 +151,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
161151
slider.minValue = 0
162152
slider.maxValue = 100
163153
slider.integerValue = prefs.integer(forKey: "\(command)-\(display.serial)")
164-
slider.action = #selector(SliderHandler.valueChanged)
154+
slider.action = #selector(SliderHandler.valueChanged)
165155

166156
view.addSubview(label)
167-
view.addSubview(labelKeyCode)
168157
view.addSubview(slider)
169158

170159
item.view = view
171-
160+
172161
menu.addItem(item)
173162
menu.addItem(NSMenuItem.separator())
174163

@@ -183,14 +172,15 @@ class AppDelegate: NSObject, NSApplicationDelegate {
183172
for m in monitorItems {
184173
statusMenu.removeItem(m)
185174
}
175+
186176
monitorItems = []
187177
displays = []
188178
sliderHandlers = []
189179

190180
sleep(1)
191181

192-
for s in NSScreen.screens()! {
193-
let id = s.deviceDescription["NSScreenNumber"] as! CGDirectDisplayID
182+
for s in NSScreen.screens {
183+
let id = s.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as! CGDirectDisplayID
194184
if CGDisplayIsBuiltin(id) != 0 {
195185
continue
196186
}
@@ -211,17 +201,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
211201
let monitorMenuItem = NSMenuItem()
212202
let monitorSubMenu = NSMenu()
213203

214-
let brightnessSlider = addSliderItem(menu: monitorSubMenu, isDefaultDisplay: isDefaultDisplay, display: d, command: BRIGHTNESS, title: "Brightness", shortcut: "⇧⌘- / ⇧⌘+")
215-
let _ = addSliderItem(menu: monitorSubMenu, isDefaultDisplay: isDefaultDisplay, display: d, command: CONTRAST, title: "Contrast", shortcut: "")
216-
let volumeSlider = addSliderItem(menu: monitorSubMenu, isDefaultDisplay: isDefaultDisplay, display: d, command: AUDIO_SPEAKER_VOLUME, title: "Volume", shortcut: "⌥⌘- / ⌥⌘+")
217-
204+
let brightnessSlider = addSliderItem(menu: monitorSubMenu, isDefaultDisplay: isDefaultDisplay, display: d, command: BRIGHTNESS, title: NSLocalizedString("Brightness", comment: "Sown in menu"))
205+
let _ = addSliderItem(menu: monitorSubMenu, isDefaultDisplay: isDefaultDisplay, display: d, command: CONTRAST, title: NSLocalizedString("Contrast", comment: "Shown in menu"))
206+
let volumeSlider = addSliderItem(menu: monitorSubMenu, isDefaultDisplay: isDefaultDisplay, display: d, command: AUDIO_SPEAKER_VOLUME, title: NSLocalizedString("Volume", comment: "Shown in menu"))
218207

219208
let defaultMonitorItem = NSMenuItem()
220209
let defaultMonitorView = NSView(frame: NSRect(x: 0, y: 5, width: 250, height: 25))
221210

222211
let defaultMonitorSelectButtom = NSButton(frame: NSRect(x: 25, y: 0, width: 200, height: 25))
223-
defaultMonitorSelectButtom.title = isDefaultDisplay ? "Default" : "Set as default"
224-
defaultMonitorSelectButtom.bezelStyle = NSRoundRectBezelStyle
212+
defaultMonitorSelectButtom.title = isDefaultDisplay ? NSLocalizedString("Default", comment: "Shown in menu") : NSLocalizedString("Set as default", comment: "Shown in menu")
213+
defaultMonitorSelectButtom.bezelStyle = NSButton.BezelStyle.rounded
225214
defaultMonitorSelectButtom.isEnabled = !isDefaultDisplay
226215

227216
defaultMonitorView.addSubview(defaultMonitorSelectButtom)
@@ -246,19 +235,19 @@ class AppDelegate: NSObject, NSApplicationDelegate {
246235
if defaultDisplay == nil {
247236
// If no DDC capable display was detected
248237
let item = NSMenuItem()
249-
item.title = "No supported display found"
238+
item.title = NSLocalizedString("No supported display found", comment: "Shown in menu")
250239
item.isEnabled = false
251240
monitorItems.append(item)
252241
statusMenu.insertItem(item, at: 0)
253242
}
254243
}
255-
244+
256245
func acquirePrivileges() {
257246
let options: NSDictionary = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true]
258247
let accessibilityEnabled = AXIsProcessTrustedWithOptions(options)
259248

260249
if !accessibilityEnabled {
261-
print("You need to enable the keylogger in the System Prefrences")
250+
print(NSLocalizedString("You need to enable the keylogger in the System Prefrences for the keyboard shortcuts to work", comment: ""))
262251
}
263252

264253
return
@@ -293,10 +282,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
293282
}
294283

295284
func getDisplayName(_ edid: EDID) -> String {
296-
return getDescriptorString(edid, 0xFC) ?? "Display"
285+
return getDescriptorString(edid, 0xFC) ?? NSLocalizedString("Display", comment: "")
297286
}
298287

299288
func getDisplaySerial(_ edid: EDID) -> String {
300-
return getDescriptorString(edid, 0xFF) ?? "Unknown"
289+
return getDescriptorString(edid, 0xFF) ?? NSLocalizedString("Unknown", comment: "")
301290
}
302291
}

MonitorControl.OSX/Assets.xcassets/AppIcon.appiconset/Contents.json renamed to MonitorControl/Assets.xcassets/AppIcon.appiconset/Contents.json

File renamed without changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"version" : 1,
4+
"author" : "xcode"
5+
}
6+
}

0 commit comments

Comments
 (0)