mirror of
https://github.com/MonitorControl/MonitorControl.git
synced 2026-05-15 14:15:55 -06:00
🎉 v1.2
Many changes in commit… - Contrast slider is back ! - Added a Preferences Window - Function keys (should) work - If (like me) your screen backlight is still too bright even at 0, you can now set the contrast to 0 when the brightness hit 0 too - App can now be started at login (Needs to be tested) Signed-off-by: Guillaume Broder <iamnotheoneyouseek@gmail.com>
This commit is contained in:
parent
087bb132c6
commit
85f4293199
12 changed files with 189 additions and 149 deletions
|
|
@ -287,13 +287,13 @@
|
|||
TargetAttributes = {
|
||||
56754EAA1D9A4016007BCDC5 = {
|
||||
CreatedOnToolsVersion = 8.0;
|
||||
DevelopmentTeam = KGY56RWR9A;
|
||||
DevelopmentTeam = AZJ6KXSZ74;
|
||||
LastSwiftMigration = 0900;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
F06792E6200A73460066C438 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
DevelopmentTeam = KGY56RWR9A;
|
||||
DevelopmentTeam = AZJ6KXSZ74;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
|
|
@ -363,13 +363,11 @@
|
|||
);
|
||||
inputPaths = (
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-MonitorControl/Pods-MonitorControl-frameworks.sh",
|
||||
"${BUILT_PRODUCTS_DIR}/HotKey/HotKey.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/MASPreferences/MASPreferences.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/MediaKeyTap/MediaKeyTap.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HotKey.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MASPreferences.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MediaKeyTap.framework",
|
||||
);
|
||||
|
|
@ -614,10 +612,10 @@
|
|||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = KGY56RWR9A;
|
||||
DEVELOPMENT_TEAM = AZJ6KXSZ74;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/MonitorControl/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControl;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControlD;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MonitorControl/Bridging-Header.h";
|
||||
|
|
@ -633,10 +631,10 @@
|
|||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = KGY56RWR9A;
|
||||
DEVELOPMENT_TEAM = AZJ6KXSZ74;
|
||||
INFOPLIST_FILE = "$(SRCROOT)/MonitorControl/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControl;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.guillaumeb.MonitorControlD;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "MonitorControl/Bridging-Header.h";
|
||||
|
|
@ -655,7 +653,7 @@
|
|||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = KGY56RWR9A;
|
||||
DEVELOPMENT_TEAM = AZJ6KXSZ74;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = MonitorControlHelper/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
|
|
@ -678,7 +676,7 @@
|
|||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = KGY56RWR9A;
|
||||
DEVELOPMENT_TEAM = AZJ6KXSZ74;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = MonitorControlHelper/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
|
|
|
|||
|
|
@ -31,9 +31,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
|||
var mediaKeyTap: MediaKeyTap?
|
||||
var prefsController: NSWindowController?
|
||||
|
||||
var keysListenedFor: [MediaKey] = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown]
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
app = self
|
||||
mediaKeyTap = MediaKeyTap.init(delegate: self, forKeys: [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown], observeBuiltIn: false)
|
||||
|
||||
let listenFor = prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue)
|
||||
if listenFor == Utils.ListenForKeys.brightnessOnlyKeys.rawValue {
|
||||
keysListenedFor.removeSubrange(2...4)
|
||||
} else if listenFor == Utils.ListenForKeys.volumeOnlyKeys.rawValue {
|
||||
keysListenedFor.removeSubrange(0...1)
|
||||
}
|
||||
|
||||
mediaKeyTap = MediaKeyTap.init(delegate: self, forKeys: keysListenedFor, observeBuiltIn: false)
|
||||
let storyboard: NSStoryboard = NSStoryboard.init(name: NSStoryboard.Name(rawValue: "Main"), bundle: Bundle.main)
|
||||
let views = [
|
||||
storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "MainPrefsVC")),
|
||||
|
|
@ -42,6 +52,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
|||
]
|
||||
prefsController = MASPreferencesWindowController(viewControllers: views, title: NSLocalizedString("Preferences", comment: "Shown in Preferences window"))
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleListenForChanged), name: NSNotification.Name.init(Utils.PrefKeys.listenFor.rawValue), object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleShowContrastChanged), name: NSNotification.Name.init(Utils.PrefKeys.showContrast.rawValue), object: nil)
|
||||
|
||||
statusItem.image = NSImage.init(named: NSImage.Name(rawValue: "status"))
|
||||
statusItem.menu = statusMenu
|
||||
|
||||
|
|
@ -65,6 +78,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
|||
@IBAction func prefsClicked(_ sender: AnyObject) {
|
||||
if let prefsController = prefsController {
|
||||
prefsController.showWindow(sender)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
prefsController.window?.makeKeyAndOrderFront(sender)
|
||||
}
|
||||
}
|
||||
|
|
@ -76,15 +90,24 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
|||
prefs.set(true, forKey: Utils.PrefKeys.appAlreadyLaunched.rawValue)
|
||||
|
||||
prefs.set(false, forKey: Utils.PrefKeys.startAtLogin.rawValue)
|
||||
prefs.set(true, forKey: Utils.PrefKeys.startWhenExternal.rawValue)
|
||||
|
||||
prefs.set(false, forKey: Utils.PrefKeys.showContrast.rawValue)
|
||||
prefs.set(false, forKey: Utils.PrefKeys.lowerContrast.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Menu
|
||||
|
||||
func clearDisplays() {
|
||||
for monitor in monitorItems {
|
||||
statusMenu.removeItem(monitor)
|
||||
if statusMenu.items.count > 2 {
|
||||
var items: [NSMenuItem] = []
|
||||
for i in 0..<statusMenu.items.count - 2 {
|
||||
items.append(statusMenu.items[i])
|
||||
}
|
||||
|
||||
for item in items {
|
||||
statusMenu.removeItem(item)
|
||||
}
|
||||
}
|
||||
|
||||
monitorItems = []
|
||||
|
|
@ -93,7 +116,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
|||
|
||||
func updateDisplays() {
|
||||
clearDisplays()
|
||||
sleep(1)
|
||||
|
||||
var filteredScreens = NSScreen.screens.filter { screen -> Bool in
|
||||
if let id = screen.deviceDescription[NSDeviceDescriptionKey.init("NSScreenNumber")] as? CGDirectDisplayID {
|
||||
|
|
@ -155,6 +177,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
|||
forDisplay: display,
|
||||
command: BRIGHTNESS,
|
||||
title: NSLocalizedString("Brightness", comment: "Shown in menu"))
|
||||
if prefs.bool(forKey: Utils.PrefKeys.showContrast.rawValue) {
|
||||
let contrastSliderHandler = Utils.addSliderMenuItem(toMenu: monitorSubMenu,
|
||||
forDisplay: display,
|
||||
command: CONTRAST,
|
||||
title: NSLocalizedString("Contrast", comment: "Shown in menu"))
|
||||
display.contrastSliderHandler = contrastSliderHandler
|
||||
}
|
||||
|
||||
display.volumeSliderHandler = volumeSliderHandler
|
||||
display.brightnessSliderHandler = brightnessSliderHandler
|
||||
displays.append(display)
|
||||
|
|
@ -173,33 +203,57 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
|||
|
||||
// MARK: - Media Key Tap delegate
|
||||
|
||||
func handle(mediaKey: MediaKey, event: KeyEvent) {
|
||||
func handle(mediaKey: MediaKey, event: KeyEvent?) {
|
||||
guard let currentDisplay = Utils.getCurrentDisplay(from: displays) else { return }
|
||||
var rel = 0
|
||||
|
||||
switch mediaKey {
|
||||
case .brightnessUp:
|
||||
rel = +self.step
|
||||
let value = currentDisplay.calcNewValue(for: BRIGHTNESS, withRel: rel)
|
||||
currentDisplay.setBrightness(to: value)
|
||||
case .brightnessDown:
|
||||
rel = -self.step
|
||||
let value = currentDisplay.calcNewValue(for: BRIGHTNESS, withRel: rel)
|
||||
currentDisplay.setBrightness(to: value)
|
||||
case .mute:
|
||||
currentDisplay.mute()
|
||||
case .volumeUp:
|
||||
rel = +self.step
|
||||
let value = currentDisplay.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: rel)
|
||||
currentDisplay.setVolume(to: value)
|
||||
case .volumeDown:
|
||||
rel = -self.step
|
||||
let value = currentDisplay.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: rel)
|
||||
currentDisplay.setVolume(to: value)
|
||||
default:
|
||||
return
|
||||
let allDisplays = prefs.bool(forKey: Utils.PrefKeys.allScreens.rawValue) ? displays : [currentDisplay]
|
||||
for display in allDisplays {
|
||||
var rel = 0
|
||||
if prefs.bool(forKey: "\(display.identifier)-state") {
|
||||
switch mediaKey {
|
||||
case .brightnessUp:
|
||||
rel = +self.step
|
||||
let value = display.calcNewValue(for: BRIGHTNESS, withRel: rel)
|
||||
display.setBrightness(to: value)
|
||||
case .brightnessDown:
|
||||
rel = -self.step
|
||||
let value = currentDisplay.calcNewValue(for: BRIGHTNESS, withRel: rel)
|
||||
display.setBrightness(to: value)
|
||||
case .mute:
|
||||
display.mute()
|
||||
case .volumeUp:
|
||||
rel = +self.step
|
||||
let value = display.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: rel)
|
||||
display.setVolume(to: value)
|
||||
case .volumeDown:
|
||||
rel = -self.step
|
||||
let value = display.calcNewValue(for: AUDIO_SPEAKER_VOLUME, withRel: rel)
|
||||
display.setVolume(to: value)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Prefs notification
|
||||
|
||||
@objc func handleListenForChanged() {
|
||||
let listenFor = prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue)
|
||||
keysListenedFor = [.brightnessUp, .brightnessDown, .mute, .volumeUp, .volumeDown]
|
||||
if listenFor == Utils.ListenForKeys.brightnessOnlyKeys.rawValue {
|
||||
keysListenedFor.removeSubrange(2...4)
|
||||
} else if listenFor == Utils.ListenForKeys.volumeOnlyKeys.rawValue {
|
||||
keysListenedFor.removeSubrange(0...1)
|
||||
}
|
||||
|
||||
mediaKeyTap?.stop()
|
||||
mediaKeyTap = MediaKeyTap.init(delegate: self, forKeys: keysListenedFor, observeBuiltIn: false)
|
||||
mediaKeyTap?.start()
|
||||
}
|
||||
|
||||
@objc func handleShowContrastChanged() {
|
||||
self.updateDisplays()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@
|
|||
<objects>
|
||||
<viewController storyboardIdentifier="MainPrefsVC" id="HNb-aq-vnV" customClass="MainPrefsViewController" customModule="MonitorControl" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="iuQ-z6-BGz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="158"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="161"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8KQ-j9-ddv">
|
||||
<rect key="frame" x="18" y="109" width="92" height="29"/>
|
||||
<rect key="frame" x="18" y="112" width="92" height="29"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="General" id="ocE-Cc-2bi">
|
||||
<font key="font" metaFont="systemBold" size="24"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dZG-M6-fsQ">
|
||||
<rect key="frame" x="18" y="83" width="199" height="18"/>
|
||||
<rect key="frame" x="18" y="86" width="199" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Start MonitorControl at Login" bezelStyle="regularSquare" imagePosition="left" inset="2" id="UTh-SV-vAQ">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
|
|
@ -32,66 +32,64 @@
|
|||
<action selector="startAtLoginClicked:" target="HNb-aq-vnV" id="OrA-9Y-N8S"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button hidden="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="K3V-Ub-wAA">
|
||||
<rect key="frame" x="14" y="13" width="152" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Check for updates" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Vi8-Ye-W28">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fds-MD-5OJ">
|
||||
<rect key="frame" x="168" y="22" width="78" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Last check: " id="I3I-EP-Ev8">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button hidden="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nnZ-LS-lOW">
|
||||
<rect key="frame" x="18" y="49" width="218" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Check for updates automatically" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="SCJ-AY-phi">
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7YI-Uo-3fM">
|
||||
<rect key="frame" x="18" y="52" width="177" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Show a slider for contrast" bezelStyle="regularSquare" imagePosition="left" inset="2" id="8cS-Fg-fKy">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="showContrastSliderClicked:" target="HNb-aq-vnV" id="9BB-1N-fjo"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rYO-48-phB">
|
||||
<rect key="frame" x="18" y="18" width="215" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Lower Contrast after Brightness" bezelStyle="regularSquare" imagePosition="left" inset="2" id="JDl-l4-s8k">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="lowerContrastClicked:" target="HNb-aq-vnV" id="hki-Wc-jOF"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="fds-MD-5OJ" firstAttribute="centerY" secondItem="K3V-Ub-wAA" secondAttribute="centerY" id="2RB-zz-fOU"/>
|
||||
<constraint firstItem="dZG-M6-fsQ" firstAttribute="leading" secondItem="iuQ-z6-BGz" secondAttribute="leading" constant="20" id="8QT-EO-uBJ"/>
|
||||
<constraint firstItem="K3V-Ub-wAA" firstAttribute="top" secondItem="nnZ-LS-lOW" secondAttribute="bottom" constant="10" id="9DT-6C-dQo"/>
|
||||
<constraint firstItem="rYO-48-phB" firstAttribute="leading" secondItem="iuQ-z6-BGz" secondAttribute="leading" constant="20" id="Cyy-k9-19a"/>
|
||||
<constraint firstItem="8KQ-j9-ddv" firstAttribute="top" secondItem="iuQ-z6-BGz" secondAttribute="top" constant="20" id="Fwh-JQ-1Cx"/>
|
||||
<constraint firstItem="K3V-Ub-wAA" firstAttribute="leading" secondItem="iuQ-z6-BGz" secondAttribute="leading" constant="20" id="RWW-67-dGr"/>
|
||||
<constraint firstItem="nnZ-LS-lOW" firstAttribute="top" secondItem="dZG-M6-fsQ" secondAttribute="bottom" constant="20" id="V4k-4m-OkH"/>
|
||||
<constraint firstAttribute="bottom" secondItem="K3V-Ub-wAA" secondAttribute="bottom" constant="20" id="bFh-Xa-GoP"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="7YI-Uo-3fM" secondAttribute="trailing" constant="20" id="Jtd-pc-orO"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="rYO-48-phB" secondAttribute="trailing" constant="20" id="XEL-Kb-iUl"/>
|
||||
<constraint firstAttribute="bottom" secondItem="rYO-48-phB" secondAttribute="bottom" constant="20" id="YZb-sI-14r"/>
|
||||
<constraint firstItem="7YI-Uo-3fM" firstAttribute="leading" secondItem="iuQ-z6-BGz" secondAttribute="leading" constant="20" id="YhE-2k-Edh"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="dZG-M6-fsQ" secondAttribute="trailing" constant="20" id="c5h-vI-6Dg"/>
|
||||
<constraint firstItem="7YI-Uo-3fM" firstAttribute="top" secondItem="dZG-M6-fsQ" secondAttribute="bottom" constant="20" id="cdD-7n-hpx"/>
|
||||
<constraint firstItem="dZG-M6-fsQ" firstAttribute="top" secondItem="8KQ-j9-ddv" secondAttribute="bottom" constant="10" id="dPJ-ev-Y3O"/>
|
||||
<constraint firstItem="rYO-48-phB" firstAttribute="top" secondItem="7YI-Uo-3fM" secondAttribute="bottom" constant="20" id="fF5-Zz-fCl"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="dZG-M6-fsQ" secondAttribute="bottom" constant="20" id="gVa-4Q-fPP"/>
|
||||
<constraint firstItem="8KQ-j9-ddv" firstAttribute="leading" secondItem="iuQ-z6-BGz" secondAttribute="leading" constant="20" id="ii0-uV-Ylg"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="nnZ-LS-lOW" secondAttribute="trailing" constant="20" id="kgN-1K-yxh"/>
|
||||
<constraint firstItem="nnZ-LS-lOW" firstAttribute="leading" secondItem="iuQ-z6-BGz" secondAttribute="leading" constant="20" id="kqS-g2-YV5"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="fds-MD-5OJ" secondAttribute="trailing" constant="20" id="mfJ-HC-8ie"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="8KQ-j9-ddv" secondAttribute="trailing" constant="20" id="qVn-hz-dRV"/>
|
||||
<constraint firstItem="fds-MD-5OJ" firstAttribute="leading" secondItem="K3V-Ub-wAA" secondAttribute="trailing" constant="10" id="xuV-jB-am4"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="lowerContrast" destination="rYO-48-phB" id="hSm-0P-Uur"/>
|
||||
<outlet property="showContrastSlider" destination="7YI-Uo-3fM" id="bPT-Jq-Lfc"/>
|
||||
<outlet property="startAtLogin" destination="dZG-M6-fsQ" id="Rwg-dp-vIj"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="ALN-AB-CU5" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="116" y="285"/>
|
||||
<point key="canvasLocation" x="156" y="227"/>
|
||||
</scene>
|
||||
<!--Keys Prefs View Controller-->
|
||||
<scene sceneID="DmR-ia-4qL">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="KeysPrefsVC" id="MHy-Dv-i5A" customClass="KeysPrefsViewController" customModule="MonitorControl" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="nfK-n0-xN4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="129"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="98"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fUf-UM-yW3">
|
||||
<rect key="frame" x="85" y="48" width="196" height="26"/>
|
||||
<rect key="frame" x="85" y="17" width="196" height="26"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Both Brightness & Volume" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Vr4-xb-B4o" id="DkZ-as-YDS">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
|
|
@ -114,43 +112,15 @@
|
|||
</connections>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jFY-ug-0KQ">
|
||||
<rect key="frame" x="18" y="53" width="61" height="17"/>
|
||||
<rect key="frame" x="18" y="22" width="61" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Listen for" id="Vh8-06-U3K">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="AeW-b4-XNc">
|
||||
<rect key="frame" x="84" y="17" width="182" height="26"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Native Mac Keys" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="AiH-h3-2w5" id="xAv-cc-EOX">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="q3o-tj-rLn">
|
||||
<items>
|
||||
<menuItem title="Native Mac Keys" state="on" id="AiH-h3-2w5">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Function Keys (F1, F2...)" tag="1" id="dQm-ww-uhA">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
<connections>
|
||||
<action selector="listenOnChanged:" target="MHy-Dv-i5A" id="ryx-Jd-IbM"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VNO-n0-6Y1">
|
||||
<rect key="frame" x="18" y="22" width="60" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Listen on" id="C3e-V0-Tn0">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Uti-Vm-YwB">
|
||||
<rect key="frame" x="18" y="80" width="59" height="29"/>
|
||||
<rect key="frame" x="18" y="49" width="59" height="29"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Keys" id="Dcz-GG-1li">
|
||||
<font key="font" metaFont="systemBold" size="24"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
|
|
@ -159,30 +129,24 @@
|
|||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="AeW-b4-XNc" firstAttribute="centerY" secondItem="VNO-n0-6Y1" secondAttribute="centerY" id="4rl-y3-iUl"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="AeW-b4-XNc" secondAttribute="trailing" constant="20" id="CXx-3J-bgf"/>
|
||||
<constraint firstItem="fUf-UM-yW3" firstAttribute="centerY" secondItem="jFY-ug-0KQ" secondAttribute="centerY" id="FA2-Mh-LBe"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="fUf-UM-yW3" secondAttribute="trailing" constant="20" id="GBB-mP-L7K"/>
|
||||
<constraint firstItem="VNO-n0-6Y1" firstAttribute="leading" secondItem="nfK-n0-xN4" secondAttribute="leading" constant="20" id="KSb-DY-Ysk"/>
|
||||
<constraint firstItem="Uti-Vm-YwB" firstAttribute="leading" secondItem="nfK-n0-xN4" secondAttribute="leading" constant="20" id="Mfl-NR-mVk"/>
|
||||
<constraint firstItem="Uti-Vm-YwB" firstAttribute="top" secondItem="nfK-n0-xN4" secondAttribute="top" constant="20" id="ReX-zz-skV"/>
|
||||
<constraint firstAttribute="bottom" secondItem="fUf-UM-yW3" secondAttribute="bottom" constant="20" id="Rys-Fj-Tne"/>
|
||||
<constraint firstItem="fUf-UM-yW3" firstAttribute="leading" secondItem="jFY-ug-0KQ" secondAttribute="trailing" constant="10" id="Tic-nC-Oax"/>
|
||||
<constraint firstAttribute="bottom" secondItem="AeW-b4-XNc" secondAttribute="bottom" constant="20" id="ULA-RM-25S"/>
|
||||
<constraint firstItem="AeW-b4-XNc" firstAttribute="top" secondItem="fUf-UM-yW3" secondAttribute="bottom" constant="10" id="Zd8-nU-IPw"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Uti-Vm-YwB" secondAttribute="trailing" constant="20" id="ctI-eo-jEw"/>
|
||||
<constraint firstItem="AeW-b4-XNc" firstAttribute="leading" secondItem="VNO-n0-6Y1" secondAttribute="trailing" constant="10" id="dkl-ho-D2N"/>
|
||||
<constraint firstItem="jFY-ug-0KQ" firstAttribute="top" secondItem="Uti-Vm-YwB" secondAttribute="bottom" constant="10" id="t7Y-Pv-HlI"/>
|
||||
<constraint firstItem="jFY-ug-0KQ" firstAttribute="leading" secondItem="nfK-n0-xN4" secondAttribute="leading" constant="20" id="xCB-fT-tMo"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="listenFor" destination="fUf-UM-yW3" id="tgS-jF-nte"/>
|
||||
<outlet property="listenOn" destination="AeW-b4-XNc" id="dqX-if-8ib"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="hVj-0D-6XC" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="624" y="179"/>
|
||||
<point key="canvasLocation" x="642" y="163"/>
|
||||
</scene>
|
||||
<!--Display Prefs View Controller-->
|
||||
<scene sceneID="kdt-oj-Qke">
|
||||
|
|
|
|||
|
|
@ -24,11 +24,10 @@ class ButtonCellView: NSTableCellView {
|
|||
case .on:
|
||||
prefs.set(true, forKey: "\(display.identifier)-state")
|
||||
case .off:
|
||||
prefs.set(true, forKey: "\(display.identifier)-state")
|
||||
prefs.set(false, forKey: "\(display.identifier)-state")
|
||||
default:
|
||||
break
|
||||
}
|
||||
// TODO: Toggle enabled display state
|
||||
print("Toggle enabled display state -> \(sender.state == .on ? "on" : "off")")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class Display {
|
|||
var isMuted: Bool = false
|
||||
var brightnessSliderHandler: SliderHandler?
|
||||
var volumeSliderHandler: SliderHandler?
|
||||
var contrastSliderHandler: SliderHandler?
|
||||
|
||||
private let prefs = UserDefaults.standard
|
||||
|
||||
|
|
@ -57,6 +58,18 @@ class Display {
|
|||
}
|
||||
|
||||
func setBrightness(to value: Int) {
|
||||
if prefs.bool(forKey: Utils.PrefKeys.lowerContrast.rawValue) {
|
||||
if value == 0 {
|
||||
Utils.sendCommand(CONTRAST, toMonitor: identifier, withValue: value)
|
||||
if let slider = contrastSliderHandler?.slider {
|
||||
slider.intValue = Int32(value)
|
||||
}
|
||||
} else if prefs.integer(forKey: "\(BRIGHTNESS)-\(identifier)") == 0 {
|
||||
let contrastValue = prefs.integer(forKey: "\(CONTRAST)-\(identifier)")
|
||||
Utils.sendCommand(CONTRAST, toMonitor: identifier, withValue: contrastValue)
|
||||
}
|
||||
}
|
||||
|
||||
Utils.sendCommand(BRIGHTNESS, toMonitor: identifier, withValue: value)
|
||||
if let slider = brightnessSliderHandler?.slider {
|
||||
slider.intValue = Int32(value)
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ class DisplayPrefsViewController: NSViewController, MASPreferencesViewController
|
|||
prefs.set(false, forKey: Utils.PrefKeys.allScreens.rawValue)
|
||||
default: break
|
||||
}
|
||||
// TODO: Toggle allScreens state
|
||||
print("Toggle allScreens state -> \(sender.state == .on ? "on" : "off")")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,24 +17,17 @@ class KeysPrefsViewController: NSViewController, MASPreferencesViewController {
|
|||
let prefs = UserDefaults.standard
|
||||
|
||||
@IBOutlet var listenFor: NSPopUpButton!
|
||||
@IBOutlet var listenOn: NSPopUpButton!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
listenFor.selectItem(at: prefs.integer(forKey: Utils.PrefKeys.listenFor.rawValue))
|
||||
listenOn.selectItem(at: prefs.integer(forKey: Utils.PrefKeys.listenOn.rawValue))
|
||||
}
|
||||
|
||||
@IBAction func listenForChanged(_ sender: NSPopUpButton) {
|
||||
prefs.set(sender.selectedTag(), forKey: Utils.PrefKeys.listenFor.rawValue)
|
||||
// TODO: Toggle keys listened for state
|
||||
print("Toggle keys listened for state state -> \(sender.selectedItem?.title ?? "")")
|
||||
NotificationCenter.default.post(name: Notification.Name.init(Utils.PrefKeys.listenFor.rawValue), object: nil)
|
||||
}
|
||||
|
||||
@IBAction func listenOnChanged(_ sender: NSPopUpButton) {
|
||||
prefs.set(sender.selectedTag(), forKey: Utils.PrefKeys.listenOn.rawValue)
|
||||
// TODO: Toggle keys listened on state
|
||||
print("Toggle keys listened on state state -> \(sender.selectedItem?.title ?? "")")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,13 +18,15 @@ class MainPrefsViewController: NSViewController, MASPreferencesViewController {
|
|||
let prefs = UserDefaults.standard
|
||||
|
||||
@IBOutlet var startAtLogin: NSButton!
|
||||
@IBOutlet var startWhenExternal: NSButton!
|
||||
@IBOutlet var showContrastSlider: NSButton!
|
||||
@IBOutlet var lowerContrast: NSButton!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
startAtLogin.state = prefs.bool(forKey: Utils.PrefKeys.startAtLogin.rawValue) ? .on : .off
|
||||
startWhenExternal.state = prefs.bool(forKey: Utils.PrefKeys.startWhenExternal.rawValue) ? .on : .off
|
||||
showContrastSlider.state = prefs.bool(forKey: Utils.PrefKeys.showContrast.rawValue) ? .on : .off
|
||||
lowerContrast.state = prefs.bool(forKey: Utils.PrefKeys.lowerContrast.rawValue) ? .on : .off
|
||||
}
|
||||
|
||||
@IBAction func startAtLoginClicked(_ sender: NSButton) {
|
||||
|
|
@ -41,15 +43,26 @@ class MainPrefsViewController: NSViewController, MASPreferencesViewController {
|
|||
print("Toggle start at login state -> \(sender.state == .on ? "on" : "off")")
|
||||
}
|
||||
|
||||
@IBAction func startWhenExternalClicked(_ sender: NSButton) {
|
||||
@IBAction func showContrastSliderClicked(_ sender: NSButton) {
|
||||
switch sender.state {
|
||||
case .on:
|
||||
prefs.set(true, forKey: Utils.PrefKeys.startWhenExternal.rawValue)
|
||||
prefs.set(true, forKey: Utils.PrefKeys.showContrast.rawValue)
|
||||
case .off:
|
||||
prefs.set(false, forKey: Utils.PrefKeys.startWhenExternal.rawValue)
|
||||
prefs.set(false, forKey: Utils.PrefKeys.showContrast.rawValue)
|
||||
default: break
|
||||
}
|
||||
// TODO: Toggle start when external plugged in state
|
||||
print("Toggle start when external plugged in state -> \(sender.state == .on ? "on" : "off")")
|
||||
print("Toggle show contrast slider state -> \(sender.state == .on ? "on" : "off")")
|
||||
NotificationCenter.default.post(name: Notification.Name.init(Utils.PrefKeys.showContrast.rawValue), object: nil)
|
||||
}
|
||||
|
||||
@IBAction func lowerContrastClicked(_ sender: NSButton) {
|
||||
switch sender.state {
|
||||
case .on:
|
||||
prefs.set(true, forKey: Utils.PrefKeys.lowerContrast.rawValue)
|
||||
case .off:
|
||||
prefs.set(false, forKey: Utils.PrefKeys.lowerContrast.rawValue)
|
||||
default: break
|
||||
}
|
||||
print("Toggle lower contrast after brightness state -> \(sender.state == .on ? "on" : "off")")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,6 +179,8 @@ class Utils: NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Enums
|
||||
|
||||
/// UserDefault Keys for the app prefs
|
||||
enum PrefKeys: String {
|
||||
/// Was the app launched once
|
||||
|
|
@ -193,11 +195,26 @@ class Utils: NSObject {
|
|||
/// Keys listened for (Brightness/Volume)
|
||||
case listenFor
|
||||
|
||||
/// Keys listened on (mac or functions)
|
||||
case listenOn
|
||||
/// Show contrast sliders
|
||||
case showContrast
|
||||
|
||||
/// Lower contrast after brightness
|
||||
case lowerContrast
|
||||
|
||||
/// Change Brightness/Volume for all screens
|
||||
case allScreens
|
||||
}
|
||||
|
||||
/// Keys for the value of listenFor option
|
||||
enum ListenForKeys: Int {
|
||||
/// Listen for Brightness and Volume keys
|
||||
case brightnessAndVolumeKeys = 0
|
||||
|
||||
/// Listen for Brightness keys only
|
||||
case brightnessOnlyKeys = 1
|
||||
|
||||
/// Listen for Volume keys only
|
||||
case volumeOnlyKeys = 2
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,6 @@
|
|||
/* Class = "NSTableColumn"; headerCell.title = "Enabled"; ObjectID = "8U8-ec-Zbv"; */
|
||||
"8U8-ec-Zbv.headerCell.title" = "Enabled";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Native Mac Keys"; ObjectID = "AiH-h3-2w5"; */
|
||||
"AiH-h3-2w5.title" = "Native Mac Keys";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Listen on"; ObjectID = "C3e-V0-Tn0"; */
|
||||
"C3e-V0-Tn0.title" = "Listen on";
|
||||
|
||||
/* Class = "NSTableColumn"; headerCell.title = "Display Name"; ObjectID = "CHc-s5-4MN"; */
|
||||
"CHc-s5-4MN.headerCell.title" = "Display Name";
|
||||
|
||||
|
|
@ -56,9 +50,6 @@
|
|||
/* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "cUg-j2-8gt"; */
|
||||
"cUg-j2-8gt.title" = "Table View Cell";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Function Keys (F1, F2...)"; ObjectID = "dQm-ww-uhA"; */
|
||||
"dQm-ww-uhA.title" = "Function Keys (F1, F2...)";
|
||||
|
||||
/* Class = "NSTableColumn"; headerCell.title = "Display Id"; ObjectID = "dgp-q7-cBK"; */
|
||||
"dgp-q7-cBK.headerCell.title" = "Display Id";
|
||||
|
||||
|
|
@ -70,3 +61,7 @@
|
|||
|
||||
/* Class = "NSTextFieldCell"; title = "General"; ObjectID = "ocE-Cc-2bi"; */
|
||||
"ocE-Cc-2bi.title" = "General";
|
||||
|
||||
"8cS-Fg-fKy.title" = "Show a slider for contrast";
|
||||
|
||||
"JDl-l4-s8k.title" = "Lower Contrast after Brightness";
|
||||
|
|
|
|||
|
|
@ -8,12 +8,6 @@
|
|||
/* Class = "NSTableColumn"; headerCell.title = "Enabled"; ObjectID = "8U8-ec-Zbv"; */
|
||||
"8U8-ec-Zbv.headerCell.title" = "Activé";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Native Mac Keys"; ObjectID = "AiH-h3-2w5"; */
|
||||
"AiH-h3-2w5.title" = "Touches macOS Native";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Listen on"; ObjectID = "C3e-V0-Tn0"; */
|
||||
"C3e-V0-Tn0.title" = "Écouter sur";
|
||||
|
||||
/* Class = "NSTableColumn"; headerCell.title = "Display Name"; ObjectID = "CHc-s5-4MN"; */
|
||||
"CHc-s5-4MN.headerCell.title" = "Nom de l'écran";
|
||||
|
||||
|
|
@ -56,9 +50,6 @@
|
|||
/* Class = "NSTextFieldCell"; title = "Table View Cell"; ObjectID = "cUg-j2-8gt"; */
|
||||
"cUg-j2-8gt.title" = "Table View Cell";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Function Keys (F1, F2...)"; ObjectID = "dQm-ww-uhA"; */
|
||||
"dQm-ww-uhA.title" = "Touches de fonctions (F1, F2...)";
|
||||
|
||||
/* Class = "NSTableColumn"; headerCell.title = "Display Id"; ObjectID = "dgp-q7-cBK"; */
|
||||
"dgp-q7-cBK.headerCell.title" = "Identifiant";
|
||||
|
||||
|
|
@ -70,3 +61,7 @@
|
|||
|
||||
/* Class = "NSTextFieldCell"; title = "General"; ObjectID = "ocE-Cc-2bi"; */
|
||||
"ocE-Cc-2bi.title" = "Général";
|
||||
|
||||
"8cS-Fg-fKy.title" = "Afficher un slider pour le contraste";
|
||||
|
||||
"JDl-l4-s8k.title" = "Diminuer le contraste après la luminosité";
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ EXTERNAL SOURCES:
|
|||
|
||||
CHECKOUT OPTIONS:
|
||||
MediaKeyTap:
|
||||
:commit: d02f865ea962d5811375ddd047525baf409fb2b0
|
||||
:commit: f369a24f6c9931f2a1485a6cd1cef94a432bb36e
|
||||
:git: https://github.com/the0neyouseek/MediaKeyTap.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue