1.5.0 - Now displaying all sensors on Apple Silicon.
This commit is contained in:
BIN
Assets/menu-arm.png
Normal file
BIN
Assets/menu-arm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 332 KiB |
BIN
Assets/sensors.png
Normal file
BIN
Assets/sensors.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 970 KiB |
@@ -32,6 +32,20 @@
|
||||
057F8B1B251D2A970048F1E1 /* Icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 057F8B1A251D2A970048F1E1 /* Icon.icns */; };
|
||||
057F8B65251D2D5F0048F1E1 /* GitHubUpdates.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 057F8B5C251D2D510048F1E1 /* GitHubUpdates.framework */; };
|
||||
057F8B67251D2D680048F1E1 /* GitHubUpdates.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 057F8B5C251D2D510048F1E1 /* GitHubUpdates.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
05A012C226FB9AEB00D3F6B6 /* SensorIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A012B026FB9AEB00D3F6B6 /* SensorIcon.swift */; };
|
||||
05A012C326FB9AEB00D3F6B6 /* SensorData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A012B126FB9AEB00D3F6B6 /* SensorData.swift */; };
|
||||
05A012C426FB9AEB00D3F6B6 /* SensorsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A012B226FB9AEB00D3F6B6 /* SensorsWindowController.swift */; };
|
||||
05A012C626FB9AEB00D3F6B6 /* SensorValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A012B526FB9AEB00D3F6B6 /* SensorValue.swift */; };
|
||||
05A012C826FB9AEB00D3F6B6 /* ArrayIsNotEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A012B726FB9AEB00D3F6B6 /* ArrayIsNotEmpty.swift */; };
|
||||
05A012C926FB9AEB00D3F6B6 /* SensorItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A012B826FB9AEB00D3F6B6 /* SensorItem.swift */; };
|
||||
05A012CA26FB9AEB00D3F6B6 /* ArrayIsEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A012BA26FB9AEB00D3F6B6 /* ArrayIsEmpty.swift */; };
|
||||
05A012CB26FB9AEB00D3F6B6 /* SensorGraphView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A012BB26FB9AEB00D3F6B6 /* SensorGraphView.swift */; };
|
||||
05A012CC26FB9AEB00D3F6B6 /* NSImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A012BC26FB9AEB00D3F6B6 /* NSImage.swift */; };
|
||||
05A012CD26FB9AEB00D3F6B6 /* Synchronizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A012BD26FB9AEB00D3F6B6 /* Synchronizable.swift */; };
|
||||
05A012CE26FB9AEB00D3F6B6 /* Sensors.m in Sources */ = {isa = PBXBuildFile; fileRef = 05A012BE26FB9AEB00D3F6B6 /* Sensors.m */; };
|
||||
05A012CF26FB9AEB00D3F6B6 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A012BF26FB9AEB00D3F6B6 /* Colors.swift */; };
|
||||
05A012D626FB9B8B00D3F6B6 /* SensorItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = 05A012D226FB9B8B00D3F6B6 /* SensorItem.xib */; };
|
||||
05A012D726FB9B8B00D3F6B6 /* SensorsWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 05A012D426FB9B8B00D3F6B6 /* SensorsWindowController.xib */; };
|
||||
05AA407A251CF3D200106CEA /* ApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05AA4079251CF3D200106CEA /* ApplicationDelegate.swift */; };
|
||||
05AA407F251CF3D600106CEA /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 05AA407D251CF3D600106CEA /* MainMenu.xib */; };
|
||||
05AA408D251CFA0600106CEA /* ThermalLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05AA408C251CFA0600106CEA /* ThermalLog.swift */; };
|
||||
@@ -155,6 +169,22 @@
|
||||
057F8B53251D2C840048F1E1 /* ccache.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ccache.sh; sourceTree = "<group>"; };
|
||||
057F8B54251D2C840048F1E1 /* Debug - zld.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Debug - zld.xcconfig"; sourceTree = "<group>"; };
|
||||
057F8B56251D2D510048F1E1 /* GitHubUpdates.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GitHubUpdates.xcodeproj; path = Submodules/GitHubUpdates/GitHubUpdates.xcodeproj; sourceTree = "<group>"; };
|
||||
05A012B026FB9AEB00D3F6B6 /* SensorIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SensorIcon.swift; sourceTree = "<group>"; };
|
||||
05A012B126FB9AEB00D3F6B6 /* SensorData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SensorData.swift; sourceTree = "<group>"; };
|
||||
05A012B226FB9AEB00D3F6B6 /* SensorsWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SensorsWindowController.swift; sourceTree = "<group>"; };
|
||||
05A012B426FB9AEB00D3F6B6 /* Sensors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sensors.h; sourceTree = "<group>"; };
|
||||
05A012B526FB9AEB00D3F6B6 /* SensorValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SensorValue.swift; sourceTree = "<group>"; };
|
||||
05A012B726FB9AEB00D3F6B6 /* ArrayIsNotEmpty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrayIsNotEmpty.swift; sourceTree = "<group>"; };
|
||||
05A012B826FB9AEB00D3F6B6 /* SensorItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SensorItem.swift; sourceTree = "<group>"; };
|
||||
05A012B926FB9AEB00D3F6B6 /* IOHID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IOHID.h; sourceTree = "<group>"; };
|
||||
05A012BA26FB9AEB00D3F6B6 /* ArrayIsEmpty.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrayIsEmpty.swift; sourceTree = "<group>"; };
|
||||
05A012BB26FB9AEB00D3F6B6 /* SensorGraphView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SensorGraphView.swift; sourceTree = "<group>"; };
|
||||
05A012BC26FB9AEB00D3F6B6 /* NSImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSImage.swift; sourceTree = "<group>"; };
|
||||
05A012BD26FB9AEB00D3F6B6 /* Synchronizable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Synchronizable.swift; sourceTree = "<group>"; };
|
||||
05A012BE26FB9AEB00D3F6B6 /* Sensors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Sensors.m; sourceTree = "<group>"; };
|
||||
05A012BF26FB9AEB00D3F6B6 /* Colors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; };
|
||||
05A012D326FB9B8B00D3F6B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/SensorItem.xib; sourceTree = "<group>"; };
|
||||
05A012D526FB9B8B00D3F6B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/SensorsWindowController.xib; sourceTree = "<group>"; };
|
||||
05AA4076251CF3D200106CEA /* Hot.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Hot.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
05AA4079251CF3D200106CEA /* ApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationDelegate.swift; sourceTree = "<group>"; };
|
||||
05AA407E251CF3D600106CEA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
@@ -333,6 +363,36 @@
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
05A012AF26FB9AEB00D3F6B6 /* Sensors */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
05A012BA26FB9AEB00D3F6B6 /* ArrayIsEmpty.swift */,
|
||||
05A012B726FB9AEB00D3F6B6 /* ArrayIsNotEmpty.swift */,
|
||||
05A012BF26FB9AEB00D3F6B6 /* Colors.swift */,
|
||||
05A012B926FB9AEB00D3F6B6 /* IOHID.h */,
|
||||
05A012B226FB9AEB00D3F6B6 /* SensorsWindowController.swift */,
|
||||
05A012BC26FB9AEB00D3F6B6 /* NSImage.swift */,
|
||||
05A012B126FB9AEB00D3F6B6 /* SensorData.swift */,
|
||||
05A012BB26FB9AEB00D3F6B6 /* SensorGraphView.swift */,
|
||||
05A012B026FB9AEB00D3F6B6 /* SensorIcon.swift */,
|
||||
05A012B826FB9AEB00D3F6B6 /* SensorItem.swift */,
|
||||
05A012B426FB9AEB00D3F6B6 /* Sensors.h */,
|
||||
05A012BE26FB9AEB00D3F6B6 /* Sensors.m */,
|
||||
05A012B526FB9AEB00D3F6B6 /* SensorValue.swift */,
|
||||
05A012BD26FB9AEB00D3F6B6 /* Synchronizable.swift */,
|
||||
);
|
||||
path = Sensors;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
05A012D126FB9B8B00D3F6B6 /* Sensors */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
05A012D226FB9B8B00D3F6B6 /* SensorItem.xib */,
|
||||
05A012D426FB9B8B00D3F6B6 /* SensorsWindowController.xib */,
|
||||
);
|
||||
path = Sensors;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
05AA406D251CF3D200106CEA = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -368,6 +428,7 @@
|
||||
05AA4088251CF3F400106CEA /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
05A012AF26FB9AEB00D3F6B6 /* Sensors */,
|
||||
057F8B0B251D273E0048F1E1 /* AboutWindowController.swift */,
|
||||
05AA4079251CF3D200106CEA /* ApplicationDelegate.swift */,
|
||||
05221E87267009A8007246FF /* SensorViewController.swift */,
|
||||
@@ -394,6 +455,7 @@
|
||||
05AA4089251CF3FC00106CEA /* UI */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
05A012D126FB9B8B00D3F6B6 /* Sensors */,
|
||||
057F8B13251D27650048F1E1 /* AboutWindowController.xib */,
|
||||
057F8AFE251D22360048F1E1 /* Images */,
|
||||
055A9321252778D00070F771 /* InfoViewController.xib */,
|
||||
@@ -434,7 +496,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1200;
|
||||
LastUpgradeCheck = 1250;
|
||||
LastUpgradeCheck = 1300;
|
||||
TargetAttributes = {
|
||||
05AA4075251CF3D200106CEA = {
|
||||
CreatedOnToolsVersion = 12.0;
|
||||
@@ -495,8 +557,10 @@
|
||||
057F8B1B251D2A970048F1E1 /* Icon.icns in Resources */,
|
||||
052FBCD5251F842C00C4322E /* CPUTemplate.pdf in Resources */,
|
||||
05221E8B26700A57007246FF /* SensorViewController.xib in Resources */,
|
||||
05A012D626FB9B8B00D3F6B6 /* SensorItem.xib in Resources */,
|
||||
055A9323252778D00070F771 /* InfoViewController.xib in Resources */,
|
||||
05B0DFA526714A830068FF9B /* UnknownTemplate.pdf in Resources */,
|
||||
05A012D726FB9B8B00D3F6B6 /* SensorsWindowController.xib in Resources */,
|
||||
057F8B15251D27650048F1E1 /* AboutWindowController.xib in Resources */,
|
||||
052FBCA7251EE46700C4322E /* PreferencesWindowController.xib in Resources */,
|
||||
05AA407F251CF3D600106CEA /* MainMenu.xib in Resources */,
|
||||
@@ -536,19 +600,31 @@
|
||||
files = (
|
||||
055A932525277E9E0070F771 /* IntegerToString.swift in Sources */,
|
||||
055A932725277F6E0070F771 /* TemperatureToString.swift in Sources */,
|
||||
05A012CD26FB9AEB00D3F6B6 /* Synchronizable.swift in Sources */,
|
||||
05221E8626700772007246FF /* DictionaryIsEmpty.swift in Sources */,
|
||||
05A012C626FB9AEB00D3F6B6 /* SensorValue.swift in Sources */,
|
||||
05A012C326FB9AEB00D3F6B6 /* SensorData.swift in Sources */,
|
||||
052FBCA3251EE43B00C4322E /* PreferencesWindowController.swift in Sources */,
|
||||
055A932B252780FD0070F771 /* GraphView.swift in Sources */,
|
||||
05A012CC26FB9AEB00D3F6B6 /* NSImage.swift in Sources */,
|
||||
05221E62266FD5BD007246FF /* AppleSilicon.m in Sources */,
|
||||
05221E88267009A8007246FF /* SensorViewController.swift in Sources */,
|
||||
05A012C426FB9AEB00D3F6B6 /* SensorsWindowController.swift in Sources */,
|
||||
057F8B11251D274C0048F1E1 /* NSApplication+LaunchServices.m in Sources */,
|
||||
05A012C926FB9AEB00D3F6B6 /* SensorItem.swift in Sources */,
|
||||
057F8B0C251D273E0048F1E1 /* AboutWindowController.swift in Sources */,
|
||||
05A012CA26FB9AEB00D3F6B6 /* ArrayIsEmpty.swift in Sources */,
|
||||
055A9320252778C10070F771 /* InfoViewController.swift in Sources */,
|
||||
05A012CB26FB9AEB00D3F6B6 /* SensorGraphView.swift in Sources */,
|
||||
05A012CF26FB9AEB00D3F6B6 /* Colors.swift in Sources */,
|
||||
05AA408D251CFA0600106CEA /* ThermalLog.swift in Sources */,
|
||||
055A932925277F990070F771 /* PercentToString.swift in Sources */,
|
||||
05AA407A251CF3D200106CEA /* ApplicationDelegate.swift in Sources */,
|
||||
05BA0BAA26F342DA00AA7BFC /* PressureToString.swift in Sources */,
|
||||
05A012C226FB9AEB00D3F6B6 /* SensorIcon.swift in Sources */,
|
||||
05A012CE26FB9AEB00D3F6B6 /* Sensors.m in Sources */,
|
||||
052FBC38251ECB8B00C4322E /* SMC.c in Sources */,
|
||||
05A012C826FB9AEB00D3F6B6 /* ArrayIsNotEmpty.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -595,6 +671,22 @@
|
||||
name = AboutWindowController.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
05A012D226FB9B8B00D3F6B6 /* SensorItem.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
05A012D326FB9B8B00D3F6B6 /* Base */,
|
||||
);
|
||||
name = SensorItem.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
05A012D426FB9B8B00D3F6B6 /* SensorsWindowController.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
05A012D526FB9B8B00D3F6B6 /* Base */,
|
||||
);
|
||||
name = SensorsWindowController.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
05AA407D251CF3D600106CEA /* MainMenu.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1250"
|
||||
LastUpgradeVersion = "1300"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -26,11 +26,12 @@ import Cocoa
|
||||
import GitHubUpdates
|
||||
|
||||
@NSApplicationMain
|
||||
class ApplicationDelegate: NSObject, NSApplicationDelegate
|
||||
class ApplicationDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate
|
||||
{
|
||||
private var statusItem: NSStatusItem?
|
||||
private var aboutWindowController: AboutWindowController?
|
||||
private var preferencesWindowController: PreferencesWindowController?
|
||||
private var sensorsWindowController: SensorsWindowController?
|
||||
private var observations: [ NSKeyValueObservation ] = []
|
||||
private var sensorViewControllers: [ SensorViewController ] = []
|
||||
|
||||
@@ -252,4 +253,37 @@ class ApplicationDelegate: NSObject, NSApplicationDelegate
|
||||
self.sensorsMenu.addItem( item )
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction public func viewAllSensors( _ sender: Any? )
|
||||
{
|
||||
if self.sensorsWindowController == nil
|
||||
{
|
||||
self.sensorsWindowController = SensorsWindowController()
|
||||
}
|
||||
|
||||
guard let window = self.sensorsWindowController?.window else
|
||||
{
|
||||
NSSound.beep()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
window.delegate = self
|
||||
|
||||
if window.isVisible == false
|
||||
{
|
||||
window.layoutIfNeeded()
|
||||
window.center()
|
||||
}
|
||||
|
||||
NSApp.activate( ignoringOtherApps: true )
|
||||
window.makeKeyAndOrderFront( nil )
|
||||
}
|
||||
|
||||
func windowWillClose( _ notification: Notification )
|
||||
{
|
||||
self.sensorsWindowController?.stop()
|
||||
|
||||
self.sensorsWindowController = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,3 +25,4 @@
|
||||
#import "NSApplication+LaunchServices.h"
|
||||
#import "SMC.h"
|
||||
#import "AppleSilicon.h"
|
||||
#import "Sensors.h"
|
||||
|
||||
@@ -58,14 +58,6 @@ public class PreferencesWindowController: NSWindowController
|
||||
}
|
||||
}
|
||||
|
||||
@objc public dynamic var startAtLogin = NSApp.isLoginItemEnabled()
|
||||
{
|
||||
didSet
|
||||
{
|
||||
NSApp.setLoginItemEnabled( self.startAtLogin )
|
||||
}
|
||||
}
|
||||
|
||||
@objc public dynamic var hideStatusIcon = UserDefaults.standard.bool( forKey: "hideStatusIcon" )
|
||||
{
|
||||
didSet
|
||||
@@ -74,6 +66,14 @@ public class PreferencesWindowController: NSWindowController
|
||||
}
|
||||
}
|
||||
|
||||
@objc public dynamic var startAtLogin = NSApp.isLoginItemEnabled()
|
||||
{
|
||||
didSet
|
||||
{
|
||||
NSApp.setLoginItemEnabled( self.startAtLogin )
|
||||
}
|
||||
}
|
||||
|
||||
@objc private dynamic var colorizeStatusItemTextLabel: String?
|
||||
|
||||
public override var windowNibName: NSNib.Name?
|
||||
|
||||
49
Hot/Classes/Sensors/ArrayIsEmpty.swift
Normal file
49
Hot/Classes/Sensors/ArrayIsEmpty.swift
Normal file
@@ -0,0 +1,49 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
import Cocoa
|
||||
|
||||
@objc( ArrayIsEmpty )
|
||||
public class ArrayIsEmpty: ValueTransformer
|
||||
{
|
||||
public override class func transformedValueClass() -> AnyClass
|
||||
{
|
||||
NSNumber.self
|
||||
}
|
||||
|
||||
public override class func allowsReverseTransformation() -> Bool
|
||||
{
|
||||
false
|
||||
}
|
||||
|
||||
public override func transformedValue( _ value: Any? ) -> Any?
|
||||
{
|
||||
guard let array = value as? NSArray else
|
||||
{
|
||||
return NSNumber( booleanLiteral: true )
|
||||
}
|
||||
|
||||
return NSNumber( booleanLiteral: array.count == 0 )
|
||||
}
|
||||
}
|
||||
49
Hot/Classes/Sensors/ArrayIsNotEmpty.swift
Normal file
49
Hot/Classes/Sensors/ArrayIsNotEmpty.swift
Normal file
@@ -0,0 +1,49 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
import Cocoa
|
||||
|
||||
@objc( ArrayIsNotEmpty )
|
||||
public class ArrayIsNotEmpty: ValueTransformer
|
||||
{
|
||||
public override class func transformedValueClass() -> AnyClass
|
||||
{
|
||||
NSNumber.self
|
||||
}
|
||||
|
||||
public override class func allowsReverseTransformation() -> Bool
|
||||
{
|
||||
false
|
||||
}
|
||||
|
||||
public override func transformedValue( _ value: Any? ) -> Any?
|
||||
{
|
||||
guard let array = value as? NSArray else
|
||||
{
|
||||
return NSNumber( booleanLiteral: false )
|
||||
}
|
||||
|
||||
return NSNumber( booleanLiteral: array.count > 0 )
|
||||
}
|
||||
}
|
||||
41
Hot/Classes/Sensors/Colors.swift
Normal file
41
Hot/Classes/Sensors/Colors.swift
Normal file
@@ -0,0 +1,41 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
import Cocoa
|
||||
|
||||
public class Colors
|
||||
{
|
||||
public class func color( for kind: SensorData.Kind ) -> NSColor
|
||||
{
|
||||
switch kind
|
||||
{
|
||||
case .thermal: return NSColor.systemOrange
|
||||
case .voltage: return NSColor.systemPurple
|
||||
case .current: return NSColor.systemTeal
|
||||
}
|
||||
}
|
||||
|
||||
private init()
|
||||
{}
|
||||
}
|
||||
127
Hot/Classes/Sensors/IOHID.h
Normal file
127
Hot/Classes/Sensors/IOHID.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
@import IOKit;
|
||||
@import CoreFoundation;
|
||||
|
||||
#import <IOKit/hidsystem/IOHIDEventSystemClient.h>
|
||||
#import <IOKit/hidsystem/IOHIDServiceClient.h>
|
||||
#import <IOKit/hid/IOHIDKeys.h>
|
||||
|
||||
/* @see https://opensource.apple.com/source/IOHIDFamily/ */
|
||||
|
||||
typedef NS_ENUM( int64_t, IOHIDPage )
|
||||
{
|
||||
IOHIDPageAppleVendor = 0xFF00,
|
||||
IOHIDPageAppleVendorKeyboard = 0xFF01,
|
||||
IOHIDPageAppleVendorMouse = 0xFF02,
|
||||
IOHIDPageAppleVendorAccelerometer = 0xFF03,
|
||||
IOHIDPageAppleVendorAmbientLightSensor = 0xFF04,
|
||||
IOHIDPageAppleVendorTemperatureSensor = 0xFF05,
|
||||
IOHIDPageAppleVendorHeadset = 0xFF07,
|
||||
IOHIDPageAppleVendorPowerSensor = 0xFF08,
|
||||
IOHIDPageAppleVendorSmartCover = 0xFF09,
|
||||
IOHIDPageAppleVendorPlatinum = 0xFF0A,
|
||||
IOHIDPageAppleVendorLisa = 0xFF0B,
|
||||
IOHIDPageAppleVendorMotion = 0xFF0C,
|
||||
IOHIDPageAppleVendorBattery = 0xFF0D,
|
||||
IOHIDPageAppleVendorIRRemote = 0xFF0E,
|
||||
IOHIDPageAppleVendorDebug = 0xFF0F,
|
||||
IOHIDPageAppleVendorFilteredEvent = 0xFF50,
|
||||
IOHIDPageAppleVendorMultitouch = 0xFF60,
|
||||
IOHIDPageAppleVendorDisplay = 0xFF92,
|
||||
IOHIDPageAppleVendorTopCase = 0x00FF
|
||||
};
|
||||
|
||||
typedef NS_ENUM( int64_t, IOHIDUsageAppleVendor )
|
||||
{
|
||||
IOHIDUsageAppleVendorTopCase = 0x01,
|
||||
IOHIDUsageAppleVendorDisplay = 0x02,
|
||||
IOHIDUsageAppleVendorAccelerometer = 0x03,
|
||||
IOHIDUsageAppleVendorAmbientLightSensor = 0x04,
|
||||
IOHIDUsageAppleVendorTemperatureSensor = 0x05,
|
||||
IOHIDUsageAppleVendorKeyboard = 0x06,
|
||||
IOHIDUsageAppleVendorHeadset = 0x07,
|
||||
IOHIDUsageAppleVendorProximitySensor = 0x08,
|
||||
IOHIDUsageAppleVendorGyro = 0x09,
|
||||
IOHIDUsageAppleVendorCompass = 0x0A,
|
||||
IOHIDUsageAppleVendorDeviceManagement = 0x0B,
|
||||
IOHIDUsageAppleVendorTrackpad = 0x0C,
|
||||
IOHIDUsageAppleVendorTopCaseReserved = 0x0D,
|
||||
IOHIDUsageAppleVendorMotion = 0x0E,
|
||||
IOHIDUsageAppleVendorKeyboardBacklight = 0x0F,
|
||||
IOHIDUsageAppleVendorDeviceMotionLite = 0x10,
|
||||
IOHIDUsageAppleVendorForce = 0x11,
|
||||
IOHIDUsageAppleVendorBluetoothRadio = 0x12,
|
||||
IOHIDUsageAppleVendorOrb = 0x13,
|
||||
IOHIDUsageAppleVendorAccessoryBattery = 0x14
|
||||
};
|
||||
|
||||
typedef NS_ENUM( int64_t, IOHIDUsageAppleVendorPowerSensor )
|
||||
{
|
||||
IOHIDUsageAppleVendorPowerSensorPower = 0x1,
|
||||
IOHIDUsageAppleVendorPowerSensorCurrent = 0x2,
|
||||
IOHIDUsageAppleVendorPowerSensorVoltage = 0x3
|
||||
};
|
||||
|
||||
typedef NS_ENUM( int64_t, IOHIDEvent )
|
||||
{
|
||||
IOHIDEventTypeNULL = 0x00,
|
||||
IOHIDEventTypeVendorDefined = 0x01,
|
||||
IOHIDEventTypeButton = 0x02,
|
||||
IOHIDEventTypeKeyboard = 0x03,
|
||||
IOHIDEventTypeTranslation = 0x04,
|
||||
IOHIDEventTypeRotation = 0x05,
|
||||
IOHIDEventTypeScroll = 0x06,
|
||||
IOHIDEventTypeScale = 0x07,
|
||||
IOHIDEventTypeZoom = 0x08,
|
||||
IOHIDEventTypeVelocity = 0x09,
|
||||
IOHIDEventTypeOrientation = 0x0A,
|
||||
IOHIDEventTypeDigitizer = 0x0B,
|
||||
IOHIDEventTypeAmbientLightSensor = 0x0C,
|
||||
IOHIDEventTypeAccelerometer = 0x0D,
|
||||
IOHIDEventTypeProximity = 0x0E,
|
||||
IOHIDEventTypeTemperature = 0x0F,
|
||||
IOHIDEventTypeNavigationSwipe = 0x10,
|
||||
IOHIDEventTypeSwipe = IOHIDEventTypeNavigationSwipe,
|
||||
IOHIDEventTypeMouse = 0x11,
|
||||
IOHIDEventTypeProgress = 0x12,
|
||||
IOHIDEventTypeCount = 0x13,
|
||||
IOHIDEventTypeGyro = 0x14,
|
||||
IOHIDEventTypeCompass = 0x15,
|
||||
IOHIDEventTypeZoomToggle = 0x16,
|
||||
IOHIDEventTypeDockSwipe = 0x17,
|
||||
IOHIDEventTypeSymbolicHotKey = 0x18,
|
||||
IOHIDEventTypePower = 0x19,
|
||||
IOHIDEventTypeBrightness = 0x1A,
|
||||
IOHIDEventTypeFluidTouchGesture = 0x1B,
|
||||
IOHIDEventTypeBoundaryScroll = 0x1C,
|
||||
IOHIDEventTypeReset = 0x1D
|
||||
};
|
||||
|
||||
extern IOHIDEventSystemClientRef IOHIDEventSystemClientCreate( CFAllocatorRef );
|
||||
extern void IOHIDEventSystemClientSetMatching( IOHIDEventSystemClientRef, CFDictionaryRef );
|
||||
extern CFTypeRef IOHIDServiceClientCopyEvent( IOHIDServiceClientRef, IOHIDEvent, int64_t, int64_t );
|
||||
extern CFDictionaryRef IOHIDServiceClientCopyProperties( IOHIDServiceClientRef, CFArrayRef );
|
||||
extern double IOHIDEventGetFloatValue( CFTypeRef, int64_t );
|
||||
46
Hot/Classes/Sensors/NSImage.swift
Normal file
46
Hot/Classes/Sensors/NSImage.swift
Normal file
@@ -0,0 +1,46 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
import Cocoa
|
||||
|
||||
extension NSImage
|
||||
{
|
||||
public func tinted( with color: NSColor ) -> NSImage
|
||||
{
|
||||
guard let copy = self.copy() as? NSImage else
|
||||
{
|
||||
return self
|
||||
}
|
||||
|
||||
copy.isTemplate = false
|
||||
let rect = NSRect( origin: NSZeroPoint, size: copy.size )
|
||||
|
||||
copy.lockFocus()
|
||||
color.set()
|
||||
rect.fill( using: .sourceAtop )
|
||||
copy.unlockFocus()
|
||||
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
137
Hot/Classes/Sensors/SensorData.swift
Normal file
137
Hot/Classes/Sensors/SensorData.swift
Normal file
@@ -0,0 +1,137 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc public class SensorData: NSObject, Synchronizable
|
||||
{
|
||||
@objc( SensorDataKind )
|
||||
public enum Kind: Int, CustomStringConvertible
|
||||
{
|
||||
case thermal
|
||||
case voltage
|
||||
case current
|
||||
|
||||
public var description: String
|
||||
{
|
||||
switch self
|
||||
{
|
||||
case .thermal: return "thermal"
|
||||
case .voltage: return "voltage"
|
||||
case .current: return "current"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc public private( set ) dynamic var kind: Kind
|
||||
@objc public private( set ) dynamic var name: String
|
||||
@objc public private( set ) dynamic var properties: [ String : Any ]?
|
||||
|
||||
private var data: [ Double ] = []
|
||||
|
||||
@objc public dynamic var values: [ NSNumber ]
|
||||
{
|
||||
return self.synchronized
|
||||
{
|
||||
return self.data.map { NSNumber( floatLiteral: $0 ) }
|
||||
}
|
||||
}
|
||||
|
||||
@objc public dynamic var min: NSNumber?
|
||||
{
|
||||
return self.synchronized
|
||||
{
|
||||
guard let max = self.data.min() else
|
||||
{
|
||||
return nil
|
||||
}
|
||||
|
||||
return NSNumber( floatLiteral: max )
|
||||
}
|
||||
}
|
||||
|
||||
@objc public dynamic var max: NSNumber?
|
||||
{
|
||||
return self.synchronized
|
||||
{
|
||||
guard let min = self.data.max() else
|
||||
{
|
||||
return nil
|
||||
}
|
||||
|
||||
return NSNumber( floatLiteral: min )
|
||||
}
|
||||
}
|
||||
|
||||
@objc public dynamic var last: NSNumber?
|
||||
{
|
||||
return self.synchronized
|
||||
{
|
||||
guard let last = self.data.last else
|
||||
{
|
||||
return nil
|
||||
}
|
||||
|
||||
return NSNumber( floatLiteral: last )
|
||||
}
|
||||
}
|
||||
|
||||
@objc public init( kind: Kind, name: String, properties: [ String : Any ]? )
|
||||
{
|
||||
self.kind = kind
|
||||
self.name = name
|
||||
self.properties = properties
|
||||
}
|
||||
|
||||
@objc( addValue: )
|
||||
public func add( value: Double )
|
||||
{
|
||||
self.synchronized
|
||||
{
|
||||
var data = self.data
|
||||
|
||||
data.append( value )
|
||||
|
||||
self.willChangeValue( for: \.values )
|
||||
self.willChangeValue( for: \.min )
|
||||
self.willChangeValue( for: \.max )
|
||||
self.willChangeValue( for: \.last )
|
||||
|
||||
self.data = data.suffix( 50 )
|
||||
|
||||
self.didChangeValue( for: \.values )
|
||||
self.didChangeValue( for: \.min )
|
||||
self.didChangeValue( for: \.max )
|
||||
self.didChangeValue( for: \.last )
|
||||
}
|
||||
}
|
||||
|
||||
public override var description: String
|
||||
{
|
||||
let min = String( format: "%.2f", self.min?.doubleValue ?? 0 )
|
||||
let max = String( format: "%.2f", self.max?.doubleValue ?? 0 )
|
||||
|
||||
return "\( super.description ): \( self.name ) (\( self.kind ), min: \( min ), max: \( max ))"
|
||||
}
|
||||
}
|
||||
248
Hot/Classes/Sensors/SensorGraphView.swift
Normal file
248
Hot/Classes/Sensors/SensorGraphView.swift
Normal file
@@ -0,0 +1,248 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
import Cocoa
|
||||
|
||||
public class SensorGraphView: NSView
|
||||
{
|
||||
@objc public enum GraphStyle: Int
|
||||
{
|
||||
case gradient
|
||||
case line
|
||||
case fill
|
||||
case bars
|
||||
}
|
||||
|
||||
@objc public dynamic var sensor: SensorData?
|
||||
|
||||
private var graphStyleObserver: NSKeyValueObservation?
|
||||
|
||||
public override init( frame: NSRect )
|
||||
{
|
||||
super.init( frame: frame )
|
||||
UserDefaults.standard.addObserver( self, forKeyPath: "graphStyle", options: .new, context: nil )
|
||||
}
|
||||
|
||||
required init?( coder: NSCoder )
|
||||
{
|
||||
super.init( coder: coder )
|
||||
UserDefaults.standard.addObserver( self, forKeyPath: "graphStyle", options: .new, context: nil )
|
||||
}
|
||||
|
||||
deinit
|
||||
{
|
||||
UserDefaults.standard.removeObserver( self, forKeyPath: "graphStyle" )
|
||||
}
|
||||
|
||||
public override func observeValue( forKeyPath keyPath: String?, of object: Any?, change: [ NSKeyValueChangeKey : Any ]?, context: UnsafeMutableRawPointer? )
|
||||
{
|
||||
if let keyPath = keyPath, keyPath == "graphStyle", let object = object as? UserDefaults, object == UserDefaults.standard
|
||||
{
|
||||
self.needsDisplay = true
|
||||
}
|
||||
else
|
||||
{
|
||||
super.observeValue( forKeyPath: keyPath, of: object, change: change, context: context )
|
||||
}
|
||||
}
|
||||
|
||||
private var graphStyle: GraphStyle
|
||||
{
|
||||
GraphStyle( rawValue: UserDefaults.standard.integer( forKey: "graphStyle" ) ) ?? .gradient
|
||||
}
|
||||
|
||||
public override func draw( _ rect: NSRect )
|
||||
{
|
||||
self.drawBorder( in: rect )
|
||||
self.drawBackground( in: rect )
|
||||
|
||||
guard let sensor = self.sensor, sensor.values.count >= 2 else
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
let values = sensor.values.map { CGFloat( $0.doubleValue ) }
|
||||
var min: CGFloat = values.min() ?? 0
|
||||
var max: CGFloat = values.max() ?? 0
|
||||
|
||||
if sensor.kind == .thermal && min >= 0 && max <= 100
|
||||
{
|
||||
min = 0
|
||||
max = 100
|
||||
}
|
||||
|
||||
if self.graphStyle == .bars
|
||||
{
|
||||
self.drawBars( in: rect,
|
||||
kind: sensor.kind,
|
||||
values: values,
|
||||
min: min,
|
||||
max: max,
|
||||
color: Colors.color( for: sensor.kind )
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
self.drawGraph( in: rect.insetBy( dx: 10, dy: 10 ),
|
||||
kind: sensor.kind,
|
||||
values: values,
|
||||
min: min,
|
||||
max: max,
|
||||
color: Colors.color( for: sensor.kind )
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func drawBorder( in rect: NSRect )
|
||||
{
|
||||
if self.graphStyle == .bars
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
let border = NSBezierPath( roundedRect: rect.insetBy( dx: 1, dy: 1 ), xRadius: 10, yRadius: 10 )
|
||||
border.lineWidth = 1
|
||||
|
||||
NSColor.gray.setStroke()
|
||||
border.stroke()
|
||||
}
|
||||
|
||||
private func drawBackground( in rect: NSRect )
|
||||
{
|
||||
if self.graphStyle == .bars
|
||||
{
|
||||
let c = 15
|
||||
let h = rect.size.height / CGFloat( c )
|
||||
let n = rect.size.width / h
|
||||
|
||||
for i in 0 ..< Int( n )
|
||||
{
|
||||
for j in 0 ..< c
|
||||
{
|
||||
let square = NSRect( x: rect.origin.x + h * CGFloat( i ), y: rect.origin.y + h * CGFloat( j ), width: h, height: h ).insetBy( dx: 1, dy: 1 )
|
||||
|
||||
NSColor.gray.withAlphaComponent( 0.2 ).setFill()
|
||||
square.fill()
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
let border = NSBezierPath( roundedRect: rect.insetBy( dx: 1, dy: 1 ), xRadius: 10, yRadius: 10 )
|
||||
|
||||
NSColor.gray.withAlphaComponent( 0.2 ).setFill()
|
||||
border.fill()
|
||||
}
|
||||
}
|
||||
|
||||
private func drawGraph( in rect: NSRect, kind: SensorData.Kind, values: [ CGFloat ], min: CGFloat, max: CGFloat, color: NSColor )
|
||||
{
|
||||
let p1 = NSBezierPath()
|
||||
let p2 = NSBezierPath()
|
||||
p1.lineWidth = 1
|
||||
p2.lineWidth = 0
|
||||
|
||||
if min == max && kind != .thermal
|
||||
{
|
||||
p1.move( to: NSPoint( x: rect.origin.x, y: rect.origin.y + rect.size.height / 2 ) )
|
||||
p1.line( to: NSPoint( x: rect.origin.x + rect.size.width, y: rect.origin.y + rect.size.height / 2 ) )
|
||||
p2.move( to: NSPoint( x: rect.origin.x, y: rect.origin.y + rect.size.height / 2 ) )
|
||||
p2.line( to: NSPoint( x: rect.origin.x + rect.size.width, y: rect.origin.y + rect.size.height / 2 ) )
|
||||
}
|
||||
else
|
||||
{
|
||||
for i in 0 ..< values.count
|
||||
{
|
||||
let value = values[ i ]
|
||||
let dx = rect.size.width / CGFloat( values.count - 1 )
|
||||
let x = rect.origin.x + CGFloat( i ) * dx
|
||||
let v = ( value - min ) / ( max - min )
|
||||
let y = rect.origin.y + v * rect.size.height
|
||||
|
||||
if i == 0
|
||||
{
|
||||
p1.move( to: NSPoint( x: x, y: y ) )
|
||||
p2.move( to: NSPoint( x: x, y: y ) )
|
||||
}
|
||||
else
|
||||
{
|
||||
p1.line( to: NSPoint( x: x, y: y ) )
|
||||
p2.line( to: NSPoint( x: x, y: y ) )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p2.line( to: NSPoint( x: rect.origin.x + rect.size.width, y: rect.origin.y - 3 ) )
|
||||
p2.line( to: NSPoint( x: rect.origin.x, y: rect.origin.y - 3 ) )
|
||||
p2.close()
|
||||
|
||||
if self.graphStyle == .gradient
|
||||
{
|
||||
let gradient = NSGradient( colors: [ color.withAlphaComponent( 0.25 ), color.withAlphaComponent( 0 ) ] )
|
||||
|
||||
gradient?.draw( in: p2, angle: -90 )
|
||||
}
|
||||
else if self.graphStyle == .fill
|
||||
{
|
||||
color.setFill()
|
||||
p2.fill()
|
||||
}
|
||||
|
||||
color.setStroke()
|
||||
p1.stroke()
|
||||
}
|
||||
|
||||
private func drawBars( in rect: NSRect, kind: SensorData.Kind, values: [ CGFloat ], min: CGFloat, max: CGFloat, color: NSColor )
|
||||
{
|
||||
let c = 15
|
||||
let h = rect.size.height / CGFloat( c )
|
||||
let n = rect.size.width / h
|
||||
|
||||
let values = Array( values.suffix( Int( n ) ) )
|
||||
|
||||
for i in 0 ..< Int( n )
|
||||
{
|
||||
if i >= values.count
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
let v = values[ i ]
|
||||
let p = ( v - min ) / ( max - min )
|
||||
|
||||
for j in 0 ..< c
|
||||
{
|
||||
if CGFloat( j ) / CGFloat( c ) > p
|
||||
{
|
||||
break
|
||||
}
|
||||
|
||||
let square = NSRect( x: rect.origin.x + h * CGFloat( i ), y: rect.origin.y + h * CGFloat( j ), width: h, height: h ).insetBy( dx: 1, dy: 1 )
|
||||
|
||||
color.setFill()
|
||||
square.fill()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Hot/Classes/Sensors/SensorIcon.swift
Normal file
54
Hot/Classes/Sensors/SensorIcon.swift
Normal file
@@ -0,0 +1,54 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
import Cocoa
|
||||
|
||||
@objc( SensorIcon )
|
||||
public class SensorIcon: ValueTransformer
|
||||
{
|
||||
public override class func transformedValueClass() -> AnyClass
|
||||
{
|
||||
NSImage.self
|
||||
}
|
||||
|
||||
public override class func allowsReverseTransformation() -> Bool
|
||||
{
|
||||
false
|
||||
}
|
||||
|
||||
public override func transformedValue( _ value: Any? ) -> Any?
|
||||
{
|
||||
guard let data = value as? SensorData else
|
||||
{
|
||||
return nil
|
||||
}
|
||||
|
||||
switch data.kind
|
||||
{
|
||||
case .thermal: return NSImage( named: "ThermalTemplate" )?.tinted( with: Colors.color( for: data.kind ) )
|
||||
case .voltage: return NSImage( named: "VoltageTemplate" )?.tinted( with: Colors.color( for: data.kind ) )
|
||||
case .current: return NSImage( named: "CurrentTemplate" )?.tinted( with: Colors.color( for: data.kind ) )
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Hot/Classes/Sensors/SensorItem.swift
Normal file
54
Hot/Classes/Sensors/SensorItem.swift
Normal file
@@ -0,0 +1,54 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
import Cocoa
|
||||
|
||||
public class SensorItem: NSCollectionViewItem
|
||||
{
|
||||
@objc public dynamic var sensor: SensorData?
|
||||
{
|
||||
didSet
|
||||
{
|
||||
self.graph.sensor = self.sensor
|
||||
}
|
||||
}
|
||||
|
||||
private var graph = SensorGraphView()
|
||||
|
||||
@IBOutlet private var graphContainer: NSView!
|
||||
|
||||
public override func viewDidLoad()
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
self.graph.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.graph.frame = self.graphContainer.bounds
|
||||
|
||||
self.graphContainer.addSubview( self.graph )
|
||||
self.graphContainer.addConstraint( NSLayoutConstraint( item: self.graph, attribute: .width, relatedBy: .equal, toItem: self.graphContainer, attribute: .width, multiplier: 1, constant: 0 ) )
|
||||
self.graphContainer.addConstraint( NSLayoutConstraint( item: self.graph, attribute: .height, relatedBy: .equal, toItem: self.graphContainer, attribute: .height, multiplier: 1, constant: 0 ) )
|
||||
self.graphContainer.addConstraint( NSLayoutConstraint( item: self.graph, attribute: .centerX, relatedBy: .equal, toItem: self.graphContainer, attribute: .centerX, multiplier: 1, constant: 0 ) )
|
||||
self.graphContainer.addConstraint( NSLayoutConstraint( item: self.graph, attribute: .centerY, relatedBy: .equal, toItem: self.graphContainer, attribute: .centerY, multiplier: 1, constant: 0 ) )
|
||||
}
|
||||
}
|
||||
49
Hot/Classes/Sensors/SensorValue.swift
Normal file
49
Hot/Classes/Sensors/SensorValue.swift
Normal file
@@ -0,0 +1,49 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
import Cocoa
|
||||
|
||||
@objc( SensorValue )
|
||||
public class SensorValue: ValueTransformer
|
||||
{
|
||||
public override class func transformedValueClass() -> AnyClass
|
||||
{
|
||||
NSString.self
|
||||
}
|
||||
|
||||
public override class func allowsReverseTransformation() -> Bool
|
||||
{
|
||||
false
|
||||
}
|
||||
|
||||
public override func transformedValue( _ value: Any? ) -> Any?
|
||||
{
|
||||
guard let n = value as? NSNumber else
|
||||
{
|
||||
return "--"
|
||||
}
|
||||
|
||||
return String( format: "%.02f", n.doubleValue )
|
||||
}
|
||||
}
|
||||
39
Hot/Classes/Sensors/Sensors.h
Normal file
39
Hot/Classes/Sensors/Sensors.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
@import Foundation;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class SensorData;
|
||||
|
||||
@interface Sensors: NSObject
|
||||
|
||||
@property( atomic, readwrite, strong ) NSArray< SensorData * > * data;
|
||||
|
||||
- ( void )stop: ( void ( ^ _Nullable )( void ) )completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
173
Hot/Classes/Sensors/Sensors.m
Normal file
173
Hot/Classes/Sensors/Sensors.m
Normal file
@@ -0,0 +1,173 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#import "Sensors.h"
|
||||
#import "Hot-Swift.h"
|
||||
#import "IOHID.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface Sensors()
|
||||
|
||||
@property( nonatomic, readwrite, strong ) NSMutableDictionary< NSString *, SensorData * > * sensors;
|
||||
@property( atomic, readwrite, strong, nullable ) void ( ^ completion )( void );
|
||||
@property( atomic, readwrite, assign ) BOOL shouldRun;
|
||||
|
||||
- ( void )run;
|
||||
- ( void )readSensors: ( NSTimer * )timer;
|
||||
- ( void )readSensors: ( SensorDataKind )kind page: ( IOHIDPage )page usage: ( NSInteger )usage event: ( IOHIDEvent )eventType client: ( IOHIDEventSystemClientRef )client;
|
||||
- ( void )addSensorData: ( double )value name: ( NSString * )name kind: ( SensorDataKind )kind properties: ( nullable NSDictionary * )properties;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@implementation Sensors
|
||||
|
||||
- ( instancetype )init
|
||||
{
|
||||
if( ( self = [ super init ] ) )
|
||||
{
|
||||
self.sensors = [ NSMutableDictionary new ];
|
||||
self.data = [ NSArray new ];
|
||||
self.shouldRun = YES;
|
||||
|
||||
dispatch_async
|
||||
(
|
||||
dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ),
|
||||
^( void )
|
||||
{
|
||||
[ self run ];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- ( void )stop: ( void ( ^ _Nullable )( void ) )completion
|
||||
{
|
||||
if( self.shouldRun == false )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.completion = completion;
|
||||
self.shouldRun = false;
|
||||
}
|
||||
|
||||
- ( void )run
|
||||
{
|
||||
IOHIDEventSystemClientRef client = IOHIDEventSystemClientCreate( kCFAllocatorDefault );
|
||||
NSDictionary * info = @{ @"Client" : [ NSValue valueWithPointer: client ] };
|
||||
NSTimer * timer = [ NSTimer timerWithTimeInterval: 1 target: self selector: @selector( readSensors: ) userInfo: info repeats: YES ];
|
||||
|
||||
[ self readSensors: timer ];
|
||||
[ [ NSRunLoop currentRunLoop ] addTimer: timer forMode: NSDefaultRunLoopMode ];
|
||||
|
||||
while( self.shouldRun )
|
||||
{
|
||||
[ [ NSRunLoop currentRunLoop ] runUntilDate: [ NSDate dateWithTimeIntervalSinceNow: 5 ] ];
|
||||
}
|
||||
|
||||
void ( ^ completion )( void ) = self.completion;
|
||||
self.completion = nil;
|
||||
|
||||
if( completion != nil )
|
||||
{
|
||||
completion();
|
||||
}
|
||||
}
|
||||
|
||||
- ( void )readSensors: ( NSTimer * )timer
|
||||
{
|
||||
IOHIDEventSystemClientRef client = ( ( NSValue * )( timer.userInfo[ @"Client" ] ) ).pointerValue;
|
||||
|
||||
if( client != nil )
|
||||
{
|
||||
[ self readSensors: SensorDataKindThermal page: IOHIDPageAppleVendor usage: IOHIDUsageAppleVendorTemperatureSensor event: IOHIDEventTypeTemperature client: client ];
|
||||
[ self readSensors: SensorDataKindVoltage page: IOHIDPageAppleVendorPowerSensor usage: IOHIDUsageAppleVendorPowerSensorVoltage event: IOHIDEventTypePower client: client ];
|
||||
[ self readSensors: SensorDataKindCurrent page: IOHIDPageAppleVendorPowerSensor usage: IOHIDUsageAppleVendorPowerSensorCurrent event: IOHIDEventTypePower client: client ];
|
||||
|
||||
dispatch_async
|
||||
(
|
||||
dispatch_get_main_queue(),
|
||||
^( void )
|
||||
{
|
||||
self.data = self.sensors.allValues;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
- ( void )readSensors: ( SensorDataKind )kind page: ( IOHIDPage )page usage: ( NSInteger )usage event: ( IOHIDEvent )eventType client: ( IOHIDEventSystemClientRef )client
|
||||
{
|
||||
NSDictionary * filter =
|
||||
@{
|
||||
@"PrimaryUsagePage" : [ NSNumber numberWithLongLong: page ],
|
||||
@"PrimaryUsage" : [ NSNumber numberWithLongLong: usage ]
|
||||
};
|
||||
|
||||
IOHIDEventSystemClientSetMatching( client, ( __bridge CFDictionaryRef )filter );
|
||||
|
||||
{
|
||||
NSArray * services = CFBridgingRelease( IOHIDEventSystemClientCopyServices( client ) );
|
||||
|
||||
for( id o in services )
|
||||
{
|
||||
IOHIDServiceClientRef service = ( __bridge IOHIDServiceClientRef )o;
|
||||
CFTypeRef event = IOHIDServiceClientCopyEvent( service, eventType, 0, 0 );
|
||||
NSString * name = CFBridgingRelease( IOHIDServiceClientCopyProperty( service, CFSTR( "Product" ) ) );
|
||||
NSArray * keys = @[ @"IOClass", @"IOProviderClass", @"IONameMatch", @"Product", @"DeviceUsagePairs" ];
|
||||
NSDictionary * properties = CFBridgingRelease( IOHIDServiceClientCopyProperties( service, ( __bridge CFArrayRef )keys ) );
|
||||
|
||||
if( name != nil && event != nil )
|
||||
{
|
||||
[ self addSensorData: IOHIDEventGetFloatValue( event, eventType << 16 ) name: name kind: kind properties: properties ];
|
||||
}
|
||||
|
||||
if( event != nil )
|
||||
{
|
||||
CFRelease( event );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- ( void )addSensorData: ( double )value name: ( NSString * )name kind: ( SensorDataKind )kind properties: ( nullable NSDictionary * )properties
|
||||
{
|
||||
NSString * key = [ NSString stringWithFormat: @"%llu.%@", ( uint64_t )kind, name ];
|
||||
SensorData * data = [ self.sensors objectForKey: key ];
|
||||
|
||||
if( data == nil )
|
||||
{
|
||||
data = [ [ SensorData alloc ] initWithKind: kind name: name properties: properties ];
|
||||
|
||||
[ self.sensors setObject: data forKey: key ];
|
||||
}
|
||||
|
||||
[ data addValue: value ];
|
||||
}
|
||||
|
||||
@end
|
||||
146
Hot/Classes/Sensors/SensorsWindowController.swift
Normal file
146
Hot/Classes/Sensors/SensorsWindowController.swift
Normal file
@@ -0,0 +1,146 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
import Cocoa
|
||||
|
||||
public class SensorsWindowController: NSWindowController
|
||||
{
|
||||
@objc private dynamic var sensors: Sensors? = Sensors()
|
||||
|
||||
@objc private dynamic var showTemperature = true
|
||||
{
|
||||
didSet { self.updateFilters() }
|
||||
}
|
||||
|
||||
@objc private dynamic var showVoltage = true
|
||||
{
|
||||
didSet { self.updateFilters() }
|
||||
}
|
||||
|
||||
@objc private dynamic var showCurrent = true
|
||||
{
|
||||
didSet { self.updateFilters() }
|
||||
}
|
||||
|
||||
@objc private dynamic var searchText: String?
|
||||
{
|
||||
didSet { self.updateFilters() }
|
||||
}
|
||||
|
||||
@objc private dynamic var graphStyle = UserDefaults.standard.integer( forKey: "graphStyle" )
|
||||
{
|
||||
didSet
|
||||
{
|
||||
UserDefaults.standard.set( self.graphStyle, forKey: "graphStyle" )
|
||||
}
|
||||
}
|
||||
|
||||
@IBOutlet private var collectionView: NSCollectionView!
|
||||
@IBOutlet private var arrayController: NSArrayController!
|
||||
|
||||
public override var windowNibName: NSNib.Name?
|
||||
{
|
||||
"SensorsWindowController"
|
||||
}
|
||||
|
||||
public override func windowDidLoad()
|
||||
{
|
||||
super.windowDidLoad()
|
||||
|
||||
self.arrayController.sortDescriptors = [ NSSortDescriptor( key: "name", ascending: true, selector: #selector( NSString.localizedCaseInsensitiveCompare( _: ) ) ) ]
|
||||
}
|
||||
|
||||
public func stop()
|
||||
{
|
||||
self.sensors?.stop
|
||||
{
|
||||
DispatchQueue.main.async
|
||||
{
|
||||
self.sensors = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateFilters()
|
||||
{
|
||||
var predicates = [ NSPredicate ]()
|
||||
|
||||
if self.showTemperature == false
|
||||
{
|
||||
predicates.append( NSPredicate { o, i in ( o as? SensorData )?.kind != .thermal } )
|
||||
}
|
||||
|
||||
if self.showVoltage == false
|
||||
{
|
||||
predicates.append( NSPredicate { o, i in ( o as? SensorData )?.kind != .voltage } )
|
||||
}
|
||||
|
||||
if self.showCurrent == false
|
||||
{
|
||||
predicates.append( NSPredicate { o, i in ( o as? SensorData )?.kind != .current } )
|
||||
}
|
||||
|
||||
if let search = self.searchText, search.count > 0
|
||||
{
|
||||
predicates.append( NSPredicate( format: "name contains[c] %@", search ) )
|
||||
}
|
||||
|
||||
if predicates.count > 0
|
||||
{
|
||||
self.arrayController.filterPredicate = NSCompoundPredicate( andPredicateWithSubpredicates: predicates )
|
||||
}
|
||||
else
|
||||
{
|
||||
self.arrayController.filterPredicate = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SensorsWindowController: NSCollectionViewDataSource
|
||||
{
|
||||
public func numberOfSections( in collectionView: NSCollectionView ) -> Int
|
||||
{
|
||||
1
|
||||
}
|
||||
|
||||
public func collectionView( _ collectionView: NSCollectionView, numberOfItemsInSection section: Int ) -> Int
|
||||
{
|
||||
( self.arrayController.arrangedObjects as? [ SensorData ] )?.count ?? 0
|
||||
}
|
||||
|
||||
public func collectionView( _ itemForRepresentedObjectAtcollectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath ) -> NSCollectionViewItem
|
||||
{
|
||||
let item = collectionView.makeItem( withIdentifier: NSUserInterfaceItemIdentifier( rawValue: "SensorItem" ), for: indexPath )
|
||||
|
||||
if let item = item as? SensorItem, let sensors = self.arrayController.arrangedObjects as? [ SensorData ]
|
||||
{
|
||||
if sensors.count > indexPath.item
|
||||
{
|
||||
item.sensor = sensors[ indexPath.item ]
|
||||
}
|
||||
}
|
||||
|
||||
return item
|
||||
}
|
||||
}
|
||||
57
Hot/Classes/Sensors/Synchronizable.swift
Normal file
57
Hot/Classes/Sensors/Synchronizable.swift
Normal file
@@ -0,0 +1,57 @@
|
||||
/*******************************************************************************
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Jean-David Gadina - www.xs-labs.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol Synchronizable
|
||||
{
|
||||
static func synchronized< T >( closure: () -> T ) -> T
|
||||
|
||||
func synchronized< T >( closure: () -> T ) -> T
|
||||
}
|
||||
|
||||
public extension Synchronizable
|
||||
{
|
||||
static func synchronized< T >( closure: () -> T ) -> T
|
||||
{
|
||||
objc_sync_enter( self )
|
||||
|
||||
let r = closure()
|
||||
|
||||
objc_sync_exit( self )
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func synchronized< T >( closure: () -> T ) -> T
|
||||
{
|
||||
objc_sync_enter( self )
|
||||
|
||||
let r = closure()
|
||||
|
||||
objc_sync_exit( self )
|
||||
|
||||
return r
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.4.1</string>
|
||||
<string>1.5.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>75</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19162" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19162"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
@@ -720,6 +720,17 @@
|
||||
</binding>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="View all Sensors..." id="0oq-7C-y22">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="viewAllSensors:" target="Voe-Tx-rLC" id="K7Q-o7-xbP"/>
|
||||
<binding destination="Voe-Tx-rLC" name="hidden" keyPath="self.infoViewController.isARM" id="y1k-9f-cAb">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">NSNegateBoolean</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="Nbp-cG-gpV"/>
|
||||
<menuItem title="Preferences..." keyEquivalent="," id="JFH-mp-6PU">
|
||||
<connections>
|
||||
|
||||
142
Hot/UI/Sensors/Base.lproj/SensorItem.xib
Normal file
142
Hot/UI/Sensors/Base.lproj/SensorItem.xib
Normal file
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner"/>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView id="c22-O7-iKe">
|
||||
<rect key="frame" x="0.0" y="0.0" width="245" height="120"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Zhs-tE-zrk">
|
||||
<rect key="frame" x="0.0" y="100" width="20" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="g2z-r1-gYP"/>
|
||||
<constraint firstAttribute="width" constant="20" id="kP1-0x-y1M"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="NSApplicationIcon" id="YpG-g1-XTw"/>
|
||||
<connections>
|
||||
<binding destination="ek2-AR-oS5" name="value" keyPath="self.sensor" id="EbR-ou-eai">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">SensorIcon</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</imageView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Vx7-2w-JY2">
|
||||
<rect key="frame" x="26" y="105" width="28" height="11"/>
|
||||
<textFieldCell key="cell" controlSize="mini" lineBreakMode="truncatingTail" title="Label" id="4QE-tU-2iF">
|
||||
<font key="font" metaFont="miniSystem"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="ek2-AR-oS5" name="toolTip" keyPath="self.sensor.name" id="sk7-rh-Bgb"/>
|
||||
<binding destination="ek2-AR-oS5" name="value" keyPath="self.sensor.name" id="COB-ax-oMZ"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qyW-PJ-lOk">
|
||||
<rect key="frame" x="219" y="105" width="28" height="11"/>
|
||||
<textFieldCell key="cell" controlSize="mini" lineBreakMode="truncatingTail" title="Label" id="05S-DK-Pm8">
|
||||
<font key="font" metaFont="miniSystem"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="ek2-AR-oS5" name="toolTip" keyPath="self.sensor.name" id="RUP-1u-HBl"/>
|
||||
<binding destination="ek2-AR-oS5" name="value" keyPath="self.sensor.last" id="671-ZR-hc1">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">SensorValue</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="XSC-DH-60t">
|
||||
<rect key="frame" x="0.0" y="19" width="245" height="73"/>
|
||||
</customView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KB3-h4-RKJ">
|
||||
<rect key="frame" x="-2" y="0.0" width="23" height="11"/>
|
||||
<textFieldCell key="cell" controlSize="mini" lineBreakMode="clipping" title="Min:" id="P1F-RJ-VTt">
|
||||
<font key="font" metaFont="miniSystem"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WDq-1D-eKf">
|
||||
<rect key="frame" x="25" y="0.0" width="28" height="11"/>
|
||||
<textFieldCell key="cell" controlSize="mini" lineBreakMode="clipping" title="Label" id="pVf-bC-aUU">
|
||||
<font key="font" metaFont="miniSystem"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="ek2-AR-oS5" name="value" keyPath="self.sensor.min" id="Hz7-WE-4XS">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">SensorValue</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PeT-bn-qut">
|
||||
<rect key="frame" x="190" y="0.0" width="25" height="11"/>
|
||||
<textFieldCell key="cell" controlSize="mini" lineBreakMode="clipping" title="Max:" id="piP-vw-2QX">
|
||||
<font key="font" metaFont="miniSystem"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zT4-AQ-MeS">
|
||||
<rect key="frame" x="219" y="0.0" width="28" height="11"/>
|
||||
<textFieldCell key="cell" controlSize="mini" lineBreakMode="clipping" title="Label" id="0uv-Pe-gZH">
|
||||
<font key="font" metaFont="miniSystem"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="ek2-AR-oS5" name="value" keyPath="self.sensor.max" id="q5h-49-6GI">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">SensorValue</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="qyW-PJ-lOk" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Vx7-2w-JY2" secondAttribute="trailing" constant="8" symbolic="YES" id="37w-zR-zSG"/>
|
||||
<constraint firstAttribute="bottom" secondItem="zT4-AQ-MeS" secondAttribute="bottom" id="6Eb-Jb-ktv"/>
|
||||
<constraint firstItem="zT4-AQ-MeS" firstAttribute="leading" secondItem="PeT-bn-qut" secondAttribute="trailing" constant="8" symbolic="YES" id="6Iv-Zk-l6p"/>
|
||||
<constraint firstItem="Vx7-2w-JY2" firstAttribute="centerY" secondItem="Zhs-tE-zrk" secondAttribute="centerY" id="E6p-Sp-hhE"/>
|
||||
<constraint firstItem="Zhs-tE-zrk" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" id="F4w-Ta-FMp"/>
|
||||
<constraint firstItem="zT4-AQ-MeS" firstAttribute="centerY" secondItem="PeT-bn-qut" secondAttribute="centerY" id="GLP-jq-o9j"/>
|
||||
<constraint firstItem="Zhs-tE-zrk" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" id="H4R-eE-jNf"/>
|
||||
<constraint firstItem="KB3-h4-RKJ" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" id="LlD-cK-jrf"/>
|
||||
<constraint firstItem="XSC-DH-60t" firstAttribute="top" secondItem="Zhs-tE-zrk" secondAttribute="bottom" constant="8" symbolic="YES" id="RBs-v9-fzB"/>
|
||||
<constraint firstItem="XSC-DH-60t" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" id="Y8P-AG-Uex"/>
|
||||
<constraint firstItem="qyW-PJ-lOk" firstAttribute="centerY" secondItem="Vx7-2w-JY2" secondAttribute="centerY" id="ZfP-Ip-Qp3"/>
|
||||
<constraint firstItem="WDq-1D-eKf" firstAttribute="leading" secondItem="KB3-h4-RKJ" secondAttribute="trailing" constant="8" symbolic="YES" id="bnr-IM-2mn"/>
|
||||
<constraint firstItem="PeT-bn-qut" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="WDq-1D-eKf" secondAttribute="trailing" constant="8" symbolic="YES" id="dRS-W2-HF3"/>
|
||||
<constraint firstAttribute="bottom" secondItem="KB3-h4-RKJ" secondAttribute="bottom" id="gjk-x9-oBq"/>
|
||||
<constraint firstAttribute="trailing" secondItem="zT4-AQ-MeS" secondAttribute="trailing" id="ivM-u5-F81"/>
|
||||
<constraint firstItem="WDq-1D-eKf" firstAttribute="centerY" secondItem="KB3-h4-RKJ" secondAttribute="centerY" id="k8I-Sa-B0p"/>
|
||||
<constraint firstAttribute="trailing" secondItem="XSC-DH-60t" secondAttribute="trailing" id="ma2-2p-gXZ"/>
|
||||
<constraint firstItem="Vx7-2w-JY2" firstAttribute="leading" secondItem="Zhs-tE-zrk" secondAttribute="trailing" constant="8" symbolic="YES" id="pSn-l6-1WW"/>
|
||||
<constraint firstItem="KB3-h4-RKJ" firstAttribute="top" secondItem="XSC-DH-60t" secondAttribute="bottom" constant="8" symbolic="YES" id="v2T-jr-FYT"/>
|
||||
<constraint firstAttribute="trailing" secondItem="qyW-PJ-lOk" secondAttribute="trailing" id="zrr-cs-S3r"/>
|
||||
</constraints>
|
||||
<point key="canvasLocation" x="26.5" y="125"/>
|
||||
</customView>
|
||||
<collectionViewItem id="ek2-AR-oS5" customClass="SensorItem" customModule="Sensors" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="graphContainer" destination="XSC-DH-60t" id="FUo-iJ-Gkw"/>
|
||||
<outlet property="view" destination="c22-O7-iKe" id="W6K-LU-Klf"/>
|
||||
</connections>
|
||||
</collectionViewItem>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="NSApplicationIcon" width="32" height="32"/>
|
||||
</resources>
|
||||
</document>
|
||||
239
Hot/UI/Sensors/Base.lproj/SensorsWindowController.xib
Normal file
239
Hot/UI/Sensors/Base.lproj/SensorsWindowController.xib
Normal file
@@ -0,0 +1,239 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19162" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19162"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="SensorsWindowController" customModule="Hot" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="arrayController" destination="k4f-4o-be4" id="F6X-Lo-Fc0"/>
|
||||
<outlet property="collectionView" destination="d8j-id-ziA" id="tbm-Yo-sK6"/>
|
||||
<outlet property="window" destination="QvC-M9-y7g" id="3Jc-9C-AuF"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Sensors" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="MainWindow" animationBehavior="default" titlebarAppearsTransparent="YES" id="QvC-M9-y7g">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" fullSizeContentView="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="800" height="515"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1025"/>
|
||||
<value key="minSize" type="size" width="800" height="515"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="800" height="515"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<visualEffectView blendingMode="behindWindow" material="underWindowBackground" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="uRj-rz-hcT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="800" height="515"/>
|
||||
<subviews>
|
||||
<scrollView wantsLayer="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aKe-JS-2Ui">
|
||||
<rect key="frame" x="20" y="102" width="760" height="383"/>
|
||||
<clipView key="contentView" drawsBackground="NO" id="zVN-hJ-YmX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="760" height="383"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<collectionView id="d8j-id-ziA">
|
||||
<rect key="frame" x="0.0" y="0.0" width="760" height="383"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES"/>
|
||||
<collectionViewFlowLayout key="collectionViewLayout" minimumInteritemSpacing="10" minimumLineSpacing="10" id="jv5-sj-5QH">
|
||||
<size key="itemSize" width="245" height="120"/>
|
||||
</collectionViewFlowLayout>
|
||||
<color key="primaryBackgroundColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<binding destination="k4f-4o-be4" name="content" keyPath="arrangedObjects" id="WfZ-ms-k8f"/>
|
||||
<binding destination="k4f-4o-be4" name="selectionIndexes" keyPath="selectionIndexes" previousBinding="WfZ-ms-k8f" id="JRi-HO-Ojp"/>
|
||||
<outlet property="dataSource" destination="-2" id="BJq-MM-cYl"/>
|
||||
</connections>
|
||||
</collectionView>
|
||||
</subviews>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="pbH-A9-JB1">
|
||||
<rect key="frame" x="-100" y="-100" width="233" height="15"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="F9Y-Pq-ygs">
|
||||
<rect key="frame" x="744" y="0.0" width="16" height="368"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<connections>
|
||||
<binding destination="-2" name="hidden" keyPath="self.sensors.data" id="CrI-nj-hKU">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">ArrayIsEmpty</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</scrollView>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="X4f-ON-Yh7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="800" height="102"/>
|
||||
<subviews>
|
||||
<searchField wantsLayer="YES" verticalHuggingPriority="750" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0b0-bM-ppa">
|
||||
<rect key="frame" x="20" y="60" width="760" height="22"/>
|
||||
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" id="m6w-FG-kg5">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</searchFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.searchText" id="n2O-a4-5b5">
|
||||
<dictionary key="options">
|
||||
<bool key="NSContinuouslyUpdatesValue" value="YES"/>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</searchField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CWx-dr-5aC">
|
||||
<rect key="frame" x="18" y="21" width="103" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Temperature" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="lZo-Np-2Ev">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.showTemperature" id="IqV-HK-3FM"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YH9-KV-isM">
|
||||
<rect key="frame" x="127" y="21" width="72" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Voltage" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="7Kn-WX-eN1">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.showVoltage" id="SEA-Tu-BOl"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="DL0-ry-nBa">
|
||||
<rect key="frame" x="205" y="21" width="72" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Current" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="WBA-tr-bDK">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.showCurrent" id="McY-1z-eLE"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wh8-P7-8p4">
|
||||
<rect key="frame" x="610" y="22" width="79" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Graph Style:" id="6Ty-8X-UI6">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="b2Z-bC-lIL">
|
||||
<rect key="frame" x="692" y="16" width="92" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Gradient" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="UpT-Zz-emE" id="ToP-qb-kLc">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="bNn-yW-MVT">
|
||||
<items>
|
||||
<menuItem title="Gradient" state="on" id="UpT-Zz-emE"/>
|
||||
<menuItem title="Line" tag="1" id="VP7-OH-4an"/>
|
||||
<menuItem title="Fill" tag="2" id="Uix-Lw-WIT"/>
|
||||
<menuItem title="Bars" tag="3" id="j4P-bQ-osF"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="selectedTag" keyPath="self.graphStyle" id="LYW-IL-Fda"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="0b0-bM-ppa" firstAttribute="leading" secondItem="X4f-ON-Yh7" secondAttribute="leading" constant="20" symbolic="YES" id="7Hg-zM-lO0"/>
|
||||
<constraint firstItem="wh8-P7-8p4" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="DL0-ry-nBa" secondAttribute="trailing" constant="8" symbolic="YES" id="AMf-YL-aMc"/>
|
||||
<constraint firstItem="YH9-KV-isM" firstAttribute="centerY" secondItem="CWx-dr-5aC" secondAttribute="centerY" id="DiY-62-WNM"/>
|
||||
<constraint firstItem="0b0-bM-ppa" firstAttribute="top" secondItem="X4f-ON-Yh7" secondAttribute="top" constant="20" symbolic="YES" id="MLt-9Q-NWw"/>
|
||||
<constraint firstItem="wh8-P7-8p4" firstAttribute="centerY" secondItem="DL0-ry-nBa" secondAttribute="centerY" id="TS8-6H-T7P"/>
|
||||
<constraint firstItem="DL0-ry-nBa" firstAttribute="centerY" secondItem="YH9-KV-isM" secondAttribute="centerY" id="UFB-Be-d69"/>
|
||||
<constraint firstItem="b2Z-bC-lIL" firstAttribute="leading" secondItem="wh8-P7-8p4" secondAttribute="trailing" constant="8" symbolic="YES" id="V4o-Be-jIe"/>
|
||||
<constraint firstItem="CWx-dr-5aC" firstAttribute="leading" secondItem="X4f-ON-Yh7" secondAttribute="leading" constant="20" symbolic="YES" id="kHN-fy-wU3"/>
|
||||
<constraint firstItem="DL0-ry-nBa" firstAttribute="leading" secondItem="YH9-KV-isM" secondAttribute="trailing" constant="8" symbolic="YES" id="kXC-gz-rYd"/>
|
||||
<constraint firstItem="b2Z-bC-lIL" firstAttribute="top" secondItem="0b0-bM-ppa" secondAttribute="bottom" constant="20" symbolic="YES" id="nmc-VG-jfo"/>
|
||||
<constraint firstAttribute="bottom" secondItem="b2Z-bC-lIL" secondAttribute="bottom" constant="20" symbolic="YES" id="qF9-cm-S3p"/>
|
||||
<constraint firstItem="YH9-KV-isM" firstAttribute="leading" secondItem="CWx-dr-5aC" secondAttribute="trailing" constant="8" symbolic="YES" id="sOb-Yn-u3A"/>
|
||||
<constraint firstAttribute="trailing" secondItem="0b0-bM-ppa" secondAttribute="trailing" constant="20" symbolic="YES" id="vhy-3U-orM"/>
|
||||
<constraint firstAttribute="trailing" secondItem="b2Z-bC-lIL" secondAttribute="trailing" constant="20" symbolic="YES" id="vmq-Sh-oFu"/>
|
||||
<constraint firstItem="b2Z-bC-lIL" firstAttribute="centerY" secondItem="wh8-P7-8p4" secondAttribute="centerY" id="wRu-AK-pi5"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="DL0-ry-nBa" secondAttribute="trailing" constant="20" symbolic="YES" id="yDe-dg-UV7"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<binding destination="-2" name="hidden" keyPath="self.sensors.data" id="oYT-Hj-xEu">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">ArrayIsEmpty</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</customView>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Asg-vC-Nsi">
|
||||
<rect key="frame" x="252" y="98" width="296" height="320"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="yFI-k2-eyH">
|
||||
<rect key="frame" x="20" y="44" width="256" height="256"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="256" id="3bQ-I0-4D7"/>
|
||||
<constraint firstAttribute="width" constant="256" id="wmc-Wj-n9C"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="NSApplicationIcon" id="ccv-7E-1yf"/>
|
||||
</imageView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="caY-ih-a0p">
|
||||
<rect key="frame" x="64" y="20" width="168" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="No sensor data available..." id="dA5-eI-Kva">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="yFI-k2-eyH" firstAttribute="leading" secondItem="Asg-vC-Nsi" secondAttribute="leading" constant="20" symbolic="YES" id="6hv-WO-Mp2"/>
|
||||
<constraint firstItem="caY-ih-a0p" firstAttribute="centerX" secondItem="yFI-k2-eyH" secondAttribute="centerX" id="FCJ-Ar-PuC"/>
|
||||
<constraint firstAttribute="bottom" secondItem="caY-ih-a0p" secondAttribute="bottom" constant="20" symbolic="YES" id="GPb-JF-OQU"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="caY-ih-a0p" secondAttribute="trailing" constant="20" symbolic="YES" id="aav-Ni-BKP"/>
|
||||
<constraint firstAttribute="trailing" secondItem="yFI-k2-eyH" secondAttribute="trailing" constant="20" symbolic="YES" id="isW-X4-uYe"/>
|
||||
<constraint firstItem="caY-ih-a0p" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Asg-vC-Nsi" secondAttribute="leading" constant="20" symbolic="YES" id="pxk-3O-W79"/>
|
||||
<constraint firstItem="caY-ih-a0p" firstAttribute="top" secondItem="yFI-k2-eyH" secondAttribute="bottom" constant="8" symbolic="YES" id="vsg-hK-dIh"/>
|
||||
<constraint firstItem="yFI-k2-eyH" firstAttribute="top" secondItem="Asg-vC-Nsi" secondAttribute="top" constant="20" symbolic="YES" id="woG-6u-Efg"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<binding destination="-2" name="hidden" keyPath="self.sensors.data" id="nDO-gY-oWD">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">ArrayIsNotEmpty</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</customView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="X4f-ON-Yh7" firstAttribute="top" secondItem="aKe-JS-2Ui" secondAttribute="bottom" id="5IN-cs-jqm"/>
|
||||
<constraint firstItem="Asg-vC-Nsi" firstAttribute="centerX" secondItem="uRj-rz-hcT" secondAttribute="centerX" id="961-zO-gq7"/>
|
||||
<constraint firstItem="aKe-JS-2Ui" firstAttribute="leading" secondItem="uRj-rz-hcT" secondAttribute="leading" constant="20" symbolic="YES" id="K0o-wa-efC"/>
|
||||
<constraint firstAttribute="bottom" secondItem="X4f-ON-Yh7" secondAttribute="bottom" id="L6P-Pp-IP7"/>
|
||||
<constraint firstAttribute="trailing" secondItem="X4f-ON-Yh7" secondAttribute="trailing" id="Lxr-7P-w7M"/>
|
||||
<constraint firstAttribute="trailing" secondItem="aKe-JS-2Ui" secondAttribute="trailing" constant="20" symbolic="YES" id="RZP-rx-Xju"/>
|
||||
<constraint firstItem="aKe-JS-2Ui" firstAttribute="top" secondItem="uRj-rz-hcT" secondAttribute="top" constant="30" id="b5q-1K-Vb2"/>
|
||||
<constraint firstItem="X4f-ON-Yh7" firstAttribute="leading" secondItem="uRj-rz-hcT" secondAttribute="leading" id="ffA-eK-qjQ"/>
|
||||
<constraint firstItem="Asg-vC-Nsi" firstAttribute="centerY" secondItem="uRj-rz-hcT" secondAttribute="centerY" id="hRR-yE-RmG"/>
|
||||
</constraints>
|
||||
</visualEffectView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="uRj-rz-hcT" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" id="WwN-si-Yl6"/>
|
||||
<constraint firstAttribute="trailing" secondItem="uRj-rz-hcT" secondAttribute="trailing" id="eKa-0b-qZ9"/>
|
||||
<constraint firstItem="uRj-rz-hcT" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" id="f7k-yq-eVb"/>
|
||||
<constraint firstAttribute="bottom" secondItem="uRj-rz-hcT" secondAttribute="bottom" id="v4a-ky-3o2"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<point key="canvasLocation" x="-110" y="144"/>
|
||||
</window>
|
||||
<arrayController selectsInsertedObjects="NO" avoidsEmptySelection="NO" clearsFilterPredicateOnInsertion="NO" automaticallyRearrangesObjects="YES" id="k4f-4o-be4">
|
||||
<connections>
|
||||
<binding destination="-2" name="contentArray" keyPath="self.sensors.data" id="V1H-EA-tCm"/>
|
||||
</connections>
|
||||
</arrayController>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="NSApplicationIcon" width="32" height="32"/>
|
||||
</resources>
|
||||
</document>
|
||||
15
README.md
15
README.md
@@ -12,7 +12,20 @@ Hot
|
||||
|
||||
Hot is macOS menu bar application that displays the CPU speed limit due to thermal issues.
|
||||
|
||||

|
||||
#### Differences between the Intel and Apple Silicon versions
|
||||
|
||||
On an Intel machine, Hot will display the CPU temperature, CPU speed limit (throttling), scheduler limit and number of available CPUs.
|
||||
By default, the menu bar text will be colorized in orange if the CPU speed limit falls below 60%.
|
||||
|
||||
On Apple Silicon, these informations are not available.
|
||||
Along with the CPU temperatire, Hot will display the system's thermal pressure.
|
||||
The menu bar text will be colorized in orange if the pressure is not nominal.
|
||||
|
||||
A graph view for all sensors may also be displayed on Apple Silicon.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Reference in New Issue
Block a user